Instead of the previous approach using GVariant, this new approach uses
human-readable text files as the serialization format for render nodes.
The format is a custom one, but it is inspired by QML and conforms to
the CSS syntax. Because of that, we can use the CSS machinery from GTK
to parse it, and in particular share code to parse properties that GTK's
CSS machinery also supports, such as colors.
This commit breaks all existing usages of node files - such as the
testsuite and various test tools - they will be fixed in further
commits.
Change the way we compute border color cutoffs to the same method that
browsers use. This method does not consider the corner sizes at all and
only looks at border-width.
Previously, when borders were too big - ie when a 100x100 rect had only
one 100x100 border, like the black part of ◔ - and then shrinking this
rect by 25px on either side, we'd end up with a 50x50 rect with a 75x75
border, and that's obviously not correct.
Floating point values cannot ever be compared for equality. GLib has a
G_APPROX_VALUE macro that lets us compare two value within a provided
precision, so we should use that instead.
Artisanal, homegrown, locally sourced, vegan reference counting has been
replaced by the appropriate API in GLib, which does small things like
saturation and type checking.
Apparently genTextures and friends only "reserves names", initializing
them will actually create them. Using glObjectLabel on textures before
initializing them will throw a GL_INVALID_VALUE error.
When rendering to a texture, collecting the render ops might bind a
different framebuffer, so bind the one we want again before doing the
actual rendering.
This adds debug groups in various places, including the debug
nodes if those are in use. This makes the traces in tools like
renderdoc much easier to read.
GL keeps the unoform state per-program, but not per-frame. So, we can't
pretend that this works for us. Keep the RenderOpBuilder around for the
entire lifetime of the renderer instead.
This fixes rendering to a texture on intel hardware. The glClear calls
would throw a GL_FRAMEBUFFER_INCOMPLETE error here, because the
gsk_gl_driver_begin_frame() call in do_render() reset the framebuffer
object in use.
This fixed the reftest introduced in the previous commit.
I'm using a mesh gradient here instead of drawing 4 individual sides to
avoid artifacts when those sides overlap in rounded corners.
We don't want the new transform while drawing things on a texture.
Instead, only apply the new transform matrix when adding the final
texture drawing ops.
This fixes the stack cube rotation transition to at least look somewhat
better.
When sending render nodes from the client to the daemon we add an id,
and whenever we're about to re-send the entire tree node we instead
send the old id. We track all the nodes for the previous frame
of the surface this way.
Having the id on the daemon side will allow us do to much better deltas.
gsk/gskenums.h:181: Error: Gsk: multiple "@GSK_TRANSFORM_CATEGORY_2D" parameters for identifier "GskTransformCategory":
* @GSK_TRANSFORM_CATEGORY_2D: The matrix is a 2D matrix. This is equivalent
^
gsk/gsktransform.c:1342: Warning: Gsk: gsk_transform_to_2d: unknown parameter 'm' in documentation comment, should be 'self'
gsk/gsktransform.c:1368: Warning: Gsk: gsk_transform_to_2d: invalid return annotation
gsk/gsktransform.c:1461: Warning: Gsk: gsk_transform_to_translate: unknown parameter 'm' in documentation comment, should be 'self'
This reinstates diffing in the same way that it worked for offset nodes.
It would be possible to add diffing for affine transforms or even all
transforms, but I think this is unnecessary right now - and also quite
expensive to compute.
Make the API expect a tranform of the proper category instead of
doing the check ourselves and returning TRUE/FALSE.
The benefit is that the mai use case is switch (transform->category)
statements and in those we know the category and don't need to check
TRUE/FALSE.
Using the wrong matrix will now cause a g_warning().
... instead of computing it every time we need it.
This should be faster and we want to use it a lot more prominently.
Also, we have the struct memory available anyway.
In particular, add a per-category querying API for the matrix:
- gsk_transform_to_translate()
- gsk_transform_to_affine()
- gsk_transform_to_2d()
- gsk_transform_to_matrix()
This way, code can use the relevant one for the given category.
Since we can do partial redraws, dropping every shadow that's been
unused for one frame happens too fast. This is also a problem when a
shadow gets drawn on a texture for a few frames.
This can happen for certain transform nodes. The transform node's
child's bounds are fine, but the transform node bounds are all nan.
Just ignore those bounds since we can't meaningfully render them anyway.
If the given matrix is explicitly of category IDENTITY, we don't need to
do anything, and in the 2D_TRANSLATE case, just offset the child bounds.
Those are the two most common cases.
The code didn't change, it was just shuffled around to make the
with_bounds() versions of the text rendering unnecessary and instead
pass through the generic append_node() path.
They were a neat idea while they lasted. But now, it's time for
categorized transform nodes, where matrices with
GSK_MATRIX_CATEGORY_2D_TRANSLATE are the exact replacement.
Renderers have not been adapted for this purpose, so they (continue to)
run slow paths.
The first set of glyphs is created with a timestamp of 1. Later we
subtract the glyph timestamp from the cache timestamp, meaning we end up
with numbers ending in 9, e.g. 59. Now unfortunately !(60 <= 59), so we
do not end up incrasing the old_pixels count of the cache. Later we then
call lookup() and DEcrease the old_pixels count, which makes the
unsigned int wrap and cause a huge old_pixels value, which causes us to
drop the cache.
The @filename@ directive will use the full path of the file being parsed
for enumeration types; we should use @basename@, instead, as it improves
the reproducibility of the build by using only the file name.
Some of the flags got lost in the meson transition or were demoted from
error flags to warning flags.
This commit reintroduces them.
It also includes fixes for the code that had warnings with those flags.
The big one being -Wshadow.
This broke the overlay blur demoe when resizing the window to a size
that would completely move the image below a button, causing the
GtkSnapshot code to remove the clip node below the blur node.
So we can check that the currently set clip is the first one and now
intersect with it. This first clip is always the entire viewport or the
entire render_area and we don't want to end up drawing things to a
texture because of it.
Instead of getting the translation x/y everytime we use the modelview,
get it once, when extracting the metadata. Do the same with the scale.
And save if the matrix is "simple" at all, i.e. if it only consists of a
translation and/or scale. This will be helpful later when we start
drawing transformed nodes on textures.
We do this for every single node, which is a little costly, especially
since the common case for the modelview matrix these days is a simple
translation. So, check whether the new modelview matrix is only a
translation matrix and if so, don't do a full matrix multiplication per
node.
Some of the _diff implementations did a whole bunch of work just to
throw it away afterwards and invalidate the entire union of the two
render nodes, most notably the two clip nodes. Fix this to only call
gsk_render_node_diff_impossible if the previous if-condition is FALSE
and not always.
This reverts commit 8e74eb382f.
This code is not necessary. It worked around a bug in graphene where
graphene was requiring stricter alignment than glib allocators could
guarantee.
Any data that is later fed to graphene must be
allocated with proper alignment, if graphene
uses SSE2 or GCC vector instructions.
This adds custom array code (a streamlined copy
of GArray with all unnecessary bells and whistles removed),
which is then used for the state_stack instead of GArray.
There's also a runtime check for the size of GtkSnapshotState
itself being a multiple of 16. If that is not so, any array
elements past the 0th element will lose alignment.
There are probably struct attributes that can
make GtkSnapshotState always have size that is a multiple
of 16, but we'll burn that bridge if we cross it.
The code is mostly stolen from graphene.
Allocators support any alignment, but their implementation
only calls system aligned allocator functions if malloc()
is not aligned to 16-byte boundaries. If it is aligned,
the implementation just calls malloc() regardless of which
alignment is requested by the caller.
This can be fixed by saving the result of meson malloc()
alignment check and adding a few conditions to the implementation,
but right now GSK and GTK only need 16-byte alignment either way.
1. Include the broadway renderer (so we can test it properly fails on
Wayland or X11)
2. List all potential renderers, print useful information when Vulkan
is not compiled in instea dof omitting it
3. Improve docs
As they require a draw context and the draw context is already bound to
the surface, it makes much more sense and reduces abiguity by moving
these APIs to the draw context.
As a side effect, we simplify GdkSurface APIs to a point where
GdkSurface now does not concern itself with drawing anymore at all,
apart from being the object that creates draw contexts.
Previously, we got the damage, then computed the changed area, then
started a frame with that changed area.
But starting a frame computes the damage for us.
So now we start a frame, then get the damage area from that, then
compute the change area.
This does nothing but disallow passing NULL to gdk_surface_begin_paint()
and instead require this context.
The ultimate goal is to split out Cairo drawing into its own source file
so it doesn't clutter up the generic rendering path.
And of course, gsk_render_node_get_name() is gone, too.
The replacement is of course debug nodes.
As a side effect, GskRenderNode is now *really* immutable.
We pulled out the bounds calculation for performance reasons, but the
caller can't know how to properly compute them. Inside gtk+, we can do
that but it's not good enough for public API.
When the max cost for finding a path gets to high, the diff can now be
aborted.
Because render nodes have a fallback method (by just marking the whole
bounds of the nodes as different), we use this to improve performance
of diffs.
This brings fishbowl (which is basically a container node with N images
that change every frame) back to close to previous performance.
Now that we have the full render nodes available, there is not much
benefit in fine-grained control over multiple rectangles.
In particular, it's causing pain with complex regions.
There might be a benefit in clipping to the region's rectangles in cases
like widget-factory where the whole diff is made up of the 2 rectangles
of spinner and the pulsing progress bar, but it needs a good heuristic
for where this is useful.
... and diff the previous node with the current one to determine the
clip region.
This doubles the work necessary to track clip regions, but the following
commits will clean that up.
It doesn't need to be exported anymore.
As a side effect, the inspector no longer has any information about the
render region, so remove the code that was taking care of that.
This includes a copy of the diff(1) algorithm used by git diff by Davide
Libenzi.
It's used for the common case ofcontainer nodes having only very few
changes for the few nodes of child widgets that changed (like a button
lighting up when hilighted or a spinning spinner).
... and gsk_render_node_can_diff(). Those are vfuncs to compute a region
containing all the pixels that differ between the two nodes.
This is just the plumbing that chains into node classes. No node
implements it yet.
Adding the offset node broke serialization in 2 ways:
1. We store the enum value in the node, so make sure to not change it
for existing values
2. The offset node was missing in the deserialization lookup table
This is a special case of the transform node that does a 2D translation.
The implementation in the Vulkan and GL renderers is crude and just does
the same as the transform node.
Nothing uses that node yet.
This way, we can postpone the actual rendeing of the node until the
renderer. This allows the renderer to choose the right scale to
render at, so it can decide to use 2x scale for hidpi on its own.
Last but not least, it makes all nodes independent of the context they
are created in, because they do not need to know at snapshot time what
they will ultimately be rendered into.