[gtk/wip/chergert/macos-iosurface: 48/48] macos: use CALayer and IOSurface for rendering




commit 3b2a3a53ba0cffe4afc66f02ee813420b92ce3ab
Author: Christian Hergert <christian hergert me>
Date:   Fri Feb 4 17:24:13 2022 -0800

    macos: use CALayer and IOSurface for rendering
    
                         ** THIS IS NOT YET COMPLETE **
    
    This commit is just a massive group commit until things are working
    completely.  In particular there is still work to be done on the OpenGL
    integration with IOSurface (particuarly around using older GL API which
    is necessary for binding IOSurface to framebuffers.
    
    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.
    
    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.
    
      ** UPDATE THIS BEFORE COMMIT **
    
    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".
    
    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.

 gdk/macos/GdkMacosCairoSubview.c                   | 167 ------
 gdk/macos/GdkMacosCairoView.c                      | 267 ----------
 gdk/macos/GdkMacosGLView.c                         | 125 -----
 gdk/macos/GdkMacosLayer.c                          | 341 ++++++++++++
 gdk/macos/{GdkMacosGLView.h => GdkMacosLayer.h}    |  34 +-
 gdk/macos/GdkMacosTile.c                           |  58 +++
 .../{GdkMacosCairoSubview.h => GdkMacosTile.h}     |  24 +-
 gdk/macos/GdkMacosView.c                           |  81 +++
 gdk/macos/{GdkMacosCairoView.h => GdkMacosView.h}  |  19 +-
 gdk/macos/GdkMacosWindow.c                         |  80 +--
 gdk/macos/GdkMacosWindow.h                         |   3 +
 gdk/macos/gdkmacosbuffer-private.h                 |  55 ++
 gdk/macos/gdkmacosbuffer.c                         | 241 +++++++++
 gdk/macos/gdkmacoscairocontext.c                   | 222 ++++----
 gdk/macos/gdkmacosglcontext-private.h              |  17 +-
 gdk/macos/gdkmacosglcontext.c                      | 575 ++++++++++++---------
 gdk/macos/gdkmacossurface-private.h                | 128 ++---
 gdk/macos/gdkmacossurface.c                        |  86 ++-
 gdk/macos/meson.build                              |   9 +-
 19 files changed, 1449 insertions(+), 1083 deletions(-)
---
diff --git a/gdk/macos/GdkMacosLayer.c b/gdk/macos/GdkMacosLayer.c
new file mode 100644
index 0000000000..5b195f2b4f
--- /dev/null
+++ b/gdk/macos/GdkMacosLayer.c
@@ -0,0 +1,341 @@
+/* GdkMacosLayer.c
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosTile.h"
+
+@implementation GdkMacosLayer
+
+#define TILE_MAX_SIZE 128
+
+typedef struct
+{
+  GdkMacosTile          *tile;
+  cairo_rectangle_int_t  cr_area;
+  CGRect                 area;
+  guint                  opaque : 1;
+} TileInfo;
+
+typedef struct
+{
+  const cairo_region_t *region;
+  guint n_rects;
+  guint iter;
+  cairo_rectangle_int_t rect;
+  cairo_rectangle_int_t stash;
+  guint finished : 1;
+} Tiler;
+
+static void
+tiler_init (Tiler                *tiler,
+            const cairo_region_t *region)
+{
+  memset (tiler, 0, sizeof *tiler);
+
+  if (region == NULL)
+    {
+      tiler->finished = TRUE;
+      return;
+    }
+
+  tiler->region = region;
+  tiler->n_rects = cairo_region_num_rectangles (region);
+
+  if (tiler->n_rects > 0)
+    cairo_region_get_rectangle (region, 0, &tiler->rect);
+  else
+    tiler->finished = TRUE;
+}
+
+static gboolean
+tiler_next (Tiler                 *tiler,
+            cairo_rectangle_int_t *tile)
+{
+  if (tiler->finished)
+    return FALSE;
+
+  if (tiler->rect.width == 0 || tiler->rect.height == 0)
+    {
+      tiler->iter++;
+
+      if (tiler->iter >= tiler->n_rects)
+        {
+          tiler->finished = TRUE;
+          return FALSE;
+        }
+
+      cairo_region_get_rectangle (tiler->region, tiler->iter, &tiler->rect);
+    }
+
+  /* If the next rectangle is too tall, slice the bottom off to
+   * leave just the height we want into tiler->stash.
+   */
+  if (tiler->rect.height > TILE_MAX_SIZE)
+    {
+      tiler->stash = tiler->rect;
+      tiler->stash.y += TILE_MAX_SIZE;
+      tiler->stash.height -= TILE_MAX_SIZE;
+      tiler->rect.height = TILE_MAX_SIZE;
+    }
+
+  /* Now we can take the next horizontal slice */
+  tile->x = tiler->rect.x;
+  tile->y = tiler->rect.y;
+  tile->height = tiler->rect.height;
+  tile->width = MIN (TILE_MAX_SIZE, tiler->rect.width);
+
+  tiler->rect.x += tile->width;
+  tiler->rect.width -= tile->width;
+
+  if (tiler->rect.width == 0)
+    {
+      tiler->rect = tiler->stash;
+      tiler->stash.width = tiler->stash.height = 0;
+    }
+
+  return TRUE;
+}
+
+static inline CGRect
+toCGRect (const cairo_rectangle_int_t *rect)
+{
+  return CGRectMake (rect->x, rect->y, rect->width, rect->height);
+}
+
+static inline cairo_rectangle_int_t
+fromCGRect (const CGRect rect)
+{
+  return (cairo_rectangle_int_t) {
+    rect.origin.x,
+    rect.origin.y,
+    rect.size.width,
+    rect.size.height
+  };
+}
+
+-(id)init
+{
+  self = [super init];
+
+  if (self == NULL)
+    return NULL;
+
+  self->_layoutInvalid = TRUE;
+
+  [self setContentsGravity:kCAGravityCenter];
+  [self setContentsScale:1.0f];
+  [self setGeometryFlipped:YES];
+
+  return self;
+}
+
+-(BOOL)contentsAreFlipped
+{
+  return YES;
+}
+
+-(BOOL)isOpaque
+{
+  return NO;
+}
+
+-(void)_applyLayout:(GArray *)tiles
+{
+  GArray *prev;
+  gboolean exhausted;
+  guint j = 0;
+
+  prev = g_steal_pointer (&self->_tiles);
+  self->_tiles = tiles;
+  exhausted = prev == NULL;
+
+  /* Try to use existing CALayer to avoid creating new layers
+   * as that can be rather expensive.
+   */
+  for (guint i = 0; i < tiles->len; i++)
+    {
+      TileInfo *info = &g_array_index (tiles, TileInfo, i);
+
+      if (!exhausted)
+        {
+          TileInfo *other = NULL;
+
+          for (; j < prev->len; j++)
+            {
+              other = &g_array_index (prev, TileInfo, j);
+
+              if (other->opaque == info->opaque)
+                {
+                  j++;
+                  break;
+                }
+
+              other = NULL;
+            }
+
+          if (other != NULL)
+            {
+              info->tile = g_steal_pointer (&other->tile);
+              [info->tile setFrame:info->area];
+              continue;
+            }
+        }
+
+      info->tile = [GdkMacosTile layer];
+
+      //[info->tile setContentsGravity:kCAGravityCenter];
+      [info->tile setContentsScale:1.0f];
+      [info->tile setOpaque:info->opaque];
+      [info->tile setFrame:info->area];
+
+      [self addSublayer:info->tile];
+    }
+
+  /* Release all of our old layers */
+  if (prev != NULL)
+    {
+      for (guint i = 0; i < prev->len; i++)
+        {
+          TileInfo *info = &g_array_index (prev, TileInfo, i);
+
+          if (info->tile != NULL)
+            [info->tile removeFromSuperlayer];
+        }
+
+      g_array_unref (prev);
+    }
+}
+
+-(void)layoutSublayers
+{
+  Tiler tiler;
+  GArray *ar;
+  cairo_region_t *transparent;
+  cairo_rectangle_int_t rect;
+
+  if (!self->_inSwapBuffer)
+    return;
+
+  ar = g_array_sized_new (FALSE, FALSE, sizeof (TileInfo), 32);
+
+  rect = fromCGRect ([self bounds]);
+  rect.x = rect.y = 0;
+
+  /* Calculate the transparent region (edges usually) */
+  transparent = cairo_region_create_rectangle (&rect);
+  if (self->_opaqueRegion)
+    cairo_region_subtract (transparent, self->_opaqueRegion);
+
+  self->_opaque = cairo_region_is_empty (transparent);
+
+  /* Track transparent children */
+  tiler_init (&tiler, transparent);
+  while (tiler_next (&tiler, &rect))
+    {
+      TileInfo *info;
+
+      g_array_set_size (ar, ar->len+1);
+
+      info = &g_array_index (ar, TileInfo, ar->len-1);
+      info->tile = NULL;
+      info->opaque = FALSE;
+      info->cr_area = rect;
+      info->area = toCGRect (&info->cr_area);
+    }
+
+  /* Track opaque children */
+  tiler_init (&tiler, self->_opaqueRegion);
+  while (tiler_next (&tiler, &rect))
+    {
+      TileInfo *info;
+
+      g_array_set_size (ar, ar->len+1);
+
+      info = &g_array_index (ar, TileInfo, ar->len-1);
+      info->tile = NULL;
+      info->opaque = TRUE;
+      info->cr_area = rect;
+      info->area = toCGRect (&info->cr_area);
+    }
+
+  cairo_region_destroy (transparent);
+
+  [self _applyLayout:g_steal_pointer (&ar)];
+  [super layoutSublayers];
+}
+
+-(void)setFrame:(NSRect)frame
+{
+  if (CGRectEqualToRect (frame, self.frame))
+    return;
+
+  self->_layoutInvalid = TRUE;
+
+  [super setFrame:frame];
+}
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion
+{
+  g_clear_pointer (&self->_opaqueRegion, cairo_region_destroy);
+  self->_opaqueRegion = cairo_region_copy (opaqueRegion);
+  self->_layoutInvalid = TRUE;
+}
+
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+  IOSurfaceRef ioSurface = _gdk_macos_buffer_get_native (buffer);
+  double scale = _gdk_macos_buffer_get_device_scale (buffer);
+  size_t width = _gdk_macos_buffer_get_width (buffer) / scale;
+  size_t height = _gdk_macos_buffer_get_height (buffer) / scale;
+
+  if (self->_layoutInvalid)
+    {
+      self->_layoutInvalid = FALSE;
+
+      self->_inSwapBuffer = TRUE;
+      [self layoutSublayers];
+      self->_inSwapBuffer = FALSE;
+    }
+
+  if (self->_tiles == NULL)
+    return;
+
+  for (guint i = 0; i < self->_tiles->len; i++)
+    {
+      const TileInfo *info = &g_array_index (self->_tiles, TileInfo, i);
+      cairo_region_overlap_t overlap;
+      CGRect area;
+
+      overlap = cairo_region_contains_rectangle (damage, &info->cr_area);
+      if (overlap == CAIRO_REGION_OVERLAP_OUT)
+        continue;
+
+      area.origin.x = info->area.origin.x / (double)width;
+      area.origin.y = info->area.origin.y / (double)height;
+      area.size.width = info->area.size.width / (double)width;
+      area.size.height = info->area.size.height / (double)height;
+
+      [info->tile swapBuffer:ioSurface withRect:area];
+    }
+}
+
+@end
diff --git a/gdk/macos/GdkMacosGLView.h b/gdk/macos/GdkMacosLayer.h
similarity index 55%
rename from gdk/macos/GdkMacosGLView.h
rename to gdk/macos/GdkMacosLayer.h
index 320b1a163b..18b7d9afae 100644
--- a/gdk/macos/GdkMacosGLView.h
+++ b/gdk/macos/GdkMacosLayer.h
@@ -1,7 +1,6 @@
-/* GdkMacosGLView.h
+/* GdkMacosLayer.h
  *
- * Copyright © 2020 Red Hat, Inc.
- * Copyright © 2005-2007 Imendio AB
+ * Copyright © 2022 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,23 +18,26 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-#include <cairo.h>
+#include <QuartzCore/QuartzCore.h>
+#include <IOSurface/IOSurface.h>
 
-#import "GdkMacosBaseView.h"
+#include <cairo.h>
+#include <glib.h>
 
-#define GDK_IS_MACOS_GL_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosGLView class]])
+#include "gdkmacosbuffer-private.h"
 
-G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+#define GDK_IS_MACOS_LAYER(obj) ((obj) && [obj isKindOfClass:[GdkMacosLayer class]])
 
-@interface GdkMacosGLView : GdkMacosBaseView
+@interface GdkMacosLayer : CALayer
 {
-  NSOpenGLContext *_openGLContext;
-}
-
--(void)setOpenGLContext:(NSOpenGLContext*)context;
--(NSOpenGLContext *)openGLContext;
--(void)invalidateRegion:(const cairo_region_t *)region;
-
-G_GNUC_END_IGNORE_DEPRECATIONS
+  cairo_region_t *_opaqueRegion;
+  GArray         *_tiles;
+  guint           _opaque : 1;
+  guint           _layoutInvalid : 1;
+  guint           _inSwapBuffer : 1;
+};
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
 
 @end
diff --git a/gdk/macos/GdkMacosTile.c b/gdk/macos/GdkMacosTile.c
new file mode 100644
index 0000000000..3c9eaf171f
--- /dev/null
+++ b/gdk/macos/GdkMacosTile.c
@@ -0,0 +1,58 @@
+/* GdkMacosTile.c
+ *
+ * Copyright © 2022 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <AppKit/AppKit.h>
+
+#import "GdkMacosTile.h"
+
+@implementation GdkMacosTile
+
+-(BOOL)contentsAreFlipped
+{
+  return YES;
+}
+
+-(void)setOpaque:(BOOL)opaque
+{
+  [super setOpaque:opaque];
+
+  if (opaque)
+    self.backgroundColor = NSColor.blackColor.CGColor;
+  else
+    self.backgroundColor = NSColor.clearColor.CGColor;
+}
+
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect
+{
+  CGRect prevRect = [self contentsRect];
+
+  self.contents = nil;
+  self.contents = (id)buffer;
+
+  if (!CGRectEqualToRect (prevRect, rect))
+    self.contentsRect = rect;
+
+  if (self.contentsScale != 1.0f)
+    self.contentsScale = 1.0f;
+}
+
+@end
diff --git a/gdk/macos/GdkMacosCairoSubview.h b/gdk/macos/GdkMacosTile.h
similarity index 59%
rename from gdk/macos/GdkMacosCairoSubview.h
rename to gdk/macos/GdkMacosTile.h
index e03b47727d..296a8d7c70 100644
--- a/gdk/macos/GdkMacosCairoSubview.h
+++ b/gdk/macos/GdkMacosTile.h
@@ -1,6 +1,6 @@
-/* GdkMacosCairoSubview.h
+/* GdkMacosTile.h
  *
- * Copyright © 2020 Red Hat, Inc.
+ * Copyright © 2022 Red Hat, Inc.
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -18,22 +18,16 @@
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
-#include <AppKit/AppKit.h>
-#include <cairo.h>
-#include <glib.h>
+#include <QuartzCore/QuartzCore.h>
 
-#define GDK_IS_MACOS_CAIRO_SUBVIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosCairoSubview class]])
+#include "gdkmacosbuffer-private.h"
 
-@interface GdkMacosCairoSubview : NSView
+#define GDK_IS_MACOS_TILE(obj) ((obj) && [obj isKindOfClass:[GdkMacosTile class]])
+
+@interface GdkMacosTile : CALayer
 {
-  BOOL        _isOpaque;
-  GArray     *clip;
-  GArray     *damage;
-  CGImageRef  image;
-}
+};
 
--(void)setOpaque:(BOOL)opaque;
--(void)setImage:(CGImageRef)theImage withDamage:(cairo_region_t *)region;
--(void)setClip:(cairo_region_t*)region;
+-(void)swapBuffer:(IOSurfaceRef)buffer withRect:(CGRect)rect;
 
 @end
diff --git a/gdk/macos/GdkMacosView.c b/gdk/macos/GdkMacosView.c
new file mode 100644
index 0000000000..66cac30be4
--- /dev/null
+++ b/gdk/macos/GdkMacosView.c
@@ -0,0 +1,81 @@
+/* GdkMacosView.c
+ *
+ * Copyright 2022 Christian Hergert <chergert redhat com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <CoreGraphics/CoreGraphics.h>
+
+#import "GdkMacosLayer.h"
+#import "GdkMacosView.h"
+
+@implementation GdkMacosView
+
+-(id)initWithFrame:(NSRect)frame
+{
+  if ((self = [super initWithFrame:frame]))
+    {
+      GdkMacosLayer *layer = [GdkMacosLayer layer];
+
+      [self setLayerContentsRedrawPolicy:NSViewLayerContentsRedrawNever];
+      [self setLayer:layer];
+      [self setWantsLayer:YES];
+    }
+
+  return self;
+}
+
+-(BOOL)isFlipped
+{
+  return YES;
+}
+
+-(BOOL)acceptsFirstMouse
+{
+  return YES;
+}
+
+-(BOOL)mouseDownCanMoveWindow
+{
+  return NO;
+}
+
+-(void)setFrame:(NSRect)rect
+{
+  [super setFrame:rect];
+  self->_nextFrameDirty = TRUE;
+}
+
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion
+{
+  [(GdkMacosLayer *)[self layer] setOpaqueRegion:opaqueRegion];
+}
+
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+  if (self->_nextFrameDirty)
+    {
+      self->_nextFrameDirty = FALSE;
+      [[self layer] setFrame:[self frame]];
+    }
+
+  [(GdkMacosLayer *)[self layer] swapBuffer:buffer withDamage:damage];
+}
+
+@end
diff --git a/gdk/macos/GdkMacosCairoView.h b/gdk/macos/GdkMacosView.h
similarity index 64%
rename from gdk/macos/GdkMacosCairoView.h
rename to gdk/macos/GdkMacosView.h
index 1c28d83b39..db3b05efa0 100644
--- a/gdk/macos/GdkMacosCairoView.h
+++ b/gdk/macos/GdkMacosView.h
@@ -1,7 +1,6 @@
-/* GdkMacosCairoView.h
+/* GdkMacosView.h
  *
- * Copyright © 2020 Red Hat, Inc.
- * Copyright © 2005-2007 Imendio AB
+ * Copyright 2022 Christian Hergert <chergert redhat com>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -23,15 +22,17 @@
 
 #import "GdkMacosBaseView.h"
 
-#define GDK_IS_MACOS_CAIRO_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosCairoView class]])
+#include "gdkmacosbuffer-private.h"
 
-@interface GdkMacosCairoView : GdkMacosBaseView
+#define GDK_IS_MACOS_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosView class]])
+
+@interface GdkMacosView : GdkMacosBaseView
 {
-  NSView *transparent;
-  GPtrArray *opaque;
+  NSRect _nextFrame;
+  guint  _nextFrameDirty : 1;
 }
 
--(void)setCairoSurface:(cairo_surface_t *)cairoSurface
-            withDamage:(cairo_region_t *)region;
+-(void)setOpaqueRegion:(const cairo_region_t *)opaqueRegion;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
 
 @end
diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c
index 8dda79ab4d..8401f45860 100644
--- a/gdk/macos/GdkMacosWindow.c
+++ b/gdk/macos/GdkMacosWindow.c
@@ -24,8 +24,7 @@
 #include <gdk/gdk.h>
 
 #import "GdkMacosBaseView.h"
-#import "GdkMacosCairoView.h"
-#import "GdkMacosGLView.h"
+#import "GdkMacosView.h"
 #import "GdkMacosWindow.h"
 
 #include "gdkmacosclipboard-private.h"
@@ -150,8 +149,7 @@ typedef NSString *CALayerContentsGravity;
       _gdk_macos_display_break_all_grabs (GDK_MACOS_DISPLAY (display), time);
 
       /* Reset gravity */
