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.
This is fairly substantial rewrite of the GDK backend for quartz and
renamed to macOS to allow for a greenfield implementation.
Many things have come across from the quartz implementation fairly
intact such as the eventloop integration design and discovery of
event windows from the NSEvent.
However much has been changed to fit in with the new GDK design and
how removal of child GdkWindow have been completely eliminated.
Furthermore, the new GdkPopup allows for regular NSWindow to be used
to provide popovers unlike the previous implementation.
The object design more closely follows the ideal for a GDK backend.
Views have been broken out into subclasses so that we can support
multiple GSK renderer paths such as GL and Cairo (and Metal in the
future). However mixed mode GL and Cairo will not be supported. Currently
only the Cairo renderer has been implemented.
A new frame clock implementation using CVDisplayLink provides more
accurate information about when to draw drawing the next frame. Some
testing will need to be done here to understand the power implications
of this.
This implementation has also gained edge snapping for CSD windows. Some
work was also done to ensure that CSD windows have opaque regions
registered with the display server.
** This is still very much a work-in-progress **
Some outstanding work that needs to be done:
- Finish a GL context for macOS and alternate NSView for GL rendering
(possibly using speciailized CALayer for OpenGL).
- Input rework to ensure that we don't loose remapping of keys that was
dropped from GDK during GTK 4 development.
- Make sure input methods continue to work.
- Drag-n-Drop is still very much a work in progress
- High resolution input scrolling needs various work in GDK to land
first before we can plumb that to NSEvent.
- gtk/ has a number of things based on GDK_WINDOWING_QUARTZ that need
to be updated to use the macOS backend.
But this is good enough to start playing with and breaking things which
is what I'd like to see.
This fixes the widget factory rendering too much.
In the widget-factory, we generally have a pretty small update area (two
spinners and a progressbar). We take the extents of that as a update
area and inital clip.
However, the first clip node we see is from the toplevel window, which
essentially increases the clip again to almost the entire window.
Fix that by ignoring such cases.
Respect that cairo won't create image surfaces larger
than 32767 x 32767.
This makes the one reftest pass that specifically checks
this condition, treeview-crash-too-wide.
If the inner clip intersects with the corners of the outer clip, we
potentially need a texture. We should add more fine-grained checks for
this in the future though.
Test case included.
Don't install headers for code that we don't build.
And don't include those headers in gsk.h.
Just as we do in gdk, require applications to include
the backend-specific headers they need explicitly.
Update the one affected demo, gtk4-node-editor.
Now that the GskRenderNode subclasses are recognised as proper
sub-types, we can annotate the constructors with their type. The C API
remains the same.
The introspection scanner tries to match a type name with a get_type()
function, in order to correctly identify a type as a class.
If the function is not available, we have two choices:
- add some special case inside the introspection scanner, similar to
how GParamSpec subclasses are handled in GObject
- add a simple get_type() function
The latter is the simplest approach, and we don't need to change that
much, since we still register all render nodes at once.
Language bindings—especially ones based on introspection—cannot deal
with custom type hiearchies. Luckily for us, GType has a derivable type
with low overhead: GTypeInstance.
By turning GskRenderNode into a GTypeInstance, and creating derived
types for each class of node, we can provide an introspectable API to
our non-C API consumers, with no functional change to the C API itself.
When we reconfigure, `configure_file()` is called again, and
`*.gresource.xml` files are regenerated, which causes many (all?)
binaries to be relinked. Now we only write those out if the contents
actually changed (or if the output didn't already exist).
This is exactly what Meson already does with `configure_file()` when
`command:` is not used.
While we're at it, also do the same for `gen-c-array.py` and
`gentypefuncs.py` for completeness. Now even if the input to those
changes, re-building of those custom targets may not result in
relinking if the outputted C files have the same contents.
Sprinkle various g_assert() around the code where gcc cannot figure out
on its own that a variable is not NULL and too much refactoring would be
needed to make it do that.
Also fix usage of g_assert_nonnull(x) to use g_assert(x) because the
first is not marked as G_GNUC_NORETURN because of course GTester
supports not aborting on aborts.
Transforming identity by an other transform does not mean we need to
painstakingly apply the individual steps of other to construct a new
transform, it means we can just return other.
Or in math terms:
I * B = B
so just return B.
Some systems (notably macOS) will not allow enumeration of an extension that has been promoted to core OpenGL for context in use. This change assumes that GL_ARB_timer_query is available on OpenGL 3.3+.
I could not find definitive information on whether GL_ARB_debug_output or GL_KHR_debug have been added to core. Other extensions in use were addressed by https://gitlab.gnome.org/GNOME/gtk/merge_requests/1422 .
Compute the pattern matrix directly instead of transforming the cairo_t.
This ensures that when node_size / texture_size is some obscure floating
point value, we don't get rounding issues from scaling by it once we
draw the texture_size rectangle.
I have no actual failure where this comes in handy, but I had written
the code anyway, so decided to keep it.
graphene treats equality for contains() operations as always matching,
so do the same thing.
This is because unlike integer math, floating point cannot do the "as
close as possible to the point, but not reaching it" operation that
integer does by just subtracting 1.
Commit 47c44644b1 was a bit overzealous in fixing
compiler warnings. We still need to call collect_textures,
even if we don't need the number that it returns.
We currently disable when draw()ing nodes using the cairo fallback path,
which means we can't just use cairo_paint(). Use a proper rectangle
instead.
Fixes#2431
These don't take a duration, instead they call g_get_monotonic_time() to
and subtract the start time for it.
Almost all our calls are like this, and this makes the callsites clearer
and avoids inlining the clock call into the call site.
When we use if (GDK_PROFILER_IS_RUNNING) this means we get an
inlined if (FALSE) when the compiler support is not compiled in, which
gets rid of all the related code completely.
We also expand to G_UNLIKELY(gdk_profiler_is_running ()) in the supported
case which might cause somewhat better code generation.
usec is the scale of the monotonic timer which is where we get almost
all the times from. The only actual source of nsec is the opengl
GPU time (but who knows what the actual resulution of that is).
Changing this to usec allows us to get rid of " * 1000" in a *lot* of
places all over the codebase, which are ugly and confusing.
This is similar to how we share texture atlases. Some added complexity
in that the program state also needed to be shared, so it had to move to
the shared Programs object.
With this change realization of additional GskRenderers when opening
popups went from ~60msec to ~35 msec on average.
I was getting assertions that normalize_angle() failed the
result < 260 check. Doing some research on this it turns out
to be a precision issue. If the incomming angle is very slightly
below zero, then adding 360 to it may end up with exactly 360.
I simplified the code a bit to avoid division and rounding, because in
practice most angles will be "just outside" the 0-360 degree anyway.
And i also added a workaround for the "result is 360" case by just
setting it to 0.
When rendering ops to an offscreen texture we take max-texture-size
in consideration and modify the scale we use such that the required
texture does not exceed the limit.
This means some rendering will be blocky/fuzzy, but that is better
than it being clipped.