gl-renderer: implement intermediate framebuffer (shadow)

Proper color management will need blending done with linear light pixel
values, that is, EOTF applied before blending, and then inverse-EOTF
applied for scanout after blending. The simplest way to set that up is
to use an intemediate framebuffer a.k.a shadow buffer containing the
composited image in linear light values, then blit from that to the
actual framebuffer.

This patch implements the shadow buffer, but the linear light
blending is left for another patch. This allows GL-renderer to turn
WESTON_CAP_COLOR_OPS on.

Half-float is chosen as the buffer format because linear light values
require more bits to encode with sufficient precision than the usual
non-linear pixel values.

v2: Use /* */ instead of // (Pekka)
    Rename fbo and tex to shadow_{fbo,tex} (Pekka)
    Check for OpenGLES capabilities before creating
    shadow_{tex,fbo} (Pekka)

Signed-off-by: Harish Krupo <harishkrupo@gmail.com>

v3: Rebased.
    Simplified GL version checks (Sebastian)
    Apply changes from "libweston: add color ops cap and bool renderer
    shadow buffer"
    Renamed supports_half_float_texture to has_gl_half_float to
    follow the existing naming pattern.
    Introduce gl_renderer_create_shadow_16f().
    Undo moving of glViewport() call.
    Replace half_float_texture_enabled with shadow_exists().
    Introduce struct gl_output_state_shadow.
    Assert no resizing with shadow.
    Fix triangle fan debug.
    Rename repaint_from_texture() to blit_shadow_to_output().
    Rewrite commit message because linear light blending is not
    implemented in this patch.
    Fix blit_shadow_to_output() for scaled/transformed outputs and
    remove redundant code.
    Fix has_gl_half_float determination.

v4: Disable blending in blit_shadow. (Daniel)
    Port to gl_renderer_get_program().
    Make a generic fbo-texture struct with parameterized format. (Daniel)
    Change has_gl_half_float into gl_half_float_type.

Signed-off-by: Pekka Paalanen <pekka.paalanen@collabora.com>
dev
Harish Krupo 6 years ago committed by Pekka Paalanen
parent 0692079aae
commit 7ef26886f5
  1. 2
      libweston/renderer-gl/gl-renderer-internal.h
  2. 227
      libweston/renderer-gl/gl-renderer.c
  3. 1
      tests/yuv-buffer-test.c

