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