This requires adding infrastructure to generate per-test data, so that
the random clip rect can be computed and reused for both test and
reference generation.
So add this infrastructure.
... and port the colorflip test.
This is so we can factor out generic parts of the code. This allows
making changes easier to those parts, like if we want to introduce
rules for what colorstates and memory depths to do diffs in.
We switched to using the Unicode (UTF-16) versions of the Windows API by
default, so we also obtain the display name in UTF-16 form as well.
This updates the implementation in the Windows backend so that we
properly acquire the names that we need in UTF-16, and then convert the
results to UTF-8, which is what we use in GTK/GLib.
When comparing textures, always pick the colorstate from the reference
texture. This allows us to define what color state we expect.
For now, there's no check that the color states are equal, because they
don't really have to be as long as the pixels are.
Always pick the color state from texture1 and download the data and
generate the diff in that color state.
That now means the order of the 2 arguments matters.
I first tried porting everything to float, but it turns out that that
makes a compare-render run (with all 1520 tests succeeding) 9s slower
so I decided to keep the existing U8 code.
A side benefit is that saving the diff to PNG will continue creating
U8 PNGs.
We use the renderer to create the reference for the rotate test by
applying the same rotate transform to the reference image instead of the
tested node.
This is somewhat suboptimal because they run very similar codepaths, but
this method works with high bit depth content and different colorstates
This concludes the port away from gdk-pixbuf and means that all rendered
content and reference images can now use high bit depth and colorstates.
We use the renderer to create the reference for the colorflip test by
applying the same colorflip matrix to the reference image instead of the
tested node.
This is somewhat suboptimal because they run very similar codepaths, but
this method works with high bit depth content and different colorstates
We use the renderer to create the reference for the clip test by
applying the same clip node to the reference image instead of the
tested node.
This is somewhat suboptimal because they run very similar codepaths, but
this method works with high bit depth content and different colorstates
We use the renderer to create the reference for the flip test by
applying the same transform node to the reference image instead of the
tested node.
This is somewhat suboptimal because they run very similar codepaths, but
this method works with high bit depth content and different colorstates
and the gdk-pixbuf method does not.
We use the renderer to create the reference for the repeat test by
applying the same repeat node to the reference image instead of the
tested node.
This is somewhat suboptimal because they run very similar codepaths, but
this method works with high bit depth content and different colorstates
and the gdk-pixbuf method does not.
After commit 447bc18c48 EGL on X11 broke.
But the handling of the GL context also was quite awkward because it was
unclear who was responsible for ensuring it got reset.
Change that by making gdk_gl_context_clear_current_if_surface() return
the context (with a reference because it might be the last reference) it
had unset, so that after changing EGL properties the code that caused
the clearing can re-make it current.
This moves the responsibility to the actual code that is dealing with
updating properties and frees the outer layers of code from that task.
And that means the X11 EGL code doesn't need to care and the code in the
Wayland backend that did care can be removed.
Related: !7662Fixes: #6964 on X11
This essentially reverts the changes from
c230546a2c but implies new semantics.
Namely, surface-attached contexts can now be bound to EGL_NO_SURFACE if
the windowing system isn't ready yet.
It is the task of the windowing system to make sure the context is
properly rebound when the contents become available.
We ensure this by checking in begin_frame() if we created the EGL window
and if we did, we make_current(). This works because creating the EGL
window creates the EGL surface and that does a clear_current(), so this
is always going to have the desired effect of re-making the current
context.
It is very convoluted though.
Fixes: #6964
Related: https://gitlab.freedesktop.org/mesa/mesa/-/issues/11784
The default implementation of unselect_all calls
set_selection, which SingleSelection does not have
an implementation for (it's unclear what it would
do with multiple selections). Add a straightforward
implementation for unselect_all.
Commit d58b545f fixed a double counting of value size, but didn't check
whether that actually looked great. It doesn't.
Because the slider has negative margins, we need to add margin to the
value if it's on a side with no marks. If it is on a side with marks,
the scale will make it touch the marks. We therefore apply a mark's size
of margin to have the value be in the same place, with or without marks.
The texture ID is not deleted on dmabuf export; a copy is made, the
GskGpuImage retains ownership.
However when doing GL export, the texture *does* take ownership, so we
need the stealing semantics for that case.
We do this because:
a) The parent class (GdkGLContext) already stores the paint regions of
previous frames, no need to do the same.
b) The painted region passed to end_frame () includes the backbuffer's
damage region, so it's not really what we want.
This also fixes a leak of cairo_region_t that I introduced by mistake
in !7418
The measured size of the range already includes borders that the scale
calculates. This includes the drawn value. So in the measurement of
scale, the size of the value shouldn't just be added to the size of the
scale, as then the size is effectively added twice.
To fix this, we first measure the minimum size of the range. Then, we
determine the minimum size of our scale. Only then do we set the minimum
size to maximum of those two values.
If both marks are set on top, and the value is drawn on top, the value
should be drawn outside the marks for a horizontal scale.
Currently, that order is incorrect, with the marks shifted up and the
value drawn between the slider and the marks.
This fixes that order.
We can get the position of the trough from the parent GtkRange class,
and can therefore align directly to that. There is no need to add
symmetry, which can always be added in CSS if needed.
Fixes#5171, #6639
This only makes the value allocation logic easier to understand, but
doesn't change anything, apart from not mixing integer and floating
operations anymore which can give a warning under some compiler
settings.
This reverts commit 2799632c02, reversing
changes made to 154035e76f.
That MR tried to fix#5171, but by doing a forced symmetry it introduced
another issue for users on Libadwaita, see #6639.
The logic can be much simplified, as the next commits show.
CI is hitting various limits after we started out with 32. In
particular, the default runners hit 90s test timeouts.
And the asan runner runs into max threads limits, so reduce that one to
4 tests max.
This is a test balloon tests that use /dev/udmabuf to produce
dmabufs that we can use in ci, even if we don't have a GPU.
Currently, the tests we can do are somewhat limited, since mesas
software renderers don't support dmabufs yet.
Instead of every test spawning their own dbus, make the tests share the
same server, just like they share their own compositor.
This should speed up things a bit and avoid weird interactions when
multiple dbus processes exist.
There are spurious failures happening in CI runs and I blame those on
too many processes running at the same time overloading either the
compositor we're running against, or causing OOM situations or just
genereally slowing things down and hitting timeout limits.
The choice of 32 is rather arbitrary. I just picked a number that felt
good.
We write a debug message and then handle things using fallback.
Fixes error messages when trying to import incompatible dmabufs.
(in my case: llvmpipe dmabufs into radv)
The value of `gtk-font-rendering` currently can't be specified
in "settings.ini", as the parser doesn't handle enums.
Allow reading values by enum nicknames.
Also cleanup obsolete references to rc files.
The non-shared context's surface must survive the lifetime of the
GL texture, and when the renderer gets unrealized the surface goes away,
but we cannot guarantee that all GL textures have been destroyed by
then.
So better use a context we know will survive becuase it isn't bound to a
surface.
This is the same fix for NGL as f3ac0535f8
was for GL.
This fixes a warning from the inspector's accessibility checker.
Users only control the menu model, not the menubar widget,
so this can only be done in gtk.
Instead of running one renderpass per clip region, run one renderpass for
the whole clip extents, and just set the scissor to the individual clip
rects.
This means that we need to use LOAD_OP_LOAD in cases where we don't
redraw the full extents, but nonetheless, the eprformance wins of
avoiding renderpasses are worth it, in particualr on tilers like the
Raspberry Pi or other mobile chips and the Apple M1/2.
We want to differentiate between CLEAR, DONT_CARE and LOAD in the
future, and the current boolean doesn't allow that.
Also implement support for the the different ops in the Vulkan
renderpass code.
This starts the renderpass at the given scissor rect.
It just splits out the gsk_gpu_render_pass_begin_op() call into a
simpler function, so it's harder to mess up.
Add gsk_gpu_node_processor_set_scissor() that allows resetting the
nodeprocessor's scissor and clip rectangle.
That in turn allows using the same nodeprocessor instance for all the
rects we draw for the clip region.
So they must not copy the fully_opaque flag from the child.
Adapted the testcase that accidentally caught it do now always catch it
by setting a proper background.
When we encounter many dead textures, we want to GC. Textures can take
up fds (potentially even more than 1) and when we are not collecting
them quickly enough, we may run out of fds.
So GC when more then 50 dead textures exist.
An example for this happening is recent Mesa with llvmpipe udmabuf
support, which takes 2 fds per texture and the test in
testsuite/gdk/memorytexture that creates 800 textures of ~1 pixel each,
which it diligently releases but doesn't GC.
Related: #6917
We need to ensure that an EGL surface exists before we call
eglMakeCurrent() with it. Otherwise we might end up binding to
EGL_NO_SURFACE and then never revising that decision.
Which leads to not rendering to the backbuffer, but into the void.
Fixes X11 rendering being black
Fixes#6930
the non-shared context's surface must survive the lifetime of the
GL texture, and when the renderer gets unrealized the surface goes away,
but we cannot guarantee that all GL textures have been destroyed by
then.
So better use a context we know will survive becuase it isn't bound to a
surface.
Since a4cc95b293, the emoji chooser
can fail to appear on smaller screens, depending on where its parent
widget is located. Stop requesting a fixed height to prevent this
issue.
The current resizing implementation in the GDK-Win32 backend is not
telling GDK early enough for Vulkan that a resize in the surface (i.e.
HWND) is done, so that GDK can re-create swapchain in time, which is
apparent on nVidia drivers (and AMD drivers that utilize the mailbox
presentation mode on Windows) when the HWND is being enlarged
interactively.
To work around this, bar a refactor in the Windows resizing/presentation
code, is to call _gdk_surface_update_size() when we really did resize
the HWND when we handle queued resizes via SetWindowsPos().
The existing call in gdksurface-win32.c in
_gdk_win32_surface_compute_size() remains required, otherwise the
surface won't display initially.
Thanks to Benjamin Otte for pointing this possibility out.
We don't need to hardcode all the interface names as string literals,
since they come as part of the wl_interface structs in the protocol
bindings we use.
While the inspector is open, look for some shortcuts:
Super-r to toggle recording
Super-c to take a screenshot
A screenshot here means just a single-frame recording.
For convenience, we put the recorded frame onto the
clipboard too.
This should have gone into !7619 but gitlab managed to finish the CI run
just as I was pushing a new version to the MR with
merge_request.merge_when_pipeline_succeeds and apparently gitlab applied
that to the previous version or something.
So now that MR merged an incomplete version to main. And here's the fix
for that.
Use the clamp() API from the previous commit to:
1. Clamp values into range
2. Emit an error if values were out of range
Unlike CSS, which just clamps and doesn't emit an error, we do want to
emit one because we care about colors being correct in our node files.
This will make it easier for themes to style radio buttons
differently from check buttons, since out CSS does not have
:has().
The concrete desire here is to use a different outline for
the focus rectangle.
Update affected tests.
Fixes: #6936
The context->variables field is expected by the resolve code to be
the keyframe variables. That code takes the style variables from
context->style anyway, so no need to pass them as context->variables
too. And crucially, the lookup code treats the keyframes variables
differently to the style variables, since it doesn't expect the
hierarchical structure that comes from parent styles. This change
fixes infinite recursion in variable lookup with css like
:root {
--a: var(--b);
}
.foo {
--b: var(--a);
color: var(--a);
}
Test included.
Fixes: #6881
We don't currently support computing transforms across native
boundaries. This could be added by using gdk_popup_get_position_x/y.
For now, just fail in this case.
Related: #6355
The easiest things trigger the silliest mistakes. Add tests
for various properties we want our transfer functions to have,
such as:
- be inverse of each other
- stay within the defined ranges
- by symmetric around 0
Set primaries without name if supported, when named primaries are not.
But prefer named primaries if available.
This is just an attempt at defensive coding.
If we get sent primaries with the values as named primaries, treat them
like named primaries.
Fixes colorstate support on Kwin, which never sends named primaries.
If the texture covers all of the black background (like when watching a
1080p stream fullscreen on a 1080p monitor) we don't need a compositor
with single pixel support.
Fixes offloading in Kwin.
There's a ton of error checking happening that we want to do.
Because it turns out it is not really useful to create a subsurface for
the single pixel buffer when we don't even support single pixel buffers.
begin_frame_full does not return a reference, we assume that the
color state is staying alive for the duration of the frame anyway,
so end_frame simply sets priv->color_state to NULL.
We need to round outwards and a 1x1 rectangle with offset 0.5,0.5 should
end up as a 3x3 rectangle with offset 0,0 when rounded, not as a 2x2
rectangle.
We need to round outwards and a 1x1 rectangle with offset 0.5,0.5 should
end up as a 3x3 rectangle with offset 0,0 when rounded, not as a 2x2
rectangle.
The backbuffer's damage region is the region of the backbuffer
that doesn't contain up-to-date contents. This is determined
by the backbuffer's age and previous frame's paint regions.
This enables incremental rendering
On Windows, always use gtk_show_uri_win32 () instead of going through
GAppInfo. Hook up gtk_file_launcher_launch () to gtk_show_uri_win32 ()
as well, always extracting the file path (and not a URI) and propagating
the always-ask flag.
Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
This is a new internal utility to open/show/launch/execute URIs and
files on Windows, using Windows's native ShellExecuteEx () and
SHOpenWithDialog () APIs.
The advantages this has over using the win32 implementation of
g_app_info_launch_default_for_uri ():
* the implementation here is fairly simple;
* it doesn't involve trying to grok the registry for app / file type
registrations (at least not inside GLib/GTK side, the implementations
of ShellExecuteEx/SHOpenWithDialog presumably do that internally);
* it doesn't require convoluted formatting / escaping of invocation
command lines that GWin32AppInfo / gspawn-win32 has to do otherwise
(again, presumably the Windows libraries implement this internally);
* it's certain to end up opening the file/URI the same way other apps
in the system would;
* it can/will open the native system UI for picking an app in case there
are multiple options (or when so requested programmatically with the
always-ask flag), or if there is no app installed that can handle the
URI scheme / file type;
* it lets us pass the parent window handle, much like the portal APIs;
presumably Windows would use this for positioning the picking UI, or
placing the launched app's window;
* presumably, this will properly elevate privileges with a User Access
Control (UAC) prompt if the app being launched requires administrator
access; this presumably is impossible with the wspawn* APIs that
gspawn-win32 uses;
* this has a much better chance to work properly with the win32 app
isolation (AppContainer) technology.
Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
Make sure the radii are strictly positive.
Also handle the case where start >= end.
We can't really underline that error, because we don't track the
locations of the start/end properties until we know that there's an
error.
So just underline the whole radial gradient declaration.
Test included
Unless there is a very good reason to use memcpy(), don't use it.
Not using it makes the compiler not screw up and waste tons of CPU that
it could have not wasted.
Gets my framerate back from 1250 => 1750 and makes sysprof no longer
report ~40% of render time spent in gsk_gpu_colorize_op().
For some node types, the child nodes play different roles. E.g.
for a mask node, one of the two children is the source, the other
the mask. Show this information in the inspector.
Show the color state, and create textures with the full color
information.
The GdkRGBA-based helper functions are no longer used with this
commit and have been dropped.
It is not perfect, since PangoRenderer, GtkTextAppearance and
GtkTextTag all carry colors as PangoColor or GdkRGBA. But at least,
we get glowing foreground colors.
When transforming back from a complex transform to a simpler transform,
the resulting clip might turn out to clip everything, because clips can
grow while transforming, but the scissor rect won't. So when this
process happens, we can end up with an empty clip by transforming:
1. Set a clip that also sets the scissor
2. transform in a way that grows the clip, say rotate(45)
3. modify the clip to shrink it
4. transform in a way that simplifies the transform, say another
rotate(45)
5. Figure out that this clip and the scissor rect do no longer overlap
Catch this case and avoid drawing anything.
Show the color state, and create textures with the full color
information.
With this commit, add_color_row and get_color_texture are no longer
used and have been dropped.
Add a new GskShadow2 struct that has a GdkColor instead of
a GdkRGBA, and a new constructor and getter to go along with
it.
With this commit, my_color_get_depth is no longer used and
has been dropped.
Now that GDK can figure it out from the rendernode, doing all this work
trying to figure it out is no longer necessary.
Plus, it was sometimes wrong and lead to obscure artifacts.
We know it at begin_frame() time, so if we pass it there instead of
end_frame(), we can use it then to make decisions about opacity.
For example, we could notice that the whole surface is opaque and choose
an RGBx format.
We don't do that yet, but now we could.
The opaque rect from the rendernodes are now used to set the opaque
region in the backend.
This means applications can now set a transparent window background and
make indivual parts of their window opaque.
But because this is a best effort method, it is not guaranteed to
succeed in finding all opaque regions, in particular if the rendernodes
used to build it are not straightforward to analyze.
This is poking into the surface directly, so not a good idea.
And I want to hide that struct in the priv member.
Technically, this code should look at the opaque region, but I am lazy
and the GL renderer is on its way out, so I think it's not worth doing.
We are using so many internal extra features that it is no longer a good
idea to use these functions.
And they aren't really used anyway.
These extra features are also constantly in flux and rely on internal
APIs, so exposing them would just cause extra pain.
By using the inlining macro trick, we can work around deprecation
warnings from removing this function as a public API, which will happen
in the next commits.
Make sure both GL renderers don't leave their contexts alive via the
current context, but ensure they dispose of them properly.
Fixes issues when the corresponding GL resources in the surfaces they
were attached to go away.
GLContexts marked as surface_attached are always attached to the surface
in make_current().
Other contexts continue to only get attached to their surface between
begin_frame() and end_frame().
All our renderer use surface-attached contexts now.
Public API only gives out non-surface-attached contexts.
The benefit here is that we can now choose whenever we want to
call make_current() because it will not cause a re-make_current() if we
call it outside vs inside the begin/end_frame() region.
Or in other words: I want to call make_current() before begin_frame()
without a performance penalty, and now I can.
... and pass the opaque region of the node.
We don't do anything with it yet, this is just the plumbing.
The original function still exists, it passes NULL which is the value
for no opaque region at all.
If an image description query is running while the surface gets
destroyed, we were not properly cleaning up, causing the callbacks to be
emitted on freed variables.
If we apply a rounded clip, we might change the clip in a way that makes
it intersectable with the scissor again, if both had diverged before.
So try and intersect with the clip.
I just spent an hour trying to figure out why things don't work. And it
was an optional dependency hidden 3 layers deep in some meson file.
This really has to stop.
And because just like in GTK, GStreamer's dmabuf APIs are always
available (they will just fail on Windows etc), there's no need to have
any conditions.
The only difference is that the GStreamer media backend now requires
GStreamer 1.24.
In the case of no offloading, we want to pass through to the child
(which is likely a big texture doing occlusion).
In the case of punching a hole, we want to punch the hole and not draw
anything behind it, so we start an occlusion pass with transparency.
And in the final case with offloading active, we don't draw anything,
so we don't draw anything.
This should fix concerns about drawing the background behind the video
as mentioned for example in
https://github.com/Rafostar/clapper/issues/343#issuecomment-1445425004
Container nodes save their opaque region, so it's quick to access. Use
that to check if the largest opaque region even qualifies for culling -
and if not, just exit.
Speeds up walking node trees by a lot.
Now that we can specify the min size for an occlusion pass, we can
specify that we want the full clip rect to be occluded for occlusion to
trigger.
The benefit of this is that for partial redraws we almost
always get the background color to cover the redrawn rectangle, so
occlusion will kick in.
When trying to cull, try culling from the largest rectangle of the
remaining draw region first. That region has the biggest chance of
containing a large area to skip.
As a side effect, we can stop trying to cull once the largest rectangle
isn't big enough anymore to contain anything worth culling.
When querying clip bounds, also check the scissor rect, because
sometimes that one is tighter than the clip bounds, because the clip
bounds need to track some larger rounded corners.
Makes a few tests harder to break.
Instead of requiring an occlusion pass to cover the whole given scissor
rect, allow using a smaller rect to start the pass.
When starting such a pass, we adjust the scissor rect to the size of
that pass and do not grow it again until the pass is done.
The rectangle subtraction at the end will then take care of subtraction
that rectangle from the remaining pixels.
To not end up with lots of tiny occlusion passes, add a limit for how
small such a pass may be.
For now that limit is arbitrarily chosen at 100k pixels.
gsk_gpu_node_processor_rect_to_device() is a useful function to have,
even if it has to return FALSE sometimes when there is no simple 1:1
mapping - ie when the modelview contains a rotation.
Instead of just iterating over all the rectangles of the region,
always draw the first rectangle of the region and subtract it when done.
This sounds more complicated, but it will allow us to modify the
rectangle in future commits.
Also change the way rectangles are printed by including the bottom right
coordinate, too.
I'm still not sure what the best way is, but at least I no longer get
confused and it has the infos I want.
Maintain cursor position even if the text in the entry has been filtered.
This is achieved by maintaining the same position with respect to
the end of the text buffer as we suppose that the text after the cursor
hasn't changed. This is measured in characters not bytes.
Fixes#6782
We were comparing with destination stride, not with source stride, and
in rare cases when those were different, this would trigger aborts in
the testsuite.
Add a function that 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.
Each time we create a new window, we create a new EGLSurface. Each time
we destroy a window, we failed to destroy the EGLSurface, due to passing
a GdkDisplay instead of a EGLDisplay to eglDestroySurface().
This effectively leaked not only the EGL surface metadata, but also the
associated DMA buffers. For applications where one opens and closes many
windows over the lifetime of the application, and where the application
runs for a long time; for example a terminal emulator server, this
causes a significant memory leak, as the memory will only ever be freed
once once the application process itself exits, if ever.
Fix this passing an actual EGLDisplay instead of an GdkDisplay, to
eglDestroySurface().
This matches what the gpu renderer does when printing
colorstates.
It also avoids it printing "S*RGBA8" for the format and instead prints
"SRGBA8(p)" now.
When creating images for use with different colorstates, ensure that
they have the depth of that colorstate. Otherwise we might lose accuracy
due to quantization.
Fixes mipmaps in rec2100 being rendered as RGBA8.
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.
This is a simple fix, I did not investigate if NULL is actually possible
here.
In function ‘bitset_container_empty’,
inlined from ‘bitset_container_const_nonzero_cardinality’ at ../gtk/roaring/roaring.h:1942:13,
inlined from ‘container_nonzero_cardinality’ at ../gtk/roaring/roaring.h:4055:20,
inlined from ‘roaring_bitmap_lazy_xor’ at ../gtk/roaring/roaring.c:9727:17:
../gtk/roaring/roaring.h:1928:13: error: potential null pointer dereference [-Werror=null-dereference]
1928 | if (bitset->cardinality == BITSET_UNKNOWN_CARDINALITY) {
| ~~~~~~^~~~~~~~~~~~~
Pass the ccs, opacity and GdkColors to the op to let it make
decisions about color conversion. Also, reorder the offset to
follow the same order as the color ops.
Update the callers.
This test checks a that a specific combination of mask with alpha
inside opacity works as expected. We were treating the mask color
as premultiplied here, although it isn't.
We can't use gsk_gpu_node_processor_color_states_self() for ops which
apply alt to colors that don't come from textures, since those are
always unpremultiplied.
This fixes the + and - in disabled spin buttons appearing completely
white.
Add a new function callback called GtkTetBufferCommitNotify to be notified
of changes to a GtkTextBuffer without being involved in a signal chain.
This is necessary for some situations because signal handlers may modify
the parameters as they proceed down to default handler. As such, the signal
is unsuitable for applications heavily utilizing plug-ins such as Builder
due to non-deterministic signal connection ordering.
This technique has been used in Builder for the better part of a decade
and now would also vastly help in situations like libspelling where you
also want to know about changes to the buffer right before they are
committed to the b-tree.
Fixes: #6133
This creates a new color node that is meant to be identical to
an existing one, so we need to use gsk_color_node_new2 to preserve
the color state information.
Test included.
Allow defining cicp color states with an @-rule:
@cicp "jpeg" {
primaries: 1;
transfer: 13;
matrix: 6;
range: full;
}
And allow using them in color() like this:
color("jpeg" 50% 0.5 1 / 75%)
Note that custom color states use a string, unlike default color
states which use an ident.
Test included.
And allow using color states for colors with a syntax similar
to modern css color syntax.
color(srgb 50% 0.5 1 / 75%)
Both floating point numbers and percentages can be used.
Currently, this is only supported for color nodes.
Test included.
Use the color state returned by this function instead of assuming
the color of a color node is always sRGB.
Node colors are converted to the css on the cpu. That is necessary
since we don't know if they are in one of the default color states,
and our shaders can't deal with non-default color states.
Make color-related ops take the ccs and a GdkColor, and make
decisions about color conversion on the cpu vs the gpu.
This makes the node processor code simpler, and lets use convert
the color directly into the op instance without extra copying.
We also pass opacity to the op, so it can be applied when we
write the color into the instance.
Lastly, rorder the offset to come right after the opacity argument.
Treat the color and rounded color ops the same way.
Update all callers.
With this, the prepare_color apis in gskgpunodeprocessor.c are
no longer used and have been dropped.
This api lets one obtain a color state and color values from
a GtkCssColor. We don't want to force everything though sRGB,
but we can't quite avoid conversion here, since we don't have
a 100% match between the css color spaces and color states.
css color cleanup
Add a function for converting a single color from one
color state to another. This is a generalization of the
already existing function to convert a GdkRGBA to another
color state.
This is an old test that isn't very relevant anymore, and it has
some linking problems because it includes private headers that
have inlined functions.
This is a leftover from GTK3 when iconhelper sizes depended on the
texture size.
Now we only need to queue a redraw with the new icon.
Fixes warnings about resizes during allocate caused by scale change
notification during allocation of GtkWindow.
We want to reuse gsk_gpu_color_to_float() for use with GdkColor and this
function will be replaced. But until that's fully done, we need 2
different names.
So rename this one to something else
This is a GTK3 leftover where the icons were manually drawn and sized.
Now that they're managed by actual widgets that enforce a correct size
that is independent of scale factor, this is no longer necessary.
Fixes warnings about resizes during allocate caused by scale change
notification during allocation of GtkWindow.
Connect to "changed" signal of the entry displayed with combobox
for some options (e.g. watermark text or passcode). This ensures
that the strings are propagated to the printer backend.
Fixes#6782
The outlook for mutter supporting this in GNOME 47 are cloudy,
so lets flip the switch back. You can still set
USE_POINTER_VIEWPORT in the environment to try this code.
It turns out the "step" variable could up as 0 when p.y ~= 3.0 ||
p.y ~= r.y - 3.0
That was not enough to trigger it though because if "start" and "end"
were the same value, the "y <= end" check in the loop would immediately
terminate it.
However, if start + epsilon == end so that end != start but (end - start)
/ 7 == 0, then step would end up as 0 and the loop would never
terminate.
And if that happened, it would bring down GPUs.
So recode this whole machinery to make it impossible to infloop.
Fixes#6896
The fix in commit 5e7f227d broke shadows while trying to make them
faster.
So use a better way to make them faster.
With the normalized blur radius, we can now conclude that all the values
too far from p.y will cause the gauss() call to return close to 0, so we
can skip any y value that is too far from p.y.
And that allows us to put an upper limit on the loop iterations.
Tests included
Fixes#6888
Instead of doing complicated math, normalize the values to a sigma
of 1.0, and then use that.
This should also be beneficial for shader performance, because 1.0 is a
constant and constant-elimination can kick in on the inlined functions.
When the compositor sends us an image description, we currently happily
reuse it.
However, those image descriptions may contain optional properties that
we do not handle - example: reference white level. So if we were to
reuse that image description, we would set a wrong reference white
level.
To avoid issues like that, never use compositor-provided image
descriptions.
However, query those image descriptions and map them to the closest
GdkColorState, so that we can quickly look up *our* version of that
image description and use that one.
When finalizing a subsurface, we need to make sure it is removed
from the sibling lists in its parent, or bad things will happen.
This should crashes seen in Epiphany nightly.
Fixes: #6891
convert_func2 is a 'from' conversion function, ie it expects to
be passed the target color state. This was wrong both in
gdk_memory_convert and gdk_memory_convert_color_state.
Previously GTK required a C99 compiler, but as discussed on
GNOME/gtk!7510 there's at least one anonymous union in public API
(in `GskPathPoint`), and that's a C11 feature.
Signed-off-by: Simon McVittie <smcv@debian.org>
When we allocate a graphene_point_t on the stack, there's no guarantee
that it will be aligned at an 8-byte boundary, which is an assumption
made by gsk_pathop_encode() (which wants to use the lowest 3 bits to
encode the operation). In the places where it matters, force the
points on the stack and embedded in structs to be nicely aligned.
By using a distinct type for this (a union with a suitable size and
alignment), we ensure that the compiler will warn or error whenever we
can't prove that a particular point is, in fact, suitably aligned.
We can go from a `GskAlignedPoint *` to a `graphene_point_t *`
(which is always valid, because the `GskAlignedPoint` is aligned)
via &aligned_points[0].pt, but we cannot go back the other way
(which is not always valid, because the `graphene_point_t` is not
necessarily aligned nicely) without a cast.
In practice, it seems that a graphene_point_t on x86_64 *is* usually
placed at an 8-byte boundary, but this is not the case on 32-bit
architectures or on s390x.
In many cases we can avoid needing an explicit reference to the more
complicated type by making use of a transparent union. There's already
at least one transparent union in GSK's public API, so it's presumably
portable enough to match GTK's requirements.
Increasing the alignment of GskAlignedPoint also requires adjusting how
a GskStandardContour is allocated and initialized. This data structure
allocates extra memory to hold an array of GskAlignedPoint outside the
bounds of the struct itself, and that array now needs to be aligned
suitably. Previously the array started with at next byte after the
flexible array of gskpathop, but the alignment of a gskpathop is only
4 bytes on 32-bit architectures, so depending on the number of gskpathop
in the trailing flexible array, that pointer might be an unsuitable
location to allocate a GskAlignedPoint.
Resolves: https://gitlab.gnome.org/GNOME/gtk/-/issues/6395
Signed-off-by: Simon McVittie <smcv@debian.org>
This is widely assumed, but is not guaranteed by Standard C, and is
known to be false on CHERI architectures (which have 64-bit sizes and
128-bit tagged pointers). Add a static assertion to ensure that GTK
will not build on platforms where this assumption does not hold.
As discussed on GNOME/gtk!7510, if GTK switches from gsize to uintptr_t
as its representation of the underlying bits in a pointer, GTK maintainers
would prefer that to be done project-wide so that it's done consistently,
after which this static assertion could be removed.
At the time of writing, GLib makes the same assumption (GNOME/glib#2842),
but GLib contributors are gradually removing it (mostly by replacing gsize
with uintptr_t where a pointer-sized quantity is needed). Finishing
that work in GLib would be a prerequisite for being able to make GTK
work on the affected platforms.
Signed-off-by: Simon McVittie <smcv@debian.org>
Similar to the previous commit, to avoid undefined behaviour we need
to avoid evaluating out-of-bounds shifts, even if their result is going
to ignored by being multiplied by 0 later.
Detected by running a subset of the test suite with
-Dsanitize=address,undefined on x86_64.
Signed-off-by: Simon McVittie <smcv@debian.org>
If, for example, e == 0, it is undefined behaviour to compute an
expression involving an out-of-range shift by (125 - e), even if the
result is in fact irrelevant because it's going to be multiplied by 0.
This was already fixed for the memorytexture test in
commit 5d1b839 "testsuite: Fix another ubsan warning", so use the
implementation from that test everywhere. It's in the header as an
inline function to keep the linking of the relevant tests simple:
its only caller in production code is fp16.c, so there will be no
duplication outside the test suite.
Detected by running a subset of the test suite with
-Dsanitize=address,undefined on x86_64.
Signed-off-by: Simon McVittie <smcv@debian.org>
Left-shifting a signed 32-bit integer by 31 bits (such that the value
overflows into the sign bit) is undefined behaviour. Use an unsigned
integer instead.
Detected by running a subset of the test suite with
-Dsanitize=address,undefined on x86_64.
Signed-off-by: Simon McVittie <smcv@debian.org>
Some callers of these functions ask to copy 0 items from a NULL source,
which would be valid if they were copied in a loop (because NULL would
never be dereferenced), but is declared to be undefined behaviour for
Standard C memcpy. Guard the call to memcpy so that we only call it
if we have more than 0 items, and therefore should have a non-NULL
source pointer.
Detected by running a subset of the test suite with
-Dsanitize=address,undefined on x86_64.
Signed-off-by: Simon McVittie <smcv@debian.org>
Shifting a 32-bit type by 32 bits is formally undefined behaviour,
even if it happens in code that is unreachable at runtime. Use a
compile-time check against GLib's GLIB_SIZEOF_SIZE_T, instead of hoping
a runtime check will be optimized away.
Signed-off-by: Simon McVittie <smcv@debian.org>
Unfortunately the format string for a size_t, `%zu`, is not portable
to all Windows compilers, and the appropriate format string for the
fundamental type that implements size_t varies between platforms
(typically `%u` on 32-bit platforms, `%lu` on 64-bit Linux or
`%llu` on 64-bit Windows).
In gtk-demo, cast the number of search results to long, to avoid
breaking up a translatable string.
Elsewhere, use GLib's abstraction for this.
Signed-off-by: Simon McVittie <smcv@debian.org>
Middle clicking without a selection left a paste point override behind.
This meant actual pastes were going to the location of the middle click,
instead of the cursor
Closes#5530
Main changes:
1. Avoid invalid writes by not passing pointers to a GArray that
realloc()s its data
2. Use a hash table to store image defs, instead of an array. This
requires a custom hash/equal function
3. Make image desc computation sync, so that setting a cs always
succeeds or always fails and doesn't depend on timing.
4. Add a few debug messages in failure paths. For lack of a category,
they ended up in MISC.
The signal is declared in GtkTestATContext with 0 parameters, but these
handlers were written as if the signal had one `guint` parameter.
On some architectures this accidentally works as intended, but on
others (reproduced on i386 and riscv64) the test tries to use arbitrary
stack contents as the `TestData *` and crashes when it tries to
dereference the resulting non-pointer.
Resolves: https://gitlab.gnome.org/GNOME/gtk/-/issues/6490
Signed-off-by: Simon McVittie <smcv@debian.org>
When the cicp values coming out of GStreamer are unspecified, replace
them with the default cicp values for YUV video: 1/13/6.
We still may end up with unspecified values inside the params, because
GStreamer returns unspecified for primaries/tfs/matrices that aren't
supported by cicp.
See also https://github.com/AOMediaCodec/libavif/wiki/CICP#unspecified
fora similar discussion.
This regression was introduced in aeac2b54.
We need percentage values to stay non-computed, since we otherwise
fail to compute relative font sizes properly. But we want percentages
not to stick around in relative colors, so tweak things to be more
aggressive with simplication when creating relative color values.
Update affected tests.
Fixes: #6868
With the changes in !7473 we now use sampler2D arguments in functions.
However, when there's a function we call with a samplerExternalOES -
which means we need to overload it with that shader variant.
The later CICP changes made the cicp params we were setting unustable.
Set ones that work in the current state of git main. They are still
imperfect, but they reflect the current code.
We were using slightly different numbers here, which isn't good.
The matrices in gdkcolordefs.h are tested in the colorstate-internal
tests, so they are at least properly inverse, and the products match.
It would be better to generate the glsl definitions, somehow.
Add some more texture conversion roundtrips. They are currently
ifdefed out, since they need cicp api.
Also add another test binary for internal tests.
When we the image color state is not a default one, use the cicp
convert op to convert it to the ccs. And when the target color
state is a non-default one, use the shader in the reverse direction.
This shader receives cicp parameters via uniforms, and converts
the texture data from or to the output colorstate. It computes
the matrix in the vertex shader, and then picks the eotf/oetf
according to the cicp parameters in the fragment shader.
This adds machinery to create colorstate objects from cicp
tuples, as well as a function to return a cicp tuple for a
colorstate.
Still missing: a conversion shader for non-default colorstates.
Our conversion machinery supports converting from any color
state to any default color state or back. Direct conversion
between two non-default color states isn't guaranteed. For
converting *to* a cicp color state, we need this function.
We want to get PFD_SWAP flags as that's required to enable incremental
rendering. However, as documented on MSDN, ChoosePixelFormat ignores
PFD_SWAP flags.
We may get PFD_SWAP flags or not depending on the way the OpenGL driver
orders its pixel formats. While PFD_SWAP flags are very important for
GUI toolkits, they are best avoided by games, as most games render the
scene in its entirety on each frame. Drivers optimized for games tend
to order pixel formats with no PFD_SWAP flags first.
Se we implement our own method to select the best pixel format. We check
for usable pixel formats and assign penalties for each one, until we find
a format with 0 penalty or the sequence ends. Then the best pixel format
is selected.
Getting a defined swap method is necessary to enable incremental
rendering. We cannot just add a swap method attribute since
exchange is generally preferred, but is not always available.
Here we try to infer what the driver prefers, then ask for
exchange or copy, in sequence.
Vulkan objects are integers on 32bit and it's failing when it's set to
just NULL.
```
../gdk/gdkvulkancontext.c:677:24: error: assignment to ‘VkSemaphore’ {aka ‘long long unsigned int’} from ‘void *’ makes integer from pointer without a cast [-Wint-conversion]
677 | priv->draw_semaphore = NULL;
```
We were passing the wrong rect to the clip mode computation, resulting
in a rounded rect every time, even though it should pretty much always
be unclipped.
The visual results are unaffected, because the clip sent to the shader
was still correct.
Instead of allocating one large descriptor pool and hoping we never run
out of descriptors, allocate small ones dynamically, so we know we never
run out.
Test incldued, though the test doesn't fail in CI, because llvmpipe
doesn't care about pool size limits. It does fail on my AMD though.
A fun side note about that test is that the GL renderer handles it best
in normal operationbecause it caches offscreens per node and we draw the
same node repeatedly.
But, the replay test expands them to duplicated unique nodes, and then
the GL renderer runs out of command queue length, so I had to disable
the test on it.
There is now a GskGpuYcbcr struct that maintains all the Vulkan
machinery related to YCbCrConversions.
It's a GskGpuCached, so it will make itself go away when it is no longer
used, ie a video stopped playing.
Now that we don't use the fancy features anymore, we don't need to
enable them.
And that also means we don't need an env var to disable it for testing.
Now that we don't do fancy texture stuff anymore, we don't need fancy
shaders either, so we can just compile against Vulkan 1.0 again.
And that means we need no fallback shaders for Vulkan 1.0 anymore.
Instead of trying to cram all descriptors into one large array and only
binding it at the start, we now keep 1 descriptor set per image+sampler
combo and just rebind it every time we switch textures.
This is the very dumb solution that essentially maps to what GL does,
but the performance impact is negligible compared to the complicated
dance we were attempting before.
Rewrite all shaders to use 2 predefined samplers called GSK_TEXTURE0 and
GSK_TEXTURE1 instead of wrapper functions.
On GL and Vulkan compat mode, these map directly to samplers.
On Vulkan proper, they map to 2 indices into the texture array, like
before.
From now on, the old nvidia GPUs - ie the 3xx drivers - should start
working again.
Fixes: #6564Fixes: #6574Fixes: #6654
This allows GskGpuFrame implementations to store data per vertex
attribute.
This is just the plumbing, no actual implementation is done in this
commit.
This guarantees that the images get ID 0 and 1 (on GL), which is going
to be quite important for the next steps.
Just for funsies, here's fps numbers on my desktop for this change:
NGL 1500 => 1400
Vulkan 2650 => 2250
This by itself is just more work refcounting all those images, but
there's actually a goal here, that will become visible in future
commits.
But this is split out for correctness and benchmarking purposes (the
overhead from refcounting seems to be negligible on my computer).
Just define GSK_N_TEXTURES in every glsl file, extract that #define in
the python parser and emit a static const uint variable
"{shader_name}_n_textures" in the generated header.
It's a struct collecting all relevant info for a texture passed to a
shader.
The ultimate goal is to get rid of the descriptors and let ops
manage them on thir own.
If GskGpuCache has an idea of what time it is, cached items can use that
time to update their last-use time instead of having to carry it around
throught function calls everywhere.
Port an optimization of the GL renderer where it fast-paths crossfades
with progress <= 0 and >=1 - which should really never happen because
nobody should emit them in the first place, but oh well.
YUV dmabufs are not sRGB.
So instead of making the dmabuf builder have sRGB as the default
colorstate, add a NULL default option that makes the builder choose
the colorstate based on fourcc when build() is called.
If that happens, we pick sRGB usually, but for YUV we pick narrow range
BT601, like we did in versions before colorstates.
We no longer hardcode the few different classes we have, but generically
walk over all classes.
As a side effect we now get new classes added to stats automatically.
The content itself did not change.
Commit 1580490670 included a reordering of
acquiring the frame before making the context current.
Sometimes (like at startup) new frames need to be created.
Setting up a new frame assumed the GL context was current.
Change it so that we delay the one GL setup we do in frames until later.
Building GTK with GCC 8 results in the following warning:
gtk/gtkurilauncher.c: In function ‘gtk_uri_launcher_launch’:
gtk/gtkurilauncher.c:315:3: warning: this ‘else’ clause does not guard... [-Wmisleading-indentation]
else
^~~~
gtk/gtkurilauncher.c:317:1: note: ...this statement, but the latter is misleadingly indented as if it were guarded by the ‘else’
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
^~~
In the compiled code, gtk_show_uri_full () is invoked whether the portal
branch is taken or not, leading to use-after-free of the task.
It looks like GCC in versions older than 12 treats the _Pragma(s) that
G_GNUC_BEGIN_IGNORE_DEPRECATIONS expands to as C-level statements, and
therefore the pragma takes up the 'else' statement slot.
See https://godbolt.org/z/e5zqbaqxo for a simple reproducer.
Signed-off-by: Sergey Bugaev <bugaevc@gmail.com>
* We cannot map with offset, because offsets need to be page-size
aligned. And our code doesn't expect an offset anyway.
* The error return value from mmap() is MAP_FAILED aka -1, not NULL aka
0.
Vulkan requires us waiting on the image acquired from
vkAcquireNextImageKHR() before we start rendering to it, as that
function is allowed to return images that are still in use by the
compositor.
Because of that requirement, vkAcquireNextImageKHR() requires a
semaphore or fence to be passed that it can signal once it's done.
We now use a side channel to begin_frame() - calling
set_draw_semaphore() - to pass that semaphore so that the
vkAcquireNextImageKHR() call inside begin_frame() can use it, and then
we can wait on it later when we submit.
And yes, this is insanely convoluted, the Vulkan developers should
totally have thought about GTK's internal designs before coming up
with that idea.
These are just factoring out gdk_draw_context_begin/end_frame() so I can
add one tiny thing there later.
And I did both even though I only need one, because it felt wrong to
just do one.
Make the function look like that:
1. handle special case
2. maybe GC
3. draw
4. queue next gc
5. cleanup
This seems like the sanest approach to avoid gc() collecting things
necessary for drawing in the future.
And I need to refactor stuff, so having it out of the way is a good
idea.
When loading or saving png files, encode the CICP flags of the color
state into the PNG.
When loading, decode the CICP flags if available and detect the
colorstate they use.
If we do not support the cicp tags, we do not load the image.
So far, we ignore the ICC profiles.
Includes regeneration of nodeparse test *reference* output to include
the new tags we write to PNGs.
The original tests do not include those tags, so we implicitly test that
we read untagged files correctly.
We only download the data when we actually need it for writing into the
PNG stream.
This allows modifying the download parameters (in particular color state
in the next commit) while writing out their settings, so the code for
selecting the right colorstate liives in only one place.
We have to be careful though, because the download now happens after the
setjmp(), so we need to make sure the error path handles both cases
without leaking: Where the download has happened and where it hasn't.
Same thing as dmabuf and GL texture builders. Preparation for adding
color state support to texture constructors.
As a bonus, we can now do update regions with memory textures.
... and plumb the color state through the downloading machinery, where
no matter what path it takes it ends up in
gdk_memory_convert_color_state() or gdk_memory_convert().
The 2nd of those has been expanded to optionally do colorstate
conversion when the 2 colorstates are different.
When a cache item is invalid, don't move it into the hash table.
Instead, just delete it.
Something like this could happen:
1. A texture is cached
In the case of #6867 this would be a webpage in epiphany.
2. The texture cache item is garbage-collected
For example, epiphany might switch to a new tab, and the previous page's
texture will remain. After 15s or so, we collect our item for that
texture.
3. The texture is cached again, but in the target colorspace
We now decide we need the texture again, but not in any colorspace, we
need it in the target colorspace. This might be because we run an
effect on it (like a crossfade) or because we want mipmaps (like in the
overview map, where its zoomed out).
4. The old invalid item is transitioned into the hash table
We now have an invalid item in the hash table. This is extra bad,
because it had only one reference (from the texture), but we treat it
like it has 2 (from us in the hash table and from the texture).
So depending on if the texture is freed before we reuse it, we get
different results: If it was free, we get invalid memory accesses, if it
was not freed, we treat it like a valid cache item and think the image
inside is still valid.
Fixes#6867
This happens when buffer creation fails in `get_dmabuf_wl_buffer()` and
we manually call `listener->release (data, NULL)`.
Fixes: 2478dd8322 ("subsurface: Split a function")
Private function `gtk_print_setup_get_printer` unconditionally calls
`gtk_printer_find()`, which does not exist on Win32.
But `gtk_print_setup_get_printer()` is never called either, so usually
that whole code gets optimized out.
Some compilation environment however do not cleanup unused functions,
leading to linker error as `gtk_printer_find()` is not found.
This patch should solve the link issue.
gsk_gpu_device_gc() may release the last ref on the GskGpuDevice,
leading to memory corruption when setting priv->cache_gc_source = 0.
Includes a bit of refactoring, so the ref/unref wraps nicely around the
actual code.
Fixes crashes seen after using the inspector and closing the window,
thereby closing all windows of a display and releasing all references to
the device.
Fixes#6861
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.
Change the glsl convert_color function to proceed in stages:
- first unpremultiply
- then linearize
- then transform linearly
- then delinearize
- then premultiply
All the steps are only taken if needed.
We only want to measure visible columns so that they may consume the
maximum width available to the widget. This fixes a situation where hidden
columns would cause less-than the whole width to be allocated. Additionally
that fixes warnings where some widgets expect more horizontal space than
they would ultimately be allocated.
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.
We need to make sure our clear values are in the right colorstate, not
in sRGB.
The occluision culling managed to sneak through the big transition for
that.
This is actually the node Loupe is using, so having tiling work with it
is important.
Because of the previous commit, different filters are supported fine.
Fixes: #6324
This allows mipmapping if downscaled a lot, like we do for non-tiled
images.
A side effect is that due to the simpler caching for tiles, we can only
cache the mipmapped images in one colorstate. But we need to pick a
potentially non-default one, because we want to mipmap in a linear
colorstate.
So this is somewhat suboptimal. Patches with improvements accepted.
Use the new cache feature to split oversized textures into tiles the
size given by the new device API.
Then number those tiles from left to right and top to bottom and use
that number as the tile id.
When we draw large images, we absolutely do not want to keep memory that
we do not need. So do a GC run after every tile. That otentially slows
down things, but it also improves the chances of not running out of
memory.
Here's the node for the image I managed to create after I applied this
patch:
repeat {
bounds: 0 0 50000 50000;
child: text {
font: "Noto Color Emoji 10000px";
glyphs: 661 0 0 0 color;
offset: 0 10000;
hint-style: none;
}
}
Functions should behave as I expect, and I just spent an hour debugging
a refcount issue because I assumed our image creation functions return
refrences. Which is a very sane assumption.
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.
The texture and texture-scale node code is creating image copies
for mipmaps and to adapt to the compositing colorstate.
Those texture should be cached.
We want to cache textures in the compositing color state, not in their
original color state. However, the compositing color state may change
(think multimonitor setups).
So we additionally keep a cache per colorstate.
That means texture lookup is now a 3-step process:
1. Look up in the compositing colorstate's cache
2. Look up in the general cache
3. Upload
GL_SRGB is doing postmultiplied alpha, so if the texture is
premultiplied, we can't use this optimization.
The optimization still works for unpremultiplied and opaque images,
because those don't do that step.
No colorstate conversions allowed here, though technically we could use
the alternate color state for the source most of the time, as the mask's
colorstate is only relevant for luminance.
This is a function that's meant to be used whenever both color states
of the shader are equal. In that case no colorspace conversion code
needs to be created and shaders can be shared.
The GL renderer is using FLOAT32 instead of GL_SRGB, which is screwing
up the node-editor by making it turn on high bit depth unconditionally.
So until someone fixes the GL renderer properly, do this quickfix.
That way, we can use it in one other place where we want to use mipmaps.
I don't really like it because it adds yet another argument,
but then the one new caller was selecting suboptimal shaders, and that's
worse.
The colormatrix shade does a whole matrix multiplication, which is
absolutely not necessary.
The convert shader has builtin opacity handling and when the colorstates
match will do no conversion.
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.
The alternative color state is used as the interpolation color state.
Colors are transformed into that space on the CPU.
For now we set the interpolation color state to SRGB, because ultimately
we want to let callers specify it, so having something that's easy to
map to that behavior is desirable.
Otherwise we might have chosen to interpolate in the compositing
colorstate.
It also means that we need to premultiply colors on the CPU now because
of the limitations of the shader colorstates APIs.
This makes use of the GskGpuColorStates by setting the ccs as output
colorstate and the color's colorstate as alternative color state.
The shader adaption is very straightforward because of that.
This is the first op to obey the compositing color state. This means
from now on until all ops obey the ccs rendering is broken when ccs is
not set to linear.
I'll keep individual ops in seperate commits for easier review, because
they all need different adaptations.
Render to an offscreen and add a final conversion if the target
colorstate is not a rendering colorstate.
This now allows the GPU renderer to render to any colorstate.
Makes the verbose output (a lot) more verbose, but it makes the
colorstates used in the shaders very visible.
And it will be relevant once people start using different colorstates
everywhere (like oklab for gradients/colors and so on).
This adds the following functions:
output_color_from_alt()
alt_color_from_output()
Converts between the two colors
output_color_alpha()
alt_color_alpha()
Multiplies a color with an alpha value
This adds a GdkColorStates that encodes 2 of the default GdkColorStates
and wether their values are premultiplied or not.
Neither do the shaders do anything with this information yet, nor do the
shaders do anything with it yet, this is just the plumbing.
If desired, try creating GL_SRGB images. Pass a try_srgb boolean down to
the image creation functions and have them attempt to create images like
that.
When it is not possible to create srgb images in the given format, just
fall back to regular images. The calling code is meant to check the
GSK_GPU_IMAGE_SRGB flags to determine the actual format of the resulting
image.
Make the node processor and the pattern writer track the current
compositing color state. Color state nodes change it. We pass
the surface color state down via the frame apis.
The name of the variable is "ccs" for "compositing color space". It's an
unused variable name and it's common enough to deserve a short and sweet
name.
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.
This commit just adds the flag, but I wanted to make it an individual
commit to explain the purpose:
The SRGB flag is meant to be used for images that have an SRGB format.
In Vulkan terms, that means VK_FORMAT_*_SRGB.
In GL, it means GL_SRGB or GL_SRGB_ALPHA.
As these formats have been madatory since GL 3.0, we can (ab)use them
uncoditionally. Images in these formats are renderable, too, so it's
not just usable for uploading.
What these images allow is treating the data as sRGB while shaders
access them as linear, thereby getting sRGB<=>linear conversions for
free.
It is also possible to switch off the linearization of these images and
treat them as sRGB, which allows all sorts of shenanigans, though one
has to be careful if that turning off applies to the relevant GL/Vulkan
code in question.
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.
If the GL texture is exportable to a dmabuf, we can just use our dmabuf
importing code to get that texture into Vulkan.
There is no need to go via host memory in that case.
And if it doesn't work, we just fall back, like before.
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.
Instead of hardcoding which textures we presumably support, just try
creating a buffer and use the failure of that for the error message.
This makes the error message a bit less obvious, but it makes it
possible to refactor the get_buffer() code without having to deal with
the error path.
If we want to improve the debug message, we can start putting debug
messages into the get_buffer() function.
But I think this is good enough.
Unimplemented nodes are a failure now.
We make this a soft failure with a g_warning() so that during
development when adding new nodes, the renderer doesn't instantly crash,
but instead prnts a warning.
But we do consider unimplemented nodes a bug now.
Because of that, add_fallback_node() is now renamed to add_cairo_node().
Everyone should draw the error pink here, because that's what the
renderers not supporting it do, and it's also what the default shader
does.
So no matter if a renderer supports GL shaders or not, it should draw
the same pink.
When determining which way is up for the offloaded texture, we
must take all transforms into account - the ones outside the
subsurface node, and the ones inside.
By moving negative affines to be treated like dihedrals, because they
also need support of the modelview, we can free up the affine branch for
doing work without it.
Not a big win I guess, but it makes scaling more efficient.
This allows handling them without ever needing to offscreen for losing
the clip, because the clip can always be transformed.
Also, all the optimizations keep working, like occlusion culling,
clears, and so on.
The main benefit of this work is the ability for offloading to now
handle dihedral transforms of the video buffer.
The other big advantage is that we can now start our rendering with a
dihedral transform from the compositor.
This category does a finer-grained categorization than
GskTransformCategory, but it is deliberatedly made to allow
easy backwards compatibility.
The reason for the categories is that they fit our renderers more
fine.
In particular, it allows implementing wl_output_transform support more
efficiently, thereby allowing rendering buffers the right way for
rotated phone screens or monitors.
The rectangles need to touch/overlap in both directions, otherwise
there's no coverage that covers both rectangles.
Test included.
Fixes rendering glitches in various apps when redrawing.
Fixes: #6849
... instead of init_draw(); add_node(); finish_node();
We hook into the infrastructure one step earlier and close to where the
default renderer_render() and renderer_render_texture() arrive in the
nodeprocessor.
Why is this relevant?
Because process() does occlusion culling.
TL;DR: offscreens do culling now
We import them as general, so they should be exported like that.
This was a longstanding issue that I never got around to fixing and I'm
touching this code anyway atm.
See commit 3aa6c27c26 for more details.
NULL disables clearing. We only implement this for GL as in Vulkan we'd
need to create different renderpasses with different attachment
descriptions and that would require more plumbing.
We need to check that the clip is inside the opaque region, not that the
opaque region is inside the clip.
Test included, using the only not that hits the fallback path with an
opaque region smaller than its bounds.
Sometimes container nodes contain lots of overlapping opaque items. In
that case we can use the container node itself as the first node even
though none of the children cover the whole paint area.
The use case for this is a grid of cells like in a terminal where all
the cells are opaque and we want to avoid drawing the background behind
them.
If the color was specified using the legacy rgb(), we must accept
values in the range [0, 255]. But when serializing it as
color(srgb...), we must scale those values to [0, 1].
Update the one affected test.
When the color property is inherited, don't resolve the inherited
value to find the used value, just inherit the used value of the
parent style.
This is what browsers do, even though the spec says something else.
Update the currentcolor4 style test to reflect these changes.
Fixes: #6833
We were not handling the is_computed and contains_current_color
flags correctly when creating new color values. Set these flags
propertly. is_computed means: calling compute() won't change the
value. contains_current_color means what it says.
We are seeing posix_fallocate fail with ENOENT occasionally.
This shouldn't happen according to the docs, but it does. Fall back
to ftruncate if it does. It gives us less guarantees, but it makes
the ci not fail so much.
Due to the way the intermediate offscreen gets drawn, we might end up
with seams at the edges.
And I don't think it's worth spending more time on than saying "not
opaque".
Fixes the compare-render testsuite
New testcase included.
We want to operate with opacities, so it makes sense to have this radily
available.
And we're doing a walk over all children on creation anyway, so why not
just capture the rect there.
We want to be able to express opaque grids. This means that the app
provides either a row of columns of opaque nodes or a column of rows,
and then the containers will magically figure it out.
The main use case for this is terminals, which are uilt using cells. And
when there's a transparent background configured but the contents are
opaque, it'd be nice if we could figure that out.
Also remove the 80% requirement. It is rather arbitrary and while it
helps for some cases, the aforementioned grid would suffer.
Clip nodes often appear in the widget tree.
And the implementation can be trivial because of the sanity checks
already performed before calling the vfunc.
This is required because transform nodes appear everywhere.
We just exit for all transforms that can't transform the clip rect
losslessly. Both because they are rare and because we'd make the
coverage possibilities much lower.
Containers can walk the list of children back to front, trying to find
the topmost node that fully covers the viewport.
And then they can skip drawing all the nodes before that one.
Asks a node to add itself if it is fully covering the clip rectangle.
In that case, it is the first node that needs to be added.
If the node is not fully covering the clip, it should not draw itself,
because there might be stuff needing to be drawn below.
If a node adds itself, it should call gsk_gpu_render_pass_begin_op().
We find the first child that covers >80% of the container and return
that.
This is a nice speedup for the common case of a GtkWindow being covered
by a large opaque background.
It will fall apart for fancy themes that play with transparency or for
small windows because the shadow region gets too large.
But then we just scan the whole node tree.
We could think about adapting the 80% number, because that wasn't chosen
with any real scientific data behind it.
This takes both the vertical and horizontal rectangle that isn't
covering the rounded corners and intersects both with the child's opaque
rect.
And then it returns the larger of the two.
This means the small slices of a window near the left/right (or
top/bottom) will never be covered, but if we wanted that, we'd need to
use something else than a rectangle - either a region or actually a
rounded rect.
But that is a lot more expensive to implement.
Tests are node files dumped into testsuite/gsk/opaque
They are named "name-X-Y-W-H.node" with X Y W H being the expected
opaque rectangle or "name.node" if there is no opacity.
A simple example is included here.
We can in fact meet complex transforms here. Asserting that they
are simple doesn't make it so. Instead, simply bail out if a
transform is too complex; in this case we can't offload anyway,
so no need to walk the tree further.
Test included.
Fixes: #6824
We wanted premultiplied images in all cases anyway, and moving that
requirement means we can also move the caching code for re-caching
textures into the texture specific code.
This uses offscreens for every call to get_node_as_image().
This is useful both for benchmarking benefits of those implementations
as well as checking that the node-specific paths produce identical
results.
gsk_gpu_node_processor_ensure_image() was a weird amalgamation of stuff
withe weird required and disallowed flags.
Refactor it to make the two operations we actually do there more
explicit: Removing straight alpha and generating mipmaps.
This untangling is also desirable in the future when we also want to
handle colorstates here.
Always return premultiplied images.
2 fallback cases for clip and transform nodes did not require that. If
those cases turn out to be important, they can call
gsk_gpu_get_node_as_image() directly as that's the more flexible option.
Pass through to the child instead of offscreening.
I mainly implemented it for the assertion, because this might be a
sneaky way to introduce bugs without exhaustive checking that we don't
offload stuff that is offscreened.
No actual bugs that I'm aware of, so no tests.
Strictly defensive coding.
When getting a texture as image, we were always returning the texture
unconditionally.
However, we want to mipmap textures when the scale factor is too large,
and this code path did not do that.
The same codepath on the GL renderer doesn't do that either, so the test
is disabled for it.
The switch statement was ugly.
Plus, the code should be close to the add_node() vfunc implementation,
so they can be modified together.
See future commits for an example where this matters.
Make the whole window area draggable, like usually the titlebar.
This is especially useful with --undecorated.
In that case we need to make the window non-resizable though, becuase
otherwise it can be accidentally maximized and whatnot.
It didn't bring any noticable benefits and it isn't compatible with the
way we intend to do colorstate support.
And nobody seems to want to spend time on it, so let's get rid of it.
We can bring it back later if someone wants to work on it.
* The variables are const. Keep them const when casting
* Print float16 values
* Print integer values as hex. This is better for detecting
byteswapping, off by one, and such. Besides, we tend to use values
that have the same 2 hex digits, so detecting corruption is also easy.
The new renderers don't support them due to the required complexity of
integrating them with Vulkan and the assumptions those nodes make about
the renderer (the GL renderer exports its internal APIs into the
GLShader).
There haven't been any complaints that I'm aware of since 4.14 was
released where the default renderer does not support the nodes, so usage
in public seems to be close to nonexistant.
The 2 uses I know of were workarounds about missing features in GTK that
have stopped since GTK now supports them:
1. GStreamer used in to do premultiplication when the old GL renderer
did not do so in hardware but on the CPU.
2. Adwaita used it for masking before the mask node wa added in 4.10.
I was watching the log in my terminal and nothing happened.
And I wasn't sure if that was because nothing was printed or because the
same thing was printed every few seconds.
Fix that by printing a timestamp, so that in a few seconds something
else will be printed.
Previously we tracked the dead pixels, but that meant we didn't know the
alive pixels (because there's also unused pixels never accounted for).
And we would free the current atlas randomly due to that.
Now we track if any pixels are alive, and if so, we never gc the current
atlas.
After 60s, we gc the atlas, too. This ensures that after that time, we
free all cache resources, so if an application gets moved to the
background, it will no longer use GPU resources. (Well, at least the
cache won't.)
Only if a non-stale item is in the cache do we consider the cache not
empty.
Once the cache is empty, the device frees it and stops running the
periodic GC.
This is for 3 reasons:
1. Separation of concerns
The device is meant to manage the Vulkan/GL device and check stuff
like image sizes.
Caching is not part of that.
2. Refcounting
Images etc want to reference the device, but the cache wants to
reference images. If the cache is the device, that's a refcycle.
3. Flexibility
It's now easier to implement >1 cache, say one per depth or one per
color state.
If we unparent the widget, we should sever a11y relations too.
Otherwise, an a11y implementation might follow them and be surprised
to find a parentless widget (and not in a good way).
Updated tests to not check the relation on an unexpanded expander.
The unary (closure) annotation is for function pointer types; function
arguments that represent the user data to be passed to the callback are
annotated on the callback argument itself, with (closure arg-name).
Commit a4cc95b2 introduced a check in layout() that closes the popover
if the width or height is smaller than the minimum width or height,
respectively. However, that was using gtk_widget_get_preferred_size(),
which finds out the minimum height for the minimum width and vice versa,
but not the minimum height for the layout width and vice versa. So,
certain popovers were not showing, even though they would not have
generated a critical to begin with.
To fix this, we copy the logic from gtk_widget_allocate() that generates
the criticals, and use that to check if we have a good width/height for
the popover native or not.
Closes#6826
Commit b9487997 introduced shadows for GtkPopover. These are correctly
subtracted while allocating the child widget, but the child is not
measured with those shadows subtracted (as is correctly done for the
arrow). This can give criticals, for example with some wrapping labels.
To fix this, we subtract the shadow size from the `for_size` before
passing it to the measure() of the child widget.
Closes#5782Fixes#6796
The compare tests use an empty container node, but running them with
--replay ends up with empty nodes in snapshots due to how containers are
replayed.
Related: !7396
Related: #6761
Keeping the GdkRGBA requires doing later conversions, which isn't
necessary if we just keep the already converted float[4].
It also prepares for future color states, where the color will need to
be converted using the colorstate.
Fill and stroke nodes were not reporting proper offscreen-for-opacity
and preferred depth.
This was unlikely to have been noticed as their child is usually a solid
color.
Some of the properties where currentcolor might make a difference
between computed and used value are arrays, so we need to be able
to resolve arrays of values. Change things around to make resolve
a GtkCssValue vfunc and turn the existing resolve() implementations
into implementations of that vfunc.
Fixes: #6814
There is no way for callers of this function to find out if
the drop is still the same, so spewing a critical if it isn't
seems useless. Just quietly do nothing.
gtk_text_set_positions is the central place for any changes to
text caret and selection bound. And it already filters out no-change
updates. So move the remaining signals from
gtk_text_set_selection_bounds here, for more accurate updates
of cursor positions in accessibility.
Fixes: #6805
These tests check various situations with inheritance and
currentColor. In particular the caret-color test was not
working correctly before we handled used values explicitly.
The way this works is that we first apply the value changes
from animations, which will trigger recomputation of the regular
style properies if the keyframes contains custom properties.
After that is done, we resolve the used values, base on the
new computed values. This is where currentcolor is resolved.
Change the style computation machinery to populate the used
values struct, and stop relying on NULL values in the values
structs to indicate currentColor occurrences. Instead, use
gtk_css_color_value_contains_current_color() when determining
style changes.
Make gtk_css_color_value_resolve() handle situations where it can't
fully resolve a color expression. This will start to happen in the
next commits, when we make currentColor compute to itself
This commit also changes the api for gtk_css_color_value_resolve
to not take the property_id, since we already pass the currentcolor
value that it is meant to help determine. Update all callers.
Track whether a value contains currentcolor (in which case
it needs to be resolved at use time).
This just adds the bit and the getter, it isn't used yet.
When we look for what values need recomputing, we also determine what
value we are going to use to recompute. For values that contain
variables, that is the 'original' value, as returned by
gtk_css_style_get_original_value. For values that we recompute
because they may contain currentcolor, that is the specified value
as returned by gtk_css_animated_style_get_intrinsic_value.
The one issue here is that we currently don't preserve currentcolor
in the computed value, so recomputing the value does not do us
any good in the color case. That will be fixed separately.
The color property does not need recomputing here since we are
in the case where we know that the keyframes contain a color value.
And it is background-color, not background-image, that contains
the color for the background shorthand.
Determine whether the style needs recomputation, then recompute
the style, and then set the values from keyframes, so we don't
end up overwriting keyframe changes by recomputing properties from
their original values.
Fixes: #6807
The feature was apparently missing, as monitors were always fullscreened at the surface best monitor.
Keep using best monitor if the selected monitor is not specified, otherwise move the window to the selected monitor before going fullscreen.
It needed to be added to the vendored XML introspection data, so it was not
marked by a GDBus client as missing outright, and the comparison in the
property handler was also wrong.
2024-06-10 22:18:36 +02:00
717 changed files with 45179 additions and 25001 deletions
GSK render nodes can be serialized and deserialized using APIs such as `gsk_render_node_serialize()` and `gsk_render_node_deserialize()`. The intended use for this is development - primarily the development of GTK - by allowing things such as creating testsuites and benchmarks, exchanging nodes in bug reports. GTK includes the `gtk4-node-editor` application for creating such test files.
The format is a text format that follows the [CSS syntax rules](https://drafts.csswg.org/css-syntax-3/). In particular, this means that every array of bytes will produce a render node when parsed, as there is a defined error recovery method. For more details on error handling, please refer to the documentation of the parsing APIs.
The grammar of a node text representation using [the CSS value definition syntax](https://drafts.csswg.org/css-values-3/#value-defs) looks like this:
Each node has its own `<node-type>` and supports a custom set of properties, each with their own `<property-name>` and syntax. The following paragraphs document each of the nodes and their properties.
@@ -25,6 +27,59 @@ Nodes can be given a name by adding a string after the `<node-type>` in their de
Just like nodes, textures can be referenced by name. When defining a named texture, the name has to be placed in front of the URL.
# Color states
Color states are represented either by an ident (for builtin ones) or a string
(for custom ones):
color-state: <ident> | <string>
Custom color states can be defined at the beginning of the document, with an @cicp rule.
The format for @cicp rules is
@cicp "name" {
...
}
The following properties can be set for custom color states:
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.