Commit Graph

314 Commits

Author SHA1 Message Date
Matthias Clasen
cc8db1805d gsk: Be safer against bad font options
Some combinations of hint-style and hint-metrics lead to bad glyph
placement in the glyph cache, so avoid them.
2024-04-09 19:12:49 -04:00
Benjamin Otte
3080e2974d gpu: ceil() offscreen size before generating offscreen
The goal is to generate an offscreen at 1x scale.
When not ceil()ing the numbers the offscreen code would do it *and*
adjust the scale accordingly, so we'd end up with something like a
1.01x scale.

And that would cause the code to reenter this codepath with the goal to
generate an offscreen at 1x scale.
And indeed, this would lead to infinite recursion.

Tests included.

Fixes #6553
2024-04-09 17:39:32 +02:00
Benjamin Otte
9fe9ea34fd vulkan: Handle generating mipmaps for 1x1 images
Testcase included.
2024-04-08 21:06:54 +02:00
Matthias Clasen
d50b780551 gsk: Keep metrics hinting on when rendering
It turns out that we mispositioned glyphs with some cff fonts
when metrics hinting is off, and hinting is on. Since we don't
fully understand the interactions of these settings at this point,
lets preserve metrics hinting as it was on the font we got.

This at least gives folks a workaround for when they experience
clipped rendering with cff fonts: Turn on hint-metrics.

We forced hint metrics off here because it made Pango do some
creative wfh for hex boxes at small sizes, but I've dropped that
on the Pango side.
2024-04-02 09:10:46 +02:00
Benjamin Otte
195ebf6848 Merge branch 'wip/otte/gl-map-buffer' into 'main'
Add GLBuffer implementation w/ persistent mapping

See merge request GNOME/gtk!7042
2024-03-17 00:27:51 +00:00
Benjamin Otte
aff34e8d1b gpu: Sort passes correctly
In a very particular situation, it could happen that our renderpass
reordering did not work out.
Consider this nesting of renderpasses (indentation indicates subpasses):

pass A
  subpass of A
pass B
  subpass of B

Out reordering code would reorder this as:

subpass of B
subpass of A
pass A
pass B

Which doesn't sound too bad, the subpasses happen before the passes
after all.

However, a subpass might be a pass that converts the image for a texture
stored in the texture cache and then updates the cached image.
If "subpass of A" is such a pass *and* if "subpass of B" then renders
with exactly this texture, then "subpass of B" will use the result of
"subpass of A" as a source.

The fix is to ensure that subpasses stay ordered, too.

The new order moves subpasses right before their parent pass, so the
order of the example now looks like:

subpass of A
pass A
subpass of B
pass B

The place where this would happen most common was when drawing thumbnail
images in Nautilus, the GTK filechooser or Fractal.
Those images are usually PNG files, which are straight alpha. They are then
drawn with a drop shadow, which requires an offscreen for drawing as
well as those images as premultipled sources, so lots of subpasses happen.
If there is then a redraw with a somewhat tricky subregion, then the
slicing of the region code could end up generating 2 passes that each draw
half of the thumbnail image - the first pass drawing the top half and the
second pass drawing the bottom half.
And due to the bug the bottom half would then be drawn from the
offscreen before the actual contents of the offscreen would be drawn,
leading to a corrupt bottom part of the image.

Test included.

Fixes: #6318
2024-03-16 23:44:59 +01:00
Benjamin Otte
47307dc7c1 vulkan: Prefer cached buffer memory
We write the buffers in small chunks, and we even sometimes read it. So
prefer it when it's cached.

Speeds up the text benchmarks by a factor of 3x on my dedicated GPU.
2024-03-16 22:32:49 +01:00
Benjamin Otte
96b800fa0c gl: Add buffer implementation using persistent mapping
If glBufferStorage() is available, we can replace our usage of
glBufferSubData() with persistently mapped storage via
glMappedBufferRange().

This has 1 disadvantage:

1. It's not supported everywhere, it requires GL 4.4 or
   GL_EXT_buffer_storage. But every GPU of the last 10 years should
   implement it. So we check for it and keep the old code.
   The old code can also be forced via GDK_GL_DISABLE=buffer-storage.

But it has 2 advantages:

1. It is what Vulkan does, so it unifies the two renderers' buffer
   handling.

2. It is a significant performance boost in use cases with large vertex
   buffers. Those are pretty rare, but do happen with lots of text at a
   small font size. An example would be a small font in a maximized VTE
   terminal or the overview in gnome-text-editor.

