vo_opengl: make fbotex helper use ra

Further work removing GL dependencies from the actual video renderer,
and moving them into ra backends.

Use of glInvalidateFramebuffer() falls away. I'd like to keep this, but
it's better to readd it once shader runs are in ra.
This commit is contained in:
wm4 2017-08-04 13:48:37 +02:00
parent 90b53fede6
commit a796745fd2
9 changed files with 157 additions and 131 deletions

View File

@ -27,6 +27,9 @@ Interface changes
warning)
- drop deprecated --video-aspect-method=hybrid option choice
- rename --hdr-tone-mapping to --tone-mapping (and generalize it)
- --opengl-fbo-format changes from a choice to a string. Also, its value
will be checked only on renderer initialization, rather than when the
option is set.
--- mpv 0.26.0 ---
- remove remaining deprecated audio device options, like --alsa-device
Some of them were removed in earlier releases.

View File

@ -80,6 +80,15 @@ const struct ra_format *ra_find_float16_format(struct ra *ra, int n_components)
return NULL;
}
const struct ra_format *ra_find_named_format(struct ra *ra, const char *name)
{
for (int n = 0; n < ra->num_formats; n++) {
const struct ra_format *fmt = ra->formats[n];
if (strcmp(fmt->name, name) == 0)
return fmt;
}
return NULL;
}
// Like ra_find_unorm_format(), but if no fixed point format is available,
// return an unsigned integer format.

View File

@ -76,6 +76,13 @@ struct ra_tex_params {
void *initial_data;
};
// Conflates the following typical GPU API concepts:
// - texture itself
// - sampler state
// - staging buffers for texture upload
// - framebuffer objects
// - wrappers for swapchain framebuffers
// - synchronization needed for upload/rendering/etc.
struct ra_tex {
// All fields are read-only after creation.
struct ra_tex_params params;
@ -149,6 +156,7 @@ const struct ra_format *ra_find_uint_format(struct ra *ra,
int bytes_per_component,
int n_components);
const struct ra_format *ra_find_float16_format(struct ra *ra, int n_components);
const struct ra_format *ra_find_named_format(struct ra *ra, const char *name);
struct ra_imgfmt_desc {
int num_planes;

View File

@ -42,7 +42,8 @@ int ra_init_gl(struct ra *ra, GL *gl)
.pixel_size = gl_bytes_per_pixel(gl_fmt->format, gl_fmt->type),
.luminance_alpha = gl_fmt->format == GL_LUMINANCE_ALPHA,
.linear_filter = gl_fmt->flags & F_TF,
.renderable = gl_fmt->flags & F_CR,
.renderable = (gl_fmt->flags & F_CR) &&
(gl->mpgl_caps & MPGL_CAP_FB),
};
int csize = gl_component_size(gl_fmt->type) * 8;
@ -100,6 +101,9 @@ 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);
p->gl->DeleteTextures(1, &tex_gl->texture);
gl_pbo_upload_uninit(&tex_gl->pbo);
talloc_free(tex_gl);
@ -167,6 +171,36 @@ static struct ra_tex *gl_tex_create(struct ra *ra,
tex->params.initial_data = NULL;
gl_check_error(gl, ra->log, "after creating texture");
if (tex->params.render_dst) {
if (!tex->params.format->renderable) {
MP_ERR(ra, "Trying to create renderable texture with unsupported "
"format.\n");
ra_tex_free(ra, &tex);
return NULL;
}
assert(gl->mpgl_caps & MPGL_CAP_FB);
gl->GenFramebuffers(1, &tex_gl->fbo);
gl->BindFramebuffer(GL_FRAMEBUFFER, tex_gl->fbo);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, tex_gl->texture, 0);
GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
if (err != GL_FRAMEBUFFER_COMPLETE) {
MP_ERR(ra, "Error: framebuffer completeness check failed (error=%d).\n",
(int)err);
ra_tex_free(ra, &tex);
return NULL;
}
gl_check_error(gl, ra->log, "after creating framebuffer");
}
return tex;
}
@ -294,4 +328,3 @@ static struct ra_fns ra_fns_gl = {
.destroy_mapped_buffer = gl_destroy_mapped_buffer,
.poll_mapped_buffer = gl_poll_mapped_buffer,
};

View File

