Compare commits

...

6 Commits

Author SHA1 Message Date
Matthias Clasen
15c8ba974f renderer: Make hdr textures if necessary
Take the preferred color state of the content into account
when deciding what color state to use for the texture we
generate.
2024-08-06 16:37:29 -04:00
Matthias Clasen
5d876fe0c1 gsk: Pass color state to download op
This lets us create a texture in the desired color state.
2024-08-06 16:37:29 -04:00
Matthias Clasen
dcf7fec1c6 vulkan: Allow specifying color state for dmabufs
When createing dmabuf textures, allow specifying a color state.
2024-08-06 16:37:29 -04:00
Matthias Clasen
f5616b45df gsk: Track whether nodes require wide gamut
Add gdk_color_state_is_wide_gamut and gsk_render_node_is_hdr, which
tracks whether a render node's content is in a wide gamut color
state (in practice, that means non-sRGB).

This will be used in render_texture to determine the color
state to use when creating a texture.
2024-08-06 16:37:29 -04:00
Matthias Clasen
20cb024cdb inspector: Show color states of textures 2024-08-06 16:32:12 -04:00
Matthias Clasen
c419d0bdf6 colorstate: Drop xyz for now
Converting to and from xyz turns out to be more difficult than
expected, depending on what whitepoint you choose, And different
specs choose different whitepoints, so we can't directly map
css xyz to cicp xyz anyway.
2024-08-06 16:32:12 -04:00
13 changed files with 93 additions and 32 deletions

View File

@@ -168,7 +168,6 @@ gdk_cicp_params_class_init (GdkCicpParamsClass *klass)
* - 5: PAL
* - 6,7: BT.601 / NTSC
* - 9: BT.2020
* - 10: CIE XYZ
* - 12: Display P3
*
* Since: 4.16

View File