A custom benchmark tailored for this problem can be created with:

  tests/rendernode-create-tests 1000000 text.node

This creates a node file called "text.node" that draws 1 million text
nodes.
(Creating that test takes a minute or so. A smaller number may be useful
on less powerful hardware than my Intel Tigerlake laptop.)
The difference can then be compared via:

  tools/gtk4-rendernode-tool benchmark --runs=20 text.node
and
  GDK_GL_DISABLE=buffer-storage tools/gtk4-rendernode-tool benchmark --runs=20 text.node

For my laptop, the difference is:
before: 1.1s
after:  0.8s

Related: !7021
2024-03-16 20:55:26 +01:00
Benjamin Otte
e7a2baf78c gpu: Remove unused arguments
It's not just unused, it's also wrong.

We are reading from the buffer when reallocating the vertex buffer
and memcpy()ing the old into the new buffer - at that point we read from
it.
2024-03-16 19:46:37 +01:00
Matthias Clasen
438d86fcf5 gsk: Move the buffer upload counter
Move the sysprof counter for buffer uploads to the generic
code, so it works for both ngl and Vulkan. This partially
reverts commit ecf1b7c18a.
2024-03-16 19:39:16 +01:00
Benjamin Otte
43373e6350 gpu: Rename env var GSK_GPU_SKIP to GSK_GPU_DISABLE
See previous commits.
2024-03-16 14:11:08 +01:00
Benjamin Otte
f725bdad25 gl: Move GL_ARB_base_instance check
It's a GLContext feature check, not a GpuRenderer thing.

So put it there.
2024-03-16 13:52:28 +01:00
Benjamin Otte
cfbe3709bf gpu: Respect the GDK_GL_DISABLE flag
It's now possible to disable sync support.
2024-03-16 13:52:21 +01:00
Benjamin Otte
141769fb46 gl: Turn has_foo flags into GdkGLFeatures
The goal is to have it mirror GdkVulkanFeatures, and in particular
having an environment variable to turn individual flags off.
2024-03-16 13:44:02 +01:00
Benjamin Otte
93cdcc5e88 gpu: Merge multiple ops into one ShaderOp
When ops get allocated that use the same stats as the last op, put them
into the same ShaderOp. This reduces the number of ShaderOps we need to
record, which has 3 benefits:

1. It's less work when iterating over all the ops.
   This isn't a big win, but it makes submit() and print() run a bit
   faster.
2. We don't need to manage data per-op.
   This is a large win because we don't need to ref/unref descriptors
   as much anymore, and refcounting is visible on profiles.
3. We save memory.
   This is a pretty big win because we iterate over ops a lot, and when
   the array is large enough (I've managed to write testcases that makes
   it grow to over 4GB) it kills all the caches and that's bad.

