summaryrefslogtreecommitdiffstats
path: root/video
diff options
context:
space:
mode:
Diffstat (limited to 'video')
-rw-r--r--video/out/gl_osd.c200
-rw-r--r--video/out/gl_osd.h39
-rw-r--r--video/out/gl_utils.c496
-rw-r--r--video/out/gl_utils.h39
-rw-r--r--video/out/gl_video.c1925
-rw-r--r--video/out/gl_video.h4
-rw-r--r--video/out/gl_video_shaders.glsl542
-rw-r--r--video/out/vo_opengl.c5
-rw-r--r--video/out/vo_opengl_cb.c20
9 files changed, 1280 insertions, 1990 deletions
diff --git a/video/out/gl_osd.c b/video/out/gl_osd.c
index 117d7feedb..0ab85f59c4 100644
--- a/video/out/gl_osd.c
+++ b/video/out/gl_osd.c
@@ -55,20 +55,46 @@ static const struct osd_fmt_entry osd_to_gl2_formats[SUBBITMAP_COUNT] = {
struct vertex {
float position[2];
- uint8_t color[4];
float texcoord[2];
+ uint8_t ass_color[4];
};
static const struct gl_vao_entry vertex_vao[] = {
- {"vertex_position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
- {"vertex_color", 4, GL_UNSIGNED_BYTE, true, offsetof(struct vertex, color)},
- {"vertex_texcoord", 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"position", 2, GL_FLOAT, false, offsetof(struct vertex, position)},
+ {"texcoord" , 2, GL_FLOAT, false, offsetof(struct vertex, texcoord)},
+ {"ass_color", 4, GL_UNSIGNED_BYTE, true, offsetof(struct vertex, ass_color)},
{0}
};
-// programs: SUBBITMAP_COUNT elements
-struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd,
- GLuint *programs)
+struct mpgl_osd_part {
+ enum sub_bitmap_format format;
+ int bitmap_id, bitmap_pos_id;
+ GLuint texture;
+ int w, h;
+ GLuint buffer;
+ int num_subparts;
+ struct sub_bitmap *subparts;
+ struct vertex *vertices;
+ struct bitmap_packer *packer;
+};
+
+struct mpgl_osd {
+ struct mp_log *log;
+ struct osd_state *osd;
+ GL *gl;
+ bool use_pbo;
+ bool scaled;
+ struct mpgl_osd_part *parts[MAX_OSD_PARTS];
+ const struct osd_fmt_entry *fmt_table;
+ bool formats[SUBBITMAP_COUNT];
+ struct gl_vao vao;
+ // temporary
+ int stereo_mode;
+ int display_size[2];
+ void *scratch;
+};
+
+struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd)
{
GLint max_texture_size;
gl->GetIntegerv(GL_MAX_TEXTURE_SIZE, &max_texture_size);
@@ -79,7 +105,6 @@ struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd
.osd = osd,
.gl = gl,
.fmt_table = osd_to_gl3_formats,
- .programs = programs,
.scratch = talloc_zero_size(ctx, 1),
};
@@ -126,6 +151,11 @@ void mpgl_osd_destroy(struct mpgl_osd *ctx)
talloc_free(ctx);
}
+void mpgl_osd_set_options(struct mpgl_osd *ctx, bool pbo)
+{
+ ctx->use_pbo = pbo;
+}
+
static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
struct sub_bitmaps *imgs)
{
@@ -154,8 +184,7 @@ static bool upload_pbo(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
if (!gl->UnmapBuffer(GL_PIXEL_UNPACK_BUFFER))
success = false;
glUploadTex(gl, GL_TEXTURE_2D, fmt.format, fmt.type, NULL, stride,
- bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y,
- 0);
+ bb[0].x, bb[0].y, bb[1].x - bb[0].x, bb[1].y - bb[0].y, 0);
}
gl->BindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
@@ -240,11 +269,12 @@ static bool upload_osd(struct mpgl_osd *ctx, struct mpgl_osd_part *osd,
return true;
}
-static struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
- struct sub_bitmaps *imgs)
+static void gen_osd_cb(void *pctx, struct sub_bitmaps *imgs)
{
+ struct mpgl_osd *ctx = pctx;
+
if (imgs->num_parts == 0 || !ctx->formats[imgs->format])
- return NULL;
+ return;
struct mpgl_osd_part *osd = ctx->parts[imgs->render_index];
@@ -256,83 +286,76 @@ static struct mpgl_osd_part *mpgl_osd_generate(struct mpgl_osd *ctx,
osd->bitmap_id = imgs->bitmap_id;
osd->bitmap_pos_id = imgs->bitmap_pos_id;
- osd->num_vertices = 0;
}
+ osd->num_subparts = osd->packer->count;
- return osd->packer->count ? osd : NULL;
+ MP_TARRAY_GROW(osd, osd->subparts, osd->num_subparts);
+ memcpy(osd->subparts, imgs->parts,
+ osd->num_subparts * sizeof(osd->subparts[0]));
}
-static void write_quad(struct vertex *va,
+static void write_quad(struct vertex *va, float matrix[3][3],
float x0, float y0, float x1, float y1,
float tx0, float ty0, float tx1, float ty1,
float tex_w, float tex_h, const uint8_t color[4])
{
+ gl_matrix_mul_vec(matrix, &x0, &y0);
+ gl_matrix_mul_vec(matrix, &x1, &y1);
+
#define COLOR_INIT {color[0], color[1], color[2], color[3]}
- va[0] = (struct vertex){ {x0, y0}, COLOR_INIT, {tx0 / tex_w, ty0 / tex_h} };
- va[1] = (struct vertex){ {x0, y1}, COLOR_INIT, {tx0 / tex_w, ty1 / tex_h} };
- va[2] = (struct vertex){ {x1, y0}, COLOR_INIT, {tx1 / tex_w, ty0 / tex_h} };
- va[3] = (struct vertex){ {x1, y1}, COLOR_INIT, {tx1 / tex_w, ty1 / tex_h} };
+ va[0] = (struct vertex){ {x0, y0}, {tx0 / tex_w, ty0 / tex_h}, COLOR_INIT };
+ va[1] = (struct vertex){ {x0, y1}, {tx0 / tex_w, ty1 / tex_h}, COLOR_INIT };
+ va[2] = (struct vertex){ {x1, y0}, {tx1 / tex_w, ty0 / tex_h}, COLOR_INIT };
+ va[3] = (struct vertex){ {x1, y1}, {tx1 / tex_w, ty1 / tex_h}, COLOR_INIT };
va[4] = va[2];
va[5] = va[1];
#undef COLOR_INIT
}
-static void draw_osd_cb(void *pctx, struct sub_bitmaps *imgs)
+static int generate_verts(struct mpgl_osd_part *part, float matrix[3][3])
{
- struct mpgl_osd *ctx = pctx;
- GL *gl = ctx->gl;
+ int num_vertices = part->num_subparts * 6;
+ MP_TARRAY_GROW(part, part->vertices, num_vertices);
- struct mpgl_osd_part *part = mpgl_osd_generate(ctx, imgs);
- if (!part)
- return;
+ for (int n = 0; n < part->num_subparts; n++) {
+ struct sub_bitmap *b = &part->subparts[n];
+ struct pos pos = part->packer->result[n];
+ struct vertex *va = part->vertices;
- assert(part->format != SUBBITMAP_EMPTY);
+ // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
+ // doesn't matter that we upload garbage for the other formats
+ uint32_t c = b->libass.color;
+ uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
+ (c >> 8) & 0xff, 255 - (c & 0xff) };
- if (!part->num_vertices) {
- part->vertices = talloc_realloc(part, part->vertices, struct vertex,
- part->packer->count * 6);
+ write_quad(&va[n * 6], matrix,
+ b->x, b->y, b->x + b->dw, b->y + b->dh,
+ pos.x, pos.y, pos.x + b->w, pos.y + b->h,
+ part->w, part->h, color);
+ }
- struct vertex *va = part->vertices;
+ return num_vertices;
+}
- for (int n = 0; n < part->packer->count; n++) {
- struct sub_bitmap *b = &imgs->parts[n];
- struct pos pos = part->packer->result[n];
-
- // NOTE: the blend color is used with SUBBITMAP_LIBASS only, so it
- // doesn't matter that we upload garbage for the other formats
- uint32_t c = b->libass.color;
- uint8_t color[4] = { c >> 24, (c >> 16) & 0xff,
- (c >> 8) & 0xff, 255 - (c & 0xff) };
-
- write_quad(&va[part->num_vertices],
- b->x, b->y, b->x + b->dw, b->y + b->dh,
- pos.x, pos.y, pos.x + b->w, pos.y + b->h,
- part->w, part->h, color);
- part->num_vertices += 6;
- }
- }
+static void draw_part(struct mpgl_osd *ctx, int index, float matrix[3][3])
+{
+ GL *gl = ctx->gl;
+ struct mpgl_osd_part *part = ctx->parts[index];
+
+ int num_vertices = generate_verts(part, matrix);
+ if (!num_vertices)
+ return;
+ gl->Enable(GL_BLEND);
gl->BindTexture(GL_TEXTURE_2D, part->texture);
const int *factors = &blend_factors[part->format][0];
gl->BlendFuncSeparate(factors[0], factors[1], factors[2], factors[3]);
- int program = ctx->programs[part->format];
-
- gl->UseProgram(program);
- bool set_offset = ctx->offset[0] != 0.0f || ctx->offset[1] != 0.0f;
- if (set_offset) {
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"),
- ctx->offset[0], ctx->offset[1], 0);
- }
-
- gl_vao_draw_data(&ctx->vao, GL_TRIANGLES, part->vertices, part->num_vertices);
+ gl_vao_draw_data(&ctx->vao, GL_TRIANGLES, part->vertices, num_vertices);
- if (set_offset)
- gl->Uniform3f(gl->GetUniformLocation(program, "translation"), 0, 0, 0);
-
- gl->UseProgram(0);
gl->BindTexture(GL_TEXTURE_2D, 0);
+ gl->Disable(GL_BLEND);
}
// number of screen divisions per axis (x=0, y=1) for the current 3D mode
@@ -347,26 +370,51 @@ static void get_3d_side_by_side(int stereo_mode, int div[2])
}
}
-void mpgl_osd_draw(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
- int stereo_mode)
+void mpgl_osd_draw_part(struct mpgl_osd *ctx, int vp_w, int vp_h, int index)
{
- GL *gl = ctx->gl;
-
- gl->Enable(GL_BLEND);
-
int div[2];
- get_3d_side_by_side(stereo_mode, div);
+ get_3d_side_by_side(ctx->stereo_mode, div);
for (int x = 0; x < div[0]; x++) {
for (int y = 0; y < div[1]; y++) {
- struct mp_osd_res s_res = res;
- s_res.w /= div[0];
- s_res.h /= div[1];
- ctx->offset[0] = s_res.w * x;
- ctx->offset[1] = s_res.h * y;
- osd_draw(ctx->osd, s_res, pts, 0, ctx->formats, draw_osd_cb, ctx);
+ float matrix[3][3];
+
+ gl_matrix_ortho2d(matrix, 0, vp_w, 0, vp_h);
+
+ float a_x = ctx->display_size[0] * x;
+ float a_y = ctx->display_size[1] * y;
+ matrix[2][0] += a_x * matrix[0][0] + a_y * matrix[1][0];
+ matrix[2][1] += a_x * matrix[0][1] + a_y * matrix[1][1];
+
+ draw_part(ctx, index, matrix);
}
}
+}
- gl->Disable(GL_BLEND);
+enum sub_bitmap_format mpgl_osd_get_part_format(struct mpgl_osd *ctx, int index)
+{
+ assert(index >= 0 && index < MAX_OSD_PARTS);
+ return ctx->parts[index]->format;
+}
+
+struct gl_vao *mpgl_osd_get_vao(struct mpgl_osd *ctx)
+{
+ return &ctx->vao;
+}
+
+void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
+ int stereo_mode)
+{
+ for (int n = 0; n < MAX_OSD_PARTS; n++)
+ ctx->parts[n]->num_subparts = 0;
+
+ int div[2];
+ get_3d_side_by_side(stereo_mode, div);
+
+ struct mp_osd_res s_res = res;
+ ctx->display_size[0] = s_res.w = s_res.w / div[0];
+ ctx->display_size[1] = s_res.h = s_res.h / div[1];
+
+ osd_draw(ctx->osd, s_res, pts, 0, ctx->formats, gen_osd_cb, ctx);
+ ctx->stereo_mode = stereo_mode;
}
diff --git a/video/out/gl_osd.h b/video/out/gl_osd.h
index 5c34911e22..0acd200ab4 100644
--- a/video/out/gl_osd.h
+++ b/video/out/gl_osd.h
@@ -7,38 +7,15 @@
#include "gl_utils.h"
#include "sub/osd.h"
-struct mpgl_osd_part {
- enum sub_bitmap_format format;
- int bitmap_id, bitmap_pos_id;
- GLuint texture;
- int w, h;
- GLuint buffer;
- int num_vertices;
- void *vertices;
- struct bitmap_packer *packer;
-};
-
-struct mpgl_osd {
- struct mp_log *log;
- struct osd_state *osd;
- GL *gl;
- bool use_pbo;
- bool scaled;
- struct mpgl_osd_part *parts[MAX_OSD_PARTS];
- const struct osd_fmt_entry *fmt_table;
- bool formats[SUBBITMAP_COUNT];
- struct gl_vao vao;
- GLuint *programs; // SUBBITMAP_COUNT elements
- // temporary
- float offset[2];
- void *scratch;
-};
-
-struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd,
- GLuint *programs);
+struct mpgl_osd *mpgl_osd_init(GL *gl, struct mp_log *log, struct osd_state *osd);
void mpgl_osd_destroy(struct mpgl_osd *ctx);
-void mpgl_osd_draw(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
- int stereo_mode);
+void mpgl_osd_set_options(struct mpgl_osd *ctx, bool pbo);
+
+void mpgl_osd_generate(struct mpgl_osd *ctx, struct mp_osd_res res, double pts,
+ int stereo_mode);
+enum sub_bitmap_format mpgl_osd_get_part_format(struct mpgl_osd *ctx, int index);
+struct gl_vao *mpgl_osd_get_vao(struct mpgl_osd *ctx);
+void mpgl_osd_draw_part(struct mpgl_osd *ctx, int vp_w, int vp_h, int index);
#endif
diff --git a/video/out/gl_utils.c b/video/out/gl_utils.c
index 80ec840582..ca2fef10bf 100644
--- a/video/out/gl_utils.c
+++ b/video/out/gl_utils.c
@@ -25,6 +25,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include <assert.h>
#include "common/common.h"
@@ -290,14 +291,6 @@ void gl_vao_unbind(struct gl_vao *vao)
}
}
-void gl_vao_bind_attribs(struct gl_vao *vao, GLuint program)
-{
- GL *gl = vao->gl;
-
- for (int n = 0; vao->entries[n].name; n++)
- gl->BindAttribLocation(program, n, vao->entries[n].name);
-}
-
// 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
@@ -320,24 +313,47 @@ void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num)
}
// Create a texture and a FBO using the texture as color attachments.
-// gl_target: GL_TEXTURE_2D
-// gl_filter: GL_LINEAR
// iformat: texture internal format
// Returns success.
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum gl_target, GLenum gl_filter, GLenum iformat)
+ GLenum iformat)
{
- bool res = true;
-
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->tex_w)
+ cw = fbo->tex_w;
+ if ((flags & FBOTEX_FUZZY_H) && ch < fbo->tex_h)
+ ch = fbo->tex_h;
+
+ if (fbo->tex_w == cw && fbo->tex_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,
- .vp_w = w,
- .vp_h = h,
.tex_w = w,
.tex_h = h,
+ .iformat = iformat,
};
mp_verbose(log, "Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
@@ -347,19 +363,20 @@ bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
gl->GenFramebuffers(1, &fbo->fbo);
gl->GenTextures(1, &fbo->texture);
- gl->BindTexture(gl_target, fbo->texture);
- gl->TexImage2D(gl_target, 0, iformat, fbo->tex_w, fbo->tex_h, 0,
+ gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
+ gl->TexImage2D(GL_TEXTURE_2D, 0, iformat, fbo->tex_w, fbo->tex_h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
- gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(gl_target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
- gl->TexParameteri(gl_target, GL_TEXTURE_MIN_FILTER, gl_filter);
- gl->TexParameteri(gl_target, GL_TEXTURE_MAG_FILTER, gl_filter);
+ 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_target, fbo->texture, 0);
+ GL_TEXTURE_2D, fbo->texture, 0);
GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
if (err != GL_FRAMEBUFFER_COMPLETE) {
@@ -375,6 +392,19 @@ bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
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;
@@ -386,8 +416,16 @@ void fbotex_uninit(struct fbotex *fbo)
}
}
+// Standard parallel 2D projection, except y1 < y0 means that the coordinate
+// system is flipped, not the projection.
void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1)
{
+ if (y1 < y0) {
+ float t = y0;
+ y0 = t - y1;
+ y1 = t;
+ }
+
memset(m, 0, 9 * sizeof(float));
m[0][0] = 2.0f / (x1 - x0);
m[1][1] = 2.0f / (y1 - y0);
@@ -422,3 +460,417 @@ void gl_set_debug_logger(GL *gl, struct mp_log *log)
}
}
}
+
+#define SC_ENTRIES 10
+#define SC_UNIFORM_ENTRIES 20
+
+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_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;
+
+ // this is modified during use (gl_sc_add() etc.)
+ char *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 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,
+ .text = talloc_strdup(sc, ""),
+ };
+ return sc;
+}
+
+void gl_sc_reset(struct gl_shader_cache *sc)
+{
+ sc->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)
+{
+ 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);
+}
+
+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_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");
+ 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 position;\n", vert_in);
+ ADD(vert_body, "gl_Position = vec4(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");
+ }
+ 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);
+ }
+ 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);
+}
diff --git a/video/out/gl_utils.h b/video/out/gl_utils.h
index 1934396afe..a1bb2ecafb 100644
--- a/video/out/gl_utils.h
+++ b/video/out/gl_utils.h
@@ -66,23 +66,54 @@ void gl_vao_init(struct gl_vao *vao, GL *gl, int stride,
void gl_vao_uninit(struct gl_vao *vao);
void gl_vao_bind(struct gl_vao *vao);
void gl_vao_unbind(struct gl_vao *vao);
-void gl_vao_bind_attribs(struct gl_vao *vao, GLuint program);
void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t num);
struct fbotex {
GL *gl;
GLuint fbo;
GLuint texture;
- int tex_w, tex_h; // size of .texture
- int vp_x, vp_y, vp_w, vp_h; // viewport of fbo / used part of the texture
+ GLenum iformat;
+ GLenum tex_filter;
+ int tex_w, tex_h; // size of .texture
};
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
- GLenum gl_target, GLenum gl_filter, GLenum iformat);
+ GLenum iformat);
void fbotex_uninit(struct fbotex *fbo);
+bool fbotex_change(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
+ GLenum iformat, int flags);
+#define FBOTEX_FUZZY_W 1
+#define FBOTEX_FUZZY_H 2
+void fbotex_set_filter(struct fbotex *fbo, GLenum gl_filter);
void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1);
+static inline void gl_matrix_mul_vec(float m[3][3], float *x, float *y)
+{
+ float vx = *x, vy = *y;
+ *x = vx * m[0][0] + vy * m[1][0] + m[2][0];
+ *y = vx * m[0][1] + vy * m[1][1] + m[2][1];
+}
+
void gl_set_debug_logger(GL *gl, struct mp_log *log);
+struct gl_shader_cache;
+
+struct gl_shader_cache *gl_sc_create(GL *gl, struct mp_log *log);
+void gl_sc_destroy(struct gl_shader_cache *sc);
+void gl_sc_add(struct gl_shader_cache *sc, const char *text);
+void gl_sc_addf(struct gl_shader_cache