@ -154,6 +154,8 @@ struct gl_renderer {
bool has_wait_sync; bool has_wait_sync;
PFNEGLWAITSYNCKHRPROC wait_sync; PFNEGLWAITSYNCKHRPROC wait_sync;
GLint gl_half_float_type;
/** struct gl_shader::link /** struct gl_shader::link
* *
* List constains cached shaders built from struct gl_shader_requirements * List constains cached shaders built from struct gl_shader_requirements

@ -29,6 +29,7 @@
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <GLES2/gl2ext.h> #include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
@ -77,6 +78,13 @@ struct gl_border_image {
void *data; void *data;
}; };
struct gl_fbo_texture {
GLuint fbo;
GLuint tex;
int32_t width;
int32_t height;
};
struct gl_output_state { struct gl_output_state {
EGLSurface egl_surface; EGLSurface egl_surface;
pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT]; pixman_region32_t buffer_damage[BUFFER_DAMAGE_COUNT];
@ -92,6 +100,8 @@ struct gl_output_state {
/* struct timeline_render_point::link */ /* struct timeline_render_point::link */
struct wl_list timeline_render_point_list; struct wl_list timeline_render_point_list;
struct gl_fbo_texture shadow;
}; };
enum buffer_type { enum buffer_type {
@ -259,6 +269,12 @@ get_surface_state(struct weston_surface *surface)
return (struct gl_surface_state *)surface->renderer_state; return (struct gl_surface_state *)surface->renderer_state;
} }
static bool
shadow_exists(const struct gl_output_state *go)
{
return go->shadow.fbo != 0;
}
static void static void
timeline_render_point_destroy(struct timeline_render_point *trp) timeline_render_point_destroy(struct timeline_render_point *trp)
{ {
@ -625,6 +641,69 @@ texture_region(struct weston_view *ev, pixman_region32_t *region,
return nvtx; return nvtx;
} }
/** Create a texture and a framebuffer object
*
* \param fbotex To be initialized.
* \param width Texture width in pixels.
* \param height Texture heigh in pixels.
* \param internal_format See glTexImage2D.
* \param format See glTexImage2D.
* \param type See glTexImage2D.
* \return True on success, false otherwise.
*/
static bool
gl_fbo_texture_init(struct gl_fbo_texture *fbotex,
int32_t width,
int32_t height,
GLint internal_format,
GLenum format,
GLenum type)
{
int fb_status;
GLuint shadow_fbo;
GLuint shadow_tex;
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &shadow_tex);
glBindTexture(GL_TEXTURE_2D, shadow_tex);
glTexImage2D(GL_TEXTURE_2D, 0, internal_format, width, height, 0,
format, type, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glBindTexture(GL_TEXTURE_2D, 0);
glGenFramebuffers(1, &shadow_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, shadow_fbo);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, shadow_tex, 0);
fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
if (fb_status != GL_FRAMEBUFFER_COMPLETE) {
glDeleteFramebuffers(1, &shadow_fbo);
glDeleteTextures(1, &shadow_tex);
return false;
}
fbotex->fbo = shadow_fbo;
fbotex->tex = shadow_tex;
fbotex->width = width;
fbotex->height = height;
return true;
}
static void
gl_fbo_texture_fini(struct gl_fbo_texture *fbotex)
{
glDeleteFramebuffers(1, &fbotex->fbo);
fbotex->fbo = 0;
glDeleteTextures(1, &fbotex->tex);
fbotex->tex = 0;
}
static struct gl_shader * static struct gl_shader *
gl_renderer_get_program(struct gl_renderer *gr, gl_renderer_get_program(struct gl_renderer *gr,
const struct gl_shader_requirements *requirements) const struct gl_shader_requirements *requirements)
@ -1484,6 +1563,77 @@ pixman_region_to_egl_y_invert(struct weston_output *output,
pixman_region32_fini(&transformed); pixman_region32_fini(&transformed);
} }
static void
blit_shadow_to_output(struct weston_output *output,
pixman_region32_t *output_damage)
{
const struct gl_shader_requirements blit_requirements = {
.variant = SHADER_VARIANT_RGBA,
};
struct gl_output_state *go = get_output_state(output);
struct gl_renderer *gr = get_renderer(output->compositor);
struct gl_shader *shader;
double width = output->current_mode->width;
double height = output->current_mode->height;
pixman_box32_t *rects;
int n_rects;
int i;
pixman_region32_t translated_damage;
GLfloat verts[4 * 2];
static const GLfloat proj[16] = { /* transpose */
2.0f, 0.0f, 0.0f, 0.0f,
0.0f, 2.0f, 0.0f, 0.0f,
0.0f, 0.0f, 1.0f, 0.0f,
-1.0f, -1.0f, 0.0f, 1.0f
};
pixman_region32_init(&translated_damage);
shader = gl_renderer_get_program(gr, &blit_requirements);
if (!gl_renderer_use_program(gr, &shader))
return;
glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, proj);
glUniform1f(shader->alpha_uniform, 1.0f);
glUniform1i(shader->tex_uniforms[0], 0);
glDisable(GL_BLEND);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, go->shadow.tex);
/* output_damage is in global coordinates */
pixman_region32_intersect(&translated_damage, output_damage,
&output->region);
/* Convert to output pixel coordinates in-place */
weston_output_region_from_global(output, &translated_damage);
rects = pixman_region32_rectangles(&translated_damage, &n_rects);
for (i = 0; i < n_rects; i++) {
verts[0] = rects[i].x1 / width;
verts[1] = (height - rects[i].y1) / height;
verts[2] = rects[i].x2 / width;
verts[3] = (height - rects[i].y1) / height;
verts[4] = rects[i].x2 / width;
verts[5] = (height - rects[i].y2) / height;
verts[6] = rects[i].x1 / width;
verts[7] = (height - rects[i].y2) / height;
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, verts);
glEnableVertexAttribArray(0);
/* texcoord: */
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, verts);
glEnableVertexAttribArray(1);
glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}
glBindTexture(GL_TEXTURE_2D, 0);
pixman_region32_fini(&translated_damage);
}
/* NOTE: We now allow falling back to ARGB gl visuals when XRGB is /* NOTE: We now allow falling back to ARGB gl visuals when XRGB is
* unavailable, so we're assuming the background has no transparency * unavailable, so we're assuming the background has no transparency
* and that everything with a blend, like drop shadows, will have something * and that everything with a blend, like drop shadows, will have something
@ -1528,12 +1678,6 @@ gl_renderer_repaint_output(struct weston_output *output,
go->begin_render_sync = create_render_sync(gr); go->begin_render_sync = create_render_sync(gr);
/* Calculate the viewport */
glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
go->borders[GL_RENDERER_BORDER_BOTTOM].height,
output->current_mode->width,
output->current_mode->height);
/* Calculate the global GL matrix */ /* Calculate the global GL matrix */
go->output_matrix = output->matrix; go->output_matrix = output->matrix;
weston_matrix_translate(&go->output_matrix, weston_matrix_translate(&go->output_matrix,
@ -1543,6 +1687,22 @@ gl_renderer_repaint_output(struct weston_output *output,
2.0 / output->current_mode->width, 2.0 / output->current_mode->width,
-2.0 / output->current_mode->height, 1); -2.0 / output->current_mode->height, 1);
/* If using shadow, redirect all drawing to it first. */
if (shadow_exists(go)) {
/* XXX: Shadow code does not support resizing. */
assert(output->current_mode->width == go->shadow.width);
assert(output->current_mode->height == go->shadow.height);
glBindFramebuffer(GL_FRAMEBUFFER, go->shadow.fbo);
glViewport(0, 0, go->shadow.width, go->shadow.height);
} else {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
go->borders[GL_RENDERER_BORDER_BOTTOM].height,
output->current_mode->width,
output->current_mode->height);
}
/* In fan debug mode, redraw everything to make sure that we clear any /* In fan debug mode, redraw everything to make sure that we clear any
* fans left over from previous draws on this buffer. * fans left over from previous draws on this buffer.
* This precludes the use of EGL_EXT_swap_buffers_with_damage and * This precludes the use of EGL_EXT_swap_buffers_with_damage and
@ -1588,7 +1748,19 @@ gl_renderer_repaint_output(struct weston_output *output,
free(egl_rects); free(egl_rects);
} }
repaint_views(output, &total_damage); if (shadow_exists(go)) {
/* Repaint into shadow. */
repaint_views(output, output_damage);
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glViewport(go->borders[GL_RENDERER_BORDER_LEFT].width,
go->borders[GL_RENDERER_BORDER_BOTTOM].height,
output->current_mode->width,
output->current_mode->height);
blit_shadow_to_output(output, &total_damage);
} else {
repaint_views(output, &total_damage);
}
pixman_region32_fini(&total_damage); pixman_region32_fini(&total_damage);
pixman_region32_fini(&previous_damage); pixman_region32_fini(&previous_damage);
@ -3143,6 +3315,9 @@ gl_renderer_output_create(struct weston_output *output,
EGLSurface surface) EGLSurface surface)
{ {
struct gl_output_state *go; struct gl_output_state *go;
struct gl_renderer *gr = get_renderer(output->compositor);
GLint internal_format;
bool ret;
int i; int i;
go = zalloc(sizeof *go); go = zalloc(sizeof *go);
@ -3159,6 +3334,31 @@ gl_renderer_output_create(struct weston_output *output,
go->begin_render_sync = EGL_NO_SYNC_KHR; go->begin_render_sync = EGL_NO_SYNC_KHR;
go->end_render_sync = EGL_NO_SYNC_KHR; go->end_render_sync = EGL_NO_SYNC_KHR;
if (output->use_renderer_shadow_buffer) {
assert(gr->gl_half_float_type);
if (gr->gl_half_float_type == GL_HALF_FLOAT_OES)
internal_format = GL_RGBA;
else
internal_format = GL_RGBA16F;
ret = gl_fbo_texture_init(&go->shadow,
output->current_mode->width,
output->current_mode->height,
internal_format,
GL_RGBA,
gr->gl_half_float_type);
if (ret) {
weston_log("Output %s uses 16F shadow.\n",
output->name);
} else {
weston_log("Output %s failed to create 16F shadow.\n",
output->name);
free(go);
return -1;
}
}
output->renderer_state = go; output->renderer_state = go;
return 0; return 0;
@ -3255,6 +3455,9 @@ gl_renderer_output_destroy(struct weston_output *output)
for (i = 0; i < 2; i++) for (i = 0; i < 2; i++)
pixman_region32_fini(&go->buffer_damage[i]); pixman_region32_fini(&go->buffer_damage[i]);
if (shadow_exists(go))
gl_fbo_texture_fini(&go->shadow);
eglMakeCurrent(gr->egl_display, eglMakeCurrent(gr->egl_display,
EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_SURFACE,
EGL_NO_CONTEXT); EGL_NO_CONTEXT);
@ -3488,6 +3691,9 @@ gl_renderer_display_create(struct weston_compositor *ec,
goto fail_with_error; goto fail_with_error;
} }
if (gr->gl_half_float_type != 0)
ec->capabilities |= WESTON_CAP_COLOR_OPS;
return 0; return 0;
fail_with_error: fail_with_error:
@ -3652,6 +3858,13 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external")) if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external"))
gr->has_egl_image_external = true; gr->has_egl_image_external = true;
if (weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float")) {
if (gr->gl_version >= gr_gl_version(3, 0))
gr->gl_half_float_type = GL_HALF_FLOAT;
else if (weston_check_egl_extension(extensions, "GL_OES_texture_half_float"))
gr->gl_half_float_type = GL_HALF_FLOAT_OES;
}
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
gr->fallback_shader = gl_renderer_create_fallback_shader(gr); gr->fallback_shader = gl_renderer_create_fallback_shader(gr);

@ -48,6 +48,7 @@ fixture_setup(struct weston_test_harness *harness)
setup.width = 324; setup.width = 324;
setup.height = 264; setup.height = 264;
setup.shell = SHELL_TEST_DESKTOP; setup.shell = SHELL_TEST_DESKTOP;
setup.logging_scopes = "log,gl-shader-generator";
return weston_test_harness_execute_as_client(harness, &setup); return weston_test_harness_execute_as_client(harness, &setup);
} }

Loading…
Cancel
Save