@ -13,6 +13,7 @@ struct ra_gl {
struct ra_tex_gl {
GLenum target;
GLuint texture;
GLuint fbo; // 0 if no rendering requested
// These 3 fields can be 0 if unknown.
GLint internal_format;
GLenum format;

View File

@ -254,37 +254,36 @@ static void gl_vao_draw_data(struct gl_vao *vao, GLenum prim, void *ptr, size_t
}
// Create a texture and a FBO using the texture as color attachments.
// iformat: texture internal format
// fmt: texture internal format
// Returns success.
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
GLenum iformat)
bool fbotex_init(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
int w, int h, const struct ra_format *fmt)
{
assert(!fbo->fbo);
assert(!fbo->texture);
return fbotex_change(fbo, gl, log, w, h, iformat, 0);
assert(!fbo->tex);
return fbotex_change(fbo, ra, log, w, h, fmt, 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 fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
int w, int h, const struct ra_format *fmt, int flags)
{
bool res = true;
if (fbo->tex) {
int cw = w, ch = h;
int rw = fbo->tex->params.w, rh = fbo->tex->params.h;
int cw = w, ch = h;
if ((flags & FBOTEX_FUZZY_W) && cw < rw)
cw = rw;
if ((flags & FBOTEX_FUZZY_H) && ch < rh)
ch = rh;
if ((flags & FBOTEX_FUZZY_W) && cw < fbo->rw)
cw = fbo->rw;
if ((flags & FBOTEX_FUZZY_H) && ch < fbo->rh)
ch = fbo->rh;
if (fbo->rw == cw && fbo->rh == ch && fbo->iformat == iformat) {
fbo->lw = w;
fbo->lh = h;
fbotex_invalidate(fbo);
return true;
if (rw == cw && rh == ch && fbo->tex->params.format == fmt) {
fbo->lw = w;
fbo->lh = h;
return true;
}
}
int lw = w, lh = h;
@ -296,80 +295,51 @@ bool fbotex_change(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
mp_verbose(log, "Create FBO: %dx%d (%dx%d)\n", lw, lh, w, h);
const struct gl_format *format = gl_find_internal_format(gl, iformat);
if (!format || (format->flags & F_CF) != F_CF) {
mp_verbose(log, "Format 0x%x not supported.\n", (unsigned)iformat);
if (!fmt || !fmt->renderable || !fmt->linear_filter) {
mp_err(log, "Format %s not supported.\n", fmt ? fmt->name : "(unset)");
return false;
}
assert(gl->mpgl_caps & MPGL_CAP_FB);
fbotex_uninit(fbo);
*fbo = (struct fbotex) {
.gl = gl,
.ra = ra,
.rw = w,
.rh = h,
.lw = lw,
.lh = lh,
.iformat = iformat,
};
gl->GenFramebuffers(1, &fbo->fbo);
gl->GenTextures(1, &fbo->texture);
gl->BindTexture(GL_TEXTURE_2D, fbo->texture);
gl->TexImage2D(GL_TEXTURE_2D, 0, format->internal_format, fbo->rw, fbo->rh, 0,
format->format, format->type, 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->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
gl->TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
gl->BindTexture(GL_TEXTURE_2D, 0);
struct ra_tex_params params = {
.dimensions = 2,
.w = w,
.h = h,
.d = 1,
.format = fmt,
.src_linear = true,
.render_src = true,
.render_dst = true,
};
gl_check_error(gl, log, "after creating framebuffer texture");
fbo->tex = ra_tex_create(fbo->ra, &params);
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;
if (!fbo->tex) {
mp_err(log, "Error: framebuffer could not be created.\n");
fbotex_uninit(fbo);
return false;
}
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
gl_check_error(gl, log, "after creating framebuffer");
return res;
return true;
}
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);
if (fbo->ra) {
ra_tex_free(fbo->ra, &fbo->tex);
*fbo = (struct fbotex) {0};
}
}
// Mark framebuffer contents as unneeded.
void fbotex_invalidate(struct fbotex *fbo)
{
GL *gl = fbo->gl;
if (!fbo->fbo || !gl->InvalidateFramebuffer)
return;
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
gl->InvalidateFramebuffer(GL_FRAMEBUFFER, 1,
(GLenum[]){GL_COLOR_ATTACHMENT0});
gl->BindFramebuffer(GL_FRAMEBUFFER, 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,

View File

@ -51,23 +51,20 @@ struct gl_vao_entry {
};
struct fbotex {
GL *gl;
GLuint fbo;
GLuint texture;
GLenum iformat;
int rw, rh; // real (texture) size
int lw, lh; // logical (configured) size
struct ra *ra;
struct ra_tex *tex;
int rw, rh; // real (texture) size, same as tex->params.w/h
int lw, lh; // logical (configured) size, <= than texture size
};
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
GLenum iformat);
bool fbotex_init(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
int w, int h, const struct ra_format *fmt);
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);
bool fbotex_change(struct fbotex *fbo, struct ra *ra, struct mp_log *log,
int w, int h, const struct ra_format *fmt, int flags);
#define FBOTEX_FUZZY_W 1
#define FBOTEX_FUZZY_H 2
#define FBOTEX_FUZZY (FBOTEX_FUZZY_W | FBOTEX_FUZZY_H)
void fbotex_invalidate(struct fbotex *fbo);
// A 3x2 matrix, with the translation part separate.
struct gl_transform {

View File

@ -227,6 +227,7 @@ struct gl_video {
bool dumb_mode;
bool forced_dumb_mode;
const struct ra_format *fbo_format;
struct fbotex merge_fbo[4];
struct fbotex scale_fbo[4];
struct fbotex integer_fbo[4];
@ -307,7 +308,7 @@ static const struct gl_video_opts gl_video_opts_def = {
.dither_depth = -1,
.dither_size = 6,
.temporal_dither_period = 1,
.fbo_format = 0,
.fbo_format = "auto",
.sigmoid_center = 0.75,
.sigmoid_slope = 6.5,
.scaler = {
@ -385,19 +386,7 @@ const struct m_sub_options gl_video_conf = {
OPT_FLAG("sigmoid-upscaling", sigmoid_upscaling, 0),
OPT_FLOATRANGE("sigmoid-center", sigmoid_center, 0, 0.0, 1.0),
OPT_FLOATRANGE("sigmoid-slope", sigmoid_slope, 0, 1.0, 20.0),
OPT_CHOICE("opengl-fbo-format", fbo_format, 0,
({"rgb8", GL_RGB8},
{"rgba8", GL_RGBA8},
{"rgb10", GL_RGB10},
{"rgb10_a2", GL_RGB10_A2},
{"rgb16", GL_RGB16},
{"rgb16f", GL_RGB16F},
{"rgb32f", GL_RGB32F},
{"rgba12", GL_RGBA12},
{"rgba16", GL_RGBA16},
{"rgba16f", GL_RGBA16F},
{"rgba32f", GL_RGBA32F},
{"auto", 0})),
OPT_STRING("opengl-fbo-format", fbo_format, 0),
OPT_CHOICE_OR_INT("dither-depth", dither_depth, 0, -1, 16,
({"no", -1}, {"auto", 0})),
OPT_CHOICE("dither", dither_algo, 0,
@ -453,6 +442,14 @@ 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])
@ -644,9 +641,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 = fbo->texture,
.gl_tex = tex_gl->texture,
.gl_target = GL_TEXTURE_2D,
.multiplier = 1.0,
.tex_w = fbo->rw,
@ -1267,11 +1265,14 @@ static void finish_pass_direct(struct gl_video *p, GLint fbo, int vp_w, int vp_h
static void finish_pass_fbo(struct gl_video *p, struct fbotex *dst_fbo,
int w, int h, int flags)
{
fbotex_change(dst_fbo, p->gl, p->log, w, h, p->opts.fbo_format, flags);
fbotex_change(dst_fbo, p->ra, p->log, w, h, p->fbo_format, flags);
if (p->pass_compute.active) {
gl_sc_uniform_image2D(p->sc, "out_image", dst_fbo->texture,
dst_fbo->iformat, GL_WRITE_ONLY);
if (!dst_fbo->tex)
return;
struct ra_tex_gl *tex_gl = dst_fbo->tex->priv;
gl_sc_uniform_image2D(p->sc, "out_image", tex_gl->texture,
tex_gl->internal_format, GL_WRITE_ONLY);
if (!p->pass_compute.directly_writes)
GLSL(imageStore(out_image, ivec2(gl_GlobalInvocationID), color);)
@ -1279,7 +1280,7 @@ 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, dst_fbo->fbo, dst_fbo->rw, dst_fbo->rh,
finish_pass_direct(p, get_fbo(dst_fbo), dst_fbo->rw, dst_fbo->rh,
&(struct mp_rect){0, 0, w, h});
}
}
@ -2762,7 +2763,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, p->blend_subs_fbo.fbo, false);
rect.w, rect.h, get_fbo(&p->blend_subs_fbo), false);
pass_read_fbo(p, &p->blend_subs_fbo);
pass_describe(p, "blend subs video");
}
@ -2792,7 +2793,8 @@ 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, p->blend_subs_fbo.fbo, false);
p->texture_w, p->texture_h, get_fbo(&p->blend_subs_fbo),
false);
pass_read_fbo(p, &p->blend_subs_fbo);
pass_describe(p, "blend subs");
}
@ -3134,10 +3136,10 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
if (frame->num_vsyncs > 1 && frame->display_synced &&
!p->dumb_mode && gl->BlitFramebuffer)
{
fbotex_change(&p->output_fbo, p->gl, p->log,
fbotex_change(&p->output_fbo, p->ra, p->log,
p->vp_w, abs(p->vp_h),
p->opts.fbo_format, FBOTEX_FUZZY);
dest_fbo = p->output_fbo.fbo;
p->fbo_format, FBOTEX_FUZZY);
dest_fbo = get_fbo(&p->output_fbo);
p->output_fbo_valid = true;
}
pass_draw_to_screen(p, dest_fbo);
@ -3147,7 +3149,7 @@ 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, p->output_fbo.fbo);
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) {
@ -3276,14 +3278,21 @@ static void reinterleave_vdpau(struct gl_video *p, struct gl_hwdec_frame *frame)
GLSLF(" ? texture(texture%d, texcoord%d)\n", ids[0], ids[0]);
GLSLF(" : texture(texture%d, texcoord%d);", ids[1], ids[1]);
fbotex_change(fbo, p->gl, p->log, w, h * 2, n == 0 ? GL_R8 : GL_RG8, 0);
const struct ra_format *fmt =
ra_find_unorm_format(p->ra, 1, n == 0 ? 1 : 2);
fbotex_change(fbo, p->ra, p->log, w, h * 2, fmt, 0);
pass_describe(p, "vdpau reinterleaving");
finish_pass_direct(p, fbo->fbo, fbo->rw, fbo->rh,
finish_pass_direct(p, get_fbo(fbo), 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 = fbo->texture,
.gl_texture = tex,
.gl_target = GL_TEXTURE_2D,
.tex_w = w,
.tex_h = h * 2,
@ -3385,19 +3394,12 @@ error:
return false;
}
static bool test_fbo(struct gl_video *p, GLint format)
static bool test_fbo(struct gl_video *p, const struct ra_format *fmt)
{
GL *gl = p->gl;
bool success = false;
MP_VERBOSE(p, "Testing FBO format 0x%x\n", (unsigned)format);
MP_VERBOSE(p, "Testing FBO format %s\n", fmt->name);
struct fbotex fbo = {0};
if (fbotex_init(&fbo, p->gl, p->log, 16, 16, format)) {
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
success = true;
}
bool success = fbotex_init(&fbo, p->ra, p->log, 16, 16, fmt);
fbotex_uninit(&fbo);
gl_check_error(gl, p->log, "FBO test");
return success;
}
@ -3443,18 +3445,21 @@ static void check_gl_features(struct gl_video *p)
bool have_compute = gl->mpgl_caps & MPGL_CAP_COMPUTE_SHADER;
bool have_ssbo = gl->mpgl_caps & MPGL_CAP_SSBO;
const GLint auto_fbo_fmts[] = {GL_RGBA16, GL_RGBA16F, GL_RGB10_A2,
GL_RGBA8, 0};
GLint user_fbo_fmts[] = {p->opts.fbo_format, 0};
const GLint *fbo_fmts = user_fbo_fmts[0] ? user_fbo_fmts : auto_fbo_fmts;
const char *auto_fbo_fmts[] = {"rgba16", "rgba16f", "rgb10_a2", "rgba8", 0};
const char *user_fbo_fmts[] = {p->opts.fbo_format, 0};
const char **fbo_fmts = user_fbo_fmts[0] && strcmp(user_fbo_fmts[0], "auto")
? user_fbo_fmts : auto_fbo_fmts;
bool have_fbo = false;
p->fbo_format = NULL;
for (int n = 0; fbo_fmts[n]; n++) {
GLint fmt = fbo_fmts[n];
const struct gl_format *f = gl_find_internal_format(gl, fmt);
if (f && (f->flags & F_CF) == F_CF && test_fbo(p, fmt)) {
MP_VERBOSE(p, "Using FBO format 0x%x.\n", (unsigned)fmt);
const char *fmt = fbo_fmts[n];
const struct ra_format *f = ra_find_named_format(p->ra, fmt);
if (!f && fbo_fmts == user_fbo_fmts)
MP_WARN(p, "FBO format '%s' not found!\n", fmt);
if (f && f->renderable && f->linear_filter && test_fbo(p, f)) {
MP_VERBOSE(p, "Using FBO format %s.\n", f->name);
have_fbo = true;
p->opts.fbo_format = fmt;
p->fbo_format = f;
break;
}
}

View File

@ -126,7 +126,7 @@ struct gl_video_opts {
int dither_size;
int temporal_dither;
int temporal_dither_period;
int fbo_format;
char *fbo_format;
int alpha_mode;
int use_rectangle;
struct m_color background;