summaryrefslogtreecommitdiffstats
path: root/video/out/gl_utils.c
diff options
context:
space:
mode:
authorNiklas Haas <git@nand.wakku.to>2015-08-29 01:10:30 +0200
committerwm4 <wm4@nowhere>2015-09-09 18:09:25 +0200
commitdeebc55014074fef121c1df6b117e9c0bf97d516 (patch)
treea69b532d4e17dd8ecf03ecec94cbfb74f2696620 /video/out/gl_utils.c
parent944fa1214a14baf24f629f41ce6c05965028ae1a (diff)
downloadmpv-deebc55014074fef121c1df6b117e9c0bf97d516.tar.bz2
mpv-deebc55014074fef121c1df6b117e9c0bf97d516.tar.xz
vo_opengl: move gl_* files to their own subdir
This is mainly just to keep things a bit more organized and separated inside the codebase.
Diffstat (limited to 'video/out/gl_utils.c')
-rw-r--r--video/out/gl_utils.c951
1 files changed, 0 insertions, 951 deletions
diff --git a/video/out/gl_utils.c b/video/out/gl_utils.c
deleted file mode 100644
index b88e04c6c0..0000000000
--- a/video/out/gl_utils.c
+++ /dev/null
@@ -1,951 +0,0 @@
-/*
- * 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 General Public License as published by
- * the Free Software Foundation; either version 2 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with mpv. If not, see <http://www.gnu.org/licenses/>.
- *
- * You can alternatively redistribute this file 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.
- */
-
-#include <stddef.h>
-#include <stdint.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdarg.h>
-#include <assert.h>
-
-#include "stream/stream.h"
-#include "common/common.h"
-#include "gl_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 glCheckError(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));
- }
-}
-
-// return the number of bytes per pixel for the given format
-// does not handle all possible variants, just those used by mpv
-int glFmt2bpp(GLenum format, GLenum type)
-{
- int component_size = 0;
- switch (type) {
- case GL_UNSIGNED_BYTE_3_3_2:
- case GL_UNSIGNED_BYTE_2_3_3_REV:
- return 1;
- case GL_UNSIGNED_SHORT_5_5_5_1:
- case GL_UNSIGNED_SHORT_1_5_5_5_REV:
- case GL_UNSIGNED_SHORT_5_6_5:
- case GL_UNSIGNED_SHORT_5_6_5_REV:
- return 2;
- case GL_UNSIGNED_BYTE:
- component_size = 1;
- break;
- case GL_UNSIGNED_SHORT:
- component_size = 2;
- break;
- }
- switch (format) {
- case GL_LUMINANCE:
- case GL_ALPHA:
- return component_size;
- case GL_RGB_422_APPLE:
- return 2;
- case GL_RGB:
- case GL_BGR:
- case GL_RGB_INTEGER:
- return 3 * component_size;
- case GL_RGBA:
- case GL_BGRA:
- case GL_RGBA_INTEGER:
- return 4 * component_size;
- case GL_RED:
- case GL_RED_INTEGER:
- return component_size;
- case GL_RG:
- case GL_LUMINANCE_ALPHA:
- case GL_RG_INTEGER:
- return 2 * component_size;
- }
- abort(); // unknown
-}
-
-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
-// slice: height of an upload slice, 0 for all at once
-void glUploadTex(GL *gl, GLenum target, GLenum format, GLenum type,
- const void *dataptr, int stride,
- int x, int y, int w, int h, int slice)
-{
- const uint8_t *data = dataptr;
- int y_max = y + h;
- if (w <= 0 || h <= 0)
- return;
- if (slice <= 0)
- slice = h;
- if (stride < 0) {
- data += (h - 1) * stride;
- stride = -stride;
- }
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
- bool use_rowlength = slice > 1 && (gl->mpgl_caps & MPGL_CAP_ROW_LENGTH);
- if (use_rowlength) {
- // this is not always correct, but should work for MPlayer
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, stride / glFmt2bpp(format, type));
- } else {
- if (stride != glFmt2bpp(format, type) * 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 (use_rowlength)
- gl->PixelStorei(GL_UNPACK_ROW_LENGTH, 0);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
-}
-
-// Like glUploadTex, but upload a byte array with all elements set to val.
-// If scratch is not NULL, points to a resizeable talloc memory block than can
-// be freely used by the function (for avoiding temporary memory allocations).
-void glClearTex(GL *gl, GLenum target, GLenum format, GLenum type,
- int x, int y, int w, int h, uint8_t val, void **scratch)
-{
- int bpp = glFmt2bpp(format, type);
- int stride = w * bpp;
- int size = h * stride;
- if (size < 1)
- return;
- void *data = scratch ? *scratch : NULL;
- if (talloc_get_size(data) < size)
- data = talloc_realloc(NULL, data, char *, size);
- memset(data, val, size);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, get_alignment(stride));
- gl->TexSubImage2D(target, 0, x, y, w, h, format, type, data);
- gl->PixelStorei(GL_UNPACK_ALIGNMENT, 4);
- if (scratch) {
- *scratch = data;
- } else {
- talloc_free(data);
- }
-}
-
-mp_image_t *glGetWindowScreenshot(GL *gl)
-{
- if (gl->es)
- return NULL; // ES can't read from front buffer
- GLint vp[4]; //x, y, w, h
- gl->GetIntegerv(GL_VIEWPORT, vp);
- mp_image_t *image = mp_image_alloc(IMGFMT_RGB24, vp[2], vp[3]);
- if (!image)
- return NULL;
- gl->PixelStorei(GL_PACK_ALIGNMENT, 1);
- gl->ReadBuffer(GL_FRONT);
- //flip image while reading (and also avoid stride-related trouble)
- for (int y = 0; y < vp[3]; y++) {
- gl->ReadPixels(vp[0], vp[1] + vp[3] - y - 1, vp[2], 1,
- GL_RGB, GL_UNSIGNED_BYTE,
- image->planes[0] + y * image->stride[0]);
- }
- gl->PixelStorei(GL_PACK_ALIGNMENT, 4);
- 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;
- }
-}
-
-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);
- }
-}
-
-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);
- }
-}
-
-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};
-}
-
-void gl_vao_bind(struct gl_vao *vao)
-{
- 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);
- }
-}
-
-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);
- }
-}
-
-// 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.
-void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
-{
- GL *gl = vao->gl;
-
- if (ptr) {
- gl->BindBuffer(GL_ARRAY_BUFFER, vao->buffer);
- gl->BufferData(GL_ARRAY_BUFFER, num * vao->stride, ptr, GL_DYNAMIC_DRAW);
- gl->BindBuffer(GL_ARRAY_BUFFER, 0);
- }
-
- gl_vao_bind(vao);
-
- gl->DrawArrays(prim, 0, num);
-
- gl_vao_unbind(vao);
-}
-
-// Create a texture and a FBO using the texture as color attachments.
-// iformat: texture internal format
-// Returns success.
-bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum iformat)
-{
- assert(!fbo->fbo);
- assert(!fbo->texture);
- return fbotex_change(fbo, gl, log, w, h, iformat, 0);
-}
-
-// Like fbotex_init(), except it can be called on an already initialized FBO;
-// and if the parameters are the same as the previous call, do not touch it.
-// flags can be 0, or a combination of FBOTEX_FUZZY_W and FBOTEX_FUZZY_H.
-// Enabling FUZZY for W or H means the w or h does not need to be exact.
-bool fbotex_change(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum iformat, int flags)
-{
- bool res = true;
-
- int cw = w, ch = h;
-
- if ((flags & FBOTEX_FUZZY_W) && cw < fbo->w)
- cw = fbo->w;
- if ((flags & FBOTEX_FUZZY_H) && ch < fbo->h)
- ch = fbo->h;
-
- if (fbo->w == cw && fbo->h == ch && fbo->iformat == iformat)
- return true;
-
- if (flags & FBOTEX_FUZZY_W)
- w = MP_ALIGN_UP(w, 256);
- if (flags & FBOTEX_FUZZY_H)
- h = MP_ALIGN_UP(h, 256);
-
- GLenum filter = fbo->tex_filter;
-
- *fbo = (struct fbotex) {
- .gl = gl,
- .w = w,
- .h = h,
- .iformat = iformat,
- };
-
- mp_verbose(log, "Create FBO: %dx%d\n", fbo->w, fbo->h);
-
- if (!(gl->mpgl_caps & MPGL_CAP_FB))
- return false;
-
- gl->GenFramebuffers(1, &fbo->fbo);
- gl->GenTextures(1, &fbo->texture);
- gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
- gl->TexImage2D(GL_TEXTURE_2D, 0, iformat, fbo->w, fbo->h, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl->BindTexture(GL_TEXTURE_2D, 0);
-
- fbotex_set_filter(fbo, filter ? filter : GL_LINEAR);
-
- glCheckError(gl, log, "after creating framebuffer texture");
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
- gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
- GL_TEXTURE_2D, fbo->texture, 0);
-
- GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
- if (err != GL_FRAMEBUFFER_COMPLETE) {
- mp_err(log, "Error: framebuffer completeness check failed (error=%d).\n",
- (int)err);
- res = false;
- }
-
- gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
-
- glCheckError(gl, log, "after creating framebuffer");
-
- return res;
-}
-
-void fbotex_set_filter(struct fbotex *fbo, GLenum tex_filter)
-{
- GL *gl = fbo->gl;
-
- if (fbo->tex_filter != tex_filter && fbo->texture) {
- gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, tex_filter);
- gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, tex_filter);
- gl->BindTexture(GL_TEXTURE_2D, 0);
- }
- fbo->tex_filter = tex_filter;
-}
-
-void fbotex_uninit(struct fbotex *fbo)
-{
- GL *gl = fbo->gl;
-
- if (gl && (gl->mpgl_caps & MPGL_CAP_FB)) {
- gl->DeleteFramebuffers(1, &fbo->fbo);
- gl->DeleteTextures(1, &fbo->texture);
- *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);
-}
-
-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) {
- if (log) {
- gl->DebugMessageCallback(gl_debug_cb, log);
- } else {
- gl->DebugMessageCallback(NULL, NULL);
- }
- }
-}
-
-#define SC_ENTRIES 16
-#define SC_UNIFORM_ENTRIES 20
-#define SC_FILE_ENTRIES 10
-
-enum uniform_type {
- UT_invalid,
- UT_i,
- UT_f,
- UT_m,
-};
-
-struct sc_uniform {
- char *name;
- enum uniform_type type;
- const char *glsl_type;
- int size;
- GLint loc;
- union {
- GLfloat f[9];
- GLint i[4];
- } v;
-};
-
-struct sc_file {
- char *path;
- char *body;
-};
-
-struct sc_entry {
- GLuint gl_shader;
- // the following fields define the shader's contents
- char *key; // vertex+frag shader (mangled)
- struct gl_vao *vao;
-};
-
-struct gl_shader_cache {
- GL *gl;
- struct mp_log *log;
- struct mpv_global *global;
-
- // this is modified during use (gl_sc_add() etc.)
- char *text;
- char *header_text;
- struct gl_vao *vao;
-
- struct sc_entry entries[SC_ENTRIES];
- int num_entries;
-
- struct sc_uniform uniforms[SC_UNIFORM_ENTRIES];
- int num_uniforms;
-
- struct sc_file files[SC_FILE_ENTRIES];
- int num_files;
-};
-
-struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log,
- struct mpv_global *global)
-{
- struct gl_shader_cache *sc = talloc_ptrtype(NULL, sc);
- *sc = (struct gl_shader_cache){
- .gl = gl,
- .log = log,
- .global = global,
- .text = talloc_strdup(sc, ""),
- .header_text = talloc_strdup(sc, ""),
- };
- return sc;
-}
-
-void gl_sc_reset(struct gl_shader_cache *sc)
-{
- sc->text[0] = '\0';
- sc->header_text[0] = '\0';
- for (int n = 0; n < sc->num_uniforms; n++)
- talloc_free(sc->uniforms[n].name);
- sc->num_uniforms = 0;
-}
-
-static void sc_flush_cache(struct gl_shader_cache *sc)
-{
- 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->key);
- }
- 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);
-}
-
-void gl_sc_add(struct gl_shader_cache *sc, const char *text)
-{
- sc->text = talloc_strdup_append(sc->text, text);
-}
-
-void gl_sc_addf(struct gl_shader_cache *sc, const char *textf, ...)
-{
- va_list ap;
- va_start(ap, textf);
- ta_xvasprintf_append(&sc->text, textf, ap);
- va_end(ap);
-}
-
-void gl_sc_hadd(struct gl_shader_cache *sc, const char *text)
-{
- sc->header_text = talloc_strdup_append(sc->header_text, text);
-}
-
-const char *gl_sc_loadfile(struct gl_shader_cache *sc, const char *path)
-{
- if (!path || !path[0] || !sc->global)
- return NULL;
- for (int n = 0; n < sc->num_files; n++) {
- if (strcmp(sc->files[n].path, path) == 0)
- return sc->files[n].body;
- }
- // not found -> load it
- if (sc->num_files == SC_FILE_ENTRIES) {
- // empty cache when it overflows
- for (int n = 0; n < sc->num_files; n++) {
- talloc_free(sc->files[n].path);
- talloc_free(sc->files[n].body);
- }
- sc->num_files = 0;
- }
- struct bstr s = stream_read_file(path, sc, sc->global, 100000); // 100 kB
- if (s.len) {
- struct sc_file *new = &sc->files[sc->num_files++];
- *new = (struct sc_file) {
- .path = talloc_strdup(sc, path),
- .body = s.start
- };
- return new->body;
- }
- return NULL;
-}
-
-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
- assert(sc->num_uniforms < SC_UNIFORM_ENTRIES); // just don't have too many
- struct sc_uniform *new = &sc->uniforms[sc->num_uniforms++];
- *new = (struct sc_uniform) { .loc = -1, .name = talloc_strdup(NULL, name) };
- return new;
-}
-
-void gl_sc_uniform_sampler(struct gl_shader_cache *sc, char *name, GLenum target,
- int unit)
-{
- struct sc_uniform *u = find_uniform(sc, name);
- u->type = UT_i;
- u->size = 1;
- switch (target) {
- case GL_TEXTURE_1D: u->glsl_type = "sampler1D"; break;
- case GL_TEXTURE_2D: u->glsl_type = "sampler2D"; break;
- case GL_TEXTURE_RECTANGLE: u->glsl_type = "sampler2DRect"; break;
- case GL_TEXTURE_3D: u->glsl_type = "sampler3D"; break;
- default: abort();
- }
- u->v.i[0] = unit;
-}
-
-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]);
-}
-
-// This will call glBindAttribLocation() on the shader before it's linked
-// (OpenGL requires this to happen before linking). Basically, it associates
-// the input variable names with the fields in the vao.
-// The vertex shader is setup such that the elements are available as fragment
-// shader variables using the names in the vao entries, which "position" being
-// set to gl_Position.
-void gl_sc_set_vao(struct gl_shader_cache *sc, struct gl_vao *vao)
-{
- sc->vao = vao;
-}
-
-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, GLuint program, struct sc_uniform *u)
-{
- GLint loc = gl->GetUniformLocation(program, u->name);
- if (loc < 0)
- return;
- switch (u->type) {
- case UT_i:
- assert(u->size == 1);
- gl->Uniform1i(loc, u->v.i[0]);
- break;
- case UT_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:
- 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();
- }
-}
-
-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;
- gl->GetShaderiv(shader, GL_COMPILE_STATUS, &status);
- GLint log_length;
- gl->GetShaderiv(shader, GL_INFO_LOG_LENGTH, &log_length);
-
- int pri = status ? (log_length > 1 ? MSGL_V : MSGL_DEBUG) : MSGL_ERR;
- const char *typestr = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
- 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);
- }
-
- gl->AttachShader(program, shader);
- gl->DeleteShader(shader);
-}
-
-static void link_shader(struct gl_shader_cache *sc, GLuint program)
-{
- GL *gl = sc->gl;
- gl->LinkProgram(program);
- GLint status;
- gl->GetProgramiv(program, GL_LINK_STATUS, &status);
- GLint log_length;
- 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);
- }
-}
-
-static GLuint create_program(struct gl_shader_cache *sc, const char *vertex,
- const char *frag)
-{
- GL *gl = sc->gl;
- MP_VERBOSE(sc, "recompiling a shader program:\n");
- if (sc->header_text[0]) {
- MP_VERBOSE(sc, "header:\n");
- mp_log_source(sc->log, MSGL_V, sc->header_text);
- MP_VERBOSE(sc, "body:\n");
- }
- mp_log_source(sc->log, MSGL_V, sc->text);
- GLuint prog = gl->CreateProgram();
- compile_attach_shader(sc, prog, GL_VERTEX_SHADER, vertex);
- compile_attach_shader(sc, prog, GL_FRAGMENT_SHADER, frag);
- for (int n = 0; sc->vao->entries[n].name; n++) {
- char vname[80];
- snprintf(vname, sizeof(vname), "vertex_%s", sc->vao->entries[n].name);
- gl->BindAttribLocation(prog, n, vname);
- }
- link_shader(sc, prog);
- return prog;
-}
-
-#define ADD(x, ...) (x) = talloc_asprintf_append(x, __VA_ARGS__)
-
-// 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 happens only the first time.
-// 2. Update the uniforms set with gl_sc_uniform_*.
-// 3. Make the new shader program current (glUseProgram()).
-// 4. Reset the sc state and prepare for a new shader program. (All uniforms
-// and fragment operations needed for the next program have to be re-added.)
-void gl_sc_gen_shader_and_reset(struct gl_shader_cache *sc)
-{
- GL *gl = sc->gl;
- void *tmp = talloc_new(NULL);
-
- assert(sc->vao);
-
- // set up shader text (header + uniforms + body)
- char *header = talloc_asprintf(tmp, "#version %d%s\n", gl->glsl_version,
- gl->es >= 300 ? " es" : "");
- if (gl->es)
- ADD(header, "precision mediump float;\n");
- char *vert_in = gl->glsl_version >= 130 ? "in" : "attribute";
- char *vert_out = gl->glsl_version >= 130 ? "out" : "varying";
- char *frag_in = gl->glsl_version >= 130 ? "in" : "varying";
-
- // vertex shader: we don't use the vertex shader, so just setup a dummy,
- // which passes through the vertex array attributes.
- char *vert_head = talloc_strdup(tmp, header);
- char *vert_body = talloc_strdup(tmp, "void main() {\n");
- char *frag_vaos = talloc_strdup(tmp, "");
- for (int n = 0; sc->vao->entries[n].name; n++) {
- const struct gl_vao_entry *e = &sc->vao->entries[n];
- const char *glsl_type = vao_glsl_type(e);
- if (strcmp(e->name, "position") == 0) {
- // setting raster pos. requires setting gl_Position magic variable
- assert(e->num_elems == 2 && e->type == GL_FLOAT);
- ADD(vert_head, "%s vec2 vertex_position;\n", vert_in);
- ADD(vert_body, "gl_Position = vec4(vertex_position, 1.0, 1.0);\n");
- } else {
- ADD(vert_head, "%s %s vertex_%s;\n", vert_in, glsl_type, e->name);
- ADD(vert_head, "%s %s %s;\n", vert_out, glsl_type, e->name);
- ADD(vert_body, "%s = vertex_%s;\n", e->name, e->name);
- ADD(frag_vaos, "%s %s %s;\n", frag_in, glsl_type, e->name);
- }
- }
- ADD(vert_body, "}\n");
- char *vert = talloc_asprintf(tmp, "%s%s", vert_head, vert_body);
-
- // fragment shader; still requires adding used uniforms and VAO elements
- char *frag = talloc_strdup(tmp, header);
- ADD(frag, "#define RG %s\n", gl->mpgl_caps & MPGL_CAP_TEX_RG ? "rg" : "ra");
- if (gl->glsl_version >= 130) {
- ADD(frag, "#define texture1D texture\n");
- ADD(frag, "#define texture3D texture\n");
- ADD(frag, "out vec4 out_color;\n");
- } else {
- ADD(frag, "#define texture texture2D\n");
- }
- ADD(frag, "%s", frag_vaos);
- for (int n = 0; n < sc->num_uniforms; n++) {
- struct sc_uniform *u = &sc->uniforms[n];
- ADD(frag, "uniform %s %s;\n", u->glsl_type, u->name);
- }
- // custom shader header
- if (sc->header_text[0]) {
- ADD(frag, "// header\n");
- ADD(frag, "%s\n", sc->header_text);
- ADD(frag, "// body\n");
- }
- ADD(frag, "void main() {\n");
- ADD(frag, "%s", sc->text);
- // we require _all_ frag shaders to write to a "vec4 color"
- if (gl->glsl_version >= 130) {
- ADD(frag, "out_color = color;\n");
- } else {
- ADD(frag, "gl_FragColor = color;\n");
- }
- ADD(frag, "}\n");
-
- char *key = talloc_asprintf(tmp, "%s%s", vert, frag);
- struct sc_entry *entry = NULL;
- for (int n = 0; n < sc->num_entries; n++) {
- if (strcmp(key, sc->entries[n].key) == 0) {
- entry = &sc->entries[n];
- break;
- }
- }
- if (!entry) {
- if (sc->num_entries == SC_ENTRIES)
- sc_flush_cache(sc);
- entry = &sc->entries[sc->num_entries++];
- *entry = (struct sc_entry){.key = talloc_strdup(NULL, key)};
- }
- // build vertex shader from vao
- if (!entry->gl_shader)
- entry->gl_shader = create_program(sc, vert, frag);
-
- gl->UseProgram(entry->gl_shader);
-
- // For now we set the uniforms every time. This is probably bad, and we
- // should switch to caching them.
- for (int n = 0; n < sc->num_uniforms; n++)
- update_uniform(gl, entry->gl_shader, &sc->uniforms[n]);
-
- talloc_free(tmp);
-
- gl_sc_reset(sc);
-}