-      if (GDK_IS_MACOS_GL_VIEW ([self contentView]))
-        [[[self contentView] layer] setContentsGravity:kCAGravityBottomLeft];
+      [[[self contentView] layer] setContentsGravity:kCAGravityBottomLeft];
 
       break;
     }
@@ -266,7 +264,7 @@ typedef NSString *CALayerContentsGravity;
 
   [[self contentView] setFrame:NSMakeRect (0, 0, surface->width, surface->height)];
 
-  _gdk_surface_update_size (surface);
+  _gdk_macos_surface_update_size (gdk_surface);
 
   gdk_surface_request_layout (surface);
 
@@ -281,7 +279,7 @@ typedef NSString *CALayerContentsGravity;
                    defer:(BOOL)flag
                   screen:(NSScreen *)screen
 {
-  GdkMacosCairoView *view;
+  GdkMacosView *view;
 
   self = [super initWithContentRect:contentRect
                                styleMask:styleMask
@@ -292,8 +290,9 @@ typedef NSString *CALayerContentsGravity;
   [self setAcceptsMouseMovedEvents:YES];
   [self setDelegate:(id<NSWindowDelegate>)self];
   [self setReleasedWhenClosed:YES];
+  [self setPreservesContentDuringLiveResize:NO];
 
-  view = [[GdkMacosCairoView alloc] initWithFrame:contentRect];
+  view = [[GdkMacosView alloc] initWithFrame:contentRect];
   [self setContentView:view];
   [view release];
 
@@ -561,49 +560,47 @@ typedef NSString *CALayerContentsGravity;
 
 -(void)beginManualResize:(GdkSurfaceEdge)edge
 {
+  CALayerContentsGravity gravity = kCAGravityBottomLeft;
+
   if (inMove || inManualMove || inManualResize)
     return;
 
   inManualResize = YES;
   resizeEdge = edge;
 
-  if (GDK_IS_MACOS_GL_VIEW ([self contentView]))
+
+  switch (edge)
     {
-      CALayerContentsGravity gravity = kCAGravityBottomLeft;
+    default:
+    case GDK_SURFACE_EDGE_NORTH:
+      gravity = kCAGravityTopLeft;
+      break;
 
-      switch (edge)
-        {
-        default:
-        case GDK_SURFACE_EDGE_NORTH:
-          gravity = kCAGravityTopLeft;
-          break;
-
-        case GDK_SURFACE_EDGE_NORTH_WEST:
-          gravity = kCAGravityTopRight;
-          break;
-
-        case GDK_SURFACE_EDGE_SOUTH_WEST:
-        case GDK_SURFACE_EDGE_WEST:
-          gravity = kCAGravityBottomRight;
-          break;
-
-        case GDK_SURFACE_EDGE_SOUTH:
-        case GDK_SURFACE_EDGE_SOUTH_EAST:
-          gravity = kCAGravityBottomLeft;
-          break;
-
-        case GDK_SURFACE_EDGE_EAST:
-          gravity = kCAGravityBottomLeft;
-          break;
-
-        case GDK_SURFACE_EDGE_NORTH_EAST:
-          gravity = kCAGravityTopLeft;
-          break;
-        }
+    case GDK_SURFACE_EDGE_NORTH_WEST:
+      gravity = kCAGravityTopRight;
+      break;
+
+    case GDK_SURFACE_EDGE_SOUTH_WEST:
+    case GDK_SURFACE_EDGE_WEST:
+      gravity = kCAGravityBottomRight;
+      break;
 
-      [[[self contentView] layer] setContentsGravity:gravity];
+    case GDK_SURFACE_EDGE_SOUTH:
+    case GDK_SURFACE_EDGE_SOUTH_EAST:
+      gravity = kCAGravityBottomLeft;
+      break;
+
+    case GDK_SURFACE_EDGE_EAST:
+      gravity = kCAGravityBottomLeft;
+      break;
+
+    case GDK_SURFACE_EDGE_NORTH_EAST:
+      gravity = kCAGravityTopLeft;
+      break;
     }
 
+  [[[self contentView] layer] setContentsGravity:gravity];
+
   initialResizeFrame = [self frame];
   initialResizeLocation = convert_nspoint_to_screen (self, [self mouseLocationOutsideOfEventStream]);
 }
@@ -852,4 +849,9 @@ typedef NSString *CALayerContentsGravity;
   return NO;
 }
 
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage
+{
+  [(GdkMacosView *)[self contentView] swapBuffer:buffer withDamage:damage];
+}
+
 @end
diff --git a/gdk/macos/GdkMacosWindow.h b/gdk/macos/GdkMacosWindow.h
index 61f546a78b..cb8b2efad1 100644
--- a/gdk/macos/GdkMacosWindow.h
+++ b/gdk/macos/GdkMacosWindow.h
@@ -21,9 +21,11 @@
 
 #import <AppKit/AppKit.h>
 #import <Foundation/Foundation.h>
+#import <IOSurface/IOSurface.h>
 
 #include <gdk/gdk.h>
 
+#include "gdkmacosbuffer-private.h"
 #include "gdkmacosdisplay.h"
 #include "gdkmacossurface.h"
 #include "edgesnapping.h"
@@ -66,5 +68,6 @@
 -(BOOL)trackManualMove;
 -(BOOL)trackManualResize;
 -(void)setDecorated:(BOOL)decorated;
+-(void)swapBuffer:(GdkMacosBuffer *)buffer withDamage:(const cairo_region_t *)damage;
 
 @end
diff --git a/gdk/macos/gdkmacosbuffer-private.h b/gdk/macos/gdkmacosbuffer-private.h
new file mode 100644
index 0000000000..62e605c609
--- /dev/null
+++ b/gdk/macos/gdkmacosbuffer-private.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright © 2021 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#ifndef __GDK_MACOS_BUFFER_PRIVATE_H__
+#define __GDK_MACOS_BUFFER_PRIVATE_H__
+
+#include <CoreGraphics/CoreGraphics.h>
+#include <Foundation/Foundation.h>
+#include <IOSurface/IOSurface.h>
+
+#include "gdkmacosdisplay.h"
+
+G_BEGIN_DECLS
+
+#define GDK_TYPE_MACOS_BUFFER (gdk_macos_buffer_get_type())
+
+G_DECLARE_FINAL_TYPE (GdkMacosBuffer, gdk_macos_buffer, GDK, MACOS_BUFFER, GObject)
+
+GdkMacosBuffer       *_gdk_macos_buffer_new              (GdkMacosDisplay *display,
+                                                          int              width,
+                                                          int              height,
+                                                          double           device_scale,
+                                                          int              bytes_per_element,
+                                                          int              bits_per_pixel);
+IOSurfaceRef          _gdk_macos_buffer_get_native       (GdkMacosBuffer  *self);
+void                  _gdk_macos_buffer_lock             (GdkMacosBuffer  *self);
+void                  _gdk_macos_buffer_unlock           (GdkMacosBuffer  *self);
+guint                 _gdk_macos_buffer_get_width        (GdkMacosBuffer  *self);
+guint                 _gdk_macos_buffer_get_height       (GdkMacosBuffer  *self);
+guint                 _gdk_macos_buffer_get_stride       (GdkMacosBuffer  *self);
+double                _gdk_macos_buffer_get_device_scale (GdkMacosBuffer  *self);
+const cairo_region_t *_gdk_macos_buffer_get_damage       (GdkMacosBuffer  *self);
+void                  _gdk_macos_buffer_set_damage       (GdkMacosBuffer  *self,
+                                                          cairo_region_t  *damage);
+gpointer              _gdk_macos_buffer_get_data         (GdkMacosBuffer  *self);
+
+G_END_DECLS
+
+#endif /* __GDK_MACOS_BUFFER_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacosbuffer.c b/gdk/macos/gdkmacosbuffer.c
new file mode 100644
index 0000000000..1ec8554640
--- /dev/null
+++ b/gdk/macos/gdkmacosbuffer.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright © 2021 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include "config.h"
+
+#include <IOSurface/IOSurface.h>
+#include <Foundation/Foundation.h>
+#include <OpenGL/CGLIOSurface.h>
+#include <QuartzCore/QuartzCore.h>
+
+#include "gdkmacosbuffer-private.h"
+
+struct _GdkMacosBuffer
+{
+  GObject          parent_instance;
+  cairo_region_t  *damage;
+  CGColorSpaceRef  colorspace;
+  IOSurfaceRef     surface;
+  int              lock_count;
+  guint            bytes_per_element;
+  guint            bits_per_pixel;
+  guint            width;
+  guint            height;
+  guint            stride;
+  double           device_scale;
+};
+
+G_DEFINE_TYPE (GdkMacosBuffer, gdk_macos_buffer, G_TYPE_OBJECT)
+
+static void
+gdk_macos_buffer_dispose (GObject *object)
+{
+  GdkMacosBuffer *self = (GdkMacosBuffer *)object;
+
+  if (self->lock_count != 0)
+    g_critical ("Attempt to dispose %s while lock is held",
+                G_OBJECT_TYPE_NAME (self));
+  
+  g_clear_pointer (&self->surface, CFRelease);
+  g_clear_pointer (&self->colorspace, CGColorSpaceRelease);
+  g_clear_pointer (&self->damage, cairo_region_destroy);
+  
+  G_OBJECT_CLASS (gdk_macos_buffer_parent_class)->dispose (object);
+}
+
+static void
+gdk_macos_buffer_class_init (GdkMacosBufferClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+  object_class->dispose = gdk_macos_buffer_dispose;
+}
+
+static void
+gdk_macos_buffer_init (GdkMacosBuffer *self)
+{
+}
+
+static void
+add_int (CFMutableDictionaryRef dict,
+         const CFStringRef      key,
+         int                    value)
+{
+  CFNumberRef number = CFNumberCreate (NULL, kCFNumberIntType, &value);
+  CFDictionaryAddValue (dict, key, number);
+  CFRelease (number);
+}
+
+static IOSurfaceRef
+create_surface (int   width,
+                int   height,
+                int   bytes_per_element,
+                guint *stride)
+{
+  CFMutableDictionaryRef props;
+  IOSurfaceRef ret;
+  size_t bytes_per_row;
+  size_t total_bytes;
+
+  props = CFDictionaryCreateMutable (kCFAllocatorDefault,
+                                     16,
+                                     &kCFTypeDictionaryKeyCallBacks,
+                                     &kCFTypeDictionaryValueCallBacks);
+  if (props == NULL)
+    return NULL;
+
+  bytes_per_row = IOSurfaceAlignProperty (kIOSurfaceBytesPerRow, width * bytes_per_element);
+  total_bytes = IOSurfaceAlignProperty (kIOSurfaceAllocSize, height * bytes_per_row);
+
+  add_int (props, kIOSurfaceAllocSize, total_bytes);
+  add_int (props, kIOSurfaceBytesPerElement, bytes_per_element);
+  add_int (props, kIOSurfaceBytesPerRow, bytes_per_row);
+  add_int (props, kIOSurfaceHeight, height);
+  add_int (props, kIOSurfacePixelFormat, (int)'BGRA');
+  add_int (props, kIOSurfaceWidth, width);
+
+  ret = IOSurfaceCreate (props);
+
+  CFRelease (props);
+
+  *stride = bytes_per_row;
+
+  return ret;
+}
+
+GdkMacosBuffer *
+_gdk_macos_buffer_new (GdkMacosDisplay *display,
+                       int              width,
+                       int              height,
+                       double           device_scale,
+                       int              bytes_per_element,
+                       int              bits_per_pixel)
+{
+  GdkMacosBuffer *self;
+
+  g_return_val_if_fail (GDK_IS_MACOS_DISPLAY (display), NULL);
+  g_return_val_if_fail (width > 0, NULL);
+  g_return_val_if_fail (height > 0, NULL);
+
+  self = g_object_new (GDK_TYPE_MACOS_BUFFER, NULL);
+  self->colorspace = CGColorSpaceCreateDeviceRGB ();
+  self->bytes_per_element = bytes_per_element;
+  self->bits_per_pixel = bits_per_pixel;
+  self->surface = create_surface (width, height, bytes_per_element, &self->stride);
+  self->width = width;
+  self->height = height;
+  self->device_scale = device_scale;
+  self->lock_count = 0;
+
+  if (self->surface == NULL)
+    g_clear_object (&self);
+
+  return self;
+}
+
+IOSurfaceRef
+_gdk_macos_buffer_get_native (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+  return self->surface;
+}
+
+void
+_gdk_macos_buffer_lock (GdkMacosBuffer *self)
+{
+  g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+  g_return_if_fail (self->lock_count == 0);
+
+  self->lock_count++;
+
+  IOSurfaceLock (self->surface, 0, NULL);
+}
+
+void
+_gdk_macos_buffer_unlock (GdkMacosBuffer *self)
+{
+  g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+  g_return_if_fail (self->lock_count == 1);
+
+  self->lock_count--;
+
+  IOSurfaceUnlock (self->surface, 0, NULL);
+}
+
+guint
+_gdk_macos_buffer_get_width (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 0);
+
+  return self->width;
+}
+
+guint
+_gdk_macos_buffer_get_height (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 0);
+
+  return self->height;
+}
+
+guint
+_gdk_macos_buffer_get_stride (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 0);
+
+  return self->stride;
+}
+
+double
+_gdk_macos_buffer_get_device_scale (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), 1.0);
+
+  return self->device_scale;
+}
+
+const cairo_region_t *
+_gdk_macos_buffer_get_damage (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+  return self->damage;
+}
+
+void
+_gdk_macos_buffer_set_damage (GdkMacosBuffer *self,
+                              cairo_region_t *damage)
+{
+  g_return_if_fail (GDK_IS_MACOS_BUFFER (self));
+
+  if (damage == self->damage)
+    return;
+
+  g_clear_pointer (&self->damage, cairo_region_destroy);
+  self->damage = cairo_region_reference (damage);
+}
+
+gpointer
+_gdk_macos_buffer_get_data (GdkMacosBuffer *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_BUFFER (self), NULL);
+
+  return IOSurfaceGetBaseAddress (self->surface);
+}
diff --git a/gdk/macos/gdkmacoscairocontext.c b/gdk/macos/gdkmacoscairocontext.c
index c1dd7f677c..e80db6d569 100644
--- a/gdk/macos/gdkmacoscairocontext.c
+++ b/gdk/macos/gdkmacoscairocontext.c
@@ -22,19 +22,17 @@
 
 #include "gdkconfig.h"
 
