Commit Graph

34 Commits

Author SHA1 Message Date
Christian Hergert
50e2a8239b macos: use GdkMacosBuffer for storing damage region
The GdkMacosBuffer object already has storage for tracking the damage
region as it is used in GdkMacosCairoContext to manually copy regions from
the front buffer to the back buffer. This makes the GdkMacosGLContext also
use that field so that we can easily drop old damage regions when the
buffer is lost. This happens during resizes, monitor changes, etc.
2022-03-16 12:25:09 -07:00
Christian Hergert
e1d3d01e2f macos: update CGL context when surface resizes 2022-02-22 13:15:25 -08:00
Christian Hergert
76a58c40db macos: force pixel format without depth/stencil
We don't need either depth or stencil buffers, so we want a pixel format
without them so that things like glClear() can do less work.
2022-02-22 13:09:30 -08:00
Christian Hergert
8b71cff71d macos: use CALayer and IOSurface for rendering
This provides a major shift in how we draw both when accelerated OpenGL
as well as software rendering with Cairo. In short, it uses tiles of Core
Animation's CALayer to display contents from an OpenGL or Cairo rendering
so that the window can provide partial damage updates. Partial damage is
not generally available when using OpenGL as the whole buffer is flipped
even if you only submitted a small change using a scissor rect.

Thankfully, this speeds up Cairo rendering a bit too by using IOSurface to
upload contents to the display server. We use the tiling system we do for
OpenGL which reduces overall complexity and differences between them.

A New Buffer
============

GdkMacosBuffer is a wrapper around an IOSurfaceRef. The term buffer was
used because 1) surface is already used and 2) it loosely maps to a
front/back buffer semantic.

However, it appears that IOSurfaceRef contents are being retained in
some fashion (likely in the compositor result) so we can update the same
IOSurfaceRef without flipping as long as we're fast. This appears to be
what Chromium does as well, but Firefox uses two IOSurfaceRef and flips
between them. We would like to avoid two surfaces because it doubles the
GPU VRAM requirements of the application.

Changes to Windows
==================

Previously, the NSWindow would dynamically change between different
types of NSView based on the renderer being used. This is no longer
necessary as we just have a single NSView type, GdkMacosView, which
inherits from GdkMacosBaseView just to keep the tedius stuff separate
from the machinery of GdkMacosView. We can merge those someday if we
are okay with that.

Changes to Views
================

GdkMacosCairoView, GdkMacosCairoSubView, GdkMacosGLView have all been
removed and replaced with GdkMacosView. This new view has a single
CALayer (GdkMacosLayer) attached to it which itself has sublayers.

The contents of the CALayer is populated with an IOSurfaceRef which
we allocated with the GdkMacosSurface. The surface is replaced when
the NSWindow resizes.

Changes to Layers
=================

We now have a dedicated GdkMacosLayer which contains sublayers of
GdkMacosTile. The tile has a maximum size of 128x128 pixels in device
units.

The GdkMacosTile is partitioned by splitting both the transparent
region (window bounds minus opaque area) and then by splitting the
opaque area.

A tile has either translucent contents (and therefore is not opaque) or
has opaque contents (and therefore is opaque). An opaque tile never
contains transparent contents. As such, the opaque tiles contain a black
background so that Core Animation will consider the tile's bounds as
opaque. This can be verified with "Quartz Debug -> Show opaque regions".

Changes to Cairo
================

GTK 4 cannot currently use cairo-quartz because of how CSS borders are
rendered. It simply causes errors in the cairo_quartz_surface_t backend.

Since we are restricted to using cairo_image_surface_t (which happens to
be faster anyway) we can use the IOSurfaceBaseAddress() to obtain a
mapping of the IOSurfaceRef in user-space. It always uses BGRA 32-bit
with alpha channel even if we will discard the alpha channel as that is
necessary to hit the fast paths in other parts of the platform. Note
that while Cairo says CAIRO_FORMAT_ARGB32, it is really 32-bit BGRA on
little-endian as we expect.

OpenGL will render flipped (Quartz Native Co-ordinates) while Cairo
renders with 0,O in the top-left. We could use cairo_translate() and
cairo_scale() to reverse this, but it looks like some cairo things may
not look quite as right if we do so. To reduce the chances of one-off
bugs this continues to draw as Cairo would normally, but instead uses
an CGAffineTransform in the tiles and some CGRect translation when
swapping buffers to get the same effect.

Changes to OpenGL
=================

To simplify things, removal of all NSOpenGL* related components have
been removed and we strictly use the Core GL (CGL*) API. This probably
should have been done long ago anyay.

Most examples found in the browsers to use IOSurfaceRef with OpenGL are
using Legacy GL and there is still work underway to make this fit in
with the rest of how the GSK GL renderer works.

Since IOSurfaceRef bound to a texture/framebuffer will not have a
default framebuffer ID of 0, we needed to add a default framebuffer id
to the GdkGLContext. GskGLRenderer can use this to setup the command
queue in such a way that our IOSurface destination has been
glBindFramebuffer() as if it were the default drawable.