@@ -676,10 +676,6 @@ gdk_color_state_new_for_cicp (const GdkCicp *cicp,
to_xyz = rec2020_to_xyz;
from_xyz = xyz_to_rec2020;
break;
case 10:
to_xyz = identity;
from_xyz = identity;
break;
case 12:
to_xyz = p3_to_xyz;
from_xyz = xyz_to_p3;

View File

@@ -33,6 +33,7 @@ struct _GskGpuDownloadOp
GskGpuImage *image;
gboolean allow_dmabuf;
GdkColorState *color_state;
GdkGpuDownloadOpCreateFunc create_func;
GskGpuDownloadFunc func;
gpointer user_data;
@@ -54,6 +55,7 @@ gsk_gpu_download_op_finish (GskGpuOp *op)
self->func (self->user_data, self->texture);
gdk_color_state_unref (self->color_state);
g_object_unref (self->texture);
g_object_unref (self->image);
g_clear_object (&self->buffer);
@@ -69,6 +71,7 @@ gsk_gpu_download_op_print (GskGpuOp *op,
gsk_gpu_print_op (string, indent, "download");
gsk_gpu_print_image (string, self->image);
gsk_gpu_print_string (string, gdk_color_state_get_name (self->color_state));
gsk_gpu_print_newline (string);
}
@@ -117,6 +120,7 @@ gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self)
guchar *data;
gsize width, height, stride;
GdkMemoryFormat format;
GdkMemoryTextureBuilder *builder;
data = gsk_gpu_buffer_map (self->buffer);
width = gsk_gpu_image_get_width (self->image);
@@ -124,11 +128,18 @@ gsk_gpu_download_op_vk_create (GskGpuDownloadOp *self)
format = gsk_gpu_image_get_format (self->image);
stride = width * gdk_memory_format_bytes_per_pixel (format);
bytes = g_bytes_new (data, stride * height);
self->texture = gdk_memory_texture_new (width,
height,
format,
bytes,
stride);
builder = gdk_memory_texture_builder_new ();
gdk_memory_texture_builder_set_bytes (builder, bytes);
gdk_memory_texture_builder_set_stride (builder, stride);
gdk_memory_texture_builder_set_width (builder, width);
gdk_memory_texture_builder_set_height (builder, height);
gdk_memory_texture_builder_set_format (builder, format);
gdk_memory_texture_builder_set_color_state (builder, self->color_state);
self->texture = gdk_memory_texture_builder_build (builder);
g_object_unref (builder);
g_bytes_unref (bytes);
gsk_gpu_buffer_unmap (self->buffer, 0);
}
@@ -143,7 +154,7 @@ gsk_gpu_download_op_vk_command (GskGpuOp *op,
#ifdef HAVE_DMABUF
if (self->allow_dmabuf)
self->texture = gsk_vulkan_image_to_dmabuf_texture (GSK_VULKAN_IMAGE (self->image));
self->texture = gsk_vulkan_image_to_dmabuf_texture (GSK_VULKAN_IMAGE (self->image), self->color_state);
if (self->texture)
{
GskGpuDevice *device = gsk_gpu_frame_get_device (frame);
@@ -315,6 +326,7 @@ gsk_gpu_download_op_gl_command (GskGpuOp *op,
gdk_dmabuf_texture_builder_set_premultiplied (db, gdk_memory_format_get_premultiplied (gsk_gpu_image_get_format (self->image)));
gdk_dmabuf_texture_builder_set_width (db, gsk_gpu_image_get_width (self->image));
gdk_dmabuf_texture_builder_set_height (db, gsk_gpu_image_get_height (self->image));
gdk_dmabuf_texture_builder_set_color_state (db, self->color_state);
self->texture = gdk_dmabuf_texture_builder_build (db, release_dmabuf_texture, texture, NULL);
@@ -341,6 +353,7 @@ gsk_gpu_download_op_gl_command (GskGpuOp *op,
gdk_gl_texture_builder_set_format (builder, gsk_gpu_image_get_format (self->image));
gdk_gl_texture_builder_set_width (builder, gsk_gpu_image_get_width (self->image));
gdk_gl_texture_builder_set_height (builder, gsk_gpu_image_get_height (self->image));
gdk_gl_texture_builder_set_color_state (builder, self->color_state);
gdk_gl_texture_builder_set_sync (builder, data->sync);
self->texture = gdk_gl_texture_builder_build (builder,
@@ -367,6 +380,7 @@ void
gsk_gpu_download_op (GskGpuFrame *frame,
GskGpuImage *image,
gboolean allow_dmabuf,
GdkColorState *color_state,
GskGpuDownloadFunc func,
gpointer user_data)
{
@@ -376,6 +390,7 @@ gsk_gpu_download_op (GskGpuFrame *frame,
self->image = g_object_ref (image);
self->allow_dmabuf = allow_dmabuf;
self->color_state = gdk_color_state_ref (color_state);
self->func = func,
self->user_data = user_data;
}
@@ -405,6 +420,7 @@ gsk_gpu_download_png_op (GskGpuFrame *frame,
gsk_gpu_download_op (frame,
image,
FALSE,
GDK_COLOR_STATE_SRGB,
gsk_gpu_download_save_png_cb,
filename);
}

View File

@@ -10,6 +10,7 @@ typedef void (* GskGpuDownloadFunc) (gpointe
void gsk_gpu_download_op (GskGpuFrame *frame,
GskGpuImage *image,
gboolean allow_dmabuf,
GdkColorState *color_state,
GskGpuDownloadFunc func,
gpointer user_data);

View File

@@ -671,7 +671,7 @@ gsk_gpu_frame_record (GskGpuFrame *self,
}
if (texture)
gsk_gpu_download_op (self, target, TRUE, copy_texture, texture);
gsk_gpu_download_op (self, target, TRUE, target_color_state, copy_texture, texture);
}
static void
@@ -779,6 +779,7 @@ gsk_gpu_frame_download_texture (GskGpuFrame *self,
gsk_gpu_download_op (self,
image,
FALSE,
color_state,
do_download,
g_memdup (&(Download) {
.format = format,

View File

@@ -352,6 +352,7 @@ gsk_gpu_renderer_render_texture (GskRenderer *renderer,
GdkTexture *texture;
graphene_rect_t rounded_viewport;
GdkColorState *color_state;
GdkMemoryDepth depth;
gsk_gpu_device_maybe_gc (priv->device);
@@ -361,8 +362,17 @@ gsk_gpu_renderer_render_texture (GskRenderer *renderer,
viewport->origin.y,
ceil (viewport->size.width),
ceil (viewport->size.height));
if (gsk_render_node_is_hdr (root))
color_state = GDK_COLOR_STATE_REC2100_PQ;
else
color_state = GDK_COLOR_STATE_SRGB;
depth = gdk_memory_depth_merge (gsk_render_node_get_preferred_depth (root),
gdk_color_state_get_depth (color_state));
image = gsk_gpu_device_create_download_image (priv->device,
gsk_render_node_get_preferred_depth (root),
depth,
rounded_viewport.size.width,
rounded_viewport.size.height);
@@ -370,9 +380,10 @@ gsk_gpu_renderer_render_texture (GskRenderer *renderer,
return gsk_gpu_renderer_fallback_render_texture (self, root, &rounded_viewport);
if (gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_SRGB)
color_state = GDK_COLOR_STATE_SRGB_LINEAR;
else
color_state = GDK_COLOR_STATE_SRGB;
{
g_assert (gdk_color_state_equal (color_state, GDK_COLOR_STATE_SRGB));
color_state = GDK_COLOR_STATE_SRGB_LINEAR;
}
frame = gsk_gpu_renderer_create_frame (self);

View File

@@ -1176,7 +1176,8 @@ gsk_vulkan_image_get_n_planes (GskVulkanImage *self,
}
GdkTexture *
gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self)
gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self,
GdkColorState *color_state)
{
GskGpuImage *image = GSK_GPU_IMAGE (self);
GdkDmabufTextureBuilder *builder;
@@ -1230,7 +1231,7 @@ gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self)
gdk_dmabuf_texture_builder_set_modifier (builder, properties.drmFormatModifier);
gdk_dmabuf_texture_builder_set_premultiplied (builder, !(gsk_gpu_image_get_flags (image) & GSK_GPU_IMAGE_STRAIGHT_ALPHA));
gdk_dmabuf_texture_builder_set_n_planes (builder, n_planes);
for (plane = 0; plane < n_planes; plane++)
{
static const VkImageAspectFlagBits aspect[GDK_DMABUF_MAX_PLANES] = {
@@ -1252,6 +1253,9 @@ gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self)
gdk_dmabuf_texture_builder_set_offset (builder, plane, layout.offset);
}
if (color_state)
gdk_dmabuf_texture_builder_set_color_state (builder, color_state);
texture = gdk_dmabuf_texture_builder_build (builder, close_the_fd, GINT_TO_POINTER (fd), &error);
g_object_unref (builder);
if (texture == NULL)

View File

@@ -44,7 +44,8 @@ GskGpuImage * gsk_vulkan_image_new_for_dmabuf (GskVulk
gsize height,
const GdkDmabuf *dmabuf,
gboolean premultiplied);
GdkTexture * gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self);
GdkTexture * gsk_vulkan_image_to_dmabuf_texture (GskVulkanImage *self,
GdkColorState *color_state);
#endif
guchar * gsk_vulkan_image_get_data (GskVulkanImage *self,

View File

@@ -827,6 +827,12 @@ gsk_render_node_get_preferred_depth (const GskRenderNode *node)
return node->preferred_depth;
}
gboolean
gsk_render_node_is_hdr (const GskRenderNode *node)
{
return node->is_hdr;
}
/* Whether we need an offscreen to handle opacity correctly for this node.
* We don't if there is only one drawing node inside (could be child
* node, or grandchild, or...).

View File

@@ -91,6 +91,13 @@ my_color_stops_get_depth (const GskColorStop *stops,
return gdk_color_state_get_depth (GDK_COLOR_STATE_SRGB);
}
static inline gboolean
color_state_is_hdr (GdkColorState *color_state)
{
return color_state != GDK_COLOR_STATE_SRGB &&
color_state != GDK_COLOR_STATE_SRGB_LINEAR;
}
/* apply a rectangle that bounds @rect in
* pixel-aligned device coordinates.
*
@@ -316,6 +323,7 @@ gsk_color_node_new2 (const GdkColor *color,
node->offscreen_for_opacity = FALSE;
node->fully_opaque = gdk_color_is_opaque (color);
node->preferred_depth = gdk_color_get_depth (color);
node->is_hdr = color_state_is_hdr (color->color_state);
gdk_color_init_copy (&self->color, color);
@@ -1978,6 +1986,7 @@ gsk_texture_node_new (GdkTexture *texture,
node = (GskRenderNode *) self;
node->offscreen_for_opacity = FALSE;
node->fully_opaque = gdk_memory_format_alpha (gdk_texture_get_format (texture)) == GDK_MEMORY_ALPHA_OPAQUE;
node->is_hdr = color_state_is_hdr (gdk_texture_get_color_state (texture));
self->texture = g_object_ref (texture);
gsk_rect_init_from_rect (&node->bounds, bounds);
@@ -2200,6 +2209,7 @@ gsk_texture_scale_node_new (GdkTexture *texture,
node->fully_opaque = gdk_memory_format_alpha (gdk_texture_get_format (texture)) == GDK_MEMORY_ALPHA_OPAQUE &&
bounds->size.width == floor (bounds->size.width) &&
bounds->size.height == floor (bounds->size.height);
node->is_hdr = color_state_is_hdr (gdk_texture_get_color_state (texture));
self->texture = g_object_ref (texture);
gsk_rect_init_from_rect (&node->bounds, bounds);
@@ -3481,6 +3491,7 @@ gsk_container_node_new (GskRenderNode **children,
{
graphene_rect_t child_opaque;
gboolean have_opaque;
gboolean is_hdr;
self->children = g_malloc_n (n_children, sizeof (GskRenderNode *));
@@ -3489,6 +3500,7 @@ gsk_container_node_new (GskRenderNode **children,
node->preferred_depth = children[0]->preferred_depth;
gsk_rect_init_from_rect (&node->bounds, &(children[0]->bounds));
have_opaque = gsk_render_node_get_opaque_rect (self->children[0], &self->opaque);
is_hdr = gsk_render_node_is_hdr (self->children[0]);
for (guint i = 1; i < n_children; i++)
{
@@ -3507,10 +3519,13 @@ gsk_container_node_new (GskRenderNode **children,
have_opaque = TRUE;
}
}
is_hdr |= gsk_render_node_is_hdr (self->children[i]);
}
node->offscreen_for_opacity = node->offscreen_for_opacity || !self->disjoint;
}
node->is_hdr = is_hdr;
}
return node;
}
@@ -3805,6 +3820,7 @@ gsk_transform_node_new (GskRenderNode *child,
&node->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -3957,6 +3973,7 @@ gsk_opacity_node_new (GskRenderNode *child,
gsk_rect_init_from_rect (&node->bounds, &child->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -4190,6 +4207,7 @@ gsk_color_matrix_node_new (GskRenderNode *child,
gsk_rect_init_from_rect (&node->bounds, &child->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -4497,6 +4515,7 @@ gsk_repeat_node_new (const graphene_rect_t *bounds,
}
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
node->fully_opaque = child->fully_opaque && gsk_rect_contains_rect (&child->bounds, &self->child_bounds);
return node;
@@ -4664,6 +4683,7 @@ gsk_clip_node_new (GskRenderNode *child,
gsk_rect_intersection (&self->clip, &child->bounds, &node->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -4847,6 +4867,7 @@ gsk_rounded_clip_node_new (GskRenderNode *child,
gsk_rect_intersection (&self->clip.bounds, &child->bounds, &node->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -5017,6 +5038,7 @@ gsk_fill_node_new (GskRenderNode *child,
node = (GskRenderNode *) self;
node->offscreen_for_opacity = child->offscreen_for_opacity;
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
self->child = gsk_render_node_ref (child);
self->path = gsk_path_ref (path);
@@ -5227,6 +5249,7 @@ gsk_stroke_node_new (GskRenderNode *child,
node = (GskRenderNode *) self;
node->offscreen_for_opacity = child->offscreen_for_opacity;
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
self->child = gsk_render_node_ref (child);
self->path = gsk_path_ref (path);
@@ -5504,6 +5527,7 @@ gsk_shadow_node_new (GskRenderNode *child,
gsk_shadow_node_get_bounds (self, &node->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
for (i = 0; i < n_shadows; i++)
{
node->preferred_depth = gdk_memory_depth_merge (node->preferred_depth,
@@ -5727,6 +5751,8 @@ gsk_blend_node_new (GskRenderNode *bottom,
node->preferred_depth = gdk_memory_depth_merge (gsk_render_node_get_preferred_depth (bottom),
gsk_render_node_get_preferred_depth (top));
node->is_hdr = gsk_render_node_is_hdr (bottom) ||
gsk_render_node_is_hdr (top);
return node;
}
@@ -5914,6 +5940,8 @@ gsk_cross_fade_node_new (GskRenderNode *start,
node->preferred_depth = gdk_memory_depth_merge (gsk_render_node_get_preferred_depth (start),
gsk_render_node_get_preferred_depth (end));
node->is_hdr = gsk_render_node_is_hdr (start) ||
gsk_render_node_is_hdr (end);
return node;
}
@@ -6566,6 +6594,7 @@ gsk_blur_node_new (GskRenderNode *child,
graphene_rect_inset (&self->render_node.bounds, - clip_radius, - clip_radius);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -6802,6 +6831,8 @@ gsk_mask_node_new (GskRenderNode *source,
self->render_node.bounds = *graphene_rect_zero ();
self->render_node.preferred_depth = gsk_render_node_get_preferred_depth (source);
self->render_node.is_hdr = gsk_render_node_is_hdr (source) ||
gsk_render_node_is_hdr (mask);
return &self->render_node;
}
@@ -6980,6 +7011,7 @@ gsk_debug_node_new (GskRenderNode *child,
gsk_rect_init_from_rect (&node->bounds, &child->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
self->render_node.is_hdr = gsk_render_node_is_hdr (child);
return node;
}
@@ -7402,6 +7434,7 @@ gsk_subsurface_node_new (GskRenderNode *child,
gsk_rect_init_from_rect (&node->bounds, &child->bounds);
node->preferred_depth = gsk_render_node_get_preferred_depth (child);
node->is_hdr = gsk_render_node_is_hdr (child);
return node;
}

View File

@@ -36,6 +36,7 @@ struct _GskRenderNode
guint preferred_depth : GDK_MEMORY_DEPTH_BITS;
guint offscreen_for_opacity : 1;
guint fully_opaque : 1;
guint is_hdr : 1;
};
typedef struct
@@ -106,6 +107,7 @@ void gsk_transform_node_get_translate (const GskRenderNode
float *dx,
float *dy);
GdkMemoryDepth gsk_render_node_get_preferred_depth (const GskRenderNode *node) G_GNUC_PURE;
gboolean gsk_render_node_is_hdr (const GskRenderNode *node) G_GNUC_PURE;
gboolean gsk_container_node_is_disjoint (const GskRenderNode *node) G_GNUC_PURE;

View File

@@ -990,6 +990,8 @@ add_texture_rows (GListStore *store,
add_text_row (store, "Type", "%s", G_OBJECT_TYPE_NAME (texture));
add_text_row (store, "Size", "%u x %u", gdk_texture_get_width (texture), gdk_texture_get_height (texture));
add_text_row (store, "Format", "%s", enum_to_nick (GDK_TYPE_MEMORY_FORMAT, gdk_texture_get_format (texture)));
add_text_row (store, "Color State", "%s", gdk_color_state_get_name (gdk_texture_get_color_state (texture)));
if (GDK_IS_MEMORY_TEXTURE (texture))
{
GBytes *bytes;

View File

@@ -115,17 +115,6 @@ find_color_state_by_name (const char *name)
gdk_cicp_params_set_matrix_coefficients (params, 0);
gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL);
color_state = gdk_cicp_params_build_color_state (params, &error);
}
else if (g_strcmp0 (name, "xyz") == 0)
{
params = gdk_cicp_params_new ();
gdk_cicp_params_set_color_primaries (params, 10);
gdk_cicp_params_set_transfer_function (params, 8);
gdk_cicp_params_set_matrix_coefficients (params, 0);
gdk_cicp_params_set_range (params, GDK_CICP_RANGE_FULL);
color_state = gdk_cicp_params_build_color_state (params, &error);
}
else if (g_strcmp0 (name, "rec2020") == 0)
@@ -190,7 +179,7 @@ char **
get_color_state_names (void)
{
static const char *names[] = {
"srgb", "srgb-linear", "display-p3", "xyz", "rec2020",
"srgb", "srgb-linear", "display-p3", "rec2020",
"rec2100-pq", "rec2100-linear", "rec2100-hlg",
"yuv", "bt601", "bt709",
NULL,