1
0
mirror of https://github.com/mpv-player/mpv synced 2025-02-16 20:27:23 +00:00

vo_opengl: move FBO helper to gl_utils

This is somewhat messy, because fbotex_init() itself was depending on
some gl_video parameters unrelated to FBO creation (like what scaler was
in use - what the fuck did this check do in this function?), so this
commit does a bit more than moving code around. In particular, the FBO
for the separate scaling intermediate step now always uses GL_NEAREST
sampling, and all FBOs are destroyed/recreated on renderer
reinitialization.

This also moves the function matrix_ortho2d() - trivial enough not to
put it into a separate commit.
This commit is contained in:
wm4 2015-01-29 14:58:26 +01:00
parent bf8abc0ca9
commit 14bbbffa99
3 changed files with 122 additions and 118 deletions

View File

@ -297,3 +297,80 @@ void gl_vao_bind_attribs(struct gl_vao *vao, GLuint program)
for (int n = 0; vao->entries[n].name; n++)
gl->BindAttribLocation(program, n, vao->entries[n].name);
}
// 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)
{
bool res = true;
assert(!fbo->fbo);
assert(!fbo->texture);
*fbo = (struct fbotex) {
.gl = gl,
.vp_w = w,
.vp_h = h,
.tex_w = w,
.tex_h = h,
};
mp_verbose(log, "Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
if (!(gl->mpgl_caps & MPGL_CAP_FB))
return false;
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_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);
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);
GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
if (err != GL_FRAMEBUFFER_COMPLETE) {
mp_err(log, "Error: framebuffer completeness check failed (error=%d).\n",
(int)err);
res = false;
}
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
glCheckError(gl, log, "after creating framebuffer");
return res;
}
void fbotex_uninit(struct fbotex *fbo)
{
GL *gl = fbo->gl;
if (gl && (gl->mpgl_caps & MPGL_CAP_FB)) {
gl->DeleteFramebuffers(1, &fbo->fbo);
gl->DeleteTextures(1, &fbo->texture);
*fbo = (struct fbotex) {0};
}
}
void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1)
{
memset(m, 0, 9 * sizeof(float));
m[0][0] = 2.0f / (x1 - x0);
m[1][1] = 2.0f / (y1 - y0);
m[2][0] = -(x1 + x0) / (x1 - x0);
m[2][1] = -(y1 + y0) / (y1 - y0);
m[2][2] = 1.0f;
}

View File

@ -66,4 +66,18 @@ 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);
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
};
bool fbotex_init(struct fbotex *fbo, GL *gl, struct mp_log *log, int w, int h,
GLenum gl_target, GLenum gl_filter, GLenum iformat);
void fbotex_uninit(struct fbotex *fbo);
void gl_matrix_ortho2d(float m[3][3], float x0, float x1, float y0, float y1);
#endif

View File

