PyAPI: Iniital gawain API for Python
[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_2fv(Gwn_Batch* batch, const char* name, const float data[2])
231         {
232         GET_UNIFORM
233         glUniform2fv(uniform->location, 1, data);
234         }
235
236 void GWN_batch_uniform_3fv(Gwn_Batch* batch, const char* name, const float data[3])
237         {
238         GET_UNIFORM
239         glUniform3fv(uniform->location, 1, data);
240         }
241
242 void GWN_batch_uniform_4fv(Gwn_Batch* batch, const char* name, const float data[4])
243         {
244         GET_UNIFORM
245         glUniform4fv(uniform->location, 1, data);
246         }
247
248 static void Batch_prime(Gwn_Batch* batch)
249         {
250         batch->vao_id = GWN_vao_alloc();
251         glBindVertexArray(batch->vao_id);
252
253         for (int v = 0; v < GWN_BATCH_VBO_MAX_LEN; ++v)
254                 {
255                 if (batch->verts[v] == NULL)
256                         break;
257                 GWN_vertbuf_use(batch->verts[v]);
258                 }
259
260         if (batch->elem)
261                 GWN_indexbuf_use(batch->elem);
262
263         // vertex attribs and element list remain bound to this VAO
264         }
265
266 void GWN_batch_draw(Gwn_Batch* batch)
267         {
268 #if TRUST_NO_ONE
269         assert(batch->phase == GWN_BATCH_READY_TO_DRAW);
270         assert(glIsProgram(batch->program));
271 #endif
272
273         if (batch->vao_id)
274                 glBindVertexArray(batch->vao_id);
275         else
276                 Batch_prime(batch);
277
278         if (batch->program_dirty)
279                 Batch_update_program_bindings(batch);
280
281         GWN_batch_program_use_begin(batch);
282
283         gpuBindMatrices(batch->interface);
284
285         if (batch->elem)
286                 {
287                 const Gwn_IndexBuf* el = batch->elem;
288
289 #if GWN_TRACK_INDEX_RANGE
290                 if (el->base_index)
291                         glDrawRangeElementsBaseVertex(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0, el->base_index);
292                 else
293                         glDrawRangeElements(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0);
294 #else
295                 glDrawElements(batch->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0);
296 #endif
297                 }
298         else
299                 glDrawArrays(batch->gl_prim_type, 0, batch->verts[0]->vertex_ct);
300
301         GWN_batch_program_use_end(batch);
302         glBindVertexArray(0);
303         }
304
305
306
307 // clement : temp stuff
308 void GWN_batch_draw_stupid(Gwn_Batch* batch)
309         {
310         if (batch->vao_id)
311                 glBindVertexArray(batch->vao_id);
312         else
313                 Batch_prime(batch);
314
315         if (batch->program_dirty)
316                 Batch_update_program_bindings(batch);
317
318         // GWN_batch_program_use_begin(batch);
319
320         //gpuBindMatrices(batch->program);
321
322         if (batch->elem)
323                 {
324                 const Gwn_IndexBuf* el = batch->elem;
325
326 #if GWN_TRACK_INDEX_RANGE
327                 if (el->base_index)
328                         glDrawRangeElementsBaseVertex(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0, el->base_index);
329                 else
330                         glDrawRangeElements(batch->gl_prim_type, el->min_index, el->max_index, el->index_ct, el->gl_index_type, 0);
331 #else
332                 glDrawElements(batch->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0);
333 #endif
334                 }
335         else
336                 glDrawArrays(batch->gl_prim_type, 0, batch->verts[0]->vertex_ct);
337
338         // GWN_batch_program_use_end(batch);
339         glBindVertexArray(0);
340         }
341
342 // clement : temp stuff
343 void GWN_batch_draw_stupid_instanced(Gwn_Batch* batch, unsigned int instance_vbo, int instance_count,
344                                  int attrib_nbr, int attrib_stride, int attrib_size[16], int attrib_loc[16])
345         {
346         if (batch->vao_id)
347                 glBindVertexArray(batch->vao_id);
348         else
349                 Batch_prime(batch);
350
351         if (batch->program_dirty)
352                 Batch_update_program_bindings(batch);
353
354         glBindBuffer(GL_ARRAY_BUFFER, instance_vbo);
355         int ptr_ofs = 0;
356         for (int i = 0; i < attrib_nbr; ++i)
357                 {
358                 int size = attrib_size[i];
359                 int loc = attrib_loc[i];
360                 int atr_ofs = 0;
361
362                 while (size > 0)
363                         {
364                         glEnableVertexAttribArray(loc + atr_ofs);
365                         glVertexAttribPointer(loc + atr_ofs, (size > 4) ? 4 : size, GL_FLOAT, GL_FALSE,
366                                               sizeof(float) * attrib_stride, (GLvoid*)(sizeof(float) * ptr_ofs));
367                         glVertexAttribDivisor(loc + atr_ofs, 1);
368                         atr_ofs++;
369                         ptr_ofs += (size > 4) ? 4 : size;
370                         size -= 4;
371                         }
372                 }
373         glBindBuffer(GL_ARRAY_BUFFER, 0);
374
375         // GWN_batch_program_use_begin(batch);
376
377         //gpuBindMatrices(batch->program);
378
379         if (batch->elem)
380                 {
381                 const Gwn_IndexBuf* el = batch->elem;
382 #if GWN_TRACK_INDEX_RANGE
383                 glDrawElementsInstancedBaseVertex(batch->gl_prim_type, el->index_ct, el->gl_index_type, 0, instance_count, el->base_index);
384 #else
385                 glDrawElementsInstanced(batch->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0, instance_count);
386 #endif
387                 }
388         else
389                 glDrawArraysInstanced(batch->gl_prim_type, 0, batch->verts[0]->vertex_ct, instance_count);
390
391         // GWN_batch_program_use_end(batch);
392         glBindVertexArray(0);
393         }
394
395 void GWN_batch_draw_stupid_instanced_with_batch(Gwn_Batch* batch_instanced, Gwn_Batch* batch_instancing)
396         {
397         if (batch_instanced->vao_id)
398                 glBindVertexArray(batch_instanced->vao_id);
399         else
400                 Batch_prime(batch_instanced);
401
402         if (batch_instanced->program_dirty)
403                 Batch_update_program_bindings(batch_instanced);
404
405         Gwn_VertBuf* verts = batch_instancing->verts[0];
406
407         const Gwn_VertFormat* format = &verts->format;
408
409         const unsigned attrib_ct = format->attrib_ct;
410         const unsigned stride = format->stride;
411
412         GWN_vertbuf_use(verts);
413
414         for (unsigned a_idx = 0; a_idx < attrib_ct; ++a_idx)
415                 {
416                 const Gwn_VertAttr* a = format->attribs + a_idx;
417
418                 const GLvoid* pointer = (const GLubyte*)0 + a->offset;
419
420                 for (unsigned n_idx = 0; n_idx < a->name_ct; ++n_idx)
421                         {
422                         const Gwn_ShaderInput* input = GWN_shaderinterface_attr(batch_instanced->interface, a->name[n_idx]);
423
424                         if (input == NULL) continue;
425
426                         glEnableVertexAttribArray(input->location);
427                         glVertexAttribDivisor(input->location, 1);
428
429                         switch (a->fetch_mode)
430                                 {
431                                 case GWN_FETCH_FLOAT:
432                                 case GWN_FETCH_INT_TO_FLOAT:
433                                         glVertexAttribPointer(input->location, a->comp_ct, a->gl_comp_type, GL_FALSE, stride, pointer);
434                                         break;
435                                 case GWN_FETCH_INT_TO_FLOAT_UNIT:
436                                         glVertexAttribPointer(input->location, a->comp_ct, a->gl_comp_type, GL_TRUE, stride, pointer);
437                                         break;
438                                 case GWN_FETCH_INT:
439                                         glVertexAttribIPointer(input->location, a->comp_ct, a->gl_comp_type, stride, pointer);
440                                 }
441                         }
442                 }
443
444         // GWN_batch_program_use_begin(batch);
445
446         //gpuBindMatrices(batch->program);
447
448         if (batch_instanced->elem)
449                 {
450                 const Gwn_IndexBuf* el = batch_instanced->elem;
451
452 #if GWN_TRACK_INDEX_RANGE
453                 glDrawElementsInstancedBaseVertex(batch_instanced->gl_prim_type, el->index_ct, el->gl_index_type, 0, verts->vertex_ct, el->base_index);
454 #else
455                 glDrawElementsInstanced(batch_instanced->gl_prim_type, el->index_ct, GL_UNSIGNED_INT, 0, verts->vertex_ct);
456 #endif
457                 }
458         else
459                 glDrawArraysInstanced(batch_instanced->gl_prim_type, 0, batch_instanced->verts[0]->vertex_ct, verts->vertex_ct);
460
461         // GWN_batch_program_use_end(batch);
462         glBindVertexArray(0);
463         }