+#include <cairo.h>
+#include <QuartzCore/QuartzCore.h>
 #include <CoreGraphics/CoreGraphics.h>
 
-#import "GdkMacosCairoView.h"
-
+#include "gdkmacosbuffer-private.h"
 #include "gdkmacoscairocontext-private.h"
 #include "gdkmacossurface-private.h"
 
 struct _GdkMacosCairoContext
 {
-  GdkCairoContext  parent_instance;
-
-  cairo_surface_t *window_surface;
-  cairo_t         *cr;
+  GdkCairoContext parent_instance;
 };
 
 struct _GdkMacosCairoContextClass
@@ -44,80 +42,120 @@ struct _GdkMacosCairoContextClass
 
 G_DEFINE_TYPE (GdkMacosCairoContext, _gdk_macos_cairo_context, GDK_TYPE_CAIRO_CONTEXT)
 
-static cairo_surface_t *
-create_cairo_surface_for_surface (GdkSurface *surface)
+static const cairo_user_data_key_t buffer_key;
+
+static void
+unlock_buffer (gpointer data)
 {
-  static const cairo_user_data_key_t buffer_key;
-  cairo_surface_t *cairo_surface;
-  guint8 *data;
-  cairo_format_t format;
-  size_t size;
-  size_t rowstride;
-  size_t width;
-  size_t height;
-  int scale;
-
-  g_assert (GDK_IS_MACOS_SURFACE (surface));
-
-  /* We use a cairo image surface here instead of a quartz surface because
-   * we get strange artifacts with the quartz surface such as empty
-   * cross-fades when hovering buttons. For performance, we want to be using
-   * GL rendering so there isn't much point here as correctness is better.
-   *
-   * Additionally, so we can take avantage of faster paths in Core
-   * Graphics, we want our data pointer to be 16-byte aligned and our rows
-   * to be 16-byte aligned or we risk errors below us. Normally, cairo
-   * image surface does not guarantee the later, which means we could end
-   * up doing some costly copies along the way to compositing.
-   */
+  GdkMacosBuffer *buffer = data;
+
+  g_assert (GDK_IS_MACOS_BUFFER (buffer));
 
-  if ([GDK_MACOS_SURFACE (surface)->window isOpaque])
-    format = CAIRO_FORMAT_RGB24;
-  else
-    format = CAIRO_FORMAT_ARGB32;
-
-  scale = gdk_surface_get_scale_factor (surface);
-  width = scale * gdk_surface_get_width (surface);
-  height = scale * gdk_surface_get_height (surface);
-  rowstride = (cairo_format_stride_for_width (format, width) + 0xF) & ~0xF;
-  size = rowstride * height;
-  data = g_malloc0 (size);
-  cairo_surface = cairo_image_surface_create_for_data (data, format, width, height, rowstride);
-  cairo_surface_set_user_data (cairo_surface, &buffer_key, data, g_free);
-  cairo_surface_set_device_scale (cairo_surface, scale, scale);
-
-  return cairo_surface;
+  _gdk_macos_buffer_unlock (buffer);
+  g_clear_object (&buffer);
 }
 
 static cairo_t *
