Cleanup: unused var, trailing space
[blender.git] / intern / gawain / src / gwn_immediate.c
1
2 // Gawain immediate mode work-alike
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_immediate.h"
13 #include "gwn_buffer_id.h"
14 #include "gwn_attr_binding.h"
15 #include "gwn_attr_binding_private.h"
16 #include "gwn_vertex_format_private.h"
17 #include "gwn_vertex_array_id.h"
18 #include "gwn_primitive_private.h"
19 #include <string.h>
20
21 // necessary functions from matrix API
22 extern void gpuBindMatrices(const Gwn_ShaderInterface*);
23 extern bool gpuMatricesDirty(void);
24
25 typedef struct {
26         // TODO: organize this struct by frequency of change (run-time)
27
28 #if IMM_BATCH_COMBO
29         Gwn_Batch* batch;
30 #endif
31         Gwn_Context* context;
32
33         // current draw call
34         GLubyte* buffer_data;
35         unsigned buffer_offset;
36         unsigned buffer_bytes_mapped;
37         unsigned vertex_ct;
38         bool strict_vertex_ct;
39         Gwn_PrimType prim_type;
40
41         Gwn_VertFormat vertex_format;
42
43         // current vertex
44         unsigned vertex_idx;
45         GLubyte* vertex_data;
46         uint16_t unassigned_attrib_bits; // which attributes of current vertex have not been given values?
47
48         GLuint vbo_id;
49         GLuint vao_id;
50
51         GLuint bound_program;
52         const Gwn_ShaderInterface* shader_interface;
53         Gwn_AttrBinding attrib_binding;
54         uint16_t prev_enabled_attrib_bits; // <-- only affects this VAO, so we're ok
55 } Immediate;
56
57 // size of internal buffer -- make this adjustable?
58 #define IMM_BUFFER_SIZE (4 * 1024 * 1024)
59
60 static bool initialized = false;
61 static Immediate imm;
62
63 void immInit(void)
64         {
65 #if TRUST_NO_ONE
66         assert(!initialized);
67 #endif
68
69         memset(&imm, 0, sizeof(Immediate));
70
71         imm.vbo_id = GWN_buf_id_alloc();
72         glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id);
73         glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW);
74
75         imm.prim_type = GWN_PRIM_NONE;
76         imm.strict_vertex_ct = true;
77
78         glBindBuffer(GL_ARRAY_BUFFER, 0);
79         initialized = true;
80
81         immActivate();
82         }
83
84 void immActivate(void)
85         {
86 #if TRUST_NO_ONE
87         assert(initialized);
88         assert(imm.prim_type == GWN_PRIM_NONE); // make sure we're not between a Begin/End pair
89         assert(imm.vao_id == 0);
90 #endif
91         imm.vao_id = GWN_vao_alloc();
92         imm.context = GWN_context_active_get();
93         }
94
95 void immDeactivate(void)
96         {
97 #if TRUST_NO_ONE
98         assert(initialized);
99         assert(imm.prim_type == GWN_PRIM_NONE); // make sure we're not between a Begin/End pair
100         assert(imm.vao_id != 0);
101 #endif
102         GWN_vao_free(imm.vao_id, imm.context);
103         imm.vao_id = 0;
104         imm.prev_enabled_attrib_bits = 0;
105         }
106
107 void immDestroy(void)
108         {
109         immDeactivate();
110         GWN_buf_id_free(imm.vbo_id);
111         initialized = false;
112         }
113
114 Gwn_VertFormat* immVertexFormat(void)
115         {
116         GWN_vertformat_clear(&imm.vertex_format);
117         return &imm.vertex_format;
118         }
119
120 void immBindProgram(GLuint program, const Gwn_ShaderInterface* shaderface)
121         {
122 #if TRUST_NO_ONE
123         assert(imm.bound_program == 0);
124         assert(glIsProgram(program));
125 #endif
126
127         imm.bound_program = program;
128         imm.shader_interface = shaderface;
129
130         if (!imm.vertex_format.packed)
131                 VertexFormat_pack(&imm.vertex_format);
132
133         glUseProgram(program);
134         get_attrib_locations(&imm.vertex_format, &imm.attrib_binding, shaderface);
135         gpuBindMatrices(shaderface);
136         }
137
138 void immUnbindProgram(void)
139         {
140 #if TRUST_NO_ONE
141         assert(imm.bound_program != 0);
142 #endif
143 #if PROGRAM_NO_OPTI
144         glUseProgram(0);
145 #endif
146         imm.bound_program = 0;
147         }
148
149 #if TRUST_NO_ONE
150 static bool vertex_count_makes_sense_for_primitive(unsigned vertex_ct, Gwn_PrimType prim_type)
151         {
152         // does vertex_ct make sense for this primitive type?
153         if (vertex_ct == 0)
154                 return false;
155
156         switch (prim_type)
157                 {
158                 case GWN_PRIM_POINTS:
159                         return true;
160                 case GWN_PRIM_LINES:
161                         return vertex_ct % 2 == 0;
162                 case GWN_PRIM_LINE_STRIP:
163                 case GWN_PRIM_LINE_LOOP:
164                         return vertex_ct >= 2;
165                 case GWN_PRIM_LINE_STRIP_ADJ:
166                         return vertex_ct >= 4;
167                 case GWN_PRIM_TRIS:
168                         return vertex_ct % 3 == 0;
169                 case GWN_PRIM_TRI_STRIP:
170                 case GWN_PRIM_TRI_FAN:
171                         return vertex_ct >= 3;
172                 default:
173                         return false;
174                 }
175         }
176 #endif
177
178 void immBegin(Gwn_PrimType prim_type, unsigned vertex_ct)
179         {
180 #if TRUST_NO_ONE
181         assert(initialized);
182         assert(imm.prim_type == GWN_PRIM_NONE); // make sure we haven't already begun
183         assert(vertex_count_makes_sense_for_primitive(vertex_ct, prim_type));
184 #endif
185
186         imm.prim_type = prim_type;
187         imm.vertex_ct = vertex_ct;
188         imm.vertex_idx = 0;
189         imm.unassigned_attrib_bits = imm.attrib_binding.enabled_bits;
190
191         // how many bytes do we need for this draw call?
192         const unsigned bytes_needed = vertex_buffer_size(&imm.vertex_format, vertex_ct);
193
194 #if TRUST_NO_ONE
195         assert(bytes_needed <= IMM_BUFFER_SIZE);
196 #endif
197
198         glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id);
199
200         // does the current buffer have enough room?
201         const unsigned available_bytes = IMM_BUFFER_SIZE - imm.buffer_offset;
202         // ensure vertex data is aligned
203         const unsigned pre_padding = padding(imm.buffer_offset, imm.vertex_format.stride); // might waste a little space, but it's safe
204         if ((bytes_needed + pre_padding) <= available_bytes)
205                 imm.buffer_offset += pre_padding;
206         else
207                 {
208                 // orphan this buffer & start with a fresh one
209 #if 1
210                 // this method works on all platforms, old & new
211                 glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW);
212 #else
213                 // TODO: use other (more recent) methods after thorough testing
214                 if (GLEW_VERSION_4_3 || GLEW_ARB_invalidate_subdata)
215                         glInvalidateBufferData(imm.vbo_id);
216                 else
217                         {
218                         // glitches!
219 //                      glMapBufferRange(GL_ARRAY_BUFFER, 0, IMM_BUFFER_SIZE, GL_MAP_INVALIDATE_BUFFER_BIT);
220
221                         // works
222 //                      glMapBufferRange(GL_ARRAY_BUFFER, 0, IMM_BUFFER_SIZE,
223 //                                       GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);
224 //                      glUnmapBuffer(GL_ARRAY_BUFFER);
225
226                         // also works
227                         glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW);
228                         }
229 #endif
230
231                 imm.buffer_offset = 0;
232                 }
233
234 //      printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1);
235
236         imm.buffer_data = glMapBufferRange(GL_ARRAY_BUFFER, imm.buffer_offset, bytes_needed,
237                                            GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | (imm.strict_vertex_ct ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT));
238
239 #if TRUST_NO_ONE
240         assert(imm.buffer_data != NULL);
241 #endif
242
243         imm.buffer_bytes_mapped = bytes_needed;
244         imm.vertex_data = imm.buffer_data;
245         }
246
247 void immBeginAtMost(Gwn_PrimType prim_type, unsigned vertex_ct)
248         {
249 #if TRUST_NO_ONE
250         assert(vertex_ct > 0);
251 #endif
252
253         imm.strict_vertex_ct = false;
254         immBegin(prim_type, vertex_ct);
255         }
256
257 #if IMM_BATCH_COMBO
258
259 Gwn_Batch* immBeginBatch(Gwn_PrimType prim_type, unsigned vertex_ct)
260         {
261 #if TRUST_NO_ONE
262         assert(initialized);
263         assert(imm.prim_type == GWN_PRIM_NONE); // make sure we haven't already begun
264         assert(vertex_count_makes_sense_for_primitive(vertex_ct, prim_type));
265 #endif
266
267         imm.prim_type = prim_type;
268         imm.vertex_ct = vertex_ct;
269         imm.vertex_idx = 0;
270         imm.unassigned_attrib_bits = imm.attrib_binding.enabled_bits;
271
272         Gwn_VertBuf* verts = GWN_vertbuf_create_with_format(&imm.vertex_format);
273         GWN_vertbuf_data_alloc(verts, vertex_ct);
274
275         imm.buffer_bytes_mapped = GWN_vertbuf_size_get(verts);
276         imm.vertex_data = verts->data;
277
278         imm.batch = GWN_batch_create_ex(prim_type, verts, NULL, GWN_BATCH_OWNS_VBO);
279         imm.batch->phase = GWN_BATCH_BUILDING;
280
281         return imm.batch;
282         }
283
284 Gwn_Batch* immBeginBatchAtMost(Gwn_PrimType prim_type, unsigned vertex_ct)
285         {
286         imm.strict_vertex_ct = false;
287         return immBeginBatch(prim_type, vertex_ct);
288         }
289
290 #endif // IMM_BATCH_COMBO
291
292 static void immDrawSetup(void)
293         {
294         // set up VAO -- can be done during Begin or End really
295         glBindVertexArray(imm.vao_id);
296
297         // enable/disable vertex attribs as needed
298         if (imm.attrib_binding.enabled_bits != imm.prev_enabled_attrib_bits)
299                 {
300                 for (unsigned loc = 0; loc < GWN_VERT_ATTR_MAX_LEN; ++loc)
301                         {
302                         bool is_enabled = imm.attrib_binding.enabled_bits & (1 << loc);
303                         bool was_enabled = imm.prev_enabled_attrib_bits & (1 << loc);
304
305                         if (is_enabled && !was_enabled)
306                                 {
307 //                              printf("enabling attrib %u\n", loc);
308                                 glEnableVertexAttribArray(loc);
309                                 }
310                         else if (was_enabled && !is_enabled)
311                                 {
312 //                              printf("disabling attrib %u\n", loc);
313                                 glDisableVertexAttribArray(loc);
314                                 }
315                         }
316
317                 imm.prev_enabled_attrib_bits = imm.attrib_binding.enabled_bits;
318                 }
319
320         const unsigned stride = imm.vertex_format.stride;
321
322         for (unsigned a_idx = 0; a_idx < imm.vertex_format.attrib_ct; ++a_idx)
323                 {
324                 const Gwn_VertAttr* a = imm.vertex_format.attribs + a_idx;
325
326                 const unsigned offset = imm.buffer_offset + a->offset;
327                 const GLvoid* pointer = (const GLubyte*)0 + offset;
328
329                 const unsigned loc = read_attrib_location(&imm.attrib_binding, a_idx);
330
331 //              printf("specifying attrib %u '%s' with offset %u, stride %u\n", loc, a->name, offset, stride);
332
333                 switch (a->fetch_mode)
334                         {
335                         case GWN_FETCH_FLOAT:
336                         case GWN_FETCH_INT_TO_FLOAT:
337                                 glVertexAttribPointer(loc, a->comp_ct, a->gl_comp_type, GL_FALSE, stride, pointer);
338                                 break;
339                         case GWN_FETCH_INT_TO_FLOAT_UNIT:
340                                 glVertexAttribPointer(loc, a->comp_ct, a->gl_comp_type, GL_TRUE, stride, pointer);
341                                 break;
342                         case GWN_FETCH_INT:
343                                 glVertexAttribIPointer(loc, a->comp_ct, a->gl_comp_type, stride, pointer);
344                         }
345                 }
346
347         if (gpuMatricesDirty())
348                 gpuBindMatrices(imm.shader_interface);
349         }
350
351 void immEnd(void)
352         {
353 #if TRUST_NO_ONE
354         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
355 #endif
356
357         unsigned buffer_bytes_used;
358         if (imm.strict_vertex_ct)
359                 {
360 #if TRUST_NO_ONE
361                 assert(imm.vertex_idx == imm.vertex_ct); // with all vertices defined
362 #endif
363                 buffer_bytes_used = imm.buffer_bytes_mapped;
364                 }
365         else
366                 {
367 #if TRUST_NO_ONE
368                 assert(imm.vertex_idx <= imm.vertex_ct);
369 #endif
370                 // printf("used %u of %u verts,", imm.vertex_idx, imm.vertex_ct);
371                 if (imm.vertex_idx == imm.vertex_ct)
372                         {
373                         buffer_bytes_used = imm.buffer_bytes_mapped;
374                         }
375                 else
376                         {
377 #if TRUST_NO_ONE
378                         assert(imm.vertex_idx == 0 || vertex_count_makes_sense_for_primitive(imm.vertex_idx, imm.prim_type));
379 #endif
380                         imm.vertex_ct = imm.vertex_idx;
381                         buffer_bytes_used = vertex_buffer_size(&imm.vertex_format, imm.vertex_ct);
382                         // unused buffer bytes are available to the next immBegin
383                         // printf(" %u of %u bytes\n", buffer_bytes_used, imm.buffer_bytes_mapped);
384                         }
385
386                 // tell OpenGL what range was modified so it doesn't copy the whole mapped range
387                 // printf("flushing %u to %u\n", imm.buffer_offset, imm.buffer_offset + buffer_bytes_used - 1);
388                 glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used);
389                 }
390
391 #if IMM_BATCH_COMBO
392         if (imm.batch)
393                 {
394                 if (buffer_bytes_used != imm.buffer_bytes_mapped)
395                         {
396                         GWN_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_ct);
397                         // TODO: resize only if vertex count is much smaller
398                         }
399
400                 GWN_batch_program_set(imm.batch, imm.bound_program, imm.shader_interface);
401                 imm.batch->phase = GWN_BATCH_READY_TO_DRAW;
402                 imm.batch = NULL; // don't free, batch belongs to caller
403                 }
404         else
405 #endif
406                 {
407                 glUnmapBuffer(GL_ARRAY_BUFFER);
408
409                 if (imm.vertex_ct > 0)
410                         {
411                         immDrawSetup();
412                         glDrawArrays(convert_prim_type_to_gl(imm.prim_type), 0, imm.vertex_ct);
413                         }
414
415                 glBindBuffer(GL_ARRAY_BUFFER, 0);
416                 glBindVertexArray(0);
417
418                 // prep for next immBegin
419                 imm.buffer_offset += buffer_bytes_used;
420                 }
421
422         // prep for next immBegin
423         imm.prim_type = GWN_PRIM_NONE;
424         imm.strict_vertex_ct = true;
425         }
426
427 static void setAttribValueBit(unsigned attrib_id)
428         {
429         uint16_t mask = 1 << attrib_id;
430
431 #if TRUST_NO_ONE
432         assert(imm.unassigned_attrib_bits & mask); // not already set
433 #endif
434
435         imm.unassigned_attrib_bits &= ~mask;
436         }
437
438
439 // --- generic attribute functions ---
440
441 void immAttrib1f(unsigned attrib_id, float x)
442         {
443         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
444
445 #if TRUST_NO_ONE
446         assert(attrib_id < imm.vertex_format.attrib_ct);
447         assert(attrib->comp_type == GWN_COMP_F32);
448         assert(attrib->comp_ct == 1);
449         assert(imm.vertex_idx < imm.vertex_ct);
450         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
451 #endif
452
453         setAttribValueBit(attrib_id);
454
455         float* data = (float*)(imm.vertex_data + attrib->offset);
456 //      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data);
457
458         data[0] = x;
459         }
460
461 void immAttrib2f(unsigned attrib_id, float x, float y)
462         {
463         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
464
465 #if TRUST_NO_ONE
466         assert(attrib_id < imm.vertex_format.attrib_ct);
467         assert(attrib->comp_type == GWN_COMP_F32);
468         assert(attrib->comp_ct == 2);
469         assert(imm.vertex_idx < imm.vertex_ct);
470         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
471 #endif
472
473         setAttribValueBit(attrib_id);
474
475         float* data = (float*)(imm.vertex_data + attrib->offset);
476 //      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data);
477
478         data[0] = x;
479         data[1] = y;
480         }
481
482 void immAttrib3f(unsigned attrib_id, float x, float y, float z)
483         {
484         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
485
486 #if TRUST_NO_ONE
487         assert(attrib_id < imm.vertex_format.attrib_ct);
488         assert(attrib->comp_type == GWN_COMP_F32);
489         assert(attrib->comp_ct == 3);
490         assert(imm.vertex_idx < imm.vertex_ct);
491         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
492 #endif
493
494         setAttribValueBit(attrib_id);
495
496         float* data = (float*)(imm.vertex_data + attrib->offset);
497 //      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data);
498
499         data[0] = x;
500         data[1] = y;
501         data[2] = z;
502         }
503
504 void immAttrib4f(unsigned attrib_id, float x, float y, float z, float w)
505         {
506         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
507
508 #if TRUST_NO_ONE
509         assert(attrib_id < imm.vertex_format.attrib_ct);
510         assert(attrib->comp_type == GWN_COMP_F32);
511         assert(attrib->comp_ct == 4);
512         assert(imm.vertex_idx < imm.vertex_ct);
513         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
514 #endif
515
516         setAttribValueBit(attrib_id);
517
518         float* data = (float*)(imm.vertex_data + attrib->offset);
519 //      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data);
520
521         data[0] = x;
522         data[1] = y;
523         data[2] = z;
524         data[3] = w;
525         }
526
527 void immAttrib1u(unsigned attrib_id, unsigned x)
528         {
529         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
530
531 #if TRUST_NO_ONE
532         assert(attrib_id < imm.vertex_format.attrib_ct);
533         assert(attrib->comp_type == GWN_COMP_U32);
534         assert(attrib->comp_ct == 1);
535         assert(imm.vertex_idx < imm.vertex_ct);
536         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
537 #endif
538
539         setAttribValueBit(attrib_id);
540
541         unsigned* data = (unsigned*)(imm.vertex_data + attrib->offset);
542
543         data[0] = x;
544         }
545
546 void immAttrib2i(unsigned attrib_id, int x, int y)
547         {
548         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
549
550 #if TRUST_NO_ONE
551         assert(attrib_id < imm.vertex_format.attrib_ct);
552         assert(attrib->comp_type == GWN_COMP_I32);
553         assert(attrib->comp_ct == 2);
554         assert(imm.vertex_idx < imm.vertex_ct);
555         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
556 #endif
557
558         setAttribValueBit(attrib_id);
559
560         int* data = (int*)(imm.vertex_data + attrib->offset);
561
562         data[0] = x;
563         data[1] = y;
564         }
565
566 void immAttrib2s(unsigned attrib_id, short x, short y)
567         {
568         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
569
570 #if TRUST_NO_ONE
571         assert(attrib_id < imm.vertex_format.attrib_ct);
572         assert(attrib->comp_type == GWN_COMP_I16);
573         assert(attrib->comp_ct == 2);
574         assert(imm.vertex_idx < imm.vertex_ct);
575         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
576 #endif
577
578         setAttribValueBit(attrib_id);
579
580         short* data = (short*)(imm.vertex_data + attrib->offset);
581
582         data[0] = x;
583         data[1] = y;
584         }
585
586 void immAttrib2fv(unsigned attrib_id, const float data[2])
587         {
588         immAttrib2f(attrib_id, data[0], data[1]);
589         }
590
591 void immAttrib3fv(unsigned attrib_id, const float data[3])
592         {
593         immAttrib3f(attrib_id, data[0], data[1], data[2]);
594         }
595
596 void immAttrib4fv(unsigned attrib_id, const float data[4])
597         {
598         immAttrib4f(attrib_id, data[0], data[1], data[2], data[3]);
599         }
600
601 void immAttrib3ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b)
602         {
603         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
604
605 #if TRUST_NO_ONE
606         assert(attrib_id < imm.vertex_format.attrib_ct);
607         assert(attrib->comp_type == GWN_COMP_U8);
608         assert(attrib->comp_ct == 3);
609         assert(imm.vertex_idx < imm.vertex_ct);
610         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
611 #endif
612
613         setAttribValueBit(attrib_id);
614
615         GLubyte* data = imm.vertex_data + attrib->offset;
616 //      printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data);
617
618         data[0] = r;
619         data[1] = g;
620         data[2] = b;
621         }
622
623 void immAttrib4ub(unsigned attrib_id, unsigned char r, unsigned char g, unsigned char b, unsigned char a)
624         {
625         Gwn_VertAttr* attrib = imm.vertex_format.attribs + attrib_id;
626
627 #if TRUST_NO_ONE
628         assert(attrib_id < imm.vertex_format.attrib_ct);
629         assert(attrib->comp_type == GWN_COMP_U8);
630         assert(attrib->comp_ct == 4);
631         assert(imm.vertex_idx < imm.vertex_ct);
632         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
633 #endif
634
635         setAttribValueBit(attrib_id);
636
637         GLubyte* data = imm.vertex_data + attrib->offset;
638 //      printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data);
639
640         data[0] = r;
641         data[1] = g;
642         data[2] = b;
643         data[3] = a;
644         }
645
646 void immAttrib3ubv(unsigned attrib_id, const unsigned char data[3])
647         {
648         immAttrib3ub(attrib_id, data[0], data[1], data[2]);
649         }
650
651 void immAttrib4ubv(unsigned attrib_id, const unsigned char data[4])
652         {
653         immAttrib4ub(attrib_id, data[0], data[1], data[2], data[3]);
654         }
655
656 void immSkipAttrib(unsigned attrib_id)
657         {
658 #if TRUST_NO_ONE
659         assert(attrib_id < imm.vertex_format.attrib_ct);
660         assert(imm.vertex_idx < imm.vertex_ct);
661         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
662 #endif
663
664         setAttribValueBit(attrib_id);
665         }
666
667 static void immEndVertex(void) // and move on to the next vertex
668         {
669 #if TRUST_NO_ONE
670         assert(imm.prim_type != GWN_PRIM_NONE); // make sure we're between a Begin/End pair
671         assert(imm.vertex_idx < imm.vertex_ct);
672 #endif
673
674         // have all attribs been assigned values?
675         // if not, copy value from previous vertex
676         if (imm.unassigned_attrib_bits)
677                 {
678 #if TRUST_NO_ONE
679                 assert(imm.vertex_idx > 0); // first vertex must have all attribs specified
680 #endif
681
682                 for (unsigned a_idx = 0; a_idx < imm.vertex_format.attrib_ct; ++a_idx)
683                         {
684                         if ((imm.unassigned_attrib_bits >> a_idx) & 1)
685                                 {
686                                 const Gwn_VertAttr* a = imm.vertex_format.attribs + a_idx;
687
688 //                              printf("copying %s from vertex %u to %u\n", a->name, imm.vertex_idx - 1, imm.vertex_idx);
689
690                                 GLubyte* data = imm.vertex_data + a->offset;
691                                 memcpy(data, data - imm.vertex_format.stride, a->sz);
692                                 // TODO: consolidate copy of adjacent attributes
693                                 }
694                         }
695                 }
696
697         imm.vertex_idx++;
698         imm.vertex_data += imm.vertex_format.stride;
699         imm.unassigned_attrib_bits = imm.attrib_binding.enabled_bits;
700         }
701
702 void immVertex2f(unsigned attrib_id, float x, float y)
703         {
704         immAttrib2f(attrib_id, x, y);
705         immEndVertex();
706         }
707
708 void immVertex3f(unsigned attrib_id, float x, float y, float z)
709         {
710         immAttrib3f(attrib_id, x, y, z);
711         immEndVertex();
712         }
713
714 void immVertex4f(unsigned attrib_id, float x, float y, float z, float w)
715         {
716         immAttrib4f(attrib_id, x, y, z, w);
717         immEndVertex();
718         }
719
720 void immVertex2i(unsigned attrib_id, int x, int y)
721         {
722         immAttrib2i(attrib_id, x, y);
723         immEndVertex();
724         }
725
726 void immVertex2s(unsigned attrib_id, short x, short y)
727         {
728         immAttrib2s(attrib_id, x, y);
729         immEndVertex();
730         }
731
732 void immVertex2fv(unsigned attrib_id, const float data[2])
733         {
734         immAttrib2f(attrib_id, data[0], data[1]);
735         immEndVertex();
736         }
737
738 void immVertex3fv(unsigned attrib_id, const float data[3])
739         {
740         immAttrib3f(attrib_id, data[0], data[1], data[2]);
741         immEndVertex();
742         }
743
744 void immVertex2iv(unsigned attrib_id, const int data[2])
745         {
746         immAttrib2i(attrib_id, data[0], data[1]);
747         immEndVertex();
748         }
749
750
751 // --- generic uniform functions ---
752
753 #if 0
754   #if TRUST_NO_ONE
755     #define GET_UNIFORM const Gwn_ShaderInput* uniform = GWN_shaderinterface_uniform(imm.shader_interface, name); assert(uniform);
756   #else
757     #define GET_UNIFORM const Gwn_ShaderInput* uniform = GWN_shaderinterface_uniform(imm.shader_interface, name);
758   #endif
759 #else
760         // NOTE: It is possible to have uniform fully optimized out from the shader.
761         //       In this case we can't assert failure or allow NULL-pointer dereference.
762         // TODO(sergey): How can we detect existing-but-optimized-out uniform but still
763         //               catch typos in uniform names passed to immUniform*() functions?
764   #define GET_UNIFORM const Gwn_ShaderInput* uniform = GWN_shaderinterface_uniform(imm.shader_interface, name); if (uniform == NULL) return;
765 #endif
766
767 void immUniform1f(const char* name, float x)
768         {
769         GET_UNIFORM
770         glUniform1f(uniform->location, x);
771         }
772
773 void immUniform2f(const char* name, float x, float y)
774         {
775         GET_UNIFORM
776         glUniform2f(uniform->location, x, y);
777         }
778
779 void immUniform2fv(const char* name, const float data[2])
780         {
781         GET_UNIFORM
782         glUniform2fv(uniform->location, 1, data);
783         }
784
785 void immUniform3f(const char* name, float x, float y, float z)
786         {
787         GET_UNIFORM
788         glUniform3f(uniform->location, x, y, z);
789         }
790
791 void immUniform3fv(const char* name, const float data[3])
792         {
793         GET_UNIFORM
794         glUniform3fv(uniform->location, 1, data);
795         }
796
797 // can increase this limit or move to another file
798 #define MAX_UNIFORM_NAME_LEN 60
799
800 void immUniformArray3fv(const char* bare_name, const float *data, int count)
801         {
802         // look up "name[0]" when given "name"
803         const size_t len = strlen(bare_name);
804 #if TRUST_NO_ONE
805         assert(len <= MAX_UNIFORM_NAME_LEN);
806 #endif
807         char name[MAX_UNIFORM_NAME_LEN];
808         strcpy(name, bare_name);
809         name[len + 0] = '[';
810         name[len + 1] = '0';
811         name[len + 2] = ']';
812         name[len + 3] = '\0';
813
814         GET_UNIFORM
815         glUniform3fv(uniform->location, count, data);
816         }
817
818 void immUniform4f(const char* name, float x, float y, float z, float w)
819         {
820         GET_UNIFORM
821         glUniform4f(uniform->location, x, y, z, w);
822         }
823
824 void immUniform4fv(const char* name, const float data[4])
825         {
826         GET_UNIFORM
827         glUniform4fv(uniform->location, 1, data);
828         }
829
830 void immUniformArray4fv(const char* bare_name, const float *data, int count)
831         {
832         // look up "name[0]" when given "name"
833         const size_t len = strlen(bare_name);
834 #if TRUST_NO_ONE
835         assert(len <= MAX_UNIFORM_NAME_LEN);
836 #endif
837         char name[MAX_UNIFORM_NAME_LEN];
838         strcpy(name, bare_name);
839         name[len + 0] = '[';
840         name[len + 1] = '0';
841         name[len + 2] = ']';
842         name[len + 3] = '\0';
843
844         GET_UNIFORM
845         glUniform4fv(uniform->location, count, data);
846         }
847
848 void immUniformMatrix4fv(const char* name, const float data[4][4])
849         {
850         GET_UNIFORM
851         glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (float *)data);
852         }
853
854 void immUniform1i(const char* name, int x)
855         {
856         GET_UNIFORM
857         glUniform1i(uniform->location, x);
858         }
859
860 void immUniform4iv(const char* name, const int data[4])
861         {
862         GET_UNIFORM
863         glUniform4iv(uniform->location, 1, data);
864         }
865
866 // --- convenience functions for setting "uniform vec4 color" ---
867
868 void immUniformColor4f(float r, float g, float b, float a)
869         {
870         const Gwn_ShaderInput* uniform = GWN_shaderinterface_uniform_builtin(imm.shader_interface, GWN_UNIFORM_COLOR);
871
872 #if TRUST_NO_ONE
873         assert(uniform != NULL);
874 #endif
875
876         glUniform4f(uniform->location, r, g, b, a);
877         }
878
879 void immUniformColor4fv(const float rgba[4])
880         {
881         immUniformColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
882         }
883
884 void immUniformColor3f(float r, float g, float b)
885         {
886         immUniformColor4f(r, g, b, 1.0f);
887         }
888
889 void immUniformColor3fv(const float rgb[3])
890         {
891         immUniformColor4f(rgb[0], rgb[1], rgb[2], 1.0f);
892         }
893
894 void immUniformColor3fvAlpha(const float rgb[3], float a)
895         {
896         immUniformColor4f(rgb[0], rgb[1], rgb[2], a);
897         }
898
899 // TODO: v-- treat as sRGB? --v
900
901 void immUniformColor3ub(unsigned char r, unsigned char g, unsigned char b)
902         {
903         const float scale = 1.0f / 255.0f;
904         immUniformColor4f(scale * r, scale * g, scale * b, 1.0f);
905         }
906
907 void immUniformColor4ub(unsigned char r, unsigned char g, unsigned char b, unsigned char a)
908         {
909         const float scale = 1.0f / 255.0f;
910         immUniformColor4f(scale * r, scale * g, scale * b, scale * a);
911         }
912
913 void immUniformColor3ubv(const unsigned char rgb[3])
914         {
915         immUniformColor3ub(rgb[0], rgb[1], rgb[2]);
916         }
917
918 void immUniformColor3ubvAlpha(const unsigned char rgb[3], unsigned char alpha)
919         {
920         immUniformColor4ub(rgb[0], rgb[1], rgb[2], alpha);
921         }
922
923 void immUniformColor4ubv(const unsigned char rgba[4])
924         {
925         immUniformColor4ub(rgba[0], rgba[1], rgba[2], rgba[3]);
926         }