gl-renderer: Add coefficients and range support to EGLImage paths

`EGL_YUV_COLOR_SPACE_HINT_EXT` and `EGL_SAMPLE_RANGE_HINT_EXT` have
to be set at image import, which again happens on wl_buffer creation.
This is problematic because in Wayland (and Vulkan) those values are
surface attributes and thus only known once the buffer gets attached
to a particular surface.

We solve that by first importing the dmabuf with default values and,
when attaching a buffer to a surface, creating additional EGLImages
as needed.

Note that we ignore `EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT` and
`EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT` for now, which are neither
required by the Wayland color-representation protocol nor supported by
Mesa shaders (as of 25.3).

Signed-off-by: Robert Mader <robert.mader@collabora.com>
This commit is contained in:
Robert Mader 2025-04-24 15:29:05 +02:00
parent b736066dc9
commit e8b7997223

View file

@ -253,6 +253,11 @@ struct yuv_format_descriptor {
struct yuv_plane_descriptor plane[3]; struct yuv_plane_descriptor plane[3];
}; };
struct gl_color_egl_image {
EGLImageKHR image;
struct weston_color_representation import_color_rep;
};
struct gl_buffer_state { struct gl_buffer_state {
struct gl_renderer *gr; struct gl_renderer *gr;
@ -267,6 +272,19 @@ struct gl_buffer_state {
int num_images; int num_images;
enum gl_shader_texture_variant shader_variant; enum gl_shader_texture_variant shader_variant;
struct weston_color_representation egl_image_import_color_rep;
/* For non-default color representations we need to re-import
* EGLImageKHR with different attributes for
* EGL_YUV_COLOR_SPACE_HINT_EXT, EGL_SAMPLE_RANGE_HINT_EXT and, in the
* future, EGL_YUV_CHROMA_HORIZONTAL_SITING_HINT_EXT and
* EGL_YUV_CHROMA_VERTICAL_SITING_HINT_EXT */
struct wl_array reimported_egl_images;
struct gl_color_egl_image *active_reimported_egl_image;
/* These values can refer to the EGLImageKHR's in either images[3] or
* active_reimported_egl_image->image. Textures will get destroyed and
* recreated when switching. */
struct gl_format_info texture_format[3]; struct gl_format_info texture_format[3];
struct gl_texture_parameters parameters[3]; struct gl_texture_parameters parameters[3];
GLuint textures[3]; GLuint textures[3];
@ -888,7 +906,9 @@ gl_renderer_create_renderbuffer(struct weston_output *output,
} }
static EGLImageKHR static EGLImageKHR
import_simple_dmabuf(struct gl_renderer *, const struct dmabuf_attributes *); import_simple_dmabuf(struct gl_renderer *,
const struct dmabuf_attributes *,
const struct weston_color_representation *);
static weston_renderbuffer_t static weston_renderbuffer_t
gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output, gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output,
@ -899,10 +919,14 @@ gl_renderer_create_renderbuffer_dmabuf(struct weston_output *output,
struct gl_renderer *gr = get_renderer(output->compositor); struct gl_renderer *gr = get_renderer(output->compositor);
struct dmabuf_attributes *attributes = dmabuf->attributes; struct dmabuf_attributes *attributes = dmabuf->attributes;
struct gl_renderbuffer *renderbuffer; struct gl_renderbuffer *renderbuffer;
const struct pixel_format_info *info;
EGLImageKHR image; EGLImageKHR image;
GLuint fb, rb; GLuint fb, rb;
image = import_simple_dmabuf(gr, attributes); info = pixel_format_get_info(attributes->format);
assert(info->color_model != COLOR_MODEL_YUV);
image = import_simple_dmabuf(gr, attributes, NULL);
if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import dmabuf\n"); weston_log("Failed to import dmabuf\n");
return NULL; return NULL;
@ -1443,7 +1467,8 @@ prepare_solid_draw(struct gl_shader_config *sconf,
} }
static void static void
recreate_and_specify_textures(struct gl_buffer_state *gb) recreate_and_specify_textures(struct gl_buffer_state *gb,
EGLImageKHR *images)
{ {
struct gl_renderer *gr = gb->gr; struct gl_renderer *gr = gb->gr;
GLenum target; GLenum target;
@ -1465,14 +1490,118 @@ recreate_and_specify_textures(struct gl_buffer_state *gb)
glBindTexture(gb->parameters[i].target, gb->textures[i]); glBindTexture(gb->parameters[i].target, gb->textures[i]);
if (gl_extensions_has(gr, EXTENSION_EXT_EGL_IMAGE_STORAGE)) { if (gl_extensions_has(gr, EXTENSION_EXT_EGL_IMAGE_STORAGE)) {
gr->image_target_tex_storage(gb->parameters[i].target, gr->image_target_tex_storage(gb->parameters[i].target,
gb->images[i], NULL); images[i], NULL);
} else { } else {
gr->image_target_texture_2d(gb->parameters[i].target, gr->image_target_texture_2d(gb->parameters[i].target,
gb->images[i]); images[i]);
} }
} }
} }
static struct gl_color_egl_image *
ensure_color_egl_image(struct gl_surface_state *gs,
struct weston_color_representation *color_rep)
{
struct weston_buffer *buffer = gs->buffer_ref.buffer;
struct gl_buffer_state *gb = gs->buffer;
struct gl_color_egl_image *color_egl_image = NULL;
struct gl_color_egl_image *color_egl_image_cand;
wl_array_for_each(color_egl_image_cand, &gb->reimported_egl_images) {
if (!weston_color_representation_equal(&color_egl_image_cand->import_color_rep,
color_rep,
WESTON_CR_COMPARISON_FLAG_IGNORE_ALPHA |
WESTON_CR_COMPARISON_FLAG_IGNORE_CHROMA_LOCATION))
continue;
color_egl_image = color_egl_image_cand;
break;
}
if (!color_egl_image) {
struct linux_dmabuf_buffer *dmabuf;
EGLImageKHR image;
assert(buffer->dmabuf);
dmabuf = buffer->dmabuf;
image = import_simple_dmabuf(gb->gr,
&dmabuf->attributes,
color_rep);
if (image == EGL_NO_IMAGE_KHR)
return NULL;
color_egl_image = wl_array_add(&gb->reimported_egl_images,
sizeof *color_egl_image);
assert(color_egl_image);
color_egl_image->image = image;
color_egl_image->import_color_rep = *color_rep;
}
return color_egl_image;
}
static void
ensure_images_and_textures(struct gl_surface_state *gs)
{
struct weston_surface *surface = gs->surface;
struct weston_compositor *compositor = surface->compositor;
struct weston_buffer *buffer = gs->buffer_ref.buffer;
const struct pixel_format_info *info = buffer->pixel_format;
struct gl_buffer_state *gb = gs->buffer;
struct weston_color_representation color_rep;
if (buffer->type != WESTON_BUFFER_DMABUF ||
(gb->shader_variant != SHADER_VARIANT_RGBA &&
gb->shader_variant != SHADER_VARIANT_EXTERNAL)) {
if (gb->num_textures == 0)
recreate_and_specify_textures(gb, gb->images);
return;
}
color_rep =
weston_fill_color_representation(&surface->color_representation,
info);
/* Check if we need to re-import the EGLImage with non-default YCbCr
* attributes. */
if (!weston_color_representation_equal(&gb->egl_image_import_color_rep,
&color_rep,
WESTON_CR_COMPARISON_FLAG_IGNORE_ALPHA |
WESTON_CR_COMPARISON_FLAG_IGNORE_CHROMA_LOCATION)) {
struct gl_color_egl_image *color_egl_image = NULL;
color_egl_image = ensure_color_egl_image(gs, &color_rep);
if (!color_egl_image) {
weston_log("GL-renderer: failed to re-import EGLImageKHR\n");
goto out;
}
if (gb->active_reimported_egl_image == color_egl_image) {
weston_assert_int_gt(compositor, gb->num_textures, 0);
return;
}
weston_assert_int_eq(compositor, gb->num_images, 1);
recreate_and_specify_textures(gb, &color_egl_image->image);
gb->active_reimported_egl_image = color_egl_image;
return;
}
/* We switched from an EGLImage with non-default YCbCr attributes to
* default values. Recreate and bind textures. */
if (gb->active_reimported_egl_image != NULL) {
weston_assert_int_eq(compositor, gb->num_images, 1);
recreate_and_specify_textures(gb, gb->images);
gb->active_reimported_egl_image = NULL;
return;
}
out:
/* Ensure we create and bind textures. */
if (gb->num_textures == 0)
recreate_and_specify_textures(gb, gb->images);
}
static void static void
gl_shader_config_set_input_textures(struct gl_shader_config *sconf, gl_shader_config_set_input_textures(struct gl_shader_config *sconf,
struct gl_buffer_state *gb) struct gl_buffer_state *gb)
@ -1499,9 +1628,7 @@ prepare_textured_draw(struct gl_shader_config *sconf,
GLint filter; GLint filter;
int i; int i;
/* Ensure we create and bind textures. */ ensure_images_and_textures(gs);
if (gb->num_textures == 0)
recreate_and_specify_textures(gb);
*sconf = (struct gl_shader_config) { *sconf = (struct gl_shader_config) {
.req.texcoord_input = SHADER_TEXCOORD_INPUT_SURFACE, .req.texcoord_input = SHADER_TEXCOORD_INPUT_SURFACE,
@ -2915,6 +3042,7 @@ done:
static void static void
destroy_buffer_state(struct gl_buffer_state *gb) destroy_buffer_state(struct gl_buffer_state *gb)
{ {
struct gl_color_egl_image *color_egl_image;
int i; int i;
glDeleteTextures(gb->num_textures, gb->textures); glDeleteTextures(gb->num_textures, gb->textures);
@ -2922,6 +3050,10 @@ destroy_buffer_state(struct gl_buffer_state *gb)
for (i = 0; i < gb->num_images; i++) for (i = 0; i < gb->num_images; i++)
gb->gr->destroy_image(gb->gr->egl_display, gb->images[i]); gb->gr->destroy_image(gb->gr->egl_display, gb->images[i]);
wl_array_for_each(color_egl_image, &gb->reimported_egl_images)
gb->gr->destroy_image(gb->gr->egl_display, color_egl_image->image);
wl_array_release(&gb->reimported_egl_images);
pixman_region32_fini(&gb->texture_damage); pixman_region32_fini(&gb->texture_damage);
wl_list_remove(&gb->destroy_listener.link); wl_list_remove(&gb->destroy_listener.link);
@ -3244,8 +3376,10 @@ gl_renderer_destroy_dmabuf(struct linux_dmabuf_buffer *dmabuf)
static EGLImageKHR static EGLImageKHR
import_simple_dmabuf(struct gl_renderer *gr, import_simple_dmabuf(struct gl_renderer *gr,
const struct dmabuf_attributes *attributes) const struct dmabuf_attributes *attributes,
const struct weston_color_representation *color_rep)
{ {
const struct pixel_format_info *info;
EGLint attribs[53]; EGLint attribs[53];
int atti = 0; int atti = 0;
bool has_modifier; bool has_modifier;
@ -3335,11 +3469,41 @@ import_simple_dmabuf(struct gl_renderer *gr,
} }
} }
attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT; info = pixel_format_get_info(attributes->format);
attribs[atti++] = EGL_ITU_REC709_EXT; assert(info);
if (info->color_model == COLOR_MODEL_YUV) {
assert(color_rep);
attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT; attribs[atti++] = EGL_YUV_COLOR_SPACE_HINT_EXT;
attribs[atti++] = EGL_YUV_NARROW_RANGE_EXT; switch (color_rep->matrix_coefficients) {
case WESTON_COLOR_MATRIX_COEF_BT601:
attribs[atti++] = EGL_ITU_REC601_EXT;
break;
case WESTON_COLOR_MATRIX_COEF_BT709:
attribs[atti++] = EGL_ITU_REC709_EXT;
break;
case WESTON_COLOR_MATRIX_COEF_BT2020:
attribs[atti++] = EGL_ITU_REC2020_EXT;
break;
case WESTON_COLOR_MATRIX_COEF_UNSET:
case WESTON_COLOR_MATRIX_COEF_IDENTITY:
weston_assert_not_reached(gr->compositor,
"invalid matrix coefficients");
}
attribs[atti++] = EGL_SAMPLE_RANGE_HINT_EXT;
switch (color_rep->quant_range) {
case WESTON_COLOR_QUANT_RANGE_LIMITED:
attribs[atti++] = EGL_YUV_NARROW_RANGE_EXT;
break;
case WESTON_COLOR_QUANT_RANGE_FULL:
attribs[atti++] = EGL_YUV_FULL_RANGE_EXT;
break;
case WESTON_COLOR_QUANT_RANGE_UNSET:
weston_assert_not_reached(gr->compositor,
"invalid quantization range");
}
}
attribs[atti++] = EGL_NONE; attribs[atti++] = EGL_NONE;
@ -3354,6 +3518,7 @@ import_dmabuf_single_plane(struct gl_renderer *gr,
const struct dmabuf_attributes *attributes, const struct dmabuf_attributes *attributes,
const struct yuv_plane_descriptor *descriptor) const struct yuv_plane_descriptor *descriptor)
{ {
const struct pixel_format_info *plane_info;
struct dmabuf_attributes plane; struct dmabuf_attributes plane;
EGLImageKHR image; EGLImageKHR image;
char fmt[4]; char fmt[4];
@ -3369,7 +3534,10 @@ import_dmabuf_single_plane(struct gl_renderer *gr,
plane.stride[0] = attributes->stride[descriptor->plane_index]; plane.stride[0] = attributes->stride[descriptor->plane_index];
plane.modifier = attributes->modifier; plane.modifier = attributes->modifier;
image = import_simple_dmabuf(gr, &plane); plane_info = pixel_format_get_info(plane.format);
assert(plane_info->color_model != COLOR_MODEL_YUV);
image = import_simple_dmabuf(gr, &plane, NULL);
if (image == EGL_NO_IMAGE_KHR) { if (image == EGL_NO_IMAGE_KHR) {
weston_log("Failed to import plane %d as %.4s\n", weston_log("Failed to import plane %d as %.4s\n",
descriptor->plane_index, descriptor->plane_index,
@ -3531,6 +3699,7 @@ import_dmabuf(struct gl_renderer *gr,
EGLImageKHR egl_image; EGLImageKHR egl_image;
struct gl_buffer_state *gb; struct gl_buffer_state *gb;
const struct pixel_format_info *info; const struct pixel_format_info *info;
struct weston_color_representation color_rep;
const struct weston_testsuite_quirks *quirks; const struct weston_testsuite_quirks *quirks;
info = pixel_format_get_info(dmabuf->attributes.format); info = pixel_format_get_info(dmabuf->attributes.format);
@ -3550,13 +3719,18 @@ import_dmabuf(struct gl_renderer *gr,
info->color_model == COLOR_MODEL_YUV) info->color_model == COLOR_MODEL_YUV)
goto import_yuv; goto import_yuv;
egl_image = import_simple_dmabuf(gr, &dmabuf->attributes); weston_reset_color_representation(&color_rep);
color_rep = weston_fill_color_representation(&color_rep, info);
egl_image = import_simple_dmabuf(gr, &dmabuf->attributes,
&color_rep);
if (egl_image != EGL_NO_IMAGE_KHR) { if (egl_image != EGL_NO_IMAGE_KHR) {
const GLint swizzles[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA }; const GLint swizzles[] = { GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA };
GLenum target = choose_texture_target(gr, &dmabuf->attributes); GLenum target = choose_texture_target(gr, &dmabuf->attributes);
gb->num_images = 1; gb->num_images = 1;
gb->images[0] = egl_image; gb->images[0] = egl_image;
gb->egl_image_import_color_rep = color_rep;
/* The driver defines its own swizzles internally in the case of /* The driver defines its own swizzles internally in the case of
* a successful dma-buf import so just set default values. */ * a successful dma-buf import so just set default values. */