The main benefit of all this are glyphs, which used to emit 1 ShaderOp
per glyph and can now end up with 1 ShaderOp for multiple text nodes,
even if those text nodes use different fonts or colors - because they
can all share the same ColorizeOp.
2024-03-15 20:25:02 +01:00
Matthias Clasen
d51912c0b4 gsk: Add gsk_gpu_frame_get_last_op
This function will be used in the future to find the previous
op during node processing, so we can make optimization decisions
based on that.
2024-03-15 20:25:02 +01:00
Benjamin Otte
bad6e1e102 gpu: Change the way we merge draw calls
With potentially multiple ops per ShaderOp, we may encounter situations
where 1 ShaderOp contains more ops than we want to merge. (With
GSK_GPU_SKIP=merge, we don't want to merge at all.)

So we still merge the ShaderOps (now unconditionally), but we then run
a loop that potentially splits the merged ops again - exactly at the
point we want to.

This way we can merge ops inside of ShaderOps and merge ShaderOps, but
still have the draw calls contain the exact number of ops we want.
2024-03-15 20:25:02 +01:00
Benjamin Otte
28a8dc5a14 gpu: Add GskGpuShaderOp.n_ops
This just introduces the variable and sets it to 1 everywhere.

The ultimate goal is to allow one ShaderOp to collect multiple ops into
one, thereby saving memory in the ops array and leading to faster
performance.
2024-03-15 19:49:17 +01:00
Benjamin Otte
975cdd8c30 gpu: Remove unused return value from function
Technically, an alloc() function should return what it allocated. But
the return value is never used.

Maybe we should rename the function?
2024-03-15 19:49:17 +01:00
Benjamin Otte
153b78e2bc gpu: Add a ShaderOp.print_instance vfunc
... and add gsk_shader_op_print() to do the generic stuff.
2024-03-15 19:49:17 +01:00
Benjamin Otte
de2b10e46c gpu: Set variable to NULL after freeing
Saw this while reviewing code.
2024-03-15 19:49:17 +01:00
Benjamin Otte
30dddf2412 gpu: Refactor waiting for frames
Instead of having renderer API to wait for any number of frames, just
have gsk_gpu_frame_wait() to wait for a single frame.

This unifies behavior on Vulkan and GL, because unlike Vulkan, GL does
not allow waiting for multiple fences.

To make up for it, we replace waiting for multiple frames with finding
the frame with the earliest timestamp and waiting for that one.

Also implement wait() for GL.
2024-03-14 06:06:33 +01:00
Benjamin Otte
b43950d0f7 gpu: Don't reuse frames while they're in use
This copies the Vulkan idea of using a fence at the end of command
submission and waiting until it gets signaled before reusing the frame.

This frees up the GL driver from doing the work of making buffers etc
reusable and instead allocates new ones when they're still in use and is
a pretty massive performance win.
2024-03-14 04:53:12 +01:00
Matthias Clasen
380523b41b gsk: Eschew more divisions
Pull out a pango_scale_inv constant, and use it.
2024-03-13 01:26:14 -04:00
Matthias Clasen
2fda256bb0 gsk: Avoid some unnecessary calls
Most of the time, the image we get for the glyphs will be the
same (the atlas), so avoid adding it to the descriptor set over
and over, and check first if have to. This matches what the
pattern variant of this function already does.
2024-03-13 01:03:32 -04:00
Matthias Clasen
c71a66b6f6 gsk: Simplify our inner loop
Pull out the if-else and precompute things before the loop.
2024-03-13 01:03:31 -04:00
Matthias Clasen
cb92778478 gsk: Drop the glyph-align flag
It wasn't doing anymore what it was designed for, and we are not
sure that we need it.
2024-03-13 01:00:49 -04:00
Matthias Clasen
5f79958716 gsk: Simplify some code
Just initialize the rect directly. This matches better what the
pattern variant of this method does, and it also has the nice
side-effect of eliminating the handling of negative scales in
gsk_rect_scale, which we don't need here, since our scales are

always positive.
2024-03-12 16:13:42 -04:00
Matthias Clasen
91992111c3 Merge branch 'matthiasc/for-main' into 'main'
gsk: Rework font reloading again

See merge request GNOME/gtk!7018
2024-03-12 11:47:20 +00:00
Matthias Clasen
9454bad891 gsk: Rework font reloading again
Make a single gsk_reload_font helper that can tweak both
scale and font options, so we can ensure that our scaled
font has hint-metrics turned off (pango pays attention to
hint metrics when sizing and rendering hex boxes, and that
hurts us.
2024-03-12 00:45:14 -04:00
Matthias Clasen
0f3fbed13c Merge branch 'wip/otte/for-main' into 'main'
Don't offscreen subsurface nodes

Closes #6499

See merge request GNOME/gtk!7009
2024-03-11 23:33:24 +00:00
Matthias Clasen
7283c5c22f gsk: Avoid some roundf calls
It seems that gcc has a hard time using intrinsics for round,
so help it out by using floor (x + .5).
2024-03-11 17:41:39 -04:00
Benjamin Otte
1985f2c9ad gpu: Make intersecting rounded rect with NONE clip always succeed
This is a tricky topic, because it can make the clip bounds grow, so
previously we were trying to be careful.

However, this can cause perfectly trivial intersections to fail that are
caused by redraw diff regions.
And in the worst case, that means we offscreen in places where we
absolutely do not want to offscreen - in subtrees with subsurface nodes.

Fixes #6499
2024-03-11 01:39:40 +01:00
Benjamin Otte
01d57a7566 gpu: Initial clip is always NONE
We scissor the initial clip (which is why it's an integer rect that is
assigned to the scissor). And if we scissor, the result is a NONE clip.
2024-03-11 01:39:40 +01:00
Benjamin Otte
7a4b2c10c7 gpu: contained clip is a rectangular clip, too
So we can treat it like rect clip and promote it to NONE after a
successful scissor.
2024-03-11 01:39:40 +01:00
Benjamin Otte
208b2d37b6 gpu: Clarify clip types
CLIP_TYPE_NONE is valid if the clip is implemented by the scissor rect.

We always have a scissor rect and there's no way to draw outside of it.

In theory that means we can reset the clip to NONE at any point we
wish if we know nodes are contained inside a certain pixel-aligned
rectangle we can clip.
In practice that's probably quite hard...
2024-03-11 01:39:40 +01:00
Matthias Clasen
3f1821e372 Merge branch 'matthiasc/for-main' into 'main'
gsk: Make uber shader handle fonts the same

See merge request GNOME/gtk!6995
2024-03-09 02:19:25 +00:00
Matthias Clasen
4f1cc8ec6d gsk: Make uber shader handle fonts the same
The code converting text nodes for the uber shader was missing
some of the recent font handling improvements. Make things match.

Related: #6514
2024-03-08 20:44:42 -05:00
Matthias Clasen
5242205a65 gsk: Ignore transforms for font handling
We were turning off hinting and subpixel positioning if the
transform isn't 2D affine. The idea behind this was that transforms
likely indicate animations, and for animations, this may reduce
jitter. But the heuristic of transform==animation is not very
reliable, and we pay for this with a jump from hinted to unhinted
at the beginning and end of it. Also, the heuristic does not even
work for the most relevant 'animation' we have today: scrolling.

So, lets drop this for now. We can revisit it later.
2024-03-08 20:44:42 -05:00
Christian Hergert
ecf1b7c18a gsk/gpu: add counter for ngl buffer uploads
It's helpful to know how much we're uploading from Sysprof captures.
2024-03-08 14:17:31 -08:00
Christian Hergert
a7c5b53f46 gsk/gpu: avoid unnecessary buffer uploads
Some maps are used for read only and do not require uploading contents
back to the GPU afterwards. In other cases, we can often upload less than
the fully allocated buffer size.
2024-03-08 13:55:57 -08:00
Matthias Clasen
77a2cd4d8d gsk: Fix mis-scaled text rendering
This was happening when rotated text was partially redrawn.

A test that failed before this change is included.

Fixes: #6504
2024-03-06 17:52:35 -05:00
Matthias Clasen
0986981d1a gpu: Empty clip is still empty after transform
When transforming an empty clip, it stays empty.

Previously, we were setting it to CONTAINED, but that's wrong, because
the bounds are not contained in the clip, the clip is contained in the bounds.

This reverts part of commit a51c6aed47.

Related: !6692
2024-03-06 17:52:22 -05:00
Christian Hergert
d181d53762 gsk/gpu: add profiler node for shader loading
This is helpful when tracking down why a frameclock cycle took so long. In
quick recordings it seems to often correlate with a shader compile.
2024-03-04 11:54:13 -08:00
Matthias Clasen
4f2d63b8ac gsk: Handle hinted fonts better
Enforce the following rules:
- No hinting or subpixel positioning in transformed context
- glyph-align determines if we use integral or fractional
  device pixel positions
- For hinting, always use an integral y position (the hinter
  assumes integral positions, and only operates vertically).
2024-03-03 13:08:29 -05:00
Matthias Clasen
16a476fe22 gsk: Stop passing scale to glyph upload op
It is always 1.0 now, so there is no point. Instead of the scale,
print the font when doing verbose logging.
2024-03-02 18:39:14 -05:00
Matthias Clasen
c8ca6930c5 gsk: Use a scaled font for glyph rendering
This changes the approach we take to rendering glyphs in the
presence of a scale transform: Instead of scaling the extents
and rendering to an image surface with device scale, simply
create a scaled font and use it for extents and rendering.

This avoids clipping problems with scaling of extents in
the presence of hinting.
2024-03-02 18:39:14 -05:00
Matthias Clasen
c7806eb908 gsk: Fix handling of hex boxes
The pango code that is drawing hex boxes, invisible glyphs, etc,
is depending on the width being set in the PangoGlyphInfo. Once
we set that, everything falls into place.

Testcase included.
2024-03-01 16:51:58 -05:00
Matthias Clasen
9b5aee0d1b Cosmetics 2024-02-25 18:20:22 -05:00
Matthias Clasen
02a7a30978 Merge branch 'disable-depth-test' into 'main'
GL, NGL: Disable depth test

Closes #6401

See merge request GNOME/gtk!6917
2024-02-19 19:14:36 +00:00