1 /*
2  * This file is part of libplacebo.
3  *
4  * libplacebo is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * libplacebo is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with libplacebo. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "gpu.h"
19 #include "formats.h"
20 #include "utils.h"
21 
gl_desc_namespace(pl_gpu gpu,enum pl_desc_type type)22 int gl_desc_namespace(pl_gpu gpu, enum pl_desc_type type)
23 {
24     return (int) type;
25 }
26 
27 #define CACHE_MAGIC {'P','L','G','L'}
28 #define CACHE_VERSION 1
29 static const char gl_cache_magic[4] = CACHE_MAGIC;
30 
31 struct gl_cache_header {
32     char magic[sizeof(gl_cache_magic)];
33     int cache_version;
34     GLenum format;
35 };
36 
load_cached_program(pl_gpu gpu,const struct pl_pass_params * params)37 static GLuint load_cached_program(pl_gpu gpu, const struct pl_pass_params *params)
38 {
39     if (!gl_test_ext(gpu, "GL_ARB_get_program_binary", 41, 30))
40         return 0;
41 
42     pl_str cache = {
43         .buf = (void *) params->cached_program,
44         .len = params->cached_program_len,
45     };
46 
47     if (cache.len < sizeof(struct gl_cache_header))
48         return false;
49 
50     struct gl_cache_header *header = (struct gl_cache_header *) cache.buf;
51     cache = pl_str_drop(cache, sizeof(*header));
52 
53     if (strncmp(header->magic, gl_cache_magic, sizeof(gl_cache_magic)) != 0)
54         return 0;
55     if (header->cache_version != CACHE_VERSION)
56         return 0;
57 
58     GLuint prog = glCreateProgram();
59     if (!gl_check_err(gpu, "load_cached_program: glCreateProgram"))
60         return 0;
61 
62     glProgramBinary(prog, header->format, cache.buf, cache.len);
63     glGetError(); // discard potential useless error
64 
65     GLint status = 0;
66     glGetProgramiv(prog, GL_LINK_STATUS, &status);
67     if (status)
68         return prog;
69 
70     glDeleteProgram(prog);
71     gl_check_err(gpu, "load_cached_program: glProgramBinary");
72     return 0;
73 }
74 
gl_log_level(GLint status,GLint log_length)75 static enum pl_log_level gl_log_level(GLint status, GLint log_length)
76 {
77     if (!status) {
78         return PL_LOG_ERR;
79     } else if (log_length > 0) {
80         return PL_LOG_INFO;
81     } else {
82         return PL_LOG_DEBUG;
83     }
84 }
85 
gl_attach_shader(pl_gpu gpu,GLuint program,GLenum type,const char * src)86 static bool gl_attach_shader(pl_gpu gpu, GLuint program, GLenum type, const char *src)
87 {
88     GLuint shader = glCreateShader(type);
89     glShaderSource(shader, 1, &src, NULL);
90     glCompileShader(shader);
91 
92     GLint status = 0;
93     glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
94     GLint log_length = 0;
95     glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
96 
97     enum pl_log_level level = gl_log_level(status, log_length);
98     if (pl_msg_test(gpu->log, level)) {
99         static const char *shader_name;
100         switch (type) {
101         case GL_VERTEX_SHADER:   shader_name = "vertex"; break;
102         case GL_FRAGMENT_SHADER: shader_name = "fragment"; break;
103         case GL_COMPUTE_SHADER:  shader_name = "compute"; break;
104         default: pl_unreachable();
105         };
106 
107         PL_MSG(gpu, level, "%s shader source:", shader_name);
108         pl_msg_source(gpu->log, level, src);
109 
110         GLchar *logstr = pl_zalloc(NULL, log_length + 1);
111         glGetShaderInfoLog(shader, log_length, NULL, logstr);
112         PL_MSG(gpu, level, "shader compile log (status=%d): %s", status, logstr);
113         pl_free(logstr);
114     }
115 
116     if (!status || !gl_check_err(gpu, "gl_attach_shader"))
117         goto error;
118 
119     glAttachShader(program, shader);
120     glDeleteShader(shader);
121     return true;
122 
123 error:
124     glDeleteShader(shader);
125     return false;
126 }
127 
gl_compile_program(pl_gpu gpu,const struct pl_pass_params * params)128 static GLuint gl_compile_program(pl_gpu gpu, const struct pl_pass_params *params)
129 {
130     GLuint prog = glCreateProgram();
131     bool ok = true;
132 
133     switch (params->type) {
134     case PL_PASS_COMPUTE:
135         ok &= gl_attach_shader(gpu, prog, GL_COMPUTE_SHADER, params->glsl_shader);
136         break;
137     case PL_PASS_RASTER:
138         ok &= gl_attach_shader(gpu, prog, GL_VERTEX_SHADER, params->vertex_shader);
139         ok &= gl_attach_shader(gpu, prog, GL_FRAGMENT_SHADER, params->glsl_shader);
140         for (int i = 0; i < params->num_vertex_attribs; i++)
141             glBindAttribLocation(prog, i, params->vertex_attribs[i].name);
142         break;
143     case PL_PASS_INVALID:
144     case PL_PASS_TYPE_COUNT:
145         pl_unreachable();
146     }
147 
148     if (!ok || !gl_check_err(gpu, "gl_compile_program: attach shader"))
149         goto error;
150 
151     glLinkProgram(prog);
152     GLint status = 0;
153     glGetProgramiv(prog, GL_LINK_STATUS, &status);
154     GLint log_length = 0;
155     glGetProgramiv(prog, GL_INFO_LOG_LENGTH, &log_length);
156 
157     enum pl_log_level level = gl_log_level(status, log_length);
158     if (pl_msg_test(gpu->log, level)) {
159         GLchar *logstr = pl_zalloc(NULL, log_length + 1);
160         glGetProgramInfoLog(prog, log_length, NULL, logstr);
161         PL_MSG(gpu, level, "shader link log (status=%d): %s", status, logstr);
162         pl_free(logstr);
163     }
164 
165     if (!gl_check_err(gpu, "gl_compile_program: link program"))
166         goto error;
167 
168     return prog;
169 
170 error:
171     glDeleteProgram(prog);
172     PL_ERR(gpu, "Failed compiling/linking GLSL program");
173     return 0;
174 }
175 
176 // For pl_pass.priv
177 struct pl_pass_gl {
178     GLuint program;
179     GLuint vao;         // the VAO object
180     uint64_t vao_id;    // buf_gl.id of VAO
181     size_t vao_offset;  // VBO offset of VAO
182     GLuint buffer;      // VBO for raw vertex pointers
183     GLint *var_locs;
184 };
185 
gl_pass_destroy(pl_gpu gpu,pl_pass pass)186 void gl_pass_destroy(pl_gpu gpu, pl_pass pass)
187 {
188     if (!MAKE_CURRENT()) {
189         PL_ERR(gpu, "Failed uninitializing pass, leaking resources!");
190         return;
191     }
192 
193     struct pl_pass_gl *pass_gl = PL_PRIV(pass);
194     if (pass_gl->vao)
195         glDeleteVertexArrays(1, &pass_gl->vao);
196     glDeleteBuffers(1, &pass_gl->buffer);
197     glDeleteProgram(pass_gl->program);
198 
199     gl_check_err(gpu, "gl_pass_destroy");
200     RELEASE_CURRENT();
201     pl_free((void *) pass);
202 }
203 
gl_update_va(pl_pass pass,size_t vbo_offset)204 static void gl_update_va(pl_pass pass, size_t vbo_offset)
205 {
206     for (int i = 0; i < pass->params.num_vertex_attribs; i++) {
207         const struct pl_vertex_attrib *va = &pass->params.vertex_attribs[i];
208         const struct gl_format **glfmtp = PL_PRIV(va->fmt);
209         const struct gl_format *glfmt = *glfmtp;
210 
211         bool norm = false;
212         switch (va->fmt->type) {
213         case PL_FMT_UNORM:
214         case PL_FMT_SNORM:
215             norm = true;
216             break;
217 
218         case PL_FMT_UNKNOWN:
219         case PL_FMT_FLOAT:
220         case PL_FMT_UINT:
221         case PL_FMT_SINT:
222             break;
223         case PL_FMT_TYPE_COUNT:
224             pl_unreachable();
225         }
226 
227         glEnableVertexAttribArray(i);
228         glVertexAttribPointer(i, va->fmt->num_components, glfmt->type, norm,
229                               pass->params.vertex_stride,
230                               (void *) (va->offset + vbo_offset));
231     }
232 }
233 
gl_pass_create(pl_gpu gpu,const struct pl_pass_params * params)234 pl_pass gl_pass_create(pl_gpu gpu, const struct pl_pass_params *params)
235 {
236     if (!MAKE_CURRENT())
237         return NULL;
238 
239     struct pl_gl *p = PL_PRIV(gpu);
240     struct pl_pass *pass = pl_zalloc_obj(NULL, pass, struct pl_pass_gl);
241     struct pl_pass_gl *pass_gl = PL_PRIV(pass);
242     pass->params = pl_pass_params_copy(pass, params);
243 
244     // Load/Compile program
245     if ((pass_gl->program = load_cached_program(gpu, params))) {
246         PL_DEBUG(gpu, "Using cached GL program");
247     } else {
248         clock_t start = clock();
249         pass_gl->program = gl_compile_program(gpu, params);
250         pl_log_cpu_time(gpu->log, start, clock(), "compiling shader");
251     }
252 
253     if (!pass_gl->program)
254         goto error;
255 
256     // Update program cache if possible
257     if (gl_test_ext(gpu, "GL_ARB_get_program_binary", 41, 30)) {
258         GLint size = 0;
259         glGetProgramiv(pass_gl->program, GL_PROGRAM_BINARY_LENGTH, &size);
260 
261         if (size > 0) {
262             uint8_t *buffer = pl_alloc(NULL, size);
263             GLsizei actual_size = 0;
264             struct gl_cache_header header = {
265                 .magic = CACHE_MAGIC,
266                 .cache_version = CACHE_VERSION,
267             };
268 
269             glGetProgramBinary(pass_gl->program, size, &actual_size,
270                                &header.format, buffer);
271             if (actual_size > 0) {
272                 pl_str cache = {0};
273                 pl_str_append(pass, &cache, (pl_str) { (void *) &header, sizeof(header) });
274                 pl_str_append(pass, &cache, (pl_str) { buffer, actual_size });
275                 pass->params.cached_program = cache.buf;
276                 pass->params.cached_program_len = cache.len;
277             }
278 
279             pl_free(buffer);
280         }
281 
282         if (!gl_check_err(gpu, "gl_pass_create: get program binary")) {
283             PL_WARN(gpu, "Failed generating program binary.. ignoring");
284             pl_free((void *) pass->params.cached_program);
285             pass->params.cached_program = NULL;
286             pass->params.cached_program_len = 0;
287         }
288     }
289 
290     glUseProgram(pass_gl->program);
291     pass_gl->var_locs = pl_calloc(pass, params->num_variables, sizeof(GLint));
292 
293     for (int i = 0; i < params->num_variables; i++) {
294         pass_gl->var_locs[i] = glGetUniformLocation(pass_gl->program,
295                                                     params->variables[i].name);
296 
297         // Due to OpenGL API restrictions, we need to ensure that this is a
298         // variable type we can actually *update*. Fortunately, this is easily
299         // checked by virtue of the fact that all legal combinations of
300         // parameters will have a valid GLSL type name
301         if (!pl_var_glsl_type_name(params->variables[i])) {
302             glUseProgram(0);
303             PL_ERR(gpu, "Input variable '%s' does not match any known type!",
304                    params->variables[i].name);
305             goto error;
306         }
307     }
308 
309     for (int i = 0; i < params->num_descriptors; i++) {
310         // For compatibility with older OpenGL, we need to explicitly update
311         // the texture/image unit bindings after creating the shader program,
312         // since specifying it directly requires GLSL 4.20+
313         GLint loc = glGetUniformLocation(pass_gl->program, params->descriptors[i].name);
314         glUniform1i(loc, params->descriptors[i].binding);
315     }
316 
317     glUseProgram(0);
318 
319     // Initialize the VAO and single vertex buffer
320     glGenBuffers(1, &pass_gl->buffer);
321     if (p->has_vao) {
322         glGenVertexArrays(1, &pass_gl->vao);
323         glBindBuffer(GL_ARRAY_BUFFER, pass_gl->buffer);
324         glBindVertexArray(pass_gl->vao);
325         gl_update_va(pass, 0);
326         glBindVertexArray(0);
327         glBindBuffer(GL_ARRAY_BUFFER, 0);
328     }
329 
330     if (!gl_check_err(gpu, "gl_pass_create"))
331         goto error;
332 
333     RELEASE_CURRENT();
334     return pass;
335 
336 error:
337     PL_ERR(gpu, "Failed creating pass");
338     gl_pass_destroy(gpu, pass);
339     RELEASE_CURRENT();
340     return NULL;
341 }
342 
update_var(pl_pass pass,const struct pl_var_update * vu)343 static void update_var(pl_pass pass, const struct pl_var_update *vu)
344 {
345     struct pl_pass_gl *pass_gl = PL_PRIV(pass);
346     const struct pl_var *var = &pass->params.variables[vu->index];
347     GLint loc = pass_gl->var_locs[vu->index];
348 
349     switch (var->type) {
350     case PL_VAR_SINT: {
351         const int *i = vu->data;
352         pl_assert(var->dim_m == 1);
353         switch (var->dim_v) {
354         case 1: glUniform1iv(loc, var->dim_a, i); break;
355         case 2: glUniform2iv(loc, var->dim_a, i); break;
356         case 3: glUniform3iv(loc, var->dim_a, i); break;
357         case 4: glUniform4iv(loc, var->dim_a, i); break;
358         default: pl_unreachable();
359         }
360         return;
361     }
362     case PL_VAR_UINT: {
363         const unsigned int *u = vu->data;
364         pl_assert(var->dim_m == 1);
365         switch (var->dim_v) {
366         case 1: glUniform1uiv(loc, var->dim_a, u); break;
367         case 2: glUniform2uiv(loc, var->dim_a, u); break;
368         case 3: glUniform3uiv(loc, var->dim_a, u); break;
369         case 4: glUniform4uiv(loc, var->dim_a, u); break;
370         default: pl_unreachable();
371         }
372         return;
373     }
374     case PL_VAR_FLOAT: {
375         const float *f = vu->data;
376         if (var->dim_m == 1) {
377             switch (var->dim_v) {
378             case 1: glUniform1fv(loc, var->dim_a, f); break;
379             case 2: glUniform2fv(loc, var->dim_a, f); break;
380             case 3: glUniform3fv(loc, var->dim_a, f); break;
381             case 4: glUniform4fv(loc, var->dim_a, f); break;
382             default: pl_unreachable();
383             }
384         } else if (var->dim_m == 2 && var->dim_v == 2) {
385             glUniformMatrix2fv(loc, var->dim_a, GL_FALSE, f);
386         } else if (var->dim_m == 3 && var->dim_v == 3) {
387             glUniformMatrix3fv(loc, var->dim_a, GL_FALSE, f);
388         } else if (var->dim_m == 4 && var->dim_v == 4) {
389             glUniformMatrix4fv(loc, var->dim_a, GL_FALSE, f);
390         } else if (var->dim_m == 2 && var->dim_v == 3) {
391             glUniformMatrix2x3fv(loc, var->dim_a, GL_FALSE, f);
392         } else if (var->dim_m == 3 && var->dim_v == 2) {
393             glUniformMatrix3x2fv(loc, var->dim_a, GL_FALSE, f);
394         } else if (var->dim_m == 2 && var->dim_v == 4) {
395             glUniformMatrix2x4fv(loc, var->dim_a, GL_FALSE, f);
396         } else if (var->dim_m == 4 && var->dim_v == 2) {
397             glUniformMatrix4x2fv(loc, var->dim_a, GL_FALSE, f);
398         } else if (var->dim_m == 3 && var->dim_v == 4) {
399             glUniformMatrix3x4fv(loc, var->dim_a, GL_FALSE, f);
400         } else if (var->dim_m == 4 && var->dim_v == 3) {
401             glUniformMatrix4x3fv(loc, var->dim_a, GL_FALSE, f);
402         } else {
403             pl_unreachable();
404         }
405         return;
406     }
407 
408     case PL_VAR_INVALID:
409     case PL_VAR_TYPE_COUNT:
410         break;
411     }
412 
413     pl_unreachable();
414 }
415 
update_desc(pl_pass pass,int index,const struct pl_desc_binding * db)416 static void update_desc(pl_pass pass, int index, const struct pl_desc_binding *db)
417 {
418     const struct pl_desc *desc = &pass->params.descriptors[index];
419 
420     static const GLenum access[] = {
421         [PL_DESC_ACCESS_READWRITE] = GL_READ_WRITE,
422         [PL_DESC_ACCESS_READONLY]  = GL_READ_ONLY,
423         [PL_DESC_ACCESS_WRITEONLY] = GL_WRITE_ONLY,
424     };
425 
426     static const GLint wraps[PL_TEX_ADDRESS_MODE_COUNT] = {
427         [PL_TEX_ADDRESS_CLAMP]  = GL_CLAMP_TO_EDGE,
428         [PL_TEX_ADDRESS_REPEAT] = GL_REPEAT,
429         [PL_TEX_ADDRESS_MIRROR] = GL_MIRRORED_REPEAT,
430     };
431 
432     static const GLint filters[PL_TEX_SAMPLE_MODE_COUNT] = {
433         [PL_TEX_SAMPLE_NEAREST] = GL_NEAREST,
434         [PL_TEX_SAMPLE_LINEAR]  = GL_LINEAR,
435     };
436 
437     switch (desc->type) {
438     case PL_DESC_SAMPLED_TEX: {
439         pl_tex tex = db->object;
440         struct pl_tex_gl *tex_gl = PL_PRIV(tex);
441         glActiveTexture(GL_TEXTURE0 + desc->binding);
442         glBindTexture(tex_gl->target, tex_gl->texture);
443 
444         GLint filter = filters[db->sample_mode];
445         GLint wrap = wraps[db->address_mode];
446         glTexParameteri(tex_gl->target, GL_TEXTURE_MIN_FILTER, filter);
447         glTexParameteri(tex_gl->target, GL_TEXTURE_MAG_FILTER, filter);
448         switch (pl_tex_params_dimension(tex->params)) {
449         case 3: glTexParameteri(tex_gl->target, GL_TEXTURE_WRAP_R, wrap);
450             // fall through
451         case 2:
452             glTexParameteri(tex_gl->target, GL_TEXTURE_WRAP_T, wrap);
453             // fall through
454         case 1:
455             glTexParameteri(tex_gl->target, GL_TEXTURE_WRAP_S, wrap);
456             break;
457         }
458         return;
459     }
460     case PL_DESC_STORAGE_IMG: {
461         pl_tex tex = db->object;
462         struct pl_tex_gl *tex_gl = PL_PRIV(tex);
463         glBindImageTexture(desc->binding, tex_gl->texture, 0, GL_FALSE, 0,
464                            access[desc->access], tex_gl->iformat);
465         return;
466     }
467     case PL_DESC_BUF_UNIFORM: {
468         pl_buf buf = db->object;
469         struct pl_buf_gl *buf_gl = PL_PRIV(buf);
470         glBindBufferRange(GL_UNIFORM_BUFFER, desc->binding, buf_gl->buffer,
471                           buf_gl->offset, buf->params.size);
472         return;
473     }
474     case PL_DESC_BUF_STORAGE: {
475         pl_buf buf = db->object;
476         struct pl_buf_gl *buf_gl = PL_PRIV(buf);
477         glBindBufferRange(GL_SHADER_STORAGE_BUFFER, desc->binding, buf_gl->buffer,
478                           buf_gl->offset, buf->params.size);
479         return;
480     }
481     case PL_DESC_BUF_TEXEL_UNIFORM:
482     case PL_DESC_BUF_TEXEL_STORAGE:
483         assert(!"unimplemented"); // TODO
484 
485     case PL_DESC_INVALID:
486     case PL_DESC_TYPE_COUNT:
487         break;
488     }
489 
490     pl_unreachable();
491 }
492 
unbind_desc(pl_pass pass,int index,const struct pl_desc_binding * db)493 static void unbind_desc(pl_pass pass, int index, const struct pl_desc_binding *db)
494 {
495     const struct pl_desc *desc = &pass->params.descriptors[index];
496 
497     switch (desc->type) {
498     case PL_DESC_SAMPLED_TEX: {
499         pl_tex tex = db->object;
500         struct pl_tex_gl *tex_gl = PL_PRIV(tex);
501         glActiveTexture(GL_TEXTURE0 + desc->binding);
502         glBindTexture(tex_gl->target, 0);
503         return;
504     }
505     case PL_DESC_STORAGE_IMG: {
506         pl_tex tex = db->object;
507         struct pl_tex_gl *tex_gl = PL_PRIV(tex);
508         glBindImageTexture(desc->binding, 0, 0, GL_FALSE, 0,
509                            GL_WRITE_ONLY, GL_R32F);
510         if (desc->access != PL_DESC_ACCESS_READONLY)
511             glMemoryBarrier(tex_gl->barrier);
512         return;
513     }
514     case PL_DESC_BUF_UNIFORM:
515         glBindBufferBase(GL_UNIFORM_BUFFER, desc->binding, 0);
516         return;
517     case PL_DESC_BUF_STORAGE: {
518         pl_buf buf = db->object;
519         struct pl_buf_gl *buf_gl = PL_PRIV(buf);
520         glBindBufferBase(GL_SHADER_STORAGE_BUFFER, desc->binding, 0);
521         if (desc->access != PL_DESC_ACCESS_READONLY)
522             glMemoryBarrier(buf_gl->barrier);
523         return;
524     }
525     case PL_DESC_BUF_TEXEL_UNIFORM:
526     case PL_DESC_BUF_TEXEL_STORAGE:
527         assert(!"unimplemented"); // TODO
528     case PL_DESC_INVALID:
529     case PL_DESC_TYPE_COUNT:
530         break;
531     }
532 
533     pl_unreachable();
534 }
535 
gl_pass_run(pl_gpu gpu,const struct pl_pass_run_params * params)536 void gl_pass_run(pl_gpu gpu, const struct pl_pass_run_params *params)
537 {
538     if (!MAKE_CURRENT())
539         return;
540 
541     pl_pass pass = params->pass;
542     struct pl_pass_gl *pass_gl = PL_PRIV(pass);
543     struct pl_gl *p = PL_PRIV(gpu);
544 
545     glUseProgram(pass_gl->program);
546 
547     for (int i = 0; i < params->num_var_updates; i++)
548         update_var(pass, &params->var_updates[i]);
549     for (int i = 0; i < pass->params.num_descriptors; i++)
550         update_desc(pass, i, &params->desc_bindings[i]);
551     glActiveTexture(GL_TEXTURE0);
552 
553     if (!gl_check_err(gpu, "gl_pass_run: updating uniforms")) {
554         RELEASE_CURRENT();
555         return;
556     }
557 
558     switch (pass->params.type) {
559     case PL_PASS_RASTER: {
560         struct pl_tex_gl *target_gl = PL_PRIV(params->target);
561         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, target_gl->fbo);
562         if (!pass->params.load_target && p->has_invalidate_fb) {
563             GLenum fb = target_gl->fbo ? GL_COLOR_ATTACHMENT0 : GL_COLOR;
564             glInvalidateFramebuffer(GL_DRAW_FRAMEBUFFER, 1, &fb);
565         }
566 
567         glViewport(params->viewport.x0, params->viewport.y0,
568                    pl_rect_w(params->viewport), pl_rect_h(params->viewport));
569         glScissor(params->scissors.x0, params->scissors.y0,
570                   pl_rect_w(params->scissors), pl_rect_h(params->scissors));
571         glEnable(GL_SCISSOR_TEST);
572         gl_check_err(gpu, "gl_pass_run: enabling viewport/scissor");
573 
574         const struct pl_blend_params *blend = pass->params.blend_params;
575         if (blend) {
576             static const GLenum map_blend[] = {
577                 [PL_BLEND_ZERO]                 = GL_ZERO,
578                 [PL_BLEND_ONE]                  = GL_ONE,
579                 [PL_BLEND_SRC_ALPHA]            = GL_SRC_ALPHA,
580                 [PL_BLEND_ONE_MINUS_SRC_ALPHA]  = GL_ONE_MINUS_SRC_ALPHA,
581             };
582 
583             glBlendFuncSeparate(map_blend[blend->src_rgb],
584                                 map_blend[blend->dst_rgb],
585                                 map_blend[blend->src_alpha],
586                                 map_blend[blend->dst_alpha]);
587             glEnable(GL_BLEND);
588         }
589         gl_check_err(gpu, "gl_pass_run: enabling blend");
590 
591         // Update VBO and VAO
592         pl_buf vert = params->vertex_buf;
593         struct pl_buf_gl *vert_gl = vert ? PL_PRIV(vert) : NULL;
594         glBindBuffer(GL_ARRAY_BUFFER, vert ? vert_gl->buffer : pass_gl->buffer);
595 
596         if (!vert) {
597             // Update the buffer directly. In theory we could also do a memcmp
598             // cache here to avoid unnecessary updates.
599             int num_vertices = 0;
600             if (params->index_data) {
601                 // Indexed draw, so we need to store all indexed vertices
602                 for (int i = 0; i < params->vertex_count; i++)
603                     num_vertices = PL_MAX(num_vertices, params->index_data[i]);
604                 num_vertices += 1;
605             } else {
606                 num_vertices = params->vertex_count;
607             }
608             size_t vert_size = num_vertices * pass->params.vertex_stride;
609             glBufferData(GL_ARRAY_BUFFER, vert_size, params->vertex_data, GL_STREAM_DRAW);
610         }
611 
612         if (pass_gl->vao)
613             glBindVertexArray(pass_gl->vao);
614 
615         uint64_t vert_id = vert ? vert_gl->id : 0;
616         size_t vert_offset = vert ? params->buf_offset : 0;
617         if (!pass_gl->vao || pass_gl->vao_id != vert_id ||
618              pass_gl->vao_offset != vert_offset)
619         {
620             // We need to update the VAO when the buffer ID or offset changes
621             gl_update_va(pass, vert_offset);
622             pass_gl->vao_id = vert_id;
623             pass_gl->vao_offset = vert_offset;
624         }
625 
626         gl_check_err(gpu, "gl_pass_run: update/bind vertex buffer");
627 
628         static const GLenum map_prim[PL_PRIM_TYPE_COUNT] = {
629             [PL_PRIM_TRIANGLE_LIST]     = GL_TRIANGLES,
630             [PL_PRIM_TRIANGLE_STRIP]    = GL_TRIANGLE_STRIP,
631         };
632         GLenum mode = map_prim[pass->params.vertex_type];
633 
634         gl_timer_begin(params->timer);
635 
636         if (params->index_data) {
637 
638             // GL allows taking indices directly from a pointer
639             glDrawElements(mode, params->vertex_count, GL_UNSIGNED_SHORT,
640                            params->index_data);
641 
642         } else if (params->index_buf) {
643 
644             // The pointer argument becomes the index buffer offset
645             struct pl_buf_gl *index_gl = PL_PRIV(params->index_buf);
646             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, index_gl->buffer);
647             glDrawElements(mode, params->vertex_count, GL_UNSIGNED_SHORT,
648                            (void *) params->index_offset);
649             glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
650 
651         } else {
652 
653             // Note: the VBO offset is handled in the VAO
654             glDrawArrays(mode, 0, params->vertex_count);
655         }
656 
657         gl_timer_end(params->timer);
658         gl_check_err(gpu, "gl_pass_run: drawing");
659 
660         if (pass_gl->vao) {
661             glBindVertexArray(0);
662         } else {
663             for (int i = 0; i < pass->params.num_vertex_attribs; i++)
664                 glDisableVertexAttribArray(i);
665         }
666 
667         glBindBuffer(GL_ARRAY_BUFFER, 0);
668         glDisable(GL_SCISSOR_TEST);
669         glDisable(GL_BLEND);
670         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
671         break;
672     }
673 
674     case PL_PASS_COMPUTE:
675         gl_timer_begin(params->timer);
676         glDispatchCompute(params->compute_groups[0],
677                           params->compute_groups[1],
678                           params->compute_groups[2]);
679         gl_timer_end(params->timer);
680         break;
681 
682     case PL_PASS_INVALID:
683     case PL_PASS_TYPE_COUNT:
684         pl_unreachable();
685     }
686 
687     for (int i = 0; i < pass->params.num_descriptors; i++)
688         unbind_desc(pass, i, &params->desc_bindings[i]);
689     glActiveTexture(GL_TEXTURE0);
690 
691     glUseProgram(0);
692     gl_check_err(gpu, "gl_pass_run");
693     RELEASE_CURRENT();
694 }
695