From 0206efa94aea3d4d20584c4446654a5ddd8e7896 Mon Sep 17 00:00:00 2001 From: wm4 Date: Fri, 4 Aug 2017 15:47:50 +0200 Subject: vo_opengl: pass ra objects during rendering instead of GL objects Another "small" step towards removing GL dependencies from the renderer. This commit generally passes ra_tex objects instead of GL FBO integer IDs to various rendering functions. video.c still manually binds the FBOs when calling shaders. This also happens to fix a memory leak with output_fbo. --- video/out/opengl/ra.h | 21 +++++ video/out/opengl/ra_gl.c | 157 +++++++++++++++++++++++++++++++- video/out/opengl/ra_gl.h | 10 ++- video/out/opengl/video.c | 230 +++++++++++++++++++---------------------------- 4 files changed, 274 insertions(+), 144 deletions(-) diff --git a/video/out/opengl/ra.h b/video/out/opengl/ra.h index bd7a904860..21d9cf15d7 100644 --- a/video/out/opengl/ra.h +++ b/video/out/opengl/ra.h @@ -21,6 +21,7 @@ struct ra { enum { RA_CAP_TEX_1D = 1 << 0, // supports 1D textures (as shader source textures) RA_CAP_TEX_3D = 1 << 1, // supports 3D textures (as shader source textures) + RA_CAP_BLIT = 1 << 2, // supports ra_fns.blit }; enum ra_ctype { @@ -144,6 +145,26 @@ struct ra_fns { // whether it was signalled yet. If true, write accesses are allowed again. // Optional, only available if flush_mapping is. bool (*poll_mapped_buffer)(struct ra *ra, struct ra_mapped_buffer *buf); + + // Clear the dst with the given color (rgba) and within the given scissor. + // dst must have dst->params.render_dst==true. Content outside of the + // scissor is preserved. + void (*clear)(struct ra *ra, struct ra_tex *dst, float color[4], + struct mp_rect *scissor); + + // Copy a sub-rectangle from one texture to another. The source/dest region + // is always within the texture bounds. Areas outside the dest region are + // preserved. The formats of the textures will be losely compatible (this + // probably has to be defined strictly). The dst texture can be a swapchain + // framebuffer, but src can not. Only 2D textures are supported. + // Both textures must have tex->params.render_dst==true (even src, which is + // an odd GL requirement). + // A rectangle with negative width or height means a flipped copy should be + // done. Coordinates are always in pixels. + // Optional. Only available if RA_CAP_BLIT is set (if it's not set, the must + // not be called, even if it's non-NULL). + void (*blit)(struct ra *ra, struct ra_tex *dst, struct ra_tex *src, + int dst_x, int dst_y, struct mp_rect *src_rc); }; struct ra_tex *ra_tex_create(struct ra *ra, const struct ra_tex_params *params); diff --git a/video/out/opengl/ra_gl.c b/video/out/opengl/ra_gl.c index a52eeec94b..227bd58fbf 100644 --- a/video/out/opengl/ra_gl.c +++ b/video/out/opengl/ra_gl.c @@ -20,6 +20,8 @@ int ra_init_gl(struct ra *ra, GL *gl) ra->caps |= RA_CAP_TEX_1D; if (gl->mpgl_caps & MPGL_CAP_3D_TEX) ra->caps |= RA_CAP_TEX_3D; + if (gl->BlitFramebuffer) + ra->caps |= RA_CAP_BLIT; int gl_fmt_features = gl_format_feature_flags(gl); @@ -101,10 +103,12 @@ static void gl_tex_destroy(struct ra *ra, struct ra_tex *tex) struct ra_gl *p = ra->priv; struct ra_tex_gl *tex_gl = tex->priv; - if (tex_gl->fbo) - p->gl->DeleteFramebuffers(1, &tex_gl->fbo); + if (tex_gl->own_objects) { + if (tex_gl->fbo) + p->gl->DeleteFramebuffers(1, &tex_gl->fbo); - p->gl->DeleteTextures(1, &tex_gl->texture); + p->gl->DeleteTextures(1, &tex_gl->texture); + } gl_pbo_upload_uninit(&tex_gl->pbo); talloc_free(tex_gl); talloc_free(tex); @@ -121,6 +125,7 @@ static struct ra_tex *gl_tex_create(struct ra *ra, struct ra_tex_gl *tex_gl = tex->priv = talloc_zero(NULL, struct ra_tex_gl); const struct gl_format *fmt = params->format->priv; + tex_gl->own_objects = true; tex_gl->internal_format = fmt->internal_format; tex_gl->format = fmt->format; tex_gl->type = fmt->type; @@ -204,6 +209,103 @@ static struct ra_tex *gl_tex_create(struct ra *ra, return tex; } +static const struct ra_format fbo_dummy_format = { + .name = "unknown_fbo", + .priv = (void *)&(const struct gl_format){ + .name = "unknown", + .format = GL_RGBA, + .flags = F_CR, + }, + .renderable = true, +}; + +static const struct ra_format tex_dummy_format = { + .name = "unknown_tex", + .priv = (void *)&(const struct gl_format){ + .name = "unknown", + .format = GL_RGBA, + .flags = F_TF, + }, + .renderable = true, + .linear_filter = true, +}; + +static const struct ra_format *find_similar_format(struct ra *ra, + GLint gl_iformat, + GLenum gl_format, + GLenum gl_type) +{ + if (gl_iformat || gl_format || gl_type) { + for (int n = 0; n < ra->num_formats; n++) { + const struct ra_format *fmt = ra->formats[n]; + const struct gl_format *gl_fmt = fmt->priv; + if ((gl_fmt->internal_format == gl_iformat || !gl_iformat) && + (gl_fmt->format == gl_format || !gl_format) && + (gl_fmt->type == gl_type || !gl_type)) + return fmt; + } + } + return NULL; +} + +static struct ra_tex *wrap_tex_fbo(struct ra *ra, GLuint gl_obj, bool is_fbo, + GLenum gl_target, GLint gl_iformat, + GLenum gl_format, GLenum gl_type, + int w, int h) +{ + const struct ra_format *format = + find_similar_format(ra, gl_iformat, gl_format, gl_type); + if (!format) + format = is_fbo ? &fbo_dummy_format : &tex_dummy_format; + + struct ra_tex *tex = talloc_zero(ra, struct ra_tex); + *tex = (struct ra_tex){ + .params = { + .dimensions = 2, + .w = w, .h = h, .d = 1, + .format = format, + .render_dst = is_fbo, + .render_src = !is_fbo, + .non_normalized = gl_target == GL_TEXTURE_RECTANGLE, + }, + }; + + struct ra_tex_gl *tex_gl = tex->priv = talloc_zero(NULL, struct ra_tex_gl); + *tex_gl = (struct ra_tex_gl){ + .target = gl_target, + .texture = is_fbo ? 0 : gl_obj, + .fbo = is_fbo ? gl_obj : 0, + .internal_format = gl_iformat, + .format = gl_format, + .type = gl_type, + }; + + return tex; +} + +// Create a ra_tex that merely wraps an existing texture. gl_format and gl_type +// can be 0, in which case possibly nonsensical fallbacks are chosen. +// Works for 2D textures only. Integer textures are not supported. +// The returned object is freed with ra_tex_free(), but this will not delete +// the texture passed to this function. +struct ra_tex *ra_create_wrapped_texture(struct ra *ra, GLuint gl_texture, + GLenum gl_target, GLint gl_iformat, + GLenum gl_format, GLenum gl_type, + int w, int h) +{ + return wrap_tex_fbo(ra, gl_texture, false, gl_target, gl_iformat, gl_format, + gl_type, w, h); +} + +// Create a ra_tex that merely wraps an existing framebuffer. gl_fbo can be 0 +// to wrap the default framebuffer. +// The returned object is freed with ra_tex_free(), but this will not delete +// the framebuffer object passed to this function. +struct ra_tex *ra_create_wrapped_fb(struct ra *ra, GLuint gl_fbo, int w, int h) +{ + return wrap_tex_fbo(ra, gl_fbo, true, 0, GL_RGBA, 0, 0, w, h); +} + static void gl_tex_upload(struct ra *ra, struct ra_tex *tex, const void *src, ptrdiff_t stride, struct ra_mapped_buffer *buf) @@ -319,6 +421,53 @@ static bool gl_poll_mapped_buffer(struct ra *ra, struct ra_mapped_buffer *buf) return !buf_gl->fence; } +static void gl_clear(struct ra *ra, struct ra_tex *dst, float color[4], + struct mp_rect *scissor) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + + assert(dst->params.render_dst); + struct ra_tex_gl *dst_gl = dst->priv; + + gl->BindFramebuffer(GL_FRAMEBUFFER, dst_gl->fbo); + + gl->Scissor(scissor->x0, scissor->y0, + scissor->x1 - scissor->x0, + scissor->y1 - scissor->y0); + + gl->Enable(GL_SCISSOR_TEST); + gl->ClearColor(color[0], color[1], color[2], color[3]); + gl->Clear(GL_COLOR_BUFFER_BIT); + gl->Disable(GL_SCISSOR_TEST); + + gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} + +static void gl_blit(struct ra *ra, struct ra_tex *dst, struct ra_tex *src, + int dst_x, int dst_y, struct mp_rect *src_rc) +{ + struct ra_gl *p = ra->priv; + GL *gl = p->gl; + + assert(dst->params.render_dst); + assert(src->params.render_dst); // even src must have a FBO + + struct ra_tex_gl *src_gl = src->priv; + struct ra_tex_gl *dst_gl = dst->priv; + + int w = mp_rect_w(*src_rc); + int h = mp_rect_h(*src_rc); + + gl->BindFramebuffer(GL_READ_FRAMEBUFFER, src_gl->fbo); + gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, dst_gl->fbo); + gl->BlitFramebuffer(src_rc->x0, src_rc->y0, src_rc->x1, src_rc->y1, + dst_x, dst_y, dst_x + w, dst_y + h, + GL_COLOR_BUFFER_BIT, GL_NEAREST); + gl->BindFramebuffer(GL_READ_FRAMEBUFFER, 0); + gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); +} + static struct ra_fns ra_fns_gl = { .destroy = gl_destroy, .tex_create = gl_tex_create, @@ -327,4 +476,6 @@ static struct ra_fns ra_fns_gl = { .create_mapped_buffer = gl_create_mapped_buffer, .destroy_mapped_buffer = gl_destroy_mapped_buffer, .poll_mapped_buffer = gl_poll_mapped_buffer, + .clear = gl_clear, + .blit = gl_blit, }; diff --git a/video/out/opengl/ra_gl.h b/video/out/opengl/ra_gl.h index d17b576afc..23e3199aeb 100644 --- a/video/out/opengl/ra_gl.h +++ b/video/out/opengl/ra_gl.h @@ -11,9 +11,10 @@ struct ra_gl { // For ra_tex.priv struct ra_tex_gl { + bool own_objects; GLenum target; - GLuint texture; - GLuint fbo; // 0 if no rendering requested + GLuint texture; // 0 if no texture data associated + GLuint fbo; // 0 if no rendering requested, or it default framebuffer // These 3 fields can be 0 if unknown. GLint internal_format; GLenum format; @@ -28,3 +29,8 @@ struct ra_mapped_buffer_gl { }; int ra_init_gl(struct ra *ra, GL *gl); +struct ra_tex *ra_create_wrapped_texture(struct ra *ra, GLuint gl_texture, + GLenum gl_target, GLint gl_iformat, + GLenum gl_format, GLenum gl_type, + int w, int h); +struct ra_tex *ra_create_wrapped_fb(struct ra *ra, GLuint gl_fbo, int w, int h); diff --git a/video/out/opengl/video.c b/video/out/opengl/video.c index 3fa85019df..0923d626d1 100644 --- a/video/out/opengl/video.c +++ b/video/out/opengl/video.c @@ -86,14 +86,8 @@ static const struct gl_vao_entry vertex_vao[] = { }; struct texplane { - struct ra_tex *texture; + struct ra_tex *tex; int w, h; - int tex_w, tex_h; - GLint gl_internal_format; - GLenum gl_target; - GLenum gl_format; - GLenum gl_type; - GLuint gl_texture; bool flipped; }; @@ -102,6 +96,8 @@ struct video_image { struct mp_image *mpi; // original input image uint64_t id; // unique ID identifying mpi contents bool hwdec_mapped; + // Temporary wrappers for GL hwdec textures. + struct ra_tex *hwdec_tex[4]; }; enum plane_type { @@ -128,10 +124,7 @@ struct img_tex { enum plane_type type; // must be set to something non-zero int components; // number of relevant coordinates float multiplier; // multiplier to be used when sampling - GLuint gl_tex; - GLenum gl_target; - GLenum gl_format; - int tex_w, tex_h; // source texture size + struct ra_tex *tex; int w, h; // logical size (after transformation) struct gl_transform transform; // rendering transformation }; @@ -442,14 +435,6 @@ static void gl_video_setup_hooks(struct gl_video *p); #define GLSLHF(...) gl_sc_haddf(p->sc, __VA_ARGS__) #define PRELUDE(...) gl_sc_paddf(p->sc, __VA_ARGS__) -static GLuint get_fbo(struct fbotex *fbo) -{ - if (!fbo->tex) - return -1; - struct ra_tex_gl *tex_gl = fbo->tex->priv; - return tex_gl->fbo ? tex_gl->fbo : -1; -} - static struct bstr load_cached_file(struct gl_video *p, const char *path) { if (!path || !path[0]) @@ -542,6 +527,7 @@ static void uninit_rendering(struct gl_video *p) fbotex_uninit(&p->indirect_fbo); fbotex_uninit(&p->blend_subs_fbo); fbotex_uninit(&p->screen_fbo); + fbotex_uninit(&p->output_fbo); for (int n = 0; n < FBOSURFACES_MAX; n++) fbotex_uninit(&p->surfaces[n].fbotex); @@ -641,14 +627,10 @@ static struct img_tex img_tex_fbo(struct fbotex *fbo, enum plane_type type, int components) { assert(type != PLANE_NONE); - struct ra_tex_gl *tex_gl = fbo->tex->priv; return (struct img_tex){ .type = type, - .gl_tex = tex_gl->texture, - .gl_target = GL_TEXTURE_2D, + .tex = fbo->tex, .multiplier = 1.0, - .tex_w = fbo->rw, - .tex_h = fbo->rh, .w = fbo->lw, .h = fbo->lh, .transform = identity_trans, @@ -772,12 +754,8 @@ static void pass_get_img_tex(struct gl_video *p, struct video_image *vimg, tex[n] = (struct img_tex){ .type = type, - .gl_tex = t->gl_texture, - .gl_target = t->gl_target, - .gl_format = t->gl_format, + .tex = t->tex, .multiplier = tex_mul, - .tex_w = t->tex_w, - .tex_h = t->tex_h, .w = t->w, .h = t->h, }; @@ -907,35 +885,25 @@ static void init_video(struct gl_video *p) plane->w = mp_image_plane_w(&layout, n); plane->h = mp_image_plane_h(&layout, n); - plane->tex_w = plane->w + p->opts.tex_pad_x; - plane->tex_h = plane->h + p->opts.tex_pad_y; struct ra_tex_params params = { .dimensions = 2, - .w = plane->tex_w, - .h = plane->tex_h, + .w = plane->w + p->opts.tex_pad_x, + .h = plane->h + p->opts.tex_pad_y, .d = 1, .format = format, .src_linear = format->linear_filter, .non_normalized = p->opts.use_rectangle, }; - plane->texture = p->ra->fns->tex_create(p->ra, ¶ms); - if (!plane->texture) - abort(); // shit happens - - struct ra_tex_gl *tex_gl = plane->texture->priv; + MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", n, + params.w, params.h); - plane->gl_texture = tex_gl->texture; - plane->gl_target = tex_gl->target; - plane->gl_format = tex_gl->format; - plane->gl_internal_format = tex_gl->internal_format; - plane->gl_type = tex_gl->type; + plane->tex = ra_tex_create(p->ra, ¶ms); + if (!plane->tex) + abort(); // shit happens p->use_integer_conversion |= format->ctype == RA_CTYPE_UINT; - - MP_VERBOSE(p, "Texture for plane %d: %dx%d\n", n, - plane->tex_w, plane->tex_h); } } @@ -951,6 +919,8 @@ static void unmap_current_image(struct gl_video *p) if (vimg->hwdec_mapped) { assert(p->hwdec_active); + for (int n = 0; n < 4; n++) + ra_tex_free(p->ra, &vimg->hwdec_tex[n]); if (p->hwdec->driver->unmap) p->hwdec->driver->unmap(p->hwdec); memset(vimg->planes, 0, sizeof(vimg->planes)); @@ -1030,8 +1000,7 @@ static void uninit_video(struct gl_video *p) for (int n = 0; n < p->plane_count; n++) { struct texplane *plane = &vimg->planes[n]; - if (plane->texture) - p->ra->fns->tex_destroy(p->ra, plane->texture); + ra_tex_free(p->ra, &plane->tex); } *vimg = (struct video_image){0}; @@ -1107,7 +1076,7 @@ static void pass_prepare_src_tex(struct gl_video *p) for (int n = 0; n < p->pass_tex_num; n++) { struct img_tex *s = &p->pass_tex[n]; - if (!s->gl_tex) + if (!s->tex) continue; char *texture_name = mp_tprintf(32, "texture%d", n); @@ -1116,15 +1085,11 @@ static void pass_prepare_src_tex(struct gl_video *p) char *texture_off = mp_tprintf(32, "texture_off%d", n); char *pixel_size = mp_tprintf(32, "pixel_size%d", n); - if (gl_is_integer_format(s->gl_format)) { - gl_sc_uniform_tex_ui(sc, texture_name, s->gl_tex); - } else { - gl_sc_uniform_tex(sc, texture_name, s->gl_target, s->gl_tex); - } + gl_sc_uniform_texture(sc, texture_name, s->tex); float f[2] = {1, 1}; - if (s->gl_target != GL_TEXTURE_RECTANGLE) { - f[0] = s->tex_w; - f[1] = s->tex_h; + if (!s->tex->params.non_normalized) { + f[0] = s->tex->params.w; + f[1] = s->tex->params.h; } gl_sc_uniform_vec2(sc, texture_size, f); gl_sc_uniform_mat2(sc, texture_rot, true, (float *)s->transform.m); @@ -1168,15 +1133,15 @@ static void dispatch_compute(struct gl_video *p, int w, int h, for (int n = 0; n < TEXUNIT_VIDEO_NUM; n++) { struct img_tex *s = &p->pass_tex[n]; - if (!s->gl_tex) + if (!s->tex) continue; // We need to rescale the coordinates to the true texture size char tex_scale[32]; snprintf(tex_scale, sizeof(tex_scale), "tex_scale%d", n); gl_sc_uniform_vec2(p->sc, tex_scale, (GLfloat[2]){ - (float)s->w / s->tex_w, - (float)s->h / s->tex_h, + (float)s->w / s->tex->params.w, + (float)s->h / s->tex->params.h, }); PRELUDE("#define texcoord%d_raw(id) (tex_scale%d * outcoord(id))\n", n, n); @@ -1223,15 +1188,15 @@ static void render_pass_quad(struct gl_video *p, int vp_w, int vp_h, v->position.y = y[n % 2]; for (int i = 0; i < p->pass_tex_num; i++) { struct img_tex *s = &p->pass_tex[i]; - if (!s->gl_tex) + if (!s->tex) continue; struct gl_transform tr = s->transform; float tx = (n / 2) * s->w; float ty = (n % 2) * s->h; gl_transform_vec(tr, &tx, &ty); - bool rect = s->gl_target == GL_TEXTURE_RECTANGLE; - v->texcoord[i].x = tx / (rect ? 1 : s->tex_w); - v->texcoord[i].y = ty / (rect ? 1 : s->tex_h); + bool rect = s->tex->params.non_normalized; + v->texcoord[i].x = tx / (rect ? 1 : s->tex->params.w); + v->texcoord[i].y = ty / (rect ? 1 : s->tex->params.h); } } @@ -1241,13 +1206,15 @@ static void render_pass_quad(struct gl_video *p, int vp_w, int vp_h, debug_check_gl(p, "after rendering"); } -static void finish_pass_direct(struct gl_video *p, GLint fbo, int vp_w, int vp_h, - const struct mp_rect *dst) +static void finish_pass_direct(struct gl_video *p, struct ra_tex *target, + int vp_w, int vp_h, const struct mp_rect *dst) { GL *gl = p->gl; pass_prepare_src_tex(p); gl_sc_set_vertex_format(p->sc, vertex_vao, sizeof(struct vertex)); pass_record(p, gl_sc_generate(p->sc, GL_FRAGMENT_SHADER)); + struct ra_tex_gl *tex_gl = target ? target->priv : NULL; + int fbo = tex_gl ? tex_gl->fbo : 0; gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); render_pass_quad(p, vp_w, vp_h, dst); gl->BindFramebuffer(GL_FRAMEBUFFER, 0); @@ -1280,14 +1247,14 @@ static void finish_pass_fbo(struct gl_video *p, struct fbotex *dst_fbo, p->gl->MemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT); p->pass_compute = (struct compute_info){0}; } else { - finish_pass_direct(p, get_fbo(dst_fbo), dst_fbo->rw, dst_fbo->rh, + finish_pass_direct(p, dst_fbo->tex, dst_fbo->rw, dst_fbo->rh, &(struct mp_rect){0, 0, w, h}); } } static const char *get_tex_swizzle(struct img_tex *img) { - return img->gl_format == GL_LUMINANCE_ALPHA ? "raaa" : "rgba"; + return img->tex->params.format->luminance_alpha ? "raaa" : "rgba"; } // Copy a texture to the vec4 color, while increasing offset. Also applies @@ -1307,7 +1274,7 @@ static void copy_img_tex(struct gl_video *p, int *offset, struct img_tex img) dst[i] = dst_fmt[*offset + i]; } - if (gl_is_integer_format(img.gl_format)) { + if (img.tex && img.tex->params.format->ctype == RA_CTYPE_UINT) { uint64_t tex_max = 1ull << p->ra_format.component_bits; img.multiplier *= 1.0 / (tex_max - 1); } @@ -1836,10 +1803,9 @@ static bool img_tex_equiv(struct img_tex a, struct img_tex b) return a.type == b.type && a.components == b.components && a.multiplier == b.multiplier && - a.gl_target == b.gl_target && - a.gl_format == b.gl_format && - a.tex_w == b.tex_w && - a.tex_h == b.tex_h && + a.tex->params.format == b.tex->params.format && + a.tex->params.w == b.tex->params.w && + a.tex->params.h == b.tex->params.h && a.w == b.w && a.h == b.h && gl_transform_eq(a.transform, b.transform); @@ -2075,7 +2041,7 @@ static void pass_read_video(struct gl_video *p) // If any textures are still in integer format by this point, we need // to introduce an explicit conversion pass to avoid breaking hooks/scaling for (int n = 0; n < 4; n++) { - if (gl_is_integer_format(tex[n].gl_format)) { + if (tex[n].tex && tex[n].tex->params.format->ctype == RA_CTYPE_UINT) { GLSLF("// use_integer fix for plane %d\n", n); copy_img_tex(p, &(int){0}, tex[n]); pass_describe(p, "use_integer fix"); @@ -2650,12 +2616,14 @@ static void pass_dither(struct gl_video *p) // Draws the OSD, in scene-referred colors.. If cms is true, subtitles are // instead adapted to the display's gamut. static void pass_draw_osd(struct gl_video *p, int draw_flags, double pts, - struct mp_osd_res rect, int vp_w, int vp_h, int fbo, - bool cms) + struct mp_osd_res rect, int vp_w, int vp_h, + struct ra_tex *target, bool cms) { + struct ra_tex_gl *tex_gl = target->priv; + mpgl_osd_generate(p->osd, rect, pts, p->image_params.stereo_out, draw_flags); - p->gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); + p->gl->BindFramebuffer(GL_FRAMEBUFFER, tex_gl->fbo); for (int n = 0; n < MAX_OSD_PARTS; n++) { // (This returns false if this part is empty with nothing to draw.) if (!mpgl_osd_draw_prepare(p->osd, n, p->sc)) @@ -2684,10 +2652,8 @@ static float chroma_realign(int size, int pixel) } // Minimal rendering code path, for GLES or OpenGL 2.1 without proper FBOs. -static void pass_render_frame_dumb(struct gl_video *p, int fbo) +static void pass_render_frame_dumb(struct gl_video *p) { - p->gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); - struct img_tex tex[4]; struct gl_transform off[4]; pass_get_img_tex(p, &p->image, tex, off); @@ -2763,7 +2729,7 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, uint64_t }; finish_pass_fbo(p, &p->blend_subs_fbo, rect.w, rect.h, 0); pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect, - rect.w, rect.h, get_fbo(&p->blend_subs_fbo), false); + rect.w, rect.h, p->blend_subs_fbo.tex, false); pass_read_fbo(p, &p->blend_subs_fbo); pass_describe(p, "blend subs video"); } @@ -2793,7 +2759,7 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, uint64_t } finish_pass_fbo(p, &p->blend_subs_fbo, p->texture_w, p->texture_h, 0); pass_draw_osd(p, OSD_DRAW_SUB_ONLY, vpts, rect, - p->texture_w, p->texture_h, get_fbo(&p->blend_subs_fbo), + p->texture_w, p->texture_h, p->blend_subs_fbo.tex, false); pass_read_fbo(p, &p->blend_subs_fbo); pass_describe(p, "blend subs"); @@ -2804,10 +2770,10 @@ static bool pass_render_frame(struct gl_video *p, struct mp_image *mpi, uint64_t return true; } -static void pass_draw_to_screen(struct gl_video *p, int fbo) +static void pass_draw_to_screen(struct gl_video *p, struct ra_tex *fbo) { if (p->dumb_mode) - pass_render_frame_dumb(p, fbo); + pass_render_frame_dumb(p); // Adjust the overall gamma before drawing to screen if (p->user_gamma != 1) { @@ -2878,7 +2844,7 @@ static bool update_fbosurface(struct gl_video *p, struct mp_image *mpi, // Draws an interpolate frame to fbo, based on the frame timing in t static void gl_video_interpolate_frame(struct gl_video *p, struct vo_frame *t, - int fbo) + struct ra_tex *fbo) { bool is_new = false; @@ -3069,9 +3035,11 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) } } - p->broken_frame = false; + struct ra_tex *target = + ra_create_wrapped_fb(p->ra, fbo, p->vp_w, abs(p->vp_h)); + struct mp_rect target_rc = {0, 0, p->vp_w, abs(p->vp_h)}; - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); + p->broken_frame = false; bool has_frame = !!frame->current; @@ -3079,20 +3047,14 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) p->dst_rect.x1 < p->vp_w || p->dst_rect.y1 < abs(p->vp_h)) { struct m_color c = p->opts.background; - gl->ClearColor(c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0); - gl->Clear(GL_COLOR_BUFFER_BIT); + float color[4] = {c.r / 255.0, c.g / 255.0, c.b / 255.0, c.a / 255.0}; + p->ra->fns->clear(p->ra, target, color, &target_rc); } if (p->hwdec_active && p->hwdec->driver->overlay_frame) { if (has_frame) { - float *c = p->hwdec->overlay_colorkey; - gl->Scissor(p->dst_rect.x0, p->dst_rect.y0, - p->dst_rect.x1 - p->dst_rect.x0, - p->dst_rect.y1 - p->dst_rect.y0); - gl->Enable(GL_SCISSOR_TEST); - gl->ClearColor(c[0], c[1], c[2], c[3]); - gl->Clear(GL_COLOR_BUFFER_BIT); - gl->Disable(GL_SCISSOR_TEST); + float *color = p->hwdec->overlay_colorkey; + p->ra->fns->clear(p->ra, target, color, &p->dst_rect); } if (frame->frame_id != p->image.id || !frame->current) @@ -3115,7 +3077,7 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) } if (interpolate) { - gl_video_interpolate_frame(p, frame, fbo); + gl_video_interpolate_frame(p, frame, target); } else { bool is_new = frame->frame_id != p->image.id; @@ -3132,14 +3094,14 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) // For the non-interpolation case, we draw to a single "cache" // FBO to speed up subsequent re-draws (if any exist) - int dest_fbo = fbo; + struct ra_tex *dest_fbo = target; if (frame->num_vsyncs > 1 && frame->display_synced && - !p->dumb_mode && gl->BlitFramebuffer) + !p->dumb_mode && (p->ra->caps & RA_CAP_BLIT)) { fbotex_change(&p->output_fbo, p->ra, p->log, p->vp_w, abs(p->vp_h), p->fbo_format, FBOTEX_FUZZY); - dest_fbo = get_fbo(&p->output_fbo); + dest_fbo = p->output_fbo.tex; p->output_fbo_valid = true; } pass_draw_to_screen(p, dest_fbo); @@ -3149,20 +3111,15 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo) if (p->output_fbo_valid) { pass_info_reset(p, true); pass_describe(p, "redraw cached frame"); - gl->BindFramebuffer(GL_READ_FRAMEBUFFER, get_fbo(&p->output_fbo)); - gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo); struct mp_rect rc = p->dst_rect; if (p->vp_h < 0) { rc.y1 = -p->vp_h - p->dst_rect.y0; rc.y0 = -p->vp_h - p->dst_rect.y1; } gl_timer_start(p->blit_timer); - gl->BlitFramebuffer(rc.x0, rc.y0, rc.x1, rc.y1, - rc.x0, rc.y0, rc.x1, rc.y1, - GL_COLOR_BUFFER_BIT, GL_NEAREST); + p->ra->fns->blit(p->ra, target, p->output_fbo.tex, + rc.x0, rc.y0, &rc); gl_timer_stop(gl); - gl->BindFramebuffer(GL_READ_FRAMEBUFFER, 0); - gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); pass_record(p, gl_timer_measure(p->blit_timer)); } } @@ -3174,8 +3131,6 @@ done: debug_check_gl(p, "after video rendering"); - gl->BindFramebuffer(GL_FRAMEBUFFER, fbo); - if (p->osd) { // If we haven't actually drawn anything so far, then we technically // need to consider this the start of a new pass. Let's call it a @@ -3184,20 +3139,21 @@ done: pass_info_reset(p, true); pass_draw_osd(p, p->opts.blend_subs ? OSD_DRAW_OSD_ONLY : 0, - p->osd_pts, p->osd_rect, p->vp_w, p->vp_h, fbo, true); + p->osd_pts, p->osd_rect, p->vp_w, p->vp_h, target, true); debug_check_gl(p, "after OSD rendering"); } - gl->UseProgram(0); if (gl_sc_error_state(p->sc) || p->broken_frame) { // Make the screen solid blue to make it visually clear that an // error has occurred - gl->ClearColor(0.0, 0.05, 0.5, 1.0); - gl->Clear(GL_COLOR_BUFFER_BIT); + float color[4] = {0.0, 0.05, 0.5, 1.0}; + p->ra->fns->clear(p->ra, target, color, &target_rc); } gl->BindFramebuffer(GL_FRAMEBUFFER, 0); + ra_tex_free(p->ra, &target); + // The playloop calls this last before waiting some time until it decides // to call flip_page(). Tell OpenGL to start execution of the GPU commands // while we sleep (this happens asynchronously). @@ -3251,7 +3207,8 @@ void gl_video_perfdata(struct gl_video *p, struct voctrl_performance_data *out) } // This assumes nv12, with textures set to GL_NEAREST filtering. -static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame) +static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame, + struct ra_tex *output[4]) { struct gl_hwdec_frame res = {0}; for (int n = 0; n < 2; n++) { @@ -3261,14 +3218,14 @@ static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame) int w = src[0].tex_w; int h = src[0].tex_h; int ids[2]; + struct ra_tex *tmp[2]; for (int t = 0; t < 2; t++) { + tmp[t] = ra_create_wrapped_texture(p->ra, src[t].gl_texture, + GL_TEXTURE_2D, 0, 0, 0, w, h); ids[t] = pass_bind(p, (struct img_tex){ - .gl_tex = src[t].gl_texture, - .gl_target = src[t].gl_target, + .tex = tmp[t], .multiplier = 1.0, .transform = identity_trans, - .tex_w = w, - .tex_h = h, .w = w, .h = h, }); @@ -3283,20 +3240,13 @@ static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame) fbotex_change(fbo, p->ra, p->log, w, h * 2, fmt, 0); pass_describe(p, "vdpau reinterleaving"); - finish_pass_direct(p, get_fbo(fbo), fbo->rw, fbo->rh, + finish_pass_direct(p, fbo->tex, fbo->rw, fbo->rh, &(struct mp_rect){0, 0, w, h * 2}); - GLuint tex = 0; - if (fbo->tex) { - struct ra_tex_gl *tex_gl = fbo->tex->priv; - tex = tex_gl->texture; - } - res.planes[n] = (struct gl_hwdec_plane){ - .gl_texture = tex, - .gl_target = GL_TEXTURE_2D, - .tex_w = w, - .tex_h = h * 2, - }; + for (int t = 0; t < 2; t++) + ra_tex_free(p->ra, &tmp[t]); + + output[n] = fbo->tex; } *frame = res; } @@ -3335,18 +3285,20 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t if (ok) { struct mp_image layout = {0}; mp_image_set_params(&layout, &p->image_params); + struct ra_tex *tex[4] = {0}; if (gl_frame.vdpau_fields) - reinterleave_vdpau(p, &gl_frame); + reinterleave_vdpau(p, &gl_frame, tex); for (int n = 0; n < p->plane_count; n++) { struct gl_hwdec_plane *plane = &gl_frame.planes[n]; + if (!tex[n]) { + vimg->hwdec_tex[n] = ra_create_wrapped_texture(p->ra, + plane->gl_texture, plane->gl_target, 0, + plane->gl_format, 0, plane->tex_w, plane->tex_h); + } vimg->planes[n] = (struct texplane){ .w = mp_image_plane_w(&layout, n), .h = mp_image_plane_h(&layout, n), - .tex_w = plane->tex_w, - .tex_h = plane->tex_h, - .gl_target = plane->gl_target, - .gl_texture = plane->gl_texture, - .gl_format = plane->gl_format, + .tex = tex[n] ? tex[n] : vimg->hwdec_tex[n], }; } } else { @@ -3366,11 +3318,11 @@ static bool pass_upload_image(struct gl_video *p, struct mp_image *mpi, uint64_t plane->flipped = mpi->stride[0] < 0; // (It's unclear whether this should be changeable on the fly.) - plane->texture->use_pbo = p->opts.pbo; + plane->tex->use_pbo = p->opts.pbo; struct dr_buffer *mapped = gl_find_dr_buffer(p, mpi->planes[n]); - p->ra->fns->tex_upload(p->ra, plane->texture, mpi->planes[n], + p->ra->fns->tex_upload(p->ra, plane->tex, mpi->planes[n], mpi->stride[n], mapped ? mapped->buffer : NULL); if (mapped && !mapped->mpi) -- cgit v1.2.3