From 766718190b70c523c5296fd6cadcb64373247281 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Thu, 18 Apr 2019 21:45:48 +0530 Subject: [PATCH 01/29] gl-renderer: Requirement based shader generation This patch modifies the shader generation code so that the shaders are stitched together based on the requirement instead of creating them during initilization. This is necessary for HDR use cases where each surface would have different properties based on which different de-gamma or tone mapping or gamma shaders are stitched together. v2: Use /* */ instead of // (Pekka) Move shader strings to gl-shaders.c file (Pekka) Remove Makefile.am changes (Pekka) Use a struct instead of uint32_t for storing requirements (Pekka) Clean up shader list on destroy (Pekka) Rename shader_release -> shader_destroy (Pekka) Move shader creation/deletion into gl-shaders.c (Pekka) Use create_shaders's multi string capbility instead of concatenating (Pekka) v3: Add length check when adding shader string (Pekka) Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer-private.h | 73 ++++ libweston/renderer-gl/gl-renderer.c | 413 ++++++-------------- libweston/renderer-gl/gl-shaders.c | 332 ++++++++++++++++ libweston/renderer-gl/meson.build | 1 + 4 files changed, 515 insertions(+), 304 deletions(-) create mode 100644 libweston/renderer-gl/gl-renderer-private.h create mode 100644 libweston/renderer-gl/gl-shaders.c diff --git a/libweston/renderer-gl/gl-renderer-private.h b/libweston/renderer-gl/gl-renderer-private.h new file mode 100644 index 000000000..c9ac88ecb --- /dev/null +++ b/libweston/renderer-gl/gl-renderer-private.h @@ -0,0 +1,73 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef GL_RENDERER_PRIVATE_H +#define GL_RENDERER_PRIVATE_H + +#include +#include +#include +#include +#include + +enum gl_shader_texture_variant { + SHADER_VARIANT_NONE = 0, + SHADER_VARIANT_RGBX, + SHADER_VARIANT_RGBA, + SHADER_VARIANT_Y_U_V, + SHADER_VARIANT_Y_UV, + SHADER_VARIANT_Y_XUXV, + SHADER_VARIANT_SOLID, + SHADER_VARIANT_EXTERNAL, +}; + +struct gl_shader_requirements +{ + enum gl_shader_texture_variant variant; + bool debug; +}; + +struct gl_shader { + struct gl_shader_requirements key; + GLuint program; + GLuint vertex_shader, fragment_shader; + GLint proj_uniform; + GLint tex_uniforms[3]; + GLint alpha_uniform; + GLint color_uniform; + struct wl_list link; /* gl_renderer::shader_list */ +}; + +void +gl_shader_requirements_init(struct gl_shader_requirements *requirements); + +void +gl_shader_destroy(struct gl_shader *shader); + +struct gl_shader * +gl_shader_create(struct gl_shader_requirements *requirements); + +#endif diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index d160a5d88..80a7d33e7 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -57,6 +57,7 @@ #include "shared/platform.h" #include "shared/timespec-util.h" #include "weston-egl-ext.h" +#include "gl-renderer-private.h" #define GR_GL_VERSION(major, minor) \ (((uint32_t)(major) << 16) | (uint32_t)(minor)) @@ -64,16 +65,6 @@ #define GR_GL_VERSION_INVALID \ GR_GL_VERSION(0, 0) -struct gl_shader { - GLuint program; - GLuint vertex_shader, fragment_shader; - GLint proj_uniform; - GLint tex_uniforms[3]; - GLint alpha_uniform; - GLint color_uniform; - const char *vertex_source, *fragment_source; -}; - #define BUFFER_DAMAGE_COUNT 2 enum gl_border_status { @@ -138,7 +129,7 @@ struct dmabuf_image { enum import_type import_type; GLenum target; - struct gl_shader *shader; + enum gl_shader_texture_variant shader_variant; }; struct yuv_plane_descriptor { @@ -158,7 +149,6 @@ struct yuv_format_descriptor { struct gl_surface_state { GLfloat color[4]; - struct gl_shader *shader; GLuint textures[3]; int num_textures; @@ -195,6 +185,7 @@ struct gl_surface_state { struct wl_listener surface_destroy_listener; struct wl_listener renderer_destroy_listener; + struct gl_shader_requirements shader_requirements; }; struct gl_renderer { @@ -243,14 +234,6 @@ struct gl_renderer { int has_gl_texture_rg; - struct gl_shader texture_shader_rgba; - struct gl_shader texture_shader_rgbx; - struct gl_shader texture_shader_egl_external; - struct gl_shader texture_shader_y_uv; - struct gl_shader texture_shader_y_u_v; - struct gl_shader texture_shader_y_xuxv; - struct gl_shader invert_color_shader; - struct gl_shader solid_shader; struct gl_shader *current_shader; struct wl_signal destroy_signal; @@ -268,6 +251,12 @@ struct gl_renderer { int has_wait_sync; PFNEGLWAITSYNCKHRPROC wait_sync; + + /** struct gl_shader::link + * + * List constains cached shaders built from struct gl_shader_requirements + */ + struct wl_list shader_list; }; enum timeline_render_point_type { @@ -286,6 +275,10 @@ struct timeline_render_point { static PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = NULL; +static void +use_gl_program(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements); + static inline const char * dump_format(uint32_t format, char out[4]) { @@ -732,6 +725,11 @@ triangle_fan_debug(struct weston_view *view, int first, int count) GLushort *index; int nelems; static int color_idx = 0; + struct gl_shader *prev_shader = NULL; + struct gl_shader_requirements shader_requirements; + + prev_shader = gr->current_shader; + static const GLfloat color[][4] = { { 1.0, 0.0, 0.0, 1.0 }, { 0.0, 1.0, 0.0, 1.0 }, @@ -754,10 +752,14 @@ triangle_fan_debug(struct weston_view *view, int first, int count) *index++ = first + i; } - glUseProgram(gr->solid_shader.program); - glUniform4fv(gr->solid_shader.color_uniform, 1, + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_SOLID; + use_gl_program(gr, &shader_requirements); + glUniform4fv(gr->current_shader->color_uniform, 1, color[color_idx++ % ARRAY_LENGTH(color)]); glDrawElements(GL_LINES, nelems, GL_UNSIGNED_SHORT, buffer); + + gr->current_shader = prev_shader; glUseProgram(gr->current_shader->program); free(buffer); } @@ -830,26 +832,38 @@ use_output(struct weston_output *output) return 0; } -static int -shader_init(struct gl_shader *shader, struct gl_renderer *gr, - const char *vertex_source, const char *fragment_source); - static void -use_shader(struct gl_renderer *gr, struct gl_shader *shader) +use_gl_program(struct gl_renderer *gr, + const struct gl_shader_requirements *requirements) { - if (!shader->program) { - int ret; + struct gl_shader *iterator, *shader = NULL; + struct gl_shader_requirements reqs; - ret = shader_init(shader, gr, - shader->vertex_source, - shader->fragment_source); + memcpy(&reqs, requirements, sizeof(struct gl_shader_requirements)); + if (gr->fragment_shader_debug) + reqs.debug = true; - if (ret < 0) - weston_log("warning: failed to compile shader\n"); + wl_list_for_each(iterator, &gr->shader_list, link) { + if (!memcmp(&reqs, &iterator->key, + sizeof(struct gl_shader_requirements))) { + shader = iterator; + break; + } + } + + if (!shader) { + shader = gl_shader_create(&reqs); + if (!shader) { + weston_log("warning: failed to generate gl program\n"); + return; + } + + wl_list_insert(&gr->shader_list, &shader->link); } if (gr->current_shader == shader) return; + glUseProgram(shader->program); gr->current_shader = shader; } @@ -953,11 +967,12 @@ draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_t surface_blend; GLint filter; int i; + struct gl_shader_requirements shader_requirements; /* In case of a runtime switch of renderers, we may not have received * an attach for this surface since the switch. In that case we don't * have a valid buffer or a proper shader set up so skip rendering. */ - if (!gs->shader) + if (!gs->shader_requirements.variant) return; pixman_region32_init(&repaint); @@ -973,13 +988,19 @@ draw_view(struct weston_view *ev, struct weston_output *output, glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + /* The built shader objects are cached in struct + * gL_renderer::shader_list and retrieved when requested with the same + * struct gl_shader_requirements. The SOILD shader is generated here so + * that the shader uniforms are cached with used later */ if (gr->fan_debug) { - use_shader(gr, &gr->solid_shader); - shader_uniforms(&gr->solid_shader, ev, output); + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_SOLID; + use_gl_program(gr, &shader_requirements); + shader_uniforms(gr->current_shader, ev, output); } - use_shader(gr, gs->shader); - shader_uniforms(gs->shader, ev, output); + use_gl_program(gr, &gs->shader_requirements); + shader_uniforms(gr->current_shader, ev, output); if (ev->transform.enabled || output->zoom.active || output->current_scale != ev->surface->buffer_viewport.buffer.scale) @@ -1013,14 +1034,17 @@ draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_copy(&surface_opaque, &ev->surface->opaque); if (pixman_region32_not_empty(&surface_opaque)) { - if (gs->shader == &gr->texture_shader_rgba) { + if (gs->shader_requirements.variant == SHADER_VARIANT_RGBA) { /* Special case for RGBA textures with possibly * bad data in alpha channel: use the shader * that forces texture alpha = 1.0. * Xwayland surfaces need this. */ - use_shader(gr, &gr->texture_shader_rgbx); - shader_uniforms(&gr->texture_shader_rgbx, ev, output); + memcpy(&shader_requirements, &gs->shader_requirements, + sizeof(struct gl_shader_requirements)); + shader_requirements.variant = SHADER_VARIANT_RGBX; + use_gl_program(gr, &shader_requirements); + shader_uniforms(gr->current_shader, ev, output); } if (ev->alpha < 1.0) @@ -1033,7 +1057,7 @@ draw_view(struct weston_view *ev, struct weston_output *output, } if (pixman_region32_not_empty(&surface_blend)) { - use_shader(gr, gs->shader); + use_gl_program(gr, &gs->shader_requirements); glEnable(GL_BLEND); repaint_region(ev, &repaint, &surface_blend); gs->used_in_output_repaint = true; @@ -1209,10 +1233,10 @@ draw_output_borders(struct weston_output *output, { struct gl_output_state *go = get_output_state(output); struct gl_renderer *gr = get_renderer(output->compositor); - struct gl_shader *shader = &gr->texture_shader_rgba; struct gl_border_image *top, *bottom, *left, *right; struct weston_matrix matrix; int full_width, full_height; + struct gl_shader_requirements shader_requirements; if (border_status == BORDER_STATUS_CLEAN) return; /* Clean. Nothing to do. */ @@ -1226,17 +1250,20 @@ draw_output_borders(struct weston_output *output, full_height = output->current_mode->height + top->height + bottom->height; glDisable(GL_BLEND); - use_shader(gr, shader); + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_RGBA; + use_gl_program(gr, &shader_requirements); glViewport(0, 0, full_width, full_height); weston_matrix_init(&matrix); weston_matrix_translate(&matrix, -full_width/2.0, -full_height/2.0, 0); weston_matrix_scale(&matrix, 2.0/full_width, -2.0/full_height, 1); - glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, matrix.d); + glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, + GL_FALSE, matrix.d); - glUniform1i(shader->tex_uniforms[0], 0); - glUniform1f(shader->alpha_uniform, 1); + glUniform1i(gr->current_shader->tex_uniforms[0], 0); + glUniform1f(gr->current_shader->alpha_uniform, 1); glActiveTexture(GL_TEXTURE0); if (border_status & BORDER_TOP_DIRTY) @@ -1706,28 +1733,28 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, switch (wl_shm_buffer_get_format(shm_buffer)) { case WL_SHM_FORMAT_XRGB8888: - gs->shader = &gr->texture_shader_rgbx; + gs->shader_requirements.variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; es->is_opaque = true; break; case WL_SHM_FORMAT_ARGB8888: - gs->shader = &gr->texture_shader_rgba; + gs->shader_requirements.variant = SHADER_VARIANT_RGBA; pitch = wl_shm_buffer_get_stride(shm_buffer) / 4; gl_format[0] = GL_BGRA_EXT; gl_pixel_type = GL_UNSIGNED_BYTE; es->is_opaque = false; break; case WL_SHM_FORMAT_RGB565: - gs->shader = &gr->texture_shader_rgbx; + gs->shader_requirements.variant = SHADER_VARIANT_RGBX; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_format[0] = GL_RGB; gl_pixel_type = GL_UNSIGNED_SHORT_5_6_5; es->is_opaque = true; break; case WL_SHM_FORMAT_YUV420: - gs->shader = &gr->texture_shader_y_u_v; + gs->shader_requirements.variant = SHADER_VARIANT_Y_U_V; pitch = wl_shm_buffer_get_stride(shm_buffer); gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 3; @@ -1759,18 +1786,18 @@ gl_renderer_attach_shm(struct weston_surface *es, struct weston_buffer *buffer, gs->hsub[1] = 2; gs->vsub[1] = 2; if (gr->has_gl_texture_rg) { - gs->shader = &gr->texture_shader_y_uv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_UV; gl_format[0] = GL_R8_EXT; gl_format[1] = GL_RG8_EXT; } else { - gs->shader = &gr->texture_shader_y_xuxv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_XUXV; gl_format[0] = GL_LUMINANCE; gl_format[1] = GL_LUMINANCE_ALPHA; } es->is_opaque = true; break; case WL_SHM_FORMAT_YUYV: - gs->shader = &gr->texture_shader_y_xuxv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_XUXV; pitch = wl_shm_buffer_get_stride(shm_buffer) / 2; gl_pixel_type = GL_UNSIGNED_BYTE; num_planes = 2; @@ -1849,26 +1876,26 @@ gl_renderer_attach_egl(struct weston_surface *es, struct weston_buffer *buffer, case EGL_TEXTURE_RGBA: default: num_planes = 1; - gs->shader = &gr->texture_shader_rgba; + gs->shader_requirements.variant = SHADER_VARIANT_RGBA; break; case EGL_TEXTURE_EXTERNAL_WL: num_planes = 1; gs->target = GL_TEXTURE_EXTERNAL_OES; - gs->shader = &gr->texture_shader_egl_external; + gs->shader_requirements.variant = SHADER_VARIANT_EXTERNAL; break; case EGL_TEXTURE_Y_UV_WL: num_planes = 2; - gs->shader = &gr->texture_shader_y_uv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_UV; es->is_opaque = true; break; case EGL_TEXTURE_Y_U_V_WL: num_planes = 3; - gs->shader = &gr->texture_shader_y_u_v; + gs->shader_requirements.variant = SHADER_VARIANT_Y_U_V; es->is_opaque = true; break; case EGL_TEXTURE_Y_XUXV_WL: num_planes = 2; - gs->shader = &gr->texture_shader_y_xuxv; + gs->shader_requirements.variant = SHADER_VARIANT_Y_XUXV; es->is_opaque = true; break; } @@ -2177,13 +2204,13 @@ import_yuv_dmabuf(struct gl_renderer *gr, switch (format->texture_type) { case EGL_TEXTURE_Y_XUXV_WL: - image->shader = &gr->texture_shader_y_xuxv; + image->shader_variant = SHADER_VARIANT_Y_XUXV; break; case EGL_TEXTURE_Y_UV_WL: - image->shader = &gr->texture_shader_y_uv; + image->shader_variant = SHADER_VARIANT_Y_UV; break; case EGL_TEXTURE_Y_U_V_WL: - image->shader = &gr->texture_shader_y_u_v; + image->shader_variant = SHADER_VARIANT_Y_U_V; break; default: assert(false); @@ -2229,10 +2256,10 @@ import_dmabuf(struct gl_renderer *gr, switch (image->target) { case GL_TEXTURE_2D: - image->shader = &gr->texture_shader_rgba; + image->shader_variant = SHADER_VARIANT_RGBA; break; default: - image->shader = &gr->texture_shader_egl_external; + image->shader_variant = SHADER_VARIANT_EXTERNAL; } } else { if (!import_yuv_dmabuf(gr, image)) { @@ -2465,7 +2492,7 @@ gl_renderer_attach_dmabuf(struct weston_surface *surface, gr->image_target_texture_2d(gs->target, gs->images[i]->image); } - gs->shader = image->shader; + gs->shader_requirements.variant = image->shader_variant; gs->pitch = buffer->width; gs->height = buffer->height; gs->buffer_type = BUFFER_TYPE_EGL; @@ -2533,7 +2560,6 @@ gl_renderer_surface_set_color(struct weston_surface *surface, float red, float green, float blue, float alpha) { struct gl_surface_state *gs = get_surface_state(surface); - struct gl_renderer *gr = get_renderer(surface->compositor); gs->color[0] = red; gs->color[1] = green; @@ -2543,7 +2569,7 @@ gl_renderer_surface_set_color(struct weston_surface *surface, gs->pitch = 1; gs->height = 1; - gs->shader = &gr->solid_shader; + gs->shader_requirements.variant = SHADER_VARIANT_SOLID; } static void @@ -2650,17 +2676,17 @@ gl_renderer_surface_copy_content(struct weston_surface *surface, glViewport(0, 0, cw, ch); glDisable(GL_BLEND); - use_shader(gr, gs->shader); + use_gl_program(gr, &gs->shader_requirements); if (gs->y_inverted) proj = projmat_normal; else proj = projmat_yinvert; - glUniformMatrix4fv(gs->shader->proj_uniform, 1, GL_FALSE, proj); - glUniform1f(gs->shader->alpha_uniform, 1.0f); + glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, GL_FALSE, proj); + glUniform1f(gr->current_shader->alpha_uniform, 1.0f); for (i = 0; i < gs->num_textures; i++) { - glUniform1i(gs->shader->tex_uniforms[i], i); + glUniform1i(gr->current_shader->tex_uniforms[i], i); glActiveTexture(GL_TEXTURE0 + i); glBindTexture(gs->target, gs->textures[i]); @@ -2780,196 +2806,6 @@ gl_renderer_create_surface(struct weston_surface *surface) return 0; } -static const char vertex_shader[] = - "uniform mat4 proj;\n" - "attribute vec2 position;\n" - "attribute vec2 texcoord;\n" - "varying vec2 v_texcoord;\n" - "void main()\n" - "{\n" - " gl_Position = proj * vec4(position, 0.0, 1.0);\n" - " v_texcoord = texcoord;\n" - "}\n"; - -/* Declare common fragment shader uniforms */ -#define FRAGMENT_CONVERT_YUV \ - " y *= alpha;\n" \ - " u *= alpha;\n" \ - " v *= alpha;\n" \ - " gl_FragColor.r = y + 1.59602678 * v;\n" \ - " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \ - " gl_FragColor.b = y + 2.01723214 * u;\n" \ - " gl_FragColor.a = alpha;\n" - -static const char fragment_debug[] = - " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n"; - -static const char fragment_brace[] = - "}\n"; - -static const char texture_fragment_shader_rgba[] = - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;" - ; - -static const char texture_fragment_shader_rgbx[] = - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform sampler2D tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb\n;" - " gl_FragColor.a = alpha;\n" - ; - -static const char texture_fragment_shader_egl_external[] = - "#extension GL_OES_EGL_image_external : require\n" - "precision mediump float;\n" - "varying vec2 v_texcoord;\n" - "uniform samplerExternalOES tex;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * texture2D(tex, v_texcoord)\n;" - ; - -static const char texture_fragment_shader_y_uv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).r - 0.5;\n" - " float v = texture2D(tex1, v_texcoord).g - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_y_u_v[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "uniform sampler2D tex2;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).x - 0.5;\n" - " float v = texture2D(tex2, v_texcoord).x - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char texture_fragment_shader_y_xuxv[] = - "precision mediump float;\n" - "uniform sampler2D tex;\n" - "uniform sampler2D tex1;\n" - "varying vec2 v_texcoord;\n" - "uniform float alpha;\n" - "void main() {\n" - " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" - " float u = texture2D(tex1, v_texcoord).g - 0.5;\n" - " float v = texture2D(tex1, v_texcoord).a - 0.5;\n" - FRAGMENT_CONVERT_YUV - ; - -static const char solid_fragment_shader[] = - "precision mediump float;\n" - "uniform vec4 color;\n" - "uniform float alpha;\n" - "void main()\n" - "{\n" - " gl_FragColor = alpha * color\n;" - ; - -static int -compile_shader(GLenum type, int count, const char **sources) -{ - GLuint s; - char msg[512]; - GLint status; - - s = glCreateShader(type); - glShaderSource(s, count, sources, NULL); - glCompileShader(s); - glGetShaderiv(s, GL_COMPILE_STATUS, &status); - if (!status) { - glGetShaderInfoLog(s, sizeof msg, NULL, msg); - weston_log("shader info: %s\n", msg); - return GL_NONE; - } - - return s; -} - -static int -shader_init(struct gl_shader *shader, struct gl_renderer *renderer, - const char *vertex_source, const char *fragment_source) -{ - char msg[512]; - GLint status; - int count; - const char *sources[3]; - - shader->vertex_shader = - compile_shader(GL_VERTEX_SHADER, 1, &vertex_source); - - if (renderer->fragment_shader_debug) { - sources[0] = fragment_source; - sources[1] = fragment_debug; - sources[2] = fragment_brace; - count = 3; - } else { - sources[0] = fragment_source; - sources[1] = fragment_brace; - count = 2; - } - - shader->fragment_shader = - compile_shader(GL_FRAGMENT_SHADER, count, sources); - - shader->program = glCreateProgram(); - glAttachShader(shader->program, shader->vertex_shader); - glAttachShader(shader->program, shader->fragment_shader); - glBindAttribLocation(shader->program, 0, "position"); - glBindAttribLocation(shader->program, 1, "texcoord"); - - glLinkProgram(shader->program); - glGetProgramiv(shader->program, GL_LINK_STATUS, &status); - if (!status) { - glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg); - weston_log("link info: %s\n", msg); - return -1; - } - - shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); - shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); - shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); - shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); - shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); - shader->color_uniform = glGetUniformLocation(shader->program, "color"); - - return 0; -} - -static void -shader_release(struct gl_shader *shader) -{ - glDeleteShader(shader->vertex_shader); - glDeleteShader(shader->fragment_shader); - glDeleteProgram(shader->program); - - shader->vertex_shader = 0; - shader->fragment_shader = 0; - shader->program = 0; -} - static void log_extensions(const char *name, const char *extensions) { @@ -3312,12 +3148,17 @@ gl_renderer_destroy(struct weston_compositor *ec) { struct gl_renderer *gr = get_renderer(ec); struct dmabuf_image *image, *next; + struct gl_shader *shader, *next_shader; wl_signal_emit(&gr->destroy_signal, gr); if (gr->has_bind_display) gr->unbind_display(gr->egl_display, ec->wl_display); + wl_list_for_each_safe(shader, next_shader, &gr->shader_list, link) { + gl_shader_destroy(shader); + } + /* Work around crash in egl_dri2.c's dri2_make_current() - when does this apply? */ eglMakeCurrent(gr->egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, @@ -3653,6 +3494,8 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, gr->base.surface_copy_content = gl_renderer_surface_copy_content; gr->egl_display = NULL; + wl_list_init(&gr->shader_list); + /* extension_suffix is supported */ if (supports) { if (!get_platform_display) { @@ -3758,38 +3601,6 @@ gl_renderer_display(struct weston_compositor *ec) return get_renderer(ec)->egl_display; } -static int -compile_shaders(struct weston_compositor *ec) -{ - struct gl_renderer *gr = get_renderer(ec); - - gr->texture_shader_rgba.vertex_source = vertex_shader; - gr->texture_shader_rgba.fragment_source = texture_fragment_shader_rgba; - - gr->texture_shader_rgbx.vertex_source = vertex_shader; - gr->texture_shader_rgbx.fragment_source = texture_fragment_shader_rgbx; - - gr->texture_shader_egl_external.vertex_source = vertex_shader; - gr->texture_shader_egl_external.fragment_source = - texture_fragment_shader_egl_external; - - gr->texture_shader_y_uv.vertex_source = vertex_shader; - gr->texture_shader_y_uv.fragment_source = texture_fragment_shader_y_uv; - - gr->texture_shader_y_u_v.vertex_source = vertex_shader; - gr->texture_shader_y_u_v.fragment_source = - texture_fragment_shader_y_u_v; - - gr->texture_shader_y_xuxv.vertex_source = vertex_shader; - gr->texture_shader_y_xuxv.fragment_source = - texture_fragment_shader_y_xuxv; - - gr->solid_shader.vertex_source = vertex_shader; - gr->solid_shader.fragment_source = solid_fragment_shader; - - return 0; -} - static void fragment_debug_binding(struct weston_keyboard *keyboard, const struct timespec *time, @@ -3798,16 +3609,13 @@ fragment_debug_binding(struct weston_keyboard *keyboard, struct weston_compositor *ec = data; struct gl_renderer *gr = get_renderer(ec); struct weston_output *output; + struct gl_shader *shader, *next; gr->fragment_shader_debug ^= 1; - shader_release(&gr->texture_shader_rgba); - shader_release(&gr->texture_shader_rgbx); - shader_release(&gr->texture_shader_egl_external); - shader_release(&gr->texture_shader_y_uv); - shader_release(&gr->texture_shader_y_u_v); - shader_release(&gr->texture_shader_y_xuxv); - shader_release(&gr->solid_shader); + wl_list_for_each_safe(shader, next, &gr->shader_list, link) { + gl_shader_destroy(shader); + } /* Force use_shader() to call glUseProgram(), since we need to use * the recompiled version of the shader. */ @@ -3963,9 +3771,6 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) glActiveTexture(GL_TEXTURE0); - if (compile_shaders(ec)) - return -1; - gr->fragment_binding = weston_compositor_add_debug_binding(ec, KEY_S, fragment_debug_binding, diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c new file mode 100644 index 000000000..0946f9106 --- /dev/null +++ b/libweston/renderer-gl/gl-shaders.c @@ -0,0 +1,332 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include +#include +#include + + +#include +#include "gl-renderer-private.h" +#include "shared/helpers.h" + +static const char vertex_shader[] = + "uniform mat4 proj;\n" + "attribute vec2 position;\n" + "attribute vec2 texcoord;\n" + "varying vec2 v_texcoord;\n" + "void main()\n" + "{\n" + " gl_Position = proj * vec4(position, 0.0, 1.0);\n" + " v_texcoord = texcoord;\n" + "}\n"; + +#define FRAGMENT_CONVERT_YUV \ + " y *= alpha;\n" \ + " u *= alpha;\n" \ + " v *= alpha;\n" \ + " gl_FragColor.r = y + 1.59602678 * v;\n" \ + " gl_FragColor.g = y - 0.39176229 * u - 0.81296764 * v;\n" \ + " gl_FragColor.b = y + 2.01723214 * u;\n" \ + " gl_FragColor.a = alpha;\n" + +static const char external_extension[] = + "#extension GL_OES_EGL_image_external : require\n"; + +static const char fragment_header[] = + "precision mediump float;\n" + "varying vec2 v_texcoord;\n" + "uniform float alpha;\n"; + +static const char uniform_tex_external[] = "uniform samplerExternalOES tex;\n"; + +static const char uniform_color[] = "uniform vec4 color;\n"; + +static const char uniform_tex2[] = "uniform sampler2D tex2;\n"; + +static const char uniform_tex1[] = "uniform sampler2D tex1;\n"; + +static const char uniform_tex[] = "uniform sampler2D tex;\n"; + +static const char fragment_main_open[] = + "void main()\n" + "{\n"; + +static const char fragment_debug[] = + " gl_FragColor = vec4(0.0, 0.3, 0.0, 0.2) + gl_FragColor * 0.8;\n"; + +static const char texture_fragment_shader_rgba[] = + " gl_FragColor = alpha * texture2D(tex, v_texcoord);\n" + ; + +static const char texture_fragment_shader_rgbx[] = + " gl_FragColor.rgb = alpha * texture2D(tex, v_texcoord).rgb;\n" + " gl_FragColor.a = alpha;\n" + ; + +static const char texture_fragment_shader_external[] = + " gl_FragColor = alpha * texture2D(tex, v_texcoord);\n" + ; + +static const char texture_fragment_shader_y_uv[] = + " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" + " vec2 uv = texture2D(tex1, v_texcoord).rg;\n" + " float u = uv.r - 0.5;\n" + " float v = uv.g - 0.5;\n" + FRAGMENT_CONVERT_YUV + ; + +static const char texture_fragment_shader_y_u_v[] = + " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" + " float u = texture2D(tex1, v_texcoord).x - 0.5;\n" + " float v = texture2D(tex2, v_texcoord).x - 0.5;\n" + FRAGMENT_CONVERT_YUV + ; + +static const char texture_fragment_shader_y_xuxv[] = + " float y = 1.16438356 * (texture2D(tex, v_texcoord).x - 0.0625);\n" + " vec2 uv = texture2D(tex1, v_texcoord).ga;\n" + " float u = uv.r - 0.5;\n" + " float v = uv.g - 0.5;\n" + FRAGMENT_CONVERT_YUV + ; + +static const char solid_fragment_shader[] = + " gl_FragColor = alpha * color;\n" + ; + +static const char fragment_brace[] = "}\n"; + +struct gl_shader_source { + const char *parts[64]; + uint32_t len; +}; + +static inline void +gl_shader_source_add(struct gl_shader_source *shader_source, const char *str) +{ + shader_source->parts[shader_source->len++] = str; + assert(shader_source->len < ARRAY_LENGTH(shader_source->parts)); +} + +static void +generate_fs_uniforms(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + /* write the header */ + /* require extension for EXTERNAL SHADERS: */ + if (requirements->variant == SHADER_VARIANT_EXTERNAL) { + gl_shader_source_add(shader_source, external_extension); + } + + gl_shader_source_add(shader_source, fragment_header); + + /* Generate uniforms based on variant */ + switch (requirements->variant) { + case SHADER_VARIANT_EXTERNAL: + gl_shader_source_add(shader_source, uniform_tex_external); + break; + case SHADER_VARIANT_SOLID: + gl_shader_source_add(shader_source, uniform_color); + break; + case SHADER_VARIANT_Y_U_V: + gl_shader_source_add(shader_source, uniform_tex2); + + /* fallthrough */ + case SHADER_VARIANT_Y_UV: + case SHADER_VARIANT_Y_XUXV: + gl_shader_source_add(shader_source, uniform_tex1); + + /* fallthrough */ + case SHADER_VARIANT_RGBX: + case SHADER_VARIANT_RGBA: + /* fallthrough */ + default: + gl_shader_source_add(shader_source, uniform_tex); + break; + } + +} + +static void +generate_fs_variants(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + /* Generate the shader based on variant */ + switch (requirements->variant) { + case SHADER_VARIANT_Y_U_V: + gl_shader_source_add(shader_source, + texture_fragment_shader_y_u_v); + break; + case SHADER_VARIANT_Y_UV: + gl_shader_source_add(shader_source, + texture_fragment_shader_y_uv); + break; + case SHADER_VARIANT_Y_XUXV: + gl_shader_source_add(shader_source, + texture_fragment_shader_y_xuxv); + break; + case SHADER_VARIANT_RGBX: + gl_shader_source_add(shader_source, + texture_fragment_shader_rgbx); + break; + case SHADER_VARIANT_RGBA: + gl_shader_source_add(shader_source, + texture_fragment_shader_rgba); + break; + case SHADER_VARIANT_EXTERNAL: + gl_shader_source_add(shader_source, + texture_fragment_shader_external); + break; + case SHADER_VARIANT_SOLID: + gl_shader_source_add(shader_source, solid_fragment_shader); + break; + case SHADER_VARIANT_NONE: + break; + } + +} + +static void +generate_fragment_shader(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + uint32_t i; + + /* Write the header and required uniforms */ + generate_fs_uniforms(shader_source, requirements); + + /* begin main function */ + gl_shader_source_add(shader_source, fragment_main_open); + + /* Generate the shader based on variant */ + generate_fs_variants(shader_source, requirements); + + if (requirements->debug) + gl_shader_source_add(shader_source, fragment_debug); + + gl_shader_source_add(shader_source, fragment_brace); + + weston_log("Generated shader length: %d, shader:\n", shader_source->len); + for(i = 0; i < shader_source->len; i++) { + weston_log_continue("%s", shader_source->parts[i]); + } + weston_log_continue("\n"); +} + +void +gl_shader_requirements_init(struct gl_shader_requirements *requirements) +{ + memset(requirements, 0, sizeof(struct gl_shader_requirements)); +} + +void +gl_shader_destroy(struct gl_shader *shader) +{ + glDeleteShader(shader->vertex_shader); + glDeleteShader(shader->fragment_shader); + glDeleteProgram(shader->program); + + shader->vertex_shader = 0; + shader->fragment_shader = 0; + shader->program = 0; + wl_list_remove(&shader->link); + free(shader); +} + +static int +compile_shader(GLenum type, int count, const char **sources) +{ + GLuint s; + char msg[512]; + GLint status; + + s = glCreateShader(type); + glShaderSource(s, count, sources, NULL); + glCompileShader(s); + glGetShaderiv(s, GL_COMPILE_STATUS, &status); + if (!status) { + glGetShaderInfoLog(s, sizeof msg, NULL, msg); + weston_log("shader info: %s\n", msg); + return GL_NONE; + } + + return s; +} + +struct gl_shader * +gl_shader_create(struct gl_shader_requirements *requirements) +{ + struct gl_shader *shader = NULL; + char msg[512]; + GLint status; + const char *vertex_source[1]; + struct gl_shader_source fragment_source; + + shader = zalloc(sizeof *shader); + if (!shader) { + weston_log("could not create shader\n"); + return NULL; + } + + memcpy(&shader->key, requirements, + sizeof(struct gl_shader_requirements)); + + vertex_source[0] = vertex_shader; + + fragment_source.len = 0; + generate_fragment_shader(&fragment_source, requirements); + + shader->vertex_shader = compile_shader(GL_VERTEX_SHADER, 1, + vertex_source); + + shader->fragment_shader = compile_shader(GL_FRAGMENT_SHADER, + fragment_source.len, + fragment_source.parts); + + shader->program = glCreateProgram(); + glAttachShader(shader->program, shader->vertex_shader); + glAttachShader(shader->program, shader->fragment_shader); + glBindAttribLocation(shader->program, 0, "position"); + glBindAttribLocation(shader->program, 1, "texcoord"); + + glLinkProgram(shader->program); + glGetProgramiv(shader->program, GL_LINK_STATUS, &status); + if (!status) { + glGetProgramInfoLog(shader->program, sizeof msg, NULL, msg); + weston_log("link info: %s\n", msg); + return NULL; + } + + shader->proj_uniform = glGetUniformLocation(shader->program, "proj"); + shader->tex_uniforms[0] = glGetUniformLocation(shader->program, "tex"); + shader->tex_uniforms[1] = glGetUniformLocation(shader->program, "tex1"); + shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); + shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); + shader->color_uniform = glGetUniformLocation(shader->program, "color"); + + return shader; +} diff --git a/libweston/renderer-gl/meson.build b/libweston/renderer-gl/meson.build index b5d77d587..91a3bfa19 100644 --- a/libweston/renderer-gl/meson.build +++ b/libweston/renderer-gl/meson.build @@ -6,6 +6,7 @@ config_h.set('ENABLE_EGL', '1') srcs_renderer_gl = [ 'gl-renderer.c', + 'gl-shaders.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, ] -- GitLab From 65d9233bc9d40ebd48f942ff9f844ff15fe5a45a Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Thu, 18 Apr 2019 21:45:48 +0530 Subject: [PATCH 02/29] gl-renderer: use intermediate texture for linear light blending Linear light blending is required when the presented surfaces are of different colorspaces. The surfaces are linearized, colorspace convertion is applied (if required) and then blended together. All this is done to an intermediate texture so that it can be blitted on to the framebuffer while applying the required non linear curve. 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 --- libweston/renderer-gl/gl-renderer.c | 155 ++++++++++++++++++++++++++-- 1 file changed, 147 insertions(+), 8 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 80a7d33e7..6dea3aa78 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -98,6 +98,8 @@ struct gl_output_state { /* struct timeline_render_point::link */ struct wl_list timeline_render_point_list; + GLuint shadow_fbo; + GLuint shadow_tex; }; enum buffer_type { @@ -257,6 +259,8 @@ struct gl_renderer { * List constains cached shaders built from struct gl_shader_requirements */ struct wl_list shader_list; + + bool supports_half_float_texture; }; enum timeline_render_point_type { @@ -1379,6 +1383,73 @@ output_rotate_damage(struct weston_output *output, go->border_damage[go->buffer_damage_index] = border_status; } +static void +repaint_from_texture(struct weston_output *output, + pixman_region32_t *output_damage) +{ + struct gl_output_state *go = get_output_state(output); + struct gl_renderer *gr = get_renderer(output->compositor); + double width = output->current_mode->width; + double height = output->current_mode->height; + pixman_box32_t *rects; + int n_rects; + int i; + struct gl_shader_requirements shader_requirements; + + GLfloat verts[4 * 2] = { 0.0f }; + + 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 + }; + + /* Bind default framebuffer */ + 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); + + gl_shader_requirements_init(&shader_requirements); + shader_requirements.variant = SHADER_VARIANT_RGBA; + use_gl_program(gr, &shader_requirements); + + glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, GL_FALSE, proj); + glUniform1f(gr->current_shader->alpha_uniform, 1.0f); + + glUniform1i(gr->current_shader->tex_uniforms[0], 0); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, go->shadow_tex); + + rects = pixman_region32_rectangles(output_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); +} + /* NOTE: We now allow falling back to ARGB gl visuals when XRGB is * unavailable, so we're assuming the background has no transparency * and that everything with a blend, like drop shadows, will have something @@ -1423,12 +1494,6 @@ gl_renderer_repaint_output(struct weston_output *output, 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 */ go->output_matrix = output->matrix; weston_matrix_translate(&go->output_matrix, @@ -1438,6 +1503,12 @@ gl_renderer_repaint_output(struct weston_output *output, 2.0 / output->current_mode->width, -2.0 / output->current_mode->height, 1); + 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); + /* if debugging, redraw everything outside the damage to clean up * debug lines from the previous draw on this buffer: */ @@ -1452,6 +1523,14 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_fini(&undamaged); } + if (gr->supports_half_float_texture) { + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); + glViewport(0, 0, + output->current_mode->width, + output->current_mode->height); + + } + pixman_region32_init(&total_damage); pixman_region32_init(&buffer_damage); @@ -1461,13 +1540,20 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_union(&total_damage, &buffer_damage, output_damage); border_damage |= go->border_status; - repaint_views(output, &total_damage); + if (gr->supports_half_float_texture) { + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); + repaint_views(output, output_damage); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + repaint_from_texture(output, &total_damage); + } else { + glBindFramebuffer(GL_FRAMEBUFFER, 0); + repaint_views(output, &total_damage); + } pixman_region32_fini(&total_damage); pixman_region32_fini(&buffer_damage); draw_output_borders(output, border_damage); - pixman_region32_copy(&output->previous_damage, output_damage); wl_signal_emit(&output->frame_signal, output); @@ -3037,7 +3123,11 @@ gl_renderer_output_create(struct weston_output *output, EGLSurface surface) { struct gl_output_state *go; + struct gl_renderer *gr = get_renderer(output->compositor); int i; + int width = output->current_mode->width; + int height = output->current_mode->height; + int fb_status; go = zalloc(sizeof *go); if (go == NULL) @@ -3055,6 +3145,33 @@ gl_renderer_output_create(struct weston_output *output, output->renderer_state = go; + if (gr->supports_half_float_texture) { + glGenTextures(1, &go->shadow_tex); + glBindTexture(GL_TEXTURE_2D, go->shadow_tex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F_EXT, width, height, 0, + GL_RGBA, GL_FLOAT, 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, &go->shadow_fbo); + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, go->shadow_tex, 0); + + fb_status = glCheckFramebufferStatus(GL_FRAMEBUFFER); + + /* no framebuffer, bail! */ + if (fb_status != GL_FRAMEBUFFER_COMPLETE) { + weston_log("Unable to create shadow FBO\n"); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return -1; + } + } + + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + return 0; } @@ -3117,6 +3234,11 @@ gl_renderer_output_destroy(struct weston_output *output) if (go->end_render_sync != EGL_NO_SYNC_KHR) gr->destroy_sync(gr->egl_display, go->end_render_sync); + if (gr->supports_half_float_texture) { + glDeleteTextures(1, &go->shadow_tex); + glDeleteFramebuffers(1, &go->shadow_fbo); + } + free(go); } @@ -3660,6 +3782,7 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) const char *extensions; EGLConfig context_config; EGLBoolean ret; + EGLint major_version; EGLint context_attribs[16] = { EGL_CONTEXT_CLIENT_VERSION, 0, @@ -3769,6 +3892,22 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface) if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external")) gr->has_egl_image_external = 1; + if (weston_check_egl_extension(extensions, "GL_OES_EGL_image_external")) + gr->has_egl_image_external = 1; + + major_version = gr->gl_version >> 16; + switch (major_version) { + case 3: + gr->supports_half_float_texture = true; + break; + case 2: + if (weston_check_egl_extension(extensions, "GL_OES_texture_half_float")) + gr->supports_half_float_texture = true; + break; + default: + gr->supports_half_float_texture = false; + } + glActiveTexture(GL_TEXTURE0); gr->fragment_binding = -- GitLab From 9813731cafbfea887030c9320611e9b8b40c94be Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Thu, 18 Apr 2019 21:45:48 +0530 Subject: [PATCH 03/29] gl-shaders: Add debug scope to print generated shader Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer-private.h | 11 +++- libweston/renderer-gl/gl-renderer.c | 7 ++- libweston/renderer-gl/gl-shaders.c | 60 +++++++++++++++++---- 3 files changed, 67 insertions(+), 11 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer-private.h b/libweston/renderer-gl/gl-renderer-private.h index c9ac88ecb..a970e2aa6 100644 --- a/libweston/renderer-gl/gl-renderer-private.h +++ b/libweston/renderer-gl/gl-renderer-private.h @@ -61,6 +61,8 @@ struct gl_shader { struct wl_list link; /* gl_renderer::shader_list */ }; +struct gl_shader_generator; + void gl_shader_requirements_init(struct gl_shader_requirements *requirements); @@ -68,6 +70,13 @@ void gl_shader_destroy(struct gl_shader *shader); struct gl_shader * -gl_shader_create(struct gl_shader_requirements *requirements); +gl_shader_create(struct gl_shader_generator *sg, + struct gl_shader_requirements *requirements); + +struct gl_shader_generator * +gl_shader_generator_create(struct weston_compositor *compositor); + +void +gl_shader_generator_destroy(struct gl_shader_generator *sg); #endif diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 6dea3aa78..f40ea33e0 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -261,6 +261,7 @@ struct gl_renderer { struct wl_list shader_list; bool supports_half_float_texture; + struct gl_shader_generator *sg; }; enum timeline_render_point_type { @@ -856,7 +857,7 @@ use_gl_program(struct gl_renderer *gr, } if (!shader) { - shader = gl_shader_create(&reqs); + shader = gl_shader_create(gr->sg, &reqs); if (!shader) { weston_log("warning: failed to generate gl program\n"); return; @@ -3307,6 +3308,8 @@ gl_renderer_destroy(struct weston_compositor *ec) if (gr->fan_binding) weston_binding_destroy(gr->fan_binding); + gl_shader_generator_destroy(gr->sg); + free(gr); } @@ -3706,6 +3709,8 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, goto fail_with_error; } + gr->sg = gl_shader_generator_create(ec); + return 0; fail_with_error: diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 0946f9106..0c90a7375 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -32,6 +32,11 @@ #include #include "gl-renderer-private.h" #include "shared/helpers.h" +#include "weston-debug.h" + +struct gl_shader_generator { + struct weston_debug_scope *debug; +}; static const char vertex_shader[] = "uniform mat4 proj;\n" @@ -211,11 +216,33 @@ generate_fs_variants(struct gl_shader_source *shader_source, } static void -generate_fragment_shader(struct gl_shader_source *shader_source, - struct gl_shader_requirements *requirements) +log_shader(struct gl_shader_generator *sg, + struct gl_shader_source *shader_source) { + char *str; + FILE *fp; + size_t len; uint32_t i; + fp = open_memstream(&str, &len); + assert(fp); + + fprintf(fp, "Generated shader length: %d, shader:\n", shader_source->len); + for(i = 0; i < shader_source->len; i++) { + fprintf(fp, "%s", shader_source->parts[i]); + } + fprintf(fp, "\n"); + fclose(fp); + + weston_debug_scope_printf(sg->debug, "%s", str); + free(str); +} + +static void +generate_fragment_shader(struct gl_shader_generator *sg, + struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ /* Write the header and required uniforms */ generate_fs_uniforms(shader_source, requirements); @@ -230,11 +257,7 @@ generate_fragment_shader(struct gl_shader_source *shader_source, gl_shader_source_add(shader_source, fragment_brace); - weston_log("Generated shader length: %d, shader:\n", shader_source->len); - for(i = 0; i < shader_source->len; i++) { - weston_log_continue("%s", shader_source->parts[i]); - } - weston_log_continue("\n"); + log_shader(sg, shader_source); } void @@ -278,7 +301,8 @@ compile_shader(GLenum type, int count, const char **sources) } struct gl_shader * -gl_shader_create(struct gl_shader_requirements *requirements) +gl_shader_create(struct gl_shader_generator *sg, + struct gl_shader_requirements *requirements) { struct gl_shader *shader = NULL; char msg[512]; @@ -298,7 +322,7 @@ gl_shader_create(struct gl_shader_requirements *requirements) vertex_source[0] = vertex_shader; fragment_source.len = 0; - generate_fragment_shader(&fragment_source, requirements); + generate_fragment_shader(sg, &fragment_source, requirements); shader->vertex_shader = compile_shader(GL_VERTEX_SHADER, 1, vertex_source); @@ -330,3 +354,21 @@ gl_shader_create(struct gl_shader_requirements *requirements) return shader; } + +struct gl_shader_generator * +gl_shader_generator_create(struct weston_compositor *compositor) +{ + struct gl_shader_generator *sg = zalloc(sizeof *sg); + sg->debug = weston_compositor_add_debug_scope(compositor, "gl-shader-generator", + "Debug messages from GL renderer", + NULL, NULL); + return sg; +} + +void +gl_shader_generator_destroy(struct gl_shader_generator *sg) +{ + weston_debug_scope_destroy(sg->debug); + sg->debug = NULL; + free(sg); +} -- GitLab From 7f2dc4bc1338f207c177738e9a4f44ab481c577b Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 18 Mar 2019 21:06:58 +0530 Subject: [PATCH 04/29] Add color space definitions Create common color primary definition (Pekka) Signed-off-by: Harish Krupo Signed-off-by: Harish Krupo --- include/libweston/colorspace.h | 79 ++++++++++++++ include/libweston/meson.build | 1 + libweston/meson.build | 1 + shared/colorspace.c | 190 +++++++++++++++++++++++++++++++++ 4 files changed, 271 insertions(+) create mode 100644 include/libweston/colorspace.h create mode 100644 shared/colorspace.c diff --git a/include/libweston/colorspace.h b/include/libweston/colorspace.h new file mode 100644 index 000000000..f02d628b2 --- /dev/null +++ b/include/libweston/colorspace.h @@ -0,0 +1,79 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_COLORSPACE_H +#define WESTON_COLORSPACE_H + +#include "matrix.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** A CIE 1931 color space*/ +struct cie_xy { + double x; + double y; +}; + +struct color_primaries { + struct cie_xy r; + struct cie_xy g; + struct cie_xy b; + struct cie_xy white_point; +}; + +struct weston_colorspace { + struct color_primaries primaries; + const char *name; + const char *whitepoint_name; +}; + +enum weston_colorspace_enums { + WESTON_CS_BT470M, + WESTON_CS_BT470BG, + WESTON_CS_SMPTE170M, + WESTON_CS_SMPTE240M, + WESTON_CS_BT709, + WESTON_CS_BT2020, + WESTON_CS_SRGB, + WESTON_CS_ADOBERGB, + WESTON_CS_DCI_P3, + WESTON_CS_PROPHOTORGB, + WESTON_CS_CIERGB, + WESTON_CS_CIEXYZ, + WESTON_CS_AP0, + WESTON_CS_AP1, + WESTON_CS_UNDEFINED +}; + +const struct weston_colorspace * +weston_colorspace_lookup(enum weston_colorspace_enums colorspace); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libweston/meson.build b/include/libweston/meson.build index 8e8f81604..7cf278df0 100644 --- a/include/libweston/meson.build +++ b/include/libweston/meson.build @@ -6,6 +6,7 @@ install_headers( 'timeline-object.h', 'windowed-output-api.h', 'zalloc.h', + 'colorspace.h', subdir: dir_include_libweston_install ) diff --git a/libweston/meson.build b/libweston/meson.build index 3c9640887..3dd1d67c7 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -28,6 +28,7 @@ srcs_libweston = [ 'weston-debug.c', 'zoom.c', '../shared/matrix.c', + '../shared/colorspace.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, linux_explicit_synchronization_unstable_v1_protocol_c, diff --git a/shared/colorspace.c b/shared/colorspace.c new file mode 100644 index 000000000..1d13dbc1b --- /dev/null +++ b/shared/colorspace.c @@ -0,0 +1,190 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include + +#ifdef IN_WESTON +#include +#else +#define WL_EXPORT +#endif + +#include "helpers.h" +#include + +static const struct weston_colorspace bt470m = { + .primaries.r = { 0.670f, 0.330f, }, + .primaries.g = { 0.210f, 0.710f, }, + .primaries.b = { 0.140f, 0.080f, }, + .primaries.white_point = { 0.3101f, 0.3162f, }, + .name = "BT.470 M", + .whitepoint_name = "C", +}; + +static const struct weston_colorspace bt470bg = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.290f, 0.600f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "BT.470 B/G", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace smpte170m = { + .primaries.r = { 0.630f, 0.340f, }, + .primaries.g = { 0.310f, 0.595f, }, + .primaries.b = { 0.155f, 0.070f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "SMPTE 170M", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace smpte240m = { + .primaries.r = { 0.630f, 0.340f, }, + .primaries.g = { 0.310f, 0.595f, }, + .primaries.b = { 0.155f, 0.070f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "SMPTE 240M", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace bt709 = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.300f, 0.600f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "BT.709", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace bt2020 = { + .primaries.r = { 0.708f, 0.292f, }, + .primaries.g = { 0.170f, 0.797f, }, + .primaries.b = { 0.131f, 0.046f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "BT.2020", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace srgb = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.300f, 0.600f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "sRGB", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace adobergb = { + .primaries.r = { 0.640f, 0.330f, }, + .primaries.g = { 0.210f, 0.710f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "AdobeRGB", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace dci_p3 = { + .primaries.r = { 0.680f, 0.320f, }, + .primaries.g = { 0.265f, 0.690f, }, + .primaries.b = { 0.150f, 0.060f, }, + .primaries.white_point = { 0.3127f, 0.3290f, }, + .name = "DCI-P3 D65", + .whitepoint_name = "D65", +}; + +static const struct weston_colorspace prophotorgb = { + .primaries.r = { 0.7347f, 0.2653f, }, + .primaries.g = { 0.1596f, 0.8404f, }, + .primaries.b = { 0.0366f, 0.0001f, }, + .primaries.white_point = { .3457, .3585 }, + .name = "ProPhoto RGB", + .whitepoint_name = "D50", +}; + +static const struct weston_colorspace ciergb = { + .primaries.r = { 0.7347f, 0.2653f, }, + .primaries.g = { 0.2738f, 0.7174f, }, + .primaries.b = { 0.1666f, 0.0089f, }, + .primaries.white_point = { 1.0f / 3.0f, 1.0f / 3.0f, }, + .name = "CIE RGB", + .whitepoint_name = "E", +}; + +static const struct weston_colorspace ciexyz = { + .primaries.r = { 1.0f, 0.0f, }, + .primaries.g = { 0.0f, 1.0f, }, + .primaries.b = { 0.0f, 0.0f, }, + .primaries.white_point = { 1.0f / 3.0f, 1.0f / 3.0f, }, + .name = "CIE XYZ", + .whitepoint_name = "E", +}; + +const struct weston_colorspace ap0 = { + .primaries.r = { 0.7347f, 0.2653f, }, + .primaries.g = { 0.0000f, 1.0000f, }, + .primaries.b = { 0.0001f, -0.0770f, }, + .primaries.white_point = { .32168f, .33767f, }, + .name = "ACES .primaries #0", + .whitepoint_name = "D60", +}; + +const struct weston_colorspace ap1 = { + .primaries.r = { 0.713f, 0.393f, }, + .primaries.g = { 0.165f, 0.830f, }, + .primaries.b = { 0.128f, 0.044f, }, + .primaries.white_point = { 0.32168f, 0.33767f, }, + .name = "ACES .primaries #1", + .whitepoint_name = "D60", +}; + +static const struct weston_colorspace * const colorspaces[] = { + [WESTON_CS_BT470M] = &bt470m, + [WESTON_CS_BT470BG] = &bt470bg, + [WESTON_CS_SMPTE170M] = &smpte170m, + [WESTON_CS_SMPTE240M] = &smpte240m, + [WESTON_CS_BT709] = &bt709, + [WESTON_CS_BT2020] = &bt2020, + [WESTON_CS_SRGB] = &srgb, + [WESTON_CS_ADOBERGB] = &adobergb, + [WESTON_CS_DCI_P3] = &dci_p3, + [WESTON_CS_PROPHOTORGB] = &prophotorgb, + [WESTON_CS_CIERGB] = &ciergb, + [WESTON_CS_CIEXYZ] = &ciexyz, + [WESTON_CS_AP0] = &ap0, + [WESTON_CS_AP1] = &ap1, +}; + +WL_EXPORT const struct weston_colorspace * +weston_colorspace_lookup(enum weston_colorspace_enums colorspace) +{ + if (colorspace < 0 || colorspace >= WESTON_CS_UNDEFINED) + return NULL; + + return colorspaces[colorspace]; +} -- GitLab From 474debedc3cfd665f1328eb322457f0816ecd93b Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 24 Dec 2018 16:38:13 +0530 Subject: [PATCH 05/29] [DO NOT MERGE] Implement the hdr-metadata-unstable-v1 protocol This is just a placeholder to get data from clients to test our HDR stack. The actual protocol is being discussed here: https://patchwork.freedesktop.org/series/57657/ The protocol is to set the colorspace details of the surface. The HDR metadata would also be part of that protocol. v2: Use the common color primary definitions (Pekka) Signed-off-by: Harish Krupo --- include/libweston/hdr_metadata_defs.h | 74 ++++++++++ include/libweston/libweston.h | 5 + libweston/compositor-drm.c | 4 + libweston/compositor.c | 13 ++ libweston/hdr_metadata.c | 204 ++++++++++++++++++++++++++ libweston/meson.build | 3 + protocol/meson.build | 1 + 7 files changed, 304 insertions(+) create mode 100644 include/libweston/hdr_metadata_defs.h create mode 100644 libweston/hdr_metadata.c diff --git a/include/libweston/hdr_metadata_defs.h b/include/libweston/hdr_metadata_defs.h new file mode 100644 index 000000000..d93128662 --- /dev/null +++ b/include/libweston/hdr_metadata_defs.h @@ -0,0 +1,74 @@ +/* + * Copyright © 2018 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_HDR_METADATA_DEFS_H +#define WESTON_HDR_METADATA_DEFS_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum hdr_metadata_type { + HDR_METADATA_TYPE1, + HDR_METADATA_TYPE2, +}; + +enum hdr_metadata_eotf { + WESTON_EOTF_TRADITIONAL_GAMMA_SDR, + WESTON_EOTF_TRADITIONAL_GAMMA_HDR, + WESTON_EOTF_ST2084, + WESTON_EOTF_HLG, +}; + +struct weston_hdr_metadata_dynamic { + uint8_t size; + uint8_t *metadata; +}; + +struct weston_hdr_metadata_static { + struct color_primaries primaries; + double max_luminance; + double min_luminance; + uint32_t max_cll; + uint32_t max_fall; + uint8_t eotf; +}; + +struct weston_hdr_metadata { + enum hdr_metadata_type metadata_type; + union { + struct weston_hdr_metadata_static static_metadata; + struct weston_hdr_metadata_dynamic dynamic_metadata; + } metadata; +}; + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 965831878..53dc15c83 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -45,6 +45,7 @@ extern "C" { #include #include #include +#include struct weston_geometry { int32_t x, y; @@ -1389,6 +1390,7 @@ struct weston_surface_state { /* zwp_surface_synchronization_v1.get_release */ struct weston_buffer_release_reference buffer_release_ref; + struct weston_hdr_metadata *hdr_metadata; }; struct weston_surface_activation_data { @@ -1518,6 +1520,8 @@ struct weston_surface { struct wl_resource *synchronization_resource; int acquire_fence_fd; struct weston_buffer_release_reference buffer_release_ref; + struct wl_resource *hdr_surface_resource; + struct weston_hdr_metadata *hdr_metadata; }; struct weston_subsurface { @@ -2388,6 +2392,7 @@ weston_debug_compositor_destroy(struct weston_compositor *compositor); void weston_buffer_send_server_error(struct weston_buffer *buffer, const char *msg); +int weston_hdr_metadata_setup(struct weston_compositor *compositor); #ifdef __cplusplus } diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 5c1ccdd7c..26012824d 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -7618,6 +7618,10 @@ drm_backend_create(struct weston_compositor *compositor, " synchronization support failed.\n"); } + if (weston_hdr_metadata_setup(compositor) < 0) + weston_log("Error: initializing hdr metadata " + "support failed.\n"); + ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME, &api, sizeof(api)); diff --git a/libweston/compositor.c b/libweston/compositor.c index 6a7c4cedc..dcdb65c6a 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -3372,6 +3372,19 @@ weston_surface_commit_state(struct weston_surface *surface, &state->feedback_list); wl_list_init(&state->feedback_list); + //Apply colorspace and HDR metadata state + if (surface->pending.hdr_metadata) { + if (!surface->hdr_metadata) + surface->hdr_metadata = + zalloc(sizeof(struct weston_hdr_metadata)); + memcpy(surface->hdr_metadata, + surface->pending.hdr_metadata, + sizeof(struct weston_hdr_metadata)); + } else if (surface->hdr_metadata) { + free(surface->hdr_metadata); + surface->hdr_metadata = NULL; + } + wl_signal_emit(&surface->commit_signal, surface); } diff --git a/libweston/hdr_metadata.c b/libweston/hdr_metadata.c new file mode 100644 index 000000000..f3bd39afc --- /dev/null +++ b/libweston/hdr_metadata.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2018 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include + +#include +#include +#include "hdr-metadata-unstable-v1-server-protocol.h" + +#define STATIC_METADATA(x) data->metadata.static_metadata.x + +static void +hdr_surface_set_metadata(struct wl_client *client, + struct wl_resource *surface_resource, + wl_fixed_t display_primary_r_x, + wl_fixed_t display_primary_r_y, + wl_fixed_t display_primary_g_x, + wl_fixed_t display_primary_g_y, + wl_fixed_t display_primary_b_x, + wl_fixed_t display_primary_b_y, + wl_fixed_t white_point_x, + wl_fixed_t white_point_y, + wl_fixed_t max_luminance, + wl_fixed_t min_luminance, + uint32_t max_cll, + uint32_t max_fall) +{ + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + struct weston_hdr_metadata *data = surface->pending.hdr_metadata; + data->metadata_type = HDR_METADATA_TYPE1; + STATIC_METADATA(primaries.r.x) = wl_fixed_to_double(display_primary_r_x); + STATIC_METADATA(primaries.r.y) = wl_fixed_to_double(display_primary_r_y); + STATIC_METADATA(primaries.g.x) = wl_fixed_to_double(display_primary_g_x); + STATIC_METADATA(primaries.g.y) = wl_fixed_to_double(display_primary_g_y); + STATIC_METADATA(primaries.b.x) = wl_fixed_to_double(display_primary_b_x); + STATIC_METADATA(primaries.b.y) = wl_fixed_to_double(display_primary_b_y); + STATIC_METADATA(primaries.white_point.x) = wl_fixed_to_double(white_point_x); + STATIC_METADATA(primaries.white_point.y) = wl_fixed_to_double(white_point_y); + STATIC_METADATA(max_luminance) = wl_fixed_to_double(max_luminance); + STATIC_METADATA(min_luminance) = wl_fixed_to_double(min_luminance); + STATIC_METADATA(max_cll) = max_cll; + STATIC_METADATA(max_fall) = max_fall; +} + +static void +hdr_surface_set_eotf(struct wl_client *client, + struct wl_resource *surface_resource, + uint32_t eotf) +{ + enum hdr_metadata_eotf internal_eotf = WESTON_EOTF_TRADITIONAL_GAMMA_SDR; + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + struct weston_hdr_metadata *data = surface->pending.hdr_metadata; + + + switch (eotf) { + case ZWP_HDR_SURFACE_V1_EOTF_ST_2084_PQ: + internal_eotf = WESTON_EOTF_ST2084; + break; + case ZWP_HDR_SURFACE_V1_EOTF_HLG: + internal_eotf = WESTON_EOTF_HLG; + break; + } + + data->metadata_type = HDR_METADATA_TYPE1; + STATIC_METADATA(eotf) = internal_eotf; +} + +static void +hdr_surface_destroy(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static const struct zwp_hdr_surface_v1_interface +zwp_hdr_surface_implementation = { + .destroy = hdr_surface_destroy, + .set = hdr_surface_set_metadata, + .set_eotf = hdr_surface_set_eotf, +}; + + +static void +hdr_metadata_destroy_request(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +destroy_hdr_surface(struct wl_resource *resource) +{ + struct weston_surface *surface = + wl_resource_get_user_data(resource); + + if (!surface) + return; + + surface->hdr_surface_resource = NULL; + if (surface->pending.hdr_metadata) + free(surface->pending.hdr_metadata); + surface->pending.hdr_metadata = NULL; +} + +static void +hdr_metadata_get_hdr_surface(struct wl_client *client, + struct wl_resource *hdr_metadata, + uint32_t id, + struct wl_resource *surface_resource) +{ + int version = wl_resource_get_version(hdr_metadata); + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + struct wl_resource *resource; + + if (surface->hdr_surface_resource) { + wl_resource_post_error(hdr_metadata, + ZWP_HDR_METADATA_V1_ERROR_HDR_SURFACE_EXISTS, + "a hdr surface for that surface already exists"); + return; + } + + resource = wl_resource_create(client, &zwp_hdr_surface_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &zwp_hdr_surface_implementation, + surface, destroy_hdr_surface); + + surface->hdr_surface_resource = resource; + surface->pending.hdr_metadata = + zalloc(sizeof(struct weston_hdr_metadata)); + + if (!surface->pending.hdr_metadata) { + wl_client_post_no_memory(client); + return; + } +} + +static const struct zwp_hdr_metadata_v1_interface +zwp_hdr_metadata_implementation = { + .destroy = hdr_metadata_destroy_request, + .get_hdr_surface = hdr_metadata_get_hdr_surface, +}; + +static void +bind_hdr_metadata(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *resource; + resource = wl_resource_create(client, &zwp_hdr_metadata_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, + &zwp_hdr_metadata_implementation, + NULL, NULL); +} + +WL_EXPORT int +weston_hdr_metadata_setup(struct weston_compositor *compositor) +{ + if (!wl_global_create(compositor->wl_display, + &zwp_hdr_metadata_v1_interface, 1, + compositor, bind_hdr_metadata)) + return -1; + + return 0; +} diff --git a/libweston/meson.build b/libweston/meson.build index 3dd1d67c7..e00c93f84 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -27,6 +27,7 @@ srcs_libweston = [ 'touch-calibration.c', 'weston-debug.c', 'zoom.c', + 'hdr_metadata.c', '../shared/matrix.c', '../shared/colorspace.c', linux_dmabuf_unstable_v1_protocol_c, @@ -55,6 +56,8 @@ srcs_libweston = [ viewporter_server_protocol_h, weston_debug_protocol_c, weston_debug_server_protocol_h, + hdr_metadata_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_server_protocol_h, ] if get_option('renderer-gl') diff --git a/protocol/meson.build b/protocol/meson.build index 34026ff92..e2691839b 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -37,6 +37,7 @@ generated_protocols = [ [ 'xdg-output', 'v1' ], [ 'xdg-shell', 'v6' ], [ 'xdg-shell', 'stable' ], + [ 'hdr-metadata', 'v1' ], ] foreach proto: generated_protocols -- GitLab From f33bddae5744b6c57bfb030c0223d6726092f57e Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 24 Dec 2018 17:41:40 +0530 Subject: [PATCH 06/29] [DO NOT MERGE] Implement the colorspace-unstable-v1 protocol This is just a placeholder to get data from clients to test our HDR stack. The actual protocol is being discussed here: https://patchwork.freedesktop.org/series/57657/ v2: Use the common color primary definitions (Pekka) Signed-off-by: Harish Krupo --- include/libweston/libweston.h | 6 ++ libweston/colorspace.c | 101 ++++++++++++++++++++++++++++++++++ libweston/compositor-drm.c | 4 ++ libweston/compositor.c | 5 +- libweston/meson.build | 3 + protocol/meson.build | 1 + 6 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 libweston/colorspace.c diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 53dc15c83..087c284b4 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -46,6 +46,7 @@ extern "C" { #include #include #include +#include struct weston_geometry { int32_t x, y; @@ -1391,6 +1392,8 @@ struct weston_surface_state { /* zwp_surface_synchronization_v1.get_release */ struct weston_buffer_release_reference buffer_release_ref; struct weston_hdr_metadata *hdr_metadata; + + uint32_t colorspace; }; struct weston_surface_activation_data { @@ -1522,6 +1525,7 @@ struct weston_surface { struct weston_buffer_release_reference buffer_release_ref; struct wl_resource *hdr_surface_resource; struct weston_hdr_metadata *hdr_metadata; + uint32_t colorspace; }; struct weston_subsurface { @@ -2393,6 +2397,8 @@ void weston_buffer_send_server_error(struct weston_buffer *buffer, const char *msg); int weston_hdr_metadata_setup(struct weston_compositor *compositor); +int weston_colorspace_setup(struct weston_compositor *compositor); + #ifdef __cplusplus } diff --git a/libweston/colorspace.c b/libweston/colorspace.c new file mode 100644 index 000000000..00d20058e --- /dev/null +++ b/libweston/colorspace.c @@ -0,0 +1,101 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include + +#include + +#include "colorspace-unstable-v1-server-protocol.h" + +static void +colorspace_destroy_request(struct wl_client *client, + struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +colorspace_set_request(struct wl_client *client, + struct wl_resource *resource, + struct wl_resource *surface_resource, + uint32_t chromacities) +{ + static uint32_t colorspace_names[] = { + [ZWP_COLORSPACE_V1_CHROMACITIES_UNDEFINED] = WESTON_CS_UNDEFINED, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT470M] = WESTON_CS_BT470M, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT470BG] = WESTON_CS_BT470BG, + [ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M] = WESTON_CS_SMPTE170M, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT709] = WESTON_CS_BT709, + [ZWP_COLORSPACE_V1_CHROMACITIES_BT2020] = WESTON_CS_BT2020, + [ZWP_COLORSPACE_V1_CHROMACITIES_ADOBERGB] = WESTON_CS_ADOBERGB, + [ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3] = WESTON_CS_DCI_P3, + [ZWP_COLORSPACE_V1_CHROMACITIES_PROPHOTORGB] = WESTON_CS_PROPHOTORGB, + [ZWP_COLORSPACE_V1_CHROMACITIES_CIERGB] = WESTON_CS_CIERGB, + [ZWP_COLORSPACE_V1_CHROMACITIES_AP0] = WESTON_CS_AP0, + [ZWP_COLORSPACE_V1_CHROMACITIES_AP1] = WESTON_CS_AP1, + }; + + struct weston_surface *surface = + wl_resource_get_user_data(surface_resource); + + surface->pending.colorspace = colorspace_names[chromacities]; +} + +static const struct zwp_colorspace_v1_interface +zwp_colorspace_implementation = { + .destroy = colorspace_destroy_request, + .set = colorspace_set_request, +}; + +static void +bind_colorspace(struct wl_client *client, + void *data, uint32_t version, uint32_t id) +{ + struct weston_compositor *compositor = data; + struct wl_resource *resource; + + resource = wl_resource_create(client, &zwp_colorspace_v1_interface, + version, id); + if (resource == NULL) { + wl_client_post_no_memory(client); + return; + } + + wl_resource_set_implementation(resource, &zwp_colorspace_implementation, + compositor, NULL); +} + +WL_EXPORT int +weston_colorspace_setup(struct weston_compositor *compositor) +{ + if (!wl_global_create(compositor->wl_display, + &zwp_colorspace_v1_interface, 1, + compositor, bind_colorspace)) + return -1; + + return 0; +} diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 26012824d..7517f2fc2 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -7622,6 +7622,10 @@ drm_backend_create(struct weston_compositor *compositor, weston_log("Error: initializing hdr metadata " "support failed.\n"); + if (weston_colorspace_setup(compositor) < 0) + weston_log("Error: initializing colorspace " + "support failed.\n"); + ret = weston_plugin_api_register(compositor, WESTON_DRM_OUTPUT_API_NAME, &api, sizeof(api)); diff --git a/libweston/compositor.c b/libweston/compositor.c index dcdb65c6a..6c3acb584 100644 --- a/libweston/compositor.c +++ b/libweston/compositor.c @@ -452,8 +452,8 @@ weston_surface_state_init(struct weston_surface_state *state) state->buffer_viewport.buffer.src_width = wl_fixed_from_int(-1); state->buffer_viewport.surface.width = -1; state->buffer_viewport.changed = 0; - state->acquire_fence_fd = -1; + state->colorspace = WESTON_CS_BT709; } static void @@ -531,6 +531,7 @@ weston_surface_create(struct weston_compositor *compositor) weston_matrix_init(&surface->buffer_to_surface_matrix); weston_matrix_init(&surface->surface_to_buffer_matrix); + surface->colorspace = WESTON_CS_BT709; wl_list_init(&surface->pointer_constraints); @@ -3385,6 +3386,8 @@ weston_surface_commit_state(struct weston_surface *surface, surface->hdr_metadata = NULL; } + surface->colorspace = state->colorspace; + wl_signal_emit(&surface->commit_signal, surface); } diff --git a/libweston/meson.build b/libweston/meson.build index e00c93f84..fa353ad1c 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -28,6 +28,7 @@ srcs_libweston = [ 'weston-debug.c', 'zoom.c', 'hdr_metadata.c', + 'colorspace.c', '../shared/matrix.c', '../shared/colorspace.c', linux_dmabuf_unstable_v1_protocol_c, @@ -58,6 +59,8 @@ srcs_libweston = [ weston_debug_server_protocol_h, hdr_metadata_unstable_v1_protocol_c, hdr_metadata_unstable_v1_server_protocol_h, + colorspace_unstable_v1_protocol_c, + colorspace_unstable_v1_server_protocol_h, ] if get_option('renderer-gl') diff --git a/protocol/meson.build b/protocol/meson.build index e2691839b..2654ce290 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -38,6 +38,7 @@ generated_protocols = [ [ 'xdg-shell', 'v6' ], [ 'xdg-shell', 'stable' ], [ 'hdr-metadata', 'v1' ], + [ 'colorspace', 'v1' ], ] foreach proto: generated_protocols -- GitLab From 30db870b21119fcd29a5d12bb6acd3b04c503e23 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 24 Dec 2018 17:54:26 +0530 Subject: [PATCH 07/29] clients: Add simple-hdr-video client The client uses ffmpeg to decode videos and to extract the colorspace information and HDR metadata. This metadata and colorspace information is sent using the colorspace-unstable-v1-protocol and hdr-metadata-v1-protocol to the compositor along with the buffer. The client supports YUV420 and YUV420_10 bit formats. It converts the YUV420_10 bit format to P010 (interleaved) format. P010 format support was introduced in mesa from the following commit: https://gitlab.freedesktop.org/mesa/mesa/commit/2a2e69f975bd791d64a88553d5b1d5eda7e8abdf Signed-off-by: Harish Krupo --- clients/meson.build | 51 ++ clients/simple-hdr-video.c | 1262 ++++++++++++++++++++++++++++++++++++ meson_options.txt | 7 + 3 files changed, 1320 insertions(+) create mode 100644 clients/simple-hdr-video.c diff --git a/clients/meson.build b/clients/meson.build index 3d36efe48..cf220072b 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -338,6 +338,57 @@ if simple_dmabuf_drm_deps.length() > 0 ) endif +simple_hdr_video_opts = get_option('simple-hdr-video') +simple_hdr_video_deps = [] +foreach driver : [ 'intel'] + if simple_hdr_video_opts.contains(driver) + required = true + enabled = true + elif simple_hdr_video_opts.contains('auto') + required = get_option('auto_features').enabled() + enabled = not get_option('auto_features').disabled() + else + enabled = false + endif + + if enabled + dep = dependency('libdrm_' + driver, required: false) + if dep.found() + simple_hdr_video_deps += dep + config_h.set('HAVE_LIBDRM_' + driver.to_upper(), 1) + elif required + error('simple-hdr-video is configured to use @0@ but it was not found. Or, you can remove @1@ from \'-Dsimple-hdr-video\' list.'.format('libdrm_' + driver, driver)) + endif + + simple_hdr_video_deps += dependency('libavutil') + simple_hdr_video_deps += dependency('libavcodec') + simple_hdr_video_deps += dependency('libavformat') + endif +endforeach + +if simple_hdr_video_deps.length() > 0 + executable( + 'weston-simple-hdr-video', + 'simple-hdr-video.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_client_protocol_h, + colorspace_unstable_v1_protocol_c, + colorspace_unstable_v1_client_protocol_h, + include_directories: include_directories('..'), + dependencies: [ + dep_wayland_client, + dep_libdrm, + simple_hdr_video_deps, + dep_toytoolkit + ], + install: true + ) +endif + if get_option('shell-desktop') exe_keyboard = executable( 'weston-keyboard', diff --git a/clients/simple-hdr-video.c b/clients/simple-hdr-video.c new file mode 100644 index 000000000..bf39cf725 --- /dev/null +++ b/clients/simple-hdr-video.c @@ -0,0 +1,1262 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef HAVE_LIBDRM_INTEL +#include +#include +#endif + +#ifdef HAVE_PANGO +#include +#endif + +#include "colorspace-unstable-v1-client-protocol.h" +#include "hdr-metadata-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" + +#include "shared/os-compatibility.h" +#include "shared/helpers.h" +#include "shared/platform.h" +#include "shared/xalloc.h" +#include +#include "window.h" + +#define DBG(fmt, ...) \ + fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__) + +#define NUM_BUFFERS 3 + +#ifndef DRM_FORMAT_MOD_LINEAR +#define DRM_FORMAT_MOD_LINEAR 0 +#endif + +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + +#ifndef DRM_FORMAT_P012 +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane, 12 bit per channel */ +#endif + +#ifndef DRM_FORMAT_P016 +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane, 16 bit per channel */ +#endif + +static int32_t option_help; +static int32_t option_fullscreen; +static int32_t option_subtitle; + +static const struct weston_option options[] = { + { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen }, + { WESTON_OPTION_BOOLEAN, "subtitle", 's', &option_subtitle }, + { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help }, +}; + +static const char help_text[] = +"Usage: %s [options] FILENAME\n" +"\n" +" -f, --fullscreen\t\tRun in fullscreen mode\n" +" -s, --subtitle\t\tShow subtiles\n" +" -h, --help\t\tShow this help text\n" +"\n"; + +struct app; +struct buffer; + +struct drm_device { + int fd; + char *name; + + int (*alloc_bo)(struct buffer *buf); + void (*free_bo)(struct buffer *buf); + int (*export_bo_to_prime)(struct buffer *buf); + int (*map_bo)(struct buffer *buf); + void (*unmap_bo)(struct buffer *buf); + void (*device_destroy)(struct buffer *buf); +}; + +struct buffer { + struct wl_buffer *buffer; + int busy; + + struct drm_device *dev; + int drm_fd; + +#ifdef HAVE_LIBDRM_INTEL + drm_intel_bufmgr *bufmgr; + drm_intel_bo *intel_bo; +#endif /* HAVE_LIBDRM_INTEL */ + + uint32_t gem_handle; + int dmabuf_fd; + uint8_t *mmap; + + int width; + int height; + int bpp; + unsigned long stride; + int format; + AVFrame *prev_frame; +}; + +struct subtitle { + + struct wl_surface *wl_surface; + int width; + int height; + + struct widget *widget; + uint32_t time; + struct wl_callback *frame_cb; + struct app *app; + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct video { + AVFormatContext *fmt_ctx; + AVCodecParserContext *parser; + AVCodecContext *codec; + AVPacket *pkt; + int stream_index; + + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct app { + struct display *display; + struct window *window; + struct widget *widget; + struct video video; + + struct subtitle *subtitle; + + struct zwp_colorspace_v1 *colorspace; + struct zwp_hdr_metadata_v1 *hdr_metadata; + + struct zwp_hdr_surface_v1 *hdr_surface; + struct zwp_linux_dmabuf_v1 *dmabuf; +}; + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format); + +static void drm_shutdown(struct buffer *my_buf); + +static void +destroy_dmabuf_buffer(struct buffer *buffer) +{ + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + close(buffer->dmabuf_fd); + buffer->dev->free_bo(buffer); + drm_shutdown(buffer); + } +} + +static void +subtitle_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct subtitle *sub = data; + struct app *app = sub->app; + struct rectangle allocation; + struct wl_surface *surface; + uint32_t format; + int i; + + widget_get_allocation(sub->widget, &allocation); + + surface = widget_get_wl_surface(widget); + + //clear surface's buffer + wl_surface_attach(surface, NULL, 0, 0); + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + format = DRM_FORMAT_ARGB8888; + + for (i = 0; i < NUM_BUFFERS; i++) { + create_dmabuf_buffer(app, &sub->buffers[i], + allocation.width, + allocation.height, + format); + + } + +} + +static struct buffer * +subtitle_next_buffer(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!sub->buffers[i].busy) + return &sub->buffers[i]; + + return NULL; +} + +#ifdef HAVE_PANGO +static PangoLayout * +create_layout(cairo_t *cr, const char *title) +{ + PangoLayout *layout; + PangoFontDescription *desc; + + layout = pango_cairo_create_layout(cr); + pango_layout_set_text(layout, title, -1); + desc = pango_font_description_from_string("Sans Bold 19"); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_auto_dir (layout, FALSE); + pango_layout_set_single_paragraph_mode (layout, TRUE); + pango_layout_set_width (layout, -1); + + return layout; +} +#endif + +static void +fill_subtitle(struct buffer *buffer) +{ + cairo_surface_t *surface; + cairo_t* cr; + char *title = "Sample subtitle string"; + PangoLayout *title_layout; + + assert(buffer->mmap); + + surface = cairo_image_surface_create_for_data(buffer->mmap, + CAIRO_FORMAT_ARGB32, + buffer->width, + buffer->height, + buffer->stride); + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); +#ifdef HAVE_PANGO + cairo_set_source_rgba(cr, 0, 0, 0, 0); +#else + cairo_set_source_rgba(cr, 1, 1, 1, 1); +#endif + + cairo_paint(cr); + +#ifdef HAVE_PANGO + /* cairo_set_operator(cr, CAIRO_OPERATOR_OVER); */ + title_layout = create_layout(cr, title); + cairo_move_to(cr, 0, 0); + cairo_set_source_rgb(cr, 1, 1, 1); + pango_cairo_show_layout(cr, title_layout); +#endif + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static void +subtitle_redraw_handler(struct widget *widget, void *data) +{ + struct subtitle *sub = data; + struct rectangle allocation; + struct buffer *buffer; + struct wl_surface *surface; + + widget_get_allocation(sub->widget, &allocation); + buffer = subtitle_next_buffer(sub); + + if (!buffer->dev->map_bo(buffer)) { + fprintf(stderr, "map_bo failed\n"); + return; + } + + fill_subtitle(buffer); + + buffer->dev->unmap_bo(buffer); + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, buffer->buffer, 0, 0); + wl_surface_damage(surface, 0, 0, allocation.width, allocation.height); + wl_surface_commit(surface); + buffer->busy = 1; + + widget_schedule_redraw(sub->widget); +} + +static struct subtitle * +subtitle_create(struct app *app) +{ + struct subtitle *sub; + + sub = xzalloc(sizeof *sub); + sub->app = app; + + sub->widget = window_add_subsurface(app->window, sub, + SUBSURFACE_SYNCHRONIZED); + + widget_set_use_cairo(sub->widget, 0); + widget_set_resize_handler(sub->widget, subtitle_resize_handler); + widget_set_redraw_handler(sub->widget, subtitle_redraw_handler); + + return sub; +} + +static void +subtitle_destroy(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + widget_destroy(sub->widget); + free(sub); +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct buffer *mybuf = data; + mybuf->busy = 0; + if (mybuf->prev_frame) + av_frame_free(&mybuf->prev_frame); + + mybuf->prev_frame = NULL; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static uint32_t av_format_to_drm_format(int format) +{ + switch (format) { + case AV_PIX_FMT_YUV420P: + return DRM_FORMAT_YUV420; + case AV_PIX_FMT_YUV420P10BE: + case AV_PIX_FMT_YUV420P10LE: + return DRM_FORMAT_P010; + case AV_PIX_FMT_YUV420P12BE: + case AV_PIX_FMT_YUV420P12LE: + return DRM_FORMAT_P012; + case AV_PIX_FMT_YUV420P16BE: + case AV_PIX_FMT_YUV420P16LE: + return DRM_FORMAT_P016; + default: + return -1; + } +} + +static inline void +ensure_hdr_surface(struct app *app) +{ + struct window *window = app->window; + struct wl_surface *surface; + + assert(app->hdr_metadata); + if (app->hdr_surface) + return; + + surface = window_get_wl_surface(window); + app->hdr_surface = zwp_hdr_metadata_v1_get_hdr_surface(app->hdr_metadata, + surface); +} + +static inline void +destroy_hdr_surface(struct app *app) +{ + if (app->hdr_surface) { + zwp_hdr_surface_v1_destroy(app->hdr_surface); + app->hdr_surface = NULL; + } +} + +static bool +decode(struct video *s, AVFrame *frame) +{ + int r; + + if (s->pkt->size == 0) + return false; + + if (s->pkt->stream_index != s->stream_index) + return false; + + r = avcodec_send_packet(s->codec, s->pkt); + if (r < 0) + return false; + + r = avcodec_receive_frame(s->codec, frame); + if (r < 0) + return false; + + return true; +} + +static AVFrame * +demux_and_decode(struct video *s) +{ + AVFrame *frame; + bool ret; + + frame = av_frame_alloc(); + if (!frame) + return NULL; + + for (;;) { + int r; + + r = av_read_frame(s->fmt_ctx, s->pkt); + if (r < 0) + break; + + ret = decode(s, frame); + + av_packet_unref(s->pkt); + + if (ret) + break; + } + + if (!ret) + av_frame_free(&frame); + + return frame; +} + +static struct buffer * +video_next_buffer(struct video *s) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!s->buffers[i].busy) + return &s->buffers[i]; + + return NULL; +} + +static void +copy_420p10_to_p010(struct buffer *buffer, AVFrame *frame) +{ + int height, linesize; + uint16_t *yplane, *dsty, *uplane, *vplane, *dstuv; + int size; + + // copy plane 0 + height = buffer->height; + yplane = (uint16_t *) frame->data[0]; + dsty = (uint16_t *) buffer->mmap; + linesize = frame->linesize[0]; + + size = (linesize * height) / 2; + // bitshift and copy y plane + for (int i = 0; i < size; i++) { + dsty[i] = yplane[i] << 6; + } + + // copy plane 1 and 2 alternatingly from source + height = buffer->height / 2; + uplane = (uint16_t *) frame->data[1]; + vplane = (uint16_t *) frame->data[2]; + dstuv = (uint16_t *) (buffer->mmap + buffer->stride * buffer->height); + + // both uplane and vplane would have same linesize + linesize = frame->linesize[1]; + + size = (linesize * height) / 2; + + // bit shift and copy u, v alternatingly + for (int i = 0; i < size; i++) { + dstuv[2 * i] = uplane[i] << 6; + dstuv[(2 * i) + 1] = vplane[i] << 6; + } +} + +static void +fill_buffer(struct buffer *buffer, AVFrame *frame) { + int linesize; + int height; + int plane_heights[4] = {0}; + int offsets[4] = {0}; + int n_planes = 0; + uint8_t *src, *dst; + int i; + assert(buffer->mmap); + + switch (buffer->format) { + case DRM_FORMAT_YUV420: + plane_heights[0] = buffer->height; + plane_heights[1] = buffer->height / 2; + plane_heights[2] = buffer->height / 2; + offsets[0] = 0; + offsets[1] = buffer->height; + offsets[2] = buffer->height * 3 / 2; + n_planes = 3; + break; + case DRM_FORMAT_P010: + copy_420p10_to_p010(buffer, frame); + return; + } + + for (i = 0; i < n_planes; i++) { + height = plane_heights[i]; + linesize = frame->linesize[i]; + + src = frame->data[i]; + dst = buffer->mmap + frame->linesize[0] * offsets[i]; + + for (;height > 0; height--) { + memcpy(dst, src, linesize); + dst += linesize; + src += linesize; + } + } + +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct app *app = data; + struct video *video = &app->video; + struct buffer *buffer; + struct wl_buffer *wlbuffer; + struct wl_surface *surface; + AVFrame *frame; + AVFrameSideData *sd; + AVMasteringDisplayMetadata *hdr_metadata; + AVContentLightMetadata *ll_metadata; + int max_cll = -1, max_fall = -1; + wl_fixed_t rx, ry, gx, gy, bx, by, wx, wy, max_luma, min_luma; + + frame = demux_and_decode(&app->video); + if (!frame) { + fprintf(stderr, "no more frames?\n"); + return; + } + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (sd) { + ll_metadata = (AVContentLightMetadata *)sd->data; + max_cll = ll_metadata->MaxCLL; + max_fall = ll_metadata->MaxFALL; + } + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (sd) { + hdr_metadata = (AVMasteringDisplayMetadata *)sd->data; + if (hdr_metadata->has_luminance && hdr_metadata->has_primaries) { + ensure_hdr_surface(app); + rx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][0])); + ry = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][1])); + gx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][0])); + gy = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][1])); + bx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][0])); + by = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][1])); + wx = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[0])); + wy = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[1])); + max_luma = wl_fixed_from_double(av_q2d(hdr_metadata->max_luminance)); + min_luma = wl_fixed_from_double(av_q2d(hdr_metadata->min_luminance)); + zwp_hdr_surface_v1_set( + app->hdr_surface, + rx, + ry, + gx, + gy, + bx, + by, + wx, + wy, + max_luma, + min_luma, + max_cll == -1 ? 0 : max_cll, + max_fall == -1 ? 0 : max_fall); + + zwp_hdr_surface_v1_set_eotf( + app->hdr_surface, + ZWP_HDR_SURFACE_V1_EOTF_ST_2084_PQ); + } + } else { + // No metadata for this frame. Destroy the created hdr surface. + destroy_hdr_surface(app); + } + + buffer = video_next_buffer(video); + + // If no free buffers available schedule redraw and return; + if(!buffer) { + widget_schedule_redraw(widget); + return; + } + + if (!buffer->dev->map_bo(buffer)) { + fprintf(stderr, "map_bo failed\n"); + return; + } + + fill_buffer(buffer, frame); + + buffer->dev->unmap_bo(buffer); + wlbuffer = buffer->buffer; + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, wlbuffer, 0, 0); + wl_surface_damage(surface, 0, 0, frame->width, frame->height); + wl_surface_commit(surface); + widget_schedule_redraw(widget); + buffer->busy = 1; + buffer->prev_frame = frame; +} + +/* + * +---------------------------+ + * | | | + * | | | + * | |vm Video | + * | | | + * | | | + * |___+-------------------+ | + * | hm| Subtitle | | + * | +-------------------+ | + * | | + * +---------------------------+ + * + * hm : horizontal margin + * vm : vertical margin + */ + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct app *app = data; + struct rectangle area; + + // margin is in percentage + int vm = 85, hm = 40; + int x, y, w, h; + int mhorizontal, mvertical; + + if (app->subtitle) { + widget_get_allocation(widget, &area); + + mhorizontal = area.width * hm / 100; + mvertical = area.height * vm / 100; + + x = area.x + mhorizontal; + y = area.y + mvertical; + w = area.width * 2 / 10; // 20% of total width + h = area.height / 20; // 5% of total height + + widget_set_allocation(app->subtitle->widget, + x, y, w, h); + } + +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct app *app = data; + + window_schedule_redraw(app->window); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, + enum wl_keyboard_key_state state, void *data) +{ + struct app *app = data; + struct rectangle winrect; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (sym) { + case XKB_KEY_Up: + window_get_allocation(window, &winrect); + winrect.height -= 100; + if (winrect.height < 150) + winrect.height = 150; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Down: + window_get_allocation(window, &winrect); + winrect.height += 100; + if (winrect.height > 600) + winrect.height = 600; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Escape: + display_exit(app->display); + break; + } +} + +static void video_close(struct video *s) +{ + av_parser_close(s->parser); + avcodec_free_context(&s->codec); + av_packet_free(&s->pkt); +} + +static const enum zwp_colorspace_v1_chromacities chromacities[] = { + [AVCOL_PRI_BT709] = ZWP_COLORSPACE_V1_CHROMACITIES_BT709, + [AVCOL_PRI_BT470M] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470M, + [AVCOL_PRI_BT470BG] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470BG, + [AVCOL_PRI_SMPTE170M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE240M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE431] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE432] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE428] = ZWP_COLORSPACE_V1_CHROMACITIES_CIEXYZ, + [AVCOL_PRI_BT2020] = ZWP_COLORSPACE_V1_CHROMACITIES_BT2020, +}; + +static enum zwp_colorspace_v1_chromacities +video_chromacities(struct video *s) +{ + if (s->codec->color_primaries >= ARRAY_LENGTH(chromacities)) + return ZWP_COLORSPACE_V1_CHROMACITIES_UNDEFINED; + + return chromacities[s->codec->color_primaries]; +} + +static bool video_open(struct app *app, + struct video *s, + const char *filename) +{ + AVCodec *codec = NULL; + AVStream *stream; + int r; + char buf[4096] = {}; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + + r = avformat_open_input(&s->fmt_ctx, filename, NULL, NULL); + if (r < 0) + return false; + + r = avformat_find_stream_info(s->fmt_ctx, NULL); + if (r < 0) + return false; + + r = av_find_best_stream(s->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); + if (r < 0) + return false; + + stream = s->fmt_ctx->streams[r]; + s->stream_index = r; + + s->codec = avcodec_alloc_context3(codec); + if (!s->codec) + return false; + + s->codec->opaque = s; + + r = avcodec_parameters_to_context(s->codec, stream->codecpar); + if (r < 0) + return false; + + r = avcodec_open2(s->codec, codec, NULL); + if (r < 0) + return false; + + s->parser = av_parser_init(codec->id); + if (!s->parser) + return false; + + avcodec_string(buf, sizeof(buf), s->codec, false); + buf[sizeof(buf)-1] = '\0'; + puts(buf); + + s->pkt = av_packet_alloc(); + + return true; +} + +static void +dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) +{ +} + +static void +dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) +{ + /* XXX: deprecated */ +} + + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, + dmabuf_modifiers +}; + +static void +global_handler(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ + + struct app *app = data; + + if (strcmp(interface, "zwp_colorspace_v1") == 0) { + app->colorspace = + display_bind(display, id, + &zwp_colorspace_v1_interface, 1); + } else if (strcmp(interface, "zwp_hdr_metadata_v1") == 0) { + app->hdr_metadata = + display_bind(display, id, + &zwp_hdr_metadata_v1_interface, 1); + } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { + if (version < 3) + return; + app->dmabuf = + display_bind(display, id, + &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(app->dmabuf, + &dmabuf_listener, + app); + } + +} + +static void +global_handler_remove(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ +} + +#ifdef HAVE_LIBDRM_INTEL +static int +intel_alloc_bo(struct buffer *my_buf) +{ + /* XXX: try different tiling modes for testing FB modifiers. */ + uint32_t tiling = I915_TILING_NONE; + + assert(my_buf->bufmgr); + + my_buf->intel_bo = drm_intel_bo_alloc_tiled(my_buf->bufmgr, "test", + my_buf->width, my_buf->height, + (my_buf->bpp / 8), &tiling, + &my_buf->stride, 0); + + if (!my_buf->intel_bo) + return 0; + + if (tiling != I915_TILING_NONE) + return 0; + + return 1; +} + +static void +intel_free_bo(struct buffer *my_buf) +{ + drm_intel_bo_unreference(my_buf->intel_bo); +} + +static int +intel_map_bo(struct buffer *my_buf) +{ + if (drm_intel_gem_bo_map_gtt(my_buf->intel_bo) != 0) + return 0; + + my_buf->mmap = my_buf->intel_bo->virtual; + + return 1; +} + +static int +intel_bo_export_to_prime(struct buffer *buffer) +{ + return drm_intel_bo_gem_export_to_prime(buffer->intel_bo, &buffer->dmabuf_fd); +} + +static void +intel_unmap_bo(struct buffer *my_buf) +{ + drm_intel_gem_bo_unmap_gtt(my_buf->intel_bo); +} + +static void +intel_device_destroy(struct buffer *my_buf) +{ + drm_intel_bufmgr_destroy(my_buf->bufmgr); +} + +#endif /* HAVE_LIBDRM_INTEL */ + +static void +drm_device_destroy(struct buffer *buf) +{ + buf->dev->device_destroy(buf); + close(buf->drm_fd); +} + +static int +drm_device_init(struct buffer *buf) +{ + struct drm_device *dev = calloc(1, sizeof(struct drm_device)); + + drmVersionPtr version = drmGetVersion(buf->drm_fd); + + dev->fd = buf->drm_fd; + dev->name = strdup(version->name); + if (0) { + /* nothing */ + } +#ifdef HAVE_LIBDRM_INTEL + else if (!strcmp(dev->name, "i915")) { + buf->bufmgr = drm_intel_bufmgr_gem_init(buf->drm_fd, 32); + if (!buf->bufmgr) { + free(dev->name); + free(dev); + return 0; + } + dev->alloc_bo = intel_alloc_bo; + dev->free_bo = intel_free_bo; + dev->export_bo_to_prime = intel_bo_export_to_prime; + dev->map_bo = intel_map_bo; + dev->unmap_bo = intel_unmap_bo; + dev->device_destroy = intel_device_destroy; + } +#endif + else { + fprintf(stderr, "Error: drm device %s unsupported.\n", + dev->name); + free(dev->name); + free(dev); + return 0; + } + buf->dev = dev; + return 1; +} + +static int +drm_connect(struct buffer *my_buf) +{ + /* This won't work with card0 as we need to be authenticated; instead, + * boot with drm.rnodes=1 and use that. */ + my_buf->drm_fd = open("/dev/dri/renderD128", O_RDWR); + if (my_buf->drm_fd < 0) + return 0; + + return drm_device_init(my_buf); +} + +static void +drm_shutdown(struct buffer *my_buf) +{ + drm_device_destroy(my_buf); +} + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format) +{ + struct zwp_linux_buffer_params_v1 *params; + uint64_t modifier = 0; + uint32_t flags = 0; + struct drm_device *drm_dev; + + if (!drm_connect(buffer)) { + fprintf(stderr, "drm_connect failed\n"); + goto error; + } + + drm_dev = buffer->dev; + + buffer->width = width; + switch (format) { + case DRM_FORMAT_NV12: + /* adjust height for allocation of NV12 Y and UV planes */ + buffer->height = height * 3 / 2; + buffer->bpp = 8; + break; + case DRM_FORMAT_YUV420: + buffer->height = height * 2; + buffer->bpp = 8; + break; + case DRM_FORMAT_P010: + buffer->height = height * 3 / 2; + buffer->bpp = 16; + break; + default: + buffer->height = height; + buffer->bpp = 32; + } + buffer->format = format; + + if (!drm_dev->alloc_bo(buffer)) { + fprintf(stderr, "alloc_bo failed\n"); + goto error1; + } + + if (drm_dev->export_bo_to_prime(buffer) != 0) { + fprintf(stderr, "gem_export_to_prime failed\n"); + goto error2; + } + if (buffer->dmabuf_fd < 0) { + fprintf(stderr, "error: dmabuf_fd < 0\n"); + goto error2; + } + + /* We now have a dmabuf! For format XRGB8888, it should contain 2x2 + * tiles (i.e. each tile is 256x256) of misc colours, and be mappable, + * either as ARGB8888, or XRGB8888. For format NV12, it should contain + * the Y and UV components, and needs to be re-adjusted for passing the + * correct height to the compositor. + */ + buffer->height = height; + + params = zwp_linux_dmabuf_v1_create_params(app->dmabuf); + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 0, /* plane_idx */ + 0, /* offset */ + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + + switch (format) { + case DRM_FORMAT_NV12: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->width * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_YUV420: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + + /* add the third plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 2, + buffer->stride * buffer->height * 3 / 2, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_P010: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + default: + break; + } + + buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params, + buffer->width, + buffer->height, + format, + flags); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + return 0; + +error2: + drm_dev->free_bo(buffer); +error1: + drm_shutdown(buffer); +error: + return -1; +} + +static struct app * +video_create(struct display *display, const char *filename) +{ + struct app *app; + struct wl_surface *surface; + struct wl_display *wldisplay; + uint32_t i, width, height, format; + int ret; + struct buffer *buffer; + + app = xzalloc(sizeof *app); + + app->display = display; + display_set_user_data(app->display, app); + display_set_global_handler(display, global_handler); + display_set_global_handler_remove(display, global_handler_remove); + + // Ensure that we have received the DMABUF format and modifier support + wldisplay = display_get_display(display); + wl_display_roundtrip(wldisplay); + + app->window = window_create(app->display); + app->widget = window_add_widget(app->window, app); + window_set_title(app->window, "Wayland Simple HDR video"); + + window_set_key_handler(app->window, key_handler); + window_set_user_data(app->window, app); + window_set_keyboard_focus_handler(app->window, keyboard_focus_handler); + + widget_set_redraw_handler(app->widget, redraw_handler); + widget_set_resize_handler(app->widget, resize_handler); + + widget_set_use_cairo(app->widget, 0); + + if (!video_open(app, &app->video, filename)) + goto err; + + surface = window_get_wl_surface(app->window); + zwp_colorspace_v1_set(app->colorspace, + surface, + video_chromacities(&app->video)); + + if (option_subtitle) + app->subtitle = subtitle_create(app); + + width = app->video.codec->width; + height = app->video.codec->height; + format = av_format_to_drm_format(app->video.codec->pix_fmt); + + if (option_fullscreen) { + window_set_fullscreen(app->window, 1); + } else { + /* if not fullscreen, resize as per the video size */ + widget_schedule_resize(app->widget, width, height); + } + + for (i = 0; i < NUM_BUFFERS; i++) { + buffer = &app->video.buffers[i]; + ret = create_dmabuf_buffer(app, buffer, width, height, format); + + if (ret < 0) + goto err; + } + + return app; + +err: + free(app); + return NULL; +} + +static void +video_destroy(struct app *app) +{ + if (app->subtitle) + subtitle_destroy(app->subtitle); + + video_close(&app->video); + + widget_destroy(app->widget); + window_destroy(app->window); + free(app); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct app *app; + + parse_options(options, ARRAY_LENGTH(options), &argc, argv); + if (option_help) { + printf(help_text, argv[0]); + return 0; + } + + display = display_create(&argc, argv); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + if (!display_has_subcompositor(display)) { + fprintf(stderr, "compositor does not support " + "the subcompositor extension\n"); + return -1; + } + + app = video_create(display, argv[argc - 1]); + if (!app) { + fprintf(stderr, "Failed to initialize!"); + exit(EXIT_FAILURE); + } + + display_run(display); + + video_destroy(app); + display_destroy(display); + + return 0; +} diff --git a/meson_options.txt b/meson_options.txt index 0e1d18338..0a6557cb2 100644 --- a/meson_options.txt +++ b/meson_options.txt @@ -171,6 +171,13 @@ option( value: [ 'intel', 'freedreno', 'etnaviv' ], description: 'List of DRM drivers to be supported by weston-simple-dmabuf-drm' ) +option( + 'simple-hdr-video', + type: 'array', + choices: [ 'auto', 'intel' ], + value: [ 'intel' ], + description: 'List of DRM drivers to be supported by weston-simple-hdr-video' +) option( 'demo-clients', type: 'boolean', -- GitLab From a29321a11f8e4a072d196f02b9f921f74e2bfa99 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Tue, 18 Jun 2019 14:30:07 +0530 Subject: [PATCH 08/29] clients: Add simple-hdr-video-gbm client The client uses ffmpeg to decode videos and to extract the colorspace information and HDR metadata. This metadata and colorspace information is sent using the colorspace-unstable-v1-protocol and hdr-metadata-v1-protocol to the compositor along with the buffer. The client supports YUV420 and YUV420_10 bit formats. It converts the YUV420_10 bit format to P010 (interleaved) format. P010 format support was introduced in mesa from the following commit: https://gitlab.freedesktop.org/mesa/mesa/commit/2a2e69f975bd791d64a88553d5b1d5eda7e8abdf Signed-off-by: Harish Krupo --- clients/meson.build | 52 ++ clients/simple-hdr-video-gbm.c | 1160 ++++++++++++++++++++++++++++++++ 2 files changed, 1212 insertions(+) create mode 100644 clients/simple-hdr-video-gbm.c diff --git a/clients/meson.build b/clients/meson.build index cf220072b..7b0db6130 100644 --- a/clients/meson.build +++ b/clients/meson.build @@ -389,6 +389,58 @@ if simple_hdr_video_deps.length() > 0 ) endif +simple_hdr_video_gbm_opts = get_option('simple-hdr-video') +simple_hdr_video_gbm_deps = [] +foreach driver : [ 'intel'] + if simple_hdr_video_gbm_opts.contains(driver) + required = true + enabled = true + elif simple_hdr_video_gbm_opts.contains('auto') + required = get_option('auto_features').enabled() + enabled = not get_option('auto_features').disabled() + else + enabled = false + endif + + if enabled + dep = dependency('libdrm_' + driver, required: false) + if dep.found() + simple_hdr_video_gbm_deps += dep + config_h.set('HAVE_LIBDRM_' + driver.to_upper(), 1) + elif required + error('simple-hdr-video is configured to use @0@ but it was not found. Or, you can remove @1@ from \'-Dsimple-hdr-video\' list.'.format('libdrm_' + driver, driver)) + endif + + simple_hdr_video_gbm_deps += dependency('libavutil') + simple_hdr_video_gbm_deps += dependency('libavcodec') + simple_hdr_video_gbm_deps += dependency('libavformat') + simple_hdr_video_gbm_deps += dependency('gbm') + endif +endforeach + +if simple_hdr_video_gbm_deps.length() > 0 + executable( + 'weston-simple-hdr-video-gbm', + 'simple-hdr-video-gbm.c', + xdg_shell_client_protocol_h, + xdg_shell_protocol_c, + linux_dmabuf_unstable_v1_client_protocol_h, + linux_dmabuf_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_protocol_c, + hdr_metadata_unstable_v1_client_protocol_h, + colorspace_unstable_v1_protocol_c, + colorspace_unstable_v1_client_protocol_h, + include_directories: include_directories('..'), + dependencies: [ + dep_wayland_client, + dep_libdrm, + simple_hdr_video_gbm_deps, + dep_toytoolkit + ], + install: true + ) +endif + if get_option('shell-desktop') exe_keyboard = executable( 'weston-keyboard', diff --git a/clients/simple-hdr-video-gbm.c b/clients/simple-hdr-video-gbm.c new file mode 100644 index 000000000..bf9030092 --- /dev/null +++ b/clients/simple-hdr-video-gbm.c @@ -0,0 +1,1160 @@ +/* + * Copyright © 2019 Harish Krupo + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +#include + +#ifdef HAVE_PANGO +#include +#endif + +#include "colorspace-unstable-v1-client-protocol.h" +#include "hdr-metadata-unstable-v1-client-protocol.h" +#include "linux-dmabuf-unstable-v1-client-protocol.h" + +#include "shared/os-compatibility.h" +#include "shared/helpers.h" +#include "shared/platform.h" +#include "shared/xalloc.h" +#include +#include "window.h" + +#define DBG(fmt, ...) \ + fprintf(stderr, "%d:%s " fmt, __LINE__, __func__, ##__VA_ARGS__) + +#define NUM_BUFFERS 3 + +#ifndef DRM_FORMAT_MOD_LINEAR +#define DRM_FORMAT_MOD_LINEAR 0 +#endif + +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + +#ifndef DRM_FORMAT_P012 +#define DRM_FORMAT_P012 fourcc_code('P', '0', '1', '2') /* 2x2 subsampled Cr:Cb plane, 12 bit per channel */ +#endif + +#ifndef DRM_FORMAT_P016 +#define DRM_FORMAT_P016 fourcc_code('P', '0', '1', '6') /* 2x2 subsampled Cr:Cb plane, 16 bit per channel */ +#endif + +static int32_t option_help; +static int32_t option_fullscreen; +static int32_t option_subtitle; + +static const struct weston_option options[] = { + { WESTON_OPTION_BOOLEAN, "fullscreen", 'f', &option_fullscreen }, + { WESTON_OPTION_BOOLEAN, "subtitle", 's', &option_subtitle }, + { WESTON_OPTION_BOOLEAN, "help", 'h', &option_help }, +}; + +static const char help_text[] = +"Usage: %s [options] FILENAME\n" +"\n" +" -f, --fullscreen\t\tRun in fullscreen mode\n" +" -s, --subtitle\t\tShow subtiles\n" +" -h, --help\t\tShow this help text\n" +"\n"; + +struct app; +struct buffer; + +struct buffer { + struct wl_buffer *buffer; + int busy; + + int drm_fd; + uint32_t gem_handle; + int dmabuf_fd; + uint8_t *mmap; + + int width; + int height; + int bpp; + unsigned long stride; + int format; + AVFrame *prev_frame; + + struct gbm_device *gbm; + int cpp; + struct gbm_bo *bo; +}; + +struct subtitle { + + struct wl_surface *wl_surface; + int width; + int height; + + struct widget *widget; + uint32_t time; + struct wl_callback *frame_cb; + struct app *app; + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct video { + AVFormatContext *fmt_ctx; + AVCodecParserContext *parser; + AVCodecContext *codec; + AVPacket *pkt; + int stream_index; + + struct buffer buffers[NUM_BUFFERS]; + struct buffer *prev_buffer; +}; + +struct app { + struct display *display; + struct window *window; + struct widget *widget; + struct video video; + + struct subtitle *subtitle; + + struct zwp_colorspace_v1 *colorspace; + struct zwp_hdr_metadata_v1 *hdr_metadata; + + struct zwp_hdr_surface_v1 *hdr_surface; + struct zwp_linux_dmabuf_v1 *dmabuf; +}; + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format); + +static void +gbm_fini(struct buffer *my_buf); + +static void +destroy_dmabuf_buffer(struct buffer *buffer) +{ + if (buffer->buffer) { + wl_buffer_destroy(buffer->buffer); + close(buffer->dmabuf_fd); + gbm_fini(buffer); + } +} + +static void +subtitle_resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct subtitle *sub = data; + struct app *app = sub->app; + struct rectangle allocation; + struct wl_surface *surface; + uint32_t format; + int i; + + widget_get_allocation(sub->widget, &allocation); + + surface = widget_get_wl_surface(widget); + + // clear surface's buffer + wl_surface_attach(surface, NULL, 0, 0); + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + format = DRM_FORMAT_ARGB8888; + + for (i = 0; i < NUM_BUFFERS; i++) { + create_dmabuf_buffer(app, &sub->buffers[i], + allocation.width, + allocation.height, + format); + + } + +} + +static struct buffer * +subtitle_next_buffer(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!sub->buffers[i].busy) + return &sub->buffers[i]; + + return NULL; +} + +#ifdef HAVE_PANGO +static PangoLayout * +create_layout(cairo_t *cr, const char *title) +{ + PangoLayout *layout; + PangoFontDescription *desc; + + layout = pango_cairo_create_layout(cr); + pango_layout_set_text(layout, title, -1); + desc = pango_font_description_from_string("Sans Bold 19"); + pango_layout_set_font_description(layout, desc); + pango_font_description_free(desc); + pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); + pango_layout_set_alignment(layout, PANGO_ALIGN_LEFT); + pango_layout_set_auto_dir (layout, FALSE); + pango_layout_set_single_paragraph_mode (layout, TRUE); + pango_layout_set_width (layout, -1); + + return layout; +} +#endif + +static void +fill_subtitle(struct buffer *buffer) +{ + cairo_surface_t *surface; + cairo_t* cr; + char *title = "Sample subtitle string"; + PangoLayout *title_layout; + + assert(buffer->mmap); + + surface = cairo_image_surface_create_for_data(buffer->mmap, + CAIRO_FORMAT_ARGB32, + buffer->width, + buffer->height, + buffer->stride); + cr = cairo_create(surface); + cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE); +#ifdef HAVE_PANGO + cairo_set_source_rgba(cr, 0, 0, 0, 0); +#else + cairo_set_source_rgba(cr, 1, 1, 1, 1); +#endif + + cairo_paint(cr); + +#ifdef HAVE_PANGO + /* cairo_set_operator(cr, CAIRO_OPERATOR_OVER); */ + title_layout = create_layout(cr, title); + cairo_move_to(cr, 0, 0); + cairo_set_source_rgb(cr, 1, 1, 1); + pango_cairo_show_layout(cr, title_layout); +#endif + + cairo_destroy(cr); + cairo_surface_destroy(surface); +} + +static void +subtitle_redraw_handler(struct widget *widget, void *data) +{ + struct subtitle *sub = data; + struct rectangle allocation; + struct buffer *buffer; + struct wl_surface *surface; + void *map_data = NULL; + uint32_t dst_stride; + + widget_get_allocation(sub->widget, &allocation); + buffer = subtitle_next_buffer(sub); + + buffer->mmap = gbm_bo_map(buffer->bo, 0, 0, buffer->width, buffer->height, + GBM_BO_TRANSFER_WRITE, &dst_stride, &map_data); + if (!buffer->mmap) { + fprintf(stderr, "failed to map failed\n"); + return; + } + + fill_subtitle(buffer); + + gbm_bo_unmap(buffer->bo, map_data); + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, buffer->buffer, 0, 0); + wl_surface_damage(surface, 0, 0, allocation.width, allocation.height); + wl_surface_commit(surface); + buffer->busy = 1; + + widget_schedule_redraw(sub->widget); +} + +static struct subtitle * +subtitle_create(struct app *app) +{ + struct subtitle *sub; + + sub = xzalloc(sizeof *sub); + sub->app = app; + + sub->widget = window_add_subsurface(app->window, sub, + SUBSURFACE_SYNCHRONIZED); + + widget_set_use_cairo(sub->widget, 0); + widget_set_resize_handler(sub->widget, subtitle_resize_handler); + widget_set_redraw_handler(sub->widget, subtitle_redraw_handler); + + return sub; +} + +static void +subtitle_destroy(struct subtitle *sub) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) { + destroy_dmabuf_buffer(&sub->buffers[i]); + } + + widget_destroy(sub->widget); + free(sub); +} + +static void +buffer_release(void *data, struct wl_buffer *buffer) +{ + struct buffer *mybuf = data; + mybuf->busy = 0; + if (mybuf->prev_frame) + av_frame_free(&mybuf->prev_frame); + + mybuf->prev_frame = NULL; +} + +static const struct wl_buffer_listener buffer_listener = { + buffer_release +}; + +static uint32_t av_format_to_drm_format(int format) +{ + switch (format) { + case AV_PIX_FMT_YUV420P: + return DRM_FORMAT_YUV420; + case AV_PIX_FMT_YUV420P10BE: + case AV_PIX_FMT_YUV420P10LE: + return DRM_FORMAT_P010; + case AV_PIX_FMT_YUV420P12BE: + case AV_PIX_FMT_YUV420P12LE: + return DRM_FORMAT_P012; + case AV_PIX_FMT_YUV420P16BE: + case AV_PIX_FMT_YUV420P16LE: + return DRM_FORMAT_P016; + default: + return -1; + } +} + +static inline void +ensure_hdr_surface(struct app *app) +{ + struct window *window = app->window; + struct wl_surface *surface; + + assert(app->hdr_metadata); + if (app->hdr_surface) + return; + + surface = window_get_wl_surface(window); + app->hdr_surface = zwp_hdr_metadata_v1_get_hdr_surface(app->hdr_metadata, + surface); +} + +static inline void +destroy_hdr_surface(struct app *app) +{ + if (app->hdr_surface) { + zwp_hdr_surface_v1_destroy(app->hdr_surface); + app->hdr_surface = NULL; + } +} + +static bool +decode(struct video *s, AVFrame *frame) +{ + int r; + + if (s->pkt->size == 0) + return false; + + if (s->pkt->stream_index != s->stream_index) + return false; + + r = avcodec_send_packet(s->codec, s->pkt); + if (r < 0) + return false; + + r = avcodec_receive_frame(s->codec, frame); + if (r < 0) + return false; + + return true; +} + +static AVFrame * +demux_and_decode(struct video *s) +{ + AVFrame *frame; + bool ret; + + frame = av_frame_alloc(); + if (!frame) + return NULL; + + for (;;) { + int r; + + r = av_read_frame(s->fmt_ctx, s->pkt); + if (r < 0) + break; + + ret = decode(s, frame); + + av_packet_unref(s->pkt); + + if (ret) + break; + } + + if (!ret) + av_frame_free(&frame); + + return frame; +} + +static struct buffer * +video_next_buffer(struct video *s) +{ + int i; + + for (i = 0; i < NUM_BUFFERS; i++) + if (!s->buffers[i].busy) + return &s->buffers[i]; + + return NULL; +} + +static void +copy_420p10_to_p010(struct buffer *buffer, AVFrame *frame) +{ + int height, linesize; + uint16_t *yplane, *dsty, *uplane, *vplane, *dstuv; + int size; + + // copy plane 0 + height = buffer->height; + yplane = (uint16_t *) frame->data[0]; + dsty = (uint16_t *) buffer->mmap; + linesize = frame->linesize[0]; + + size = (linesize * height) / 2; + // bitshift and copy y plane + for (int i = 0; i < size; i++) { + dsty[i] = yplane[i] << 6; + } + + // copy plane 1 and 2 alternatingly from source + height = buffer->height / 2; + uplane = (uint16_t *) frame->data[1]; + vplane = (uint16_t *) frame->data[2]; + dstuv = (uint16_t *) (buffer->mmap + buffer->stride * buffer->height); + + // both uplane and vplane would have same linesize + linesize = frame->linesize[1]; + + size = (linesize * height) / 2; + + // bit shift and copy u, v alternatingly + for (int i = 0; i < size; i++) { + dstuv[2 * i] = uplane[i] << 6; + dstuv[(2 * i) + 1] = vplane[i] << 6; + } +} + +static int +fill_buffer(struct buffer *buffer, AVFrame *frame) { + int linesize; + int height; + int plane_heights[4] = {0}; + int offsets[4] = {0}; + int n_planes = 0; + uint8_t *src, *dst; + int i; + void *map_data = NULL; + uint32_t dst_stride; + + buffer->mmap = gbm_bo_map(buffer->bo, 0, 0, buffer->width, buffer->height, + GBM_BO_TRANSFER_WRITE, &dst_stride, &map_data); + if (!buffer->mmap) { + fprintf(stderr, "Unable to mmap buffer\n"); + return 0; + } + + switch (buffer->format) { + case DRM_FORMAT_YUV420: + plane_heights[0] = buffer->height; + plane_heights[1] = buffer->height / 2; + plane_heights[2] = buffer->height / 2; + offsets[0] = 0; + offsets[1] = buffer->height; + offsets[2] = buffer->height * 3 / 2; + n_planes = 3; + break; + case DRM_FORMAT_P010: + copy_420p10_to_p010(buffer, frame); + gbm_bo_unmap(buffer->bo, map_data); + return 1; + } + + for (i = 0; i < n_planes; i++) { + height = plane_heights[i]; + linesize = frame->linesize[i]; + + src = frame->data[i]; + dst = buffer->mmap + frame->linesize[0] * offsets[i]; + + for (;height > 0; height--) { + memcpy(dst, src, linesize); + dst += linesize; + src += linesize; + } + } + + gbm_bo_unmap(buffer->bo, map_data); + return 1; +} + +static void +redraw_handler(struct widget *widget, void *data) +{ + struct app *app = data; + struct video *video = &app->video; + struct buffer *buffer; + struct wl_buffer *wlbuffer; + struct wl_surface *surface; + AVFrame *frame; + AVFrameSideData *sd; + AVMasteringDisplayMetadata *hdr_metadata; + AVContentLightMetadata *ll_metadata; + int max_cll = -1, max_fall = -1; + wl_fixed_t rx, ry, gx, gy, bx, by, wx, wy, max_luma, min_luma; + + frame = demux_and_decode(&app->video); + if (!frame) { + fprintf(stderr, "no more frames?\n"); + return; + } + + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL); + if (sd) { + ll_metadata = (AVContentLightMetadata *)sd->data; + max_cll = ll_metadata->MaxCLL; + max_fall = ll_metadata->MaxFALL; + } + sd = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA); + if (sd) { + hdr_metadata = (AVMasteringDisplayMetadata *)sd->data; + if (hdr_metadata->has_luminance && hdr_metadata->has_primaries) { + ensure_hdr_surface(app); + rx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][0])); + ry = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[0][1])); + gx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][0])); + gy = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[1][1])); + bx = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][0])); + by = wl_fixed_from_double(av_q2d(hdr_metadata->display_primaries[2][1])); + wx = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[0])); + wy = wl_fixed_from_double(av_q2d(hdr_metadata->white_point[1])); + max_luma = wl_fixed_from_double(av_q2d(hdr_metadata->max_luminance)); + min_luma = wl_fixed_from_double(av_q2d(hdr_metadata->min_luminance)); + zwp_hdr_surface_v1_set( + app->hdr_surface, + rx, + ry, + gx, + gy, + bx, + by, + wx, + wy, + max_luma, + min_luma, + max_cll == -1 ? 0 : max_cll, + max_fall == -1 ? 0 : max_fall); + + zwp_hdr_surface_v1_set_eotf( + app->hdr_surface, + ZWP_HDR_SURFACE_V1_EOTF_ST_2084_PQ); + } + } else { + // No metadata for this frame. Destroy the created hdr surface. + destroy_hdr_surface(app); + } + + buffer = video_next_buffer(video); + + // If no free buffers available schedule redraw and return; + if(!buffer) { + widget_schedule_redraw(widget); + return; + } + + if (!fill_buffer(buffer, frame)) { + fprintf(stderr, "failed to fill buffer\n"); + return; + } + + wlbuffer = buffer->buffer; + + surface = widget_get_wl_surface(widget); + wl_surface_attach(surface, wlbuffer, 0, 0); + wl_surface_damage(surface, 0, 0, frame->width, frame->height); + wl_surface_commit(surface); + widget_schedule_redraw(widget); + buffer->busy = 1; + buffer->prev_frame = frame; +} + +/* + * +---------------------------+ + * | | | + * | | | + * | |vm Video | + * | | | + * | | | + * |___+-------------------+ | + * | hm| Subtitle | | + * | +-------------------+ | + * | | + * +---------------------------+ + * + * hm : horizontal margin + * vm : vertical margin + */ + +static void +resize_handler(struct widget *widget, + int32_t width, int32_t height, void *data) +{ + struct app *app = data; + struct rectangle area; + + // margin is in percentage + int vm = 85, hm = 40; + int x, y, w, h; + int mhorizontal, mvertical; + + if (app->subtitle) { + widget_get_allocation(widget, &area); + + mhorizontal = area.width * hm / 100; + mvertical = area.height * vm / 100; + + x = area.x + mhorizontal; + y = area.y + mvertical; + w = area.width * 2 / 10; // 20% of total width + h = area.height / 20; // 5% of total height + + widget_set_allocation(app->subtitle->widget, + x, y, w, h); + } + +} + +static void +keyboard_focus_handler(struct window *window, + struct input *device, void *data) +{ + struct app *app = data; + + window_schedule_redraw(app->window); +} + +static void +key_handler(struct window *window, struct input *input, uint32_t time, + uint32_t key, uint32_t sym, + enum wl_keyboard_key_state state, void *data) +{ + struct app *app = data; + struct rectangle winrect; + + if (state == WL_KEYBOARD_KEY_STATE_RELEASED) + return; + + switch (sym) { + case XKB_KEY_Up: + window_get_allocation(window, &winrect); + winrect.height -= 100; + if (winrect.height < 150) + winrect.height = 150; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Down: + window_get_allocation(window, &winrect); + winrect.height += 100; + if (winrect.height > 600) + winrect.height = 600; + window_schedule_resize(window, winrect.width, winrect.height); + break; + case XKB_KEY_Escape: + display_exit(app->display); + break; + } +} + +static void video_close(struct video *s) +{ + av_parser_close(s->parser); + avcodec_free_context(&s->codec); + av_packet_free(&s->pkt); +} + +static const enum zwp_colorspace_v1_chromacities chromacities[] = { + [AVCOL_PRI_BT709] = ZWP_COLORSPACE_V1_CHROMACITIES_BT709, + [AVCOL_PRI_BT470M] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470M, + [AVCOL_PRI_BT470BG] = ZWP_COLORSPACE_V1_CHROMACITIES_BT470BG, + [AVCOL_PRI_SMPTE170M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE240M] = ZWP_COLORSPACE_V1_CHROMACITIES_SMPTE170M, + [AVCOL_PRI_SMPTE431] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE432] = ZWP_COLORSPACE_V1_CHROMACITIES_DCI_P3, + [AVCOL_PRI_SMPTE428] = ZWP_COLORSPACE_V1_CHROMACITIES_CIEXYZ, + [AVCOL_PRI_BT2020] = ZWP_COLORSPACE_V1_CHROMACITIES_BT2020, +}; + +static enum zwp_colorspace_v1_chromacities +video_chromacities(struct video *s) +{ + if (s->codec->color_primaries >= ARRAY_LENGTH(chromacities)) + return ZWP_COLORSPACE_V1_CHROMACITIES_UNDEFINED; + + return chromacities[s->codec->color_primaries]; +} + +static bool video_open(struct app *app, + struct video *s, + const char *filename) +{ + AVCodec *codec = NULL; + AVStream *stream; + int r; + char buf[4096] = {}; +#if LIBAVCODEC_VERSION_INT < AV_VERSION_INT(58, 9, 100) + av_register_all(); +#endif + + r = avformat_open_input(&s->fmt_ctx, filename, NULL, NULL); + if (r < 0) + return false; + + r = avformat_find_stream_info(s->fmt_ctx, NULL); + if (r < 0) + return false; + + r = av_find_best_stream(s->fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &codec, 0); + if (r < 0) + return false; + + stream = s->fmt_ctx->streams[r]; + s->stream_index = r; + + s->codec = avcodec_alloc_context3(codec); + if (!s->codec) + return false; + + s->codec->opaque = s; + + r = avcodec_parameters_to_context(s->codec, stream->codecpar); + if (r < 0) + return false; + + r = avcodec_open2(s->codec, codec, NULL); + if (r < 0) + return false; + + s->parser = av_parser_init(codec->id); + if (!s->parser) + return false; + + avcodec_string(buf, sizeof(buf), s->codec, false); + buf[sizeof(buf)-1] = '\0'; + puts(buf); + + s->pkt = av_packet_alloc(); + + return true; +} + +static void +dmabuf_modifiers(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, + uint32_t format, uint32_t modifier_hi, uint32_t modifier_lo) +{ +} + +static void +dmabuf_format(void *data, struct zwp_linux_dmabuf_v1 *zwp_linux_dmabuf, uint32_t format) +{ + /* XXX: deprecated */ +} + + +static const struct zwp_linux_dmabuf_v1_listener dmabuf_listener = { + dmabuf_format, + dmabuf_modifiers +}; + +static void +global_handler(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ + + struct app *app = data; + + if (strcmp(interface, "zwp_colorspace_v1") == 0) { + app->colorspace = + display_bind(display, id, + &zwp_colorspace_v1_interface, 1); + } else if (strcmp(interface, "zwp_hdr_metadata_v1") == 0) { + app->hdr_metadata = + display_bind(display, id, + &zwp_hdr_metadata_v1_interface, 1); + } else if (strcmp(interface, "zwp_linux_dmabuf_v1") == 0) { + if (version < 3) + return; + app->dmabuf = + display_bind(display, id, + &zwp_linux_dmabuf_v1_interface, 3); + zwp_linux_dmabuf_v1_add_listener(app->dmabuf, + &dmabuf_listener, + app); + } + +} + +static void +global_handler_remove(struct display *display, uint32_t id, + const char *interface, uint32_t version, void *data) +{ +} + +static int +gbm_init(struct buffer *my_buf) +{ + my_buf->drm_fd = open("/dev/dri/renderD128", O_RDWR); + if (my_buf->drm_fd < 0) + return 0; + + if (!my_buf->gbm) + my_buf->gbm = gbm_create_device(my_buf->drm_fd); + + if (!my_buf->gbm) + return 0; + + return 1; +} + +static void +gbm_fini(struct buffer *my_buf) +{ + gbm_device_destroy(my_buf->gbm); + close(my_buf->drm_fd); +} + +static int +create_dmabuf_buffer(struct app *app, struct buffer *buffer, + int width, int height, int format) +{ + struct zwp_linux_buffer_params_v1 *params; + uint64_t modifier = DRM_FORMAT_MOD_LINEAR; + uint32_t flags = 0; + unsigned buf_w, buf_h; + int pixel_format; + + memset(buffer, 0, sizeof(*buffer)); + if (!gbm_init(buffer)) { + fprintf(stderr, "drm_connect failed\n"); + goto error; + } + + buffer->width = width; + buffer->height = height; + buffer->format = format; + + switch (format) { + case DRM_FORMAT_NV12: + pixel_format = GBM_FORMAT_GR88; + buf_w = width / 2; + buf_h = height * 3 / 2; + buffer->cpp = 1; + break; + case DRM_FORMAT_YUV420: + pixel_format = GBM_FORMAT_GR88; + buf_w = width / 2; + buf_h = height * 2; /* U/V not interleaved */ + buffer->cpp = 1; + break; + case DRM_FORMAT_P010: + pixel_format = GBM_FORMAT_GR88; + buf_w = width; + buf_h = height * 3 / 2; + buffer->cpp = 2; + break; + case DRM_FORMAT_ARGB8888: + pixel_format = GBM_FORMAT_ARGB8888; + buf_w = width; + buf_h = height; + buffer->cpp = 1; + break; + default: + buffer->height = height; + buffer->cpp = 1; + } + + buffer->bo = gbm_bo_create_with_modifiers(buffer->gbm, buf_w, buf_h, pixel_format, &modifier, 1); + if (!buffer->bo) { + fprintf(stderr, "error: unable to allocate bo\n"); + goto error1; + } + + buffer->dmabuf_fd = gbm_bo_get_fd(buffer->bo); + buffer->stride = gbm_bo_get_stride(buffer->bo); + + if (buffer->dmabuf_fd < 0) { + fprintf(stderr, "error: dmabuf_fd < 0\n"); + goto error2; + } + + params = zwp_linux_dmabuf_v1_create_params(app->dmabuf); + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 0, /* plane_idx */ + 0, /* offset */ + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + + switch (format) { + case DRM_FORMAT_NV12: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_YUV420: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + + /* add the third plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 2, + buffer->stride * buffer->height * 3 / 2, + buffer->stride / 2, + modifier >> 32, + modifier & 0xffffffff); + break; + case DRM_FORMAT_P010: + /* add the second plane params */ + zwp_linux_buffer_params_v1_add(params, + buffer->dmabuf_fd, + 1, + buffer->stride * buffer->height, + buffer->stride, + modifier >> 32, + modifier & 0xffffffff); + break; + default: + break; + } + + buffer->buffer = zwp_linux_buffer_params_v1_create_immed(params, + buffer->width, + buffer->height, + format, + flags); + wl_buffer_add_listener(buffer->buffer, &buffer_listener, buffer); + + return 0; + +error2: + gbm_bo_destroy(buffer->bo); +error1: + gbm_fini(buffer); +error: + return -1; +} + +static struct app * +video_create(struct display *display, const char *filename) +{ + struct app *app; + struct wl_surface *surface; + struct wl_display *wldisplay; + uint32_t i, width, height, format; + int ret; + struct buffer *buffer; + + app = xzalloc(sizeof *app); + + app->display = display; + display_set_user_data(app->display, app); + display_set_global_handler(display, global_handler); + display_set_global_handler_remove(display, global_handler_remove); + + // Ensure that we have received the DMABUF format and modifier support + wldisplay = display_get_display(display); + wl_display_roundtrip(wldisplay); + + app->window = window_create(app->display); + app->widget = window_add_widget(app->window, app); + window_set_title(app->window, "Wayland Simple HDR video"); + + window_set_key_handler(app->window, key_handler); + window_set_user_data(app->window, app); + window_set_keyboard_focus_handler(app->window, keyboard_focus_handler); + + widget_set_redraw_handler(app->widget, redraw_handler); + widget_set_resize_handler(app->widget, resize_handler); + + widget_set_use_cairo(app->widget, 0); + + if (!video_open(app, &app->video, filename)) + goto err; + + surface = window_get_wl_surface(app->window); + zwp_colorspace_v1_set(app->colorspace, + surface, + video_chromacities(&app->video)); + + if (option_subtitle) + app->subtitle = subtitle_create(app); + + width = app->video.codec->width; + height = app->video.codec->height; + format = av_format_to_drm_format(app->video.codec->pix_fmt); + + if (option_fullscreen) { + window_set_fullscreen(app->window, 1); + } else { + /* if not fullscreen, resize as per the video size */ + widget_schedule_resize(app->widget, width, height); + } + + for (i = 0; i < NUM_BUFFERS; i++) { + buffer = &app->video.buffers[i]; + ret = create_dmabuf_buffer(app, buffer, width, height, format); + + if (ret < 0) + goto err; + } + + return app; + +err: + free(app); + return NULL; +} + +static void +video_destroy(struct app *app) +{ + if (app->subtitle) + subtitle_destroy(app->subtitle); + + video_close(&app->video); + + widget_destroy(app->widget); + window_destroy(app->window); + free(app); +} + +int +main(int argc, char *argv[]) +{ + struct display *display; + struct app *app; + + parse_options(options, ARRAY_LENGTH(options), &argc, argv); + if (option_help) { + printf(help_text, argv[0]); + return 0; + } + + display = display_create(&argc, argv); + if (display == NULL) { + fprintf(stderr, "failed to create display: %m\n"); + return -1; + } + + if (!display_has_subcompositor(display)) { + fprintf(stderr, "compositor does not support " + "the subcompositor extension\n"); + return -1; + } + + app = video_create(display, argv[argc - 1]); + if (!app) { + fprintf(stderr, "Failed to initialize!"); + exit(EXIT_FAILURE); + } + + display_run(display); + + video_destroy(app); + display_destroy(display); + + return 0; +} -- GitLab From ec95ca78819e49787062e316d0ae7eb10d00ee5e Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 24 Dec 2018 00:09:57 +0530 Subject: [PATCH 09/29] gitlab-ci: Add lib{avformat, avutil, avcodec} to apt-get Signed-off-by: Harish Krupo --- .gitlab-ci/debian-install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci/debian-install.sh b/.gitlab-ci/debian-install.sh index 758c15dd2..2a9eda24c 100644 --- a/.gitlab-ci/debian-install.sh +++ b/.gitlab-ci/debian-install.sh @@ -4,7 +4,7 @@ set -o xtrace echo 'deb http://deb.debian.org/debian stretch-backports main' >> /etc/apt/sources.list apt-get update -apt-get -y --no-install-recommends install build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev libpixman-1-dev libpng-dev libjpeg-dev libcolord-dev mesa-common-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libwayland-dev libxcb1-dev libxcb-composite0-dev libxcb-xfixes0-dev libxcb-xkb-dev libx11-xcb-dev libx11-dev libudev-dev libgbm-dev libxkbcommon-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libxcursor-dev libmtdev-dev libpam0g-dev libvpx-dev libsystemd-dev libevdev-dev libinput-dev libwebp-dev libjpeg-dev libva-dev liblcms2-dev git libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freerdp2-dev curl python3-pip python3-setuptools ninja-build +apt-get -y --no-install-recommends install build-essential automake autoconf libtool pkg-config libexpat1-dev libffi-dev libxml2-dev libpixman-1-dev libpng-dev libjpeg-dev libcolord-dev mesa-common-dev libglu1-mesa-dev libegl1-mesa-dev libgles2-mesa-dev libwayland-dev libxcb1-dev libxcb-composite0-dev libxcb-xfixes0-dev libxcb-xkb-dev libx11-xcb-dev libx11-dev libudev-dev libgbm-dev libxkbcommon-dev libcairo2-dev libpango1.0-dev libgdk-pixbuf2.0-dev libxcursor-dev libmtdev-dev libpam0g-dev libvpx-dev libsystemd-dev libevdev-dev libinput-dev libwebp-dev libjpeg-dev libva-dev liblcms2-dev git libgstreamer1.0-dev libgstreamer-plugins-base1.0-dev freerdp2-dev curl python3-pip python3-setuptools ninja-build libavformat-dev libavutil-dev libavcodec-dev pip3 install --user git+https://github.com/mesonbuild/meson.git@0.49 -- GitLab From ef90265a9052712d70a77028fa1f7bfb79d4e393 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Tue, 19 Dec 2017 21:50:58 +0200 Subject: [PATCH 10/29] matrix: Add weston_matrix_diag() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add weston_matrix_diag() which gives us a diagonal matrix populated with the elements from the passed in vector. Commit message edit (Harish krupo) v2: Set matrix type to WESTON_MATRIX_TRANSFORM_OTHER (Pekka) Signed-off-by: Ville Syrjälä Signed-off-by: Harish Krupo --- include/libweston/matrix.h | 2 ++ shared/matrix.c | 13 +++++++++++++ 2 files changed, 15 insertions(+) diff --git a/include/libweston/matrix.h b/include/libweston/matrix.h index be4d4eb0d..d83065010 100644 --- a/include/libweston/matrix.h +++ b/include/libweston/matrix.h @@ -60,6 +60,8 @@ void weston_matrix_rotate_xy(struct weston_matrix *matrix, float cos, float sin); void weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v); +void +weston_matrix_diag(struct weston_matrix *matrix, const struct weston_vector *v); int weston_matrix_invert(struct weston_matrix *inverse, diff --git a/shared/matrix.c b/shared/matrix.c index 4e8d6b40b..532355a20 100644 --- a/shared/matrix.c +++ b/shared/matrix.c @@ -129,6 +129,19 @@ weston_matrix_transform(struct weston_matrix *matrix, struct weston_vector *v) *v = t; } +WL_EXPORT void +weston_matrix_diag(struct weston_matrix *matrix, const struct weston_vector *v) +{ + unsigned c; + + weston_matrix_init(matrix); + + for (c = 0; c < 4; c++) + matrix->d[c * 4 + c] = v->f[c]; + + matrix->type = WESTON_MATRIX_TRANSFORM_OTHER; +} + static inline void swap_rows(double *a, double *b) { -- GitLab From caaf9e822829db87fbc0836aacccbf8eaeed3e40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ville=20Syrj=C3=A4l=C3=A4?= Date: Sun, 10 Mar 2019 07:43:15 +0530 Subject: [PATCH 11/29] Colorspace conversion matrix generator MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CSC matrices are created based on the input and output color spaces. They are generated as follows: mat1 <- src_rgb_to_xyz matrix mat2 <- inverse of (dst_rgb_to_xyz matrix) mat3 <- White point adaptation (Bradford matrix, if required) csc <- mat2*mat3*mat1 Added commit message and meson support (Harish Krupo) Signed-off-by: Ville Syrjälä Signed-off-by: Harish Krupo --- libweston/meson.build | 1 + shared/csc.c | 217 ++++++++++++++++++++++++++++++++++++++++++ shared/csc.h | 45 +++++++++ 3 files changed, 263 insertions(+) create mode 100644 shared/csc.c create mode 100644 shared/csc.h diff --git a/libweston/meson.build b/libweston/meson.build index fa353ad1c..39fd1685e 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -31,6 +31,7 @@ srcs_libweston = [ 'colorspace.c', '../shared/matrix.c', '../shared/colorspace.c', + '../shared/csc.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, linux_explicit_synchronization_unstable_v1_protocol_c, diff --git a/shared/csc.c b/shared/csc.c new file mode 100644 index 000000000..f4d27aec5 --- /dev/null +++ b/shared/csc.c @@ -0,0 +1,217 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include + +#ifdef IN_WESTON +#include +#else +#define WL_EXPORT +#endif + +#include "csc.h" + +static void xy_to_xyz(struct weston_vector *xyz, + const struct cie_xy *xy, + float luminance) +{ + float xy_z = 1.0f - xy->x - xy->y; + float xy_y_inv = 1.0f / xy->y; + + xyz->f[0] = luminance * xy->x * xy_y_inv; + xyz->f[1] = luminance; + xyz->f[2] = luminance * xy_z * xy_y_inv; + xyz->f[3] = 1.0f; +} + +static void +rgb_to_xyz_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *cs) +{ + struct weston_vector r, g, b, w; + struct weston_matrix p, p_inv; + int err; + + xy_to_xyz(&w, &cs->primaries.white_point, 1.0f); + xy_to_xyz(&r, &cs->primaries.r, cs->primaries.r.y); + xy_to_xyz(&g, &cs->primaries.g, cs->primaries.g.y); + xy_to_xyz(&b, &cs->primaries.b, cs->primaries.b.y); + + if (cs->primaries.r.x == 1.0f && cs->primaries.r.y == 0.0f && + cs->primaries.g.x == 0.0f && cs->primaries.g.y == 1.0f && + cs->primaries.b.x == 0.0f && cs->primaries.b.y == 0.0f) { + r.f[0] = 1.0f; + r.f[1] = 0.0f; + r.f[2] = 0.0f; + g.f[0] = 0.0f; + g.f[1] = 1.0f; + g.f[2] = 0.0f; + b.f[0] = 0.0f; + b.f[1] = 0.0f; + b.f[2] = 1.0f; + } + + weston_matrix_init(&p); + + p.d[0 * 4 + 0] = r.f[0]; + p.d[1 * 4 + 0] = g.f[0]; + p.d[2 * 4 + 0] = b.f[0]; + p.d[0 * 4 + 1] = r.f[1]; + p.d[1 * 4 + 1] = g.f[1]; + p.d[2 * 4 + 1] = b.f[1]; + p.d[0 * 4 + 2] = r.f[2]; + p.d[1 * 4 + 2] = g.f[2]; + p.d[2 * 4 + 2] = b.f[2]; + + err = weston_matrix_invert(&p_inv, &p); + assert(err == 0); + + weston_matrix_transform(&p_inv, &w); + + weston_matrix_diag(matrix, &w); + + weston_matrix_multiply(matrix, &p); +} + +static void +xyz_to_lms_matrix(struct weston_matrix *matrix) +{ + weston_matrix_init(matrix); + +#if 0 + /* von Kries */ + matrix->d[0 * 4 + 0] = 0.4002f; + matrix->d[1 * 4 + 0] = 0.7076f; + matrix->d[2 * 4 + 0] = -0.0808f; + + matrix->d[0 * 4 + 1] = -0.2263f; + matrix->d[1 * 4 + 1] = 1.1653f; + matrix->d[2 * 4 + 1] = 0.0457f; + + matrix->d[0 * 4 + 2] = 0.0000f; + matrix->d[1 * 4 + 2] = 0.0000f; + matrix->d[2 * 4 + 2] = 0.9182f; +#endif +#if 1 + /* Bradford */ + matrix->d[0 * 4 + 0] = 0.8951f; + matrix->d[1 * 4 + 0] = 0.2664f; + matrix->d[2 * 4 + 0] = -0.1614f; + + matrix->d[0 * 4 + 1] = -0.7502f; + matrix->d[1 * 4 + 1] = 1.7135f; + matrix->d[2 * 4 + 1] = 0.0367f; + + matrix->d[0 * 4 + 2] = 0.0389f; + matrix->d[1 * 4 + 2] = -0.0685f; + matrix->d[2 * 4 + 2] = 1.0296f; +#endif +} + +static void +cat_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *dst, + const struct weston_colorspace *src) +{ + struct weston_matrix xyz_to_lms; + struct weston_vector w_xyz_dst; + struct weston_vector w_xyz_src; + struct weston_vector w_lms_dst; + struct weston_vector w_lms_src; + + xy_to_xyz(&w_xyz_dst, &dst->primaries.white_point, 1.0f); + xy_to_xyz(&w_xyz_src, &src->primaries.white_point, 1.0f); + + xyz_to_lms_matrix(&xyz_to_lms); + + w_lms_dst = w_xyz_dst; + weston_matrix_transform(&xyz_to_lms, &w_xyz_dst); + + w_lms_src = w_xyz_src; + weston_matrix_transform(&xyz_to_lms, &w_xyz_src); + + weston_matrix_init(matrix); + + matrix->d[0 * 4 + 0] = w_lms_dst.f[0] / w_lms_src.f[0]; + matrix->d[1 * 4 + 1] = w_lms_dst.f[1] / w_lms_src.f[1]; + matrix->d[2 * 4 + 2] = w_lms_dst.f[2] / w_lms_src.f[2]; + matrix->d[3 * 4 + 3] = 1.0f; +} + +WL_EXPORT void +weston_csc_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *dst, + const struct weston_colorspace *src, + float luminance_scale) +{ + struct weston_matrix rgb_to_xyz_src; + struct weston_matrix xyz_to_lms; + struct weston_matrix cat; + struct weston_matrix lms_to_xyz; + struct weston_matrix rgb_to_xyz_dst; + struct weston_matrix xyz_to_rgb_dst; + int err; + + rgb_to_xyz_matrix(&rgb_to_xyz_src, src); + rgb_to_xyz_matrix(&rgb_to_xyz_dst, dst); + err = weston_matrix_invert(&xyz_to_rgb_dst, &rgb_to_xyz_dst); + assert(err == 0); + + xyz_to_lms_matrix(&xyz_to_lms); + err = weston_matrix_invert(&lms_to_xyz, &xyz_to_lms); + assert(err == 0); + + cat_matrix(&cat, dst, src); + +#if 0 + printf("RGB to XYZ\n"); + weston_matrix_print(&rgb_to_xyz_src); + printf("XYZ to LMS\n"); + weston_matrix_print(&xyz_to_lms); + printf("CAT\n"); + weston_matrix_print(&cat); + printf("LMS to XYZ\n"); + weston_matrix_print(&lms_to_xyz); + printf("XYZ to RGB\n"); + weston_matrix_print(&xyz_to_rgb_dst); +#endif + + weston_matrix_init(matrix); + + weston_matrix_multiply(matrix, &rgb_to_xyz_src); + weston_matrix_multiply(matrix, &xyz_to_lms); + weston_matrix_multiply(matrix, &cat); + weston_matrix_multiply(matrix, &lms_to_xyz); + weston_matrix_multiply(matrix, &xyz_to_rgb_dst); + + weston_matrix_scale(matrix, + luminance_scale, + luminance_scale, + luminance_scale); +} diff --git a/shared/csc.h b/shared/csc.h new file mode 100644 index 000000000..7b5a9bd22 --- /dev/null +++ b/shared/csc.h @@ -0,0 +1,45 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_CSC_H +#define WESTON_CSC_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +void weston_csc_matrix(struct weston_matrix *matrix, + const struct weston_colorspace *dst, + const struct weston_colorspace *src, + float luminance_scale); + +#ifdef __cplusplus +} +#endif + +#endif -- GitLab From c2939b8fd9eaefa615b7f4cef7eea4f9d3730b33 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 11 Mar 2019 19:39:10 +0530 Subject: [PATCH 12/29] compositor.h: Add new renderer interfaces for HDR Add set_output_{colorspace,hdr_metadata} interface for renderers so that the compositor backend can inform the renderer of the expected target color space and target hdr_metadata. Signed-off-by: Harish Krupo --- include/libweston/libweston.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/include/libweston/libweston.h b/include/libweston/libweston.h index 087c284b4..e3167f267 100644 --- a/include/libweston/libweston.h +++ b/include/libweston/libweston.h @@ -927,6 +927,12 @@ struct weston_renderer { void (*query_dmabuf_modifiers)(struct weston_compositor *ec, int format, uint64_t **modifiers, int *num_modifiers); + + void (*set_output_colorspace)(struct weston_output *output, + uint32_t colorspace); + + void (*set_output_hdr_metadata)(struct weston_output *output, + struct weston_hdr_metadata *metadata); }; enum weston_capability { -- GitLab From 24b04dd2fa9705616dd26c57a6185a7a22ac2c41 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 11 Mar 2019 19:39:55 +0530 Subject: [PATCH 13/29] noop-renderer: Implement the output colorspace / HDR interface NULL implementations for now. Signed-off-by: Harish Krupo --- libweston/noop-renderer.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libweston/noop-renderer.c b/libweston/noop-renderer.c index aef93002c..716cbda48 100644 --- a/libweston/noop-renderer.c +++ b/libweston/noop-renderer.c @@ -116,6 +116,9 @@ noop_renderer_init(struct weston_compositor *ec) renderer->attach = noop_renderer_attach; renderer->surface_set_color = noop_renderer_surface_set_color; renderer->destroy = noop_renderer_destroy; + renderer->set_output_colorspace = NULL; + renderer->set_output_hdr_metadata = NULL; + ec->renderer = renderer; return 0; -- GitLab From 463108571bdfe466227c2270e223d6a54dab0ffc Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 11 Mar 2019 19:40:11 +0530 Subject: [PATCH 14/29] pixman-renderer: Implement the output colorspace / HDR interface NULL implementations for now. Signed-off-by: Harish Krupo --- libweston/pixman-renderer.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libweston/pixman-renderer.c b/libweston/pixman-renderer.c index 8677023cf..ecca4df8b 100644 --- a/libweston/pixman-renderer.c +++ b/libweston/pixman-renderer.c @@ -881,6 +881,8 @@ pixman_renderer_init(struct weston_compositor *ec) pixman_renderer_surface_get_content_size; renderer->base.surface_copy_content = pixman_renderer_surface_copy_content; + renderer->base.set_output_colorspace = NULL; + renderer->base.set_output_hdr_metadata = NULL; ec->renderer = &renderer->base; ec->capabilities |= WESTON_CAP_ROTATION_ANY; ec->capabilities |= WESTON_CAP_CAPTURE_YFLIP; -- GitLab From 403a2e68234d6bb671349ca7875a04eb3d03a67d Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 11 Mar 2019 19:40:35 +0530 Subject: [PATCH 15/29] gl-renderer: Implement the renderer colorspace / HDR interface The uses the set colorspace/hdr metadata to take decisions about the colorspace conversion and/or the tone mapping to be applied. Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer.c | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index f40ea33e0..c97e16b5c 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -100,6 +100,9 @@ struct gl_output_state { struct wl_list timeline_render_point_list; GLuint shadow_fbo; GLuint shadow_tex; + enum weston_colorspace_enums target_colorspace; + struct weston_hdr_metadata *target_hdr_metadata; + bool hdr_state_changed; }; enum buffer_type { @@ -3173,6 +3176,10 @@ gl_renderer_output_create(struct weston_output *output, glBindFramebuffer(GL_FRAMEBUFFER, 0); + go->target_colorspace = WESTON_CS_BT709; + go->target_hdr_metadata = NULL; + go->hdr_state_changed = false; + return 0; } @@ -3588,6 +3595,35 @@ gl_renderer_create_pbuffer_surface(struct gl_renderer *gr) { return 0; } +static void +gl_renderer_set_output_colorspace(struct weston_output *output, + uint32_t colorspace) +{ + struct gl_output_state *go = get_output_state(output); + if (go->target_colorspace == colorspace) + return; + + go->target_colorspace = colorspace; + go->hdr_state_changed = true; +} + +static void +gl_renderer_set_output_hdr_metadata(struct weston_output *output, + struct weston_hdr_metadata *hdr_metadata) +{ + struct gl_output_state *go = get_output_state(output); + + if (go->target_hdr_metadata == hdr_metadata) + return; + + if (go->target_hdr_metadata && hdr_metadata && + !memcmp(go->target_hdr_metadata, hdr_metadata, sizeof(*hdr_metadata))) + return; + + go->target_hdr_metadata = hdr_metadata; + go->hdr_state_changed = true; +} + static int gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, void *native_window, const EGLint *platform_attribs, @@ -3617,6 +3653,8 @@ gl_renderer_display_create(struct weston_compositor *ec, EGLenum platform, gr->base.surface_get_content_size = gl_renderer_surface_get_content_size; gr->base.surface_copy_content = gl_renderer_surface_copy_content; + gr->base.set_output_colorspace = gl_renderer_set_output_colorspace; + gr->base.set_output_hdr_metadata = gl_renderer_set_output_hdr_metadata; gr->egl_display = NULL; wl_list_init(&gr->shader_list); -- GitLab From a545207835fa316093aa5d72a2a1107fb9077c8c Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Tue, 23 Apr 2019 12:57:09 +0530 Subject: [PATCH 16/29] gl-renderer: Use full damage when HDR state changes Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer.c | 37 +++++++++++++++++++++++++---- 1 file changed, 33 insertions(+), 4 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index c97e16b5c..0c36ba48c 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -1477,10 +1477,16 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_t buffer_damage, total_damage; enum gl_border_status border_damage = BORDER_STATUS_CLEAN; struct weston_view *view; + pixman_region32_t full_damage; + pixman_region32_t *repaint_damage, *repaint_texture_damage; if (use_output(output) < 0) return; + pixman_region32_init_rect(&full_damage, 0, 0, + output->current_mode->width, + output->current_mode->height); + /* Clear the used_in_output_repaint flag, so that we can properly track * which surfaces were used in this output repaint. */ wl_list_for_each_reverse(view, &compositor->view_list, link) { @@ -1539,19 +1545,39 @@ gl_renderer_repaint_output(struct weston_output *output, pixman_region32_init(&buffer_damage); output_get_damage(output, &buffer_damage, &border_damage); - output_rotate_damage(output, output_damage, go->border_status); + + if (go->hdr_state_changed) { + output_rotate_damage(output, &full_damage, go->border_status); + } else { + output_rotate_damage(output, output_damage, go->border_status); + } pixman_region32_union(&total_damage, &buffer_damage, output_damage); border_damage |= go->border_status; + if (gr->supports_half_float_texture) { + if (go->hdr_state_changed) { + repaint_damage = &full_damage; + repaint_texture_damage = &full_damage; + } else { + repaint_damage = output_damage; + repaint_texture_damage = &total_damage; + } + glBindFramebuffer(GL_FRAMEBUFFER, go->shadow_fbo); - repaint_views(output, output_damage); + repaint_views(output, repaint_damage); glBindFramebuffer(GL_FRAMEBUFFER, 0); - repaint_from_texture(output, &total_damage); + repaint_from_texture(output, repaint_texture_damage); } else { + if (go->hdr_state_changed) { + repaint_damage = &full_damage; + } else { + repaint_damage = &total_damage; + } + glBindFramebuffer(GL_FRAMEBUFFER, 0); - repaint_views(output, &total_damage); + repaint_views(output, repaint_damage); } pixman_region32_fini(&total_damage); @@ -1619,6 +1645,9 @@ gl_renderer_repaint_output(struct weston_output *output, TIMELINE_RENDER_POINT_TYPE_END); update_buffer_release_fences(compositor, output); + + go->hdr_state_changed = false; + pixman_region32_fini(&full_damage); } static int -- GitLab From 2546be0cbbc3b80b9985e86c24fc6d9f6cb6c787 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Sat, 2 Feb 2019 22:40:00 +0530 Subject: [PATCH 17/29] gl-shaders: Add EOTF, OETF and CSC shaders Add sRGB, SMPTE 2084 PQ and Hybrid log gamma (HLG) eotf and oetf shaders to convert the input buffer from non linear space to linear space and vice-versa Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer-private.h | 20 +++ libweston/renderer-gl/gl-shaders.c | 183 ++++++++++++++++++++ 2 files changed, 203 insertions(+) diff --git a/libweston/renderer-gl/gl-renderer-private.h b/libweston/renderer-gl/gl-renderer-private.h index a970e2aa6..8b41872e4 100644 --- a/libweston/renderer-gl/gl-renderer-private.h +++ b/libweston/renderer-gl/gl-renderer-private.h @@ -44,10 +44,29 @@ enum gl_shader_texture_variant { SHADER_VARIANT_EXTERNAL, }; +enum gl_shader_degamma_variant { + SHADER_DEGAMMA_NONE = 0, + SHADER_DEGAMMA_LINEAR, + SHADER_DEGAMMA_SRGB, + SHADER_DEGAMMA_PQ, + SHADER_DEGAMMA_HLG, +}; + +enum gl_shader_gamma_variant { + SHADER_GAMMA_NONE = 0, + SHADER_GAMMA_LINEAR, + SHADER_GAMMA_SRGB, + SHADER_GAMMA_PQ, + SHADER_GAMMA_HLG, +}; + struct gl_shader_requirements { enum gl_shader_texture_variant variant; bool debug; + bool csc_matrix; + enum gl_shader_degamma_variant degamma; + enum gl_shader_gamma_variant gamma; }; struct gl_shader { @@ -58,6 +77,7 @@ struct gl_shader { GLint tex_uniforms[3]; GLint alpha_uniform; GLint color_uniform; + GLint csc_uniform; struct wl_list link; /* gl_renderer::shader_list */ }; diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 0c90a7375..11eca084e 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -125,6 +125,127 @@ static const char solid_fragment_shader[] = static const char fragment_brace[] = "}\n"; +/* eotfs */ +static const char eotf_srgb[] = + "float eotf_srgb_single(float c) {\n" + " return c < 0.04045 ? c / 12.92 : pow(((c + 0.055) / 1.055), 2.4);\n" + "}\n" + "\n" + "vec3 eotf_srgb(vec3 color) {\n" + " float r = eotf_srgb_single(color.r);\n" + " float g = eotf_srgb_single(color.g);\n" + " float b = eotf_srgb_single(color.b);\n" + " return vec3(r, g, b);\n" + "}\n" + "\n" + "vec3 eotf(vec3 color) {\n" + " return sign(color) * eotf_srgb(abs(color.rgb));\n" + "}\n" + "\n" + ; + +static const char eotf_pq[] = + "vec3 eotf(vec3 v) {\n" + " float m1 = 0.25 * 2610.0 / 4096.0;\n" + " float m2 = 128.0 * 2523.0 / 4096.0;\n" + " float c3 = 32.0 * 2392.0 / 4096.0;\n" + " float c2 = 32.0 * 2413.0 / 4096.0;\n" + " float c1 = c3 - c2 + 1.0;\n" + " vec3 n = pow(v, vec3(1.0 / m2));\n" + " return pow(max(n - c1, 0.0) / (c2 - c3 * n), vec3(1.0 / m1));\n" + "}\n" + "\n" + ; + +static const char eotf_hlg[] = + "vec3 eotf(vec3 l) {\n" + " float a = 0.17883277;\n" + " float b = 1.0 - 4.0 * a;\n" + " float c = 0.5 - a * log(4.0 * a);\n" + " float x = step(1.0 / 2.0, l);\n" + " vec3 v0 = pow(l, 2.0) / 3.0;\n" + " vec3 v1 = (exp((l - c) / a) + b) / 12.0;\n" + " return mix(v0, v1, x);\n" + "}\n" + "\n" + ; + +static const char eotf_default[] = + "vec3 eotf(vec3 color) {\n" + " return color;\n" + "}\n" + "\n" + ; + +/* oetfs */ +static const char oetf_srgb[] = + "float oetf_srgb_single(float c) {\n" + " float ret = 0.0;\n" + " if (c < 0.0031308) {\n" + " ret = 12.92 * c;\n" + " } else {\n" + " ret = 1.055 * pow(c, 1.0 / 2.4) - 0.055;\n" + " }\n" + " return ret;\n" + "}\n" + "\n" + "vec3 oetf_srgb(vec3 color) {\n" + " float r = oetf_srgb_single(color.r);\n" + " float g = oetf_srgb_single(color.g);\n" + " float b = oetf_srgb_single(color.b);\n" + " return vec3(r, g, b);\n" + "}\n" + "\n" + "vec3 oetf(vec3 linear) {\n" + " return sign(linear) * oetf_srgb(abs(linear.rgb));\n" + "}\n" + "\n" + ; + +static const char oetf_pq[] = + "vec3 oetf(vec3 l) {\n" + " float m1 = 0.25 * 2610.0 / 4096.0;\n" + " float m2 = 128.0 * 2523.0 / 4096.0;\n" + " float c3 = 32.0 * 2392.0 / 4096.0;\n" + " float c2 = 32.0 * 2413.0 / 4096.0;\n" + " float c1 = c3 - c2 + 1.0;\n" + " vec3 n = pow(l, vec3(m1));\n" + " return pow((c1 + c2 * n) / (1.0 + c3 * n), vec3(m2));\n" + "}\n" + "\n" + ; + +static const char oetf_hlg[] = + "vec3 oetf(vec3 l) {\n" + " float a = 0.17883277;\n" + " float b = 1.0 - 4.0 * a;\n" + " float c = 0.5 - a * log(4.0 * a);\n" + " float x = step(1.0 / 12.0, l);\n" + " vec3 v0 = a * log(12.0 * l - b) + c;\n" + " vec3 v1 = sqrt(3.0 * l);\n" + " return mix(v0, v1, x);\n" + "}\n" + "\n" + ; + +static const char oetf_default[] = + "vec3 oetf(vec3 color) {\n" + " return color;\n" + "}\n" + "\n" + ; + +static const char eotf_shader[] = + " gl_FragColor.rgb = eotf(gl_FragColor.rgb);\n" + ; + +static const char oetf_shader[] = + " gl_FragColor.rgb = oetf(gl_FragColor.rgb);\n" + ; + +static const char csc_shader[] = + " gl_FragColor.rgb = clamp((csc * gl_FragColor.rgb), 0.0, 1.0);\n" + ; struct gl_shader_source { const char *parts[64]; uint32_t len; @@ -137,6 +258,62 @@ gl_shader_source_add(struct gl_shader_source *shader_source, const char *str) assert(shader_source->len < ARRAY_LENGTH(shader_source->parts)); } +static void +generate_fs_hdr_shader(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + // Write the hdr uniforms + if (requirements->csc_matrix) + gl_shader_source_add(shader_source, "uniform mat3 csc;\n"); + + // Choose the EOTF + switch (requirements->degamma) { + case SHADER_DEGAMMA_SRGB: + gl_shader_source_add(shader_source, eotf_srgb); + break; + case SHADER_DEGAMMA_PQ: + gl_shader_source_add(shader_source, eotf_pq); + break; + case SHADER_DEGAMMA_HLG: + gl_shader_source_add(shader_source, eotf_hlg); + break; + default: + gl_shader_source_add(shader_source, eotf_default); + break; + } + + // Choose the OETF + switch (requirements->gamma) { + case SHADER_GAMMA_SRGB: + gl_shader_source_add(shader_source, oetf_srgb); + break; + case SHADER_GAMMA_PQ: + gl_shader_source_add(shader_source, oetf_pq); + break; + case SHADER_GAMMA_HLG: + gl_shader_source_add(shader_source, oetf_hlg); + break; + default: + gl_shader_source_add(shader_source, oetf_default); + break; + } +} + +static void +generate_hdr_process_shader(struct gl_shader_source *shader_source, + struct gl_shader_requirements *requirements) +{ + if (requirements->degamma) + gl_shader_source_add(shader_source, eotf_shader); + + if (requirements->csc_matrix) + gl_shader_source_add(shader_source, csc_shader); + + if (requirements->gamma) + gl_shader_source_add(shader_source, oetf_shader); + +} + static void generate_fs_uniforms(struct gl_shader_source *shader_source, struct gl_shader_requirements *requirements) @@ -246,12 +423,17 @@ generate_fragment_shader(struct gl_shader_generator *sg, /* Write the header and required uniforms */ generate_fs_uniforms(shader_source, requirements); + // Write shaders needed for HDR + generate_fs_hdr_shader(shader_source, requirements); + /* begin main function */ gl_shader_source_add(shader_source, fragment_main_open); /* Generate the shader based on variant */ generate_fs_variants(shader_source, requirements); + generate_hdr_process_shader(shader_source, requirements); + if (requirements->debug) gl_shader_source_add(shader_source, fragment_debug); @@ -351,6 +533,7 @@ gl_shader_create(struct gl_shader_generator *sg, shader->tex_uniforms[2] = glGetUniformLocation(shader->program, "tex2"); shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); shader->color_uniform = glGetUniformLocation(shader->program, "color"); + shader->csc_uniform = glGetUniformLocation(shader->program, "csc"); return shader; } -- GitLab From 72dc1032140c152e4e7ff4401e6ce5584c12dd19 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 11 Mar 2019 06:58:44 +0530 Subject: [PATCH 18/29] gl-renderer: Generate CSC shaders based on requirement When buffers with different color spaces are presented, a target color space is picked by the compositor backend based on the color space support available at the output. The backend uses the set_output_colorspace hook to inform the renderer of the target colorspace. The renderer then uses this information to generate csc matrices for each surface, to convert from the surface's source color space to the target color space set by the compositor backend. Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer.c | 123 +++++++++++++++++++++++++++- libweston/renderer-gl/meson.build | 2 + 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index 0c36ba48c..b02590abb 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -58,6 +58,7 @@ #include "shared/timespec-util.h" #include "weston-egl-ext.h" #include "gl-renderer-private.h" +#include "shared/csc.h" #define GR_GL_VERSION(major, minor) \ (((uint32_t)(major) << 16) | (uint32_t)(minor)) @@ -884,6 +885,14 @@ shader_uniforms(struct gl_shader *shader, int i; struct gl_surface_state *gs = get_surface_state(view->surface); struct gl_output_state *go = get_output_state(output); + struct weston_surface *surface = view->surface; + const struct weston_colorspace *src_cs, *dst_cs; + struct weston_matrix csc_matrix; + float csc[9] = {0}; + float *dst; + + // shader key contains the set of requirements used to build the shader + struct gl_shader_requirements *requirements = &shader->key; glUniformMatrix4fv(shader->proj_uniform, 1, GL_FALSE, go->output_matrix.d); @@ -892,6 +901,21 @@ shader_uniforms(struct gl_shader *shader, for (i = 0; i < gs->num_textures; i++) glUniform1i(shader->tex_uniforms[i], i); + + if (requirements->csc_matrix) { + weston_matrix_init(&csc_matrix); + src_cs = weston_colorspace_lookup(surface->colorspace); + dst_cs = weston_colorspace_lookup(go->target_colorspace); + + weston_csc_matrix(&csc_matrix, dst_cs, src_cs, 1.0); + dst = csc_matrix.d; + for (i = 0; i < 3; i++) { + memcpy(csc + 3 * i, dst, 3 * sizeof(float)); + dst += 4; + } + glUniformMatrix3fv(shader->csc_uniform, 1, GL_FALSE, csc); + } + } static int @@ -960,6 +984,62 @@ ensure_surface_buffer_is_ready(struct gl_renderer *gr, return (wait_ret == EGL_TRUE && destroy_ret == EGL_TRUE) ? 0 : -1; } +static void +compute_hdr_requirements_from_view(struct weston_view *ev, + struct weston_output *output) +{ + struct weston_surface *surface = ev->surface; + struct gl_renderer *gr = get_renderer(surface->compositor); + struct gl_surface_state *gs = get_surface_state(ev->surface); + struct gl_output_state *go = get_output_state(output); + struct weston_hdr_metadata *src_md = surface->hdr_metadata; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; + uint32_t target_colorspace = go->target_colorspace; + bool needs_csc = false; + uint32_t degamma = 0, gamma = 0; + + // Start by assuming that we don't need color space conversion + gs->shader_requirements.csc_matrix = false; + needs_csc = surface->colorspace != target_colorspace; + + if (needs_csc) + gs->shader_requirements.csc_matrix = true; + + /* identify degamma curve from input metadata */ + degamma = SHADER_DEGAMMA_SRGB; + + if (src_md) { + switch (src_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + degamma = SHADER_DEGAMMA_PQ; + break; + case WESTON_EOTF_HLG: + degamma = SHADER_DEGAMMA_HLG; + break; + } + } + + gs->shader_requirements.degamma = degamma; + + // If RGBA16F textures aren't supported, then we are forced to go for + // non linear blending + if (!gr->supports_half_float_texture) { + gamma = SHADER_GAMMA_SRGB; + if (dst_md) { + switch (dst_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + gamma = SHADER_GAMMA_PQ; + break; + case WESTON_EOTF_HLG: + gamma = SHADER_GAMMA_HLG; + break; + } + } + + gs->shader_requirements.gamma = gamma; + } +} + static void draw_view(struct weston_view *ev, struct weston_output *output, pixman_region32_t *damage) /* in global coordinates */ @@ -983,6 +1063,8 @@ draw_view(struct weston_view *ev, struct weston_output *output, if (!gs->shader_requirements.variant) return; + compute_hdr_requirements_from_view(ev, output); + pixman_region32_init(&repaint); pixman_region32_intersect(&repaint, &ev->transform.boundingbox, damage); @@ -1241,10 +1323,13 @@ draw_output_borders(struct weston_output *output, { struct gl_output_state *go = get_output_state(output); struct gl_renderer *gr = get_renderer(output->compositor); + uint32_t target_colorspace = go->target_colorspace; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; struct gl_border_image *top, *bottom, *left, *right; struct weston_matrix matrix; int full_width, full_height; struct gl_shader_requirements shader_requirements; + int gamma = 0; if (border_status == BORDER_STATUS_CLEAN) return; /* Clean. Nothing to do. */ @@ -1260,6 +1345,28 @@ draw_output_borders(struct weston_output *output, glDisable(GL_BLEND); gl_shader_requirements_init(&shader_requirements); shader_requirements.variant = SHADER_VARIANT_RGBA; + + // assuming that the borders are always BT709 + if (target_colorspace != WESTON_CS_BT709) + shader_requirements.csc_matrix = true; + else + shader_requirements.csc_matrix = false; + + shader_requirements.degamma = SHADER_DEGAMMA_SRGB; + + gamma = SHADER_GAMMA_SRGB; + if (dst_md) { + switch (dst_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + gamma = SHADER_GAMMA_PQ; + break; + case WESTON_EOTF_HLG: + gamma = SHADER_GAMMA_HLG; + break; + } + } + shader_requirements.gamma = gamma; + use_gl_program(gr, &shader_requirements); glViewport(0, 0, full_width, full_height); @@ -1395,9 +1502,10 @@ repaint_from_texture(struct weston_output *output, struct gl_renderer *gr = get_renderer(output->compositor); double width = output->current_mode->width; double height = output->current_mode->height; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; pixman_box32_t *rects; int n_rects; - int i; + int i, gamma = 0; struct gl_shader_requirements shader_requirements; GLfloat verts[4 * 2] = { 0.0f }; @@ -1416,8 +1524,21 @@ repaint_from_texture(struct weston_output *output, output->current_mode->width, output->current_mode->height); + gamma = SHADER_GAMMA_SRGB; + if (dst_md) { + switch (dst_md->metadata.static_metadata.eotf) { + case WESTON_EOTF_ST2084: + gamma = SHADER_GAMMA_PQ; + break; + case WESTON_EOTF_HLG: + gamma = SHADER_GAMMA_HLG; + break; + } + } + gl_shader_requirements_init(&shader_requirements); shader_requirements.variant = SHADER_VARIANT_RGBA; + shader_requirements.gamma = gamma; use_gl_program(gr, &shader_requirements); glUniformMatrix4fv(gr->current_shader->proj_uniform, 1, GL_FALSE, proj); diff --git a/libweston/renderer-gl/meson.build b/libweston/renderer-gl/meson.build index 91a3bfa19..47e99bef3 100644 --- a/libweston/renderer-gl/meson.build +++ b/libweston/renderer-gl/meson.build @@ -7,6 +7,8 @@ config_h.set('ENABLE_EGL', '1') srcs_renderer_gl = [ 'gl-renderer.c', 'gl-shaders.c', + '../../shared/colorspace.c', + '../../shared/csc.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, ] -- GitLab From 2d8018ae9fbcb89d2489e5bdb5747f313249667f Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Wed, 3 Apr 2019 10:43:30 +0530 Subject: [PATCH 19/29] gl-shaders: Add tone mapping shaders This commit adds the following tone mapping shaders: HDR->HDR: ======== The tone mapping employs linear luminance scaling based on the maximum display luminance. It appies the scaling as follows: Range = content_max_luma - content_min_luma out_luma = disp_max_luma * ( in_luma - content_min_luma / Range) SDR->HDR: ======== The current implementation of SDR to HDR tone mapping shader uses a gamma function and produces a maximum output of 1000.0 nits. Signed-off-by: Harish Krupo HDR->SDR: ======== The HDR to SDR luminance mapping is applied using the hable tone mapping operator. Reference: http://filmicworlds.com/blog/filmic-tonemapping-operators/ Luminance Scaling: ================= This patch also adds shaders to scale/normalize luminance based on the encoded eotf/oetf curve. Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer-private.h | 12 ++ libweston/renderer-gl/gl-shaders.c | 185 +++++++++++++++++++- 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/libweston/renderer-gl/gl-renderer-private.h b/libweston/renderer-gl/gl-renderer-private.h index 8b41872e4..a0864e5b0 100644 --- a/libweston/renderer-gl/gl-renderer-private.h +++ b/libweston/renderer-gl/gl-renderer-private.h @@ -60,13 +60,22 @@ enum gl_shader_gamma_variant { SHADER_GAMMA_HLG, }; +enum gl_shader_tone_map_variant { + SHADER_TONE_MAP_NONE = 0, + SHADER_TONE_MAP_HDR_TO_SDR, + SHADER_TONE_MAP_SDR_TO_HDR, + SHADER_TONE_MAP_HDR_TO_HDR, +}; + struct gl_shader_requirements { enum gl_shader_texture_variant variant; bool debug; bool csc_matrix; enum gl_shader_degamma_variant degamma; + enum gl_shader_gamma_variant nl_variant; enum gl_shader_gamma_variant gamma; + enum gl_shader_tone_map_variant tone_mapping; }; struct gl_shader { @@ -78,6 +87,9 @@ struct gl_shader { GLint alpha_uniform; GLint color_uniform; GLint csc_uniform; + GLint display_max_luminance; + GLint content_max_luminance; + GLint content_min_luminance; struct wl_list link; /* gl_renderer::shader_list */ }; diff --git a/libweston/renderer-gl/gl-shaders.c b/libweston/renderer-gl/gl-shaders.c index 11eca084e..a4aab6780 100644 --- a/libweston/renderer-gl/gl-shaders.c +++ b/libweston/renderer-gl/gl-shaders.c @@ -246,6 +246,146 @@ static const char oetf_shader[] = static const char csc_shader[] = " gl_FragColor.rgb = clamp((csc * gl_FragColor.rgb), 0.0, 1.0);\n" ; + +static const char hdr_uniforms[] = + "uniform float display_max_luminance;\n" + "uniform float content_max_luminance;\n" + "uniform float content_min_luminance;\n" + ; + +#define LUMINANCE_FROM_RGB \ + " // These are ITU 2100 recommendations\n" \ + " float kr = 0.2627;\n" \ + " float kb = 0.0593;\n" \ + " float kg = 1.0 - kr - kb;\n" \ + " float luma = dot(color, vec3(kr, kg, kb));\n" \ + +/* Luminance scaling */ + +static const char sl_srgb[] = + "vec3 ScaleLuminance(vec3 color) {\n" + " return color * display_max_luminance;\n" + "}\n" + "\n" + ; + +static const char sl_pq[] = + "vec3 ScaleLuminance(vec3 color) {\n" + " return color * 10000.0;\n" + "}\n" + "\n" + ; + +static const char sl_hlg[] = + "vec3 ScaleLuminance(vec3 color) {\n" + LUMINANCE_FROM_RGB + " return color * 1000.0 * pow(luma, 0.2);\n" + "}\n" + "\n" + ; + +/* Luminance Normalization */ + +static const char nl_srgb[] = + "vec3 NormalizeLuminance(vec3 color) {\n" + " return color / display_max_luminance;\n" + "}\n" + "\n" + ; + +static const char nl_pq[] = + "vec3 NormalizeLuminance(vec3 color) {\n" + " return color / 10000.0;\n" + "}\n" + "\n" + ; + +static const char nl_hlg[] = + "vec3 NormalizeLuminance(vec3 color) {\n" + LUMINANCE_FROM_RGB + " return (color / 1000.0) * pow(luma, -0.2);\n" + "}\n" + "\n" + ; + +static const char sl_shader[] = + " gl_FragColor.rgb = ScaleLuminance(gl_FragColor.rgb);\n" + ; + +static const char nl_shader[] = + " gl_FragColor.rgb = NormalizeLuminance(gl_FragColor.rgb);\n" + ; + +/* Tone mapping Shaders */ + +static const char hdr_shader[] = + " gl_FragColor.rgb = tone_mapping(gl_FragColor.rgb);\n" + ; + +/* No tone mapping */ +static const char noop_tm[] = + "vec3 tone_mapping(vec3 color) {\n" + " return color;\n" + "}\n" + "\n" + ; + +/* HDR->SDR */ +static const char hdr_to_sdr_tm[] = + "vec3 hable_curve(vec3 c) {\n" + " float A = 0.15;\n" + " float B = 0.50;\n" + " float C = 0.10;\n" + " float D = 0.20;\n" + " float E = 0.02;\n" + " float F = 0.30;\n" + " vec3 numerator = (c * (A * c + C * B) + D * E);\n" + " vec3 denominator = (c * (A * c + B) + D * F);\n" + " c = (numerator / denominator) - E / F;\n" + " return c;\n" + "}\n" + "\n" + "vec3 tone_mapping(vec3 color) {\n" + " float W = 11.2;\n" + " float exposure = 100.0;\n" + " color *= exposure;\n" + " color = hable_curve(color);\n" + " float white = hable_curve(vec3(W, 0, 0)).x;\n" + " color /= white;\n" + " return color;\n" + "}\n" + "\n" + ; + +static const char sdr_to_hdr_tm[] = + "vec3 tone_mapping(vec3 color) {\n" + LUMINANCE_FROM_RGB + " highp float tone_mapped_luma = 0.0;" + "\n" + " if (luma > 5.0) {\n" + " tone_mapped_luma = luma / display_max_luminance;\n" + " tone_mapped_luma = pow(tone_mapped_luma, 1.5);\n" + " tone_mapped_luma *= display_max_luminance;\n" + " color *= tone_mapped_luma / luma;\n" + " }\n" + " return color;\n" + "}\n" + "\n" + ; + +static const char hdr_to_hdr_tm[] = + "vec3 tone_mapping(vec3 color) {\n" + " float range = content_max_luminance - content_min_luminance;\n" + LUMINANCE_FROM_RGB + " float tone_mapped_luma = luma - content_min_luminance;\n" + " tone_mapped_luma /= range;\n" + " tone_mapped_luma *= display_max_luminance;\n" + " color *= tone_mapped_luma / luma;\n" + " return color;\n" + "}\n" + "\n" + ; + struct gl_shader_source { const char *parts[64]; uint32_t len; @@ -266,16 +406,21 @@ generate_fs_hdr_shader(struct gl_shader_source *shader_source, if (requirements->csc_matrix) gl_shader_source_add(shader_source, "uniform mat3 csc;\n"); + gl_shader_source_add(shader_source, hdr_uniforms); + // Choose the EOTF switch (requirements->degamma) { case SHADER_DEGAMMA_SRGB: gl_shader_source_add(shader_source, eotf_srgb); + gl_shader_source_add(shader_source, sl_srgb); break; case SHADER_DEGAMMA_PQ: gl_shader_source_add(shader_source, eotf_pq); + gl_shader_source_add(shader_source, sl_pq); break; case SHADER_DEGAMMA_HLG: gl_shader_source_add(shader_source, eotf_hlg); + gl_shader_source_add(shader_source, sl_hlg); break; default: gl_shader_source_add(shader_source, eotf_default); @@ -283,32 +428,64 @@ generate_fs_hdr_shader(struct gl_shader_source *shader_source, } // Choose the OETF - switch (requirements->gamma) { + switch (requirements->gamma | requirements->nl_variant) { case SHADER_GAMMA_SRGB: gl_shader_source_add(shader_source, oetf_srgb); + gl_shader_source_add(shader_source, nl_srgb); break; case SHADER_GAMMA_PQ: gl_shader_source_add(shader_source, oetf_pq); + gl_shader_source_add(shader_source, nl_pq); break; case SHADER_GAMMA_HLG: gl_shader_source_add(shader_source, oetf_hlg); + gl_shader_source_add(shader_source, nl_hlg); break; default: gl_shader_source_add(shader_source, oetf_default); break; } + + // Pick the tone mapping shader variant + switch (requirements->tone_mapping) { + case SHADER_TONE_MAP_NONE: + gl_shader_source_add(shader_source, noop_tm); + break; + case SHADER_TONE_MAP_HDR_TO_SDR: + gl_shader_source_add(shader_source, hdr_to_sdr_tm); + break; + case SHADER_TONE_MAP_SDR_TO_HDR: + gl_shader_source_add(shader_source, sdr_to_hdr_tm); + break; + case SHADER_TONE_MAP_HDR_TO_HDR: + gl_shader_source_add(shader_source, hdr_to_hdr_tm); + break; + } } static void generate_hdr_process_shader(struct gl_shader_source *shader_source, struct gl_shader_requirements *requirements) { + uint32_t need_range_increment = + (requirements->tone_mapping == SHADER_TONE_MAP_HDR_TO_HDR) || + (requirements->tone_mapping == SHADER_TONE_MAP_SDR_TO_HDR); + if (requirements->degamma) gl_shader_source_add(shader_source, eotf_shader); if (requirements->csc_matrix) gl_shader_source_add(shader_source, csc_shader); + if (requirements->degamma && need_range_increment) + gl_shader_source_add(shader_source, sl_shader); + + if (requirements->tone_mapping) + gl_shader_source_add(shader_source, hdr_shader); + + if (requirements->nl_variant) + gl_shader_source_add(shader_source, nl_shader); + if (requirements->gamma) gl_shader_source_add(shader_source, oetf_shader); @@ -534,6 +711,12 @@ gl_shader_create(struct gl_shader_generator *sg, shader->alpha_uniform = glGetUniformLocation(shader->program, "alpha"); shader->color_uniform = glGetUniformLocation(shader->program, "color"); shader->csc_uniform = glGetUniformLocation(shader->program, "csc"); + shader->display_max_luminance = + glGetUniformLocation(shader->program, "display_max_luminance"); + shader->content_max_luminance = + glGetUniformLocation(shader->program, "content_max_luminance"); + shader->content_min_luminance = + glGetUniformLocation(shader->program, "content_min_luminance"); return shader; } -- GitLab From a2f49301983c355e87a535c36aa5d251dbedc536 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Mon, 11 Mar 2019 07:47:16 +0530 Subject: [PATCH 20/29] gl-renderer: Generate HDR shaders based on requirement The HDR tone mapping shaders (HDR->HDR, SDR->HDR, HDR->SDR) are generated as follows: if (display has hdr metadata) if (surface has hdr metadata) Apply HDR->HDR tone mapping else Apply SDR->HDR tone mapping else if (surface has hdr metadata) Apply HDR->SDR tone mapping else Skip tone mapping Signed-off-by: Harish Krupo --- libweston/renderer-gl/gl-renderer.c | 60 ++++++++++++++++++++++++++++- 1 file changed, 58 insertions(+), 2 deletions(-) diff --git a/libweston/renderer-gl/gl-renderer.c b/libweston/renderer-gl/gl-renderer.c index b02590abb..951416d69 100644 --- a/libweston/renderer-gl/gl-renderer.c +++ b/libweston/renderer-gl/gl-renderer.c @@ -886,10 +886,16 @@ shader_uniforms(struct gl_shader *shader, struct gl_surface_state *gs = get_surface_state(view->surface); struct gl_output_state *go = get_output_state(output); struct weston_surface *surface = view->surface; + struct weston_hdr_metadata *src_md = surface->hdr_metadata; + struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; + struct weston_hdr_metadata_static *static_metadata; const struct weston_colorspace *src_cs, *dst_cs; struct weston_matrix csc_matrix; float csc[9] = {0}; float *dst; + uint32_t display_max_luminance; + uint32_t content_max_luminance; + uint32_t content_min_luminance; // shader key contains the set of requirements used to build the shader struct gl_shader_requirements *requirements = &shader->key; @@ -916,6 +922,26 @@ shader_uniforms(struct gl_shader *shader, glUniformMatrix3fv(shader->csc_uniform, 1, GL_FALSE, csc); } + switch(requirements->tone_mapping) { + case SHADER_TONE_MAP_HDR_TO_HDR: + static_metadata = &src_md->metadata.static_metadata; + content_max_luminance = static_metadata->max_luminance; + content_min_luminance = static_metadata->min_luminance; + glUniform1f(shader->content_max_luminance, + content_max_luminance); + glUniform1f(shader->content_min_luminance, + content_min_luminance); + /* fallthrough */ + case SHADER_TONE_MAP_SDR_TO_HDR: + static_metadata = &dst_md->metadata.static_metadata; + display_max_luminance = static_metadata->max_luminance; + glUniform1f(shader->display_max_luminance, + display_max_luminance); + break; + default: + glUniform1f(shader->display_max_luminance, 1.0); + break; + } } static int @@ -995,12 +1021,18 @@ compute_hdr_requirements_from_view(struct weston_view *ev, struct weston_hdr_metadata *src_md = surface->hdr_metadata; struct weston_hdr_metadata *dst_md = go->target_hdr_metadata; uint32_t target_colorspace = go->target_colorspace; - bool needs_csc = false; + bool needs_csc = false, needs_tm = false; uint32_t degamma = 0, gamma = 0; + enum gl_shader_tone_map_variant tone_map_type = SHADER_TONE_MAP_NONE; - // Start by assuming that we don't need color space conversion + /* Start by assuming that we don't need color space conversion */ + /* and tone mapping. This resets the csc and tone mapping requirements */ + /* when the hdr requirements for the surface are removed. */ gs->shader_requirements.csc_matrix = false; + gs->shader_requirements.tone_mapping = SHADER_TONE_MAP_NONE; + needs_csc = surface->colorspace != target_colorspace; + needs_tm = src_md || dst_md; if (needs_csc) gs->shader_requirements.csc_matrix = true; @@ -1021,6 +1053,22 @@ compute_hdr_requirements_from_view(struct weston_view *ev, gs->shader_requirements.degamma = degamma; + if (needs_tm) { + if (dst_md) { + if (src_md) + tone_map_type = SHADER_TONE_MAP_HDR_TO_HDR; + else + tone_map_type = SHADER_TONE_MAP_SDR_TO_HDR; + } else { + if (src_md) + tone_map_type = SHADER_TONE_MAP_HDR_TO_SDR; + else + tone_map_type = SHADER_TONE_MAP_NONE; + } + } + + gs->shader_requirements.tone_mapping = tone_map_type; + // If RGBA16F textures aren't supported, then we are forced to go for // non linear blending if (!gr->supports_half_float_texture) { @@ -1036,6 +1084,7 @@ compute_hdr_requirements_from_view(struct weston_view *ev, } } + gs->shader_requirements.nl_variant = gamma; gs->shader_requirements.gamma = gamma; } } @@ -1367,6 +1416,9 @@ draw_output_borders(struct weston_output *output, } shader_requirements.gamma = gamma; + if (dst_md) + shader_requirements.tone_mapping = SHADER_TONE_MAP_SDR_TO_HDR; + use_gl_program(gr, &shader_requirements); glViewport(0, 0, full_width, full_height); @@ -1379,6 +1431,8 @@ draw_output_borders(struct weston_output *output, glUniform1i(gr->current_shader->tex_uniforms[0], 0); glUniform1f(gr->current_shader->alpha_uniform, 1); + glUniform1f(gr->current_shader->display_max_luminance, 1.0); + glActiveTexture(GL_TEXTURE0); if (border_status & BORDER_TOP_DIRTY) @@ -1538,6 +1592,7 @@ repaint_from_texture(struct weston_output *output, gl_shader_requirements_init(&shader_requirements); shader_requirements.variant = SHADER_VARIANT_RGBA; + shader_requirements.nl_variant = gamma; shader_requirements.gamma = gamma; use_gl_program(gr, &shader_requirements); @@ -1545,6 +1600,7 @@ repaint_from_texture(struct weston_output *output, glUniform1f(gr->current_shader->alpha_uniform, 1.0f); glUniform1i(gr->current_shader->tex_uniforms[0], 0); + glUniform1f(gr->current_shader->display_max_luminance, 1.0); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, go->shadow_tex); -- GitLab From d566283b94c3d71c1ecdc77e64303b1b72bb5786 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Fri, 8 Mar 2019 20:54:19 +0530 Subject: [PATCH 21/29] pixel-formats: Add P010 in pixel formats This patch adds details for P010 pixel format, in the formats list. This will allow us to play 10-bit HDR P010 videos. Signed-off-by: Shashank Sharma --- libweston/pixel-formats.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libweston/pixel-formats.c b/libweston/pixel-formats.c index cade24226..5b60ce08d 100644 --- a/libweston/pixel-formats.c +++ b/libweston/pixel-formats.c @@ -52,6 +52,10 @@ #define SAMPLER_TYPE(type) .sampler_type = 0 #endif +#ifndef DRM_FORMAT_P010 +#define DRM_FORMAT_P010 fourcc_code('P', '0', '1', '0') /* 2x2 subsampled Cb:Cr plane 10 bits per channel */ +#endif + #define DRM_FORMAT(f) .format = DRM_FORMAT_ ## f, .drm_format_name = #f #include "weston-egl-ext.h" @@ -348,6 +352,14 @@ static const struct pixel_format_info pixel_format_table[] = { .hsub = 2, .vsub = 2, }, + { + DRM_FORMAT(P010), + SAMPLER_TYPE(EGL_TEXTURE_Y_UV_WL), + .num_planes = 2, + .chroma_order = ORDER_UV, + .hsub = 2, + .vsub = 2, + }, { DRM_FORMAT(YUV422), SAMPLER_TYPE(EGL_TEXTURE_Y_U_V_WL), -- GitLab From 78a51b9534c8686100e2fc7b65f5ab92e3e45606 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 Apr 2019 14:35:35 +0530 Subject: [PATCH 22/29] compositor-drm: Parse HDR metadata from EDID This patch is the first patch of HDR metadata enabling series for drm-compositor. It does the following: - Defines a new data structure to capture display's HDR metadata related information from its EDID. - Places this new data structures in drm-head. - Adds a new file, which contains functions to parse EDID's CEA extension block and extract static HDR metadata data (CEA-861-G). - While parsing EDID, parses hdr-metadata and saves into drm-head. V2: Addressed review comments from Pekka Signed-off-by: Shashank Sharma --- libweston/compositor-drm.c | 57 ++++++++++ libweston/drm-hdr-metadata.c | 200 +++++++++++++++++++++++++++++++++++ libweston/drm-hdr-metadata.h | 49 +++++++++ libweston/meson.build | 1 + 4 files changed, 307 insertions(+) create mode 100644 libweston/drm-hdr-metadata.c create mode 100644 libweston/drm-hdr-metadata.h diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 7517f2fc2..14ad64ed3 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -67,6 +67,7 @@ #include "linux-dmabuf.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" #include "linux-explicit-synchronization.h" +#include "drm-hdr-metadata.h" #ifndef DRM_CLIENT_CAP_ASPECT_RATIO #define DRM_CLIENT_CAP_ASPECT_RATIO 4 @@ -510,6 +511,46 @@ struct drm_plane { } formats[]; }; +/* CTA-861-G: HDR Metadata names and types */ +enum drm_hdr_eotf_type { + DRM_EOTF_SDR_TRADITIONAL = 0, + DRM_EOTF_HDR_TRADITIONAL, + DRM_EOTF_HDR_ST2084, + DRM_EOTF_HLG_BT2100, + DRM_EOTF_MAX +}; + +enum drm_colorspace { + DRM_COLORSPACE_INVALID, + DRM_COLORSPACE_REC709, + DRM_COLORSPACE_DCIP3, + DRM_COLORSPACE_REC2020, + DRM_COLORSPACE_MAX, +}; + +/* Static HDR metadata to be sent to kernel, matches kernel structure */ +struct drm_hdr_metadata_static { + uint8_t eotf; + uint8_t metadata_type; + struct { + uint16_t x, y; + } display_primaries[3]; + struct { + uint16_t x, y; + } white_point; + uint16_t max_display_mastering_luminance; + uint16_t min_display_mastering_luminance; + uint16_t max_cll; + uint16_t max_fall; +}; + +struct hdr_output_metadata { + uint32_t metadata_type; + union { + struct drm_hdr_metadata_static static_md; + }; +}; + struct drm_head { struct weston_head base; struct drm_backend *backend; @@ -518,6 +559,12 @@ struct drm_head { uint32_t connector_id; struct drm_edid edid; + /* Display's static HDR metadata */ + struct drm_edid_hdr_metadata_static *hdr_md; + + /* Display's supported color spaces */ + uint32_t clrspaces; + /* Holds the properties for the connector */ struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT]; @@ -5421,6 +5468,13 @@ find_and_parse_output_edid(struct drm_head *head, if (head->edid.serial_number[0] != '\0') *serial_number = head->edid.serial_number; } + + if (head->hdr_md) + drm_release_hdr_metadata(head->hdr_md); + head->hdr_md = drm_get_display_hdr_metadata(edid_blob->data, + edid_blob->length); + head->clrspaces = drm_get_display_clrspace(edid_blob->data, + edid_blob->length); drmModeFreePropertyBlob(edid_blob); } @@ -6570,6 +6624,9 @@ drm_head_destroy(struct drm_head *head) drm_property_info_free(head->props_conn, WDRM_CONNECTOR__COUNT); drmModeFreeConnector(head->connector); + if (head->hdr_md) + drm_release_hdr_metadata(head->hdr_md); + if (head->backlight) backlight_destroy(head->backlight); diff --git a/libweston/drm-hdr-metadata.c b/libweston/drm-hdr-metadata.c new file mode 100644 index 000000000..0dea2a58e --- /dev/null +++ b/libweston/drm-hdr-metadata.c @@ -0,0 +1,200 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +#include +#include +#include "drm-hdr-metadata.h" +#include "shared/helpers.h" + +#define EDID_BLOCK_LENGTH 128 +#define EDID_CEA_EXT_ID 0x02 +#define EDID_CEA_TAG_EXTENDED 0x7 + +/* CEA-861-G new EDID blocks for HDR */ +#define EDID_CEA_TAG_COLORIMETRY 0x5 +#define EDID_CEA_EXT_TAG_STATIC_METADATA 0x6 +#define EDID_CEA_EXT_TAG_DYNAMIC_METADATA 0x7 + +static const uint8_t * +edid_find_cea_extension_block(const uint8_t *edid) +{ + uint8_t ext_blks; + int blk; + const uint8_t *ext = NULL; + + if (!edid) { + weston_log("No EDID\n"); + return NULL; + } + + ext_blks = edid[126]; + if (!ext_blks) { + weston_log("EDID doesn't have any extension block\n"); + return NULL; + } + + for (blk = 0; blk < ext_blks; blk++) { + ext = edid + EDID_BLOCK_LENGTH * (blk + 1); + if (ext[0] == EDID_CEA_EXT_ID) + break; + } + + if (blk == ext_blks) + return NULL; + + return ext; +} + +static const uint8_t * +edid_find_extended_data_block(const uint8_t *edid, + uint8_t *data_len, + uint32_t block_tag) +{ + uint8_t d; + uint8_t tag; + uint8_t extended_tag; + uint8_t dblen; + + const uint8_t *dbptr; + const uint8_t *cea_db_start; + const uint8_t *cea_db_end; + const uint8_t *cea_ext_blk; + + if (!edid) { + weston_log("No EDID in blob\n"); + return NULL; + } + + cea_ext_blk = edid_find_cea_extension_block(edid); + if (!cea_ext_blk) { + weston_log("No CEA extension block available\n"); + return NULL; + } + + /* CEA DB starts at blk[4] and ends at blk[d] */ + d = cea_ext_blk[2]; + cea_db_start = cea_ext_blk + 4; + cea_db_end = cea_ext_blk + d - 1; + + for (dbptr = cea_db_start; dbptr < cea_db_end; dbptr += (dblen + 1)) { + + /* First data byte contains db length and tag */ + dblen = dbptr[0] & 0x1F; + tag = dbptr[0] >> 5; + + /* Metadata bock is extended tag block */ + if (tag != EDID_CEA_TAG_EXTENDED) + continue; + + /* Extended block uses one extra byte for extended tag */ + extended_tag = dbptr[1]; + if (extended_tag != block_tag) + continue; + + *data_len = dblen - 1; + return dbptr + 2; + } + + return NULL; +} + +static struct drm_edid_hdr_metadata_static * +drm_get_hdr_static_metadata(const uint8_t *hdr_db, uint32_t data_len) +{ + struct drm_edid_hdr_metadata_static *s; + + if (data_len < 2) { + weston_log("Invalid metadata input to static parser\n"); + return NULL; + } + + s = zalloc(sizeof (struct drm_edid_hdr_metadata_static)); + if (!s) { + weston_log("OOM while parsing static metadata\n"); + return NULL; + } + + memset(s, 0, sizeof(struct drm_edid_hdr_metadata_static)); + + s->eotf = hdr_db[0] & 0x3F; + s->metadata_type = hdr_db[1]; + + if (data_len > 2 && data_len < 6) { + s->desired_max_ll = hdr_db[2]; + s->desired_max_fall = hdr_db[3]; + s->desired_min_ll = hdr_db[4]; + + if (!s->desired_max_ll) + s->desired_max_ll = 0xFF; + } + return s; +} + +uint16_t +drm_get_display_clrspace(const uint8_t *edid, uint32_t edid_len) +{ + uint8_t data_len = 0; + const uint8_t *clr_db; + uint16_t clrspaces = 0; + + clr_db = edid_find_extended_data_block(edid, &data_len, + EDID_CEA_TAG_COLORIMETRY); + if (clr_db && data_len != 0) + /* db[4] bit 7 is DCI-P3 support information (added in CTA-861-G) */ + clrspaces = ((!!(clr_db[1] & 0x80)) << 8) | (clr_db[0]); + + return clrspaces; +} + +struct drm_edid_hdr_metadata_static * +drm_get_display_hdr_metadata(const uint8_t *edid, uint32_t edid_len) +{ + uint8_t data_len = 0; + const uint8_t *hdr_db; + struct drm_edid_hdr_metadata_static *md = NULL; + + if (!edid) { + weston_log("Invalid EDID\n"); + return NULL; + } + + hdr_db = edid_find_extended_data_block(edid, &data_len, + EDID_CEA_EXT_TAG_STATIC_METADATA); + if (hdr_db && data_len != 0) { + md = drm_get_hdr_static_metadata(hdr_db, data_len); + if (!md) { + weston_log("Can't find static HDR metadata in EDID\n"); + return NULL; + } + weston_log("Monitor supports HDR\n"); + } + + return md; +} + +void +drm_release_hdr_metadata(struct drm_edid_hdr_metadata_static *md) +{ + free(md); +} diff --git a/libweston/drm-hdr-metadata.h b/libweston/drm-hdr-metadata.h new file mode 100644 index 000000000..f5d7ac440 --- /dev/null +++ b/libweston/drm-hdr-metadata.h @@ -0,0 +1,49 @@ +/* + * Copyright © 2019 Intel Corporation + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef WESTON_DRM_HDR_METADATA_H +#define WESTON_DRM_HDR_METADATA_H + +#include + +/* Monitor's HDR metadata */ +struct drm_edid_hdr_metadata_static { + uint8_t eotf; + uint8_t metadata_type; + uint8_t desired_max_ll; + uint8_t desired_max_fall; + uint8_t desired_min_ll; +}; + +void +drm_release_hdr_metadata(struct drm_edid_hdr_metadata_static *md); + +struct drm_edid_hdr_metadata_static * +drm_get_display_hdr_metadata(const uint8_t *edid, uint32_t edid_len); + +uint16_t +drm_get_display_clrspace(const uint8_t *edid, uint32_t edid_len); + +#endif diff --git a/libweston/meson.build b/libweston/meson.build index 39fd1685e..2514a31f7 100644 --- a/libweston/meson.build +++ b/libweston/meson.build @@ -183,6 +183,7 @@ if get_option('backend-drm') 'libbacklight.c', 'libinput-device.c', 'libinput-seat.c', + 'drm-hdr-metadata.c', linux_dmabuf_unstable_v1_protocol_c, linux_dmabuf_unstable_v1_server_protocol_h, presentation_time_server_protocol_h, -- GitLab From b18d1b08c36936cb2085370c6d16b4806de29947 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 Apr 2019 14:40:26 +0530 Subject: [PATCH 23/29] compositor-drm: Add connector's HDR color property In order to display HDR output properly, we have to set two output DRM properties in drm-backend: - HDR output metadata property. - Output color space property. This patch: - Adds a new connector property, HDR metadata, in drm-backend's connector property list. This property will allow compositor to control the output HDR metadata in form of AVI infoframes. - Also adds a connector's color state structure to maintain a drm head's color correction status per flip. - Sets/Resets/Modifies this color state, to reflect the color corrcetion during atomic flip. PS: The kernel counterpart of this property in the drm framework can be found here: https://patchwork.freedesktop.org/series/25091/ V2: Addressed review comments from Pekka Signed-off-by: Shashank Sharma --- libweston/compositor-drm.c | 51 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 14ad64ed3..b434aa93e 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -229,6 +229,7 @@ enum wdrm_connector_property { WDRM_CONNECTOR_DPMS, WDRM_CONNECTOR_CRTC_ID, WDRM_CONNECTOR_NON_DESKTOP, + WDRM_CONNECTOR_HDR_METADATA, WDRM_CONNECTOR__COUNT }; @@ -264,6 +265,7 @@ static const struct drm_property_info connector_props[] = { }, [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", }, [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", }, + [WDRM_CONNECTOR_HDR_METADATA] = { .name = "HDR_OUTPUT_METADATA", }, }; /** @@ -551,6 +553,16 @@ struct hdr_output_metadata { }; }; +/* Connector's color correction status */ +struct drm_conn_color_state { + bool changed; + bool output_is_hdr; + uint8_t o_cs; + uint8_t o_eotf; + uint32_t hdr_md_blob_id; + struct drm_hdr_metadata_static o_md; +}; + struct drm_head { struct weston_head base; struct drm_backend *backend; @@ -565,6 +577,9 @@ struct drm_head { /* Display's supported color spaces */ uint32_t clrspaces; + /* Connector's color correction status */ + struct drm_conn_color_state color_state; + /* Holds the properties for the connector */ struct drm_property_info props_conn[WDRM_CONNECTOR__COUNT]; @@ -2620,6 +2635,36 @@ plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend, return 0; } +static int +connector_add_color_correction(drmModeAtomicReq *req, + struct drm_head *head, uint32_t *flags) +{ + int ret; + struct drm_conn_color_state *conn_state = &head->color_state; + + if (!conn_state->changed) + return 0; + + if ((int)conn_state->hdr_md_blob_id == -1) + return 0; + + ret = connector_add_prop(req, + head, + WDRM_CONNECTOR_HDR_METADATA, + conn_state->hdr_md_blob_id); + if (ret != 0) { + weston_log("Failed to apply output HDR metadata\n"); + return ret; + } + + *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + + if (!(*flags & DRM_MODE_ATOMIC_TEST_ONLY)) + conn_state->changed = false; + + return 0; +} + static int drm_output_apply_state_atomic(struct drm_output_state *state, drmModeAtomicReq *req, @@ -2655,6 +2700,7 @@ drm_output_apply_state_atomic(struct drm_output_state *state, wl_list_for_each(head, &output->base.head_list, base.output_link) { ret |= connector_add_prop(req, head, WDRM_CONNECTOR_CRTC_ID, output->crtc_id); + ret |= connector_add_color_correction(req, head, flags); } } else { ret |= crtc_add_prop(req, output, WDRM_CRTC_MODE_ID, 0); @@ -6619,6 +6665,8 @@ err_alloc: static void drm_head_destroy(struct drm_head *head) { + struct drm_backend *b = head->backend; + weston_head_release(&head->base); drm_property_info_free(head->props_conn, WDRM_CONNECTOR__COUNT); @@ -6630,6 +6678,9 @@ drm_head_destroy(struct drm_head *head) if (head->backlight) backlight_destroy(head->backlight); + if (head->color_state.hdr_md_blob_id) + drmModeDestroyPropertyBlob(b->drm.fd, head->color_state.hdr_md_blob_id); + free(head); } -- GitLab From a5737bd1dfaf5e2a78f6c6660a058d7309db4248 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 Apr 2019 14:48:19 +0530 Subject: [PATCH 24/29] compositor-drm: Add connector's output colorspace property In order to display accurate HDR output, we need to set the output color space of the content properly.This patch tries to add limited control on output colorspace magagement in DRM backend using connector's output-colorspace property. This patch: - Adds a new property, output colorspace, in the connector's property list, as well as in connector's color state (added in previos patch) - Adds logic to set/reset/modify this property from the context of driving a HDR/SDR content. This patch just adds color correction infrastructure for output colorspace, which will be used by HDR playback scenario later in this series. Using this infrastructure, compositor can color correct many more scenarios involving various other cases. V2: Addressed Pekka's review comments Signed-off-by: Shashank Sharma --- libweston/compositor-drm.c | 80 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index b434aa93e..b8db96779 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -81,6 +81,27 @@ #define GBM_BO_USE_LINEAR (1 << 4) #endif +/* Colorspace values as per CEA spec */ +#define DRM_MODE_COLORIMETRY_DEFAULT 0 + +/* CEA 861 Normal Colorimetry options */ +#define DRM_MODE_COLORIMETRY_NO_DATA 0 +#define DRM_MODE_COLORIMETRY_SMPTE_170M_YCC 1 +#define DRM_MODE_COLORIMETRY_BT709_YCC 2 + +/* CEA 861 Extended Colorimetry Options */ +#define DRM_MODE_COLORIMETRY_XVYCC_601 3 +#define DRM_MODE_COLORIMETRY_XVYCC_709 4 +#define DRM_MODE_COLORIMETRY_SYCC_601 5 +#define DRM_MODE_COLORIMETRY_OPYCC_601 6 +#define DRM_MODE_COLORIMETRY_OPRGB 7 +#define DRM_MODE_COLORIMETRY_BT2020_CYCC 8 +#define DRM_MODE_COLORIMETRY_BT2020_RGB 9 +#define DRM_MODE_COLORIMETRY_BT2020_YCC 10 +/* Additional Colorimetry extension added as part of CTA 861.G */ +#define DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65 11 +#define DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER 12 + /** * A small wrapper to print information into the 'drm-backend' debug scope. * @@ -230,6 +251,7 @@ enum wdrm_connector_property { WDRM_CONNECTOR_CRTC_ID, WDRM_CONNECTOR_NON_DESKTOP, WDRM_CONNECTOR_HDR_METADATA, + WDRM_CONNECTOR_OUTPUT_COLORSPACE, WDRM_CONNECTOR__COUNT }; @@ -256,6 +278,33 @@ static struct drm_property_enum_info dpms_state_enums[] = { }, }; +static struct drm_property_enum_info hdmi_clrspace_enums[] = { + /* For Default case, driver will set the colorspace */ + [DRM_MODE_COLORIMETRY_DEFAULT] = { .name = "Default"}, + /* Standard Definition Colorimetry based on CEA 861 */ + [DRM_MODE_COLORIMETRY_SMPTE_170M_YCC] = { .name = "SMPTE_170M_YCC"}, + [DRM_MODE_COLORIMETRY_BT709_YCC] = { .name = "BT709_YCC"}, + /* Standard Definition Colorimetry based on IEC 61966-2-4 */ + [DRM_MODE_COLORIMETRY_XVYCC_601] = { .name = "XVYCC_601"}, + /* High Definition Colorimetry based on IEC 61966-2-4 */ + [DRM_MODE_COLORIMETRY_XVYCC_709] = { .name = "XVYCC_709"}, + /* Colorimetry based on IEC 61966-2-1/Amendment 1 */ + [DRM_MODE_COLORIMETRY_SYCC_601] = { .name = "SYCC_601"}, + /* Colorimetry based on IEC 61966-2-5 [33] */ + [DRM_MODE_COLORIMETRY_OPYCC_601] = { .name = "opYCC_601"}, + /* Colorimetry based on IEC 61966-2-5 */ + [DRM_MODE_COLORIMETRY_OPRGB] = { .name = "opRGB"}, + /* Colorimetry based on ITU-R BT.2020 */ + [DRM_MODE_COLORIMETRY_BT2020_CYCC] = { .name = "BT2020_CYCC"}, + /* Colorimetry based on ITU-R BT.2020 */ + [DRM_MODE_COLORIMETRY_BT2020_RGB] = { .name = "BT2020_RGB"}, + /* Colorimetry based on ITU-R BT.2020 */ + [DRM_MODE_COLORIMETRY_BT2020_YCC] = { .name = "BT2020_YCC"}, + /* Added as part of Additional Colorimetry Extension in 861.G */ + [DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65] = { .name = "DCI-P3_RGB_D65" }, + [DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER] = { .name = "DCI-P3_RGB_Theater"}, +}; + static const struct drm_property_info connector_props[] = { [WDRM_CONNECTOR_EDID] = { .name = "EDID" }, [WDRM_CONNECTOR_DPMS] = { @@ -266,6 +315,11 @@ static const struct drm_property_info connector_props[] = { [WDRM_CONNECTOR_CRTC_ID] = { .name = "CRTC_ID", }, [WDRM_CONNECTOR_NON_DESKTOP] = { .name = "non-desktop", }, [WDRM_CONNECTOR_HDR_METADATA] = { .name = "HDR_OUTPUT_METADATA", }, + [WDRM_CONNECTOR_OUTPUT_COLORSPACE] = { + .name = "Colorspace", + .enum_values = hdmi_clrspace_enums, + .num_enum_values = 13, + }, }; /** @@ -2635,11 +2689,27 @@ plane_add_damage(drmModeAtomicReq *req, struct drm_backend *backend, return 0; } +/* Return the colorspace values to be going to AVI infoframe */ +static inline uint32_t +to_kernel_colorspace(uint8_t colorspace) +{ + switch(colorspace) { + case DRM_COLORSPACE_DCIP3: + return DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65; + case DRM_COLORSPACE_REC2020: + return DRM_MODE_COLORIMETRY_BT2020_RGB; + case DRM_COLORSPACE_REC709: + default: + return DRM_MODE_COLORIMETRY_DEFAULT; + } +} + static int connector_add_color_correction(drmModeAtomicReq *req, struct drm_head *head, uint32_t *flags) { int ret; + uint32_t kernel_cs; struct drm_conn_color_state *conn_state = &head->color_state; if (!conn_state->changed) @@ -2659,6 +2729,16 @@ connector_add_color_correction(drmModeAtomicReq *req, *flags |= DRM_MODE_ATOMIC_ALLOW_MODESET; + kernel_cs = to_kernel_colorspace(conn_state->o_cs); + ret = connector_add_prop(req, + head, + WDRM_CONNECTOR_OUTPUT_COLORSPACE, + kernel_cs); + if (ret != 0) { + weston_log("Failed to apply output colorspace\n"); + return ret; + } + if (!(*flags & DRM_MODE_ATOMIC_TEST_ONLY)) conn_state->changed = false; -- GitLab From 9e9214b82b43b620b7d50a0f4fe96dc4e891352f Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 Apr 2019 14:55:07 +0530 Subject: [PATCH 25/29] compositor-drm: Prepare connector's color state This patch adds initial code to handle a HDR view in DRM backend, while plane assignment. In order to handle HDR playback, the first step is to identify a HDR view, by identifying a HDR surface. ie, if any of the surface comes with HDR metadata, its a HDR surface, which makes this view a HDR view. Now, once we identify an HDR view, we have to set output colorspace If output is HDR -> set output colorspace as REC2020/DCIP3 If output is SDR -> set output colorspace as REC709/default This patch prepares the required calculations, and prepares the connector's color state to be commited, and reflect these changes in current atomic commit. V2: Addressed Pekka's review comments Signed-off-by: Shashank Sharma --- libweston/compositor-drm.c | 202 ++++++++++++++++++++++++++++++++++++- 1 file changed, 201 insertions(+), 1 deletion(-) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index b8db96779..e41b648d2 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -102,6 +102,19 @@ #define DRM_MODE_COLORIMETRY_DCI_P3_RGB_D65 11 #define DRM_MODE_COLORIMETRY_DCI_P3_RGB_THEATER 12 +/* Colorspace bits */ +#define EDID_CS_BT2020RGB (1 << 7) +#define EDID_CS_BT2020YCC (1 << 6) +#define EDID_CS_BT2020CYCC (1 << 5) +#define EDID_CS_DCIP3 (1 << 15) +#define EDID_CS_HDR_GAMUT_MASK (EDID_CS_BT2020RGB | \ + EDID_CS_BT2020YCC | \ + EDID_CS_BT2020CYCC | \ + EDID_CS_DCIP3) +#define EDID_CS_HDR_CS_BASIC (EDID_CS_BT2020RGB | \ + EDID_CS_DCIP3 | \ + EDID_CS_BT2020YCC) + /** * A small wrapper to print information into the 'drm-backend' debug scope. * @@ -610,7 +623,6 @@ struct hdr_output_metadata { /* Connector's color correction status */ struct drm_conn_color_state { bool changed; - bool output_is_hdr; uint8_t o_cs; uint8_t o_eotf; uint32_t hdr_md_blob_id; @@ -691,6 +703,9 @@ struct drm_output { bool virtual; submit_frame_cb virtual_submit_frame; + + /* HDR sesstion is active */ + bool output_is_hdr; }; static const char *const aspect_ratio_as_string[] = { @@ -4065,6 +4080,170 @@ drm_propose_state_mode_to_string(enum drm_output_propose_state_mode mode) return drm_output_propose_state_mode_as_string[mode]; } +/* Color primaries in HDR metadata are expected to be in power of 0.00002, +* and the maximum value allowed is 1 */ +#define PRIMARY(p) (p > 1 ? 50000 : (p * 50000)) + +/* Whitepoints in HDR metadata are expected to be in power of 0.00010, +* and the maximum value allowed is 1 */ +#define WP_PRIMARY(p) (p > 1 ? 10000 : (p * 10000)) + +static void +drm_prepare_output_hdr_metadata(struct drm_backend *b, + struct drm_head *head, + struct weston_hdr_metadata *surface_md, + struct drm_hdr_metadata_static *out_md) +{ + struct weston_hdr_metadata_static *c_md; + struct drm_edid_hdr_metadata_static *d_md; + + memset(out_md, 0, sizeof(*out_md)); + + c_md = &surface_md->metadata.static_metadata; + d_md = head->hdr_md; + + /* This function gets called only when there is an input HDR surface, + * which means we have to handle only H2S or H2H cases */ + if (d_md) { + out_md->max_cll = c_md->max_cll; + out_md->max_fall = c_md->max_fall; + out_md->max_display_mastering_luminance = c_md->max_luminance; + out_md->min_display_mastering_luminance = c_md->min_luminance; + out_md->white_point.x = WP_PRIMARY(c_md->primaries.white_point.x); + out_md->white_point.y = WP_PRIMARY(c_md->primaries.white_point.y); + out_md->display_primaries[0].x = PRIMARY(c_md->primaries.r.x); + out_md->display_primaries[0].y = PRIMARY(c_md->primaries.r.y); + out_md->display_primaries[1].x = PRIMARY(c_md->primaries.g.x); + out_md->display_primaries[1].y = PRIMARY(c_md->primaries.g.y); + out_md->display_primaries[2].x = PRIMARY(c_md->primaries.b.x); + out_md->display_primaries[0].y = PRIMARY(c_md->primaries.b.y); + out_md->eotf = DRM_EOTF_HDR_ST2084; + out_md->metadata_type = 1; + } +} + +static int +drm_head_prepare_hdr_metadata_blob(struct drm_backend *b, + struct weston_surface *hdr_surf, + struct drm_head *drm_head, + struct drm_conn_color_state *target) +{ + int ret; + uint32_t blob_id = 0; + struct hdr_output_metadata output_metadata = {0,}; + + /* Prepare and setup tone mapping metadata as output metadata */ + drm_prepare_output_hdr_metadata(b, + drm_head, + hdr_surf->hdr_metadata, + &target->o_md); + + memcpy(&output_metadata.static_md, &target->o_md, sizeof (target->o_md)); + + /* create blob to be set during next commit */ + ret = drmModeCreatePropertyBlob(b->drm.fd, + (const void *)&output_metadata, + sizeof(output_metadata), + &blob_id); + if (ret || !blob_id) { + drm_debug(b, "\t\t\t[view] Set HDR blob failed\n"); + memset(&target->o_md, 0, sizeof(target->o_md)); + return -1; + } + + return blob_id; +} + +static int +drm_head_prepare_color_state(struct drm_backend *b, + struct weston_output *output_base, + struct weston_surface *hdr_surf) +{ + int i; + uint32_t blob_id = 0; + struct drm_output *output = to_drm_output(output_base); + struct drm_edid_hdr_metadata_static *display_md; + struct weston_head *w_head = weston_output_get_first_head(&output->base); + struct drm_head *drm_head = to_drm_head(w_head); + struct drm_conn_color_state *target; + enum drm_colorspace target_cs = DRM_COLORSPACE_REC709; + uint8_t target_eotf = DRM_EOTF_SDR_TRADITIONAL; + uint16_t display_cs = drm_head->clrspaces & EDID_CS_HDR_CS_BASIC; + + if (!drm_head) + return 0; + + /* This is an active HDR session, so the state is already set */ + if (output->output_is_hdr) + return 0; + + /* Check if setting output HDR metadata is supported */ + for (i = 0; i < WDRM_CONNECTOR__COUNT; i++) { + struct drm_property_info *info = &drm_head->props_conn[i]; + + if (info && !strcmp(info->name, "HDR_OUTPUT_METADATA")) + goto found_hdr; + } + + /* Can't find HDR metadata property */ + weston_log("Warning: No support for HDR metadata property\n"); + return -1; + +found_hdr: + /* Check if output colorspace setting is supported */ + for (i = 0; i < WDRM_CONNECTOR__COUNT; i++) { + struct drm_property_info *info = &drm_head->props_conn[i]; + + if (info && !strcmp(info->name, "Colorspace")) + goto found_cs; + } + + /* Can't find colorspace property */ + weston_log("Warning: No support for output colorspace property\n"); + return -1; + +found_cs: + display_md = drm_head->hdr_md; + target = &drm_head->color_state; + + if (display_md && display_cs) { + /* Display is HDR and supports basic HDR wide gamuts */ + target_eotf = DRM_EOTF_HDR_ST2084; + if (display_cs & EDID_CS_BT2020RGB) + target_cs = DRM_COLORSPACE_REC2020; + else + target_cs = DRM_COLORSPACE_DCIP3; + } + + blob_id = drm_head_prepare_hdr_metadata_blob(b, hdr_surf, drm_head, target); + if (blob_id <= 0) { + drm_debug(b, "\t\t\t[view] failed to setup output hdr metadata\n"); + return -1; + } + + /* TODO: Setup output gamma here */ + target->o_eotf = target_eotf; + target->o_cs = target_cs; + target->changed = true; + target->hdr_md_blob_id = blob_id; + return 0; +} + + +static inline struct weston_surface * +drm_get_first_hdr_surface(struct weston_output *output_base) +{ + struct weston_view *ev; + struct weston_surface *hdr_surface = NULL; + /* Check if we are dealing with HDR view */ + wl_list_for_each(ev, &output_base->compositor->view_list, link) { + hdr_surface = ev->surface; + if (hdr_surface && hdr_surface->hdr_metadata) + return hdr_surface; + } + return NULL; +} + static void drm_assign_planes(struct weston_output *output_base, void *repaint_data) { @@ -4076,10 +4255,31 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data) struct weston_view *ev; struct weston_plane *primary = &output_base->compositor->primary_plane; enum drm_output_propose_state_mode mode = DRM_OUTPUT_PROPOSE_STATE_PLANES_ONLY; + struct weston_surface *hdr_surface = NULL; + int ret; drm_debug(b, "\t[repaint] preparing state for output %s (%lu)\n", output_base->name, (unsigned long) output_base->id); + hdr_surface = drm_get_first_hdr_surface(output_base); + if (hdr_surface) { + + /* This is a HDR view, handle output HDR metadata */ + ret = drm_head_prepare_color_state(b, output_base, hdr_surface); + if (ret) + weston_log("Failed to create HDR target\n"); + output->output_is_hdr = true; + } else { + /* If this is first SDR view after a HDR view, do following: + * Reset the output HDR metadata + * Reset the output colorspace + */ + if (output->output_is_hdr) { + drm_head_reset_color_state(b, output_base); + output->output_is_hdr = false; + } + } + if (!b->sprites_are_broken && !output->virtual) { drm_debug(b, "\t[repaint] trying planes-only build state\n"); state = drm_output_propose_state(output_base, pending_state, mode); -- GitLab From 44969a45ac5aa4ee1695313193789fc5bcce3183 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Thu, 28 Mar 2019 21:26:33 +0530 Subject: [PATCH 26/29] compositor-drm: Reset HDR state This patch handles a special situation, which is resetting of HDR state. Consider this case, when HDR playback stops, and now we are moving back to SDR desktop view. In this case, we need to reset the HDR properties and color corrections applied on the connector state. This patch detects if this view is end of HDR sesstion, and resets the connector state. Signed-off-by: Shashank Sharma --- libweston/compositor-drm.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index e41b648d2..d1f7b817f 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -4244,6 +4244,22 @@ drm_get_first_hdr_surface(struct weston_output *output_base) return NULL; } +static void +drm_head_reset_color_state(struct drm_backend *b, + struct weston_output *output_base) +{ + struct weston_head *w_head = weston_output_get_first_head(output_base); + struct drm_head *head = to_drm_head(w_head); + struct drm_conn_color_state *cs; + + if (!head) + return; + + cs = &head->color_state; + memset(cs, 0, sizeof(*cs)); + cs->changed = 1; +} + static void drm_assign_planes(struct weston_output *output_base, void *repaint_data) { -- GitLab From 496017212e6911bfc4c35f544f0924bc662b6e76 Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Fri, 15 Mar 2019 09:31:31 +0530 Subject: [PATCH 27/29] compositor-drm: Set HDR metadata for renderer Use the renderer's set_output_{colorspace, hdr_metadata} hooks to set the target colorspace and target hdr_metadata. Signed-off-by: Harish Krupo --- libweston/compositor-drm.c | 47 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index d1f7b817f..68501d69c 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -2222,6 +2222,31 @@ err: return NULL; } +static uint32_t +drm_cs_to_weston_cs(uint32_t drm_cs) { + switch (drm_cs) { + case DRM_COLORSPACE_REC709: + return WESTON_CS_BT709; + case DRM_COLORSPACE_DCIP3: + return WESTON_CS_DCI_P3; + case DRM_COLORSPACE_REC2020: + return WESTON_CS_BT2020; + } + + return WESTON_CS_BT709; +} + +static uint32_t +drm_eotf_to_weston_eotf(uint32_t drm_eotf) { + switch (drm_eotf) { + case DRM_EOTF_HDR_ST2084: + return WESTON_EOTF_ST2084; + case DRM_EOTF_HLG_BT2100: + return WESTON_EOTF_HLG; + } + return WESTON_EOTF_TRADITIONAL_GAMMA_SDR; +} + static struct drm_fb * drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) { @@ -2229,6 +2254,28 @@ drm_output_render_gl(struct drm_output_state *state, pixman_region32_t *damage) struct drm_backend *b = to_drm_backend(output->base.compositor); struct gbm_bo *bo; struct drm_fb *ret; + struct weston_head *w_head = weston_output_get_first_head(&output->base); + struct drm_head *head = to_drm_head(w_head); + struct drm_hdr_metadata_static *dmd = &head->color_state.o_md; + uint32_t target_cs = drm_cs_to_weston_cs(head->color_state.o_cs); + struct weston_hdr_metadata whm = {0}; + struct weston_renderer *renderer = output->base.compositor->renderer; + + // If we have eotf other than SDR gamma, then set HDR properties for the + // renderer + if (dmd->eotf) { + whm.metadata.static_metadata.eotf = + drm_eotf_to_weston_eotf(dmd->eotf); + + whm.metadata.static_metadata.max_luminance = + dmd->max_display_mastering_luminance; + + renderer->set_output_colorspace(&output->base, target_cs); + renderer->set_output_hdr_metadata(&output->base, &whm); + } else { + renderer->set_output_colorspace(&output->base, WESTON_CS_BT709); + renderer->set_output_hdr_metadata(&output->base, NULL); + } output->base.compositor->renderer->repaint_output(&output->base, damage); -- GitLab From 42f8b42c08758e09c6c1c5c0d58fd9590a383c78 Mon Sep 17 00:00:00 2001 From: Shashank Sharma Date: Mon, 22 Apr 2019 15:22:50 +0530 Subject: [PATCH 28/29] compositor-drm: Allow HDR surfaces in render-only mode only As the display sw stack doesn't have support to handle tone-mapping and color corrections for HDR surfaces, do not allow HDR view composition in plane-only/mixed mode, force it in render-only composition mode. Signed-off-by: Shashank Sharma --- libweston/compositor-drm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index 68501d69c..dfe83b4c3 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -4343,7 +4343,7 @@ drm_assign_planes(struct weston_output *output_base, void *repaint_data) } } - if (!b->sprites_are_broken && !output->virtual) { + if (!b->sprites_are_broken && !output->virtual && !hdr_surface) { drm_debug(b, "\t[repaint] trying planes-only build state\n"); state = drm_output_propose_state(output_base, pending_state, mode); if (!state) { -- GitLab From e9e66ef5113b9c4e16a7ab9fd58fb77ec3bd0b0c Mon Sep 17 00:00:00 2001 From: Harish Krupo Date: Tue, 23 Apr 2019 10:05:17 +0530 Subject: [PATCH 29/29] compositor-drm: Reject heads if HDR head exists Don't attach a head to an output which has a head with HDR capabilities. Signed-off-by: Harish Krupo --- libweston/compositor-drm.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libweston/compositor-drm.c b/libweston/compositor-drm.c index dfe83b4c3..d7fdc5690 100644 --- a/libweston/compositor-drm.c +++ b/libweston/compositor-drm.c @@ -5952,6 +5952,7 @@ drm_output_attach_head(struct weston_output *output_base, struct weston_head *head_base) { struct drm_backend *b = to_drm_backend(output_base->compositor); + struct drm_head *head = NULL; if (wl_list_length(&output_base->head_list) >= MAX_CLONED_CONNECTORS) return -1; @@ -5959,6 +5960,16 @@ drm_output_attach_head(struct weston_output *output_base, if (!output_base->enabled) return 0; + /* If we already have a head attached to this output and if the head has + * HDR capabilities, then don't attach any other head */ + + if (wl_list_length(&output_base->head_list) == 1) { + head = to_drm_head(container_of(output_base->head_list.next, + struct weston_head, output_link)); + if (head->hdr_md) + return -1; + } + /* XXX: ensure the configuration will work. * This is actually impossible without major infrastructure * work. */ -- GitLab