This is a still experimental protocol (thus the xx prefix).
We are using it go obtain information about the compositors
preferred color state, and pass that on to our rendering machinery.
The currently supported color states are srgb, srgb-linear, rec2100-pq
and rec2100-linear. We don't have any support for ICC profiles.
Unlike other protocols, keep the support code for this protocol
fairly isolated behind wrapper objects, since the protocol is
still subject to change.
begin_frame is the place where we make decisions about the format,
depth and colorstate for our rendering. Make these calls take the
surface color state into account.
In particular, if the surface colorstate is suitable for GL_SRGB,
and we don't need high depth, set things up for that.
The modern incantation to get validation layers enabled is via
VK_INSTANCE_LAYERS=VK_LAYER_KHRONOS_validation
Vulkan has a bunch of environment variables to toggle stuff, let's use
those instead of doing our own.
The settings portal is reporting enums as string values, so
we need to translate this setting back to what we need.
Fixes
(gtk4-demo:18902): GLib-CRITICAL **: 19:06:14.783: g_variant_get_int32: assertion 'g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)' failed
that could be seen in recent nightly flatpaks.
Previously, we were always downloading into CAIRO_FORMAT_ARGB32.
Now we check the texture depth and pick a suitable format.
This improves rendering for high depth content, but it's slower.
That's why we're not yet making sure the depth is suitable for the
colorspace conversion. That would force all SRGB textures into float
surfaces as we don't consider conversions suitable for U8 in our generic
code.
This shader converts between two color states, by using the
same functions that we use on the cpu. The conversion to perform
is passed as part of the variation.
As premultiplication is part of color states on the shader, we also
encode the premultiplication in the shader.
And because opacity is a useful optimization, we also allow setting
opacity.
For now, the only possible color states are srgb and srgb-linear.
This adds the following:
- ccs argument to GskRenderNode::draw
This is the compositing color state to use when drawing.
- make implementations use the CCS argument
FIXME: Some implementations are missing
- gsk_render_node_draw_with_color_state()
Draws a node with any color state, by switching to its compositing
color state, drawing in that color state and then converting to the
desired color state.
This does draw the result OVER the previous contents in the passed in
color state, so this function should be called with the target being
empty.
- gsk_render_node_draw_ccs()
This needs to be passed a css and then draws with that ccs.
The main use for this is chaining up in rendernode draw()
implementations.
- split out shared Cairo functions into gdkcairoprivate.h
gskrendernode.c and gskrendernodeimpl.c need the same functions.
Plus, there's various code in GDK that wants to use it, so put it in
gdk/ not in gsk/
gsk_render_node_draw() now calls gsk_render_node_draw_with_color_state()
with GDK_COLOR_STATE_SRGB.
Make begin_frame() set a rendering colorstate and depth, and provide it
to the renderers via gdk_draw_context_get_depth() and
gdk_draw_context_get_color_state().
This allows the draw contexts to define their own values, so that ie the
Cairo and GL renderer can choose different settings for rendering (in
particular, GL can choose GL_SRGB and do the srgb conversion; while
Cairo relies on the renderer).
That's basically the "undefined" value. We need that when drawing
nothing, which so far only happens with empty container nodes.
But empty container nodes can be children of other nodes, and that makes
things propagate. So instead of catching them, force the whole rest of
the code to deal with an undefined depth.
We also can't just set a random depth, because that will cause merging
to fail.
Make our visual selection code prefer fbconfigs that are
'srgb framebuffer capable', and mark the surface as 'is srgb'
in this case.
This arranges things so that GSK knows not to use an offscreen
for converting contents back to srgb in the end.
For GDK_MEMORY_U8_SRGB depth, try to create an SRGB surface.
This requires the EXT_KHR_gl_colorspace extension, which
isn't super-common in the wild (37%), so we fall back to regular U8 if
that fails.
But if we have the extension, create our egl surface with the
srgb colorspace, and report that fact in gdk_surface_gl_is_srgb().
We still only differentiate between high bit depth or not, but we now
choose at the end instead of the start, which makes it easier to adapt
to a different method of choosing.
This is an experiment for now, but it seems that encoding srgb inside
the depth makes sense, as we not just use depth to decide on the
GL fbconfigs/Vulkan formats to pick, depth also encodes how the [0...1]
color values are quantized when stored.
Let's see where this goes.
Returns the linear color state that renderers should render in when
this is the target color state.
We disable this function unless linear compositing is enabled and just
return @self by default.
This function checks if the colorstate uses an sRGB transfer function
as final operation. In that case, it is suitable for use with GL_SRGB
(and the Vulkan equivalents).
We disable this function (by always returning NULL) unless linear
compositing is enabled, because this function is used to transition
textures and framebuffers to their linear counterparts.
This is mostly an empty shell for now. We only have static instances
for srgb and srgb-linear, which we will use as markers during our
node processing.
In the future, this object may grow more instances, as well as the
ability to create them from and save them to icc profiles or cicp
data. And a color conversion API.
This is a temporary solution to allow testing how well linear rendering
already works while refactoring code.
This will be removed once linear rendering is the default.
We have code with proper error handling for dmabuf export, we can just
try to use it.
And if it doesn't work, we don't offload the texture like before.
But it does work - at least for me.