1 /**
2 Copyright: Copyright (c) 2013-2016 Andrey Penechko.
3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
4 Authors: Andrey Penechko.
5 */
6 module voxelman.world.gen.worker;
7 
8 import std.experimental.logger;
9 
10 import voxelman.block.utils;
11 import voxelman.core.config;
12 import voxelman.utils.worker;
13 import voxelman.world.storage.coordinates;
14 import voxelman.world.storage.chunk;
15 
16 import voxelman.world.gen.utils;
17 import voxelman.world.gen.generator;
18 
19 //version = DBG_OUT;
20 void chunkGenWorkerThread(shared(Worker)* workerInfo, BlockInfoTable blockInfos)
21 {
22 	import std.array : uninitializedArray;
23 
24 	ubyte[] compressBuffer = uninitializedArray!(ubyte[])(CHUNK_SIZE_CUBE*BlockId.sizeof);
25 	try
26 	{
27 		while (workerInfo.needsToRun)
28 		{
29 			workerInfo.waitForNotify();
30 
31 			if (!workerInfo.taskQueue.empty)
32 			{
33 				ulong _cwp = workerInfo.taskQueue.popItem!ulong();
34 				ChunkWorldPos cwp = ChunkWorldPos(_cwp);
35 				IGenerator generator = workerInfo.taskQueue.popItem!IGenerator();
36 				genChunk(cwp, &workerInfo.resultQueue,
37 					generator, blockInfos, compressBuffer);
38 			}
39 		}
40 	}
41 	catch(Throwable t)
42 	{
43 		import std.conv : to;
44 		infof("%s from gen worker", t.to!string);
45 		throw t;
46 	}
47 	version(DBG_OUT)infof("Gen worker stopped");
48 }
49 
50 //version = DBG_COMPR;
51 void genChunk(
52 	ChunkWorldPos cwp,
53 	shared(SharedQueue)* resultQueue,
54 	IGenerator generator,
55 	BlockInfoTable blockInfos,
56 	ubyte[] compressBuffer)
57 {
58 	BlockId[CHUNK_SIZE_CUBE] blocks;
59 	ChunkGeneratorResult chunk = generator.generateChunk(
60 		cwp.xyz, blocks);
61 
62 	enum layerId = FIRST_LAYER;
63 	enum timestamp = 0;
64 	enum numLayers = 1;
65 
66 	resultQueue.startMessage();
67 	auto header = ChunkHeaderItem(cwp, numLayers);
68 	resultQueue.pushMessagePart(header);
69 
70 	if(chunk.uniform)
71 	{
72 		ushort metadata = calcChunkFullMetadata(chunk.uniformBlockId, blockInfos);
73 		auto layer = ChunkLayerItem(StorageType.uniform, layerId,
74 			BLOCKID_UNIFORM_FILL_BITS, timestamp,
75 			chunk.uniformBlockId, metadata);
76 		resultQueue.pushMessagePart(layer);
77 	}
78 	else
79 	{
80 		ushort metadata = calcChunkFullMetadata(blocks, blockInfos);
81 
82 		ubyte[] compactBlocks = compressLayerData(cast(ubyte[])blocks, compressBuffer);
83 
84 		StorageType storageType;
85 		LayerDataLenType dataLength;
86 		ubyte* data;
87 
88 		if (compactBlocks.length <= LayerDataLenType.max)
89 		{
90 			version(DBG_COMPR)infof("Gen1 %s %s %s\n(%(%02x%))", cwp, compactBlocks.ptr, compactBlocks.length, cast(ubyte[])compactBlocks);
91 			compactBlocks = compactBlocks.dup;
92 			version(DBG_COMPR)infof("Gen2 %s %s %s\n(%(%02x%))", cwp, compactBlocks.ptr, compactBlocks.length, cast(ubyte[])compactBlocks);
93 			dataLength = cast(LayerDataLenType)compactBlocks.length;
94 			data = cast(ubyte*)compactBlocks.ptr;
95 			storageType = StorageType.compressedArray;
96 		}
97 		else
98 		{
99 			infof("Gen non-compressed %s", cwp);
100 			dataLength = cast(LayerDataLenType)blocks.length;
101 			assert(dataLength == CHUNK_SIZE_CUBE);
102 			data = cast(ubyte*)blocks.dup.ptr;
103 			storageType = StorageType.fullArray;
104 		}
105 
106 		// Add root to data.
107 		// Data can be collected by GC if no-one is referencing it.
108 		// It is needed to pass array trough shared queue.
109 		// Root is removed inside ChunkProvider
110 		import core.memory : GC;
111 		GC.addRoot(data); // TODO remove when moved to non-GC allocator
112 		auto layer = ChunkLayerItem(storageType, layerId, dataLength, timestamp, data, metadata);
113 		resultQueue.pushMessagePart(layer);
114 	}
115 	resultQueue.endMessage();
116 }