-do_cairo_create (GdkMacosCairoContext *self)
+_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
 {
+  GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+  const cairo_region_t *damage;
+  cairo_surface_t *image_surface;
+  GdkMacosBuffer *buffer;
   GdkSurface *surface;
+  NSWindow *nswindow;
   cairo_t *cr;
+  gpointer data;
+  double scale;
+  guint width;
+  guint height;
+  guint stride;
+  gboolean opaque;
 
   g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
 
   surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
-  cr = cairo_create (self->window_surface);
-
-  /* Draw upside down as quartz prefers */
-  cairo_translate (cr, 0, surface->height);
-  cairo_scale (cr, 1.0, -1.0);
-
-  return cr;
-}
-
-static cairo_t *
-_gdk_macos_cairo_context_cairo_create (GdkCairoContext *cairo_context)
-{
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)cairo_context;
+  nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
+  opaque = [nswindow isOpaque];
+
+  buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+  damage = _gdk_macos_buffer_get_damage (buffer);
+  width = _gdk_macos_buffer_get_width (buffer);
+  height = _gdk_macos_buffer_get_height (buffer);
+  scale = _gdk_macos_buffer_get_device_scale (buffer);
+  stride = _gdk_macos_buffer_get_stride (buffer);
+  data = _gdk_macos_buffer_get_data (buffer);
+
+  /* Instead of forcing cairo to do everything through a CGContext,
+   * we just use an image surface backed by an IOSurfaceRef mapped
+   * into user-space. We can then use pixman which is quite fast as
+   * far as software rendering goes.
+   *
+   * Additionally, cairo_quartz_surface_t can't handle a number of
+   * tricks that the GSK cairo renderer does with border nodes and
+   * shadows, so an image surface is necessary for that.
+   *
+   * Since our IOSurfaceRef is width*scale-by-height*scale, we undo
+   * the scaling using cairo_surface_set_device_scale() so the renderer
+   * just thinks it's on a 2x scale surface for HiDPI.
+   */
+  image_surface = cairo_image_surface_create_for_data (data,
+                                                       CAIRO_FORMAT_ARGB32,
+                                                       width,
+                                                       height,
+                                                       stride);
+  cairo_surface_set_device_scale (image_surface, scale, scale);
+
+  /* Lock the buffer so we can modify it safely */
+  _gdk_macos_buffer_lock (buffer);
+  cairo_surface_set_user_data (image_surface,
+                               &buffer_key,
+                               g_object_ref (buffer),
+                               unlock_buffer);
+
+  if (!(cr = cairo_create (image_surface)))
+    goto failure;
+
+  /* Clip to the current damage region */
+  if (damage != NULL)
+    {
+      gdk_cairo_region (cr, damage);
+      cairo_clip (cr);
+    }
 
-  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
+  /* If we have some exposed transparent area in the damage region,
+   * we need to clear the existing content first to leave an transparent
+   * area for cairo. We use (surface_bounds or damage)-(opaque) to get
+   * the smallest set of rectangles we need to clear as it's expensive.
+   */
+  if (!opaque)
+    {
+      cairo_region_t *transparent;
+      cairo_rectangle_int_t r = { 0, 0, width/scale, height/scale };
+
+      cairo_save (cr);
+
+      if (damage != NULL)
+        cairo_region_get_extents (damage, &r);
+      transparent = cairo_region_create_rectangle (&r);
+      if (surface->opaque_region)
+        cairo_region_subtract (transparent, surface->opaque_region);
+
+      if (!cairo_region_is_empty (transparent))
+        {
+          gdk_cairo_region (cr, transparent);
+          cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR);
+          cairo_fill (cr);
+        }
+
+      cairo_region_destroy (transparent);
+      cairo_restore (cr);
+    }
 
-  if (self->cr != NULL)
-    return cairo_reference (self->cr);
+failure:
+  cairo_surface_destroy (image_surface);
 
-  return do_cairo_create (self);
+  return cr;
 }
 
 static void
@@ -126,80 +164,48 @@ _gdk_macos_cairo_context_begin_frame (GdkDrawContext *draw_context,
                                       cairo_region_t *region)
 {
   GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
+  GdkMacosBuffer *buffer;
   GdkSurface *surface;
-  NSWindow *nswindow;
 
   g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
 
-  surface = gdk_draw_context_get_surface (draw_context);
-  nswindow = _gdk_macos_surface_get_native (GDK_MACOS_SURFACE (surface));
-
-  if (self->window_surface == NULL)
-    self->window_surface = create_cairo_surface_for_surface (surface);
+  [CATransaction begin];
+  [CATransaction setDisableActions:YES];
 
-  self->cr = do_cairo_create (self);
+  surface = gdk_draw_context_get_surface (draw_context);
+  buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
 
-  if (![nswindow isOpaque])
-    {
-      cairo_save (self->cr);
-      gdk_cairo_region (self->cr, region);
-      cairo_set_source_rgba (self->cr, 0, 0, 0, 0);
-      cairo_set_operator (self->cr, CAIRO_OPERATOR_SOURCE);
-      cairo_fill (self->cr);
-      cairo_restore (self->cr);
-    }
+  _gdk_macos_buffer_set_damage (buffer, region);
 }
 
 static void
 _gdk_macos_cairo_context_end_frame (GdkDrawContext *draw_context,
                                     cairo_region_t *painted)
 {
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
   GdkSurface *surface;
-  NSView *nsview;
 
-  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
-  g_assert (self->window_surface != NULL);
+  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (draw_context));
 
   surface = gdk_draw_context_get_surface (draw_context);
