We can reach the code that removes the item from the hash table
before or after the weak unref has triggered. Just leave the
weakref in place and let it do its thing, if it hasn't gone
off yet. That matches what we do in free.
Fixes: #6377
We do gc in a timeout, when an arbitrary GL context might be
current, so we need to make sure its ours and we don't free
random textures in another context.
Fixes: #6366
Count dead pixels in textures (ie the number of pixels in GPU
textures that are no longer backed by an alive GdkTexture object),
and when the there's too many, do a gc before rendering the next
frame.
Count the uses of cached texture - from the device (via the linked
list) and from the texture (via render data / weak ref), and only
free the item once the use count reaches zero.
Instead of forever running a timeout to do gc, ensure the timeout
is scheduled whenever we render a frame (this is done by calling
gsk_gpu_device_maybe_gc () before gsk_gpu_frame_render (), and
gsk_gpu_device_queue_gc () after).
Read the GSK_CACHE_TIMEOUT environment variable to override the
default 15s timeout for cache gc. This is mainly meant for debugging.
Since we don't really need two knobs, reuse the gc timeout value
for the max age of items too.
The node processing wasn't skipping 0-size nodes when using the
uber shader, leading to assertions down the road. Since the ngl
renderer doesn't use uber shaders, this only affects vulkan.
Test included.
Fixes: #6370
When we don't have an embedded font file via a url, then we want
to parse fonts "as normal", i.e. allow fallback for aliases like
"Monospace 10". This was broken when the url support was added.
Make it work again.
Update affected tests. In particular, the output of the text-fail
test goes back to be the same it was before the url changes.
GL needs version 4.2 before it supports explicit bindings. We use GLES
usually, and Mesa supports GL 4.6, so we didn't hit this case before.
However, MacOS does use GL and Mac OS is stuck on GL 4.1.
Fixes#6363
The intent of this change to get wider testing and verify that the
new renderers are production-ready. If significant problems show
up, we will revert this change for 4.14.
The new preference order is ngl > gl > vulkan > cairo.
The gl renderer is still there because we need it to support gles2
systems, and vulkan still has some rough edges in application support
(no gl area support, webkit only works with gl).
If you need to override the default renderer choice, you can
still use the GSK_RENDERER environment variable.
When a border side has a width of 0 but we're having rounded corners, we
draw content in the edges of that side, and naturally pick its color.
That is wrong though, when the width is zero, we're supposed to keep
using the color of the other side in that corner.
So do that.
Fixes the border-corner-zero-width-rendering.ui reftest.
Count how many dead pixels we have, and free the atlas if more than
half of its pixels are dead.
As part of this, change when glyphs are freed. We now keep them
in the hash table until their atlas is freed and we only do dead
pixel accounting when should_collect is called. This keeps the
glyphs available for use from the cache as long as are in the atlas.
If a stale glyph is sused, we 'revive' it by removing its pixels
from the dead.
This matches more closely what the gl renderer does.
If we gc a cached texture for which the GdkTexture is still alive,
the cached texture object will remain accessible via the render
data, so need to make sure not to leave a dangling pointer behind
here.
This is straightforward. If a texture hasn't been used for 4 seconds,
we consider it stale, and drop it the next time gc comes around.
The choice of 4 seconds is arbitrary.
Fixes: #6346
Add GSK_GPU_SKIP=glyph-align to turn off the glyph aligning.
FIXME: Should this be handled by the renderer at all or should we rely
on higher rendering layers to align glyphs properly?
This is kind of a tricky question just like with texture-scale nodes and
NEAREST filtering, because rendernodes can be embedded in other nodes
that disturb the pixel grid.
Previously, we only checked if the cache had exhausted the maximum
number of slices.
But we also need to check that the height of the slices doesn't exceed
the height of the texture.
After the node-editor crashed on me once too often, I decided to take a
good hard look at the parsing code and add a bunch of weird corner
cases into the testsuite.
That meant redoing the parser so that the error paths cause neither
crashes nor duplicated or wrong error messages.
If we see custom fonts when serializeing text nodes, write data
url that contains the font file, the first time we see it.
This does not add blobs standard fonts, like Cantarell or Monospace.
Update all affected nodeparser tests.
This will let us store complete test fonts inside node files,
as data: urls. You can also use a file: url to refer to a local
file.
The syntax is as follows:
text {
font: "FONT DESCRIPTION" url("data:font/ttf;base64,FONT DATA");
}
with the url being optional.
A PangoFont keeps a weak reference to its fontmap. In addition,
keep a strong reference in GskTextNode, so we can be sure that
custom font maps won't go away before the node is finalized.
This clip is different from "none" in that the bounds rect cannot be
ignored and that potential drawing outside the clip must be avoided.
In particular it means that clip nodes cannot be discarded if they
encompass the full clip region.
Fixes#6322
Make sure fallbacks and fill/stroke masks use image surfaces with the
same pixel grid as the target if possible.
Fixes blurriness with some path renderings.
We need to respect the offset when converting to the pixel grid, so pass
the current offset into the function.
Also move the rounded out of gsk_gpu_get_node_as_image() and into the 2
callers, because the offset is not passed into the function and I see no
reason to change that.
Instead of using the bounds of the clip region, emit individual
renderpasses for each rectangle of the clip region.
The benefit of this depends on how many pixels the clip region covers,
but for widget factory it reduces the required rendering by a huge
amount.
This is now the best clipping renderer - Cairo doesn't clip at all and
GL clips based on the extents.
Previously, we would set a scissor rect when doing a partial redraw, but
we would not clip the nodes based on that rectangle.
Do that now.
This massively reduces the amount of ops we emit for small redraws.
The Vulkan renderer can just be public API, because it doesn't expose
any Vulkan-specific APIs.
And it can just exist when compiled without Vulkan, because it can fail
to realize.
Also move get rid of the gsk/vulkan/gskvulkanrenderer.h header. It was
experimental and isn't necessary now that the renderer is included via
gsk.h.
If shaders don't support nonuniform indexing, we emulate it via if/else
ladders (or switch ladders) which get inlined by the GLSL compiles and
massively blow up the code.
And that makes compilation of the shaders take minutes and results in
shader code that isn't necessarily faster.
So we disable it on GL entirely and on Vulkan if the required features
aren't available.
As it's only an optimization and does not fall back to Cairo anymore,
this should be fine.
Make the generator generate calls for the correct glBindAttribLocation()
calls.
Usually this was done correctly, but we can't rely on it. So do it
explicitly.
When downscaling more than 2x in either dimension, force mipmap use for
the texture in a texture node.
It improves the quality of textures but takes extra work.
The GL renderer does this, too (for textures that aren't in the icon cache).
This can be disabled via GSK_GPU_SKIP=mipmap.
Fixes the big-checkerboard-scaled-down2 test.
Unless GSK_GPU_SKIP=gradients is given, we sample every point 4x instead
of 1x. That makes the shader run slower (by roughly a factor of 2.5x)
but it improves quality quite a bit.
I'm a bit unsure about using the zero rect in the fallback situtation
where one image doesn't exist, but it seems to work.
This removes the last pattern-only rendernode and with that the last
fallback usage with disabled ubershader.
This way we can toggle opacity handling on/off.
THe shader slowly turns into a fancy texture op - but I don't want to
rename it to "fancytexture" just yet.
A variation is a #define/specialization constant that every shader can
use to specialize itself as it sees fit.
This commit adds the infrastrcture, future commits will add
implementations.
If we enter the situation where we need to redirect the clipping to an
offscreen, make sure that:
* the ubershader gets only used when beneficial
* we size the offscreen properly and don't let it grow infinitely.
Fixes the clip-intersection-fail-opacity test
There are various places where the alpha is implicitly assumed to be
handled, so just handle it.
As a bonus, this simplifies a bunch of code and makes the texture node
rendering work with alpha.
Use an offscreen and mask it if the clips get too complicated.
Technically, the code could be improved to set the rounded clip on the
offscreen instead of rendering it as a mask, but that would require more
sophisticated tracking of clip regions by respecting the scissor, and
the current clip handling can't do that yet.
This removes one of the last places where the GPU renderer was still
using Cairo fallbacks.
This is for generating descriptors for more than 1 image. The arguments
for this function are very awkward, but I couldn't come up with better
ones and the function isn't that important.
And the calling places still look a lot nicer now.
For now this uses Cairo to generate a mask and then runs a mask op.
This is different from just using fallback in that the child is rendered
with the GPU and not via fallback.
A generic part that can be shared by all gradient shaders that does the
color stop handling and a gradient-specific part that needs to be
implemented individually by each gradient implementation.
If there are more than 7 color stops, we can split the gradient into
multiple gradients with color stops like so:
0, 1, 2, 3, 4, 5, transparent
transparent, 6, 7, 8, 9, 10, transparent
...
transparent, n-2, n-1, n
and use the new BLEND_ADD to draw them on top of each other.
Adapt the testcae that tests this to use colors that work with the fancy
algorithm we use now, so that BLEND_ADD and transitions to transparent
do not cause issues.
Instead of scaled coordinates, use the unscaled ones.
This ensure that gradients get computed correctly as they are not safe
against nonorthogonal transforms - like scales with different scale
factors.
The shader can only deal with up to 7 color stops - but that's good
enough for the real world.
Plus, we have the uber shader.
And if that fails, we can still fall back to Cairo.
The code also doesn't handle repeating linear gradients yet.
This shader can take over from the ubershader. And it can be used
instead of launching the ubershader when no offscreens are necessary.
Also includes an optimization that uses the colorize shader when
appropriate.
The ubershader has some corner cases where it can't be used, in
particular when the child is massively larger than the repeat node and
the repeat node is used to clip lots of the source.
It's better than the Cairo renderer, so use it instead.
It's still only picked once GL fails, so it will probably only ever be
picked when people use GDK_DEBUG=gl-disable, but at least it will be
picked.
per-backend renderers and GL renderers are a different thing, so treat
them as such.
Also, try the GL renderer unconditionally. The renderer initialization
code will take care of GL not being available.
This is using the Vulkan renderer.
It also allows claiming support for all the formats that only Vulkan
supports, but that neither GL nor native mmap can handle.
Add GSK_GPU_IMAGE_RENDERABLE and GSK_GPU_IMAGE_FILTERABLE and make sure
to check formats for this feature.
This requires reorganizing code to actually do this work instead of just
pretending formats are supported.
This fixes GLES upload tests with NGL.
This ensures both that we signal a semaphore for a dmabuf when we export
an image and that we import semaphores for dmabufs and wait on them.
Fixes Vulkan node-editor displaying the Vulkan renderer in the sidebar.
Make gsk_renderer_render_texture() create a dmabuf texture if that is
possible.
If it isn't (ie if we're not on Linux or if dmabufs are otherwise not
working) fall back to the previous code of creating a memory texture.
When using the uber shader a lot, we may overflow the (only 16kB large)
storage buffer.
Stop crashing when that happens and instead just allocate a new one.
This makes the (currently single) storage buffer handled by
GskGpuDescriptors.
A side effect is that we now have support for multiple buffers in place.
We just have to use it.
Mixed into this commit is a complete rework of the pattern writer.
Instead of writing straight into the buffer (complete with repeatedly
backtracking when we have to do offscreens), write into a temporary
buffer and copy into the storage buffer on committing.
The GL branch should eventually call into gdk_gl_context_get_scale(),
which is what checks for GDK_DEBUG=gl-fractional; whereas the Vulkan
branch needs no change.
If we have the choice between running the ubershader or a normal shader
with offscreens, make the choice depend on if the ubershader would
offscreen anyway.
If so, just run the normal shader.
This really gets rid of all ubershader invocations in Adwaita
widget-factory.
Instead of using an enum, use a usual custom class struct like we use
for GskGpuOp.
As a side effect of that refactoring, the display gained a hash table
for textures where we can't use the render data because the texture is
used in multiple renderers.
The goal here is that a texture is always cached and we can ensure that
there is a 1:1 relation between textures and their GskGpuImage. This is
important in particular for external textures - like dmabufs - where we
absolutely don't want 2 images with 2 device memories, and where we use
toggle references to keep them alive.
Reserve 3 texture units per immutable sampler (because that's the
maximum per YUV sampler).
Ensure that the max-sampler calculations always include the immutable
samplers, too.
We now handle the case where memory is not HOST_CACHED.
We also track the memory type now so we can avoid mapping image memory
that is not HOST_CACHED and use buffer transfers instead.
Shader compilers struggle with compiling code that indexes texture
arrays by indexes, so keep the fallback shaders simple and don't do that
there.
There's not much of a performance difference anyway between those two
methods.
In the case where descriptor indexing is not enabled and the number of
max images is small (or we use extensive amounts of immutable samplers),
we need to be able to switch descriptors.
This patch makes that possible.
We compile custom shaders for Vulkan 1.0 that don't require the
extension.
We also ensure that our accesses are uniform by only executing one
shader at a time.
Let the objects track the number of samplers or buffers needed.
This is a required step for making Vulkan work with less featureful
(read: mobile) implementations.
This is relevant went encountering repeat nodes, where the repeat cutoff
will make the fwidth of the position go wild otherwise.
Gradients require more work now, because we need to compute offsets
twice - once for the pixel, once for the offst.
Carry an n_external_textures variable around when selecting programs and
compile different programs for different amounts of external textures.
For now, this code is unused, but dmabufs will need it.
This adds GSK_GPU_IMAGE_CAN_MIPMAP and GSK_GPU_IMAGE_MIPMAP flags and
support to ensure_image() and image creation functions for creating a
mipmapped image.
Mipmaps are created using the new mipmap op that uses
glGenerateMipmap() on GL and equivalent blit ops on Vulkan.
This is then used to ensure the image is mipmapped when rendering it
with a texture-scale node.
Add a GSK_GPU_IMAGE_STRAIGHT_ALPHA and use it for images that have
straight alpha.
Make sure those images get passed through a premultiplying pass with
the new straight alpha shader.
Also remove the old Postprocess flags from the Vulkan image that were a
leftover from copying that code from the old Vulkan renderer.
There's a well hidden line in the spec that says in
https://registry.khronos.org/vulkan/specs/1.3/html/chap15.html#interfaces-resources-descset
If the combined image sampler enables sampler Y′CBCR conversion,
it **must** be indexed only by constant integral expressions when
aggregated into arrays in shader code, irrespective of the
shaderSampledImageArrayDynamicIndexing feature.
So we'll use the same trick that we use for old GL here and do an
if dance that gives us dynamically uniform expressions.
This now uses all the previously added features to allow displaying YUV
images.
Also add a utility function that turns an image into a toggle ref for a
texture. This makes sure that reffing the image also refs the texture
and that ensures that textures stay alive as long as the image is in
use.
For now, the flags are just there because, and nobody uses them yet.
The only flag is EXTERNAL, which for now I'm using for YUV buffers,
though it's a bit undefined what that means.
Images can now have samplers - meaning they must be rendered with that
sampler. It also means that sampler must be handled as an immutable
sampler in descriptorsets.
These samplers can be created with a samplerYcbcrConversion, so code has
been added to pass that conversion when creating the imageview.
Also add code to GskVulkanFrame to track immutable samplers.
Nobody is making use of this yet.
Define an array with a compile-time-constant variable size for the
immutable samplers.
A bunch of work is necessary to ensure that at least one element is in
the sampler array, because the GLSL code
sampler2D immutable_textures[0];
is invalid.
This allows having different layouts sothat we can support immutable
samplers, whcih are required for multiplane and YUV formats.
We don't use them yet.
use it to collect the optional features we are interested in and turn
them on only if available.
For now we add the dmabuf features, but we don't use them yet.
The main reason here is that we want to not fail when the texture size
is larger than the supported GpuImage size.
When that happens, for now we just fallback slowly - ulitmately to
drawing with Cairo, which is going to be clipped.