c95b67e39cf448e5735fe18f173d0abdfd34f92f
[blender.git] / intern / gawain / src / gwn_batch.c
1
2 // Gawain geometry batch
3 //
4 // This code is part of the Gawain library, with modifications
5 // specific to integration with Blender.
6 //
7 // Copyright 2016 Mike Erwin
8 //
9 // This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of
10 // the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0/.
11
12 #include "gwn_batch.h"
13 #include "gwn_buffer_id.h"
14 #include "gwn_primitive_private.h"
15 #include <stdlib.h>
16
17 // necessary functions from matrix API
18 extern void gpuBindMatrices(const Gwn_ShaderInterface* shaderface);
19 extern bool gpuMatricesDirty(void); // how best to use this here?
20
21 Gwn_Batch* GWN_batch_create_ex(
22         Gwn_PrimType prim_type, Gwn_VertBuf* verts, Gwn_IndexBuf* elem,
23         unsigned owns_flag)
24         {
25         Gwn_Batch* batch = calloc(1, sizeof(Gwn_Batch));
26
27         GWN_batch_init_ex(batch, prim_type, verts, elem, owns_flag);
28
29         return batch;
30         }
31
32 void GWN_batch_init_ex(
33         Gwn_Batch* batch, Gwn_PrimType prim_type, Gwn_VertBuf* verts, Gwn_IndexBuf* elem,
34         unsigned owns_flag)
35         {
36 #if TRUST_NO_ONE
37         assert(verts != NULL);
38 #endif
39
40         batch->verts[0] = verts;
41         for (int v = 1; v < GWN_BATCH_VBO_MAX_LEN; ++v)
42                 batch->verts[v] = NULL;
43         batch->elem = elem;
44         batch->prim_type = prim_type;
45         batch->gl_prim_type = convert_prim_type_to_gl(prim_type);
46         batch->phase = GWN_BATCH_READY_TO_DRAW;
47         batch->owns_flag = owns_flag;
48         }
49
50 void GWN_batch_discard(Gwn_Batch* batch)
51         {
52         if (batch->owns_flag & GWN_BATCH_OWNS_INDEX)
53                 GWN_indexbuf_discard(batch->elem);
54
55         if ((batch->owns_flag & ~GWN_BATCH_OWNS_INDEX) != 0)
56                 {
57                 for (int v = 0; v < GWN_BATCH_VBO_MAX_LEN; ++v)
58                         {
59                         if (batch->verts[v] == NULL)
60                                 break;
61                         if (batch->owns_flag & (1 << v))
62                                 GWN_vertbuf_discard(batch->verts[v]);
63                         }
64                 }
65
66         if (batch->vao_id)
67                 GWN_vao_free(batch->vao_id);
68
69         free(batch);
70         }
71
72 int GWN_batch_vertbuf_add_ex(
73         Gwn_Batch* batch, Gwn_VertBuf* verts,
74         bool own_vbo)
75         {
76         for (unsigned v = 0; v < GWN_BATCH_VBO_MAX_LEN; ++v)
77                 {
78                 if (batch->verts[v] == NULL)
79                         {
80 #if TRUST_NO_ONE
81                         // for now all VertexBuffers must have same vertex_ct
82                         assert(verts->vertex_ct == batch->verts[0]->vertex_ct);
83                         // in the near future we will enable instanced attribs which have their own vertex_ct
84 #endif
85                         batch->verts[v] = verts;
86                         // TODO: mark dirty so we can keep attrib bindings up-to-date
87                         if (own_vbo)
88                                 batch->owns_flag |= (1 << v);
89                         return v;
90                         }
91                 }
92         
93         // we only make it this far if there is no room for another Gwn_VertBuf
94 #if TRUST_NO_ONE
95         assert(false);
96 #endif
97         return -1;
98         }
99
100 void GWN_batch_program_set(Gwn_Batch* batch, GLuint program, const Gwn_ShaderInterface* shaderface)
101         {
102 #if TRUST_NO_ONE
103         assert(glIsProgram(program));
104 #endif
105
106         batch->program = program;
107         batch->interface = shaderface;
108         batch->program_dirty = true;
109
110         GWN_batch_program_use_begin(batch); // hack! to make Batch_Uniform* simpler
111         }
112
113 static void Batch_update_program_bindings(Gwn_Batch* batch)
114         {
115         // disable all as a precaution
116         // why are we not using prev_attrib_enabled_bits?? see immediate.c
117         for (unsigned a_idx = 0; a_idx < GWN_VERT_ATTR_MAX_LEN; ++a_idx)
118                 glDisableVertexAttribArray(a_idx);
119
120         for (int v = 0; v < GWN_BATCH_VBO_MAX_LEN; ++v)
121                 {
122                 Gwn_VertBuf* verts = batch->verts[v];
123                 if (verts == NULL)
124                         break;
125
126                 const Gwn_VertFormat* format = &verts->format;
127
128                 const unsigned attrib_ct = format->attrib_ct;
129                 const unsigned stride = format->stride;
130
131                 GWN_vertbuf_use(verts);
132
133                 for (unsigned a_idx = 0; a_idx < attrib_ct; ++a_idx)
134                         {
135                         const Gwn_VertAttr* a = format->attribs + a_idx;
136
137                         const GLvoid* pointer = (const GLubyte*)0 + a->offset;
138
139                         for (unsigned n_idx = 0; n_idx < a->name_ct; ++n_idx)
140                                 {
141                                 const Gwn_ShaderInput* input = GWN_shaderinterface_attr(batch->interface, a->name[n_idx]);
142
143                                 if (input == NULL) continue;
144
145                                 glEnableVertexAttribArray(input->location);
146
147                                 switch (a->fetch_mode)
148                                         {
149                                         case GWN_FETCH_FLOAT:
150                                         case GWN_FETCH_INT_TO_FLOAT:
151                                                 glVertexAttribPointer(input->location, a->comp_ct, a->gl_comp_type, GL_FALSE, stride, pointer);
152                                                 break;
153                                         case GWN_FETCH_INT_TO_FLOAT_UNIT:
154                                                 glVertexAttribPointer(input->location, a->comp_ct, a->gl_comp_type, GL_TRUE, stride, pointer);
155                                                 break;
156                                         case GWN_FETCH_INT:
157                                                 glVertexAttribIPointer(input->location, a->comp_ct, a->gl_comp_type, stride, pointer);
158                                         }
159                                 }
160                         }
161                 }
162
163         batch->program_dirty = false;
164         }
165
166 void GWN_batch_program_use_begin(Gwn_Batch* batch)
167         {
168         // NOTE: use_program & done_using_program are fragile, depend on staying in sync with
169         //       the GL context's active program. use_program doesn't mark other programs as "not used".
170         // TODO: make not fragile (somehow)
171
172         if (!batch->program_in_use)
173                 {
174                 glUseProgram(batch->program);
175                 batch->program_in_use = true;
176                 }
177         }
178
179 void GWN_batch_program_use_end(Gwn_Batch* batch)
180         {
181         if (batch->program_in_use)
182                 {
183                 glUseProgram(0);
184                 batch->program_in_use = false;
185                 }
186         }
187
188 #if TRUST_NO_ONE
189   #define GET_UNIFORM const Gwn_ShaderInput* uniform = GWN_shaderinterface_uniform(batch->interface, name); assert(uniform);
190 #else
191   #define GET_UNIFORM const Gwn_ShaderInput* uniform = GWN_shaderinterface_uniform(batch->interface, name);
192 #endif
193
194 void GWN_batch_uniform_1i(Gwn_Batch* batch, const char* name, int value)
195         {
196         GET_UNIFORM
197         glUniform1i(uniform->location, value);
198         }
199
200 void GWN_batch_uniform_1b(Gwn_Batch* batch, const char* name, bool value)
201         {
202         GET_UNIFORM
203         glUniform1i(uniform->location, value ? GL_TRUE : GL_FALSE);
204         }
205
206 void GWN_batch_uniform_2f(Gwn_Batch* batch, const char* name, float x, float y)
207         {
208         GET_UNIFORM
209         glUniform2f(uniform->location, x, y);
210         }
211
212 void GWN_batch_uniform_3f(Gwn_Batch* batch, const char* name, float x, float y, float z)
213         {
214         GET_UNIFORM
215         glUniform3f(uniform->location, x, y, z);
216         }
217
218 void GWN_batch_uniform_4f(Gwn_Batch* batch, const char* name, float x, float y, float z, float w)
219         {
220         GET_UNIFORM
221         glUniform4f(uniform->location, x, y, z, w);
222         }
223
224 void GWN_batch_uniform_1f(Gwn_Batch* batch, const char* name, float x)
225         {
226         GET_UNIFORM
227         glUniform1f(uniform->location, x);
228         }
229
230 void GWN_batch_uniform_3fv(Gwn_Batch* batch, const char* name, const float data[3])
231         {
232         GET_UNIFORM
233         glUniform3fv(uniform->location, 1, data);
234         }
235
236 void GWN_batch_uniform_4fv(Gwn_Batch* batch, const char* name, const float data[4])
237         {
238         GET_UNIFORM
239         glUniform4fv(uniform->location, 1, data);
240         }
241
242 static void Batch_prime(Gwn_Batch* batch)
243         {
244         batch->vao_id = GWN_vao_alloc();
245         glBindVertexArray(batch->vao_id);
246
247         for (int v = 0; v < GWN_BATCH_VBO_MAX_LEN; ++v)
248                 {
249                 if (batch->verts[v] == NULL)
250                         break;
251                 GWN_vertbuf_use(batch->verts[v]);
252                 }
253
254         if (batch->elem)
255                 GWN_indexbuf_use(batch->elem);
256
257         // vertex attribs and element list remain bound to this VAO
258         }
259
260 void GWN_batch_draw(Gwn_Batch* batch)
261         {
262 #if TRUST_NO_ONE
263         assert(batch->phase == GWN_BATCH_READY_TO_DRAW);
264         assert(glIsProgram(batch->program));
265 #endif
266
267         if (batch->vao_id)
268                 glBindVertexArray(batch->vao_id);
269         else
270                 Batch_prime(batch);
271
272         if (batch->program_dirty)
273                 Batch_update_program_bindings(batch);
274
275         GWN_batch_program_use_begin(batch);
276
277         gpuBindMatrices(batch->interface);
278
279         if (batch->elem)
280                 {
281                 const Gwn_IndexBuf* el = batch->elem;
282
283 #if GWN_TRACK_INDEX_RANGE
284                 if (el->base_index)
285                         glDrawRangeElementsBaseVertex(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0, el->base_index);
286                 else
287                         glDrawRangeElements(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0);
288 #else
289                 glDrawElements(batch->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0);
290 #endif
291                 }
292         else
293                 glDrawArrays(batch->gl_prim_type, 0, batch->verts[0]->vertex_ct);
294
295         GWN_batch_program_use_end(batch);
296         glBindVertexArray(0);
297         }
298
299
300
301 // clement : temp stuff
302 void GWN_batch_draw_stupid(Gwn_Batch* batch)
303         {
304         if (batch->vao_id)
305                 glBindVertexArray(batch->vao_id);
306         else
307                 Batch_prime(batch);
308
309         if (batch->program_dirty)
310                 Batch_update_program_bindings(batch);
311
312         // GWN_batch_program_use_begin(batch);
313
314         //gpuBindMatrices(batch->program);
315
316         if (batch->elem)
317                 {
318                 const Gwn_IndexBuf* el = batch->elem;
319
320 #if GWN_TRACK_INDEX_RANGE
321                 if (el->base_index)
322                         glDrawRangeElementsBaseVertex(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0, el->base_index);
323                 else
324                         glDrawRangeElements(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0);
325 #else
326                 glDrawElements(batch->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0);
327 #endif
328                 }
329         else
330                 glDrawArrays(batch->gl_prim_type, 0, batch->verts[0]->vertex_ct);
331
332         // GWN_batch_program_use_end(batch);
333         glBindVertexArray(0);
334         }
335
336 // clement : temp stuff
337 void GWN_batch_draw_stupid_instanced(Gwn_Batch* batch, unsigned int instance_vbo, int instance_count,
338                                  int attrib_nbr, int attrib_stride, int attrib_size[16], int attrib_loc[16])
339         {
340         if (batch->vao_id)
341                 glBindVertexArray(batch->vao_id);
342         else
343                 Batch_prime(batch);
344
345         if (batch->program_dirty)
346                 Batch_update_program_bindings(batch);
347
348         glBindBuffer(GL_ARRAY_BUFFER, instance_vbo);
349         int ptr_ofs = 0;
350         for (int i = 0; i < attrib_nbr; ++i)
351                 {
352                 int size = attrib_size[i];
353                 int loc = attrib_loc[i];
354                 int atr_ofs = 0;
355
356                 while (size > 0)
357                         {
358                         glEnableVertexAttribArray(loc + atr_ofs);
359                         glVertexAttribPointer(loc + atr_ofs, (size > 4) ? 4 : size, GL_FLOAT, GL_FALSE,
360                                               sizeof(float) * attrib_stride, (GLvoid*)(sizeof(float) * ptr_ofs));
361                         glVertexAttribDivisor(loc + atr_ofs, 1);
362                         atr_ofs++;
363                         ptr_ofs += (size > 4) ? 4 : size;
364                         size -= 4;
365                         }
366                 }
367         glBindBuffer(GL_ARRAY_BUFFER, 0);
368
369         // GWN_batch_program_use_begin(batch);
370
371         //gpuBindMatrices(batch->program);
372
373         if (batch->elem)
374                 {
375                 const Gwn_IndexBuf* el = batch->elem;
376 #if GWN_TRACK_INDEX_RANGE
377                 glDrawElementsInstancedBaseVertex(batch->gl_prim_type, el->index_ct, el->gl_index_type, 0, instance_count, el->base_index);
378 #else
379                 glDrawElementsInstanced(batch->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0, instance_count);
380 #endif
381                 }
382         else
383                 glDrawArraysInstanced(batch->gl_prim_type, 0, batch->verts[0]->vertex_ct, instance_count);
384
385         // GWN_batch_program_use_end(batch);
386         glBindVertexArray(0);
387         }
388
389 void GWN_batch_draw_stupid_instanced_with_batch(Gwn_Batch* batch_instanced, Gwn_Batch* batch_instancing)
390         {
391         if (batch_instanced->vao_id)
392                 glBindVertexArray(batch_instanced->vao_id);
393         else
394                 Batch_prime(batch_instanced);
395
396         if (batch_instanced->program_dirty)
397                 Batch_update_program_bindings(batch_instanced);
398
399         Gwn_VertBuf* verts = batch_instancing->verts[0];
400
401         const Gwn_VertFormat* format = &verts->format;
402
403         const unsigned attrib_ct = format->attrib_ct;
404         const unsigned stride = format->stride;
405
406         GWN_vertbuf_use(verts);
407
408         for (unsigned a_idx = 0; a_idx < attrib_ct; ++a_idx)
409                 {
410                 const Gwn_VertAttr* a = format->attribs + a_idx;
411
412                 const GLvoid* pointer = (const GLubyte*)0 + a->offset;
413
414                 for (unsigned n_idx = 0; n_idx < a->name_ct; ++n_idx)
415                         {
416                         const Gwn_ShaderInput* input = GWN_shaderinterface_attr(batch_instanced->interface, a->name[n_idx]);
417
418                         if (input == NULL) continue;
419
420                         glEnableVertexAttribArray(input->location);
421                         glVertexAttribDivisor(input->location, 1);
422
423                         switch (a->fetch_mode)
424                                 {
425                                 case GWN_FETCH_FLOAT:
426                                 case GWN_FETCH_INT_TO_FLOAT:
427                                         glVertexAttribPointer(input->location, a->comp_ct, a->gl_comp_type, GL_FALSE, stride, pointer);
428                                         break;
429                                 case GWN_FETCH_INT_TO_FLOAT_UNIT:
430                                         glVertexAttribPointer(input->location, a->comp_ct, a->gl_comp_type, GL_TRUE, stride, pointer);
431                                         break;
432                                 case GWN_FETCH_INT:
433                                         glVertexAttribIPointer(input->location, a->comp_ct, a->gl_comp_type, stride, pointer);
434                                 }
435                         }
436                 }
437
438         // GWN_batch_program_use_begin(batch);
439
440         //gpuBindMatrices(batch->program);
441
442         if (batch_instanced->elem)
443                 {
444                 const Gwn_IndexBuf* el = batch_instanced->elem;
445
446 #if GWN_TRACK_INDEX_RANGE
447                 glDrawElementsInstancedBaseVertex(batch_instanced->gl_prim_type, el->index_ct, el->gl_index_type, 0, verts->vertex_ct, el->base_index);
448 #else
449                 glDrawElementsInstanced(batch_instanced->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0, verts->vertex_ct);
450 #endif
451                 }
452         else
453                 glDrawArraysInstanced(batch_instanced->gl_prim_type, 0, batch_instanced->verts[0]->vertex_ct, verts->vertex_ct);
454
455         // GWN_batch_program_use_end(batch);
456         glBindVertexArray(0);
457         }