-  nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface));
-
-  g_clear_pointer (&self->cr, cairo_destroy);
+  _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
 
-  if (GDK_IS_MACOS_CAIRO_VIEW (nsview))
-    [(GdkMacosCairoView *)nsview setCairoSurface:self->window_surface
-                                      withDamage:painted];
+  [CATransaction commit];
 }
 
 static void
 _gdk_macos_cairo_context_surface_resized (GdkDrawContext *draw_context)
 {
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)draw_context;
-
-  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (self));
-
-  g_clear_pointer (&self->window_surface, cairo_surface_destroy);
-}
-
-static void
-_gdk_macos_cairo_context_dispose (GObject *object)
-{
-  GdkMacosCairoContext *self = (GdkMacosCairoContext *)object;
-
-  g_clear_pointer (&self->window_surface, cairo_surface_destroy);
+  g_assert (GDK_IS_MACOS_CAIRO_CONTEXT (draw_context));
 
-  G_OBJECT_CLASS (_gdk_macos_cairo_context_parent_class)->dispose (object);
+  /* Do nothing, next begin_frame will get new buffer */
 }
 
 static void
 _gdk_macos_cairo_context_class_init (GdkMacosCairoContextClass *klass)
 {
-  GObjectClass *object_class = G_OBJECT_CLASS (klass);
   GdkCairoContextClass *cairo_context_class = GDK_CAIRO_CONTEXT_CLASS (klass);
   GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass);
 
-  object_class->dispose = _gdk_macos_cairo_context_dispose;
-
   draw_context_class->begin_frame = _gdk_macos_cairo_context_begin_frame;
   draw_context_class->end_frame = _gdk_macos_cairo_context_end_frame;
   draw_context_class->surface_resized = _gdk_macos_cairo_context_surface_resized;
diff --git a/gdk/macos/gdkmacosglcontext-private.h b/gdk/macos/gdkmacosglcontext-private.h
index 781035677a..9d9b378b16 100644
--- a/gdk/macos/gdkmacosglcontext-private.h
+++ b/gdk/macos/gdkmacosglcontext-private.h
@@ -29,7 +29,7 @@
 #include "gdkmacossurface.h"
 
 #import <OpenGL/OpenGL.h>
-#import <OpenGL/gl.h>
+#import <OpenGL/gl3.h>
 #import <AppKit/AppKit.h>
 
 G_BEGIN_DECLS
@@ -38,17 +38,15 @@ struct _GdkMacosGLContext
 {
   GdkGLContext parent_instance;
 
+  cairo_region_t *damage;
+
   G_GNUC_BEGIN_IGNORE_DEPRECATIONS
-  NSOpenGLContext *gl_context;
+  CGLContextObj cgl_context;
   G_GNUC_END_IGNORE_DEPRECATIONS
 
-  NSWindow *dummy_window;
-  NSView *dummy_view;
-
-  cairo_region_t *damage;
-
-  guint is_attached : 1;
-  guint needs_resize : 1;
+  GLuint texture;
+  GLuint target;
+  GLuint fbo;
 };
 
 struct _GdkMacosGLContextClass
@@ -56,7 +54,6 @@ struct _GdkMacosGLContextClass
   GdkGLContextClass parent_class;
 };
 
-
 G_END_DECLS
 
 #endif /* __GDK_MACOS_GL_CONTEXT_PRIVATE_H__ */
diff --git a/gdk/macos/gdkmacosglcontext.c b/gdk/macos/gdkmacosglcontext.c
index 1800815786..0686ae048c 100644
--- a/gdk/macos/gdkmacosglcontext.c
+++ b/gdk/macos/gdkmacosglcontext.c
@@ -25,14 +25,108 @@
 
 #include "gdkintl.h"
 
-#include <OpenGL/gl.h>
+#include <OpenGL/gl3.h>
+#include <OpenGL/glext.h>
+#include <OpenGL/CGLIOSurface.h>
 
-#import "GdkMacosGLView.h"
+#include "gdkmacosbuffer-private.h"
 
 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
 
 G_DEFINE_TYPE (GdkMacosGLContext, gdk_macos_gl_context, GDK_TYPE_GL_CONTEXT)
 
+static inline gboolean
+_CHECK (GError     **error,
+        const char  *location,
+        CGLError     cgl_error)
+{
+  if (cgl_error != kCGLNoError)
+    {
+      g_log ("Core OpenGL",
+             G_LOG_LEVEL_CRITICAL,
+             "%s: %s",
+             location, CGLErrorString (cgl_error));
+      g_set_error (error,
+                   GDK_GL_ERROR,
+                   GDK_GL_ERROR_NOT_AVAILABLE,
+                   "%s",
+                   CGLErrorString (cgl_error));
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+static inline gboolean
+_CHECK_GL (GError     **error,
+           const char  *location,
+           GLenum       gl_error)
+{
+  const char *msg;
+
+  switch (gl_error)
+    {
+    case GL_INVALID_ENUM:
+      msg = "invalid enum";
+      break;
+    case GL_INVALID_VALUE:
+      msg = "invalid value";
+      break;
+    case GL_INVALID_OPERATION:
+      msg = "invalid operation";
+      break;
+    case GL_INVALID_FRAMEBUFFER_OPERATION:
+      msg = "invalid framebuffer operation";
+      break;
+    case GL_OUT_OF_MEMORY:
+      msg = "out of memory";
+      break;
+    default:
+      msg = "unknown error";
+      break;
+    }
+
+  if (gl_error != GL_NO_ERROR)
+    {
+      g_log ("OpenGL",
+             G_LOG_LEVEL_CRITICAL,
+             "%s: %s", location, msg);
+      g_set_error (error,
+                   GDK_GL_ERROR,
+                   GDK_GL_ERROR_NOT_AVAILABLE,
+                   "%s", msg);
+      return FALSE;
+    }
+
+  return TRUE;
+}
+
+#define CHECK(error,cgl_error) _CHECK(error, G_STRLOC, cgl_error)
+#define CHECK_GL(error,func) _CHECK_GL(error, G_STRLOC, ({ func; glGetError(); }))
+
+static inline gboolean
+is_opaque_surface (GdkMacosGLContext *self)
+{
+  GdkSurface *surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
+  cairo_region_t *region = GDK_MACOS_SURFACE (surface)->opaque_region;
+
+  if (region != NULL && cairo_region_num_rectangles (region) == 1)
+    {
+      cairo_rectangle_int_t extents;
+
+      cairo_region_get_extents (region, &extents);
+
+      if (extents.x == 0 &&
+          extents.y == 0 &&
+          extents.width == surface->width &&
+          extents.height == surface->height)
+        return TRUE;
+    }
+
+  return FALSE;
+}
+
+
 static const char *
 get_renderer_name (GLint id)
 {
@@ -72,95 +166,166 @@ get_renderer_name (GLint id)
   }
 }
 
-static NSOpenGLContext *
-get_ns_open_gl_context (GdkMacosGLContext  *self,
-                        GError            **error)
+static GLuint
+create_texture (GLuint target)
 {
-  g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
-
-  if (self->gl_context == nil)
+  GLuint texture = 0;
+
+  if (!CHECK_GL (NULL, glGenTextures (1, &texture)) ||
+      !CHECK_GL (NULL, glBindTexture (target, texture)) ||
+      !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_BASE_LEVEL, 0)) ||
+      !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_MIN_FILTER, GL_NEAREST)) ||
+      !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_MAG_FILTER, GL_NEAREST)) ||
+      !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)) ||
+      !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE)) ||
+      !CHECK_GL (NULL, glTexParameteri (target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)))
     {
-      g_set_error_literal (error,
-                           GDK_GL_ERROR,
-                           GDK_GL_ERROR_NOT_AVAILABLE,
-                           "Cannot access NSOpenGLContext for surface");
-      return NULL;
+      glDeleteTextures (1, &texture);
+      return 0;
     }
 
-  return self->gl_context;
+  return texture;
 }
 
-static NSOpenGLPixelFormat *
-create_pixel_format (int      major,
-                     int      minor,
-                     GError **error)
+static void
+gdk_macos_gl_context_allocate (GdkMacosGLContext *self)
 {
-  NSOpenGLPixelFormatAttribute attrs[] = {
-    NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy,
-    NSOpenGLPFAAccelerated,
-    NSOpenGLPFADoubleBuffer,
-    NSOpenGLPFABackingStore,
-    NSOpenGLPFAColorSize, 24,
-    NSOpenGLPFAAlphaSize, 8,
-    0
-  };
+  GdkSurface *surface;
 
-  if (major == 3 && minor == 2)
-    attrs[1] = NSOpenGLProfileVersion3_2Core;
-  else if (major == 4 && minor == 1)
-    attrs[1] = NSOpenGLProfileVersion4_1Core;
+  g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
+  g_assert (self->cgl_context != NULL);
 
-  NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
+  if (!(surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self))))
+    return;
 
-  if (format == NULL)
-    g_set_error (error,
-                 GDK_GL_ERROR,
-                 GDK_GL_ERROR_NOT_AVAILABLE,
-                 "Failed to create pixel format");
+  if (self->texture == 0)
+    {
+      GdkMacosBuffer *buffer;
+      IOSurfaceRef io_surface;
+      guint width;
+      guint height;
+
+      buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
+      io_surface = _gdk_macos_buffer_get_native (buffer);
+      width = _gdk_macos_buffer_get_width (buffer);
+      height = _gdk_macos_buffer_get_height (buffer);
+
+      self->texture = create_texture (self->target);
+      if (!CHECK_GL (NULL, glGenFramebuffers (1, &self->fbo)) ||
+          !CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, self->fbo)) ||
+          !CHECK_GL (NULL, glBindTexture (self->target, self->texture)))
+        return;
+
+      if (!CHECK (NULL, CGLTexImageIOSurface2D (self->cgl_context,
+                                                self->target,
+                                                GL_RGBA,
+                                                width,
+                                                height,
+                                                GL_BGRA,
+                                                GL_UNSIGNED_INT_8_8_8_8_REV,
+                                                io_surface,
+                                                0)))
+        return;
+    }
 
-  return g_steal_pointer (&format);
+  glBindTexture (self->target, self->texture);
+  glBindFramebuffer (GL_FRAMEBUFFER, self->fbo);
+
+  if (!CHECK_GL (NULL, glFramebufferTexture2D (GL_FRAMEBUFFER,
+                                               GL_COLOR_ATTACHMENT0,
+                                               self->target,
+                                               self->texture,
+                                               0)))
+    return;
+
+  switch (glCheckFramebufferStatus (GL_FRAMEBUFFER))
+    {
+    case GL_FRAMEBUFFER_COMPLETE:
+      break;
+
+    case GL_FRAMEBUFFER_UNDEFINED:
+      g_critical ("Framebuffer is undefined");
+      break;
+
+    case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
+      g_critical ("Framebuffer has incomplete attachment");
+      break;
+
+    case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
+      g_critical ("Framebuffer has missing attachment");
+      break;
+
+    case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
+      g_critical ("Framebuffer has incomplete draw buffer");
+      break;
+
+    case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
+      g_critical ("Framebuffer has incomplete read buffer");
+      break;
+
+    case GL_FRAMEBUFFER_UNSUPPORTED:
+      g_critical ("Framebuffer is unsupported");
+      break;
+
+    case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
+      g_critical ("Framebuffer has incomplete multisample");
+      break;
+
+    default:
+      g_critical ("Framebuffer has unknown error");
+      break;
+    }
 }
 
