gl-renderer: shaders implementation of color mapping function

The following GL extensions provide support for shaders CM:
 -GL_OES_texture_float_linear makes GL_RGB32F linear filterable.
 -GL ES 3.0 provides Texture3D support in GL API.
 -GL_OES_texture_3D provides sampler3D support in ESSL 1.00.

If abovesaid is supported then renderer sets flag WESTON_CAP_COLOR_OPS
which means that all fields in struct weston_color_transform are
supported, for example, 1DLUT and 3DLUT.

Use GL_OES_texture_3D to implement 3DLUT function which
uses trilinear interpolation for pixel processing or bypass as is.
Quote from https://nick-shaw.github.io/cinematiccolor/luts-and-transforms.html
"3D LUTs have long been embraced by color scientists and are one of
the tools commonly used for gamut mapping. In fact, 3D LUTs are used
within ICC profiles to model the complex device behaviors necessary
for accurate color image reproduction".
Quote from https://developer.nvidia.com/gpugems/gpugems2/part-iii-high-quality-rendering/
chapter-24-using-lookup-tables-accelerate-color
is about interpolation: "By generating intermediate results based
on a weighted average of the eight corners of the bounding cube,
this algorithm is typically sufficient for color processing,
and it is implemented in graphics hardware".

Signed-off-by: Vitaly Prosyak <vitaly.prosyak@amd.com>
dev
Vitaly Prosyak 3 years ago
parent cda130e4b0
commit 93c6180c71
  1. 44
      libweston/renderer-gl/fragment.glsl
  2. 3
      libweston/renderer-gl/gl-renderer.c
  3. 98
      libweston/renderer-gl/gl-shader-config-color-transformation.c
  4. 60
      libweston/renderer-gl/gl-shaders.c

@ -2,6 +2,7 @@
* Copyright 2012 Intel Corporation
* Copyright 2015,2019,2021 Collabora, Ltd.
* Copyright 2016 NVIDIA Corporation
* Copyright 2021 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -46,10 +47,18 @@
#define SHADER_COLOR_CURVE_IDENTITY 0
#define SHADER_COLOR_CURVE_LUT_3x1D 1
/* enum gl_shader_color_mapping */
#define SHADER_COLOR_MAPPING_IDENTITY 0
#define SHADER_COLOR_MAPPING_3DLUT 1
#if DEF_VARIANT == SHADER_VARIANT_EXTERNAL
#extension GL_OES_EGL_image_external : require
#endif
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
#extension GL_OES_texture_3D : require
#endif
#ifdef GL_FRAGMENT_PRECISION_HIGH
#define HIGHPRECISION highp
#else
@ -66,6 +75,7 @@ compile_const int c_variant = DEF_VARIANT;
compile_const bool c_input_is_premult = DEF_INPUT_IS_PREMULT;
compile_const bool c_green_tint = DEF_GREEN_TINT;
compile_const int c_color_pre_curve = DEF_COLOR_PRE_CURVE;
compile_const int c_color_mapping = DEF_COLOR_MAPPING;
vec4
yuva2rgba(vec4 yuva)
@ -110,6 +120,11 @@ uniform vec4 unicolor;
uniform HIGHPRECISION sampler2D color_pre_curve_lut_2d;
uniform HIGHPRECISION vec2 color_pre_curve_lut_scale_offset;
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
uniform HIGHPRECISION sampler3D color_mapping_lut_3d;
uniform HIGHPRECISION vec2 color_mapping_lut_scale_offset;
#endif
vec4
sample_input_texture()
{
@ -168,6 +183,12 @@ lut_texcoord(float x, vec2 scale_offset)
return x * scale_offset.s + scale_offset.t;
}
vec3
lut_texcoord(vec3 pos, vec2 scale_offset)
{
return pos * scale_offset.s + scale_offset.t;
}
/*
* Sample a 1D LUT which is a single row of a 2D texture. The 2D texture has
* four rows so that the centers of texels have precise y-coordinates.
@ -199,6 +220,28 @@ color_pre_curve(vec3 color)
}
}
vec3
sample_color_mapping_lut_3d(vec3 color)
{
vec3 pos, ret = vec3(0.0, 0.0, 0.0);
#if DEF_COLOR_MAPPING == SHADER_COLOR_MAPPING_3DLUT
pos = lut_texcoord(color, color_mapping_lut_scale_offset);
ret = texture3D(color_mapping_lut_3d, pos).rgb;
#endif
return ret;
}
vec3
color_mapping(vec3 color)
{
if (c_color_mapping == SHADER_COLOR_MAPPING_IDENTITY)
return color;
else if (c_color_mapping == SHADER_COLOR_MAPPING_3DLUT)
return sample_color_mapping_lut_3d(color);
else /* Never reached, bad c_color_mapping. */
return vec3(1.0, 0.3, 1.0);
}
vec4
color_pipeline(vec4 color)
{
@ -206,6 +249,7 @@ color_pipeline(vec4 color)
color.a *= alpha;
color.rgb = color_pre_curve(color.rgb);
color.rgb = color_mapping(color.rgb);
return color;
}

