Merge branch 'blender2.7'
[blender.git] / source / blender / gpu / intern / gpu_immediate.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2016 by Mike Erwin.
17  * All rights reserved.
18  */
19
20 /** \file \ingroup gpu
21  *
22  * GPU immediate mode work-alike
23  */
24
25 #include "UI_resources.h"
26
27 #include "GPU_attr_binding.h"
28 #include "GPU_immediate.h"
29
30 #include "gpu_attr_binding_private.h"
31 #include "gpu_context_private.h"
32 #include "gpu_primitive_private.h"
33 #include "gpu_shader_private.h"
34 #include "gpu_vertex_format_private.h"
35
36 #include <string.h>
37 #include <stdlib.h>
38
39 /* necessary functions from matrix API */
40 extern void GPU_matrix_bind(const GPUShaderInterface *);
41 extern bool GPU_matrix_dirty_get(void);
42
43 typedef struct {
44         /* TODO: organize this struct by frequency of change (run-time) */
45
46         GPUBatch *batch;
47         GPUContext *context;
48
49         /* current draw call */
50         GLubyte *buffer_data;
51         uint buffer_offset;
52         uint buffer_bytes_mapped;
53         uint vertex_len;
54         bool strict_vertex_len;
55         GPUPrimType prim_type;
56
57         GPUVertFormat vertex_format;
58
59         /* current vertex */
60         uint vertex_idx;
61         GLubyte *vertex_data;
62         uint16_t unassigned_attr_bits; /* which attributes of current vertex have not been given values? */
63
64         GLuint vbo_id;
65         GLuint vao_id;
66
67         GLuint bound_program;
68         const GPUShaderInterface *shader_interface;
69         GPUAttrBinding attr_binding;
70         uint16_t prev_enabled_attr_bits; /* <-- only affects this VAO, so we're ok */
71 } Immediate;
72
73 /* size of internal buffer -- make this adjustable? */
74 #define IMM_BUFFER_SIZE (4 * 1024 * 1024)
75
76 static bool initialized = false;
77 static Immediate imm;
78
79 void immInit(void)
80 {
81 #if TRUST_NO_ONE
82         assert(!initialized);
83 #endif
84         memset(&imm, 0, sizeof(Immediate));
85
86         imm.vbo_id = GPU_buf_alloc();
87         glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id);
88         glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW);
89
90         imm.prim_type = GPU_PRIM_NONE;
91         imm.strict_vertex_len = true;
92
93         glBindBuffer(GL_ARRAY_BUFFER, 0);
94         initialized = true;
95 }
96
97 void immActivate(void)
98 {
99 #if TRUST_NO_ONE
100         assert(initialized);
101         assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */
102         assert(imm.vao_id == 0);
103 #endif
104         imm.vao_id = GPU_vao_alloc();
105         imm.context = GPU_context_active_get();
106 }
107
108 void immDeactivate(void)
109 {
110 #if TRUST_NO_ONE
111         assert(initialized);
112         assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we're not between a Begin/End pair */
113         assert(imm.vao_id != 0);
114 #endif
115         GPU_vao_free(imm.vao_id, imm.context);
116         imm.vao_id = 0;
117         imm.prev_enabled_attr_bits = 0;
118 }
119
120 void immDestroy(void)
121 {
122         GPU_buf_free(imm.vbo_id);
123         initialized = false;
124 }
125
126 GPUVertFormat *immVertexFormat(void)
127 {
128         GPU_vertformat_clear(&imm.vertex_format);
129         return &imm.vertex_format;
130 }
131
132 void immBindProgram(GLuint program, const GPUShaderInterface *shaderface)
133 {
134 #if TRUST_NO_ONE
135         assert(imm.bound_program == 0);
136         assert(glIsProgram(program));
137 #endif
138
139         imm.bound_program = program;
140         imm.shader_interface = shaderface;
141
142         if (!imm.vertex_format.packed)
143                 VertexFormat_pack(&imm.vertex_format);
144
145         glUseProgram(program);
146         get_attr_locations(&imm.vertex_format, &imm.attr_binding, shaderface);
147         GPU_matrix_bind(shaderface);
148 }
149
150 void immBindBuiltinProgram(eGPUBuiltinShader shader_id)
151 {
152         GPUShader *shader = GPU_shader_get_builtin_shader(shader_id);
153         immBindProgram(shader->program, shader->interface);
154 }
155
156 void immUnbindProgram(void)
157 {
158 #if TRUST_NO_ONE
159         assert(imm.bound_program != 0);
160 #endif
161 #if PROGRAM_NO_OPTI
162         glUseProgram(0);
163 #endif
164         imm.bound_program = 0;
165 }
166
167 #if TRUST_NO_ONE
168 static bool vertex_count_makes_sense_for_primitive(uint vertex_len, GPUPrimType prim_type)
169 {
170         /* does vertex_len make sense for this primitive type? */
171         if (vertex_len == 0) {
172                 return false;
173         }
174
175         switch (prim_type) {
176                 case GPU_PRIM_POINTS:
177                         return true;
178                 case GPU_PRIM_LINES:
179                         return vertex_len % 2 == 0;
180                 case GPU_PRIM_LINE_STRIP:
181                 case GPU_PRIM_LINE_LOOP:
182                         return vertex_len >= 2;
183                 case GPU_PRIM_LINE_STRIP_ADJ:
184                         return vertex_len >= 4;
185                 case GPU_PRIM_TRIS:
186                         return vertex_len % 3 == 0;
187                 case GPU_PRIM_TRI_STRIP:
188                 case GPU_PRIM_TRI_FAN:
189                         return vertex_len >= 3;
190                 default:
191                         return false;
192         }
193 }
194 #endif
195
196 void immBegin(GPUPrimType prim_type, uint vertex_len)
197 {
198 #if TRUST_NO_ONE
199         assert(initialized);
200         assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */
201         assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
202 #endif
203         imm.prim_type = prim_type;
204         imm.vertex_len = vertex_len;
205         imm.vertex_idx = 0;
206         imm.unassigned_attr_bits = imm.attr_binding.enabled_bits;
207
208         /* how many bytes do we need for this draw call? */
209         const uint bytes_needed = vertex_buffer_size(&imm.vertex_format, vertex_len);
210
211 #if TRUST_NO_ONE
212         assert(bytes_needed <= IMM_BUFFER_SIZE);
213 #endif
214
215         glBindBuffer(GL_ARRAY_BUFFER, imm.vbo_id);
216
217         /* does the current buffer have enough room? */
218         const uint available_bytes = IMM_BUFFER_SIZE - imm.buffer_offset;
219         /* ensure vertex data is aligned */
220         const uint pre_padding = padding(imm.buffer_offset, imm.vertex_format.stride); /* might waste a little space, but it's safe */
221         if ((bytes_needed + pre_padding) <= available_bytes) {
222                 imm.buffer_offset += pre_padding;
223         }
224         else {
225                 /* orphan this buffer & start with a fresh one */
226                 /* this method works on all platforms, old & new */
227                 glBufferData(GL_ARRAY_BUFFER, IMM_BUFFER_SIZE, NULL, GL_DYNAMIC_DRAW);
228
229                 imm.buffer_offset = 0;
230         }
231
232 /*      printf("mapping %u to %u\n", imm.buffer_offset, imm.buffer_offset + bytes_needed - 1); */
233
234         imm.buffer_data = glMapBufferRange(GL_ARRAY_BUFFER, imm.buffer_offset, bytes_needed,
235                                            GL_MAP_WRITE_BIT | GL_MAP_UNSYNCHRONIZED_BIT | (imm.strict_vertex_len ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT));
236
237 #if TRUST_NO_ONE
238         assert(imm.buffer_data != NULL);
239 #endif
240
241         imm.buffer_bytes_mapped = bytes_needed;
242         imm.vertex_data = imm.buffer_data;
243 }
244
245 void immBeginAtMost(GPUPrimType prim_type, uint vertex_len)
246 {
247 #if TRUST_NO_ONE
248         assert(vertex_len > 0);
249 #endif
250
251         imm.strict_vertex_len = false;
252         immBegin(prim_type, vertex_len);
253 }
254
255
256 GPUBatch *immBeginBatch(GPUPrimType prim_type, uint vertex_len)
257 {
258 #if TRUST_NO_ONE
259         assert(initialized);
260         assert(imm.prim_type == GPU_PRIM_NONE); /* make sure we haven't already begun */
261         assert(vertex_count_makes_sense_for_primitive(vertex_len, prim_type));
262 #endif
263         imm.prim_type = prim_type;
264         imm.vertex_len = vertex_len;
265         imm.vertex_idx = 0;
266         imm.unassigned_attr_bits = imm.attr_binding.enabled_bits;
267
268         GPUVertBuf *verts = GPU_vertbuf_create_with_format(&imm.vertex_format);
269         GPU_vertbuf_data_alloc(verts, vertex_len);
270
271         imm.buffer_bytes_mapped = GPU_vertbuf_size_get(verts);
272         imm.vertex_data = verts->data;
273
274         imm.batch = GPU_batch_create_ex(prim_type, verts, NULL, GPU_BATCH_OWNS_VBO);
275         imm.batch->phase = GPU_BATCH_BUILDING;
276
277         return imm.batch;
278 }
279
280 GPUBatch *immBeginBatchAtMost(GPUPrimType prim_type, uint vertex_len)
281 {
282         imm.strict_vertex_len = false;
283         return immBeginBatch(prim_type, vertex_len);
284 }
285
286 static void immDrawSetup(void)
287 {
288         /* set up VAO -- can be done during Begin or End really */
289         glBindVertexArray(imm.vao_id);
290
291         /* Enable/Disable vertex attributes as needed. */
292         if (imm.attr_binding.enabled_bits != imm.prev_enabled_attr_bits) {
293                 for (uint loc = 0; loc < GPU_VERT_ATTR_MAX_LEN; ++loc) {
294                         bool is_enabled = imm.attr_binding.enabled_bits & (1 << loc);
295                         bool was_enabled = imm.prev_enabled_attr_bits & (1 << loc);
296
297                         if (is_enabled && !was_enabled) {
298                                 glEnableVertexAttribArray(loc);
299                         }
300                         else if (was_enabled && !is_enabled) {
301                                 glDisableVertexAttribArray(loc);
302                         }
303                 }
304
305                 imm.prev_enabled_attr_bits = imm.attr_binding.enabled_bits;
306         }
307
308         const uint stride = imm.vertex_format.stride;
309
310         for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; ++a_idx) {
311                 const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx];
312
313                 const uint offset = imm.buffer_offset + a->offset;
314                 const GLvoid *pointer = (const GLubyte *)0 + offset;
315
316                 const uint loc = read_attr_location(&imm.attr_binding, a_idx);
317
318                 switch (a->fetch_mode) {
319                         case GPU_FETCH_FLOAT:
320                         case GPU_FETCH_INT_TO_FLOAT:
321                                 glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_FALSE, stride, pointer);
322                                 break;
323                         case GPU_FETCH_INT_TO_FLOAT_UNIT:
324                                 glVertexAttribPointer(loc, a->comp_len, a->gl_comp_type, GL_TRUE, stride, pointer);
325                                 break;
326                         case GPU_FETCH_INT:
327                                 glVertexAttribIPointer(loc, a->comp_len, a->gl_comp_type, stride, pointer);
328                 }
329         }
330
331         if (GPU_matrix_dirty_get()) {
332                 GPU_matrix_bind(imm.shader_interface);
333         }
334 }
335
336 void immEnd(void)
337 {
338 #if TRUST_NO_ONE
339         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
340 #endif
341
342         uint buffer_bytes_used;
343         if (imm.strict_vertex_len) {
344 #if TRUST_NO_ONE
345                 assert(imm.vertex_idx == imm.vertex_len); /* with all vertices defined */
346 #endif
347                 buffer_bytes_used = imm.buffer_bytes_mapped;
348         }
349         else {
350 #if TRUST_NO_ONE
351                 assert(imm.vertex_idx <= imm.vertex_len);
352 #endif
353                 if (imm.vertex_idx == imm.vertex_len) {
354                         buffer_bytes_used = imm.buffer_bytes_mapped;
355                 }
356                 else {
357 #if TRUST_NO_ONE
358                         assert(imm.vertex_idx == 0 || vertex_count_makes_sense_for_primitive(imm.vertex_idx, imm.prim_type));
359 #endif
360                         imm.vertex_len = imm.vertex_idx;
361                         buffer_bytes_used = vertex_buffer_size(&imm.vertex_format, imm.vertex_len);
362                         /* unused buffer bytes are available to the next immBegin */
363                 }
364                 /* tell OpenGL what range was modified so it doesn't copy the whole mapped range */
365                 glFlushMappedBufferRange(GL_ARRAY_BUFFER, 0, buffer_bytes_used);
366         }
367
368         if (imm.batch) {
369                 if (buffer_bytes_used != imm.buffer_bytes_mapped) {
370                         GPU_vertbuf_data_resize(imm.batch->verts[0], imm.vertex_len);
371                         /* TODO: resize only if vertex count is much smaller */
372                 }
373                 GPU_batch_program_set(imm.batch, imm.bound_program, imm.shader_interface);
374                 imm.batch->phase = GPU_BATCH_READY_TO_DRAW;
375                 imm.batch = NULL; /* don't free, batch belongs to caller */
376         }
377         else {
378                 glUnmapBuffer(GL_ARRAY_BUFFER);
379                 if (imm.vertex_len > 0) {
380                         immDrawSetup();
381                         glDrawArrays(convert_prim_type_to_gl(imm.prim_type), 0, imm.vertex_len);
382                 }
383                 /* These lines are causing crash on startup on some old GPU + drivers.
384                  * They are not required so just comment them. (T55722) */
385                 // glBindBuffer(GL_ARRAY_BUFFER, 0);
386                 // glBindVertexArray(0);
387                 /* prep for next immBegin */
388                 imm.buffer_offset += buffer_bytes_used;
389         }
390
391         /* prep for next immBegin */
392         imm.prim_type = GPU_PRIM_NONE;
393         imm.strict_vertex_len = true;
394 }
395
396 static void setAttrValueBit(uint attr_id)
397 {
398         uint16_t mask = 1 << attr_id;
399 #if TRUST_NO_ONE
400         assert(imm.unassigned_attr_bits & mask); /* not already set */
401 #endif
402         imm.unassigned_attr_bits &= ~mask;
403 }
404
405
406 /* --- generic attribute functions --- */
407
408 void immAttr1f(uint attr_id, float x)
409 {
410         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
411 #if TRUST_NO_ONE
412         assert(attr_id < imm.vertex_format.attr_len);
413         assert(attr->comp_type == GPU_COMP_F32);
414         assert(attr->comp_len == 1);
415         assert(imm.vertex_idx < imm.vertex_len);
416         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
417 #endif
418         setAttrValueBit(attr_id);
419
420         float *data = (float *)(imm.vertex_data + attr->offset);
421 /*      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
422
423         data[0] = x;
424 }
425
426 void immAttr2f(uint attr_id, float x, float y)
427 {
428         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
429 #if TRUST_NO_ONE
430         assert(attr_id < imm.vertex_format.attr_len);
431         assert(attr->comp_type == GPU_COMP_F32);
432         assert(attr->comp_len == 2);
433         assert(imm.vertex_idx < imm.vertex_len);
434         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
435 #endif
436         setAttrValueBit(attr_id);
437
438         float *data = (float *)(imm.vertex_data + attr->offset);
439 /*      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
440
441         data[0] = x;
442         data[1] = y;
443 }
444
445 void immAttr3f(uint attr_id, float x, float y, float z)
446 {
447         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
448 #if TRUST_NO_ONE
449         assert(attr_id < imm.vertex_format.attr_len);
450         assert(attr->comp_type == GPU_COMP_F32);
451         assert(attr->comp_len == 3);
452         assert(imm.vertex_idx < imm.vertex_len);
453         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
454 #endif
455         setAttrValueBit(attr_id);
456
457         float *data = (float *)(imm.vertex_data + attr->offset);
458 /*      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
459
460         data[0] = x;
461         data[1] = y;
462         data[2] = z;
463 }
464
465 void immAttr4f(uint attr_id, float x, float y, float z, float w)
466 {
467         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
468 #if TRUST_NO_ONE
469         assert(attr_id < imm.vertex_format.attr_len);
470         assert(attr->comp_type == GPU_COMP_F32);
471         assert(attr->comp_len == 4);
472         assert(imm.vertex_idx < imm.vertex_len);
473         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
474 #endif
475         setAttrValueBit(attr_id);
476
477         float *data = (float *)(imm.vertex_data + attr->offset);
478 /*      printf("%s %td %p\n", __FUNCTION__, (GLubyte*)data - imm.buffer_data, data); */
479
480         data[0] = x;
481         data[1] = y;
482         data[2] = z;
483         data[3] = w;
484 }
485
486 void immAttr1u(uint attr_id, uint x)
487 {
488         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
489 #if TRUST_NO_ONE
490         assert(attr_id < imm.vertex_format.attr_len);
491         assert(attr->comp_type == GPU_COMP_U32);
492         assert(attr->comp_len == 1);
493         assert(imm.vertex_idx < imm.vertex_len);
494         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
495 #endif
496         setAttrValueBit(attr_id);
497
498         uint *data = (uint *)(imm.vertex_data + attr->offset);
499
500         data[0] = x;
501 }
502
503 void immAttr2i(uint attr_id, int x, int y)
504 {
505         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
506 #if TRUST_NO_ONE
507         assert(attr_id < imm.vertex_format.attr_len);
508         assert(attr->comp_type == GPU_COMP_I32);
509         assert(attr->comp_len == 2);
510         assert(imm.vertex_idx < imm.vertex_len);
511         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
512 #endif
513         setAttrValueBit(attr_id);
514
515         int *data = (int *)(imm.vertex_data + attr->offset);
516
517         data[0] = x;
518         data[1] = y;
519 }
520
521 void immAttr2s(uint attr_id, short x, short y)
522 {
523         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
524 #if TRUST_NO_ONE
525         assert(attr_id < imm.vertex_format.attr_len);
526         assert(attr->comp_type == GPU_COMP_I16);
527         assert(attr->comp_len == 2);
528         assert(imm.vertex_idx < imm.vertex_len);
529         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
530 #endif
531         setAttrValueBit(attr_id);
532
533         short *data = (short *)(imm.vertex_data + attr->offset);
534
535         data[0] = x;
536         data[1] = y;
537 }
538
539 void immAttr2fv(uint attr_id, const float data[2])
540 {
541         immAttr2f(attr_id, data[0], data[1]);
542 }
543
544 void immAttr3fv(uint attr_id, const float data[3])
545 {
546         immAttr3f(attr_id, data[0], data[1], data[2]);
547 }
548
549 void immAttr4fv(uint attr_id, const float data[4])
550 {
551         immAttr4f(attr_id, data[0], data[1], data[2], data[3]);
552 }
553
554 void immAttr3ub(uint attr_id, uchar r, uchar g, uchar b)
555 {
556         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
557 #if TRUST_NO_ONE
558         assert(attr_id < imm.vertex_format.attr_len);
559         assert(attr->comp_type == GPU_COMP_U8);
560         assert(attr->comp_len == 3);
561         assert(imm.vertex_idx < imm.vertex_len);
562         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
563 #endif
564         setAttrValueBit(attr_id);
565
566         GLubyte *data = imm.vertex_data + attr->offset;
567 /*      printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */
568
569         data[0] = r;
570         data[1] = g;
571         data[2] = b;
572 }
573
574 void immAttr4ub(uint attr_id, uchar r, uchar g, uchar b, uchar a)
575 {
576         GPUVertAttr *attr = &imm.vertex_format.attrs[attr_id];
577 #if TRUST_NO_ONE
578         assert(attr_id < imm.vertex_format.attr_len);
579         assert(attr->comp_type == GPU_COMP_U8);
580         assert(attr->comp_len == 4);
581         assert(imm.vertex_idx < imm.vertex_len);
582         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
583 #endif
584         setAttrValueBit(attr_id);
585
586         GLubyte *data = imm.vertex_data + attr->offset;
587 /*      printf("%s %td %p\n", __FUNCTION__, data - imm.buffer_data, data); */
588
589         data[0] = r;
590         data[1] = g;
591         data[2] = b;
592         data[3] = a;
593 }
594
595 void immAttr3ubv(uint attr_id, const uchar data[3])
596 {
597         immAttr3ub(attr_id, data[0], data[1], data[2]);
598 }
599
600 void immAttr4ubv(uint attr_id, const uchar data[4])
601 {
602         immAttr4ub(attr_id, data[0], data[1], data[2], data[3]);
603 }
604
605 void immAttrSkip(uint attr_id)
606 {
607 #if TRUST_NO_ONE
608         assert(attr_id < imm.vertex_format.attr_len);
609         assert(imm.vertex_idx < imm.vertex_len);
610         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
611 #endif
612         setAttrValueBit(attr_id);
613 }
614
615 static void immEndVertex(void) /* and move on to the next vertex */
616 {
617 #if TRUST_NO_ONE
618         assert(imm.prim_type != GPU_PRIM_NONE); /* make sure we're between a Begin/End pair */
619         assert(imm.vertex_idx < imm.vertex_len);
620 #endif
621
622         /* Have all attributes been assigned values?
623          * If not, copy value from previous vertex. */
624         if (imm.unassigned_attr_bits) {
625 #if TRUST_NO_ONE
626                 assert(imm.vertex_idx > 0); /* first vertex must have all attributes specified */
627 #endif
628                 for (uint a_idx = 0; a_idx < imm.vertex_format.attr_len; ++a_idx) {
629                         if ((imm.unassigned_attr_bits >> a_idx) & 1) {
630                                 const GPUVertAttr *a = &imm.vertex_format.attrs[a_idx];
631
632 /*                              printf("copying %s from vertex %u to %u\n", a->name, imm.vertex_idx - 1, imm.vertex_idx); */
633
634                                 GLubyte *data = imm.vertex_data + a->offset;
635                                 memcpy(data, data - imm.vertex_format.stride, a->sz);
636                                 /* TODO: consolidate copy of adjacent attributes */
637                         }
638                 }
639         }
640
641         imm.vertex_idx++;
642         imm.vertex_data += imm.vertex_format.stride;
643         imm.unassigned_attr_bits = imm.attr_binding.enabled_bits;
644 }
645
646 void immVertex2f(uint attr_id, float x, float y)
647 {
648         immAttr2f(attr_id, x, y);
649         immEndVertex();
650 }
651
652 void immVertex3f(uint attr_id, float x, float y, float z)
653 {
654         immAttr3f(attr_id, x, y, z);
655         immEndVertex();
656 }
657
658 void immVertex4f(uint attr_id, float x, float y, float z, float w)
659 {
660         immAttr4f(attr_id, x, y, z, w);
661         immEndVertex();
662 }
663
664 void immVertex2i(uint attr_id, int x, int y)
665 {
666         immAttr2i(attr_id, x, y);
667         immEndVertex();
668 }
669
670 void immVertex2s(uint attr_id, short x, short y)
671 {
672         immAttr2s(attr_id, x, y);
673         immEndVertex();
674 }
675
676 void immVertex2fv(uint attr_id, const float data[2])
677 {
678         immAttr2f(attr_id, data[0], data[1]);
679         immEndVertex();
680 }
681
682 void immVertex3fv(uint attr_id, const float data[3])
683 {
684         immAttr3f(attr_id, data[0], data[1], data[2]);
685         immEndVertex();
686 }
687
688 void immVertex2iv(uint attr_id, const int data[2])
689 {
690         immAttr2i(attr_id, data[0], data[1]);
691         immEndVertex();
692 }
693
694
695 /* --- generic uniform functions --- */
696
697 #if 0
698 #  if TRUST_NO_ONE
699 #    define GET_UNIFORM const GPUShaderInput* uniform = GPU_shaderinterface_uniform_ensure(imm.shader_interface, name); assert(uniform);
700 #  else
701 #    define GET_UNIFORM const GPUShaderInput* uniform = GPU_shaderinterface_uniform_ensure(imm.shader_interface, name);
702 #  endif
703 #else
704         /* NOTE: It is possible to have uniform fully optimized out from the shader.
705          *       In this case we can't assert failure or allow NULL-pointer dereference.
706          * TODO(sergey): How can we detect existing-but-optimized-out uniform but still
707          *               catch typos in uniform names passed to immUniform*() functions? */
708 #  define GET_UNIFORM const GPUShaderInput* uniform = GPU_shaderinterface_uniform_ensure(imm.shader_interface, name); if (uniform == NULL) return;
709 #endif
710
711 void immUniform1f(const char *name, float x)
712 {
713         GET_UNIFORM
714         glUniform1f(uniform->location, x);
715 }
716
717 void immUniform2f(const char *name, float x, float y)
718 {
719         GET_UNIFORM
720         glUniform2f(uniform->location, x, y);
721 }
722
723 void immUniform2fv(const char *name, const float data[2])
724 {
725         GET_UNIFORM
726         glUniform2fv(uniform->location, 1, data);
727 }
728
729 void immUniform3f(const char *name, float x, float y, float z)
730 {
731         GET_UNIFORM
732         glUniform3f(uniform->location, x, y, z);
733 }
734
735 void immUniform3fv(const char *name, const float data[3])
736 {
737         GET_UNIFORM
738         glUniform3fv(uniform->location, 1, data);
739 }
740
741 /* can increase this limit or move to another file */
742 #define MAX_UNIFORM_NAME_LEN 60
743
744 void immUniformArray3fv(const char *bare_name, const float *data, int count)
745 {
746         /* look up "name[0]" when given "name" */
747         const size_t len = strlen(bare_name);
748 #if TRUST_NO_ONE
749         assert(len <= MAX_UNIFORM_NAME_LEN);
750 #endif
751         char name[MAX_UNIFORM_NAME_LEN];
752         strcpy(name, bare_name);
753         name[len + 0] = '[';
754         name[len + 1] = '0';
755         name[len + 2] = ']';
756         name[len + 3] = '\0';
757
758         GET_UNIFORM
759         glUniform3fv(uniform->location, count, data);
760 }
761
762 void immUniform4f(const char *name, float x, float y, float z, float w)
763 {
764         GET_UNIFORM
765         glUniform4f(uniform->location, x, y, z, w);
766 }
767
768 void immUniform4fv(const char *name, const float data[4])
769 {
770         GET_UNIFORM
771         glUniform4fv(uniform->location, 1, data);
772 }
773
774 void immUniformArray4fv(const char *bare_name, const float *data, int count)
775 {
776         /* look up "name[0]" when given "name" */
777         const size_t len = strlen(bare_name);
778 #if TRUST_NO_ONE
779         assert(len <= MAX_UNIFORM_NAME_LEN);
780 #endif
781         char name[MAX_UNIFORM_NAME_LEN];
782         strcpy(name, bare_name);
783         name[len + 0] = '[';
784         name[len + 1] = '0';
785         name[len + 2] = ']';
786         name[len + 3] = '\0';
787
788         GET_UNIFORM
789         glUniform4fv(uniform->location, count, data);
790 }
791
792 void immUniformMatrix4fv(const char *name, const float data[4][4])
793 {
794         GET_UNIFORM
795         glUniformMatrix4fv(uniform->location, 1, GL_FALSE, (float *)data);
796 }
797
798 void immUniform1i(const char *name, int x)
799 {
800         GET_UNIFORM
801         glUniform1i(uniform->location, x);
802 }
803
804 void immUniform4iv(const char *name, const int data[4])
805 {
806         GET_UNIFORM
807         glUniform4iv(uniform->location, 1, data);
808 }
809
810 /* --- convenience functions for setting "uniform vec4 color" --- */
811
812 void immUniformColor4f(float r, float g, float b, float a)
813 {
814         const GPUShaderInput *uniform = GPU_shaderinterface_uniform_builtin(imm.shader_interface, GPU_UNIFORM_COLOR);
815 #if TRUST_NO_ONE
816         assert(uniform != NULL);
817 #endif
818         glUniform4f(uniform->location, r, g, b, a);
819 }
820
821 void immUniformColor4fv(const float rgba[4])
822 {
823         immUniformColor4f(rgba[0], rgba[1], rgba[2], rgba[3]);
824 }
825
826 void immUniformColor3f(float r, float g, float b)
827 {
828         immUniformColor4f(r, g, b, 1.0f);
829 }
830
831 void immUniformColor3fv(const float rgb[3])
832 {
833         immUniformColor4f(rgb[0], rgb[1], rgb[2], 1.0f);
834 }
835
836 void immUniformColor3fvAlpha(const float rgb[3], float a)
837 {
838         immUniformColor4f(rgb[0], rgb[1], rgb[2], a);
839 }
840
841 /* TODO: v-- treat as sRGB? --v */
842
843 void immUniformColor3ub(uchar r, uchar g, uchar b)
844 {
845         const float scale = 1.0f / 255.0f;
846         immUniformColor4f(scale * r, scale * g, scale * b, 1.0f);
847 }
848
849 void immUniformColor4ub(uchar r, uchar g, uchar b, uchar a)
850 {
851         const float scale = 1.0f / 255.0f;
852         immUniformColor4f(scale * r, scale * g, scale * b, scale * a);
853 }
854
855 void immUniformColor3ubv(const uchar rgb[3])
856 {
857         immUniformColor3ub(rgb[0], rgb[1], rgb[2]);
858 }
859
860 void immUniformColor3ubvAlpha(const uchar rgb[3], uchar alpha)
861 {
862         immUniformColor4ub(rgb[0], rgb[1], rgb[2], alpha);
863 }
864
865 void immUniformColor4ubv(const uchar rgba[4])
866 {
867         immUniformColor4ub(rgba[0], rgba[1], rgba[2], rgba[3]);
868 }
869
870 void immUniformThemeColor(int color_id)
871 {
872         float color[4];
873         UI_GetThemeColor4fv(color_id, color);
874         immUniformColor4fv(color);
875 }
876
877 void immUniformThemeColor3(int color_id)
878 {
879         float color[3];
880         UI_GetThemeColor3fv(color_id, color);
881         immUniformColor3fv(color);
882 }
883
884 void immUniformThemeColorShade(int color_id, int offset)
885 {
886         float color[4];
887         UI_GetThemeColorShade4fv(color_id, offset, color);
888         immUniformColor4fv(color);
889 }
890
891 void immUniformThemeColorShadeAlpha(int color_id, int color_offset, int alpha_offset)
892 {
893         float color[4];
894         UI_GetThemeColorShadeAlpha4fv(color_id, color_offset, alpha_offset, color);
895         immUniformColor4fv(color);
896 }
897
898 void immUniformThemeColorBlendShade(int color_id1, int color_id2, float fac, int offset)
899 {
900         float color[4];
901         UI_GetThemeColorBlendShade4fv(color_id1, color_id2, fac, offset, color);
902         immUniformColor4fv(color);
903 }
904
905 void immUniformThemeColorBlend(int color_id1, int color_id2, float fac)
906 {
907         uint8_t color[3];
908         UI_GetThemeColorBlend3ubv(color_id1, color_id2, fac, color);
909         immUniformColor3ubv(color);
910 }
911
912 void immThemeColorShadeAlpha(int colorid, int coloffset, int alphaoffset)
913 {
914         uchar col[4];
915         UI_GetThemeColorShadeAlpha4ubv(colorid, coloffset, alphaoffset, col);
916         immUniformColor4ub(col[0], col[1], col[2], col[3]);
917 }