-static NSView *
-ensure_gl_view (GdkMacosGLContext *self)
+static void
+gdk_macos_gl_context_release (GdkMacosGLContext *self)
 {
-  GdkMacosSurface *surface;
-  NSWindow *nswindow;
-  NSView *nsview;
-
   g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
 
-  surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self)));
-  nsview = _gdk_macos_surface_get_view (surface);
-  nswindow = _gdk_macos_surface_get_native (surface);
+  glBindTexture (self->target, 0);
 
-  if G_UNLIKELY (!GDK_IS_MACOS_GL_VIEW (nsview))
+  if (self->fbo != 0)
     {
-      NSRect frame;
-
-      frame = [[nswindow contentView] bounds];
-      nsview = [[GdkMacosGLView alloc] initWithFrame:frame];
-      [nsview setWantsBestResolutionOpenGLSurface:YES];
-      [nsview setPostsFrameChangedNotifications: YES];
-      [nsview setNeedsDisplay:YES];
-      [nswindow setContentView:nsview];
-      [nswindow makeFirstResponder:nsview];
-      [nsview release];
-
-      if (self->dummy_view != NULL)
-        {
-          NSView *dummy_view = g_steal_pointer (&self->dummy_view);
-          [dummy_view release];
-        }
+      glDeleteFramebuffers (1, &self->fbo);
+      self->fbo = 0;
+    }
 
-      if (self->dummy_window != NULL)
-        {
-          NSWindow *dummy_window = g_steal_pointer (&self->dummy_window);
-          [dummy_window release];
-        }
+  if (self->texture != 0)
+    {
+      glDeleteTextures (1, &self->texture);
+      self->texture = 0;
     }
+}
+
+static CGLPixelFormatObj
+create_pixel_format (int      major,
+                     int      minor,
+                     GError **error)
+{
+  CGLPixelFormatAttribute attrs[] = {
+    kCGLPFAOpenGLProfile, (CGLPixelFormatAttribute)kCGLOGLPVersion_Legacy,
+    kCGLPFAAccelerated,
+    //kCGLPFAAllowOfflineRenderers,
+    //kCGLPFADoubleBuffer,
+    //kCGLPFABackingStore,
+    kCGLPFAColorSize, 24,
+    kCGLPFAAlphaSize, 8,
+    0
+  };
+  CGLPixelFormatObj format = NULL;
+  GLint n_format = 1;
 
-  return [nswindow contentView];
+  if (major == 3 && minor == 2)
+    attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL3_Core;
+  else if (major == 4 && minor == 1)
+    attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core;
+
+  g_print (">>> GL %d.%d\n", major, minor);
+
+  if (!CHECK (error, CGLChoosePixelFormat (attrs, &format, &n_format)))
+    return NULL;
+
+  return g_steal_pointer (&format);
 }
 
 static GdkGLAPI
@@ -170,26 +335,25 @@ gdk_macos_gl_context_real_realize (GdkGLContext  *context,
   GdkMacosGLContext *self = (GdkMacosGLContext *)context;
   GdkSurface *surface;
   GdkDisplay *display;
-  NSOpenGLContext *shared_gl_context = nil;
-  NSOpenGLContext *gl_context;
-  NSOpenGLPixelFormat *pixelFormat;
+  CGLPixelFormatObj pixelFormat;
+  CGLContextObj shared_gl_context = nil;
   CGLContextObj cgl_context;
+  CGLContextObj existing;
   GdkGLContext *shared;
-  NSOpenGLContext *existing;
   GLint sync_to_framerate = 1;
   GLint validate = 0;
-  GLint swapRect[4];
+  GLint renderer_id = 0;
   int major, minor;
 
   g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
 
-  if (self->gl_context != nil)
+  if (self->cgl_context != nil)
     return GDK_GL_API_GL;
 
   if (!gdk_gl_context_is_api_allowed (context, GDK_GL_API_GL, error))
     return 0;
 
-  existing = [NSOpenGLContext currentContext];
+  existing = CGLGetCurrentContext ();
 
   gdk_gl_context_get_required_version (context, &major, &minor);
 
@@ -199,99 +363,59 @@ gdk_macos_gl_context_real_realize (GdkGLContext  *context,
 
   if (shared != NULL)
     {
-      if (!(shared_gl_context = get_ns_open_gl_context (GDK_MACOS_GL_CONTEXT (shared), error)))
-        return 0;
+      if (!(shared_gl_context = GDK_MACOS_GL_CONTEXT (shared)->cgl_context))
+        {
+          g_set_error_literal (error,
+                               GDK_GL_ERROR,
+                               GDK_GL_ERROR_NOT_AVAILABLE,
+                               "Cannot access shared CGLContextObj");
+          return 0;
+        }
     }
 
   GDK_DISPLAY_NOTE (display,
                     OPENGL,
-                    g_message ("Creating NSOpenGLContext (version %d.%d)",
+                    g_message ("Creating CGLContextObj (version %d.%d)",
                                major, minor));
 
   if (!(pixelFormat = create_pixel_format (major, minor, error)))
     return 0;
 
-  gl_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
-                                          shareContext:shared_gl_context];
-
-  [pixelFormat release];
-
-  if (gl_context == nil)
+  if (!CHECK (error, CGLCreateContext (pixelFormat, shared_gl_context, &cgl_context)))
     {
-      g_set_error_literal (error,
-                           GDK_GL_ERROR,
-                           GDK_GL_ERROR_NOT_AVAILABLE,
-                           "Failed to create NSOpenGLContext");
+      CGLReleasePixelFormat (pixelFormat);
       return 0;
     }
 
-  cgl_context = [gl_context CGLContextObj];
+  CGLSetCurrentContext (cgl_context);
+  CGLReleasePixelFormat (pixelFormat);
 
-  swapRect[0] = 0;
-  swapRect[1] = 0;
-  swapRect[2] = surface ? surface->width : 0;
-  swapRect[3] = surface ? surface->height : 0;
-
-  CGLSetParameter (cgl_context, kCGLCPSwapRectangle, swapRect);
-  CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate);
-
-  CGLEnable (cgl_context, kCGLCESwapRectangle);
   if (validate)
-    CGLEnable (cgl_context, kCGLCEStateValidation);
+    CHECK (NULL, CGLEnable (cgl_context, kCGLCEStateValidation));
 
-  self->dummy_window = [[NSWindow alloc] initWithContentRect:NSZeroRect
-                                                   styleMask:0
-                                                     backing:NSBackingStoreBuffered
-                                                       defer:NO
-                                                      screen:nil];
-  self->dummy_view = [[NSView alloc] initWithFrame:NSZeroRect];
-  [self->dummy_window setContentView:self->dummy_view];
-  [gl_context setView:self->dummy_view];
+  if (!CHECK (error, CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate)) ||
+      !CHECK (error, CGLGetParameter (cgl_context, kCGLCPCurrentRendererID, &renderer_id)))
+   {
+      CGLReleaseContext (cgl_context);
+      return 0;
+   }
 
-  GLint renderer_id = 0;
-  [gl_context getValues:&renderer_id forParameter:NSOpenGLContextParameterCurrentRendererID];
   GDK_DISPLAY_NOTE (display,
                     OPENGL,
-                    g_message ("Created NSOpenGLContext[%p] using %s",
-                               gl_context,
+                    g_message ("Created CGLContextObj@%p using %s",
+                               cgl_context,
                                get_renderer_name (renderer_id)));
 
-  self->gl_context = g_steal_pointer (&gl_context);
+  self->cgl_context = g_steal_pointer (&cgl_context);
+
+  gdk_macos_gl_context_allocate (self);
 
   if (existing != NULL)
-    [existing makeCurrentContext];
+    CGLSetCurrentContext (existing);
 
   return GDK_GL_API_GL;
 }
 
-static gboolean
-opaque_region_covers_surface (GdkMacosGLContext *self)
-{
-  GdkSurface *surface;
-  cairo_region_t *region;
-
-  g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
-
-  surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self));
-  region = GDK_MACOS_SURFACE (surface)->opaque_region;
-
-  if (region != NULL &&
-      cairo_region_num_rectangles (region) == 1)
-    {
-      cairo_rectangle_int_t extents;
-
-      cairo_region_get_extents (region, &extents);
-
-      if (extents.x == 0 &&
-          extents.y == 0 &&
-          extents.width == surface->width &&
-          extents.height == surface->height)
-        return TRUE;
-    }
-
-  return FALSE;
-}
-
 static void
 gdk_macos_gl_context_begin_frame (GdkDrawContext *context,
                                   gboolean        prefers_high_depth,
@@ -299,66 +423,32 @@ gdk_macos_gl_context_begin_frame (GdkDrawContext *context,
 {
   GdkMacosGLContext *self = (GdkMacosGLContext *)context;
   GdkSurface *surface;
+  GLint opaque;
 
   g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
 
-  surface = gdk_draw_context_get_surface (context);
+  g_print ("Begin frame\n");
+
+  gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
 
   g_clear_pointer (&self->damage, cairo_region_destroy);
   self->damage = cairo_region_copy (painted);
 
-  /* If begin frame is called, that means we are trying to draw to
-   * the NSWindow using our view. That might be a GdkMacosCairoView
-   * but we need it to be a GL view. Also, only in this case do we
-   * want to replace our damage region for the next frame (to avoid
-   * doing it multiple times).
-   */
-  ensure_gl_view (self);
-
-  if (self->needs_resize)
-    {
-      CGLContextObj cgl_context = [self->gl_context CGLContextObj];
-      GLint opaque;
-
-      self->needs_resize = FALSE;
-
-      if (self->dummy_view != NULL)
-        {
-          NSRect frame = NSMakeRect (0, 0, surface->width, surface->height);
-
-          [self->dummy_window setFrame:frame display:NO];
-          [self->dummy_view setFrame:frame];
-        }
-
-      /* Possibly update our opaque setting depending on a resize. We can
-       * rely on getting a resize if decoarated is changed, so this reduces
-       * how much we adjust the parameter.
-       */
-      if (GDK_IS_MACOS_TOPLEVEL_SURFACE (surface))
-        opaque = GDK_MACOS_TOPLEVEL_SURFACE (surface)->decorated;
-      else
-        opaque = FALSE;
+  surface = gdk_draw_context_get_surface (context);
+  opaque = is_opaque_surface (self);
 
-      /* If we are maximized, we might be able to make it opaque */
-      if (opaque == FALSE)
-        opaque = opaque_region_covers_surface (self);
+  /* If the whole surface is opaque, we can help CGL out a bit */
+  if (!CHECK (NULL, CGLSetParameter (self->cgl_context, kCGLCPSurfaceOpacity, &opaque)))
+    return;
 
-      CGLSetParameter (cgl_context, kCGLCPSurfaceOpacity, &opaque);
+  gdk_macos_gl_context_allocate (self);
 
-      [self->gl_context update];
-    }
+  //if (!CHECK (NULL, CGLUpdateContext (self->cgl_context)))
+    //return;
 
   GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, 
painted);
 
-  if (!self->is_attached)
-    {
-      NSView *nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface));
-
-      g_assert (self->gl_context != NULL);
-      g_assert (GDK_IS_MACOS_GL_VIEW (nsview));
-
-      [(GdkMacosGLView *)nsview setOpenGLContext:self->gl_context];
-    }
+  g_print ("Begin frame\n");
 }
 
 static void
