1 /**
2 Copyright: Copyright (c) 2013-2018 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.storage.chunk.layer;
7
8 import voxelman.log;
9 import std.array : uninitializedArray;
10 import std..string : format;
11 import std.typecons : Nullable;
12
13 import cbor;
14 import voxelman.math;
15
16 import voxelman.core.config;
17 import voxelman.world.block;
18 import voxelman.algorithm.arraycopy3d;
19 import voxelman.world.storage.coordinates;
20 import voxelman.world.storage.utils;
21 import voxelman.platform.compression;
22
23 enum BLOCK_LAYER = 0;
24 enum ENTITY_LAYER = 1;
25 enum METADATA_LAYER = 2;
26 enum NUM_CHUNK_LAYERS = 3;
27
28 enum BLOCKS_DATA_LENGTH = CHUNK_SIZE_CUBE * BlockId.sizeof;
29 enum BLOCKID_UNIFORM_FILL_BITS = bitsToUniformLength!BlockId;
30 enum BLOCK_METADATA_UNIFORM_FILL_BITS = bitsToUniformLength!BlockMetadata;
31
32 ubyte bitsToUniformLength(T)()
33 {
34 return bitsToUniformLength(T.sizeof * 8);
35 }
36
37 ubyte bitsToUniformLength(ubyte bits) {
38 if (bits == 1)
39 return 1;
40 else if (bits == 2)
41 return 2;
42 else if (bits == 4)
43 return 3;
44 else if (bits == 8)
45 return 4;
46 else if (bits == 16)
47 return 5;
48 else if (bits == 32)
49 return 6;
50 else if (bits == 64)
51 return 7;
52 else
53 assert(false);
54 }
55
56 /// is used as array size when non-uniform.
57 /// Used to allocate full array when uniform.
58 /// Assumes array length as CHUNK_SIZE_CUBE, but element size is dataLength/CHUNK_SIZE_CUBE if CHUNK_SIZE_CUBE > dataLength
59 /// Or CHUNK_SIZE_CUBE/dataLength otherwise. Element size is used to fill created array.
60 /// If dataLength < CHUNK_SIZE_CUBE then element size is less than byte.
61 /// Element size must be power of two, either of bytes or of bits.
62 alias LayerDataLenType = uint;
63
64 enum StorageType : ubyte
65 {
66 uniform,
67 //linearMap,
68 //hashMap,
69 compressedArray,
70 fullArray,
71 }
72
73 import std.experimental.allocator;
74 import std.experimental.allocator.mallocator;
75
76 ubyte[] allocLayerArray(ubyte[] dataToCopy) {
77 auto res = makeArray!ubyte(Mallocator.instance, dataToCopy);
78 //tracef("allocLayerArray1 %s", res.ptr);
79 return res;
80 }
81
82 ubyte[] reallocLayerArray(Layer)(ref Layer layer, size_t newLength) {
83 assert(layer.type != StorageType.uniform);
84 void[] data = layer.getArray!ubyte;
85 Mallocator.instance.reallocate(data, newLength);
86 layer.dataPtr = data.ptr;
87 layer.dataLength = cast(LayerDataLenType)data.length;
88 //tracef("reallocLayerArray %s", data.ptr);
89 return cast(ubyte[])data;
90 }
91
92 ubyte[] allocLayerArray(size_t length) {
93 auto res = makeArray!ubyte(Mallocator.instance, length);
94 //tracef("allocLayerArray2 %s", res.ptr);
95 return res;
96 }
97
98 void freeLayerArray(Layer)(ref Layer layer) {
99 //tracef("freeLayerArray %s %s", layer.dataPtr, layer);
100 if(layer.type != StorageType.uniform)
101 {
102 ubyte[] data = layer.getArray!ubyte;
103 dispose(Mallocator.instance, data);
104 layer.dataPtr = null;
105 layer.dataLength = 0;
106 }
107 }
108
109 struct ChunkHeaderItem {
110 ChunkWorldPos cwp;
111 uint numLayers;
112 uint metadata; // for task purposes
113 }
114 static assert(ChunkHeaderItem.sizeof == 16);
115
116 struct ChunkLayerTimestampItem {
117 uint timestamp;
118 ubyte layerId;
119 }
120 static assert(ChunkLayerTimestampItem.sizeof == 8);
121
122 /// Stores layer of chunk data. Used for transferring and saving chunk layers.
123 /// Stores layerId. Stores no user count.
124 align(1)
125 struct ChunkLayerItem
126 {
127 StorageType type;
128 ubyte layerId;
129 ushort metadata;
130 uint timestamp;
131 union {
132 ulong uniformData;
133 void* dataPtr; /// Stores ptr to the first byte of data. The length of data is in dataLength.
134 }
135 LayerDataLenType dataLength;
136
137 this(ubyte _layerId, LayerDataLenType _dataLength, uint _timestamp, ulong _uniformData, ushort _metadata = 0) {
138 type = StorageType.uniform; layerId = _layerId; dataLength = _dataLength; timestamp = _timestamp; uniformData = _uniformData; metadata = _metadata;
139 }
140 this(StorageType _type, ubyte _layerId, LayerDataLenType _dataLength, uint _timestamp, ubyte* _dataPtr, ushort _metadata = 0) {
141 type = _type; layerId = _layerId; dataLength = _dataLength; timestamp = _timestamp; dataPtr = _dataPtr; metadata = _metadata;
142 }
143 this(T)(StorageType _type, ubyte _layerId, uint _timestamp, T[] _array, ushort _metadata = 0) {
144 ubyte[] data = cast(ubyte[])_array;
145 type = _type; layerId = _layerId; dataLength = cast(LayerDataLenType)data.length; timestamp = _timestamp; dataPtr = cast(void*)data.ptr; metadata = _metadata;
146 }
147 this(ChunkLayerSnap l, ubyte _layerId) {
148 type = l.type;
149 layerId = _layerId;
150 dataLength = l.dataLength;
151 timestamp = l.timestamp;
152 uniformData = l.uniformData;
153 metadata = l.metadata;
154 }
155
156 void toString()(scope void delegate(const(char)[]) sink) const
157 {
158 import std.format : formattedWrite;
159 sink.formattedWrite("ChunkLayerItem(%s, %s, %s, %s, {%s, %s}, %s)",
160 type, layerId, dataLength, timestamp, uniformData, dataPtr, metadata);
161 }
162 }
163 static assert(ChunkLayerItem.sizeof == 20);
164
165 struct WriteBuffer
166 {
167 ChunkLayerItem layer;
168 // If false, no changes will be performed on commit.
169 bool isModified = true;
170 // If true, after commit current snapshot is null.
171 bool removeSnapshot = false;
172
173 void makeUniform(ulong uniformData, LayerDataLenType dataLength, ushort metadata = 0) {
174 freeLayerArray(layer);
175 layer.uniformData = uniformData;
176 layer.dataLength = dataLength;
177 layer.metadata = metadata;
178 }
179
180 void makeUniform(T)(ulong uniformData, ushort metadata = 0) {
181 freeLayerArray(layer);
182 layer.uniformData = uniformData;
183 layer.dataLength = bitsToUniformLength!T;
184 layer.metadata = metadata;
185 }
186
187 bool isUniform() {
188 return layer.type == StorageType.uniform;
189 }
190
191 T getUniform(T)() {
192 return layer.getUniform!T;
193 }
194
195 T[] getArray(T)() {
196 return layer.getArray!T;
197 }
198 }
199
200 void expandUniformLayer(Layer)(ref Layer layer)
201 if (isSomeLayer!Layer)
202 {
203 assert(layer.type == StorageType.uniform);
204
205 // zero is used to denote that uniform cannot be expanded and should produce empty array.
206 assert(layer.dataLength >= 0 && layer.dataLength <= 7,
207 format("dataLength == %s", layer.dataLength));
208 if (layer.dataLength > 0)
209 {
210 size_t itemBitsPowerOfTwo = layer.dataLength - 1; // [1; 7] => [0; 6]
211 size_t itemBits = 1 << itemBitsPowerOfTwo; // [1..64]
212 size_t arraySize = (CHUNK_SIZE_CUBE * itemBits) / 8;
213
214 ubyte[] buffer = ensureLayerArrayLength(layer, arraySize);
215 expandUniform(buffer, cast(ubyte)itemBits, layer.uniformData);
216 layer.dataPtr = buffer.ptr;
217 layer.dataLength = cast(LayerDataLenType)buffer.length;
218 }
219 else
220 {
221 // empty array
222 }
223 layer.type = StorageType.fullArray;
224 }
225
226 // copies layer array without umcompressing
227 void copyLayer(Layer1, Layer2)(const Layer1 source, ref Layer2 dest)
228 if (isSomeLayer!Layer1 && isSomeLayer!Layer2)
229 {
230 assert(dest.isUniform);
231 dest = source;
232 if (!dest.isUniform)
233 {
234 auto data = dest.getArray!ubyte;
235 auto copy = allocLayerArray(data.length);
236 copy[] = data;
237 dest.dataPtr = copy.ptr;
238 dest.dataLength = cast(LayerDataLenType)copy.length;
239 }
240 }
241
242 // copies layer's data into write buffer filling fullArray
243 void applyLayer(Layer1, Layer2)(const Layer1 layer, ref Layer2 writeBuffer)
244 if (isSomeLayer!Layer1 && isSomeLayer!Layer2)
245 {
246 ubyte[] buffer;
247 if (layer.type == StorageType.uniform)
248 {
249 // zero is used to denote that uniform cannot be expanded and should produce empty array.
250 assert(layer.dataLength >= 0 && layer.dataLength <= 7,
251 format("dataLength == %s", layer.dataLength));
252 if (layer.dataLength > 0)
253 {
254 size_t itemBitsPowerOfTwo = layer.dataLength - 1; // [1; 7] => [0; 6]
255 size_t itemBits = 1 << itemBitsPowerOfTwo; // [1..64]
256 size_t arraySize = (CHUNK_SIZE_CUBE * itemBits) / 8;
257
258 buffer = ensureLayerArrayLength(writeBuffer, arraySize);
259 expandUniform(buffer, cast(ubyte)itemBits, layer.uniformData);
260 }
261 else
262 {
263 // empty array
264 }
265 }
266 else if (layer.type == StorageType.fullArray)
267 {
268 buffer = ensureLayerArrayLength(writeBuffer, layer.dataLength);
269 buffer[] = layer.getArray!ubyte;
270 }
271 else if (layer.type == StorageType.compressedArray)
272 {
273 ubyte[] compressedData = layer.getArray!ubyte;
274 size_t uncompressedLength = uncompressedDataLength(compressedData);
275 buffer = ensureLayerArrayLength(writeBuffer, uncompressedLength);
276 ubyte[] decompressedData = decompress(compressedData, buffer);
277 assert(decompressedData.length == buffer.length);
278 }
279 writeBuffer.type = StorageType.fullArray;
280 writeBuffer.metadata = layer.metadata;
281 writeBuffer.dataPtr = buffer.ptr;
282 writeBuffer.dataLength = cast(LayerDataLenType)buffer.length;
283 }
284
285 ubyte[] ensureLayerArrayLength(Layer)(ref Layer layer, size_t length)
286 if (isSomeLayer!Layer)
287 {
288 ubyte[] buffer;
289 if (layer.isUniform)
290 {
291 buffer = allocLayerArray(length);
292 }
293 else
294 {
295 if (layer.dataLength == length)
296 {
297 buffer = layer.getArray!ubyte;
298 }
299 else
300 {
301 //buffer = reallocLayerArray(layer, length);
302 freeLayerArray(layer); // TODO realloc
303 buffer = allocLayerArray(length);
304 }
305 }
306 return buffer;
307 }
308
309 // tables of repeated bit patterns for 1, 2 and 4 bit items.
310 private static immutable ubyte[2] bitsTable1 = [0b0000_0000, 0b1111_1111];
311 private static immutable ubyte[4] bitsTable2 = [0b00_00_00_00, 0b01_01_01_01, 0b10_10_10_10, 0b11_11_11_11];
312 private static immutable ubyte[16] bitsTable4 = [
313 0b0000_0000, 0b0001_0001, 0b0010_0010, 0b0011_0011, 0b0100_0100, 0b0101_0101, 0b0110_0110, 0b0111_0111,
314 0b1000_1000, 0b1001_1001, 0b1010_1010, 0b1011_1011, 0b1100_1100, 0b1101_1101, 0b1110_1110, 0b1111_1111];
315
316 void expandUniform(ubyte[] buffer, ubyte itemBits, ulong uniformData)
317 {
318 switch(itemBits) {
319 case 1:
320 ubyte byteFiller = bitsTable1[uniformData & 0b0001];
321 buffer[] = byteFiller;
322 break;
323 case 2:
324 ubyte byteFiller = bitsTable2[uniformData & 0b0011];
325 buffer[] = byteFiller;
326 break;
327 case 4:
328 ubyte byteFiller = bitsTable4[uniformData & 0b1111];
329 buffer[] = byteFiller;
330 break;
331 case 8:
332 ubyte byteFiller = uniformData & ubyte.max;
333 buffer[] = byteFiller;
334 break;
335 case 16:
336 ushort ushortFiller = uniformData & ushort.max;
337 (cast(ushort[])buffer)[] = ushortFiller;
338 break;
339 case 32:
340 uint uintFiller = uniformData & uint.max;
341 (cast(uint[])buffer)[] = uintFiller;
342 break;
343 case 64:
344 ulong ulongFiller = uniformData & ulong.max;
345 (cast(ulong[])buffer)[] = ulongFiller;
346 break;
347 default:
348 assert(false, "Invalid itemBits");
349 }
350 }
351
352 /// Container for chunk updates
353 /// If blockChanges is null uses newBlockData
354 struct ChunkChange
355 {
356 uvec3 a, b; // box
357 BlockId blockId;
358 BlockMetadata blockMeta;
359 }
360
361 // container of single block change.
362 // position is chunk local [0; CHUNK_SIZE-1];
363 struct BlockChange
364 {
365 ushort index;
366 BlockId blockId;
367 BlockMetadata blockMeta;
368 }
369
370 ushort[2] areaOfImpact(BlockChange[] changes)
371 {
372 ushort start;
373 ushort end;
374
375 foreach(change; changes)
376 {
377 if (change.index < start)
378 start = change.index;
379 if (change.index > end)
380 end = change.index;
381 }
382
383 return cast(ushort[2])[start, end+1];
384 }
385
386 // stores all used snapshots of the chunk.
387 struct BlockDataSnapshot
388 {
389 ChunkLayerData blockData;
390 TimestampType timestamp;
391 uint numUsers;
392 }
393
394 /// Stores layer of chunk data. Blocks are stored as array of blocks or uniform.
395 struct ChunkLayerSnap
396 {
397 union {
398 ulong uniformData;
399 void* dataPtr; /// Stores ptr to the first byte of data. The length of data is in dataLength.
400 }
401 LayerDataLenType dataLength; // unused when uniform
402 uint timestamp;
403 ushort numUsers;
404 ushort metadata;
405 StorageType type;
406 this(LayerDataLenType _dataLength, uint _timestamp, ulong _uniformData, ushort _metadata = 0) {
407 type = StorageType.uniform; dataLength = _dataLength; timestamp = _timestamp; uniformData = _uniformData; metadata = _metadata;
408 }
409 this(StorageType _type, LayerDataLenType _dataLength, uint _timestamp, void* _dataPtr, ushort _metadata = 0) {
410 type = _type; dataLength = _dataLength; timestamp = _timestamp; dataPtr = _dataPtr; metadata = _metadata;
411 }
412 this(T)(StorageType _type, uint _timestamp, T[] _array, ushort _metadata = 0) {
413 ubyte[] data = cast(ubyte[])_array;
414 type = _type; dataLength = cast(LayerDataLenType)data.length; timestamp = _timestamp; dataPtr = data.ptr; metadata = _metadata;
415 }
416 this(ChunkLayerItem l) {
417 numUsers = 0;
418 timestamp = l.timestamp;
419 type = l.type;
420 dataLength = l.dataLength;
421 uniformData = l.uniformData;
422 metadata = l.metadata;
423 }
424 }
425
426 enum isSomeLayer(Layer) = is(Layer == ChunkLayerSnap) || is(Layer == ChunkLayerItem) || is(Layer == Nullable!ChunkLayerSnap);
427
428 T[] getArray(T, Layer)(const ref Layer layer)
429 if (isSomeLayer!Layer)
430 {
431 assert(layer.type != StorageType.uniform);
432 return cast(T[])(layer.dataPtr[0..layer.dataLength]);
433 }
434 T getUniform(T, Layer)(const ref Layer layer)
435 if (isSomeLayer!Layer)
436 {
437 assert(layer.type == StorageType.uniform);
438 return cast(T)layer.uniformData;
439 }
440
441 BlockId getBlockId(Layer)(const ref Layer layer, BlockChunkIndex index)
442 if (isSomeLayer!Layer)
443 {
444 if (layer.type == StorageType.fullArray) return (cast(BlockId*)layer.dataPtr)[index];
445 if (layer.type == StorageType.uniform) return cast(BlockId)layer.uniformData;
446
447 BlockId[CHUNK_SIZE_CUBE] buffer;
448 decompressLayerData(layer, cast(ubyte[])buffer[]);
449 return buffer[index];
450 }
451
452 BlockId getBlockId(Layer)(const ref Layer layer, int x, int y, int z)
453 if (isSomeLayer!Layer)
454 {
455 return getBlockId(layer, BlockChunkIndex(x, y, z));
456 }
457
458 T getLayerItemNoncompressed(T, Layer)(const ref Layer layer, BlockChunkIndex index)
459 if (isSomeLayer!Layer)
460 {
461 if (layer.type == StorageType.fullArray) return (cast(T*)layer.dataPtr)[index];
462 if (layer.type == StorageType.uniform) return cast(T)layer.uniformData;
463 assert(false);
464 }
465
466 bool isUniform(Layer)(const ref Layer layer) @property
467 if (isSomeLayer!Layer)
468 {
469 return layer.type == StorageType.uniform;
470 }
471
472 ChunkLayerData toBlockData(Layer)(const ref Layer layer, ubyte layerId)
473 if (isSomeLayer!Layer)
474 {
475 ChunkLayerData res;
476 res.uniform = layer.type == StorageType.uniform;
477 res.metadata = layer.metadata;
478 res.layerId = layerId;
479 if (!res.uniform) {
480 res.blocks = layer.getArray!ubyte();
481 } else {
482 res.uniformType = layer.uniformData;
483 res.dataLength = layer.dataLength;
484 }
485 return res;
486 }
487
488 ChunkLayerItem fromBlockData(const ref ChunkLayerData bd)
489 {
490 if (bd.uniform)
491 return ChunkLayerItem(bd.layerId, bd.dataLength, 0, bd.uniformType, bd.metadata);
492 else
493 return ChunkLayerItem(StorageType.compressedArray, bd.layerId, 0, bd.blocks, bd.metadata);
494 }
495
496 void copyToBuffer(Layer)(Layer layer, BlockId[] outBuffer)
497 if (isSomeLayer!Layer)
498 {
499 assert(outBuffer.length == CHUNK_SIZE_CUBE);
500 if (layer.type == StorageType.uniform)
501 outBuffer[] = cast(BlockId)layer.uniformData;
502 else if (layer.type == StorageType.fullArray)
503 outBuffer[] = layer.getArray!BlockId;
504 else if (layer.type == StorageType.compressedArray)
505 decompressLayerData(layer, outBuffer);
506 }
507
508 size_t getLayerDataBytes(Layer)(const ref Layer layer)
509 if (isSomeLayer!Layer)
510 {
511 if (layer.type == StorageType.uniform)
512 return 0;
513 else
514 return layer.dataLength;
515 }
516
517 void applyChanges(WriteBuffer* writeBuffer, BlockChange[] changes)
518 {
519 assert(!writeBuffer.isUniform);
520 assert(writeBuffer.layer.dataLength == BLOCKS_DATA_LENGTH);
521 BlockId[] blocks = writeBuffer.layer.getArray!BlockId;
522 foreach(change; changes)
523 {
524 blocks[change.index] = change.blockId;
525 }
526 }
527
528 void applyChanges(WriteBuffer* writeBuffer, ChunkChange[] changes)
529 {
530 assert(!writeBuffer.isUniform);
531 assert(writeBuffer.layer.dataLength == BLOCKS_DATA_LENGTH);
532 BlockId[] blocks = writeBuffer.layer.getArray!BlockId;
533 foreach(change; changes)
534 {
535 setSubArray3d(blocks, CHUNK_SIZE_VECTOR, Box(ivec3(change.a), ivec3(change.b)), change.blockId);
536 }
537 }
538
539 ubyte[] compressLayerData(ubyte[] data, ubyte[] buffer)
540 {
541 size_t size = encodeCbor(buffer[], data.length);
542 size += compress(data, buffer[size..$]).length;
543 return buffer[0..size];
544 }
545
546 ubyte[] decompressLayerData(Layer)(const Layer layer, ubyte[] outBuffer) if (isSomeLayer!Layer)
547 {
548 assert(layer.type == StorageType.compressedArray);
549 return decompressLayerData(layer.getArray!ubyte, outBuffer);
550 }
551
552 ubyte[] decompressLayerData(const ubyte[] _compressedData)
553 {
554 ubyte[] compressedData = cast(ubyte[])_compressedData;
555 auto dataSize = uncompressedDataLength(compressedData);
556 ubyte[] buffer = allocLayerArray(dataSize);
557 ubyte[] decompressedData = decompress(compressedData, buffer);
558 //assert(buffer.length == decompressedData.length, "data size and length dont match");
559 return decompressedData;
560 }
561
562 // pops and returns size of uncompressed data. Modifies provided array.
563 size_t uncompressedDataLength()(auto ref ubyte[] compressedData)
564 {
565 return decodeCborSingle!size_t(compressedData);
566 }
567
568 ubyte[] decompressLayerData(const ubyte[] _compressedData, ubyte[] outBuffer)
569 {
570 ubyte[] compressedData = cast(ubyte[])_compressedData;
571 auto dataSize = decodeCborSingle!size_t(compressedData);
572 //assert(outBuffer.length == dataSize, format("%s != %s", outBuffer.length, dataSize));
573 ubyte[] decompressedData = decompress(compressedData, outBuffer);
574 //assert(outBuffer.length == decompressedData.length, "data size and length dont match");
575 return decompressedData;
576 }
577
578 // Stores blocks of the chunk.
579 // Still used because ChunkLayerSnap needs custom (de)serizalizer.
580 struct ChunkLayerData
581 {
582 void validate()
583 {
584 if (layerId == 0 && !uniform && blocks.length != BLOCKS_DATA_LENGTH) {
585 fatalf("Size of uniform chunk != CHUNK_SIZE_CUBE, == %s", blocks.length);
586 }
587 }
588
589 /// null if uniform is true, or contains chunk data otherwise
590 ubyte[] blocks;
591
592 /// type of common block
593 ulong uniformType = 0; // Unknown block
594
595 /// is chunk filled with block of the same type
596 bool uniform = true;
597 uint dataLength;
598
599 ushort metadata;
600 ubyte layerId;
601 }