From 60e67a62e6beb8e0b1d2086dd80c8a0673bbe703 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 29 Oct 2020 09:33:59 -0700 Subject: [PATCH 1/6] macos: move setNeedsDisplay helper to GdkMacosCairoView This isn't needed in the base class, which will eventually get a GL subclass. --- gdk/macos/GdkMacosBaseView.c | 6 ------ gdk/macos/GdkMacosCairoView.c | 6 ++++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/gdk/macos/GdkMacosBaseView.c b/gdk/macos/GdkMacosBaseView.c index 24be4af00e..c4ba8c1020 100644 --- a/gdk/macos/GdkMacosBaseView.c +++ b/gdk/macos/GdkMacosBaseView.c @@ -57,12 +57,6 @@ return self; } --(void)setNeedsDisplay:(BOOL)needsDisplay -{ - for (id child in [self subviews]) - [child setNeedsDisplay:needsDisplay]; -} - -(void)setOpaqueRegion:(cairo_region_t *)region { /* Do nothing */ diff --git a/gdk/macos/GdkMacosCairoView.c b/gdk/macos/GdkMacosCairoView.c index e6a31178b4..2f82488912 100644 --- a/gdk/macos/GdkMacosCairoView.c +++ b/gdk/macos/GdkMacosCairoView.c @@ -53,6 +53,12 @@ return YES; } +-(void)setNeedsDisplay:(BOOL)needsDisplay +{ + for (id child in [self subviews]) + [child setNeedsDisplay:needsDisplay]; +} + -(void)setCairoSurface:(cairo_surface_t *)cairoSurface withDamage:(cairo_region_t *)cairoRegion { From 0040667965968e1dddf8bac59f6ed7063ced25f0 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 29 Oct 2020 09:36:22 -0700 Subject: [PATCH 2/6] macos: remove unused GL layer This isn't being used, and instead we'll go the route of a NSView for the OpenGL implementation. --- gdk/macos/GdkMacosGLLayer.c | 157 ------------------------------------ gdk/macos/GdkMacosGLLayer.h | 40 --------- gdk/macos/meson.build | 1 - 3 files changed, 198 deletions(-) delete mode 100644 gdk/macos/GdkMacosGLLayer.c delete mode 100644 gdk/macos/GdkMacosGLLayer.h diff --git a/gdk/macos/GdkMacosGLLayer.c b/gdk/macos/GdkMacosGLLayer.c deleted file mode 100644 index a1ab55a4dc..0000000000 --- a/gdk/macos/GdkMacosGLLayer.c +++ /dev/null @@ -1,157 +0,0 @@ -/* GdkMacosGLLayer.c - * - * Copyright © 2020 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 . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -/* Based on Chromium image_transport_surface_calayer_mac.mm - * See the BSD-style license above. - */ - -#include "config.h" - -#include - -#import "GdkMacosGLLayer.h" - -@implementation GdkMacosGLLayer - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - --(id)initWithContext:(NSOpenGLContext *)shared -{ - [super init]; - _shared = [shared retain]; - return self; -} - --(void)dealloc -{ - [_shared release]; - _shared = nil; - - [super dealloc]; -} - --(void)setContentsRect:(NSRect)bounds -{ - _pixelSize = bounds.size; - [super setContentsRect:bounds]; -} - --(CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask -{ - return CGLRetainPixelFormat ([[_shared pixelFormat] CGLPixelFormatObj]); -} - --(CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat -{ - CGLContextObj context = NULL; - CGLCreateContext (pixelFormat, [_shared CGLContextObj], &context); - return context; -} - --(BOOL)canDrawInCGLContext:(CGLContextObj)glContext - pixelFormat:(CGLPixelFormatObj)pixelFormat - forLayerTime:(CFTimeInterval)timeInterval - displayTime:(const CVTimeStamp*)timeStamp -{ - return YES; -} - --(void)drawInCGLContext:(CGLContextObj)glContext - pixelFormat:(CGLPixelFormatObj)pixelFormat - forLayerTime:(CFTimeInterval)timeInterval - displayTime:(const CVTimeStamp*)timeStamp -{ - if (_texture == 0) - return; - - glClearColor (1, 0, 1, 1); - glClear (GL_COLOR_BUFFER_BIT); - GLint viewport[4] = {0, 0, 0, 0}; - glGetIntegerv (GL_VIEWPORT, viewport); - NSSize viewportSize = NSMakeSize (viewport[2], viewport[3]); - - /* Set the coordinate system to be one-to-one with pixels. */ - glMatrixMode (GL_PROJECTION); - glLoadIdentity (); - glOrtho (0, viewportSize.width, 0, viewportSize.height, -1, 1); - glMatrixMode (GL_MODELVIEW); - glLoadIdentity (); - - /* Draw a fullscreen quad. */ - glColor4f (1, 1, 1, 1); - glEnable (GL_TEXTURE_RECTANGLE_ARB); - glBindTexture (GL_TEXTURE_RECTANGLE_ARB, _texture); - glBegin (GL_QUADS); - { - glTexCoord2f (0, 0); - glVertex2f (0, 0); - glTexCoord2f (0, _pixelSize.height); - glVertex2f (0, _pixelSize.height); - glTexCoord2f (_pixelSize.width, _pixelSize.height); - glVertex2f (_pixelSize.width, _pixelSize.height); - glTexCoord2f (_pixelSize.width, 0); - glVertex2f (_pixelSize.width, 0); - } - glEnd (); - glBindTexture (0, _texture); - glDisable (GL_TEXTURE_RECTANGLE_ARB); - [super drawInCGLContext:glContext - pixelFormat:pixelFormat - forLayerTime:timeInterval - displayTime:timeStamp]; -} - --(void)setTexture:(GLuint)texture -{ - _texture = texture; - [self setNeedsDisplay]; -} - -G_GNUC_END_IGNORE_DEPRECATIONS - -@end diff --git a/gdk/macos/GdkMacosGLLayer.h b/gdk/macos/GdkMacosGLLayer.h deleted file mode 100644 index dd90c1ca08..0000000000 --- a/gdk/macos/GdkMacosGLLayer.h +++ /dev/null @@ -1,40 +0,0 @@ -/* GdkMacosGLLayer.h - * - * Copyright © 2020 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 . - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include -#include - -#define GDK_IS_MACOS_GL_LAYER(obj) ((obj) && [obj isKindOfClass:[GdkMacosGLLayer class]]) - -G_GNUC_BEGIN_IGNORE_DEPRECATIONS - -@interface GdkMacosGLLayer : CAOpenGLLayer -{ - NSOpenGLContext *_shared; - GLuint _texture; - NSSize _pixelSize; -} - --(id)initWithContext:(NSOpenGLContext *)shared; --(void)setTexture:(GLuint)texture; - -@end - -G_GNUC_END_IGNORE_DEPRECATIONS diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index 0c0fbff9c2..cf602d4439 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -23,7 +23,6 @@ gdk_macos_sources = files([ 'GdkMacosBaseView.c', 'GdkMacosCairoView.c', 'GdkMacosCairoSubview.c', - 'GdkMacosGLLayer.c', 'GdkMacosWindow.c', ]) From a3fd46c516643b259351f963787bc9ece066f70f Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 29 Oct 2020 09:37:33 -0700 Subject: [PATCH 3/6] glsl: tweak pre-processor to support Apple GLSL compiler The Apple GLSL compiler cannot deal with empty pre-processor blocks such as #if GSK_GLES #elif GSK_LEGACY --- gsk/resources/glsl/preamble.fs.glsl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/gsk/resources/glsl/preamble.fs.glsl b/gsk/resources/glsl/preamble.fs.glsl index 85b11c013e..c2cd1cdc29 100644 --- a/gsk/resources/glsl/preamble.fs.glsl +++ b/gsk/resources/glsl/preamble.fs.glsl @@ -5,10 +5,9 @@ uniform float u_alpha;// = 1.0; uniform vec4 u_viewport; uniform vec4[3] u_clip_rect; -#if GSK_GLES -#elif GSK_LEGACY +#if defined(GSK_LEGACY) _OUT_ vec4 outputColor; -#else +#elif !defined(GSK_GLES) _OUT_ vec4 outputColor; #endif From adf60fb3a10cb12115fe7599dd13f3282b79e2e4 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 29 Oct 2020 09:46:02 -0700 Subject: [PATCH 4/6] macos: be tolerant of NULL GL context Some code appears to unconditionally attempt to make the context current, so this makes things tolerant to a NULL GdkGLContext and just return FALSE. --- gdk/macos/gdkmacosdisplay.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gdk/macos/gdkmacosdisplay.c b/gdk/macos/gdkmacosdisplay.c index 6a89def8c1..665a567e70 100644 --- a/gdk/macos/gdkmacosdisplay.c +++ b/gdk/macos/gdkmacosdisplay.c @@ -649,7 +649,10 @@ gdk_macos_display_make_gl_context_current (GdkDisplay *display, GdkGLContext *gl_context) { g_assert (GDK_IS_MACOS_DISPLAY (display)); - g_assert (GDK_IS_MACOS_GL_CONTEXT (gl_context)); + g_assert (!gl_context || GDK_IS_MACOS_GL_CONTEXT (gl_context)); + + if (gl_context == NULL) + return FALSE; return _gdk_macos_gl_context_make_current (GDK_MACOS_GL_CONTEXT (gl_context)); } From eb809ba42547f098f5cb1d916b525cf0c8c384c1 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 29 Oct 2020 10:30:41 -0700 Subject: [PATCH 5/6] macos: tweak gravity while resizing This helps a situation where the window contents has not changed in time for a drawing. Setting the texture gravity helps that side or corner to be less jittery while moving. Ideally, we can get to a point where we are synchronized and keeping up with drawing fast enough to not need this. That may require some work to drive frame clocks from drawRect: though. --- gdk/macos/GdkMacosWindow.c | 42 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/gdk/macos/GdkMacosWindow.c b/gdk/macos/GdkMacosWindow.c index c4a133afcc..b511233cdf 100644 --- a/gdk/macos/GdkMacosWindow.c +++ b/gdk/macos/GdkMacosWindow.c @@ -25,6 +25,7 @@ #import "GdkMacosBaseView.h" #import "GdkMacosCairoView.h" +#import "GdkMacosGLView.h" #import "GdkMacosWindow.h" #include "gdkmacosdisplay-private.h" @@ -140,6 +141,10 @@ _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]; + break; } @@ -543,6 +548,43 @@ inManualResize = YES; resizeEdge = edge; + if (GDK_IS_MACOS_GL_VIEW ([self contentView])) + { + CALayerContentsGravity gravity = kCAGravityBottomLeft; + + 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; + } + + [[[self contentView] layer] setContentsGravity:gravity]; + } + initialResizeFrame = [self frame]; initialResizeLocation = [self convertPointToScreen:[self mouseLocationOutsideOfEventStream]]; } From de9c9efa6fbb23db2955357d8ea599d52ed12434 Mon Sep 17 00:00:00 2001 From: Christian Hergert Date: Thu, 29 Oct 2020 10:33:22 -0700 Subject: [PATCH 6/6] 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. --- gdk/macos/GdkMacosGLView.c | 124 ++++++++ gdk/macos/GdkMacosGLView.h | 41 +++ gdk/macos/gdkmacosglcontext-private.h | 6 +- gdk/macos/gdkmacosglcontext.c | 389 ++++++++++++++++++++++---- gdk/macos/meson.build | 1 + 5 files changed, 502 insertions(+), 59 deletions(-) create mode 100644 gdk/macos/GdkMacosGLView.c create mode 100644 gdk/macos/GdkMacosGLView.h diff --git a/gdk/macos/GdkMacosGLView.c b/gdk/macos/GdkMacosGLView.c new file mode 100644 index 0000000000..64bd0e89c3 --- /dev/null +++ b/gdk/macos/GdkMacosGLView.c @@ -0,0 +1,124 @@ +/* GdkMacosGLView.c + * + * Copyright 2020 Christian Hergert + * + * 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 . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include +#include + +#include "gdkinternals.h" +#include "gdkmacossurface-private.h" + +#import "GdkMacosGLView.h" + +@implementation GdkMacosGLView + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +-(void)lockFocus +{ + NSOpenGLContext *context; + + [super lockFocus]; + + context = [self openGLContext]; + + if ([context view] != self) + [context setView: self]; +} + +-(void)drawRect:(NSRect)rect +{ +} + +-(void)clearGLContext +{ + if (_openGLContext != nil) + [_openGLContext clearDrawable]; + + _openGLContext = nil; +} + +-(void)setOpenGLContext:(NSOpenGLContext*)context +{ + if (_openGLContext != context) + { + if (_openGLContext != nil) + [_openGLContext clearDrawable]; + + _openGLContext = context; + + if (_openGLContext != nil) + { + [_openGLContext setView:self]; + [self setWantsLayer:YES]; + [self.layer setContentsGravity:kCAGravityBottomLeft]; + [_openGLContext update]; + } + } +} + +-(NSOpenGLContext *)openGLContext +{ + return _openGLContext; +} + +-(BOOL)isOpaque +{ + return NO; +} + +-(BOOL)isFlipped +{ + return YES; +} + +-(BOOL)acceptsFirstMouse +{ + return YES; +} + +-(BOOL)mouseDownCanMoveWindow +{ + return NO; +} + +-(void)invalidateRegion:(const cairo_region_t *)region +{ + if (region != NULL) + { + guint n_rects = cairo_region_num_rectangles (region); + + for (guint i = 0; i < n_rects; i++) + { + cairo_rectangle_int_t rect; + NSRect nsrect; + + cairo_region_get_rectangle (region, i, &rect); + nsrect = NSMakeRect (rect.x, rect.y, rect.width, rect.height); + + [self setNeedsDisplayInRect:nsrect]; + } + } +} + +G_GNUC_END_IGNORE_DEPRECATIONS + +@end diff --git a/gdk/macos/GdkMacosGLView.h b/gdk/macos/GdkMacosGLView.h new file mode 100644 index 0000000000..320b1a163b --- /dev/null +++ b/gdk/macos/GdkMacosGLView.h @@ -0,0 +1,41 @@ +/* GdkMacosGLView.h + * + * Copyright © 2020 Red Hat, Inc. + * Copyright © 2005-2007 Imendio AB + * + * 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 . + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include + +#import "GdkMacosBaseView.h" + +#define GDK_IS_MACOS_GL_VIEW(obj) ((obj) && [obj isKindOfClass:[GdkMacosGLView class]]) + +G_GNUC_BEGIN_IGNORE_DEPRECATIONS + +@interface GdkMacosGLView : GdkMacosBaseView +{ + NSOpenGLContext *_openGLContext; +} + +-(void)setOpenGLContext:(NSOpenGLContext*)context; +-(NSOpenGLContext *)openGLContext; +-(void)invalidateRegion:(const cairo_region_t *)region; + +G_GNUC_END_IGNORE_DEPRECATIONS + +@end diff --git a/gdk/macos/gdkmacosglcontext-private.h b/gdk/macos/gdkmacosglcontext-private.h index e976939eb1..e47a3e2eec 100644 --- a/gdk/macos/gdkmacosglcontext-private.h +++ b/gdk/macos/gdkmacosglcontext-private.h @@ -41,7 +41,11 @@ struct _GdkMacosGLContext NSOpenGLContext *gl_context; G_GNUC_END_IGNORE_DEPRECATIONS - gboolean is_attached; + NSWindow *dummy_window; + NSView *dummy_view; + + guint is_attached : 1; + guint needs_resize : 1; }; struct _GdkMacosGLContextClass diff --git a/gdk/macos/gdkmacosglcontext.c b/gdk/macos/gdkmacosglcontext.c index 0d1e03e1d2..e1d792a41e 100644 --- a/gdk/macos/gdkmacosglcontext.c +++ b/gdk/macos/gdkmacosglcontext.c @@ -25,31 +25,343 @@ #include "gdkinternals.h" #include "gdkintl.h" +#include + +#import "GdkMacosGLView.h" + G_GNUC_BEGIN_IGNORE_DEPRECATIONS G_DEFINE_TYPE (GdkMacosGLContext, gdk_macos_gl_context, GDK_TYPE_GL_CONTEXT) +static const char * +get_renderer_name (GLint id) +{ + static char renderer_name[32]; + + switch (id & kCGLRendererIDMatchingMask) + { + case kCGLRendererGenericID: return "Generic"; + case kCGLRendererGenericFloatID: return "Generic Float"; + case kCGLRendererAppleSWID: return "Apple Software Renderer"; + case kCGLRendererATIRage128ID: return "ATI Rage 128"; + case kCGLRendererATIRadeonID: return "ATI Radeon"; + case kCGLRendererATIRageProID: return "ATI Rage Pro"; + case kCGLRendererATIRadeon8500ID: return "ATI Radeon 8500"; + case kCGLRendererATIRadeon9700ID: return "ATI Radeon 9700"; + case kCGLRendererATIRadeonX1000ID: return "ATI Radeon X1000"; + case kCGLRendererATIRadeonX2000ID: return "ATI Radeon X2000"; + case kCGLRendererATIRadeonX3000ID: return "ATI Radeon X3000"; + case kCGLRendererATIRadeonX4000ID: return "ATI Radeon X4000"; + case kCGLRendererGeForce2MXID: return "GeForce 2 MX"; + case kCGLRendererGeForce3ID: return "GeForce 3"; + case kCGLRendererGeForceFXID: return "GeForce FX"; + case kCGLRendererGeForce8xxxID: return "GeForce 8xxx"; + case kCGLRendererGeForceID: return "GeForce"; + case kCGLRendererVTBladeXP2ID: return "VT Blade XP 2"; + case kCGLRendererIntel900ID: return "Intel 900"; + case kCGLRendererIntelX3100ID: return "Intel X3100"; + case kCGLRendererIntelHDID: return "Intel HD"; + case kCGLRendererIntelHD4000ID: return "Intel HD 4000"; + case kCGLRendererIntelHD5000ID: return "Intel HD 5000"; + case kCGLRendererMesa3DFXID: return "Mesa 3DFX"; + + default: + snprintf (renderer_name, sizeof renderer_name, "0x%08x", id & kCGLRendererIDMatchingMask); + renderer_name[sizeof renderer_name-1] = 0; + return renderer_name; + } +} + +static NSOpenGLContext * +get_ns_open_gl_context (GdkMacosGLContext *self, + GError **error) +{ + g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); + + if (self->gl_context == nil) + { + g_set_error_literal (error, + GDK_GL_ERROR, + GDK_GL_ERROR_NOT_AVAILABLE, + "Cannot access NSOpenGLContext for surface"); + return NULL; + } + + return self->gl_context; +} + +static NSOpenGLPixelFormat * +create_pixel_format (int major, + int minor, + GError **error) +{ + NSOpenGLPixelFormatAttribute attrs[] = { + NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersionLegacy, + NSOpenGLPFAAccelerated, + NSOpenGLPFADoubleBuffer, + + (NSOpenGLPixelFormatAttribute)nil + }; + + if (major == 3 && minor == 2) + attrs[1] = NSOpenGLProfileVersion3_2Core; + else if (major == 4 && minor == 1) + attrs[1] = NSOpenGLProfileVersion4_1Core; + + NSOpenGLPixelFormat *format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs]; + + if (format == NULL) + g_set_error (error, + GDK_GL_ERROR, + GDK_GL_ERROR_NOT_AVAILABLE, + "Failed to create pixel format"); + + return g_steal_pointer (&format); +} + +static NSView * +ensure_gl_view (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); + + if (!GDK_IS_MACOS_GL_VIEW (nsview)) + { + NSRect frame; + + frame = [[nswindow contentView] bounds]; + nsview = [[GdkMacosGLView alloc] initWithFrame:frame]; + [nsview setWantsBestResolutionOpenGLSurface:YES]; + [nsview setPostsFrameChangedNotifications: YES]; + [nsview setNeedsDisplay:YES]; + [nswindow setContentView:nsview]; + [nsview release]; + } + + return [nswindow contentView]; +} + +static gboolean +gdk_macos_gl_context_real_realize (GdkGLContext *context, + GError **error) +{ + GdkMacosGLContext *self = (GdkMacosGLContext *)context; + GdkSurface *surface; + NSOpenGLContext *shared_gl_context = nil; + NSOpenGLContext *gl_context; + NSOpenGLPixelFormat *pixelFormat; + GdkGLContext *shared; + GdkGLContext *shared_data; + GdkGLContext *existing; + GLint sync_to_framerate = 1; + GLint opaque = 0; + GLint validate = 0; + int major, minor; + + g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); + + if (self->gl_context != nil) + return TRUE; + + existing = gdk_gl_context_get_current (); + + gdk_gl_context_get_required_version (context, &major, &minor); + + surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context)); + shared = gdk_gl_context_get_shared_context (context); + shared_data = gdk_surface_get_shared_data_gl_context (surface); + + if (shared != NULL) + { + if (!(shared_gl_context = get_ns_open_gl_context (GDK_MACOS_GL_CONTEXT (shared), error))) + return FALSE; + } + else if (shared_data != NULL) + { + if (!(shared_gl_context = get_ns_open_gl_context (GDK_MACOS_GL_CONTEXT (shared_data), error))) + return FALSE; + } + + GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), + OPENGL, + g_message ("Creating NSOpenGLContext (version %d.%d)", + major, minor)); + + if (!(pixelFormat = create_pixel_format (major, minor, error))) + return FALSE; + + gl_context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat + shareContext:shared_gl_context]; + + [pixelFormat release]; + + if (gl_context == nil) + { + g_set_error_literal (error, + GDK_GL_ERROR, + GDK_GL_ERROR_NOT_AVAILABLE, + "Failed to create NSOpenGLContext"); + return FALSE; + } + + [gl_context setValues:&sync_to_framerate forParameter:NSOpenGLCPSwapInterval]; + [gl_context setValues:&opaque forParameter:NSOpenGLCPSurfaceOpacity]; + [gl_context setValues:&validate forParameter:NSOpenGLContextParameterStateValidation]; + + if (self->is_attached || shared == NULL) + { + NSRect frame = NSMakeRect (0, 0, 1, 1); + + self->dummy_window = [[NSWindow alloc] initWithContentRect:frame + styleMask:0 + backing:NSBackingStoreBuffered + defer:NO + screen:nil]; + self->dummy_view = [[NSView alloc] initWithFrame:frame]; + [self->dummy_window setContentView:self->dummy_view]; + [gl_context setView:self->dummy_view]; + } + + [gl_context makeCurrentContext]; + GLint renderer_id = 0; + [gl_context getValues:&renderer_id forParameter:NSOpenGLContextParameterCurrentRendererID]; + GDK_DISPLAY_NOTE (gdk_draw_context_get_display (GDK_DRAW_CONTEXT (context)), + OPENGL, + g_message ("Created NSOpenGLContext[%p] using %s", + gl_context, + get_renderer_name (renderer_id))); + [NSOpenGLContext clearCurrentContext]; + + self->gl_context = g_steal_pointer (&gl_context); + + if (existing != NULL) + [GDK_MACOS_GL_CONTEXT (existing)->gl_context makeCurrentContext]; + + return TRUE; +} + +static void +gdk_macos_gl_context_begin_frame (GdkDrawContext *context, + cairo_region_t *painted) +{ + GdkMacosGLContext *self = (GdkMacosGLContext *)context; + + g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); + + /* 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. + */ + if (!self->is_attached && + gdk_gl_context_get_shared_context (GDK_GL_CONTEXT (context))) + ensure_gl_view (self); + + if (self->needs_resize) + { + self->needs_resize = FALSE; + + if (self->dummy_view != NULL) + { + GdkSurface *surface = gdk_draw_context_get_surface (context); + GLint vals[2] = { surface->width, surface->height }; + + [self->gl_context setValues:vals forParameter:NSOpenGLContextParameterSurfaceBackingSize]; + } + + [self->gl_context update]; + } + + GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, painted); + + if (!self->is_attached) + { + GdkMacosSurface *surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (context)); + NSView *nsview = _gdk_macos_surface_get_view (surface); + + g_assert (self->gl_context != NULL); + g_assert (GDK_IS_MACOS_GL_VIEW (nsview)); + + [(GdkMacosGLView *)nsview setOpenGLContext:self->gl_context]; + } +} + static void gdk_macos_gl_context_end_frame (GdkDrawContext *context, cairo_region_t *painted) { GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context); + GdkMacosSurface *surface; + NSView *nsview; + cairo_rectangle_int_t extents; + + g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); + g_assert (self->gl_context != nil); + + surface = GDK_MACOS_SURFACE (gdk_draw_context_get_surface (context)); + nsview = self->dummy_view ? + self->dummy_view : + _gdk_macos_surface_get_view (surface); + + GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->end_frame (context, painted); + + G_STATIC_ASSERT (sizeof (GLint) == sizeof (int)); + + cairo_region_get_extents (painted, &extents); + + [self->gl_context + setValues:(GLint *)&extents + forParameter:NSOpenGLCPSwapRectangle]; + [self->gl_context flushBuffer]; +} + +static void +gdk_macos_gl_context_surface_resized (GdkDrawContext *draw_context) +{ + GdkMacosGLContext *self = (GdkMacosGLContext *)draw_context; g_assert (GDK_IS_MACOS_GL_CONTEXT (self)); - [self->gl_context flushBuffer]; + self->needs_resize = TRUE; } static void gdk_macos_gl_context_dispose (GObject *gobject) { - GdkMacosGLContext *context_macos = GDK_MACOS_GL_CONTEXT (gobject); + GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (gobject); - if (context_macos->gl_context != NULL) + if (self->dummy_view != nil) { - [context_macos->gl_context clearDrawable]; - [context_macos->gl_context release]; - context_macos->gl_context = NULL; + NSView *nsview = g_steal_pointer (&self->dummy_view); + + if (GDK_IS_MACOS_GL_VIEW (nsview)) + [(GdkMacosGLView *)nsview setOpenGLContext:nil]; + + [nsview release]; + } + + if (self->dummy_window != nil) + { + NSWindow *nswindow = g_steal_pointer (&self->dummy_window); + + [nswindow release]; + } + + if (self->gl_context != nil) + { + NSOpenGLContext *gl_context = g_steal_pointer (&self->gl_context); + + if (gl_context == [NSOpenGLContext currentContext]) + [NSOpenGLContext clearCurrentContext]; + + [gl_context clearDrawable]; + [gl_context release]; } G_OBJECT_CLASS (gdk_macos_gl_context_parent_class)->dispose (gobject); @@ -60,10 +372,15 @@ gdk_macos_gl_context_class_init (GdkMacosGLContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GdkDrawContextClass *draw_context_class = GDK_DRAW_CONTEXT_CLASS (klass); + GdkGLContextClass *gl_class = GDK_GL_CONTEXT_CLASS (klass); object_class->dispose = gdk_macos_gl_context_dispose; + draw_context_class->begin_frame = gdk_macos_gl_context_begin_frame; draw_context_class->end_frame = gdk_macos_gl_context_end_frame; + draw_context_class->surface_resized = gdk_macos_gl_context_surface_resized; + + gl_class->realize = gdk_macos_gl_context_real_realize; } static void @@ -77,65 +394,17 @@ _gdk_macos_gl_context_new (GdkMacosSurface *surface, GdkGLContext *share, GError **error) { - static const NSOpenGLPixelFormatAttribute attrs[] = { - NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core, - NSOpenGLPFADoubleBuffer, - NSOpenGLPFAColorSize, 24, - NSOpenGLPFAAlphaSize, 8, - 0 - }; - - NSOpenGLPixelFormat *format; - GdkMacosGLContext *context = NULL; - NSOpenGLContext *ctx; - GdkDisplay *display; - NSView *nsview; - GLint sync_to_framerate = 1; + GdkMacosGLContext *context; g_return_val_if_fail (GDK_IS_MACOS_SURFACE (surface), NULL); g_return_val_if_fail (!share || GDK_IS_MACOS_GL_CONTEXT (share), NULL); - display = gdk_surface_get_display (GDK_SURFACE (surface)); - - if (!(format = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs])) - { - g_set_error_literal (error, - GDK_GL_ERROR, - GDK_GL_ERROR_NOT_AVAILABLE, - _("Unable to create a GL pixel format")); - goto failure; - } - - ctx = [[NSOpenGLContext alloc] initWithFormat:format - shareContext:share ? GDK_MACOS_GL_CONTEXT (share)->gl_context : nil]; - if (ctx == NULL) - { - g_set_error_literal (error, - GDK_GL_ERROR, - GDK_GL_ERROR_NOT_AVAILABLE, - _("Unable to create a GL context")); - goto failure; - } - - nsview = _gdk_macos_surface_get_view (surface); - [nsview setWantsBestResolutionOpenGLSurface:YES]; - [ctx setValues:&sync_to_framerate forParameter:NSOpenGLCPSwapInterval]; - [ctx setView:nsview]; - - GDK_NOTE (OPENGL, - g_print ("Created NSOpenGLContext[%p]\n", ctx)); - context = g_object_new (GDK_TYPE_MACOS_GL_CONTEXT, "surface", surface, "shared-context", share, NULL); - context->gl_context = ctx; - context->is_attached = attached; - -failure: - if (format != NULL) - [format release]; + context->is_attached = !!attached; return GDK_GL_CONTEXT (context); } @@ -145,9 +414,13 @@ _gdk_macos_gl_context_make_current (GdkMacosGLContext *self) { g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE); - [self->gl_context makeCurrentContext]; + if (self->gl_context != nil) + { + [self->gl_context makeCurrentContext]; + return TRUE; + } - return TRUE; + return FALSE; } G_GNUC_END_IGNORE_DEPRECATIONS diff --git a/gdk/macos/meson.build b/gdk/macos/meson.build index cf602d4439..e2bba8549b 100644 --- a/gdk/macos/meson.build +++ b/gdk/macos/meson.build @@ -23,6 +23,7 @@ gdk_macos_sources = files([ 'GdkMacosBaseView.c', 'GdkMacosCairoView.c', 'GdkMacosCairoSubview.c', + 'GdkMacosGLView.c', 'GdkMacosWindow.c', ])