@@ -366,32 +456,34 @@ gdk_macos_gl_context_end_frame (GdkDrawContext *context,
                                 cairo_region_t *painted)
 {
   GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
+  GdkSurface *surface;
+  cairo_rectangle_int_t flush_rect;
+  GLint swapRect[4];
 
   g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
-  g_assert (self->gl_context != nil);
+  g_assert (self->cgl_context != nil);
 
   GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->end_frame (context, painted);
 
-  if (!self->is_attached)
-    {
-      GdkSurface *surface = gdk_draw_context_get_surface (context);
-      CGLContextObj glctx = [self->gl_context CGLContextObj];
-      cairo_rectangle_int_t flush_rect;
-      GLint swapRect[4];
-
-      /* Coordinates are in display coordinates, where as flush_rect is
-       * in GDK coordinates. Must flip Y to match display coordinates where
-       * 0,0 is the bottom-left corner.
-       */
-      cairo_region_get_extents (painted, &flush_rect);
-      swapRect[0] = flush_rect.x;                   /* left */
-      swapRect[1] = surface->height - flush_rect.y; /* bottom */
-      swapRect[2] = flush_rect.width;               /* width */
-      swapRect[3] = flush_rect.height;              /* height */
-      CGLSetParameter (glctx, kCGLCPSwapRectangle, swapRect);
-
-      [self->gl_context flushBuffer];
-    }
+  surface = gdk_draw_context_get_surface (context);
+
+  /* Coordinates are in display coordinates, where as flush_rect is
+  * in GDK coordinates. Must flip Y to match display coordinates where
+  * 0,0 is the bottom-left corner.
+  */
+  cairo_region_get_extents (painted, &flush_rect);
+  swapRect[0] = flush_rect.x;                   /* left */
+  swapRect[1] = surface->height - flush_rect.y; /* bottom */
+  swapRect[2] = flush_rect.width;               /* width */
+  swapRect[3] = flush_rect.height;              /* height */
+  CGLSetParameter (self->cgl_context, kCGLCPSwapRectangle, swapRect);
+  CGLEnable (self->cgl_context, kCGLCESwapRectangle);
+
+  glFlush ();
+
+  g_print ("End frame\n");
+
+  _gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
 }
 
 static void
@@ -401,7 +493,10 @@ gdk_macos_gl_context_surface_resized (GdkDrawContext *draw_context)
 
   g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
 
-  self->needs_resize = TRUE;
+  gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
+
+  gdk_macos_gl_context_release (self);
+  gdk_macos_gl_context_allocate (self);
 
   g_clear_pointer (&self->damage, cairo_region_destroy);
 }
@@ -410,22 +505,13 @@ static gboolean
 gdk_macos_gl_context_clear_current (GdkGLContext *context)
 {
   GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
-  NSOpenGLContext *current;
 
   g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
 
-  current = [NSOpenGLContext currentContext];
-
-  if (self->gl_context == current)
+  if (self->cgl_context == CGLGetCurrentContext ())
     {
-      /* The OpenGL mac programming guide suggests that glFlush() is called
-       * before switching current contexts to ensure that the drawing commands
-       * are submitted.
-       */
-      if (current != NULL)
-        glFlush ();
-
-      [NSOpenGLContext clearCurrentContext];
+      glFlush ();
+      CGLSetCurrentContext (NULL);
     }
 
   return TRUE;
@@ -436,22 +522,26 @@ gdk_macos_gl_context_make_current (GdkGLContext *context,
                                    gboolean      surfaceless)
 {
   GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
-  NSOpenGLContext *current;
+  CGLContextObj current;
 
   g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
 
-  current = [NSOpenGLContext currentContext];
+  current = CGLGetCurrentContext ();
 
-  if (self->gl_context != current)
+  if (self->cgl_context != current)
     {
       /* The OpenGL mac programming guide suggests that glFlush() is called
        * before switching current contexts to ensure that the drawing commands
        * are submitted.
+       *
+       * TODO: investigate if we need this because we may switch contexts
+       *       durring composition and only need it when returning to a
+       *       previous context that uses the other context.
        */
       if (current != NULL)
         glFlush ();
 
-      [self->gl_context makeCurrentContext];
+      CGLSetCurrentContext (self->cgl_context);
     }
 
   return TRUE;
@@ -475,27 +565,18 @@ gdk_macos_gl_context_dispose (GObject *gobject)
 {
   GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (gobject);
 
-  if (self->dummy_view != nil)
-    {
-      NSView *nsview = g_steal_pointer (&self->dummy_view);
-      [nsview release];
-    }
-
-  if (self->dummy_window != nil)
-    {
-      NSWindow *nswindow = g_steal_pointer (&self->dummy_window);
-      [nswindow release];
-    }
+  self->texture = 0;
+  self->fbo = 0;
 
-  if (self->gl_context != nil)
+  if (self->cgl_context != nil)
     {
-      NSOpenGLContext *gl_context = g_steal_pointer (&self->gl_context);
+      CGLContextObj cgl_context = g_steal_pointer (&self->cgl_context);
 
-      if (gl_context == [NSOpenGLContext currentContext])
-        [NSOpenGLContext clearCurrentContext];
+      if (cgl_context == CGLGetCurrentContext ())
+        CGLSetCurrentContext (NULL);
 
-      [gl_context clearDrawable];
-      [gl_context release];
+      CGLClearDrawable (cgl_context);
+      CGLDestroyContext (cgl_context);
     }
 
   g_clear_pointer (&self->damage, cairo_region_destroy);
@@ -527,6 +608,10 @@ gdk_macos_gl_context_class_init (GdkMacosGLContextClass *klass)
 static void
 gdk_macos_gl_context_init (GdkMacosGLContext *self)
 {
+  /* We must use GL_TEXTURE_RECTANGLE_ARB to be able to bind an
+   * IOSurfaceRef to the GL texture/framebuffer.
+   */
+  self->target = GL_TEXTURE_RECTANGLE_ARB;
 }
 
 G_GNUC_END_IGNORE_DEPRECATIONS
diff --git a/gdk/macos/gdkmacossurface-private.h b/gdk/macos/gdkmacossurface-private.h
index ccb8a83179..17686a04ee 100644
--- a/gdk/macos/gdkmacossurface-private.h
+++ b/gdk/macos/gdkmacossurface-private.h
@@ -25,6 +25,7 @@
 
 #include "gdksurfaceprivate.h"
 
+#include "gdkmacosbuffer-private.h"
 #include "gdkmacosdisplay.h"
 #include "gdkmacossurface.h"
 
@@ -45,6 +46,7 @@ struct _GdkMacosSurface
   GList frame;
 
   GdkMacosWindow *window;
+  GdkMacosBuffer *buffer;
   GPtrArray *monitors;
   cairo_region_t *input_region;
   cairo_region_t *opaque_region;
@@ -69,68 +71,70 @@ struct _GdkMacosSurfaceClass
   GdkSurfaceClass parent_class;
 };
 
-GdkMacosSurface   *_gdk_macos_surface_new                     (GdkMacosDisplay    *display,
-                                                               GdkSurfaceType      surface_type,
-                                                               GdkSurface         *parent,
-                                                               int                 x,
-                                                               int                 y,
-                                                               int                 width,
-                                                               int                 height);
-NSWindow          *_gdk_macos_surface_get_native              (GdkMacosSurface    *self);
-CGDirectDisplayID  _gdk_macos_surface_get_screen_id           (GdkMacosSurface    *self);
-const char        *_gdk_macos_surface_get_title               (GdkMacosSurface    *self);
-void               _gdk_macos_surface_set_title               (GdkMacosSurface    *self,
-                                                               const char         *title);
-void               _gdk_macos_surface_get_shadow              (GdkMacosSurface    *self,
-                                                               int                *top,
-                                                               int                *right,
-                                                               int                *bottom,
-                                                               int                *left);
-void               _gdk_macos_surface_set_shadow              (GdkMacosSurface    *self,
-                                                               int                 top,
-                                                               int                 right,
-                                                               int                 bottom,
-                                                               int                 left);
-NSView            *_gdk_macos_surface_get_view                (GdkMacosSurface    *self);
-gboolean           _gdk_macos_surface_get_modal_hint          (GdkMacosSurface    *self);
-void               _gdk_macos_surface_set_modal_hint          (GdkMacosSurface    *self,
-                                                               gboolean            modal_hint);
-void               _gdk_macos_surface_set_geometry_hints      (GdkMacosSurface    *self,
-                                                               const GdkGeometry  *geometry,
-                                                               GdkSurfaceHints     geom_mask);
-void               _gdk_macos_surface_resize                  (GdkMacosSurface    *self,
-                                                               int                 width,
-                                                               int                 height);
-void               _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface    *self);
-void               _gdk_macos_surface_update_position         (GdkMacosSurface    *self);
-void               _gdk_macos_surface_show                    (GdkMacosSurface    *self);
-void               _gdk_macos_surface_publish_timings         (GdkMacosSurface    *self,
-                                                               gint64              
predicted_presentation_time,
-                                                               gint64              refresh_interval);
-CGContextRef       _gdk_macos_surface_acquire_context         (GdkMacosSurface    *self,
-                                                               gboolean            clear_scale,
-                                                               gboolean            antialias);
-void               _gdk_macos_surface_release_context         (GdkMacosSurface    *self,
-                                                               CGContextRef        cg_context);
-void               _gdk_macos_surface_synthesize_null_key     (GdkMacosSurface    *self);
-void               _gdk_macos_surface_move                    (GdkMacosSurface    *self,
-                                                               int                 x,
-                                                               int                 y);
-void               _gdk_macos_surface_move_resize             (GdkMacosSurface    *self,
-                                                               int                 x,
-                                                               int                 y,
-                                                               int                 width,
-                                                               int                 height);
-gboolean           _gdk_macos_surface_is_tracking             (GdkMacosSurface    *self,
-                                                               NSTrackingArea     *area);
-void               _gdk_macos_surface_monitor_changed         (GdkMacosSurface    *self);
-GdkMonitor        *_gdk_macos_surface_get_best_monitor        (GdkMacosSurface    *self);
-void               _gdk_macos_surface_reposition_children     (GdkMacosSurface    *self);
-void               _gdk_macos_surface_set_opacity             (GdkMacosSurface    *self,
-                                                               double              opacity);
-void               _gdk_macos_surface_get_root_coords         (GdkMacosSurface    *self,
-                                                               int                *x,
-                                                               int                *y);
+GdkMacosSurface   *_gdk_macos_surface_new                     (GdkMacosDisplay      *display,
+                                                               GdkSurfaceType        surface_type,
+                                                               GdkSurface           *parent,
+                                                               int                   x,
+                                                               int                   y,
+                                                               int                   width,
+                                                               int                   height);
+NSWindow          *_gdk_macos_surface_get_native              (GdkMacosSurface      *self);
+CGDirectDisplayID  _gdk_macos_surface_get_screen_id           (GdkMacosSurface      *self);
+const char        *_gdk_macos_surface_get_title               (GdkMacosSurface      *self);
+void               _gdk_macos_surface_set_title               (GdkMacosSurface      *self,
+                                                               const char           *title);
+void               _gdk_macos_surface_get_shadow              (GdkMacosSurface      *self,
+                                                               int                  *top,
+                                                               int                  *right,
+                                                               int                  *bottom,
+                                                               int                  *left);
+void               _gdk_macos_surface_set_shadow              (GdkMacosSurface      *self,
+                                                               int                   top,
+                                                               int                   right,
+                                                               int                   bottom,
+                                                               int                   left);
+NSView            *_gdk_macos_surface_get_view                (GdkMacosSurface      *self);
+gboolean           _gdk_macos_surface_get_modal_hint          (GdkMacosSurface      *self);
+void               _gdk_macos_surface_set_modal_hint          (GdkMacosSurface      *self,
+                                                               gboolean              modal_hint);
+void               _gdk_macos_surface_set_geometry_hints      (GdkMacosSurface      *self,
+                                                               const GdkGeometry    *geometry,
+                                                               GdkSurfaceHints       geom_mask);
+void               _gdk_macos_surface_resize                  (GdkMacosSurface      *self,
+                                                               int                   width,
+                                                               int                   height);
+void               _gdk_macos_surface_update_fullscreen_state (GdkMacosSurface      *self);
+void               _gdk_macos_surface_update_position         (GdkMacosSurface      *self);
+void               _gdk_macos_surface_show                    (GdkMacosSurface      *self);
+void               _gdk_macos_surface_publish_timings         (GdkMacosSurface      *self,
+                                                               gint64                
predicted_presentation_time,
+                                                               gint64                refresh_interval);
+CGContextRef       _gdk_macos_surface_acquire_context         (GdkMacosSurface      *self,
+                                                               gboolean              clear_scale,
+                                                               gboolean              antialias);
+void               _gdk_macos_surface_synthesize_null_key     (GdkMacosSurface      *self);
+void               _gdk_macos_surface_move                    (GdkMacosSurface      *self,
+                                                               int                   x,
+                                                               int                   y);
+void               _gdk_macos_surface_move_resize             (GdkMacosSurface      *self,
+                                                               int                   x,
+                                                               int                   y,
+                                                               int                   width,
+                                                               int                   height);
+gboolean           _gdk_macos_surface_is_tracking             (GdkMacosSurface      *self,
+                                                               NSTrackingArea       *area);
+void               _gdk_macos_surface_monitor_changed         (GdkMacosSurface      *self);
+GdkMonitor        *_gdk_macos_surface_get_best_monitor        (GdkMacosSurface      *self);
+void               _gdk_macos_surface_reposition_children     (GdkMacosSurface      *self);
+void               _gdk_macos_surface_set_opacity             (GdkMacosSurface      *self,
+                                                               double                opacity);
+void               _gdk_macos_surface_get_root_coords         (GdkMacosSurface      *self,
+                                                               int                  *x,
+                                                               int                  *y);
+void               _gdk_macos_surface_update_size             (GdkMacosSurface      *self);
+GdkMacosBuffer    *_gdk_macos_surface_get_buffer              (GdkMacosSurface      *self);
+void               _gdk_macos_surface_swap_buffers            (GdkMacosSurface      *self,
+                                                               const cairo_region_t *damage);
 
 G_END_DECLS
 
diff --git a/gdk/macos/gdkmacossurface.c b/gdk/macos/gdkmacossurface.c
index c5cf05edf8..cd66c5924f 100644
--- a/gdk/macos/gdkmacossurface.c
+++ b/gdk/macos/gdkmacossurface.c
@@ -23,7 +23,7 @@
 #include <float.h>
 #include <gdk/gdk.h>
 
-#import "GdkMacosCairoView.h"
+#import "GdkMacosView.h"
 
 #include "gdkmacossurface-private.h"
 
@@ -111,9 +111,8 @@ gdk_macos_surface_set_opaque_region (GdkSurface     *surface,
       self->opaque_region = cairo_region_copy (region);
     }
 
-  if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))) &&
-      GDK_IS_MACOS_CAIRO_VIEW (nsview))
-    [(GdkMacosCairoView *)nsview setOpaqueRegion:region];
+  if ((nsview = _gdk_macos_surface_get_view (GDK_MACOS_SURFACE (surface))))
+    [(GdkMacosView *)nsview setOpaqueRegion:region];
 }
 
 static void
