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 }