nsgl_context.m (11858B)
1 //======================================================================== 2 // GLFW 3.3 macOS - www.glfw.org 3 //------------------------------------------------------------------------ 4 // Copyright (c) 2009-2019 Camilla Löwy <elmindreda@glfw.org> 5 // 6 // This software is provided 'as-is', without any express or implied 7 // warranty. In no event will the authors be held liable for any damages 8 // arising from the use of this software. 9 // 10 // Permission is granted to anyone to use this software for any purpose, 11 // including commercial applications, and to alter it and redistribute it 12 // freely, subject to the following restrictions: 13 // 14 // 1. The origin of this software must not be misrepresented; you must not 15 // claim that you wrote the original software. If you use this software 16 // in a product, an acknowledgment in the product documentation would 17 // be appreciated but is not required. 18 // 19 // 2. Altered source versions must be plainly marked as such, and must not 20 // be misrepresented as being the original software. 21 // 22 // 3. This notice may not be removed or altered from any source 23 // distribution. 24 // 25 //======================================================================== 26 // It is fine to use C99 in this file because it will not be built with VS 27 //======================================================================== 28 29 #include "internal.h" 30 31 #include <unistd.h> 32 #include <math.h> 33 34 static void makeContextCurrentNSGL(_GLFWwindow* window) 35 { 36 @autoreleasepool { 37 38 if (window) 39 [window->context.nsgl.object makeCurrentContext]; 40 else 41 [NSOpenGLContext clearCurrentContext]; 42 43 _glfwPlatformSetTls(&_glfw.contextSlot, window); 44 45 } // autoreleasepool 46 } 47 48 static void swapBuffersNSGL(_GLFWwindow* window) 49 { 50 @autoreleasepool { 51 52 // HACK: Simulate vsync with usleep as NSGL swap interval does not apply to 53 // windows with a non-visible occlusion state 54 if (window->ns.occluded) 55 { 56 int interval = 0; 57 [window->context.nsgl.object getValues:&interval 58 forParameter:NSOpenGLContextParameterSwapInterval]; 59 60 if (interval > 0) 61 { 62 const double framerate = 60.0; 63 const uint64_t frequency = _glfwPlatformGetTimerFrequency(); 64 const uint64_t value = _glfwPlatformGetTimerValue(); 65 66 const double elapsed = value / (double) frequency; 67 const double period = 1.0 / framerate; 68 const double delay = period - fmod(elapsed, period); 69 70 usleep(floorl(delay * 1e6)); 71 } 72 } 73 74 [window->context.nsgl.object flushBuffer]; 75 76 } // autoreleasepool 77 } 78 79 static void swapIntervalNSGL(int interval) 80 { 81 @autoreleasepool { 82 83 _GLFWwindow* window = _glfwPlatformGetTls(&_glfw.contextSlot); 84 if (window) 85 { 86 [window->context.nsgl.object setValues:&interval 87 forParameter:NSOpenGLContextParameterSwapInterval]; 88 } 89 90 } // autoreleasepool 91 } 92 93 static int extensionSupportedNSGL(const char* extension) 94 { 95 // There are no NSGL extensions 96 return GLFW_FALSE; 97 } 98 99 static GLFWglproc getProcAddressNSGL(const char* procname) 100 { 101 CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, 102 procname, 103 kCFStringEncodingASCII); 104 105 GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, 106 symbolName); 107 108 CFRelease(symbolName); 109 110 return symbol; 111 } 112 113 static void destroyContextNSGL(_GLFWwindow* window) 114 { 115 @autoreleasepool { 116 117 [window->context.nsgl.pixelFormat release]; 118 window->context.nsgl.pixelFormat = nil; 119 120 [window->context.nsgl.object release]; 121 window->context.nsgl.object = nil; 122 123 } // autoreleasepool 124 } 125 126 127 ////////////////////////////////////////////////////////////////////////// 128 ////// GLFW internal API ////// 129 ////////////////////////////////////////////////////////////////////////// 130 131 // Initialize OpenGL support 132 // 133 GLFWbool _glfwInitNSGL(void) 134 { 135 if (_glfw.nsgl.framework) 136 return GLFW_TRUE; 137 138 _glfw.nsgl.framework = 139 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); 140 if (_glfw.nsgl.framework == NULL) 141 { 142 _glfwInputError(GLFW_API_UNAVAILABLE, 143 "NSGL: Failed to locate OpenGL framework"); 144 return GLFW_FALSE; 145 } 146 147 return GLFW_TRUE; 148 } 149 150 // Terminate OpenGL support 151 // 152 void _glfwTerminateNSGL(void) 153 { 154 } 155 156 // Create the OpenGL context 157 // 158 GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, 159 const _GLFWctxconfig* ctxconfig, 160 const _GLFWfbconfig* fbconfig) 161 { 162 if (ctxconfig->client == GLFW_OPENGL_ES_API) 163 { 164 _glfwInputError(GLFW_API_UNAVAILABLE, 165 "NSGL: OpenGL ES is not available on macOS"); 166 return GLFW_FALSE; 167 } 168 169 if (ctxconfig->major > 2) 170 { 171 if (ctxconfig->major == 3 && ctxconfig->minor < 2) 172 { 173 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 174 "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); 175 return GLFW_FALSE; 176 } 177 178 if (!ctxconfig->forward || ctxconfig->profile != GLFW_OPENGL_CORE_PROFILE) 179 { 180 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 181 "NSGL: The targeted version of macOS only supports forward-compatible core profile contexts for OpenGL 3.2 and above"); 182 return GLFW_FALSE; 183 } 184 } 185 186 // Context robustness modes (GL_KHR_robustness) are not yet supported by 187 // macOS but are not a hard constraint, so ignore and continue 188 189 // Context release behaviors (GL_KHR_context_flush_control) are not yet 190 // supported by macOS but are not a hard constraint, so ignore and continue 191 192 // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not 193 // a hard constraint, so ignore and continue 194 195 // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but 196 // are not a hard constraint, so ignore and continue 197 198 #define addAttrib(a) \ 199 { \ 200 assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ 201 attribs[index++] = a; \ 202 } 203 #define setAttrib(a, v) { addAttrib(a); addAttrib(v); } 204 205 NSOpenGLPixelFormatAttribute attribs[40]; 206 int index = 0; 207 208 addAttrib(NSOpenGLPFAAccelerated); 209 addAttrib(NSOpenGLPFAClosestPolicy); 210 211 if (ctxconfig->nsgl.offline) 212 { 213 addAttrib(NSOpenGLPFAAllowOfflineRenderers); 214 // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in 215 // Info.plist for unbundled applications 216 // HACK: This assumes that NSOpenGLPixelFormat will remain 217 // a straightforward wrapper of its CGL counterpart 218 addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); 219 } 220 221 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 222 if (ctxconfig->major >= 4) 223 { 224 setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); 225 } 226 else 227 #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ 228 if (ctxconfig->major >= 3) 229 { 230 setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); 231 } 232 233 if (ctxconfig->major <= 2) 234 { 235 if (fbconfig->auxBuffers != GLFW_DONT_CARE) 236 setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); 237 238 if (fbconfig->accumRedBits != GLFW_DONT_CARE && 239 fbconfig->accumGreenBits != GLFW_DONT_CARE && 240 fbconfig->accumBlueBits != GLFW_DONT_CARE && 241 fbconfig->accumAlphaBits != GLFW_DONT_CARE) 242 { 243 const int accumBits = fbconfig->accumRedBits + 244 fbconfig->accumGreenBits + 245 fbconfig->accumBlueBits + 246 fbconfig->accumAlphaBits; 247 248 setAttrib(NSOpenGLPFAAccumSize, accumBits); 249 } 250 } 251 252 if (fbconfig->redBits != GLFW_DONT_CARE && 253 fbconfig->greenBits != GLFW_DONT_CARE && 254 fbconfig->blueBits != GLFW_DONT_CARE) 255 { 256 int colorBits = fbconfig->redBits + 257 fbconfig->greenBits + 258 fbconfig->blueBits; 259 260 // macOS needs non-zero color size, so set reasonable values 261 if (colorBits == 0) 262 colorBits = 24; 263 else if (colorBits < 15) 264 colorBits = 15; 265 266 setAttrib(NSOpenGLPFAColorSize, colorBits); 267 } 268 269 if (fbconfig->alphaBits != GLFW_DONT_CARE) 270 setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); 271 272 if (fbconfig->depthBits != GLFW_DONT_CARE) 273 setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); 274 275 if (fbconfig->stencilBits != GLFW_DONT_CARE) 276 setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); 277 278 if (fbconfig->stereo) 279 { 280 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 281 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 282 "NSGL: Stereo rendering is deprecated"); 283 return GLFW_FALSE; 284 #else 285 addAttrib(NSOpenGLPFAStereo); 286 #endif 287 } 288 289 if (fbconfig->doublebuffer) 290 addAttrib(NSOpenGLPFADoubleBuffer); 291 292 if (fbconfig->samples != GLFW_DONT_CARE) 293 { 294 if (fbconfig->samples == 0) 295 { 296 setAttrib(NSOpenGLPFASampleBuffers, 0); 297 } 298 else 299 { 300 setAttrib(NSOpenGLPFASampleBuffers, 1); 301 setAttrib(NSOpenGLPFASamples, fbconfig->samples); 302 } 303 } 304 305 // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB 306 // framebuffer, so there's no need (and no way) to request it 307 308 addAttrib(0); 309 310 #undef addAttrib 311 #undef setAttrib 312 313 window->context.nsgl.pixelFormat = 314 [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; 315 if (window->context.nsgl.pixelFormat == nil) 316 { 317 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 318 "NSGL: Failed to find a suitable pixel format"); 319 return GLFW_FALSE; 320 } 321 322 NSOpenGLContext* share = nil; 323 324 if (ctxconfig->share) 325 share = ctxconfig->share->context.nsgl.object; 326 327 window->context.nsgl.object = 328 [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat 329 shareContext:share]; 330 if (window->context.nsgl.object == nil) 331 { 332 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 333 "NSGL: Failed to create OpenGL context"); 334 return GLFW_FALSE; 335 } 336 337 if (fbconfig->transparent) 338 { 339 GLint opaque = 0; 340 [window->context.nsgl.object setValues:&opaque 341 forParameter:NSOpenGLContextParameterSurfaceOpacity]; 342 } 343 344 [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; 345 346 [window->context.nsgl.object setView:window->ns.view]; 347 348 window->context.makeCurrent = makeContextCurrentNSGL; 349 window->context.swapBuffers = swapBuffersNSGL; 350 window->context.swapInterval = swapIntervalNSGL; 351 window->context.extensionSupported = extensionSupportedNSGL; 352 window->context.getProcAddress = getProcAddressNSGL; 353 window->context.destroy = destroyContextNSGL; 354 355 return GLFW_TRUE; 356 } 357 358 359 ////////////////////////////////////////////////////////////////////////// 360 ////// GLFW native API ////// 361 ////////////////////////////////////////////////////////////////////////// 362 363 GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) 364 { 365 _GLFWwindow* window = (_GLFWwindow*) handle; 366 _GLFW_REQUIRE_INIT_OR_RETURN(nil); 367 368 if (window->context.client == GLFW_NO_API) 369 { 370 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); 371 return nil; 372 } 373 374 return window->context.nsgl.object; 375 } 376