This makes compositing gamma correct by assuming all input surfaces are in
the sRGB color space. It can be enabled by setting color-managed to true
in the compositor section of weston.ini.

It's implemented by converting from sRGB gamma using the new 
CONVERSION_FROM_SRGB
shader attribute and drawing the surfaces into a temporary buffer (the
indirect rendering introduced in the previous patch). That buffer is then
drawed to the framebuffer using the OUTPUT_TO_SRGB shader attribute which
converts back into sRGB gamma.

Both the temporary buffer and sRGB decode LUT needs atleast 12-bits per
channel for flawless results with 8-bit input/output. This is not provided
by OpenGL ES 2 by default so desktop OpenGL is required for usable results.

It also adds a check to ensure we have enough texture units for the planes
and the LUT.
---
 src/compositor.c  |   7 ++-
 src/compositor.h  |   2 +
 src/gl-internal.h |  14 ++++-
 src/gl-renderer.c |  47 ++++++++++++---
 src/gl-shaders.c  | 169 +++++++++++++++++++++++++++++++++++++++++++++++++++---
 5 files changed, 222 insertions(+), 17 deletions(-)

diff --git a/src/compositor.c b/src/compositor.c
index 9198b3b..22bd3ea 100644
--- a/src/compositor.c
+++ b/src/compositor.c
@@ -2963,10 +2963,15 @@ weston_compositor_init(struct weston_compositor *ec,
                { "keymap_variant", CONFIG_KEY_STRING, &xkb_names.variant },
                { "keymap_options", CONFIG_KEY_STRING, &xkb_names.options },
         };
+        const struct config_key compositor_config_keys[] = {
+                { "color-managed", CONFIG_KEY_BOOLEAN, &ec->color_managed },
+        };
        const struct config_section cs[] = {
                 { "keyboard",
                   keyboard_config_keys, ARRAY_LENGTH(keyboard_config_keys) },
-       };
+                { "compositor",
+                  compositor_config_keys, ARRAY_LENGTH(compositor_config_keys) 
},
+        };
 
        memset(&xkb_names, 0, sizeof(xkb_names));
        parse_config_file(config_file, cs, ARRAY_LENGTH(cs), ec);
