diff options
Diffstat (limited to 'video')
-rw-r--r-- | video/out/gl_osd.c | 200 | ||||
-rw-r--r-- | video/out/gl_osd.h | 39 | ||||
-rw-r--r-- | video/out/gl_utils.c | 496 | ||||
-rw-r--r-- | video/out/gl_utils.h | 39 | ||||
-rw-r--r-- | video/out/gl_video.c | 1925 | ||||
-rw-r--r-- | video/out/gl_video.h | 4 | ||||
-rw-r--r-- | video/out/gl_video_shaders.glsl | 542 | ||||
-rw-r--r-- | video/out/vo_opengl.c | 5 | ||||
-rw-r--r-- | video/out/vo_opengl_cb.c | 20 |
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]; |