vo_opengl: use glBlitFramebuffer to draw repeated frames

In the display-sync, non-interpolation case, and if the display refresh
rate is higher than the video framerate, we duplicate display frames by
rendering exactly the same screen again. The redrawing is cached with a
FBO to speed up the repeat.

Use glBlitFramebuffer() instead of another shader pass. It should be
faster.

For some reason, post-process was run again on each display refresh.
Stop doing this, which should also be slightly faster. The only
disadvantage is that temporal dithering will be run only once per video
frame, but I can live with this.

One aspect is messy: clearing the background is done at the start on the
target framebuffer, so to avoid clearing twice and duplicating the code,
only copy the part of the framebuffer that contains the rendered video.
(Which also gets slightly messy - needs to compensate for coordinate
system flipping.)
This commit is contained in:
wm4 2015-11-15 18:30:54 +01:00
parent c7d82dd25c
commit 883d311413
3 changed files with 27 additions and 16 deletions

View File

@ -172,6 +172,7 @@ static const struct gl_functions gl_functions[] = {
.ver_es_core = 300,
.functions = (const struct gl_function[]) {
DEF_FN(BindBufferBase),
DEF_FN(BlitFramebuffer),
DEF_FN(GetStringi),
// for ES 3.0
DEF_FN(GetTexLevelParameteriv),

View File

@ -234,6 +234,8 @@ struct GL {
GLenum (GLAPIENTRY *CheckFramebufferStatus)(GLenum);
void (GLAPIENTRY *FramebufferTexture2D)(GLenum, GLenum, GLenum, GLuint,
GLint);
void (GLAPIENTRY *BlitFramebuffer)(GLint, GLint, GLint, GLint, GLint, GLint,
GLint, GLint, GLbitfield, GLenum);
void (GLAPIENTRY *Uniform1f)(GLint, GLfloat);
void (GLAPIENTRY *Uniform2f)(GLint, GLfloat, GLfloat);

View File

@ -2157,34 +2157,42 @@ void gl_video_render_frame(struct gl_video *p, struct vo_frame *frame, int fbo)
if (p->opts.interpolation && (p->frames_drawn || !frame->still)) {
gl_video_interpolate_frame(p, frame, fbo);
} else {
// For the non-interplation case, we draw to a single "cache"
// FBO to speed up subsequent re-draws (if any exist)
int vp_w = p->dst_rect.x1 - p->dst_rect.x0,
vp_h = p->dst_rect.y1 - p->dst_rect.y0;
bool is_new = !frame->redraw && !frame->repeat;
if (is_new || !p->output_fbo_valid) {
p->output_fbo_valid = false;
gl_video_upload_image(p, frame->current);
pass_render_frame(p);
if (frame->num_vsyncs == 1 || !frame->display_synced ||
p->opts.dumb_mode)
// For the non-interplation case, we draw to a single "cache"
// FBO to speed up subsequent re-draws (if any exist)
int dest_fbo = fbo;
if (frame->num_vsyncs > 1 && frame->display_synced &&
!p->opts.dumb_mode && gl->BlitFramebuffer)
{
// Disable output_fbo_valid to signal that this frame
// does not require any redraws from the FBO.
pass_draw_to_screen(p, fbo);
p->output_fbo_valid = false;
} else {
finish_pass_fbo(p, &p->output_fbo, vp_w, vp_h, 0, FBOTEX_FUZZY);
fbotex_change(&p->output_fbo, p->gl, p->log,
p->vp_w, abs(p->vp_h),
p->opts.fbo_format, 0);
dest_fbo = p->output_fbo.fbo;
p->output_fbo_valid = true;
}
pass_draw_to_screen(p, dest_fbo);
}
// "output fbo valid" and "output fbo needed" are equivalent
if (p->output_fbo_valid) {
pass_load_fbotex(p, &p->output_fbo, vp_w, vp_h, 0);
GLSL(vec4 color = texture(texture0, texcoord0);)
pass_draw_to_screen(p, fbo);
gl->BindFramebuffer(GL_READ_FRAMEBUFFER, p->output_fbo.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->BlitFramebuffer(rc.x0, rc.y0, rc.x1, rc.y1,
rc.x0, rc.y0, rc.x1, rc.y1,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
gl->BindFramebuffer(GL_READ_FRAMEBUFFER, 0);
gl->BindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
}
}
}