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 gtk_snapshot_offset(), provide a full set of functions
kept in sync with GtkTransform APIs.
On top of that, add gtk_snapshot_save() and gtk_snapshot_restore()
mirroring cairo_save()/restore() that allow saving a snapshot's
transform state.
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.
Instead of just tracking 2 integer translate_x/y coordinates, tracka a
full GtkTransform.
When creating actual nodes, if the transform is simple enough, just
create the node in a way that makes use of the transform. If the
node, can't represent the transform, just push a transform node instead
and automatically pop that node with the next gtk_snapshot_pop() call.
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.
Instead of style + rect_of_one_box, pass the new GtkCssBoxes object.
This has the nice side effect that when drawing background + border +
outline, we only compute all the boxes we need once.
Most of the time, the GtkSnapshot objects we create while snapshotting
widgets don't end up containing all that many nodes or states in their
respective node or state stack. This undermines the amortized allocation
behavior of the G(Ptr)Array we use for the stacks. So instead, use the
(until now unused) parent_snapshot GtkSnapshot* passed to
gtk_widget_create_render_node and reuse its node and state stack.
We do not avoid allocating a new GtkSnapshot object, but we do avoid
allocating a ton of G(Ptr)Array objects and we also avoid realloc'ing
their storage.
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.
A container node inside another container node doesn't make a lot of
sense, we can instead just use the parent container node and add the
child container node's children to it directly.
If widgets want to clip things, they now need to do it themselves.
By not taking care of clip, we avoid the need to track clip. And by not
tracking clip, we can avoid all unnecessary cache invalidations that we
were doing for render nodes whenever the clip changed.
And when you are scrolling, the clip changes *a lot*.
1. Pass clip rectangles to gtk_snapshot_push_state() that point into
the state array.
2. g_array_set_size(len+1) the state array
3. Make that function realloc() the state array.
4. The clip rectangle now points into invalid memory
5. Use the clip array
This patch fixes things by moving step 5 to before step 2.
This allows being more specific about the size.
It's useful in particular when the resulting render nodes might be
too small for the size, not only when they are too large. For the
latter case, using a clip node would be enough.
It also requires adding a clip node when rendering the resulting
paintable, but that should be optimized out by GtkSnapshot when not
necessary.
When the clip changes that is passed to a snapshot function, we need to
create eventual cached render nodes because they might not have drawn
their whole area before.
Fixes issues with redrawing when scrolling.
This requires a bunch of refactorings:
1. Don't pass the current clip region to gtk_widget_snapshot()
so we don't create full widget contents
3. Have a widget->priv->draw_needed that we invalidate on every
queue_draw() call and set on every snapshot()
2. In queue_draw(), walk the widget chain to invalidate the
render nodes of all parents
This is the equivalent snapshot function to pango_cairo_show_layout().
Not to be confused with gtk_snapshot_render_layout(), which is the
equivalent to gtk_render_layout().
In certain cases, we might create large cairo nodes, resulting in
surfaces so large, cairo can't handle them. Fix this by limiting the
cairo node to the current clip region.
Remove all the old 2.x and 3.x version annotations.
GTK+ 4 is a new start, and from the perspective of a
GTK+ 4 developer all these APIs have been around since
the beginning.
This way, we can also clip the created node bounds to the current clip
of the GtkSnapshot. This works as long as we don't modify the start and
end points, and happens all the time while rendering.
Clipping a color node is trivial so we do it here directly since that
might later save the entire clip node as well as freeing the fragment
shaders from coloring lots of pixels that will be clipped away.
Color matrix nodes as the child of other color matrix nodes can happen
quite frequently as a result of CSS. To ease the renderer
implementations, collapse chains of color matrix nodes into one.
If the rounded clip node is rectilinear, we can simplify it to a normal
clip node. If not, we really need to use a rounded clip node. In both
cases, we can do the same check we do when collecting normal clips and
avoid the clip node altogether if the child node does not get clipped
anyway.
This saves between 3 and 10 nodes in the widget factory, depending on
what page gets rendered.
Clip nodes have a clip rect and we only need to actually create a clip
node if any child node gets clipped at all. If the clip rect conains the
child node bounds entirely, we don't need a clip node.
I got a lot of "clip in clip" cases, for example a CellClip with a
CellTextClip inside. It is really trivial to merge these when we
pop and makes it easier for all backends, so lets do that.
The Vulkan renderer creates a fallback surface for each shadow
node, even if we end up not rendering anything to it. Avoiding
this is a nice optimization.
An empty container has the same effect as transparency
with the cairo renderer, but creates black with Vulkan.
To avoid this, explicitly use a transparent color node.
This fixes the css blendmode example in gtk4-demo with
the Vulkan renderer.
This is important since _push_state returns a pointer into a GArray
which could be invalidated and point to garbage after the subsequent
push_state call.
Instead of creating one GPtrArray per GtkSnapshotState and saving nodes
in there, create one GPtrArray per snapshot and assign a
start_node_index to every GtkSnapshotState as well as a n_nodes variable
so every state knows which nodes belong to it.
This showed up in profiles in certain scenarios, so export a
_get_n_shadows getter instead and let callers provide a sufficiently
large allocated array of GskShadows, which we can use with
g_alloc/g_newa.
We can't pass the same string to two different snapshot states since
removing one of them will free the passed string, so just create another
one for the second state.
gtk_snapshot_pop() => removed
gtk_snapshot_pop_and_append() => gtk_snapshot_pop()
So now there is no way to get a rendernode out of the snapshotting API
until you gtk_snapshot_finish().
... and use it.
The function is a bit awkward because it requires 2 calls to
gtk_snapshot_pop(), but once you accept that, it's very convenient to
use, as can be seen by the 2 implementations.
We already take ints when setting the translation, so it can't
currently take any other values. Additionally, I was seeing large
costs in int -> double -> int for the rects in
gtk_snapshot_clips_rect(), as all callers really are ints (widget
allocations) and the clip region is int-based.
This change completely cleared a 2% rectangle_init_from_graphene from
the profile and is likely to have nice performance effects elsewhere
too.
This means we allocate the collect data with the state, avoiding
an extra allocation. Also, a union means every state object
is the same size and we could reuse the state objects.