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