Colors are not state that we carry across draw ops,
so setting the color on the render job doesn't make
much sense. Instead, pass the color to the various
draw calls. Add a few new ones for that purpose.
Also, shorten the names of some by going from
'load_vertices_from_offscreen' to 'draw_offscreen'.
This reduces how many changes we make when recording uniform state, which
increases the chances that the data offset will be the same when applying
uniforms.
Since we make full snapshots when recording uniform state of batches, we
need to perform some deduplication to avoid so many repeated uniform calls.
This uses a closed hashtable to determine if we are likely changing the
value to something new.
This does not currently compare values, it instead only compares that we
are going to point at a new offset into the uniform buffer. We could go
further if we compare upon updating values (we did that early on in the
prototype) so that offsets are less likely to be changed.
When the color passed is transparent black, use
the color from the texture as source, instead of
as mask. This lets use use the coloring program
both for regular and color glyphs, avoiding
program changes in text with Emoji.
Instead of using uniforms for color used in multiple
programs, pass it as vertex attributes. This will let
us batch more draw calls, since we don't have to change
uniforms so often. In particular, for syntax-highlighted
text.
We may want to change the interface between the
shaders and the renderer for ngl, and therefore,
sharing the shaders between gl and ngl will not
be practical, going forward.
Hook up the "Show fallback rendering" switch for Vulkan.
This brings home the sobering truth that the Vulkan renderer
is doing *all* fallback, since we switched from offset nodes
to transform nodes.
The primary goal here was to cleanup the current GL renderer to make
maintenance easier going forward. Furthermore, it tracks state to allow
us to implement more advanced renderer features going forward.
Reordering
This renderer will reorder batches by render target to reduce the number
of times render targets are changed.
In the future, we could also reorder by program within the render target
if we can determine that vertices do not overlap.
Uniform Snapshots
To allow for reordering of batches all uniforms need to be tracked for
the programs. This allows us to create the full uniform state when the
batch has been moved into a new position.
Some care was taken as it can be performance sensitive.
Attachment Snapshots
Similar to uniform snapshots, we need to know all of the texture
attachments so that we can rebind them when necessary.
Render Jobs
To help isolate the process of creating GL commands from the renderer
abstraction a render job abstraction was added. This could be extended
in the future if we decided to do tiling.
Command Queue
Render jobs create batches using the command queue. The command queue
will snapshot uniform and attachment state so that it can reorder
batches right before executing them.
Currently, the only reordering done is to ensure that we only visit
each render target once. We could extend this by tracking vertices,
attachments, and others.
This code currently uses an inline array helper to reduce overhead
from GArray which was showing up on profiles. It could be changed to
use GdkArray without too much work, but had roughly double the
instructions. Cycle counts have not yet been determined.
GLSL Programs
This was simplified to use XMACROS so that we can just extend one file
(gskglprograms.defs) instead of multiple places. The programs are added
as fields in the driver for easy access.
Driver
The driver manages textures, render targets, access to atlases,
programs, and more. There is one driver per display, by using the
shared GL context.
Some work could be done here to batch uploads so that we make fewer
calls to upload when sending icon theme data to the GPU. We'd need
to keep a copy of the atlas data for such purposes.
When we are rendering a texture node to an offscreen,
and we have a clip, we must force the offscreen rendering.
Otherwise, the code will notice: Hey, it already is a texture
node, so no need to render it to a texture again. But when
clipping is involved, that is exactly what we want to do.
Testcase included.
Fixes: #3651
There isn't any state to modify in the type so we can use const here.
Doing so allows some of the renderer code to use const across a
number of functions so that repeated calls are elided if inlined.
These do not do modify anything so they can be marked as pure to
potentially ellide calls. Since they do dereference, I do not believe
we can make them const although that is unclear since we could technically
just return a pointer + offset. Therefore it *might* be possible to also
make these G_GNUC_CONST.
This also removes the return if fail macros from these as a good portion
of them didn't have them anyway. I think it's fair to say that access to
these incorrectly is a programmer error.
It significantly reduces the amount of code generated into generally a
movss,ret.
According to OpenGL spec, a shader object will only be flagged
for deletion unless it has been detached; when a program object
is deleted, those shader objects attached to it will be detached
but not deleted unless they have already been flagged for deletion.
So we shall detach a shader object before it is deleted, and delete
it before the program object is deleted best.
Here is a command to reproduce this testcase:
GDK_DEBUG=gl-gles gtk4-demo --run gears
Without this patch, Mesa throws this compile error:
0:130(13): error: no matching function for call to `mod(error, float)'; candidates are:
This is caused by `u_rotation - 90` being of type error since
`u_rotation` is a float and it’s illegal to subtract it with an integer.
The special case for ASCII glyphs is unfortunately not
working very well, because of an oversight in pango:
When I added subpixel positioning, I made pango_shape()
default to not rounding, and make PangoLayout call
pango_shape_with_flags() and pass the rounding information
down. The upshot is that we need to use the _with_flags
variant here and tell it to round position, so it matches
what the text node contains.
This way we can render the first frame of tests/testoutsetshadowdrawing
in 153 ops instead of 183.
And the first frame of gtk4-demo in 260 instead of 300.
These positions are not guaranteed to be in a specific order when linked
into the final GPU program. They need to be specified so that our code
in gskglrenderer.c can use known positions for them to match up with
our GskQuadVertex.
This fixes the GL renderer on macOS's OpenGL shader compiler.
Fixes#3420
Catch the error when it happens, so that we can emit a specific and more
helpful error message.
Also verify that all branches in the code now do indeed set a proper
GError when they fail, so that the final catch-all is no longer needed.
Instead, assert that the error is set so that we catch future code
additions early that do not set the GError.
The use of volatile was incorrect in GLib and has been that way for
a long time. Recently however that has changed, and this makes GTK
follow suit to avoid using volatile in the type registration.
See also: https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1719
Combined with the above merge request for GLib, this fixes a large
number of compilation warnings when using Clang.
Using GtkCssSection in public headers here may be
ok from the C perspective, since it all ends up in
the same library anyway. But it causes circular
dependency problems for our gir files that are still
split by namespace.
To avoid this problem, copy the GtkCssLocation struct
struct as GskParseLocation, and pass take two of them
instead of a GtkCssSection in the error callback.
Update all users.
Fixes: #2454
glFrameBufferTexture maps to all faces of a cube and that is not needed
here. Additionally, texture_id is not deleted after we use the additional
flipped texture, but should be.
Currently GTK can be built with G_ENABLE_DEBUG which enables various debug code and parsing
of those env vars, or without, which instead of parsing them prints a warning if they are set.
While building with G_ENABLE_DEBUG isn't strictly needed it's the only way to make GTK_DEBUG=interactive work,
which is a nice thing to have always.
This enables parsing of those env vars in any case and allows specific values being marked as also
available when not built with G_ENABLE_DEBUG (interactive for example). If not built with G_ENABLE_DEBUG
then all unavailable values will be marked as such in the help output and a note is added that
GTK needs to be built with G_ENABLE_DEBUG to use them, which should help discoverability.
On desktop GL, GL 1.5 or GL_ARB_occlusion_query is required to get the
glGenQueries() etc. symbols. This isn’t the case on GLES, where they
are provided by GL_EXT_occlusion_query_boolean, and more importantly
have never been made core.
This patch allows gtk4-demo to start when GDK_DEBUG=gl-gles is set, on
my Mali 400 MP running the Lima driver from Mesa.
When encoding big blobs of data in base64, insert newlines.
Base64 allows it, CSS allows it, so not need to make GtkTextView
struggle with multi-megabyte lines.
Update nodeparser tests to reflect this change.
Rename _gtk_css_print_string to strip the _ and add
an insert_newlines argument to it. Update all callers,
and make the render node serializer insert newlines.
We're caching two things, either a node itself being rendered, or a
parent storing a cached version of a child as rendered to an offscreen
the size and location of the parent.
If both the parent and child uses the cache this will cause a conflict in
the cache as it is currently use keying of a node pointer which will have
the same value for the node-as-itself and the child-node-of-the-parent.
We fix this by adding another part to the key "pointer_is_child" which means
we can have the same node pointer twice in the cache.
Additionally, in the child-is-rendered-offscreen case the offscreen
result actually depends on the position and size of the parent viewport,
so we need to store the parent bounds in that case.
This allows us to avoid updating uniforms if that is not necessary. This
in turn allows us to sometimes reuse the same draw op by just extending the
vertex array size we draw rather than doing a separate glDraw call.
For example, in the fishbowl demo, all the icons added at the same
time will have the same time and size, so we emit single draw calls
with 100s of triangles instead of 100s of draw calls with 2 triangles.
For vulkan/broadway this just means to ignore it, but for the gl
backend we support (with up to 4 texture inputs, which is similar to
what shadertoy does, so should be widely supported).
A GskGLShader is an abstraction of a GLSL fragment shader that
can produce pixel values given inputs:
* N (currently max 4) textures
* Current arguments for the shader uniform
Uniform types are: float,(u)int,bool,vec234)
There is also a builder for the uniform arguments which are
passed around as immutable GBytes in the built form.
A GskGLShaderNode is a render node that renders a GskGLShader inside a
specified rectangular bounds. It renders its child nodes as textures
and passes those as texture arguments to the shader. You also pass it
a uniform arguments object.
Print out the full assembled shader sources when
GSK_DEBUG=shaders is given. This is very verbose,
but may be useful to see what we actually pass
to the compiler.
This adds a gsk prefix to the stuff in the preamble, as we want to
avoid it conflicting with things in the main shader. Especially once
we start allow some customization of shaders.
Almost always the source is created by combining various sources, which
means the line numbers in the error messages are hard to use. Adding
the line numbers to the source in the error message helps with this.
There is no real reason to have this on the side indexed via the
index, as it is stored next to each other anyway. Plus, storing them
together lets use use `Program` structures not in the array.
I found that the gears demo was spending 40% cpu
downloading a GL texture every frame, only to
upload it again to another context.
While the GSK rendering and the GtkGLArea use different
GL contexts, they are (usually) connected by sharing data
with the same global context, so we can just use the
texture without the download/upload dance. This brings
gears down to < 10% cpu.
Do custom uploads rather than using gdk_cairo_surface_upload_to_gl(),
because this way we avoids a roundtrip (memcpy and possibly conversion)
to the cairo image surface format.
GLES doesn't support the GL_BGRA + GL_UNSIGNED_INT_24_8 hack that
we use on desktop OpenGL to upload textures directly in the cairo
pixel format. This adds the required conversions to all the places
that currently need it.
We also add a data_format to the internal gdk_gl_context_upload_texture()
function to make it clearer what the format are. Currently it is always
the cairo image surface format, but eventually we want to support other
formats so that we can avoid some of the unnecessary conversions we do.
Also, the current gdk_gl_context_upload_texture() code always converts
to a cairo format and uploads that like we did before. Later commits
will allow this to use other upload formats that gl supports to avoid
conversions.
We need to include both the scale and the filtering
in the key for the texture cache, since those affect
the texture.
This fixes misrendering in the recorder in the inspector
whenever transforms are involved. An example where this
was showing up is testrevealer's swing transition.
The only likely place where this is going to happen
is if a renderer was explicitly requested with the
GSK_RENDERER environment variable, and in that case,
it is misleading to silently use a different renderer.
When rendering to an offscreen because of transforms,
check if transforming the bounds of the node results
in a non-axis-aligned quad. If it doesn't, we want
GL_NEAREST interpolation to get sharp edges. Otherwise,
we use GL_LINEAR to get better results for things
that are actually transformed.
This is a projecting version of the corresponding
graphene api. While we are at it, rewrite
gsk_matrix_transform_bounds() to use
gsk_matrix_transform_rect().
Replace our uses of graphene_matrix_transform_point,
_point3d and _bounds by our own versions that handle
projective transforms correctly.
This fixes render node bounds being incorrect for widgets
involving projective transforms (e.g. testrevealer swing
transformations), and also fixes picking on such widgets.
If some node is fully outside the clip region we don't send it to the daemon.
This helps a lot in how much data we send for scrolling viewports.
However, sending partial trees makes node reuse a bit more tricky. We
can't save for reuse any node that could possibly clip different depending on
the clip region, as that could be different next frame. So, unless the
node is fully contained in the current clip (and we thus know it is not
parial) we don't allow reusing that next frame.
This fixes#3086
Since we have now made the Win32 GL contexts share the global context as
the other backends have, we are more ready to use the GL renderer by
default on Windows as well.
Note that currently we can only enable this when not running on
OpenGL/ES as the OpenGL/ES shaders are not ready at this point, and the
OpenGL/ES support that we have from libANGLE does not support full
desktop OpenGL operations.
Track what we really need to send for inset shadows, which are used
as a border replacement in many cases.
Fishbowl says I can draw around 200-300 more switches per frame like
this too.