vrend: convert linear color to srgb for 24bpp imported EGL resources

Resources imported from dma-bufs as EGL images are given internal format
GL_RGB8 (24bpp) by host mesa. When placing srgb texture views on such
resources, the appropriate view format would be GL_SRGB8, but this is
not color-renderable for GL or GLES and results in an incomplete
framebuffer. Instead, GL_RGB8 must be used as the attachment format and
linear->srgb colorspace conversions must be applied manually in the frag
shader and for calls to glClearColor() involving these surfaces.

This resolves a
dEQP-EGL.functional.wide_color.window_888_colorspace_srgb test failure
for GBM-enabled hosts with newer host mesa, which imports
B8G8R8X8_UNORM and R8G8B8X8_UNORM as 24bpp instead of the more
compatible 32bpp.

Signed-off-by: Ryan Neph <ryanneph@google.com>
Reviewed-by: Lepton Wu <lepton@chromium.org>
Reviewed-by: John Bates <jbates@chromium.org>
macos/master
Ryan Neph 3 years ago committed by Chia-I Wu
parent 673f4d0c1d
commit ebb2cf3c76
  1. 67
      src/vrend_renderer.c
  2. 21
      src/vrend_shader.c
  3. 1
      src/vrend_shader.h

@ -707,6 +707,7 @@ struct vrend_sub_context {
uint32_t abo_used_mask; uint32_t abo_used_mask;
struct vrend_context_tweaks tweaks; struct vrend_context_tweaks tweaks;
uint8_t swizzle_output_rgb_to_bgr; uint8_t swizzle_output_rgb_to_bgr;
uint8_t convert_linear_to_srgb_on_write;
int fake_occlusion_query_samples_passed_multiplier; int fake_occlusion_query_samples_passed_multiplier;
int prim_mode; int prim_mode;
@ -893,6 +894,14 @@ static bool vrend_resource_is_emulated_bgra(struct vrend_resource *res)
return false; return false;
} }
static bool vrend_resource_has_24bpp_internal_format(struct vrend_resource *res)
{
/* Some shared resources imported to guest mesa as EGL images occupy 24bpp instead of more common 32bpp. */
return (has_bit(res->storage_bits, VREND_STORAGE_EGL_IMAGE) &&
(res->base.format == VIRGL_FORMAT_B8G8R8X8_UNORM ||
res->base.format == VIRGL_FORMAT_R8G8B8X8_UNORM));
}
static bool vrend_blit_needs_swizzle(enum virgl_formats src, static bool vrend_blit_needs_swizzle(enum virgl_formats src,
enum virgl_formats dst) enum virgl_formats dst)
{ {
@ -2537,6 +2546,7 @@ static void vrend_hw_emit_framebuffer_state(struct vrend_sub_context *sub_ctx)
surf = sub_ctx->surf[i]; surf = sub_ctx->surf[i];
if (util_format_is_srgb(surf->format)) { if (util_format_is_srgb(surf->format)) {
use_srgb = true; use_srgb = true;
break;
} }
} }
} }
@ -2549,6 +2559,7 @@ static void vrend_hw_emit_framebuffer_state(struct vrend_sub_context *sub_ctx)
} }
sub_ctx->swizzle_output_rgb_to_bgr = 0; sub_ctx->swizzle_output_rgb_to_bgr = 0;
sub_ctx->convert_linear_to_srgb_on_write = 0;
for (int i = 0; i < sub_ctx->nr_cbufs; i++) { for (int i = 0; i < sub_ctx->nr_cbufs; i++) {
if (sub_ctx->surf[i]) { if (sub_ctx->surf[i]) {
struct vrend_surface *surf = sub_ctx->surf[i]; struct vrend_surface *surf = sub_ctx->surf[i];
@ -2557,6 +2568,22 @@ static void vrend_hw_emit_framebuffer_state(struct vrend_sub_context *sub_ctx)
i, util_format_name(surf->format), util_format_name(surf->texture->base.format)); i, util_format_name(surf->format), util_format_name(surf->texture->base.format));
sub_ctx->swizzle_output_rgb_to_bgr |= 1 << i; sub_ctx->swizzle_output_rgb_to_bgr |= 1 << i;
} }
/* [R8G8B8|B8G8R8]X8_UNORM formatted resources imported to mesa as EGL images occupy 24bpp instead of
* more common 32bpp (with an ignored alpha channel). GL_RGB8 internal format must be specified when
* interacting with these textures in the host driver. Unfortunately, GL_SRGB8 is not guaranteed to
* be color-renderable on either GL or GLES, and is typically not supported. Thus, rendering to such
* surfaces by using an SRGB texture view will have no colorspace conversion effects.
* To work around this, manual colorspace conversion is used instead in the fragment shader and
* during glClearColor() setting.
*/
if (vrend_resource_has_24bpp_internal_format(surf->texture) && util_format_is_srgb(surf->format)) {
VREND_DEBUG(dbg_tex, sub_ctx->parent,
"manually converting linear->srgb for EGL-backed framebuffer color attachment 0x%x"
" (surface format is %s; resource format is %s)\n",
i, util_format_name(surf->format), util_format_name(surf->texture->base.format));
sub_ctx->convert_linear_to_srgb_on_write |= 1 << i;
}
} }
} }
@ -3366,6 +3393,7 @@ static inline void vrend_sync_shader_io(struct vrend_sub_context *sub_ctx,
if (type == PIPE_SHADER_FRAGMENT) { if (type == PIPE_SHADER_FRAGMENT) {
key->fs.invert_origin = !sub_ctx->inverted_fbo_content; key->fs.invert_origin = !sub_ctx->inverted_fbo_content;
key->fs.swizzle_output_rgb_to_bgr = sub_ctx->swizzle_output_rgb_to_bgr; key->fs.swizzle_output_rgb_to_bgr = sub_ctx->swizzle_output_rgb_to_bgr;
key->fs.convert_linear_to_srgb_on_write = sub_ctx->convert_linear_to_srgb_on_write;
if (vrend_state.use_gles && can_emulate_logicop(sub_ctx->blend_state.logicop_func)) { if (vrend_state.use_gles && can_emulate_logicop(sub_ctx->blend_state.logicop_func)) {
key->fs.logicop_enabled = sub_ctx->blend_state.logicop_enable; key->fs.logicop_enabled = sub_ctx->blend_state.logicop_enable;
key->fs.logicop_func = sub_ctx->blend_state.logicop_func; key->fs.logicop_func = sub_ctx->blend_state.logicop_func;
@ -3786,6 +3814,13 @@ void vrend_bind_shader(struct vrend_context *ctx,
vrend_shader_state_reference(&sub_ctx->shaders[sel->type], sel); vrend_shader_state_reference(&sub_ctx->shaders[sel->type], sel);
} }
static float
vrend_color_convert_linear_to_srgb(float color) {
return color <= 0.0031308f
? 12.92f * color
: 1.055f * powf(color, (1.f / 2.4f)) - 0.055f;
}
void vrend_clear(struct vrend_context *ctx, void vrend_clear(struct vrend_context *ctx,
unsigned buffers, unsigned buffers,
const union pipe_color_union *color, const union pipe_color_union *color,
@ -3812,14 +3847,29 @@ void vrend_clear(struct vrend_context *ctx,
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
float colorf[4];
memcpy(colorf, color->f, sizeof(colorf));
if (sub_ctx->nr_cbufs && sub_ctx->surf[0] &&
vrend_resource_has_24bpp_internal_format(sub_ctx->surf[0]->texture) &&
util_format_is_srgb(sub_ctx->surf[0]->format)) {
VREND_DEBUG(dbg_tex, ctx,
"manually converting glClearColor from linear->srgb colorspace for EGL-backed framebuffer color attachment"
" (surface format is %s; resource format is %s)\n",
util_format_name(sub_ctx->surf[0]->format),
util_format_name(sub_ctx->surf[0]->texture->base.format));
for (int i = 0; i < 3; ++i) // i < 3: don't convert alpha channel
colorf[i] = vrend_color_convert_linear_to_srgb(colorf[i]);
}
if (buffers & PIPE_CLEAR_COLOR) { if (buffers & PIPE_CLEAR_COLOR) {
if (sub_ctx->nr_cbufs && sub_ctx->surf[0] && vrend_format_is_emulated_alpha(sub_ctx->surf[0]->format)) { if (sub_ctx->nr_cbufs && sub_ctx->surf[0] && vrend_format_is_emulated_alpha(sub_ctx->surf[0]->format)) {
glClearColor(color->f[3], 0.0, 0.0, 0.0); glClearColor(colorf[3], 0.0, 0.0, 0.0);
} else if (sub_ctx->nr_cbufs && sub_ctx->surf[0] && vrend_resource_is_emulated_bgra(sub_ctx->surf[0]->texture)) { } else if (sub_ctx->nr_cbufs && sub_ctx->surf[0] && vrend_resource_is_emulated_bgra(sub_ctx->surf[0]->texture)) {
VREND_DEBUG(dbg_bgra, ctx, "swizzling glClearColor() since rendering surface is an externally-stored BGR* resource\n"); VREND_DEBUG(dbg_bgra, ctx, "swizzling glClearColor() since rendering surface is an externally-stored BGR* resource\n");
glClearColor(color->f[2], color->f[1], color->f[0], color->f[3]); glClearColor(colorf[2], colorf[1], colorf[0], colorf[3]);
} else { } else {
glClearColor(color->f[0], color->f[1], color->f[2], color->f[3]); glClearColor(colorf[0], colorf[1], colorf[2], colorf[3]);
} }
/* This function implements Gallium's full clear callback (st->pipe->clear) on the host. This /* This function implements Gallium's full clear callback (st->pipe->clear) on the host. This
@ -3869,13 +3919,13 @@ void vrend_clear(struct vrend_context *ctx,
i = u_bit_scan(&mask); i = u_bit_scan(&mask);
if (i < PIPE_MAX_COLOR_BUFS && sub_ctx->surf[i] && util_format_is_pure_uint(sub_ctx->surf[i] && sub_ctx->surf[i]->format)) if (i < PIPE_MAX_COLOR_BUFS && sub_ctx->surf[i] && util_format_is_pure_uint(sub_ctx->surf[i] && sub_ctx->surf[i]->format))
glClearBufferuiv(GL_COLOR, glClearBufferuiv(GL_COLOR,
i, (GLuint *)color); i, (GLuint *)colorf);
else if (i < PIPE_MAX_COLOR_BUFS && sub_ctx->surf[i] && util_format_is_pure_sint(sub_ctx->surf[i] && sub_ctx->surf[i]->format)) else if (i < PIPE_MAX_COLOR_BUFS && sub_ctx->surf[i] && util_format_is_pure_sint(sub_ctx->surf[i] && sub_ctx->surf[i]->format))
glClearBufferiv(GL_COLOR, glClearBufferiv(GL_COLOR,
i, (GLint *)color); i, (GLint *)colorf);
else else
glClearBufferfv(GL_COLOR, glClearBufferfv(GL_COLOR,
i, (GLfloat *)color); i, (GLfloat *)colorf);
} }
} }
else else
@ -4377,7 +4427,7 @@ static void vrend_draw_bind_ssbo_shader(struct vrend_sub_context *sub_ctx, int s
ssbo = &sub_ctx->ssbo[shader_type][i]; ssbo = &sub_ctx->ssbo[shader_type][i];
res = (struct vrend_resource *)ssbo->res; res = (struct vrend_resource *)ssbo->res;
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, i, res->id, glBindBufferRange(GL_SHADER_STORAGE_BUFFER, i, res->id,
ssbo->buffer_offset, ssbo->buffer_size); ssbo->buffer_offset, ssbo->buffer_size);
} }
} }
@ -4742,7 +4792,8 @@ int vrend_draw_vbo(struct vrend_context *ctx,
sub_ctx->prim_mode = (int)info->mode; sub_ctx->prim_mode = (int)info->mode;
} }
if (sub_ctx->shader_dirty || sub_ctx->swizzle_output_rgb_to_bgr) if (sub_ctx->shader_dirty || sub_ctx->swizzle_output_rgb_to_bgr ||
sub_ctx->convert_linear_to_srgb_on_write)
new_program = vrend_select_program(sub_ctx, info); new_program = vrend_select_program(sub_ctx, info);
if (!sub_ctx->prog) { if (!sub_ctx->prog) {

@ -2250,6 +2250,24 @@ static void emit_cbuf_swizzle(const struct dump_ctx *ctx,
} }
} }
static void emit_cbuf_colorspace_convert(const struct dump_ctx *ctx,
struct vrend_glsl_strbufs *glsl_strbufs)
{
for (uint i = 0; i < ctx->num_outputs; i++) {
if (ctx->key->fs.convert_linear_to_srgb_on_write & (1 << i)) {
emit_buff(glsl_strbufs,
"{\n"
" vec3 temp = fsout_c%d.xyz;\n"
" bvec3 thresh = lessThanEqual(temp, vec3(0.0031308));\n"
" vec3 a = temp * vec3(12.92);\n"
" vec3 b = ( vec3(1.055) * pow(temp, vec3(1.0/2.4)) ) - vec3(0.055);\n"
" fsout_c%d.xyz = mix(b, a, thresh);\n"
"}\n"
, i, i);
}
}
}
static void handle_fragment_proc_exit(const struct dump_ctx *ctx, static void handle_fragment_proc_exit(const struct dump_ctx *ctx,
struct vrend_glsl_strbufs *glsl_strbufs) struct vrend_glsl_strbufs *glsl_strbufs)
{ {
@ -2269,6 +2287,9 @@ static void handle_fragment_proc_exit(const struct dump_ctx *ctx,
if (ctx->key->fs.swizzle_output_rgb_to_bgr) if (ctx->key->fs.swizzle_output_rgb_to_bgr)
emit_cbuf_swizzle(ctx, glsl_strbufs); emit_cbuf_swizzle(ctx, glsl_strbufs);
if (ctx->key->fs.convert_linear_to_srgb_on_write)
emit_cbuf_colorspace_convert(ctx, glsl_strbufs);
if (ctx->write_all_cbufs) if (ctx->write_all_cbufs)
emit_cbuf_writes(ctx, glsl_strbufs); emit_cbuf_writes(ctx, glsl_strbufs);

@ -145,6 +145,7 @@ struct vrend_shader_key {
uint8_t surface_component_bits[PIPE_MAX_COLOR_BUFS]; uint8_t surface_component_bits[PIPE_MAX_COLOR_BUFS];
uint32_t coord_replace; uint32_t coord_replace;
uint8_t swizzle_output_rgb_to_bgr; uint8_t swizzle_output_rgb_to_bgr;
uint8_t convert_linear_to_srgb_on_write;
uint8_t cbufs_are_a8_bitmask; uint8_t cbufs_are_a8_bitmask;
uint8_t cbufs_signed_int_bitmask; uint8_t cbufs_signed_int_bitmask;
uint8_t cbufs_unsigned_int_bitmask; uint8_t cbufs_unsigned_int_bitmask;

Loading…
Cancel
Save