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 }