@@ -823,16 +822,6 @@ _gdk_macos_surface_acquire_context (GdkMacosSurface *self,
   return cg_context;
 }
 
-void
-_gdk_macos_surface_release_context (GdkMacosSurface *self,
-                                    CGContextRef     cg_context)
-{
-  g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
-
-  CGContextRestoreGState (cg_context);
-  CGContextSetAllowsAntialiasing (cg_context, TRUE);
-}
-
 void
 _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self)
 {
@@ -861,6 +850,37 @@ _gdk_macos_surface_synthesize_null_key (GdkMacosSurface *self)
   _gdk_event_queue_append (display, event);
 }
 
+static void
+gdk_macos_surface_update_buffers (GdkMacosSurface *self)
+{
+  GdkMacosDisplay *display;
+  GdkMacosBuffer *buffer;
+  double scale;
+  guint width;
+  guint height;
+
+  g_assert (GDK_IS_MACOS_SURFACE (self));
+
+  scale = gdk_surface_get_scale_factor (GDK_SURFACE (self));
+  width = GDK_SURFACE (self)->width * scale;
+  height = GDK_SURFACE (self)->height * scale;
+
+  /* Create replacement buffer. We always use 4-byte and 32-bit BGRA for
+   * our surface as that can work with both Cairo and GL. The GdkMacosTile
+   * handles opaque regions for the compositor, so using 3-byte/24-bit is
+   * not a necessary optimization.
+   */
+  display = GDK_MACOS_DISPLAY (gdk_surface_get_display (GDK_SURFACE (self)));
+  buffer = _gdk_macos_buffer_new (display, width, height, scale, 4, 32);
+
+  /* If we failed, just keep the old buffer around. */
+  if (buffer != NULL)
+    {
+      g_clear_object (&self->buffer);
+      self->buffer = buffer;
+    }
+}
+
 void
 _gdk_macos_surface_move (GdkMacosSurface *self,
                          int              x,
@@ -927,7 +947,10 @@ _gdk_macos_surface_move_resize (GdkMacosSurface *self,
   [self->window setFrame:frame_rect display:YES];
 
   if (size_changed)
-    gdk_surface_invalidate_rect (surface, NULL);
+    {
+      gdk_macos_surface_update_buffers (self);
+      gdk_surface_invalidate_rect (surface, NULL);
+    }
 }
 
 gboolean
@@ -995,7 +1018,7 @@ _gdk_macos_surface_monitor_changed (GdkMacosSurface *self)
       g_object_unref (monitor);
     }
 
-  _gdk_surface_update_size (GDK_SURFACE (self));
+  _gdk_macos_surface_update_size (self);
   gdk_surface_invalidate_rect (GDK_SURFACE (self), NULL);
 }
 
@@ -1077,3 +1100,34 @@ _gdk_macos_surface_get_root_coords (GdkMacosSurface *self,
   if (y)
     *y = out_y;
 }
+
+void
+_gdk_macos_surface_update_size (GdkMacosSurface *self)
+{
+  g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
+
+  _gdk_surface_update_size (GDK_SURFACE (self));
+  gdk_macos_surface_update_buffers (self);
+}
+
+GdkMacosBuffer *
+_gdk_macos_surface_get_buffer (GdkMacosSurface *self)
+{
+  g_return_val_if_fail (GDK_IS_MACOS_SURFACE (self), NULL);
+
+  return self->buffer;
+}
+
+void
+_gdk_macos_surface_swap_buffers (GdkMacosSurface      *self,
+                                 const cairo_region_t *damage)
+{
+  g_return_if_fail (GDK_IS_MACOS_SURFACE (self));
+  g_return_if_fail (damage != NULL);
+
+  /* This code looks like it swaps buffers, but since the IOSurfaceRef
+   * appears to be retained on the other side, we really just ask all
+   * of the GdkMacosTile CALayer's to update their contents.
+   */
+  [self->window swapBuffer:self->buffer withDamage:damage];
+}
diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build
index 3a10dbf944..86f20bd2ea 100644
--- a/gdk/macos/meson.build
+++ b/gdk/macos/meson.build
@@ -2,6 +2,7 @@ gdk_macos_sources = files([
   'edgesnapping.c',
 
   'gdkdisplaylinksource.c',
+  'gdkmacosbuffer.c',
   'gdkmacoscairocontext.c',
   'gdkmacosclipboard.c',
   'gdkmacoscursor.c',
@@ -20,11 +21,10 @@ gdk_macos_sources = files([
   'gdkmacosseat.c',
   'gdkmacossurface.c',
   'gdkmacostoplevelsurface.c',
-
   'GdkMacosBaseView.c',
-  'GdkMacosCairoView.c',
-  'GdkMacosCairoSubview.c',
-  'GdkMacosGLView.c',
+  'GdkMacosLayer.c',
+  'GdkMacosTile.c',
+  'GdkMacosView.c',
   'GdkMacosWindow.c',
 ])
 
@@ -46,6 +46,7 @@ gdk_macos_frameworks = [
   'CoreVideo',
   'CoreServices',
   'Foundation',
+  'IOSurface',
   'OpenGL',
   'QuartzCore',
 ]


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]