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