This stuff is pretty slight-of-hand, so where things are and what needs
flushing when and where has been a bit of an experiment to see what
actually works to get synchronization across subsystems.

Efficient Damages
=================

After we draw with Cairo, we unlock the IOSurfaceRef and the contents
are uploaded to the GPU. To make the contents visible to the app,
we must clear the tiles contents with `layer.contents=nil;` and then
re-apply the IOSurfaceRef. Since the buffer has likely not changed, we
only do this if the tile overlaps the damage region.

This gives the effect of having more tightly controlled damage regions
even though updating the layer would damage be the whole window (as it
is with OpenGL/Metal today with the exception of scissor-rect).

This too can be verified usign "Quartz Debug -> Flash screen udpates".

Frame Synchronized Resize
=========================

In GTK 4, we have the ability to perform sizing changes from compute-size
during the layout phase. Since the macOS backend already tracks window
resizes manually, we can avoid doing the setFrame: immediately and instead
do it within the frame clock's layout phase.

Doing so gives us vastly better resize experience as we're more likely to
get the size-change and updated-contents in the same frame on screen. It
makes things feel "connected" in a way they weren't before.

Some additional effort to tweak gravity during the process is also
necessary but we were already doing that in the GTK 4 backend.

Backporting
===========

The design here has made an attempt to make it possible to backport by
keeping GdkMacosBuffer, GdkMacosLayer, and GdkMacosTile fairly
independent. There may be an opportunity to integrate this into GTK 3's
quartz backend with a fair bit of work. Doing so could improve the
situation for applications which are damage-rich such as The GIMP.
2022-02-22 12:01:29 -08:00
Benjamin Otte
6536e3dc54 macos: Fix return value 2021-11-03 22:53:59 +01:00
Benjamin Otte
f584d4f500 gl: Check allowed APIs in realize()
Add gdk_gl_context_is_api_allowed() for backends and make them use it.

Finally, have them return the final API as the return value (or 0 on
error).

And then use that api instead of a use_es boolean flag.

Fixes #4221
2021-10-08 03:31:07 +02:00
Benjamin Otte
dc6e831524 gdk: hdr => high depth
The term "hdr" is so overloaded, we shouldn't use them anywhere, except
from maybe describing all of this work in blog posts and other marketing
materials.

So do renames:
* hdr => high_depth
* request_hdr => prefers_high_depth

This more accurately describes what is going on.
2021-10-06 22:50:07 +02:00
Benjamin Otte
7ede468849 gdk: Add a request_hdr argument to begin_frame()
It's not used by anyone, it's just there.

gdk_draw_context_begin_frame_full() has been added so renderers can
make use of it.
2021-10-06 03:44:58 +02:00
Christian Hergert
e77eaa0ace macos: handle NULL surface when creating GL context
Fixes #4279
2021-09-28 17:36:23 -07:00
Benjamin Otte
4e836493cc macos: Don't include gdkinternals.h 2021-09-24 22:50:29 +02:00
Benjamin Otte
d61c71c378 macos: Actually set the vfuncs 2021-09-24 22:11:57 +02:00
Benjamin Otte
23acc993cc gdk: Make sure only one GL backend is used
Creative people managed to create an X11 display and a Wayland display
at once, thereby getting EGL and GLX involved in a fight to the death
over the ownership of the glFoo() symbolspace.

A way to force such a fight with available tools here is (on Wayland)
running something like:
GTK_INSPECTOR_DISPLAY=:1 GTK_DEBUG=interactive gtk4-demo

Related: xdg-desktop-portal-gnome#5
2021-09-24 01:59:37 +02:00
Benjamin Otte
e06e0e8555 gdk: Move GL context construction to GdkGLContext
Now that we have the display's context to hook into, we can use it to
construct other GL contexts and don't need a GdkSurface vfunc anymore.

This has the added benefit that backends can have different GdkGLContext
classes on the display and get new GLContexts generated from them, so
we get multiple GL backend support per GDK backend for free.

I originally wanted to make this a vfunc on GdkGLContextClass, but
it turns out all the abckends would just call g_object_new() anyway.
2021-07-22 16:27:32 +02:00
Benjamin Otte
9f1d6e1f44 gl: Move vfunc
Instead of
  Display::make_gl_context_current()
we now have
  GLContext::clear_current()
  GLContext::make_current()

This fits better with the backends (we can actually implement
clearCurrent on macOS now) and makes it easier to implement different GL
backends for backends (like EGL/GLX on X11).

We also pass a surfaceless boolean to make_current() so the calling code
can decide if a surface needs to be bound or not, because the backends
were all doing whatever, which was very counterproductive.
2021-07-22 16:27:32 +02:00
Benjamin Otte
505436340b gdk: Get rid of paint contexts
... or more exactly: Only use paint contexts with
gdk_cairo_draw_from_gl().

Instead of paint contexts being the only contexts who call swapBuffer(),
any context can be used for this, when it's used with
begin_frame()/end_frame().

