1/* 2 * GStreamer 3 * Copyright (C) 2012 Matthew Waters <ystreet00@gmail.com> 4 * 5 * This library is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU Library General Public 7 * License as published by the Free Software Foundation; either 8 * version 2 of the License, or (at your option) any later version. 9 * 10 * This library is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 * Library General Public License for more details. 14 * 15 * You should have received a copy of the GNU Library General Public 16 * License along with this library; if not, write to the 17 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 18 * Boston, MA 02110-1301, USA. 19 */ 20 21#ifdef HAVE_CONFIG_H 22#include "config.h" 23#endif 24 25#include <Cocoa/Cocoa.h> 26 27#include "gstglcontext_cocoa.h" 28#include "gstgl_cocoa_private.h" 29 30static gboolean gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api, 31 GstGLContext * other_context, GError **error); 32static void gst_gl_context_cocoa_destroy_context (GstGLContext *context); 33static guintptr gst_gl_context_cocoa_get_gl_context (GstGLContext * window); 34static gboolean gst_gl_context_cocoa_activate (GstGLContext * context, gboolean activate); 35static GstGLAPI gst_gl_context_cocoa_get_gl_api (GstGLContext * context); 36static GstGLPlatform gst_gl_context_cocoa_get_gl_platform (GstGLContext * context); 37static void gst_gl_context_cocoa_swap_buffers (GstGLContext * context); 38 39GST_DEBUG_CATEGORY_STATIC (gst_gl_context_cocoa_debug); 40#define GST_CAT_DEFAULT gst_gl_context_cocoa_debug 41 42G_DEFINE_TYPE_WITH_CODE (GstGLContextCocoa, gst_gl_context_cocoa, 43 GST_TYPE_GL_CONTEXT, 44 G_ADD_PRIVATE (GstGLContextCocoa) 45 GST_DEBUG_CATEGORY_INIT (gst_gl_context_cocoa_debug, "glcontext_cocoa", 0, "Cocoa GL Context"); ); 46 47static void 48gst_gl_context_cocoa_class_init (GstGLContextCocoaClass * klass) 49{ 50 GstGLContextClass *context_class = (GstGLContextClass *) klass; 51 52 context_class->swap_buffers = 53 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_swap_buffers); 54 context_class->destroy_context = 55 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_destroy_context); 56 context_class->create_context = 57 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_create_context); 58 context_class->get_gl_context = 59 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_context); 60 context_class->activate = GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_activate); 61 context_class->get_gl_api = 62 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_api); 63 context_class->get_gl_platform = 64 GST_DEBUG_FUNCPTR (gst_gl_context_cocoa_get_gl_platform); 65} 66 67static void 68gst_gl_context_cocoa_init (GstGLContextCocoa * context) 69{ 70 context->priv = gst_gl_context_cocoa_get_instance_private (context); 71} 72 73/* Must be called in the gl thread */ 74GstGLContextCocoa * 75gst_gl_context_cocoa_new (GstGLDisplay * display) 76{ 77 GstGLContextCocoa *context; 78 79 if ((gst_gl_display_get_handle_type (display) & GST_GL_DISPLAY_TYPE_COCOA) == 0) 80 /* we require an cocoa display to create CGL contexts */ 81 return NULL; 82 83 context = g_object_new (GST_TYPE_GL_CONTEXT_COCOA, NULL); 84 gst_object_ref_sink (context); 85 86 return context; 87} 88 89struct pixel_attr 90{ 91 CGLPixelFormatAttribute attr; 92 const gchar *attr_name; 93}; 94 95static struct pixel_attr pixel_attrs[] = { 96 {kCGLPFAAllRenderers, "All Renderers"}, 97 {kCGLPFADoubleBuffer, "Double Buffered"}, 98 {kCGLPFAAuxBuffers, "Aux Buffers"}, 99 {kCGLPFAColorSize, "Color Size"}, 100 {kCGLPFAAlphaSize, "Alpha Size"}, 101 {kCGLPFADepthSize, "Depth Size"}, 102 {kCGLPFAStencilSize, "Stencil Size"}, 103 {kCGLPFAAccumSize, "Accum Size"}, 104 {kCGLPFAMinimumPolicy, "Minimum Policy"}, 105 {kCGLPFAMaximumPolicy, "Maximum Policy"}, 106 {kCGLPFASampleBuffers, "Sample Buffers"}, 107 {kCGLPFASamples, "Samples"}, 108 {kCGLPFAAuxDepthStencil, "Aux Depth Stencil"}, 109 {kCGLPFAColorFloat, "Color Float"}, 110 {kCGLPFAMultisample, "Multisample"}, 111 {kCGLPFASupersample, "Supersample"}, 112 {kCGLPFARendererID, "Renderer ID"}, 113 {kCGLPFANoRecovery, "No Recovery"}, 114 {kCGLPFAAccelerated, "Accelerated"}, 115 {kCGLPFAClosestPolicy, "Closest Policy"}, 116 {kCGLPFABackingStore, "Backing Store"}, 117 {kCGLPFADisplayMask, "Display Mask"}, 118 {kCGLPFAAllowOfflineRenderers, "Allow Offline Renderers"}, 119 {kCGLPFAAcceleratedCompute, "Accelerated Compute"}, 120 {kCGLPFAOpenGLProfile, "OpenGL Profile"}, 121 {kCGLPFAVirtualScreenCount, "Virtual Screen Count"}, 122#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_11 123 {kCGLPFAStereo, "Stereo"}, 124#endif 125#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_9 126 {kCGLPFACompliant, "Compliant"}, 127 {kCGLPFARemotePBuffer, "Remote PBuffer"}, 128 {kCGLPFASingleRenderer, "Single Renderer"}, 129 {kCGLPFAWindow, "Window"}, 130#endif 131#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7 132// {kCGLPFAOffScreen, "Off Screen"}, 133// {kCGLPFAPBuffer, "PBuffer"}, 134#endif 135#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 136// {kCGLPFAFullScreen, "Full Screen"}, 137#endif 138#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_5 139// {kCGLPFAMPSafe, "MP Safe"}, 140// {kCGLPFAMultiScreen, "Multi Screen"}, 141// {kCGLPFARobust, "Robust"}, 142#endif 143}; 144 145void 146gst_gl_context_cocoa_dump_pixel_format (CGLPixelFormatObj fmt) 147{ 148 int i; 149 150 for (i = 0; i < G_N_ELEMENTS (pixel_attrs); i++) { 151 gint val; 152 CGLError ret = CGLDescribePixelFormat (fmt, 0, pixel_attrs[i].attr, &val); 153 154 if (ret != kCGLNoError) { 155 GST_WARNING ("failed to get pixel format %p attribute %s", fmt, pixel_attrs[i].attr_name); 156 } else { 157 GST_DEBUG ("Pixel format %p attr %s = %i", fmt, pixel_attrs[i].attr_name, 158 val); 159 } 160 } 161} 162 163CGLPixelFormatObj 164gst_gl_context_cocoa_get_pixel_format (GstGLContextCocoa *context) 165{ 166 return context->priv->pixel_format; 167} 168 169static gboolean 170gst_gl_context_cocoa_create_context (GstGLContext *context, GstGLAPI gl_api, 171 GstGLContext *other_context, GError **error) 172{ 173 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context); 174 GstGLContextCocoaPrivate *priv = context_cocoa->priv; 175 GstGLWindow *window = gst_gl_context_get_window (context); 176 GstGLWindowCocoa *window_cocoa = GST_GL_WINDOW_COCOA (window); 177 GstGLAPI context_api = GST_GL_API_NONE; 178 const GLint swapInterval = 1; 179 CGLPixelFormatObj fmt = NULL; 180 CGLContextObj glContext; 181 CGLPixelFormatAttribute attribs[] = { 182 kCGLPFADoubleBuffer, 183 kCGLPFAAccumSize, 32, 184 0 185 }; 186 CGLError ret; 187 gint pix_fmt_i = 0; 188 gint npix; 189 190 if ((gl_api & (GST_GL_API_OPENGL | GST_GL_API_OPENGL3)) == GST_GL_API_NONE) { 191 g_set_error (error, GST_GL_CONTEXT_ERROR, 192 GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 193 "The CGL backend only supports GL and GL3"); 194 goto error; 195 } 196 197 priv->gl_context = nil; 198 if (other_context) 199 priv->external_gl_context = (CGLContextObj) gst_gl_context_get_gl_context (other_context); 200 else 201 priv->external_gl_context = NULL; 202 203 if (priv->external_gl_context) { 204 gint profile; 205 206 fmt = CGLGetPixelFormat (priv->external_gl_context); 207 208#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 209 /* core profile is only available in >= 10.7 */ 210 if (kCGLNoError == CGLDescribePixelFormat (fmt, 0, kCGLPFAOpenGLProfile, 211 &profile)) { 212 if (profile == kCGLOGLPVersion_3_2_Core) { 213 context_api = GST_GL_API_OPENGL3; 214 } else { 215 context_api =GST_GL_API_OPENGL; 216 } 217 } 218#else 219 context_api = GST_GL_API_OPENGL; 220#endif 221 } 222 223 if (!fmt) { 224#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070 225 if (gl_api & GST_GL_API_OPENGL3) { 226 attribs[pix_fmt_i++] = kCGLPFAOpenGLProfile; 227 attribs[pix_fmt_i++] = (int) kCGLOGLPVersion_3_2_Core; 228 context_api = GST_GL_API_OPENGL3; 229 } else { 230 context_api = GST_GL_API_OPENGL; 231 } 232#else 233 context_api = GST_GL_API_OPENGL; 234#endif 235 236 attribs[pix_fmt_i++] = 0; 237 238 ret = CGLChoosePixelFormat (attribs, &fmt, &npix); 239 if (ret != kCGLNoError) { 240 g_set_error (error, GST_GL_CONTEXT_ERROR, 241 GST_GL_CONTEXT_ERROR_WRONG_CONFIG, "cannot choose a pixel format: %s", CGLErrorString (ret)); 242 goto error; 243 } 244 } 245 246 gst_gl_context_cocoa_dump_pixel_format (fmt); 247 248 ret = CGLCreateContext (fmt, priv->external_gl_context, &glContext); 249 if (ret != kCGLNoError) { 250 g_set_error (error, GST_GL_CONTEXT_ERROR, GST_GL_CONTEXT_ERROR_CREATE_CONTEXT, 251 "failed to create context: %s", CGLErrorString (ret)); 252 goto error; 253 } 254 255 context_cocoa->priv->pixel_format = fmt; 256 context_cocoa->priv->gl_context = glContext; 257 258 _invoke_on_main ((GstGLWindowCB) gst_gl_window_cocoa_create_window, 259 gst_object_ref (window_cocoa), (GDestroyNotify) gst_object_unref); 260 261 if (!context_cocoa->priv->gl_context) { 262 goto error; 263 } 264 265 GST_INFO_OBJECT (context, "GL context created: %p", context_cocoa->priv->gl_context); 266 267 CGLSetCurrentContext (context_cocoa->priv->gl_context); 268 269 /* Back and front buffers are swapped only during the vertical retrace of the monitor. 270 * Discarded if you configured your driver to Never-use-V-Sync. 271 */ 272 CGLSetParameter (context_cocoa->priv->gl_context, kCGLCPSwapInterval, &swapInterval); 273 274 context_cocoa->priv->context_api = context_api; 275 276 if (window) 277 gst_object_unref (window); 278 279 return TRUE; 280 281error: 282 { 283 if (window) 284 gst_object_unref (window); 285 return FALSE; 286 } 287} 288 289static void 290gst_gl_context_cocoa_swap_buffers (GstGLContext * context) 291{ 292} 293 294static void 295gst_gl_context_cocoa_destroy_context (GstGLContext *context) 296{ 297 /* FIXME: Need to release context and other things? */ 298} 299 300static guintptr 301gst_gl_context_cocoa_get_gl_context (GstGLContext * context) 302{ 303 return (guintptr) GST_GL_CONTEXT_COCOA (context)->priv->gl_context; 304} 305 306static gboolean 307gst_gl_context_cocoa_activate (GstGLContext * context, gboolean activate) 308{ 309 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context); 310 gpointer context_handle = activate ? context_cocoa->priv->gl_context : NULL; 311 312 return kCGLNoError == CGLSetCurrentContext (context_handle); 313} 314 315static GstGLAPI 316gst_gl_context_cocoa_get_gl_api (GstGLContext * context) 317{ 318 GstGLContextCocoa *context_cocoa = GST_GL_CONTEXT_COCOA (context); 319 320 if (context_cocoa->priv->gl_context) 321 return context_cocoa->priv->context_api; 322 323 return GST_GL_API_OPENGL | GST_GL_API_OPENGL3; 324} 325 326static GstGLPlatform 327gst_gl_context_cocoa_get_gl_platform (GstGLContext * context) 328{ 329 return GST_GL_PLATFORM_CGL; 330} 331 332guintptr 333gst_gl_context_cocoa_get_current_context (void) 334{ 335 return (guintptr) CGLGetCurrentContext (); 336} 337