@ -119,13 +119,6 @@ struct scaler {
struct filter_kernel kernel_storage;
};
struct fbotex {
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
};
struct fbosurface {
struct fbotex fbotex;
int64_t pts;
@ -596,105 +589,11 @@ static void write_quad(struct vertex *va,
}
}
static bool fbotex_init(struct gl_video *p, struct fbotex *fbo, int w, int h,
GLenum iformat)
{
GL *gl = p->gl;
bool res = true;
assert(!fbo->fbo);
assert(!fbo->texture);
*fbo = (struct fbotex) {
.vp_w = w,
.vp_h = h,
};
texture_size(p, w, h, &fbo->tex_w, &fbo->tex_h);
MP_VERBOSE(p, "Create FBO: %dx%d\n", fbo->tex_w, fbo->tex_h);
if (!(gl->mpgl_caps & MPGL_CAP_FB))
return false;
gl->GenFramebuffers(1, &fbo->fbo);
gl->GenTextures(1, &fbo->texture);
gl->BindTexture(p->gl_target, fbo->texture);
gl->TexImage2D(p->gl_target, 0, iformat,
fbo->tex_w, fbo->tex_h, 0,
GL_RGBA, GL_UNSIGNED_BYTE, NULL);
default_tex_params(gl, p->gl_target);
// Convolution filters don't need linear sampling, so using nearest is
// often faster.
if (p->scalers[0].kernel) {
gl->TexParameteri(p->gl_target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
gl->TexParameteri(p->gl_target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
}
debug_check_gl(p, "after creating framebuffer texture");
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo->fbo);
gl->FramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
p->gl_target, fbo->texture, 0);
GLenum err = gl->CheckFramebufferStatus(GL_FRAMEBUFFER);
if (err != GL_FRAMEBUFFER_COMPLETE) {
MP_ERR(p, "Error: framebuffer completeness check failed (error=%d).\n",
(int)err);
res = false;
}
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
debug_check_gl(p, "after creating framebuffer");
return res;
}
static void fbotex_uninit(struct gl_video *p, struct fbotex *fbo)
{
GL *gl = p->gl;
if (gl->mpgl_caps & MPGL_CAP_FB) {
gl->DeleteFramebuffers(1, &fbo->fbo);
gl->DeleteTextures(1, &fbo->texture);
*fbo = (struct fbotex) {0};
}
}
static void fbosurfaces_uninit(struct gl_video *p, struct fbosurface *surfaces)
{
for (int i = 0; i < FBOSURFACES_MAX; i++)
if (surfaces[i].fbotex.fbo)
fbotex_uninit(p, &surfaces[i].fbotex);
}
static void fbosurfaces_init(struct gl_video *p, struct fbosurface *surfaces,
int w, int h, GLenum iformat)
{
for (int i = 0; i < FBOSURFACES_MAX; i++)
if (!surfaces[i].fbotex.fbo)
fbotex_init(p, &surfaces[i].fbotex, w, h, iformat);
}
static size_t fbosurface_next(struct gl_video *p)
{
return (p->surface_num + 1) % FBOSURFACES_MAX;
}
static void matrix_ortho2d(float m[3][3], float x0, float x1,
float y0, float y1)
{
memset(m, 0, 9 * sizeof(float));
m[0][0] = 2.0f / (x1 - x0);
m[1][1] = 2.0f / (y1 - y0);
m[2][0] = -(x1 + x0) / (x1 - x0);
m[2][1] = -(y1 + y0) / (y1 - y0);
m[2][2] = 1.0f;
}
static void update_uniforms(struct gl_video *p, GLuint program)
{
GL *gl = p->gl;
@ -723,7 +622,7 @@ static void update_uniforms(struct gl_video *p, GLuint program)
int vvp[2] = {p->vp_h, 0};
if (p->vp_vflipped)
MPSWAP(int, vvp[0], vvp[1]);
matrix_ortho2d(matrix, 0, p->vp_w, vvp[0], vvp[1]);
gl_matrix_ortho2d(matrix, 0, p->vp_w, vvp[0], vvp[1]);
gl->UniformMatrix3fv(loc, 1, GL_FALSE, &matrix[0][0]);
}
@ -1550,6 +1449,8 @@ static const char *expected_scaler(struct gl_video *p, int unit)
static void reinit_rendering(struct gl_video *p)
{
GL *gl = p->gl;
MP_VERBOSE(p, "Reinit rendering.\n");
debug_check_gl(p, "before scaler initialization");
@ -1573,15 +1474,25 @@ static void reinit_rendering(struct gl_video *p)
int w = p->image_w;
int h = p->image_h;
if (p->indirect_program && !p->indirect_fbo.fbo)
fbotex_init(p, &p->indirect_fbo, w, h, p->opts.fbo_format);
// Convolution filters don't need linear sampling, so using nearest is
// often faster.
GLenum filter = p->scalers[0].kernel ? GL_NEAREST : GL_LINEAR;
if (p->inter_program && !p->inter_fbo.fbo) {
fbotex_init(p, &p->inter_fbo, w, h, p->opts.fbo_format);
if (p->indirect_program) {
fbotex_init(&p->indirect_fbo, gl, p->log, w, h, p->gl_target, filter,
p->opts.fbo_format);
}
if (p->inter_program) {
fbosurfaces_init(p, p->surfaces, w, h, p->opts.fbo_format);
fbotex_init(&p->inter_fbo, gl, p->log, w, h, p->gl_target, filter,
p->opts.fbo_format);
}
if (p->inter_program) {
for (int i = 0; i < FBOSURFACES_MAX; i++) {
fbotex_init(&p->surfaces[i].fbotex, gl, p->log, w, h, p->gl_target,
filter, p->opts.fbo_format);
}
}
recreate_osd(p);
@ -1603,7 +1514,13 @@ static void uninit_rendering(struct gl_video *p)
gl->DeleteTextures(1, &p->dither_texture);
p->dither_texture = 0;
fbotex_uninit(p, &p->indirect_fbo);
fbotex_uninit(&p->indirect_fbo);
fbotex_uninit(&p->inter_fbo);
for (int i = 0; i < FBOSURFACES_MAX; i++)
fbotex_uninit(&p->surfaces[i].fbotex);
fbotex_uninit(&p->scale_sep_fbo);
}
void gl_video_set_lut3d(struct gl_video *p, struct lut3d *lut3d)
@ -1784,11 +1701,6 @@ static void uninit_video(struct gl_video *p)
}
mp_image_unrefp(&vimg->mpi);
fbotex_uninit(p, &p->inter_fbo);
fbotex_uninit(p, &p->indirect_fbo);
fbotex_uninit(p, &p->scale_sep_fbo);
fbosurfaces_uninit(p, p->surfaces);
// Invalidate image_params to ensure that gl_video_config() will call
// init_video() on uninitialized gl_video.
p->image_params = (struct mp_image_params){0};
@ -2023,12 +1935,12 @@ static void update_window_sized_objects(struct gl_video *p)
if ((p->image_params.rotate % 180) == 90)
MPSWAP(int, w, h);
if (h > p->scale_sep_fbo.tex_h) {
fbotex_uninit(p, &p->scale_sep_fbo);
fbotex_uninit(&p->scale_sep_fbo);
// Round up to an arbitrary alignment to make window resizing or
// panscan controls smoother (less texture reallocations).
int height = FFALIGN(h, 256);
fbotex_init(p, &p->scale_sep_fbo, p->image_w, height,
p->opts.fbo_format);
fbotex_init(&p->scale_sep_fbo, p->gl, p->log, p->image_w, height,
p->gl_target, GL_NEAREST, p->opts.fbo_format);
}
p->scale_sep_fbo.vp_w = p->image_w;
p->scale_sep_fbo.vp_h = h;
@ -2275,12 +2187,13 @@ static bool test_fbo(struct gl_video *p, GLenum format)
GL *gl = p->gl;
bool success = false;
struct fbotex fbo = {0};
if (fbotex_init(p, &fbo, 16, 16, format)) {
if (fbotex_init(&fbo, p->gl, p->log, 16, 16, p->gl_target, GL_LINEAR, format))
{
gl->BindFramebuffer(GL_FRAMEBUFFER, fbo.fbo);
gl->BindFramebuffer(GL_FRAMEBUFFER, 0);
success = true;
}
fbotex_uninit(p, &fbo);
fbotex_uninit(&fbo);
glCheckError(gl, p->log, "FBO test");
gl_video_set_gl_state(p);
return success;