1 module Engine.Batch;
2 
3 import std.stdio;
4 import derelict.opengl3.gl;
5 import Engine.Buffer;
6 import gl3n.linalg;
7 import Engine.CStorage;
8 import Engine.Texture;
9 import Engine.Material;
10 import Engine.Core;
11 import engine = Engine.Entity;
12 import Engine.Transform;
13 import std.parallelism;
14 import std.bitmanip;
15 import Engine.Allocator;
16 import std.datetime;
17 import Engine.Component;
18 
19 struct BatchData {
20 	enum Type {
21 		Transform,
22 		UV,
23 		Color,
24 		Vertex,
25 		Index,
26 		Size,
27 	}
28 
29 	this(Batch batch, Transform transform, Batchable batchable,int vertexIndex,int indexIndex,int vertexCount,int indexCount,int totalVertexCount,int totalIndexCount) {
30 		this.batch = batch;
31 		this.transform = transform;
32 		this.batchable = batchable;
33 		this.vertexIndex = vertexIndex;
34 		this.indexIndex = indexIndex;
35 		this.vertexCount = vertexCount;
36 		this.indexCount = indexCount;
37 		this.totalIndexCount = totalIndexCount;
38 		this.totalVertexCount = totalVertexCount;
39 		updateTransform = true;
40 		updateUV = true;
41 		updateColor = true;
42 		updateVertex = true;
43 		updateIndex = true;
44 	}
45 
46 	Batch batch;
47 	Transform transform;
48 	Batchable batchable;
49 	size_t batchIndex;
50 	int vertexIndex;
51 	int indexIndex;
52 	int vertexCount;
53 	int indexCount;
54 
55 	int totalVertexCount;
56 	int totalIndexCount;
57 
58 /*
59 	bool updateColor;
60 	bool updateTransform;
61 	bool updateUV;
62 	bool updateVertex;
63 	bool updateIndex;
64 	bool updateSize;
65 	*/
66 	package mixin(bitfields!(
67 			bool, "updateTransform",    1,
68 			bool, "updateUV",    1,
69 			bool, "updateColor",    1,
70 			bool, "updateVertex", 1,
71 			bool, "updateIndex", 1,
72 			bool, "updateSize", 1,
73 			int, "", 2));
74 	
75 	void MarkCheck(const Type[] types...) {
76 		foreach (t;types) {
77 			switch(t) {
78 				case Type.Transform:
79 					updateTransform = true;
80 					break;
81 				case Type.UV:
82 					updateUV = true;
83 					break;
84 				case Type.Color:
85 					updateColor = true;
86 					break;
87 				case Type.Vertex:
88 					updateVertex = true;
89 					break;
90 				case Type.Index:
91 					updateIndex = true;
92 					break;
93 				case Type.Size:
94 					if (updateSize)
95 						return;
96 					batch.CheckBatches ~= &this;
97 					this.updateSize = true;
98 					break;
99 				default:
100 					break;
101 			}	
102 		}
103 	}
104 
105 	final void Update()(vec3[] vertex,vec2[] uv, vec4[] color,uint[] index, uint indexPosition) {
106 		int call = 4;
107 		if (!updateUV) {
108 			uv = null;
109 			call--;
110 		}
111 		if (!updateColor) {
112 			color = null;
113 			call--;
114 		}
115 		if (!updateVertex) {
116 			vertex = null;
117 			call--;
118 		}
119 		if (!updateIndex) {
120 			index = null;
121 			call--;
122 		}
123 		if (call > 0) {	
124 			batchable.UpdateBatch(vertex,uv,color,index,indexPosition);
125 			updateUV = false;
126 			updateIndex = false;
127 			updateVertex = false;
128 			updateColor = false;
129 		}
130 	}
131 
132 	final void ForceUpdate()(vec3[] vertex,vec2[] uv, vec4[] color,uint[] index, uint indexPosition) {
133 		batchable.UpdateBatch(vertex,uv,color,index,indexPosition);
134 	}
135 }
136 
137 class Batch {
138 
139 	Buffer vertex;
140 	Buffer uv;
141 	Buffer color;
142 	Buffer index;
143 	Buffer matrix;
144 
145 	//ChunkAllocator!(BatchData, 100) batchAllocator;
146 
147 	BatchData*[] Batches;
148 	BatchData*[] CheckBatches;
149 	BatchData[] DeleteBatches;
150 	int totalIndecies;
151 	Material material;
152 
153 	int vertexIndex;
154 	int indexIndex;
155 	int vsize;
156 	int isize;
157 	int deletedIndecies = 0;
158 	bool resize;
159 
160 	this(int size,Material material) {
161 		Batches = new BatchData*[size];
162 		CheckBatches = new BatchData*[4];  
163 		DeleteBatches = new BatchData[4];
164 		DeleteBatches.length = 0;
165 		CheckBatches.length = 0;
166 		this.vsize = size*4;
167 		this.isize = size*6;
168 		Batches.length = 0;
169 		vertex = new Buffer(this.vsize*vec3.sizeof, GL_ARRAY_BUFFER,GL_STREAM_DRAW);
170 		color = new Buffer(this.vsize*vec4.sizeof, GL_ARRAY_BUFFER,GL_STREAM_DRAW);
171 		uv = new Buffer(this.vsize*vec2.sizeof,GL_ARRAY_BUFFER,GL_STREAM_DRAW);
172 		index  = new Buffer(this.isize*int.sizeof,GL_ELEMENT_ARRAY_BUFFER,GL_STREAM_DRAW);
173 		matrix = new Buffer(this.vsize*mat4.sizeof,GL_ARRAY_BUFFER,GL_STREAM_DRAW);
174 		this.material = material;
175 	}
176 
177 
178 	void Rebuild() {
179 		vertexIndex = 0;
180 		indexIndex = 0;
181 		foreach (batch; Batches) {
182 			batch.vertexIndex = vertexIndex;
183 			batch.indexIndex = indexIndex;
184 			vertexIndex += batch.totalVertexCount;
185 			indexIndex += batch.totalIndexCount;
186 		}
187 	}	
188 
189 	void Add(engine.Entity entity, Batchable batch) {
190 		if (vertexIndex + batch.vertecies > vsize || 
191 			indexIndex + batch.indecies > isize ) {
192 			vsize = 2 * (vertexIndex + batch.vertecies);
193 			isize = 2 * (indexIndex + batch.indecies);
194 			resize = true;
195 		}		
196 		auto b = new BatchData(this,entity.transform,batch,vertexIndex,indexIndex,batch.vertecies,batch.indecies,batch.vertecies,batch.indecies);
197 		entity.AddComponent(b);
198 		Batches ~= b;
199 		b.batchIndex = Batches.length-1;
200 		vertexIndex += batch.vertecies;
201 		indexIndex += batch.indecies;
202 		batch.OnBatchSetup(b);
203 	}
204 
205 	void Remove(engine.Entity entity, Batchable batch) {
206 		foreach(c;entity.components) {
207 			auto b = c.Cast!BatchData();
208 			if (b !is null && b.batchable == batch) {
209 				DeleteBatches ~= *b;
210 				auto bd = Batches[Batches.length-1];
211 				Batches[b.batchIndex] = bd;
212 				Batches.length--;
213 				bd.batchIndex = b.batchIndex;
214 				break;
215 			}
216 		}
217 	}
218 
219 	void Resize(BatchData* batch) {
220 		if (vertexIndex + batch.totalVertexCount > vsize || 
221 			indexIndex + batch.totalIndexCount > isize ) {	
222 			vsize = 2 * (vertexIndex + batch.totalVertexCount);
223 			isize = 2 * (indexIndex + batch.totalIndexCount);
224 			resize = true;
225 		} else {
226 			batch.vertexIndex = vertexIndex;
227 			batch.indexIndex = indexIndex;
228 			vertexIndex += batch.totalVertexCount;
229 			indexIndex += batch.totalIndexCount;
230 		}
231 	}
232 
233 	void Update() {
234 		 //Checking for resizing/deactiving/etc
235 		 for (int i = 0;i<CheckBatches.length;i++) {
236 			 auto b = CheckBatches[i];
237 			 b.updateSize = false;
238 
239 			 b.updateUV = true;
240 			 b.updateColor = true;
241 			 b.updateIndex = true;
242 			 b.updateVertex = true;
243 
244 			 b.vertexCount = b.batchable.vertecies;
245 			 b.indexCount = b.batchable.indecies;
246 			 auto tic = b.totalIndexCount;
247 		  	 auto ii = b.indexIndex;
248 		  	 //Increase vertex size if needed
249 			 if (b.vertexCount > b.totalVertexCount || b.indexCount > b.totalIndexCount) {
250 				 b.totalVertexCount = b.vertexCount * 2;
251 				 b.totalIndexCount = b.indexCount * 2;
252 				 Resize(b);
253 			 }	
254 			 //Delete batch data if needed
255 			 if (!resize ) {
256 				DeleteBatches ~= BatchData(null,null,null,0,ii,0,0,0,tic);
257 			 }
258 		 }			
259 		 CheckBatches.length = 0;
260 	
261 		 //Resizing & rebuilding
262 		 bool updateAll = false;
263 		 if (resize) {
264 		 	 DeleteBatches.length = 0;
265 			 vertex.Resize(vsize*vec3.sizeof);
266 			 color.Resize(vsize*vec4.sizeof);
267 			 uv.Resize(vsize*vec2.sizeof);
268 			 index.Resize(isize*int.sizeof);
269 			 matrix.Resize(vsize*mat4.sizeof);
270 			 resize = false;
271 			 updateAll = true;
272 			 Rebuild();
273 		 }
274 		
275 		
276 		 StopWatch t1;
277 		 StopWatch t2;
278 		 t2.start();
279 		 t1.start();
280 		 auto varr = vertex.Map!vec3(0, vertexIndex*vec3.sizeof);
281 		 auto carr = color.Map!vec4(0, vertexIndex*vec4.sizeof);
282 		 auto uvarr = uv.Map!vec2(0, vertexIndex*vec2.sizeof);
283 		 auto iarr = index.Map!uint(0, indexIndex*uint.sizeof);
284 		 auto matrcies = matrix.Map!(vec3[3])(0, vertexIndex*(vec3[3]).sizeof);
285 		 t1.stop();
286 		// writeln("buffer map", cast(double)t1.peek().nsecs / 1000000);
287 	
288 		 uint vindex = 0;
289 		 totalIndecies = 0;
290 		 scope(exit) {
291 			 t1.start();
292 			 vertex.Unmap();
293 			 color.Unmap();
294 			 uv.Unmap();
295 			 index.Unmap();
296 			 matrix.Unmap();
297 			 t1.stop();
298 			 t2.stop();
299 			// writeln("buffer unmap", cast(double)t1.peek().nsecs / 1000000);
300 			// writeln("batch update", cast(double)t2.peek().nsecs / 1000000);
301 		 }
302 
303 		//deactiving batches		
304 		for (int i = 0;i<DeleteBatches.length;i++) {
305 			auto e = &DeleteBatches[i];
306 			iarr[e.indexIndex..e.indexIndex+e.totalIndexCount] = 0;
307 		}	
308 		DeleteBatches.length = 0;
309 
310 
311 		//Multithreaded batch updates.
312 		auto ti = taskPool.workerLocalStorage(0);
313 		if (updateAll) {
314 			foreach( e; parallel(Batches)) {
315 				auto indx = e.vertexIndex;
316 				auto max = indx+e.vertexCount;	
317 				e.ForceUpdate(varr[indx..max],uvarr[indx..max],carr[indx..max],iarr[e.indexIndex..e.indexIndex+e.indexCount],indx);
318 				
319 				//if (e.updateTransform || 1 == 1) {
320 				auto t = e.transform;
321 				auto p = t.position;
322 				auto r = t.rotation;
323 				auto s = t.scale;
324 				
325 				for (;indx<max;indx++) {
326 					matrcies[indx][0] = p;
327 					matrcies[indx][1] = r;
328 					matrcies[indx][2] = s;
329 				}
330 			}
331 		} else {	
332 			foreach( e; parallel(Batches)) {
333 				
334 				auto indx = e.vertexIndex;
335 				auto max = indx+e.vertexCount;	
336 				e.Update(varr[indx..max],uvarr[indx..max],carr[indx..max],iarr[e.indexIndex..e.indexIndex+e.indexCount],indx);
337 
338 				//if (e.updateTransform || 1 == 1) {
339 				auto t = e.transform;
340 				auto p = t.position;
341 				auto r = t.rotation;
342 				auto s = t.scale;
343 				
344 				for (;indx<max;indx++) {
345 					matrcies[indx][0] = p;
346 					matrcies[indx][1] = r;
347 					matrcies[indx][2] = s;
348 				}
349 			}
350 		}
351 
352 
353 		totalIndecies = indexIndex;
354 
355 
356 		/*
357 		foreach(ref e; Core.entities) {
358 			if (e.sprite !is null && e.sprite.texture.id == texture.id) {
359 				int vertecies,indecies;
360 				e.sprite.Sizes(vertecies,indecies);
361 				e.sprite.UpdateBatch(varr,uvarr,carr,iarr,vindex);
362 				vindex += vertecies;
363 				totalIndecies += indecies;
364 				varr = varr[vertecies..$];
365 				carr = carr[vertecies..$];
366 				uvarr = uvarr[vertecies..$];
367 				iarr = iarr[indecies..$];
368 
369 				auto t = e.Transform;
370 				auto p = t.Position;
371 				auto r = t.Rotation;
372 				auto s = t.Scale;
373 				
374 				for (int i=0;i<vertecies;i++) {
375 					matrcies[i] = [p,r,s];
376 				}
377 				matrcies = matrcies[vertecies..$];
378 			}
379 		}
380 
381 		auto mat = this.Transform.Matrix();
382 		vertex[0] = (mat*vec4(-0.5f, -0.5f, 0.0f,1)).xyz;
383 		vertex[1] = (mat*vec4(0.5f,  -0.5f, 0.0f,1)).xyz;
384 		vertex[2] = (mat*vec4(0.5f,  0.5f, 0.0f,1)).xyz;
385 		vertex[3] = (mat*vec4(-0.5f,  0.5f, 0.0f,1)).xyz;
386 		*/
387 	}
388 
389 	void Draw() {
390 		StopWatch t1;
391 		t1.start();
392 		material.render(this);
393 		t1.stop();
394 		//writeln("batch Draw", cast(double)t1.peek().nsecs / 1000000);
395 	}
396 }
397 
398 interface Batchable {
399 	@property Material material();
400 	@property int vertecies();
401 	@property int indecies();
402 	
403 	void OnBatchSetup( BatchData* data);
404 	void UpdateBatch(vec3[] vertex, vec2[] uv, vec4[] color, uint[] index, uint indexPosition);
405 }