summaryrefslogtreecommitdiffstats
path: root/video/out/opengl/utils.c
diff options
context:
space:
mode:
Diffstat (limited to 'video/out/opengl/utils.c')
-rw-r--r--video/out/opengl/utils.c1489
1 files changed, 24 insertions, 1465 deletions
diff --git a/video/out/opengl/utils.c b/video/out/opengl/utils.c
index 239009d4ac..6848e02ddc 100644
--- a/video/out/opengl/utils.c
+++ b/video/out/opengl/utils.c
@@ -1,256 +1,35 @@
-/*
- * This file is part of mpv.
- * Parts based on MPlayer code by Reimar Döffinger.
- *
- * mpv is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 2.1 of the License, or (at your option) any later version.
- *
- * mpv is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <assert.h>
-
-#include <libavutil/sha.h>
-#include <libavutil/intreadwrite.h>
-#include <libavutil/mem.h>
-
-#include "osdep/io.h"
-
-#include "common/common.h"
-#include "options/path.h"
-#include "stream/stream.h"
-#include "formats.h"
-#include "ra_gl.h"
+#include "common/msg.h"
#include "utils.h"
-// GLU has this as gluErrorString (we don't use GLU, as it is legacy-OpenGL)
-static const char *gl_error_to_string(GLenum error)
-{
- switch (error) {
- case GL_INVALID_ENUM: return "INVALID_ENUM";
- case GL_INVALID_VALUE: return "INVALID_VALUE";
- case GL_INVALID_OPERATION: return "INVALID_OPERATION";
- case GL_INVALID_FRAMEBUFFER_OPERATION: return "INVALID_FRAMEBUFFER_OPERATION";
- case GL_OUT_OF_MEMORY: return "OUT_OF_MEMORY";
- default: return "unknown";
- }
-}
-
-void gl_check_error(GL *gl, struct mp_log *log, const char *info)
-{
- for (;;) {
- GLenum error = gl->GetError();
- if (error == GL_NO_ERROR)
- break;
- mp_msg(log, MSGL_ERR, "%s: OpenGL error %s.\n", info,
- gl_error_to_string(error));
- }
-}
-
-static int get_alignment(int stride)
-{
- if (stride % 8 == 0)
- return 8;
- if (stride % 4 == 0)
- return 4;
- if (stride % 2 == 0)
- return 2;
- return 1;
-}
-
-// upload a texture, handling things like stride and slices
-// target: texture target, usually GL_TEXTURE_2D
-// format, type: texture parameters
-// dataptr, stride: image data
-// x, y, width, height: part of the image to upload
-void gl_upload_tex(GL *gl, GLenum target, GLenum format, GLenum type,
- const void *dataptr, int stride,
- int x, int y, int w, int h)
-{
- int bpp = gl_bytes_per_pixel(format, type);
- const uint8_t *data = dataptr;
- int y_max = y + h;
- if (w <= 0 || h <= 0 || !bpp)
- return;
- if (stride < 0) {
- data += (h - 1) * stride;
- stride = -stride;
- }
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
- int slice = h;
- if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH) {
- // this is not always correct, but should work for MPlayer
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / bpp);
- } else {
- if (stride != bpp * w)
- slice = 1; // very inefficient, but at least it works
- }
- for (; y + slice <= y_max; y += slice) {
- gl->TexSubImage2D(target, 0, x, y, w, slice, format, type, data);
- data += stride * slice;
- }
- if (y < y_max)
- gl->TexSubImage2D(target, 0, x, y, w, y_max - y, format, type, data);
- if (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH)
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
-}
-
-mp_image_t *gl_read_fbo_contents(GL *gl, int fbo, int w, int h)
-{
- if (gl->es)
- return NULL; // ES can't read from front buffer
- mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, w, h);
- if (!image)
- return NULL;
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo);
- GLenum obj = fbo ? GL_COLOR_ATTACHMENT0 : GL_FRONT;
- gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
- gl->ReadBuffer(obj);
- //flip image while reading (and also avoid stride-related trouble)
- for (int y = 0; y < h; y++) {
- gl->ReadPixels(0, h - y - 1, w, 1, GL_RGB, GL_UNSIGNED_BYTE,
- image->planes[0] + y * image->stride[0]);
- }
- gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
- return image;
-}
-
-void mp_log_source(struct mp_log *log, int lev, const char *src)
-{
- int line = 1;
- if (!src)
- return;
- while (*src) {
- const char *end = strchr(src, '\n');
- const char *next = end + 1;
- if (!end)
- next = end = src + strlen(src);
- mp_msg(log, lev, "[%3d] %.*s\n", line, (int)(end - src), src);
- line++;
- src = next;
- }
-}
-
-
-struct gl_vao {
- GL *gl;
- GLuint vao; // the VAO object, or 0 if unsupported by driver
- GLuint buffer; // GL_ARRAY_BUFFER used for the data
- int stride; // size of each element (interleaved elements are assumed)
- const struct gl_vao_entry *entries;
-};
-
-static void gl_vao_enable_attribs(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- for (int n = 0; vao->entries[n].name; n++) {
- const struct gl_vao_entry *e = &vao->entries[n];
-
- gl->EnableVertexAttribArray(n);
- gl->VertexAttribPointer(n, e->num_elems, e->type, e->normalized,
- vao->stride, (void *)(intptr_t)e->offset);
- }
-}
-
-static void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
- const struct gl_vao_entry *entries)
-{
- assert(!vao->vao);
- assert(!vao->buffer);
-
- *vao = (struct gl_vao){
- .gl = gl,
- .stride = stride,
- .entries = entries,
- };
-
- gl->GenBuffers(1, &vao->buffer);
-
- if (gl->BindVertexArray) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
-
- gl->GenVertexArrays(1, &vao->vao);
- gl->BindVertexArray(vao->vao);
- gl_vao_enable_attribs(vao);
- gl->BindVertexArray(0);
-
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-}
-
-static void gl_vao_uninit(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
- if (!gl)
- return;
-
- if (gl->DeleteVertexArrays)
- gl->DeleteVertexArrays(1, &vao->vao);
- gl->DeleteBuffers(1, &vao->buffer);
-
- *vao = (struct gl_vao){0};
-}
-
-static void gl_vao_bind(struct gl_vao *vao)
+// Standard parallel 2D projection, except y1 < y0 means that the coordinate
+// system is flipped, not the projection.
+void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
+ float y0, float y1)
{
- GL *gl = vao->gl;
-
- if (gl->BindVertexArray) {
- gl->BindVertexArray(vao->vao);
- } else {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl_vao_enable_attribs(vao);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
+ if (y1 < y0) {
+ float tmp = y0;
+ y0 = tmp - y1;
+ y1 = tmp;
}
-}
-static void gl_vao_unbind(struct gl_vao *vao)
-{
- GL *gl = vao->gl;
-
- if (gl->BindVertexArray) {
- gl->BindVertexArray(0);
- } else {
- for (int n = 0; vao->entries[n].name; n++)
- gl->DisableVertexAttribArray(n);
- }
+ t->m[0][0] = 2.0f / (x1 - x0);
+ t->m[0][1] = 0.0f;
+ t->m[1][0] = 0.0f;
+ t->m[1][1] = 2.0f / (y1 - y0);
+ t->t[0] = -(x1 + x0) / (x1 - x0);
+ t->t[1] = -(y1 + y0) / (y1 - y0);
}
-// Draw the vertex data (as described by the gl_vao_entry entries) in ptr
-// to the screen. num is the number of vertexes. prim is usually GL_TRIANGLES.
-// If ptr is NULL, then skip the upload, and use the data uploaded with the
-// previous call.
-static void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
+// Apply the effects of one transformation to another, transforming it in the
+// process. In other words: post-composes t onto x
+void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
{
- GL *gl = vao->gl;
-
- if (ptr) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_STREAM_DRAW);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- gl_vao_bind(vao);
-
- gl->DrawArrays(prim, 0, num);
-
- gl_vao_unbind(vao);
+ struct gl_transform xt = *x;
+ x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
+ x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
+ x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
+ x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
+ gl_transform_vec(t, &x->t[0], &x->t[1]);
}
// Create a texture and a FBO using the texture as color attachments.
@@ -339,1223 +118,3 @@ void fbotex_uninit(struct fbotex *fbo)
*fbo = (struct fbotex) {0};
}
}
-
-// Standard parallel 2D projection, except y1 < y0 means that the coordinate
-// system is flipped, not the projection.
-void gl_transform_ortho(struct gl_transform *t, float x0, float x1,
- float y0, float y1)
-{
- if (y1 < y0) {
- float tmp = y0;
- y0 = tmp - y1;
- y1 = tmp;
- }
-
- t->m[0][0] = 2.0f / (x1 - x0);
- t->m[0][1] = 0.0f;
- t->m[1][0] = 0.0f;
- t->m[1][1] = 2.0f / (y1 - y0);
- t->t[0] = -(x1 + x0) / (x1 - x0);
- t->t[1] = -(y1 + y0) / (y1 - y0);
-}
-
-// Apply the effects of one transformation to another, transforming it in the
-// process. In other words: post-composes t onto x
-void gl_transform_trans(struct gl_transform t, struct gl_transform *x)
-{
- struct gl_transform xt = *x;
- x->m[0][0] = t.m[0][0] * xt.m[0][0] + t.m[0][1] * xt.m[1][0];
- x->m[1][0] = t.m[1][0] * xt.m[0][0] + t.m[1][1] * xt.m[1][0];
- x->m[0][1] = t.m[0][0] * xt.m[0][1] + t.m[0][1] * xt.m[1][1];
- x->m[1][1] = t.m[1][0] * xt.m[0][1] + t.m[1][1] * xt.m[1][1];
- gl_transform_vec(t, &x->t[0], &x->t[1]);
-}
-
-static void GLAPIENTRY gl_debug_cb(GLenum source, GLenum type, GLuint id,
- GLenum severity, GLsizei length,
- const GLchar *message, const void *userParam)
-{
- // keep in mind that the debug callback can be asynchronous
- struct mp_log *log = (void *)userParam;
- int level = MSGL_ERR;
- switch (severity) {
- case GL_DEBUG_SEVERITY_NOTIFICATION:level = MSGL_V; break;
- case GL_DEBUG_SEVERITY_LOW: level = MSGL_INFO; break;
- case GL_DEBUG_SEVERITY_MEDIUM: level = MSGL_WARN; break;
- case GL_DEBUG_SEVERITY_HIGH: level = MSGL_ERR; break;
- }
- mp_msg(log, level, "GL: %s\n", message);
-}
-
-void gl_set_debug_logger(GL *gl, struct mp_log *log)
-{
- if (gl->DebugMessageCallback)
- gl->DebugMessageCallback(log ? gl_debug_cb : NULL, log);
-}
-
-// Force cache flush if more than this number of shaders is created.
-#define SC_MAX_ENTRIES 48
-
-enum uniform_type {
- UT_invalid,
- UT_i,
- UT_f,
- UT_m,
-};
-
-union uniform_val {
- GLfloat f[9];
- GLint i[4];
-};
-
-struct sc_uniform {
- char *name;
- enum uniform_type type;
- const char *glsl_type;
- int size;
- GLint loc;
- union uniform_val v;
- // Set for sampler uniforms.
- GLenum tex_target;
- GLuint tex_handle;
- // Set for image uniforms
- GLuint img_handle;
- GLenum img_access;
- GLenum img_iformat;
-};
-
-struct sc_buffer {
- char *name;
- char *format;
- GLuint binding;
- GLuint ssbo;
-};
-
-struct sc_cached_uniform {
- GLint loc;
- union uniform_val v;
-};
-
-struct sc_entry {
- GLuint gl_shader;
- struct sc_cached_uniform *uniforms;
- int num_uniforms;
- bstr frag;
- bstr vert;
- bstr comp;
- struct gl_timer *timer;
- struct gl_vao vao;
-};
-
-struct gl_shader_cache {
- GL *gl;
- struct mp_log *log;
-
- // permanent
- char **exts;
- int num_exts;
-
- // this is modified during use (gl_sc_add() etc.) and reset for each shader
- bstr prelude_text;
- bstr header_text;
- bstr text;
- int next_texture_unit;
- int next_image_unit;
- int next_buffer_binding;
- struct gl_vao *vao; // deprecated
-
- struct sc_entry *entries;
- int num_entries;
-
- struct sc_entry *current_shader; // set by gl_sc_generate()
-
- struct sc_uniform *uniforms;
- int num_uniforms;
- struct sc_buffer *buffers;
- int num_buffers;
-
- const struct gl_vao_entry *vertex_entries;
- size_t vertex_size;
-
- // For checking that the user is calling gl_sc_reset() properly.
- bool needs_reset;
-
- bool error_state; // true if an error occurred
-
- // temporary buffers (avoids frequent reallocations)
- bstr tmp[5];
-
- // For the disk-cache.
- char *cache_dir;
- struct mpv_global *global; // can be NULL
-};
-
-struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log)
-{
- struct gl_shader_cache *sc = talloc_ptrtype(NULL, sc);
- *sc = (struct gl_shader_cache){
- .gl = gl,
- .log = log,
- };
- gl_sc_reset(sc);
- return sc;
-}
-
-// Reset the previous pass. This must be called after
-// Unbind all GL state managed by sc - the current program and texture units.
-void gl_sc_reset(struct gl_shader_cache *sc)
-{
- GL *gl = sc->gl;
-
- if (sc->needs_reset) {
- gl_timer_stop(gl);
- gl->UseProgram(0);
-
- for (int n = 0; n < sc->num_uniforms; n++) {
- struct sc_uniform *u = &sc->uniforms[n];
- if (u->type == UT_i && u->tex_target) {
- gl->ActiveTexture(GL_TEXTURE0 + u->v.i[0]);
- gl->BindTexture(u->tex_target, 0);
- }
- if (u->type == UT_i && u->img_access) {
- gl->BindImageTexture(u->v.i[0], 0, 0, GL_FALSE, 0,
- u->img_access, u->img_iformat);
- }
- }
- gl->ActiveTexture(GL_TEXTURE0);
-
- for (int n = 0; n < sc->num_buffers; n++) {
- struct sc_buffer *b = &sc->buffers[n];
- gl->BindBufferBase(GL_SHADER_STORAGE_BUFFER, b->binding, 0);
- }
- }
-
- sc->prelude_text.len = 0;
- sc->header_text.len = 0;
- sc->text.len = 0;
- for (int n = 0; n < sc->num_uniforms; n++)
- talloc_free(sc->uniforms[n].name);
- sc->num_uniforms = 0;
- for (int n = 0; n < sc->num_buffers; n++) {
- talloc_free(sc->buffers[n].name);
- talloc_free(sc->buffers[n].format);
- }
- sc->num_buffers = 0;
- sc->next_texture_unit = 1; // not 0, as 0 is "free for use"
- sc->next_image_unit = 1;
- sc->next_buffer_binding = 1;
- sc->vertex_entries = NULL;
- sc->vertex_size = 0;
- sc->current_shader = NULL;
- sc->needs_reset = false;
-}
-
-static void sc_flush_cache(struct gl_shader_cache *sc)
-{
- MP_VERBOSE(sc, "flushing shader cache\n");
-
- for (int n = 0; n < sc->num_entries; n++) {
- struct sc_entry *e = &sc->entries[n];
- sc->gl->DeleteProgram(e->gl_shader);
- talloc_free(e->vert.start);
- talloc_free(e->frag.start);
- talloc_free(e->comp.start);
- talloc_free(e->uniforms);
- gl_timer_free(e->timer);
- gl_vao_uninit(&e->vao);
- }
- sc->num_entries = 0;
-}
-
-void gl_sc_destroy(struct gl_shader_cache *sc)
-{
- if (!sc)
- return;
- gl_sc_reset(sc);
- sc_flush_cache(sc);
- talloc_free(sc);
-}
-
-bool gl_sc_error_state(struct gl_shader_cache *sc)
-{
- return sc->error_state;
-}
-
-void gl_sc_reset_error(struct gl_shader_cache *sc)
-{
- sc->error_state = false;
-}
-
-void gl_sc_enable_extension(struct gl_shader_cache *sc, char *name)
-{
- for (int n = 0; n < sc->num_exts; n++) {
- if (strcmp(sc->exts[n], name) == 0)
- return;
- }
- MP_TARRAY_APPEND(sc, sc->exts, sc->num_exts, talloc_strdup(sc, name));
-}
-
-#define bstr_xappend0(sc, b, s) bstr_xappend(sc, b, bstr0(s))
-
-void gl_sc_add(struct gl_shader_cache *sc, const char *text)
-{
- bstr_xappend0(sc, &sc->text, text);
-}
-
-void gl_sc_addf(struct gl_shader_cache *sc, const char *textf, ...)
-{
- va_list ap;
- va_start(ap, textf);
- bstr_xappend_vasprintf(sc, &sc->text, textf, ap);
- va_end(ap);
-}
-
-void gl_sc_hadd(struct gl_shader_cache *sc, const char *text)
-{
- bstr_xappend0(sc, &sc->header_text, text);
-}
-
-void gl_sc_haddf(struct gl_shader_cache *sc, const char *textf, ...)
-{
- va_list ap;
- va_start(ap, textf);
- bstr_xappend_vasprintf(sc, &sc->header_text, textf, ap);
- va_end(ap);
-}
-
-void gl_sc_hadd_bstr(struct gl_shader_cache *sc, struct bstr text)
-{
- bstr_xappend(sc, &sc->header_text, text);
-}
-
-void gl_sc_paddf(struct gl_shader_cache *sc, const char *textf, ...)
-{
- va_list ap;
- va_start(ap, textf);
- bstr_xappend_vasprintf(sc, &sc->prelude_text, textf, ap);
- va_end(ap);
-}
-
-static struct sc_uniform *find_uniform(struct gl_shader_cache *sc,
- const char *name)
-{
- for (int n = 0; n < sc->num_uniforms; n++) {
- if (strcmp(sc->uniforms[n].name, name) == 0)
- return &sc->uniforms[n];
- }
- // not found -> add it
- struct sc_uniform new = {
- .loc = -1,
- .name = talloc_strdup(NULL, name),
- };
- MP_TARRAY_APPEND(sc, sc->uniforms, sc->num_uniforms, new);
- return &sc->uniforms[sc->num_uniforms - 1];
-}
-
-static struct sc_buffer *find_buffer(struct gl_shader_cache *sc,
- const char *name)
-{
- for (int n = 0; n < sc->num_buffers; n++) {
- if (strcmp(sc->buffers[n].name, name) == 0)
- return &sc->buffers[n];
- }
- // not found -> add it
- struct sc_buffer new = {
- .name = talloc_strdup(NULL, name),
- };
- MP_TARRAY_APPEND(sc, sc->buffers, sc->num_buffers, new);
- return &sc->buffers[sc->num_buffers - 1];
-}
-
-const char *mp_sampler_type(GLenum texture_target)
-{
- switch (texture_target) {
- case GL_TEXTURE_1D: return "sampler1D";
- case GL_TEXTURE_2D: return "sampler2D";
- case GL_TEXTURE_RECTANGLE: return "sampler2DRect";
- case GL_TEXTURE_EXTERNAL_OES: return "samplerExternalOES";
- case GL_TEXTURE_3D: return "sampler3D";
- default: abort();
- }
-}
-
-void gl_sc_uniform_tex(struct gl_shader_cache *sc, char *name, GLenum target,
- GLuint texture)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_i;
- u->size = 1;
- u->glsl_type = mp_sampler_type(target);
- u->v.i[0] = sc->next_texture_unit++;
- u->tex_target = target;
- u->tex_handle = texture;
-}
-
-void gl_sc_uniform_tex_ui(struct gl_shader_cache *sc, char *name, GLuint texture)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_i;
- u->size = 1;
- u->glsl_type = sc->gl->es ? "highp usampler2D" : "usampler2D";
- u->v.i[0] = sc->next_texture_unit++;
- u->tex_target = GL_TEXTURE_2D;
- u->tex_handle = texture;
-}
-
-void gl_sc_uniform_texture(struct gl_shader_cache *sc, char *name,
- struct ra_tex *tex)
-{
- struct ra_tex_gl *tex_gl = tex->priv;
- if (tex->params.format->ctype == RA_CTYPE_UINT) {
- gl_sc_uniform_tex_ui(sc, name, tex_gl->texture);
- } else {
- gl_sc_uniform_tex(sc, name, tex_gl->target, tex_gl->texture);
- }
-}
-
-static const char *mp_image2D_type(GLenum access)
-{
- switch (access) {
- case GL_WRITE_ONLY: return "writeonly image2D";
- case GL_READ_ONLY: return "readonly image2D";
- case GL_READ_WRITE: return "image2D";
- default: abort();
- }
-}
-
-void gl_sc_uniform_image2D(struct gl_shader_cache *sc, const char *name,
- GLuint texture, GLuint iformat, GLenum access)
-{
- gl_sc_enable_extension(sc, "GL_ARB_shader_image_load_store");
-
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_i;
- u->size = 1;
- u->glsl_type = mp_image2D_type(access);
- u->v.i[0] = sc->next_image_unit++;
- u->img_handle = texture;
- u->img_access = access;
- u->img_iformat = iformat;
-}
-
-void gl_sc_ssbo(struct gl_shader_cache *sc, char *name, GLuint ssbo,
- char *format, ...)
-{
- gl_sc_enable_extension(sc, "GL_ARB_shader_storage_buffer_object");
-
- struct sc_buffer *b = find_buffer(sc, name);
- b->binding = sc->next_buffer_binding++;
- b->ssbo = ssbo;
- b->format = format;
-
- va_list ap;
- va_start(ap, format);
- b->format = ta_vasprintf(sc, format, ap);
- va_end(ap);
-}
-
-void gl_sc_uniform_f(struct gl_shader_cache *sc, char *name, GLfloat f)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_f;
- u->size = 1;
- u->glsl_type = "float";
- u->v.f[0] = f;
-}
-
-void gl_sc_uniform_i(struct gl_shader_cache *sc, char *name, GLint i)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_i;
- u->size = 1;
- u->glsl_type = "int";
- u->v.i[0] = i;
-}
-
-void gl_sc_uniform_vec2(struct gl_shader_cache *sc, char *name, GLfloat f[2])
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_f;
- u->size = 2;
- u->glsl_type = "vec2";
- u->v.f[0] = f[0];
- u->v.f[1] = f[1];
-}
-
-void gl_sc_uniform_vec3(struct gl_shader_cache *sc, char *name, GLfloat f[3])
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_f;
- u->size = 3;
- u->glsl_type = "vec3";
- u->v.f[0] = f[0];
- u->v.f[1] = f[1];
- u->v.f[2] = f[2];
-}
-
-static void transpose2x2(float r[2 * 2])
-{
- MPSWAP(float, r[0+2*1], r[1+2*0]);
-}
-
-void gl_sc_uniform_mat2(struct gl_shader_cache *sc, char *name,
- bool transpose, GLfloat *v)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_m;
- u->size = 2;
- u->glsl_type = "mat2";
- for (int n = 0; n < 4; n++)
- u->v.f[n] = v[n];
- if (transpose)
- transpose2x2(&u->v.f[0]);
-}
-
-static void transpose3x3(float r[3 * 3])
-{
- MPSWAP(float, r[0+3*1], r[1+3*0]);
- MPSWAP(float, r[0+3*2], r[2+3*0]);
- MPSWAP(float, r[1+3*2], r[2+3*1]);
-}
-
-void gl_sc_uniform_mat3(struct gl_shader_cache *sc, char *name,
- bool transpose, GLfloat *v)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_m;
- u->size = 3;
- u->glsl_type = "mat3";
- for (int n = 0; n < 9; n++)
- u->v.f[n] = v[n];
- if (transpose)
- transpose3x3(&u->v.f[0]);
-}
-
-// Tell the shader generator (and later gl_sc_draw_data()) about the vertex
-// data layout and attribute names. The entries array is terminated with a {0}
-// entry. The array memory must remain valid indefinitely (for now).
-void gl_sc_set_vertex_format(struct gl_shader_cache *sc,
- const struct gl_vao_entry *entries,
- size_t vertex_size)
-{
- sc->vertex_entries = entries;
- sc->vertex_size = vertex_size;
-}
-
-static const char *vao_glsl_type(const struct gl_vao_entry *e)
-{
- // pretty dumb... too dumb, but works for us
- switch (e->num_elems) {
- case 1: return "float";
- case 2: return "vec2";
- case 3: return "vec3";
- case 4: return "vec4";
- default: abort();
- }
-}
-
-// Assumes program is current (gl->UseProgram(program)).
-static void update_uniform(GL *gl, struct sc_entry *e, struct sc_uniform *u, int n)
-{
- struct sc_cached_uniform *un = &e->uniforms[n];
- GLint loc = un->loc;
- if (loc < 0)
- return;
- switch (u->type) {
- case UT_i:
- assert(u->size == 1);
- if (memcmp(un->v.i, u->v.i, sizeof(u->v.i)) != 0) {
- memcpy(un->v.i, u->v.i, sizeof(u->v.i));
- gl->Uniform1i(loc, u->v.i[0]);
- }
- // For samplers: set the actual texture.
- if (u->tex_target) {
- gl->ActiveTexture(GL_TEXTURE0 + u->v.i[0]);
- gl->BindTexture(u->tex_target, u->tex_handle);
- }
- if (u->img_handle) {
- gl->BindImageTexture(u->v.i[0], u->img_handle, 0, GL_FALSE, 0,
- u->img_access, u->img_iformat);
- }
- break;
- case UT_f:
- if (memcmp(un->v.f, u->v.f, sizeof(u->v.f)) != 0) {
- memcpy(un->v.f, u->v.f, sizeof(u->v.f));
- switch (u->size) {
- case 1: gl->Uniform1f(loc, u->v.f[0]); break;
- case 2: gl->Uniform2f(loc, u->v.f[0], u->v.f[1]); break;
- case 3: gl->Uniform3f(loc, u->v.f[0], u->v.f[1], u->v.f[2]); break;
- case 4: gl->Uniform4f(loc, u->v.f[0], u->v.f[1], u->v.f[2],
- u->v.f[3]); break;
- default: abort();
- }
- }
- break;
- case UT_m:
- if (memcmp(un->v.f, u->v.f, sizeof(u->v.f)) != 0) {
- memcpy(un->v.f, u->v.f, sizeof(u->v.f));
- switch (u->size) {
- case 2: gl->UniformMatrix2fv(loc, 1, GL_FALSE, &u->v.f[0]); break;
- case 3: gl->UniformMatrix3fv(loc, 1, GL_FALSE, &u->v.f[0]); break;
- default: abort();
- }
- }
- break;
- default:
- abort();
- }
-}
-
-void gl_sc_set_cache_dir(struct gl_shader_cache *sc, struct mpv_global *global,
- const char *dir)
-{
- talloc_free(sc->cache_dir);
- sc->cache_dir = talloc_strdup(sc, dir);
- sc->global = global;
-}
-
-static const char *shader_typestr(GLenum type)
-{
- switch (type) {
- case GL_VERTEX_SHADER: return "vertex";
- case GL_FRAGMENT_SHADER: return "fragment";
- case GL_COMPUTE_SHADER: return "compute";
- default: abort();
- }
-}
-
-static void compile_attach_shader(struct gl_shader_cache *sc, GLuint program,
- GLenum type, const char *source)
-{
- GL *gl = sc->gl;
-
- GLuint shader = gl->CreateShader(type);
- gl->ShaderSource(shader, 1, &source, NULL);
- gl->CompileShader(shader);
- GLint status = 0;
- gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
- GLint log_length = 0;
- gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
-
- int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
- const char *typestr = shader_typestr(type);
- if (mp_msg_test(sc->log, pri)) {
- MP_MSG(sc, pri, "%s shader source:\n", typestr);
- mp_log_source(sc->log, pri, source);
- }
- if (log_length > 1) {
- GLchar *logstr = talloc_zero_size(NULL, log_length + 1);
- gl->GetShaderInfoLog(shader, log_length, NULL, logstr);
- MP_MSG(sc, pri, "%s shader compile log (status=%d):\n%s\n",
- typestr, status, logstr);
- talloc_free(logstr);
- }
- if (gl->GetTranslatedShaderSourceANGLE && mp_msg_test(sc->log, MSGL_DEBUG)) {
- GLint len = 0;
- gl->GetShaderiv(shader, GL_TRANSLATED_SHADER_SOURCE_LENGTH_ANGLE, &len);
- if (len > 0) {
- GLchar *sstr = talloc_zero_size(NULL, len + 1);
- gl->GetTranslatedShaderSourceANGLE(shader, len, NULL, sstr);
- MP_DBG(sc, "Translated shader:\n");
- mp_log_source(sc->log, MSGL_DEBUG, sstr);
- }
- }
-
- gl->AttachShader(program, shader);
- gl->DeleteShader(shader);
-
- if (!status)
- sc->error_state = true;
-}
-
-static void link_shader(struct gl_shader_cache *sc, GLuint program)
-{
- GL *gl = sc->gl;
- gl->LinkProgram(program);
- GLint status = 0;
- gl->GetProgramiv(program, GL_LINK_STATUS, &status);
- GLint log_length = 0;
- gl->GetProgramiv(program, GL_INFO_LOG_LENGTH, &log_length);
-
- int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
- if (mp_msg_test(sc->log, pri)) {
- GLchar *logstr = talloc_zero_size(NULL, log_length + 1);
- gl->GetProgramInfoLog(program, log_length, NULL, logstr);
- MP_MSG(sc, pri, "shader link log (status=%d): %s\n", status, logstr);
- talloc_free(logstr);
- }
-
- if (!status)
- sc->error_state = true;
-}
-
-// either 'compute' or both 'vertex' and 'frag' are needed
-static GLuint compile_program(struct gl_shader_cache *sc, struct bstr *vertex,
- struct bstr *frag, struct bstr *compute)
-{
- GL *gl = sc->gl;
- GLuint prog = gl->CreateProgram();
- if (compute)
- compile_attach_shader(sc, prog, GL_COMPUTE_SHADER, compute->start);
- if (vertex && frag) {
- compile_attach_shader(sc, prog, GL_VERTEX_SHADER, vertex->start);
- compile_attach_shader(sc, prog, GL_FRAGMENT_SHADER, frag->start);
- for (int n = 0; sc->vertex_entries[n].name; n++) {
- char *vname = mp_tprintf(80, "vertex_%s", sc->vertex_entries[n].name);
- gl->BindAttribLocation(prog, n, vname);
- }
- }
- link_shader(sc, prog);
- return prog;
-}
-
-static GLuint load_program(struct gl_shader_cache *sc, struct bstr *vertex,
- struct bstr *frag, struct bstr *compute)
-{
- GL *gl = sc->gl;
-
- MP_VERBOSE(sc, "new shader program:\n");
- if (sc->header_text.len) {
- MP_VERBOSE(sc, "header:\n");
- mp_log_source(sc->log, MSGL_V, sc->header_text.start);
- MP_VERBOSE(sc, "body:\n");
- }
- if (sc->text.len)
- mp_log_source(sc->log, MSGL_V, sc->text.start);
-
- if (!sc->cache_dir || !sc->cache_dir[0] || !gl->ProgramBinary)
- return compile_program(sc, vertex, frag, compute);
-
- // Try to load it from a disk cache, or compiling + saving it.
-
- GLuint prog = 0;
- void *tmp = talloc_new(NULL);
- char *dir = mp_get_user_path(tmp, sc->global, sc->cache_dir);
-
- struct AVSHA *sha = av_sha_alloc();
- if (!sha)
- abort();
- av_sha_init(sha, 256);
-
- if (vertex)
- av_sha_update(sha, vertex->start, vertex->len + 1);
- if (frag)
- av_sha_update(sha, frag->start, frag->len + 1);
- if (compute)
- av_sha_update(sha, compute->start, compute->len + 1);
-
- // In theory, the array could change order, breaking old binaries.
- for (int n = 0; sc->vertex_entries[n].name; n++) {
- av_sha_update(sha, sc->vertex_entries[n].name,
- strlen(sc->vertex_entries[n].name) + 1);
- }
-
- uint8_t hash[256 / 8];
- av_sha_final(sha, hash);
- av_free(sha);
-
- char hashstr[256 / 8 * 2 + 1];
- for (int n = 0; n < 256 / 8; n++)
- snprintf(hashstr + n * 2, sizeof(hashstr) - n * 2, "%02X", hash[n]);
-
- const char *header = "mpv shader cache v1\n";
- size_t header_size = strlen(header) + 4;
-
- char *filename = mp_path_join(tmp, dir, hashstr);
- if (stat(filename, &(struct stat){0}) == 0) {
- MP_VERBOSE(sc, "Trying to load shader from disk...\n");
- struct bstr cachedata = stream_read_file(filename, tmp, sc->global,
- 1000000000); // 1 GB
- if (cachedata.len > header_size) {
- GLenum format = AV_RL32(cachedata.start + header_size - 4);
- prog = gl->CreateProgram();
- gl_check_error(gl, sc->log, "before loading program");
- gl->ProgramBinary(prog, format, cachedata.start + header_size,
- cachedata.len - header_size);
- gl->GetError(); // discard potential useless error
- GLint status = 0;
- gl->GetProgramiv(prog, GL_LINK_STATUS, &status);
- if (!status) {
- gl->DeleteProgram(prog);
- prog = 0;
- }
- }
- MP_VERBOSE(sc, "Loading cached shader %s.\n", prog ? "ok" : "failed");
- }
-
- if (!prog) {
- prog = compile_program(sc, vertex, frag, compute);
-
- GLint size = 0;
- gl->GetProgramiv(prog, GL_PROGRAM_BINARY_LENGTH, &size);
- uint8_t *buffer = talloc_size(tmp, size + header_size);
- GLsizei actual_size = 0;
- GLenum binary_format = 0;
- gl->GetProgramBinary(prog, size, &actual_size, &binary_format,
- buffer + header_size);
- memcpy(buffer, header, header_size - 4);
- AV_WL32(buffer + header_size - 4, binary_format);
-
- if (actual_size) {
- mp_mkdirp(dir);
-
- MP_VERBOSE(sc, "Writing shader cache file: %s\n", filename);
- FILE *out = fopen(filename, "wb");
- if (out) {
- fwrite(buffer, header_size + actual_size, 1, out);
- fclose(out);
- }
- }
- }
-
- talloc_free(tmp);
- return prog;
-}
-
-#define ADD(x, ...) bstr_xappend_asprintf(sc, (x), __VA_ARGS__)
-#define ADD_BSTR(x, s) bstr_xappend(sc, (x), (s))
-
-// 1. Generate vertex and fragment shaders from the fragment shader text added
-// with gl_sc_add(). The generated shader program is cached (based on the
-// text), so actual compilation happen