This removes 2 features:

1. We no longer need a big sharing hierarchy. All contexts are now
   shared with gdk_display_get_gl_context().
2. There is no longer a difference between attached and non-attached
   contexts. All contexts work the same way.
2021-07-22 16:27:31 +02:00
Benjamin Otte
430b6f8fb1 gdk: Add GdkDisplay::init_gl vfunc
The vfunc is called to initialize GL and it returns a "base" context
that GDK then uses as the context all others are shared with. So the GL
context share tree now looks like:

+ context from init_gl
  - context1
  - context2
  ...

So this is a flat tree now, the complexity is gone.

The only caveat is that backends now need to create a GL context when
initializing GL so some refactoring was needed.

Two new functions have been added:

* gdk_display_prepare_gl()
  This is public API and can be used to ensure that GL has been
  initialized or if not, retrieve an error to display (or debug-print).
* gdk_display_get_gl_context()
  This is a private function to retrieve the base context from
  init_gl(). It replaces gdk_surface_get_shared_data_context().
2021-07-22 16:23:56 +02:00
Zhi
5776fcd955 fix: make the new nsview as the first responder.
Make the new view as the first responder(focused) so the new view can
accept events from input method.

Fixes #3968.
2021-06-15 00:09:46 +08:00
Tom Schoonjans
d5de73fb9d gdkmacosglcontext: fix compilation error
See https://developer.apple.com/documentation/appkit/nsopenglpixelformat/1436219-initwithattributes?language=occ
2021-03-24 10:08:11 +00:00
Christian Hergert
b2fd09625c macos: make OpenGL context opaque when possible
If our opaque region is the entire surface, then we can make the OpenGL
context opaque like we do for decorated windows. This improves performance
as the compositor does not need to blend the surface with the contents
behind the window.
2021-02-19 13:23:26 -08:00
Christian Hergert
13e162c404 macos: improve use of swap rectangles with OpenGL 2021-02-08 11:30:11 -08:00
Christian Hergert
65296228d7 macos: only flush when not attached
This makes it so we only flush the context for the NSView, not the context
that is the center of our center of the GL context spokes.
2021-02-08 11:30:05 -08:00
Christian Hergert
443d199868 macos: mark region as unlikely 2021-02-08 11:29:56 -08:00
Christian Hergert
6e0fffa0f8 macos: use CGLSetParameter and CGLEnable
We don't need to go through the NSOpenGLContext for these.
We can just use the C API directly. It's also clearer what is using
CGLEnable() vs CGLSetParameter().
2020-12-07 11:47:51 -08:00
Christian Hergert
b431e39d4e macos: we only need 24-bit for color 2020-12-07 11:27:11 -08:00
Christian Hergert
27b9a9e7ef macos: glFlush() when switching GL contexts
The Mac OpenGL programming guide suggests that you glFlush() before changing
contexts to ensure that the commands have been submitted.
2020-12-01 16:19:04 -08:00
Christian Hergert
f32ae2964a macos: fix various compiler warnings 2020-11-18 20:16:37 -08:00
Christian Hergert
90141ef305 macos: use NSOpenGLContext directly for current tracking
We don't need to interact with GdkGLContext here to keep the
current context active.
2020-11-05 13:57:41 -08:00
Christian Hergert
bc05da8dc0 macos: explicitly request color and alpha sizes 2020-11-05 13:37:25 -08:00
Christian Hergert
bf3e1e5b40 macos: use opaque GL context when possible
This is better for situations where the window is decorated and therefore we can
rely on window system clipping of rounded corners.
2020-11-05 13:34:29 -08:00
Christian Hergert
65b2ea1888 macos: resize extra GL window/view when surface changes
Once we figure out what is going on with textures, changes are we'll be
able to let this stay a zero rect. But that is still a bit up in the air right now.
2020-11-04 17:21:21 -08:00
Christian Hergert
6309bd12ac macos: clip damage when swapping buffers
This ensures that we only copy the changed area.
2020-11-04 17:21:17 -08:00
Christian Hergert
ce9cc2db86 macos: always create dummy window/view for GL context
This simplifies the creation by always creating the dummy views, and then
removes it if necessary by detecting the begin_frame/end_frame pair.
2020-11-04 16:52:12 -08:00
Christian Hergert
de9c9efa6f macos: implement GL context
This implements the basics for a GdkGLContext on macOS. Currently, rendering
only is fully working for the GskCairoRenderer case where we read back pixels
into a cairo surface for rendering. More work on synchronization is required for
the GL on GskGLRenderer case.

When we attempt to render a surface itself with GL, the context will ensure that
the new GdkMacosGLView is placed within the NSWindow. In other cases, we
use a dummy NSView and NSWindow for backing the NSOpenGLContext to
ensure that we can get accelerated drawing.

This gets GtkGLArea working when running with GSK_RENDERER=cairo.
2020-10-29 10:57:02 -07:00
Christian Hergert
9dbf99d91a macos: prototype new GDK backend for macOS
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.
2020-07-21 14:45:12 -07:00