forked from AuroraMiddleware/gtk
50e2a8239b
The GdkMacosBuffer object already has storage for tracking the damage region as it is used in GdkMacosCairoContext to manually copy regions from the front buffer to the back buffer. This makes the GdkMacosGLContext also use that field so that we can easily drop old damage regions when the buffer is lost. This happens during resizes, monitor changes, etc.
657 lines
20 KiB
C
657 lines
20 KiB
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.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 "gdkconfig.h"
|
|
|
|
#include <OpenGL/gl3.h>
|
|
#include <OpenGL/CGLIOSurface.h>
|
|
#include <QuartzCore/QuartzCore.h>
|
|
|
|
#include "gdkmacosbuffer-private.h"
|
|
#include "gdkmacosglcontext-private.h"
|
|
#include "gdkmacossurface-private.h"
|
|
|
|
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
|
|
|
|
G_DEFINE_TYPE (GdkMacosGLContext, gdk_macos_gl_context, GDK_TYPE_GL_CONTEXT)
|
|
|
|
#define CHECK(error,cgl_error) _CHECK_CGL(error, G_STRLOC, cgl_error)
|
|
static inline gboolean
|
|
_CHECK_CGL (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;
|
|
}
|
|
|
|
/* Apple's OpenGL implementation does not contain the extension to
|
|
* perform log handler callbacks when errors occur. Therefore, to aid in
|
|
* tracking down issues we have a CHECK_GL() macro that can wrap GL
|
|
* calls and check for an error afterwards.
|
|
*
|
|
* To make this easier, we use a statement expression, as this will
|
|
* always be using something GCC-compatible on macOS.
|
|
*/
|
|
#define CHECK_GL(error,func) _CHECK_GL(error, G_STRLOC, ({ func; glGetError(); }))
|
|
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);
|
|
if (error != NULL)
|
|
g_set_error (error,
|
|
GDK_GL_ERROR,
|
|
GDK_GL_ERROR_NOT_AVAILABLE,
|
|
"%s", msg);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
check_framebuffer_status (GLenum target)
|
|
{
|
|
switch (glCheckFramebufferStatus (target))
|
|
{
|
|
case GL_FRAMEBUFFER_COMPLETE:
|
|
return TRUE;
|
|
|
|
case GL_FRAMEBUFFER_UNDEFINED:
|
|
g_critical ("Framebuffer is undefined");
|
|
return FALSE;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
|
g_critical ("Framebuffer has incomplete attachment");
|
|
return FALSE;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
|
g_critical ("Framebuffer has missing attachment");
|
|
return FALSE;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
|
g_critical ("Framebuffer has incomplete draw buffer");
|
|
return FALSE;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
|
g_critical ("Framebuffer has incomplete read buffer");
|
|
return FALSE;
|
|
|
|
case GL_FRAMEBUFFER_UNSUPPORTED:
|
|
g_critical ("Framebuffer is unsupported");
|
|
return FALSE;
|
|
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE:
|
|
g_critical ("Framebuffer has incomplete multisample");
|
|
return FALSE;
|
|
|
|
default:
|
|
g_critical ("Framebuffer has unknown error");
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
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 GLuint
|
|
create_texture (CGLContextObj cgl_context,
|
|
GLuint target,
|
|
IOSurfaceRef io_surface,
|
|
guint width,
|
|
guint height)
|
|
{
|
|
GLuint texture = 0;
|
|
|
|
if (!CHECK_GL (NULL, glActiveTexture (GL_TEXTURE0)) ||
|
|
!CHECK_GL (NULL, glGenTextures (1, &texture)) ||
|
|
!CHECK_GL (NULL, glBindTexture (target, texture)) ||
|
|
!CHECK (NULL, CGLTexImageIOSurface2D (cgl_context,
|
|
target,
|
|
GL_RGBA,
|
|
width,
|
|
height,
|
|
GL_BGRA,
|
|
GL_UNSIGNED_INT_8_8_8_8_REV,
|
|
io_surface,
|
|
0)) ||
|
|
!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)) ||
|
|
!CHECK_GL (NULL, glBindTexture (target, 0)))
|
|
{
|
|
glDeleteTextures (1, &texture);
|
|
return 0;
|
|
}
|
|
|
|
return texture;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_gl_context_allocate (GdkMacosGLContext *self)
|
|
{
|
|
GdkSurface *surface;
|
|
GLint opaque;
|
|
|
|
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
|
|
g_assert (self->cgl_context != NULL);
|
|
g_assert (self->target != 0);
|
|
g_assert (self->texture != 0 || self->fbo == 0);
|
|
g_assert (self->fbo != 0 || self->texture == 0);
|
|
|
|
if (!(surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (self))))
|
|
return;
|
|
|
|
/* Alter to an opaque surface if necessary */
|
|
opaque = _gdk_macos_surface_is_opaque (GDK_MACOS_SURFACE (surface));
|
|
if (opaque != self->last_opaque)
|
|
{
|
|
self->last_opaque = !!opaque;
|
|
if (!CHECK (NULL, CGLSetParameter (self->cgl_context, kCGLCPSurfaceOpacity, &opaque)))
|
|
return;
|
|
}
|
|
|
|
if (self->texture == 0)
|
|
{
|
|
GdkMacosBuffer *buffer;
|
|
IOSurfaceRef io_surface;
|
|
guint width;
|
|
guint height;
|
|
GLuint texture = 0;
|
|
GLuint fbo = 0;
|
|
|
|
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);
|
|
|
|
/* We might need to re-enforce our CGL context here to keep
|
|
* video playing correctly. Something, somewhere, might have
|
|
* changed the context without touching GdkGLContext.
|
|
*
|
|
* Without this, video_player often breaks in gtk-demo when using
|
|
* the GStreamer backend.
|
|
*/
|
|
CGLSetCurrentContext (self->cgl_context);
|
|
|
|
if (!(texture = create_texture (self->cgl_context, self->target, io_surface, width, height)) ||
|
|
!CHECK_GL (NULL, glGenFramebuffers (1, &fbo)) ||
|
|
!CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, fbo)) ||
|
|
!CHECK_GL (NULL, glBindTexture (self->target, texture)) ||
|
|
!CHECK_GL (NULL, glFramebufferTexture2D (GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT0,
|
|
self->target,
|
|
texture,
|
|
0)) ||
|
|
!check_framebuffer_status (GL_FRAMEBUFFER))
|
|
{
|
|
glDeleteFramebuffers (1, &fbo);
|
|
glDeleteTextures (1, &texture);
|
|
return;
|
|
}
|
|
|
|
glBindTexture (self->target, 0);
|
|
glBindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
|
|
self->texture = texture;
|
|
self->fbo = fbo;
|
|
}
|
|
}
|
|
|
|
static void
|
|
gdk_macos_gl_context_release (GdkMacosGLContext *self)
|
|
{
|
|
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
|
|
g_assert (self->texture != 0 || self->fbo == 0);
|
|
g_assert (self->fbo != 0 || self->texture == 0);
|
|
|
|
glBindFramebuffer (GL_FRAMEBUFFER, 0);
|
|
glActiveTexture (GL_TEXTURE0);
|
|
glBindTexture (self->target, 0);
|
|
|
|
if (self->fbo != 0)
|
|
{
|
|
glDeleteFramebuffers (1, &self->fbo);
|
|
self->fbo = 0;
|
|
}
|
|
|
|
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,
|
|
kCGLPFAAllowOfflineRenderers, /* allow sharing across GPUs */
|
|
kCGLPFADepthSize, 0,
|
|
kCGLPFAStencilSize, 0,
|
|
kCGLPFAColorSize, 24,
|
|
kCGLPFAAlphaSize, 8,
|
|
0
|
|
};
|
|
CGLPixelFormatObj format = NULL;
|
|
GLint n_format = 1;
|
|
|
|
if (major == 3 && minor == 2)
|
|
attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL3_Core;
|
|
else if (major == 4 && minor == 1)
|
|
attrs[1] = (CGLPixelFormatAttribute)kCGLOGLPVersion_GL4_Core;
|
|
|
|
if (!CHECK (error, CGLChoosePixelFormat (attrs, &format, &n_format)))
|
|
return NULL;
|
|
|
|
return g_steal_pointer (&format);
|
|
}
|
|
|
|
static GdkGLAPI
|
|
gdk_macos_gl_context_real_realize (GdkGLContext *context,
|
|
GError **error)
|
|
{
|
|
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
|
|
GdkSurface *surface;
|
|
GdkDisplay *display;
|
|
CGLPixelFormatObj pixelFormat;
|
|
CGLContextObj shared_gl_context = nil;
|
|
CGLContextObj cgl_context;
|
|
CGLContextObj existing;
|
|
GdkGLContext *shared;
|
|
GLint sync_to_framerate = 1;
|
|
GLint validate = 0;
|
|
GLint renderer_id = 0;
|
|
GLint swapRect[4];
|
|
int major, minor;
|
|
|
|
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
|
|
|
|
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 = CGLGetCurrentContext ();
|
|
|
|
gdk_gl_context_get_required_version (context, &major, &minor);
|
|
|
|
display = gdk_gl_context_get_display (context);
|
|
shared = gdk_display_get_gl_context (display);
|
|
|
|
if (shared != NULL)
|
|
{
|
|
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 CGLContextObj (version %d.%d)",
|
|
major, minor));
|
|
|
|
if (!(pixelFormat = create_pixel_format (major, minor, error)))
|
|
return 0;
|
|
|
|
if (!CHECK (error, CGLCreateContext (pixelFormat, shared_gl_context, &cgl_context)))
|
|
{
|
|
CGLReleasePixelFormat (pixelFormat);
|
|
return 0;
|
|
}
|
|
|
|
CGLSetCurrentContext (cgl_context);
|
|
CGLReleasePixelFormat (pixelFormat);
|
|
|
|
if (validate)
|
|
CHECK (NULL, CGLEnable (cgl_context, kCGLCEStateValidation));
|
|
|
|
if (!CHECK (error, CGLSetParameter (cgl_context, kCGLCPSwapInterval, &sync_to_framerate)) ||
|
|
!CHECK (error, CGLGetParameter (cgl_context, kCGLCPCurrentRendererID, &renderer_id)))
|
|
{
|
|
CGLReleaseContext (cgl_context);
|
|
return 0;
|
|
}
|
|
|
|
surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context));
|
|
|
|
if (surface != NULL)
|
|
{
|
|
/* Setup initial swap rectangle. We might not actually need this
|
|
* anymore though as we are rendering to an IOSurface and we have
|
|
* a scissor clip when rendering to it.
|
|
*/
|
|
swapRect[0] = 0;
|
|
swapRect[1] = 0;
|
|
swapRect[2] = surface->width;
|
|
swapRect[3] = surface->height;
|
|
CGLSetParameter (cgl_context, kCGLCPSwapRectangle, swapRect);
|
|
CGLEnable (cgl_context, kCGLCESwapRectangle);
|
|
}
|
|
|
|
GDK_DISPLAY_NOTE (display,
|
|
OPENGL,
|
|
g_message ("Created CGLContextObj@%p using %s",
|
|
cgl_context,
|
|
get_renderer_name (renderer_id)));
|
|
|
|
self->cgl_context = g_steal_pointer (&cgl_context);
|
|
|
|
if (existing != NULL)
|
|
CGLSetCurrentContext (existing);
|
|
|
|
return GDK_GL_API_GL;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_gl_context_begin_frame (GdkDrawContext *context,
|
|
gboolean prefers_high_depth,
|
|
cairo_region_t *region)
|
|
{
|
|
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
|
|
GdkMacosBuffer *buffer;
|
|
cairo_region_t *copy;
|
|
GdkSurface *surface;
|
|
|
|
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
|
|
|
|
copy = cairo_region_copy (region);
|
|
surface = gdk_draw_context_get_surface (context);
|
|
buffer = _gdk_macos_surface_get_buffer (GDK_MACOS_SURFACE (surface));
|
|
|
|
_gdk_macos_buffer_set_flipped (buffer, TRUE);
|
|
_gdk_macos_buffer_set_damage (buffer, region);
|
|
|
|
/* Create our render target and bind it */
|
|
gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
|
|
gdk_macos_gl_context_allocate (self);
|
|
|
|
GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->begin_frame (context, prefers_high_depth, region);
|
|
|
|
gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
|
|
CHECK_GL (NULL, glBindFramebuffer (GL_FRAMEBUFFER, self->fbo));
|
|
}
|
|
|
|
static void
|
|
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->cgl_context != nil);
|
|
|
|
GDK_DRAW_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->end_frame (context, painted);
|
|
|
|
surface = gdk_draw_context_get_surface (context);
|
|
gdk_gl_context_make_current (GDK_GL_CONTEXT (self));
|
|
|
|
/* 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);
|
|
|
|
gdk_macos_gl_context_release (self);
|
|
|
|
glFlush ();
|
|
|
|
/* Begin a Core Animation transaction so that all changes we
|
|
* make within the window are seen atomically.
|
|
*/
|
|
[CATransaction begin];
|
|
[CATransaction setDisableActions:YES];
|
|
_gdk_macos_surface_swap_buffers (GDK_MACOS_SURFACE (surface), painted);
|
|
[CATransaction commit];
|
|
}
|
|
|
|
static void
|
|
gdk_macos_gl_context_surface_resized (GdkDrawContext *draw_context)
|
|
{
|
|
GdkMacosGLContext *self = (GdkMacosGLContext *)draw_context;
|
|
|
|
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
|
|
|
|
if (self->cgl_context != NULL)
|
|
CGLUpdateContext (self->cgl_context);
|
|
}
|
|
|
|
static gboolean
|
|
gdk_macos_gl_context_clear_current (GdkGLContext *context)
|
|
{
|
|
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
|
|
|
|
g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
|
|
|
|
if (self->cgl_context == CGLGetCurrentContext ())
|
|
{
|
|
glFlush ();
|
|
CGLSetCurrentContext (NULL);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static gboolean
|
|
gdk_macos_gl_context_make_current (GdkGLContext *context,
|
|
gboolean surfaceless)
|
|
{
|
|
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (context);
|
|
CGLContextObj current;
|
|
|
|
g_return_val_if_fail (GDK_IS_MACOS_GL_CONTEXT (self), FALSE);
|
|
|
|
current = CGLGetCurrentContext ();
|
|
|
|
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 ();
|
|
|
|
CGLSetCurrentContext (self->cgl_context);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static cairo_region_t *
|
|
gdk_macos_gl_context_get_damage (GdkGLContext *context)
|
|
{
|
|
GdkMacosGLContext *self = (GdkMacosGLContext *)context;
|
|
const cairo_region_t *damage;
|
|
GdkMacosBuffer *buffer;
|
|
GdkSurface *surface;
|
|
|
|
g_assert (GDK_IS_MACOS_GL_CONTEXT (self));
|
|
|
|
if ((surface = gdk_draw_context_get_surface (GDK_DRAW_CONTEXT (context))) &&
|
|
(buffer = GDK_MACOS_SURFACE (surface)->front) &&
|
|
(damage = _gdk_macos_buffer_get_damage (buffer)))
|
|
return cairo_region_copy (damage);
|
|
|
|
return GDK_GL_CONTEXT_CLASS (gdk_macos_gl_context_parent_class)->get_damage (context);
|
|
}
|
|
|
|
static guint
|
|
gdk_macos_gl_context_get_default_framebuffer (GdkGLContext *context)
|
|
{
|
|
return GDK_MACOS_GL_CONTEXT (context)->fbo;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_gl_context_dispose (GObject *gobject)
|
|
{
|
|
GdkMacosGLContext *self = GDK_MACOS_GL_CONTEXT (gobject);
|
|
|
|
self->texture = 0;
|
|
self->fbo = 0;
|
|
|
|
if (self->cgl_context != nil)
|
|
{
|
|
CGLContextObj cgl_context = g_steal_pointer (&self->cgl_context);
|
|
|
|
if (cgl_context == CGLGetCurrentContext ())
|
|
CGLSetCurrentContext (NULL);
|
|
|
|
CGLClearDrawable (cgl_context);
|
|
CGLDestroyContext (cgl_context);
|
|
}
|
|
|
|
G_OBJECT_CLASS (gdk_macos_gl_context_parent_class)->dispose (gobject);
|
|
}
|
|
|
|
static void
|
|
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->get_damage = gdk_macos_gl_context_get_damage;
|
|
gl_class->clear_current = gdk_macos_gl_context_clear_current;
|
|
gl_class->make_current = gdk_macos_gl_context_make_current;
|
|
gl_class->realize = gdk_macos_gl_context_real_realize;
|
|
gl_class->get_default_framebuffer = gdk_macos_gl_context_get_default_framebuffer;
|
|
|
|
gl_class->backend_type = GDK_GL_CGL;
|
|
}
|
|
|
|
static void
|
|
gdk_macos_gl_context_init (GdkMacosGLContext *self)
|
|
{
|
|
self->target = GL_TEXTURE_RECTANGLE;
|
|
}
|
|
|
|
G_GNUC_END_IGNORE_DEPRECATIONS
|