nsgl_context.m (11919B)
1 //======================================================================== 2 // GLFW 3.4 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 27 #include "internal.h" 28 29 #if defined(_GLFW_COCOA) 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 assert(window != NULL); 85 86 [window->context.nsgl.object setValues:&interval 87 forParameter:NSOpenGLContextParameterSwapInterval]; 88 89 } // autoreleasepool 90 } 91 92 static int extensionSupportedNSGL(const char* extension) 93 { 94 // There are no NSGL extensions 95 return GLFW_FALSE; 96 } 97 98 static GLFWglproc getProcAddressNSGL(const char* procname) 99 { 100 CFStringRef symbolName = CFStringCreateWithCString(kCFAllocatorDefault, 101 procname, 102 kCFStringEncodingASCII); 103 104 GLFWglproc symbol = CFBundleGetFunctionPointerForName(_glfw.nsgl.framework, 105 symbolName); 106 107 CFRelease(symbolName); 108 109 return symbol; 110 } 111 112 static void destroyContextNSGL(_GLFWwindow* window) 113 { 114 @autoreleasepool { 115 116 [window->context.nsgl.pixelFormat release]; 117 window->context.nsgl.pixelFormat = nil; 118 119 [window->context.nsgl.object release]; 120 window->context.nsgl.object = nil; 121 122 } // autoreleasepool 123 } 124 125 126 ////////////////////////////////////////////////////////////////////////// 127 ////// GLFW internal API ////// 128 ////////////////////////////////////////////////////////////////////////// 129 130 // Initialize OpenGL support 131 // 132 GLFWbool _glfwInitNSGL(void) 133 { 134 if (_glfw.nsgl.framework) 135 return GLFW_TRUE; 136 137 _glfw.nsgl.framework = 138 CFBundleGetBundleWithIdentifier(CFSTR("com.apple.opengl")); 139 if (_glfw.nsgl.framework == NULL) 140 { 141 _glfwInputError(GLFW_API_UNAVAILABLE, 142 "NSGL: Failed to locate OpenGL framework"); 143 return GLFW_FALSE; 144 } 145 146 return GLFW_TRUE; 147 } 148 149 // Terminate OpenGL support 150 // 151 void _glfwTerminateNSGL(void) 152 { 153 } 154 155 // Create the OpenGL context 156 // 157 GLFWbool _glfwCreateContextNSGL(_GLFWwindow* window, 158 const _GLFWctxconfig* ctxconfig, 159 const _GLFWfbconfig* fbconfig) 160 { 161 if (ctxconfig->client == GLFW_OPENGL_ES_API) 162 { 163 _glfwInputError(GLFW_API_UNAVAILABLE, 164 "NSGL: OpenGL ES is not available via NSGL"); 165 return GLFW_FALSE; 166 } 167 168 if (ctxconfig->major > 2) 169 { 170 if (ctxconfig->major == 3 && ctxconfig->minor < 2) 171 { 172 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 173 "NSGL: The targeted version of macOS does not support OpenGL 3.0 or 3.1 but may support 3.2 and above"); 174 return GLFW_FALSE; 175 } 176 } 177 178 if (ctxconfig->major >= 3 && ctxconfig->profile == GLFW_OPENGL_COMPAT_PROFILE) 179 { 180 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 181 "NSGL: The compatibility profile is not available on macOS"); 182 return GLFW_FALSE; 183 } 184 185 // Context robustness modes (GL_KHR_robustness) are not yet supported by 186 // macOS but are not a hard constraint, so ignore and continue 187 188 // Context release behaviors (GL_KHR_context_flush_control) are not yet 189 // supported by macOS but are not a hard constraint, so ignore and continue 190 191 // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not 192 // a hard constraint, so ignore and continue 193 194 // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but 195 // are not a hard constraint, so ignore and continue 196 197 #define ADD_ATTRIB(a) \ 198 { \ 199 assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ 200 attribs[index++] = a; \ 201 } 202 #define SET_ATTRIB(a, v) { ADD_ATTRIB(a); ADD_ATTRIB(v); } 203 204 NSOpenGLPixelFormatAttribute attribs[40]; 205 int index = 0; 206 207 ADD_ATTRIB(NSOpenGLPFAAccelerated); 208 ADD_ATTRIB(NSOpenGLPFAClosestPolicy); 209 210 if (ctxconfig->nsgl.offline) 211 { 212 ADD_ATTRIB(NSOpenGLPFAAllowOfflineRenderers); 213 // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in 214 // Info.plist for unbundled applications 215 // HACK: This assumes that NSOpenGLPixelFormat will remain 216 // a straightforward wrapper of its CGL counterpart 217 ADD_ATTRIB(kCGLPFASupportsAutomaticGraphicsSwitching); 218 } 219 220 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 221 if (ctxconfig->major >= 4) 222 { 223 SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); 224 } 225 else 226 #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ 227 if (ctxconfig->major >= 3) 228 { 229 SET_ATTRIB(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); 230 } 231 232 if (ctxconfig->major <= 2) 233 { 234 if (fbconfig->auxBuffers != GLFW_DONT_CARE) 235 SET_ATTRIB(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); 236 237 if (fbconfig->accumRedBits != GLFW_DONT_CARE && 238 fbconfig->accumGreenBits != GLFW_DONT_CARE && 239 fbconfig->accumBlueBits != GLFW_DONT_CARE && 240 fbconfig->accumAlphaBits != GLFW_DONT_CARE) 241 { 242 const int accumBits = fbconfig->accumRedBits + 243 fbconfig->accumGreenBits + 244 fbconfig->accumBlueBits + 245 fbconfig->accumAlphaBits; 246 247 SET_ATTRIB(NSOpenGLPFAAccumSize, accumBits); 248 } 249 } 250 251 if (fbconfig->redBits != GLFW_DONT_CARE && 252 fbconfig->greenBits != GLFW_DONT_CARE && 253 fbconfig->blueBits != GLFW_DONT_CARE) 254 { 255 int colorBits = fbconfig->redBits + 256 fbconfig->greenBits + 257 fbconfig->blueBits; 258 259 // macOS needs non-zero color size, so set reasonable values 260 if (colorBits == 0) 261 colorBits = 24; 262 else if (colorBits < 15) 263 colorBits = 15; 264 265 SET_ATTRIB(NSOpenGLPFAColorSize, colorBits); 266 } 267 268 if (fbconfig->alphaBits != GLFW_DONT_CARE) 269 SET_ATTRIB(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); 270 271 if (fbconfig->depthBits != GLFW_DONT_CARE) 272 SET_ATTRIB(NSOpenGLPFADepthSize, fbconfig->depthBits); 273 274 if (fbconfig->stencilBits != GLFW_DONT_CARE) 275 SET_ATTRIB(NSOpenGLPFAStencilSize, fbconfig->stencilBits); 276 277 if (fbconfig->stereo) 278 { 279 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 280 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 281 "NSGL: Stereo rendering is deprecated"); 282 return GLFW_FALSE; 283 #else 284 ADD_ATTRIB(NSOpenGLPFAStereo); 285 #endif 286 } 287 288 if (fbconfig->doublebuffer) 289 ADD_ATTRIB(NSOpenGLPFADoubleBuffer); 290 291 if (fbconfig->samples != GLFW_DONT_CARE) 292 { 293 if (fbconfig->samples == 0) 294 { 295 SET_ATTRIB(NSOpenGLPFASampleBuffers, 0); 296 } 297 else 298 { 299 SET_ATTRIB(NSOpenGLPFASampleBuffers, 1); 300 SET_ATTRIB(NSOpenGLPFASamples, fbconfig->samples); 301 } 302 } 303 304 // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB 305 // framebuffer, so there's no need (and no way) to request it 306 307 ADD_ATTRIB(0); 308 309 #undef ADD_ATTRIB 310 #undef SET_ATTRIB 311 312 window->context.nsgl.pixelFormat = 313 [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; 314 if (window->context.nsgl.pixelFormat == nil) 315 { 316 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 317 "NSGL: Failed to find a suitable pixel format"); 318 return GLFW_FALSE; 319 } 320 321 NSOpenGLContext* share = nil; 322 323 if (ctxconfig->share) 324 share = ctxconfig->share->context.nsgl.object; 325 326 window->context.nsgl.object = 327 [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat 328 shareContext:share]; 329 if (window->context.nsgl.object == nil) 330 { 331 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 332 "NSGL: Failed to create OpenGL context"); 333 return GLFW_FALSE; 334 } 335 336 if (fbconfig->transparent) 337 { 338 GLint opaque = 0; 339 [window->context.nsgl.object setValues:&opaque 340 forParameter:NSOpenGLContextParameterSurfaceOpacity]; 341 } 342 343 [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.scaleFramebuffer]; 344 345 [window->context.nsgl.object setView:window->ns.view]; 346 347 window->context.makeCurrent = makeContextCurrentNSGL; 348 window->context.swapBuffers = swapBuffersNSGL; 349 window->context.swapInterval = swapIntervalNSGL; 350 window->context.extensionSupported = extensionSupportedNSGL; 351 window->context.getProcAddress = getProcAddressNSGL; 352 window->context.destroy = destroyContextNSGL; 353 354 return GLFW_TRUE; 355 } 356 357 358 ////////////////////////////////////////////////////////////////////////// 359 ////// GLFW native API ////// 360 ////////////////////////////////////////////////////////////////////////// 361 362 GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) 363 { 364 _GLFWwindow* window = (_GLFWwindow*) handle; 365 _GLFW_REQUIRE_INIT_OR_RETURN(nil); 366 367 if (_glfw.platform.platformID != GLFW_PLATFORM_COCOA) 368 { 369 _glfwInputError(GLFW_PLATFORM_UNAVAILABLE, 370 "NSGL: Platform not initialized"); 371 return nil; 372 } 373 374 if (window->context.source != GLFW_NATIVE_CONTEXT_API) 375 { 376 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); 377 return nil; 378 } 379 380 return window->context.nsgl.object; 381 } 382 383 #endif // _GLFW_COCOA 384