diff --git a/src/compositor.h b/src/compositor.h
index 8842372..88a3cdd 100644
--- a/src/compositor.h
+++ b/src/compositor.h
@@ -338,6 +338,8 @@ struct weston_compositor {
        struct weston_plane primary_plane;
        int fan_debug;
 
+       int color_managed;
+
        uint32_t focus;
 
        struct weston_renderer *renderer;
diff --git a/src/gl-internal.h b/src/gl-internal.h
index 205c2d9..44b1c75 100644
--- a/src/gl-internal.h
+++ b/src/gl-internal.h
@@ -47,11 +47,13 @@ enum gl_shader_attribute {
 
 enum gl_conversion_attribute {
        CONVERSION_NONE,
+       CONVERSION_FROM_SRGB,
        CONVERSION_COUNT
 };
 
 enum gl_output_attribute {
        OUTPUT_BLEND,
+       OUTPUT_TO_SRGB,
        OUTPUT_COUNT
 };
 
@@ -87,6 +89,7 @@ struct gl_output_state {
 struct gl_surface_state {
        GLfloat color[4];
        enum gl_input_attribute input;
+       enum gl_conversion_attribute conversion;
 
        GLuint textures[MAX_PLANES];
        int num_textures;
@@ -114,7 +117,12 @@ struct gl_renderer {
                int32_t width, height;
        } border;
 
+       GLuint srgb_decode_lut;
+       GLuint srgb_encode_lut;
+
        GLenum bgra_internal_format, bgra_format;
+       GLenum rgba16_internal_format;
+       GLenum l16_internal_format;
        GLenum short_type;
 
        PFNGLEGLIMAGETARGETTEXTURE2DOESPROC image_target_texture_2d;
@@ -158,6 +166,9 @@ get_renderer(struct weston_compositor *ec)
 int
 gl_init_shaders(struct gl_renderer *gr);
 
+int
+gl_compile_shaders(struct gl_renderer *gr);
+
 void
 gl_destroy_shaders(struct gl_renderer *gr);
 
@@ -172,7 +183,8 @@ gl_use_shader(struct gl_renderer *gr,
 struct gl_shader *
 gl_select_shader(struct gl_renderer *gr,
                        enum gl_input_attribute input,
-                       enum gl_output_attribute output);
+                       enum gl_output_attribute output,
+                       enum gl_conversion_attribute conversion);
 
 void
 gl_shader_setup(struct gl_shader *shader,
diff --git a/src/gl-renderer.c b/src/gl-renderer.c
index ac8696f..25f5f84 100644
--- a/src/gl-renderer.c
+++ b/src/gl-renderer.c
@@ -700,7 +700,10 @@ repaint_surfaces_start(struct weston_output *output, 
pixman_region32_t *damage)
 {
        struct gl_output_state *go = get_output_state(output);
 
-       go->indirect_drawing = 0 && !go->indirect_disable;
+       go->indirect_drawing = output->compositor->color_managed;
+
+       if (go->indirect_disable)
+               go->indirect_drawing = 0;
 
        if (go->indirect_drawing) {
                glBindFramebuffer(GL_FRAMEBUFFER, go->indirect_fbo);
@@ -720,11 +723,16 @@ repaint_surfaces_finish(struct weston_output *output, 
pixman_region32_t *damage)
        if (go->indirect_drawing) {
                glBindFramebuffer(GL_FRAMEBUFFER, 0);
 
-               shader = gl_select_shader(gr, INPUT_RGBX, OUTPUT_BLEND);
+               shader = gl_select_shader(gr,
+                       INPUT_RGBX,
+                       OUTPUT_TO_SRGB,
+                       CONVERSION_NONE);
 
                gl_use_shader(gr, shader);
                gl_shader_set_output(shader, output);
-               glUniform1f(shader->alpha_uniform, 1.0);
+
+               glActiveTexture(GL_TEXTURE0 + MAX_PLANES);
+               glBindTexture(GL_TEXTURE_2D, gr->srgb_encode_lut);
 
                glDisable(GL_BLEND);
 
@@ -770,7 +778,7 @@ draw_surface(struct weston_surface *es, struct 
weston_output *output,
                gl_shader_setup(gr->solid_shader, es, output);
        }
 
-       shader = gl_select_shader(gr, gs->input, OUTPUT_BLEND);
+       shader = gl_select_shader(gr, gs->input, OUTPUT_BLEND, gs->conversion);
 
        gl_use_shader(gr, shader);
        gl_shader_setup(shader, es, output);
@@ -800,7 +808,10 @@ draw_surface(struct weston_surface *es, struct 
weston_output *output,
                         * Xwayland surfaces need this.
                         */
 
-                       struct gl_shader *rgbx_shader = gl_select_shader(gr, 
INPUT_RGBX, OUTPUT_BLEND);
+                       struct gl_shader *rgbx_shader = gl_select_shader(gr,
+                               INPUT_RGBX,
+                               OUTPUT_BLEND,
+                               gs->conversion);
                        gl_use_shader(gr, rgbx_shader);
                        gl_shader_setup(rgbx_shader, es, output);
                }
@@ -926,7 +937,8 @@ draw_border(struct weston_output *output)
        GLfloat *v;
        int n;
 
-       shader = gl_select_shader(gr, INPUT_RGBA, OUTPUT_BLEND);
+       shader = gl_select_shader(gr, INPUT_RGBA, OUTPUT_BLEND,
+               CONVERSION_NONE);
 
        glDisable(GL_BLEND);
        gl_use_shader(gr, shader);
@@ -1232,6 +1244,11 @@ gl_renderer_attach(struct weston_surface *es, struct 
wl_buffer *buffer)
                weston_log("unhandled buffer type!\n");
                weston_buffer_reference(&gs->buffer_ref, NULL);
        }
+
+       if (ec->color_managed)
+               gs->conversion = CONVERSION_FROM_SRGB;
+       else
+               gs->conversion = CONVERSION_NONE;
 }
 
 static void
@@ -1246,6 +1263,7 @@ gl_renderer_surface_set_color(struct weston_surface 
*surface,
        gs->color[3] = alpha;
 
        gs->input = INPUT_SOLID;
+       gs->conversion = CONVERSION_NONE;
 }
 
 static int
@@ -1624,7 +1642,7 @@ fragment_debug_binding(struct wl_seat *seat, uint32_t 
time, uint32_t key,
 
        gr->fragment_shader_debug ^= 1;
 
-       gl_init_shaders(gr);
+       gl_compile_shaders(gr);
 
        weston_compositor_damage_all(ec);
 }
@@ -1635,6 +1653,7 @@ gl_renderer_setup(struct weston_compositor *ec, 
EGLSurface egl_surface)
        struct gl_renderer *gr = get_renderer(ec);
        const char *extensions;
        EGLBoolean ret;
+       GLint param;
 
 #ifdef BUILD_OPENGL
        static const EGLint context_attribs[] = {
@@ -1646,6 +1665,8 @@ gl_renderer_setup(struct weston_compositor *ec, 
EGLSurface egl_surface)
        gr->bgra_internal_format = GL_RGBA;
        gr->bgra_format = GL_BGRA;
        gr->short_type = GL_UNSIGNED_SHORT;
+       gr->rgba16_internal_format = GL_RGBA16;
+       gr->l16_internal_format = GL_LUMINANCE16;
 #else
        static const EGLint context_attribs[] = {
                EGL_CONTEXT_CLIENT_VERSION, 2,
@@ -1655,6 +1676,8 @@ gl_renderer_setup(struct weston_compositor *ec, 
EGLSurface egl_surface)
        gr->bgra_internal_format = GL_BGRA_EXT;
        gr->bgra_format = GL_BGRA_EXT;
        gr->short_type = GL_UNSIGNED_BYTE;
+       gr->rgba16_internal_format = GL_RGBA;
+       gr->l16_internal_format = GL_LUMINANCE;
 #endif
 
        if (!eglBindAPI(OPENGL_ES_VER ? EGL_OPENGL_ES_API : EGL_OPENGL_API)) {
@@ -1720,6 +1743,16 @@ gl_renderer_setup(struct weston_compositor *ec, 
EGLSurface egl_surface)
                return -1;
        }
 
+       glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &param);
+
+       if (ec->color_managed)
+               param--;
+
+       if (param < MAX_PLANES) {
+               weston_log("Too few OpenGL texture units available\n");
+               return -1;
+       }
+
        if (strstr(extensions, "GL_EXT_read_format_bgra"))
                ec->read_format = PIXMAN_a8r8g8b8;
        else
diff --git a/src/gl-shaders.c b/src/gl-shaders.c
index 0f6438e..b3c81cd 100644
--- a/src/gl-shaders.c
+++ b/src/gl-shaders.c
@@ -150,13 +150,52 @@ append(struct shader_string_list *list, const char 
*string)
                *data = str;
 }
 
+static void
+add_conversion(struct shader_builder *sb)
+{
+       int alpha = sb->desc->transparent;
+
+       if (sb->attributes[ATTRIBUTE_CONVERSION] != CONVERSION_FROM_SRGB)
+               return;
+
+       if (alpha)
+               append(&sb->body,
+                       "gl_FragColor.rgb *= gl_FragColor.a > 0.0 ? " \
+                       "1.0 / gl_FragColor.a : 0.0;\n");
+
+       append(&sb->global, "uniform sampler2D srgb_lut;\n");
+       append(&sb->body,
+               "gl_FragColor.rgb = gl_FragColor.rgb * 0.9473684210526316 + " \
+                       "0.02631578947368421;\n" \
+               "gl_FragColor.rgb = vec3(" \
+                       "texture2D(srgb_lut, vec2(gl_FragColor.r, 0.5)).x," \
+                       "texture2D(srgb_lut, vec2(gl_FragColor.g, 0.5)).x," \
+                       "texture2D(srgb_lut, vec2(gl_FragColor.b, 0.5)).x);\n");
+
+       if (alpha)
+               append(&sb->body, "gl_FragColor.rgb *= gl_FragColor.a;\n");
+}
+
+static void
+add_conversion_uniforms(struct shader_builder *builder,
+                       struct gl_shader *shader)
+{
+       if (builder->attributes[ATTRIBUTE_CONVERSION] != CONVERSION_FROM_SRGB)
+               return;
+
+       glUniform1i(glGetUniformLocation(shader->program, "srgb_lut"),
+               MAX_PLANES);
+}
+
 static int
 shader_rgbx_constructor(struct shader_builder *sb)
 {
        append(&sb->global, "uniform sampler2D texture;\n");
        append(&sb->body,
-               "gl_FragColor.rgb = texture2D(texture, texture_coord).rgb;\n" \
-               "gl_FragColor.a = 1.0;\n");
+               "gl_FragColor.rgb = texture2D(texture, texture_coord).rgb;\n");
+
+       if (sb->attributes[ATTRIBUTE_OUTPUT] != OUTPUT_TO_SRGB)
+               append(&sb->body, "gl_FragColor.a = 1.0;\n");
 
        return 1;
 }
@@ -168,7 +207,6 @@ shader_rgba_constructor(struct shader_builder *sb)
        append(&sb->body,
                "gl_FragColor = texture2D(texture, texture_coord);\n");
 
-
        return 1;
 }
 
@@ -252,6 +290,9 @@ shader_yuv_uniforms(struct shader_builder *sb, struct 
gl_shader *shader)
 static int
 shader_solid_constructor(struct shader_builder *sb)
 {
+       if (sb->attributes[ATTRIBUTE_CONVERSION])
+               return 0;
+
        append(&sb->global, "uniform vec4 color;\n");
        append(&sb->body, "gl_FragColor = color;\n");
 
@@ -288,6 +329,20 @@ static struct gl_input_type_desc 
input_type_descs[INPUT_COUNT] = {
 };
 
 static void
+add_to_srgb_conversion(struct shader_builder *sb)
+{
+       append(&sb->global, "uniform sampler2D srgb_lut;\n");
+       append(&sb->body,
+               "gl_FragColor.rgb = gl_FragColor.rgb * 0.9946236559139785 + " \
+                       "0.002688172043010753;\n" \
+               "gl_FragColor.rgb = vec3(" \
+                       "texture2D(srgb_lut, vec2(gl_FragColor.r, 0.5)).x," \
+                       "texture2D(srgb_lut, vec2(gl_FragColor.g, 0.5)).x," \
+                       "texture2D(srgb_lut, vec2(gl_FragColor.b, 0.5)).x);\n");
+
+}
+
+static void
 attributes_from_permutation(size_t permutation, size_t *attributes)
 {
        int i;
@@ -421,6 +476,16 @@ create_shader_permutation(struct gl_renderer *renderer,
        sb.renderer = renderer;
        sb.desc = &input_type_descs[sb.attributes[ATTRIBUTE_INPUT]];
 
+       if (sb.attributes[ATTRIBUTE_OUTPUT] == OUTPUT_TO_SRGB) {
+               /* transparent inputs must be blended first */
+               if (sb.desc->transparent)
+                       return 0;
+
+               /* useless conversion from and to sRGB */
+               if (sb.attributes[ATTRIBUTE_CONVERSION] == CONVERSION_FROM_SRGB)
+                       return 0;
+       }
+
        shader_builder_init(&sb);
 
        if (OPENGL_ES_VER)
@@ -435,9 +500,21 @@ create_shader_permutation(struct gl_renderer *renderer,
                return 0;
        }
 
-       append(&sb.body, "gl_FragColor *= alpha;\n");
+       add_conversion(&sb);
+
+       switch (sb.attributes[ATTRIBUTE_OUTPUT]) {
+       case OUTPUT_BLEND:
+               append(&sb.body, "gl_FragColor *= alpha;\n");
+               break;
+       case OUTPUT_TO_SRGB:
+               add_to_srgb_conversion(&sb);
+               break;
+       default:
+               break;
+       }
 
-       if (renderer->fragment_shader_debug)
+       if (renderer->fragment_shader_debug &&
+                       sb.attributes[ATTRIBUTE_OUTPUT] != OUTPUT_TO_SRGB)
                append(&sb.body, "gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + " \
                        "gl_FragColor * 0.8;\n");
 
@@ -457,6 +534,12 @@ create_shader_permutation(struct gl_renderer *renderer,
 
        sb.desc->setup_uniforms(&sb, *shader);
 
+       add_conversion_uniforms(&sb, *shader);
+
+       if (sb.attributes[ATTRIBUTE_OUTPUT] == OUTPUT_TO_SRGB)
+               glUniform1i(glGetUniformLocation((*shader)->program, 
"srgb_lut"),
+                       MAX_PLANES);
+
        shader_builder_release(&sb);
 
        return 0;
@@ -513,13 +596,14 @@ error:
 struct gl_shader *
 gl_select_shader(struct gl_renderer *gr,
                        enum gl_input_attribute input,
-                       enum gl_output_attribute output)
+                       enum gl_output_attribute output,
+                       enum gl_conversion_attribute conversion)
 {
        struct gl_shader *shader;
        size_t attributes[ATTRIBUTE_COUNT] = {
                input,
                output,
-               CONVERSION_NONE
+               conversion
        };
 
        shader = gr->shaders[permutation_from_attributes(attributes)];
@@ -559,6 +643,7 @@ gl_shader_setup(struct gl_shader *shader,
                       struct weston_surface *surface,
                       struct weston_output *output)
 {
+       struct gl_renderer *gr = get_renderer(output->compositor);
        struct gl_surface_state *gs = get_surface_state(surface);
 
        gl_shader_set_output(shader, output);
@@ -566,12 +651,77 @@ gl_shader_setup(struct gl_shader *shader,
        if (gs->input == INPUT_SOLID)
                glUniform4fv(shader->color_uniform, 1, gs->color);
 
+       if (gs->conversion == CONVERSION_FROM_SRGB) {
+               glActiveTexture(GL_TEXTURE0 + MAX_PLANES);
+               glBindTexture(GL_TEXTURE_2D, gr->srgb_decode_lut);
+       }
+
        glUniform1f(shader->alpha_uniform, surface->alpha);
 }
 
+static void
+setup_lut(GLuint *texture, const void *data,
+       GLsizei entries, GLenum internal_format, GLenum type)
+{
+       glGenTextures(1, texture);
+
+       glBindTexture(GL_TEXTURE_2D, *texture);
+
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
+       glTexImage2D(GL_TEXTURE_2D, 0, internal_format, entries, 1, 0,
+               GL_LUMINANCE, type, data);
+}
+
+static const uint16_t srgb_decode_lut[] = {
+       0, 281, 751, 1519, 2618, 4073, 5919, 8166, 10847, 13984, 17589, 21690,
+       26301, 31424, 37095, 43321, 50125, 57488, 65535
+};
+
+static const uint8_t srgb_encode_lut[] = {
+       0, 17, 27, 34, 40, 46, 50, 55, 59, 62, 66, 69, 72, 75, 78, 80, 83, 85,
+       88, 90, 92, 95, 97, 99, 101, 103, 105, 107, 108, 110, 112, 114, 115,
+       117, 119, 120, 122, 124, 125, 127, 128, 130, 131, 132, 134, 135, 137,
+       138, 139, 141, 142, 143, 145, 146, 147, 148, 149, 151, 152, 153, 154,
+       156, 156, 158, 159, 160, 161, 162, 163, 165, 165, 166, 168, 168, 170,
+       170, 172, 172, 174, 174, 176, 176, 178, 178, 180, 181, 181, 183, 183,
+       185, 185, 186, 187, 188, 189, 190, 190, 192, 192, 194, 194, 195, 196,
+       196, 198, 198, 200, 200, 201, 202, 203, 203, 204, 205, 206, 207, 207,
+       208, 209, 210, 211, 211, 212, 213, 214, 214, 215, 216, 217, 217, 218,
+       219, 221, 218, 222, 222, 223, 223, 224, 225, 226, 226, 227, 228, 228,
+       229, 231, 229, 231, 232, 232, 233, 234, 235, 235, 236, 237, 237, 238,
+       239, 239, 240, 241, 241, 242, 242, 243, 245, 242, 245, 247, 245, 247,
+       248, 248, 249, 249, 250, 252, 250, 252, 253, 253, 255, 252, 255
+};
+
+static void
+setup_luts(struct gl_renderer *gr)
+{
+       setup_lut(&gr->srgb_decode_lut, srgb_decode_lut,
+               ARRAY_LENGTH(srgb_decode_lut),
+               gr->l16_internal_format, GL_UNSIGNED_SHORT);
+
+       setup_lut(&gr->srgb_encode_lut, srgb_encode_lut,
+               ARRAY_LENGTH(srgb_encode_lut), GL_LUMINANCE, GL_UNSIGNED_BYTE);
+}
+
 int
 gl_init_shaders(struct gl_renderer *gr)
 {
+       if (gl_compile_shaders(gr) < 0)
+               return -1;
+
+       setup_luts(gr);
+
+       return 0;
+}
+
+int
+gl_compile_shaders(struct gl_renderer *gr)
+{
        struct gl_shader **shaders = create_shader_permutations(gr);
 
        if (!shaders)
@@ -581,7 +731,10 @@ gl_init_shaders(struct gl_renderer *gr)
                gl_destroy_shaders(gr);
 
        gr->shaders = shaders;
-       gr->solid_shader = gl_select_shader(gr, INPUT_SOLID, OUTPUT_BLEND);
+       gr->solid_shader = gl_select_shader(gr,
+               INPUT_SOLID,
+               OUTPUT_BLEND,
+               CONVERSION_NONE);
 
        /* Force use_shader() to call glUseProgram(), since we need to use
         * the recompiled version of the shader. */
-- 
1.8.1.3

_______________________________________________
wayland-devel mailing list
[email protected]
http://lists.freedesktop.org/mailman/listinfo/wayland-devel

Reply via email to