@ -3977,7 +3977,8 @@ gl_renderer_setup(struct weston_compositor *ec, EGLSurface egl_surface)
if (gr->gl_version >= gr_gl_version(3, 0) &&
weston_check_egl_extension(extensions, "GL_OES_texture_float_linear") &&
weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float")) {
weston_check_egl_extension(extensions, "GL_EXT_color_buffer_half_float") &&
weston_check_egl_extension(extensions, "GL_OES_texture_3D")) {
gr->gl_supports_color_transforms = true;
}

@ -1,5 +1,6 @@
/*
* Copyright 2021 Collabora, Ltd.
* Copyright 2021 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -44,11 +45,22 @@ struct gl_renderer_color_curve {
float offset;
};
struct gl_renderer_color_mapping {
enum gl_shader_color_mapping type;
union {
struct {
GLuint tex3d;
float scale;
float offset;
} lut3d;
};
} ;
struct gl_renderer_color_transform {
struct weston_color_transform *owner;
struct wl_listener destroy_listener;
struct gl_renderer_color_curve pre_curve;
struct gl_renderer_color_mapping mapping;
};
static void
@ -58,10 +70,19 @@ gl_renderer_color_curve_fini(struct gl_renderer_color_curve *gl_curve)
glDeleteTextures(1, &gl_curve->tex);
}
static void
gl_renderer_color_mapping_fini(struct gl_renderer_color_mapping *gl_mapping)
{
if (gl_mapping->type == SHADER_COLOR_MAPPING_3DLUT &&
gl_mapping->lut3d.tex3d)
glDeleteTextures(1, &gl_mapping->lut3d.tex3d);
}
static void
gl_renderer_color_transform_destroy(struct gl_renderer_color_transform *gl_xform)
{
gl_renderer_color_curve_fini(&gl_xform->pre_curve);
gl_renderer_color_mapping_fini(&gl_xform->mapping);
wl_list_remove(&gl_xform->destroy_listener.link);
free(gl_xform);
}
@ -152,6 +173,47 @@ gl_color_curve_lut_3x1d(struct gl_renderer_color_curve *gl_curve,
return true;
}
static bool
gl_3d_lut(struct gl_renderer_color_transform *gl_xform,
struct weston_color_transform *xform)
{
GLuint tex3d;
float *lut;
const unsigned dim_size = xform->mapping.u.lut3d.optimal_len;
lut = calloc(3 * dim_size * dim_size * dim_size, sizeof *lut);
if (!lut)
return false;
xform->mapping.u.lut3d.fill_in(xform, lut, dim_size);
glActiveTexture(GL_TEXTURE0);
glGenTextures(1, &tex3d);
glBindTexture(GL_TEXTURE_3D, tex3d);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
glPixelStorei(GL_UNPACK_SKIP_PIXELS_EXT, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS_EXT, 0);
glPixelStorei(GL_UNPACK_ROW_LENGTH_EXT, 0);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB32F, dim_size, dim_size, dim_size, 0,
GL_RGB, GL_FLOAT, lut);
glBindTexture(GL_TEXTURE_3D, 0);
gl_xform->mapping.type = SHADER_COLOR_MAPPING_3DLUT;
gl_xform->mapping.lut3d.tex3d = tex3d;
gl_xform->mapping.lut3d.scale = (float)(dim_size - 1) / dim_size;
gl_xform->mapping.lut3d.offset = 0.5f / dim_size;
free(lut);
return true;
}
static const struct gl_renderer_color_transform *
gl_renderer_color_transform_from(struct weston_color_transform *xform)
{
@ -160,6 +222,7 @@ gl_renderer_color_transform_from(struct weston_color_transform *xform)
.pre_curve.tex = 0,
.pre_curve.scale = 0.0f,
.pre_curve.offset = 0.0f,
.mapping.type = SHADER_COLOR_MAPPING_IDENTITY,
};
struct gl_renderer_color_transform *gl_xform;
bool ok = false;
@ -190,6 +253,19 @@ gl_renderer_color_transform_from(struct weston_color_transform *xform)
break;
}
if (!ok) {
gl_renderer_color_transform_destroy(gl_xform);
return NULL;
}
switch (xform->mapping.type) {
case WESTON_COLOR_MAPPING_TYPE_IDENTITY:
gl_xform->mapping = no_op_gl_xform.mapping;
ok = true;
break;
case WESTON_COLOR_MAPPING_TYPE_3D_LUT:
ok = gl_3d_lut(gl_xform, xform);
break;
}
if (!ok) {
gl_renderer_color_transform_destroy(gl_xform);
return NULL;
@ -203,6 +279,7 @@ gl_shader_config_set_color_transform(struct gl_shader_config *sconf,
struct weston_color_transform *xform)
{
const struct gl_renderer_color_transform *gl_xform;
bool ret = false;
gl_xform = gl_renderer_color_transform_from(xform);
if (!gl_xform)
@ -213,5 +290,22 @@ gl_shader_config_set_color_transform(struct gl_shader_config *sconf,
sconf->color_pre_curve_lut_scale_offset[0] = gl_xform->pre_curve.scale;
sconf->color_pre_curve_lut_scale_offset[1] = gl_xform->pre_curve.offset;
return true;
sconf->req.color_mapping = gl_xform->mapping.type;
switch (gl_xform->mapping.type) {
case SHADER_COLOR_MAPPING_3DLUT:
sconf->color_mapping.lut3d.tex = gl_xform->mapping.lut3d.tex3d;
sconf->color_mapping.lut3d.scale_offset[0] =
gl_xform->mapping.lut3d.scale;
sconf->color_mapping.lut3d.scale_offset[1] =
gl_xform->mapping.lut3d.offset;
assert(sconf->color_mapping.lut3d.scale_offset[0] > 0.0);
assert(sconf->color_mapping.lut3d.scale_offset[1] > 0.0);
ret = true;
break;
case SHADER_COLOR_MAPPING_IDENTITY:
ret = true;
break;
}
return ret;
}

@ -4,6 +4,7 @@
* Copyright 2016 NVIDIA Corporation
* Copyright 2019 Harish Krupo
* Copyright 2019 Intel Corporation
* Copyright 2021 Advanced Micro Devices, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
@ -60,6 +61,12 @@ struct gl_shader {
GLint color_uniform;
GLint color_pre_curve_lut_2d_uniform;
GLint color_pre_curve_lut_scale_offset_uniform;
union {
struct {
GLint tex_uniform;
GLint scale_offset_uniform;
} lut3d;
} color_mapping;
struct wl_list link; /* gl_renderer::shader_list */
struct timespec last_used;
};
@ -97,6 +104,19 @@ gl_shader_color_curve_to_string(enum gl_shader_color_curve kind)
return "!?!?"; /* never reached */
}
static const char *
gl_shader_color_mapping_to_string(enum gl_shader_color_mapping kind)
{
switch (kind) {
#define CASERET(x) case x: return #x;
CASERET(SHADER_COLOR_MAPPING_IDENTITY)
CASERET(SHADER_COLOR_MAPPING_3DLUT)
#undef CASERET
}
return "!?!?"; /* never reached */
}
static void
dump_program_with_line_numbers(int count, const char **sources)
{
@ -162,9 +182,10 @@ create_shader_description_string(const struct gl_shader_requirements *req)
int size;
char *str;
size = asprintf(&str, "%s %s %cinput_is_premult %cgreen",
size = asprintf(&str, "%s %s %s %cinput_is_premult %cgreen",
gl_shader_texture_variant_to_string(req->variant),
gl_shader_color_curve_to_string(req->color_pre_curve),
gl_shader_color_mapping_to_string(req->color_mapping),
req->input_is_premult ? '+' : '-',
req->green_tint ? '+' : '-');
if (size < 0)
@ -182,10 +203,12 @@ create_shader_config_string(const struct gl_shader_requirements *req)
"#define DEF_GREEN_TINT %s\n"
"#define DEF_INPUT_IS_PREMULT %s\n"
"#define DEF_COLOR_PRE_CURVE %s\n"
"#define DEF_COLOR_MAPPING %s\n"
"#define DEF_VARIANT %s\n",
req->green_tint ? "true" : "false",
req->input_is_premult ? "true" : "false",
gl_shader_color_curve_to_string(req->color_pre_curve),
gl_shader_color_mapping_to_string(req->color_mapping),
gl_shader_texture_variant_to_string(req->variant));
if (size < 0)
return NULL;
@ -268,6 +291,16 @@ gl_shader_create(struct gl_renderer *gr,
shader->color_pre_curve_lut_scale_offset_uniform =
glGetUniformLocation(shader->program, "color_pre_curve_lut_scale_offset");
switch(requirements->color_mapping) {
case SHADER_COLOR_MAPPING_3DLUT:
shader->color_mapping.lut3d.tex_uniform =
glGetUniformLocation(shader->program, "color_mapping_lut_3d");
shader->color_mapping.lut3d.scale_offset_uniform =
glGetUniformLocation(shader->program,"color_mapping_lut_scale_offset");
break;
case SHADER_COLOR_MAPPING_IDENTITY:
break;
}
free(conf);
wl_list_insert(&gr->shader_list, &shader->link);
@ -376,6 +409,7 @@ gl_renderer_create_fallback_shader(struct gl_renderer *gr)
.variant = SHADER_VARIANT_SOLID,
.input_is_premult = true,
.color_pre_curve = SHADER_COLOR_CURVE_IDENTITY,
.color_mapping = SHADER_COLOR_MAPPING_IDENTITY,
};
struct gl_shader *shader;
@ -497,9 +531,8 @@ gl_shader_load_config(struct gl_shader *shader,
glTexParameteri(in_tgt, GL_TEXTURE_MAG_FILTER, in_filter);
}
/* Fixed texture unit for color_pre_curve LUT */
/* Fixed texture unit for color_pre_curve LUT if it is available */
i = GL_SHADER_INPUT_TEX_MAX;
glActiveTexture(GL_TEXTURE0 + i);
switch (sconf->req.color_pre_curve) {
case SHADER_COLOR_CURVE_IDENTITY:
assert(sconf->color_pre_curve_lut_tex == 0);
@ -508,13 +541,32 @@ gl_shader_load_config(struct gl_shader *shader,
assert(sconf->color_pre_curve_lut_tex != 0);
assert(shader->color_pre_curve_lut_2d_uniform != -1);
assert(shader->color_pre_curve_lut_scale_offset_uniform != -1);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, sconf->color_pre_curve_lut_tex);
glUniform1i(shader->color_pre_curve_lut_2d_uniform, i);
i++;
glUniform2fv(shader->color_pre_curve_lut_scale_offset_uniform,
1, sconf->color_pre_curve_lut_scale_offset);
break;
}
switch (sconf->req.color_mapping) {
case SHADER_COLOR_MAPPING_IDENTITY:
break;
case SHADER_COLOR_MAPPING_3DLUT:
assert(shader->color_mapping.lut3d.tex_uniform != -1);
assert(sconf->color_mapping.lut3d.tex != 0);
assert(shader->color_mapping.lut3d.scale_offset_uniform != -1);
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_3D, sconf->color_mapping.lut3d.tex);
glUniform1i(shader->color_mapping.lut3d.tex_uniform, i);
glUniform2fv(shader->color_mapping.lut3d.scale_offset_uniform,
1, sconf->color_mapping.lut3d.scale_offset);
break;
default:
assert(false);
break;
}
}
bool

Loading…
Cancel
Save