Instead of constantly recalculating this (especially recursively for
parents!) we do it only on construction, because everything is
immutable anyway. Also, most nodes had a bounds already and can
use the new parent member instead.
We also do direct access to the node bounds rather than calling
gsk_render_node_get_bounds in various places, which means
we do less copying.
This code makes renderers fall back to Cairo rendering if they don't
know how to handle a render node's type.
This allows adding new render nodes with impunity.
Instead of having a setter for the transform, have a GskTransformNode.
Most of the oprations that GTK does do not require a transform, so it
doesn't make sense to have it as a primary attribute.
Also, changing the transform requires updating the uniforms of the GL
renderer, so we're happy if we can avoid that.
In the brave new world of refactored render nodes, this function doesn't
really make any sense anymore. We could turn it into a vfunc, but I
don't think it's useful.
Especially because even in the brave old world, this function was
causing a vastl overallocation of nodes when the GL renderer needed render
targets.
If we ever feel, we need this function again, we can readd it later.
But nobody is using it other than for overriding opactiy. And you can
just override opacity directly if you care.
This is a way to query the damaged area of the backbuffer.
The GL renderer uses this to compute the extents of that damage region
(computed via buffer age) and use them to minimize the area to redraw.
This changes the semantics of GL rendering to "When calling
gdk_window_begin_frame() with a GL context, the area by
gdk_gl_context_get_damage() needs to be redrawn and every other pixel of
the backbuffer is guaranteed to be correct.
After gdk_window_end_frame() on a GL-drawn window, the whole backbuffer
must be correct.
We can always glXBufferSwap() now because of this.
... instead of a gl context.
This requires some refactoring in the way we mark the shared context as
drawing: We now call begin_frame/end_frame() on it and ignore the call
on the main context.
Unfortunately we need to do this check in all vfuncs, which sucks. But I
haven't found a better way.
Reenable GL drawing, but do it without Cairo.
Now, the context passed to gdk_window_begin_draw_frame() decides how
drawing is going to happen. If it is NULL, Cairo is used like before.
If a context is passed, Cairo may not be used for drawing and
gdk_drawing_context_get_cairo_context() is going to return NULL.
Instead, the GL renderer must draw to the GL backbuffer and
end_draw_frame() is then swapping that to the front.
The GskGLRenderer has lost the texture it used to render to and adapted
to render directly to the backbuffer instead.
The only thing missing is for GtkGLArea to gain back a performant way to
render. But it didn't have one since the introduction of GSK, this
patchset doesn't change anything about it.
The new rendering avoids two indirections (the GSK renderer's texture
and the GDK double buffering surface).
It improves icon count in the fishbowl demo by 30%.
This way, we don't spam criticals when GL is not available. Instead, we
print a useful debug message to stderr and continue with the Cairo renderer.
Signed-off-by: Emmanuele Bassi <ebassi@gnome.org>
Instead of having a gsk_renderer_set_window() call, pass the window to
realize(). This way, the realization can fail with the wrong window.
Signed-off-by: Emmanuele Bassi <ebassi@gnome.org>
This allows renderers (or anyone really) to attach "render data" to
textures. Only the first render data sticks.
You can gsk_texture_set_render_data() with the key you will use to
look the data up again, and if no data has been set yet, yours will be
set.
You can retrieve this data via gsk_texture_get_render_data() later on.
If your data has been cleared, NULL will be returned.
When gsk_texture_clear_render_data() is called (which the texture will
call when it is finalized), your destory notify will be called and you
have to release your render data.
The GL driver uses this to attach texture ids to GskTextures.
We do no longer bind textures to a renderer, instead they are a way for
applications to provide texture data.
For now, that's it. We've reverted to uploading it from scratch every
frame.
The GLSL versions are:
OpenGL 2.1: #version 110
OpenGL 3.0: #version 130
OpenGL 3.2: #version 150
OpenGLES 2.0: #version 100
OpenGLES 3.0: #version 300 es
So we need to check the version of the GdkGLContext if we want use the
appropriate version, especially for legacy OpenGL contexts, which can be
both 3.x and 2.x.
While porting GTK to GskRenderer we noticed that the current fallback
code for widgets using Cairo to draw is not enough to cover all the
possible cases.
For instance, if a container widget still uses GtkWidget::draw to render
its children, and at least one of them has been ported to using render
nodes instead, the container won't know how to draw it.
For this reason we want to provide to layers above GSK the ability to
create a "fallback" renderer instance, created using a "parent"
GskRenderer instance, but using a Cairo context as the rendering target
instead of a GdkDrawingContext.
GTK will use this inside the gtk_widget_draw() implementation, if a
widget implements GtkWidgetClass.get_render_node().
We're going to need to allow rendering on a specific cairo_t in order to
implement fallback code paths inside GTK; this means that there will be
times when we have a transient GskRenderer instance that does not have a
GdkDrawingContext to draw on.
Instead of adding a new render() implementation for those cases and then
decide which one to use, we can remove the drawing context argument from
the virtual function itself, and allow using a NULL GdkDrawingContext
when calling gsk_renderer_render(). A later commit will add a generic
function to create a transient GskRenderer with a cairo_t attached to
it.
Renderers inside GSK will have to check whether we have access to a
GdkDrawingContext, in which case we're going to use it; or if we have
access to a cairo_t and a window.
We store the vertices in (unscaled) window coords (but the item size
is still scaled to match the texture size). Also, the
projection/model-view multiplication order is switched so that the scale
is applied at the right place.
The renderer will always use nearest-neighbor filters because it renders
at 1:1 pixel to texel ratio.
On the other hand, render nodes may be scaled, so we need to offer a way
to control the minification and magnification filters.
If we already have a GL texture we definitely don't want to use
gdk_cairo_draw_from_gl() to draw on a Cairo context if we're going
to take the Cairo surface to which we draw and put it into an OpenGL
texture.
The details of the modelview and projection matrices are only useful for
the GL renderer; there's really no point in having those details
available in the generic API — especially as the Cairo fallback renderer
cannot really set up a complex modelview or a projection matrix.
Just like we reuse texture ids with the same size we can, at the expense
of a little memory, reuse vertex buffers if they reference the same
attributes and contain the same data.
Each VAO is marked as free at the end of the frame, and if it's not
reused in the following frame, it gets dropped.
Instead of passing the size of the buffer, we should pass the number of
quads; we know what the size of a single quad structure is, so we can do
the multiplication internally when creating the VAO.
This allows us to print the quads for debugging purposes.
We keep the textures used inside a frame around until the end of the
following frame; whenever we need a texture with the same size, and
it's not marked in use, then we just reuse the existing texture.
This was overwhelming other useful debug output, so make it
opt-in. We print the render items for both opengl and transforms,
since the matrices bleed into each other, otherwise.
Since we use an FBO to render the contents of the render node tree, the
coordinate space is going to be flipped in GL. We can undo the flip by
using an appropriate projection matrix, instead of changing the sampling
coordinates in the shaders and updating all our coordinates at render
time.
We need to apply a scaling factor whenever we deal with user-supplied
coordinates, like:
- when creating textures
- when setting up the viewport
- when submitting the scene
If a node is non-opaque and has a non-zero opacity we need to paint its
contents and children first to an off screen buffer, and then render the
resulting texture at the desired opacity — otherwise the opacities will
combine and result in the wrong rendering.