nsgl_context.m (11597B)
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 // 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.object occlusionState] & NSWindowOcclusionStateVisible)) 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 = (_GLFWwindow*)_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 = (GLFWglproc)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 179 // Context robustness modes (GL_KHR_robustness) are not yet supported by 180 // macOS but are not a hard constraint, so ignore and continue 181 182 // Context release behaviors (GL_KHR_context_flush_control) are not yet 183 // supported by macOS but are not a hard constraint, so ignore and continue 184 185 // Debug contexts (GL_KHR_debug) are not yet supported by macOS but are not 186 // a hard constraint, so ignore and continue 187 188 // No-error contexts (GL_KHR_no_error) are not yet supported by macOS but 189 // are not a hard constraint, so ignore and continue 190 191 #define addAttrib(a) \ 192 { \ 193 assert((size_t) index < sizeof(attribs) / sizeof(attribs[0])); \ 194 attribs[index++] = a; \ 195 } 196 #define setAttrib(a, v) { addAttrib(a); addAttrib(v); } 197 198 NSOpenGLPixelFormatAttribute attribs[40]; 199 int index = 0; 200 201 addAttrib(NSOpenGLPFAAccelerated); 202 addAttrib(NSOpenGLPFAClosestPolicy); 203 204 if (ctxconfig->nsgl.offline) 205 { 206 addAttrib(NSOpenGLPFAAllowOfflineRenderers); 207 // NOTE: This replaces the NSSupportsAutomaticGraphicsSwitching key in 208 // Info.plist for unbundled applications 209 // HACK: This assumes that NSOpenGLPixelFormat will remain 210 // a straightforward wrapper of its CGL counterpart 211 addAttrib(kCGLPFASupportsAutomaticGraphicsSwitching); 212 } 213 214 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101000 215 if (ctxconfig->major >= 4) 216 { 217 setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core); 218 } 219 else 220 #endif /*MAC_OS_X_VERSION_MAX_ALLOWED*/ 221 if (ctxconfig->major >= 3) 222 { 223 setAttrib(NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion3_2Core); 224 } 225 226 if (ctxconfig->major <= 2) 227 { 228 if (fbconfig->auxBuffers != GLFW_DONT_CARE) 229 setAttrib(NSOpenGLPFAAuxBuffers, fbconfig->auxBuffers); 230 231 if (fbconfig->accumRedBits != GLFW_DONT_CARE && 232 fbconfig->accumGreenBits != GLFW_DONT_CARE && 233 fbconfig->accumBlueBits != GLFW_DONT_CARE && 234 fbconfig->accumAlphaBits != GLFW_DONT_CARE) 235 { 236 const int accumBits = fbconfig->accumRedBits + 237 fbconfig->accumGreenBits + 238 fbconfig->accumBlueBits + 239 fbconfig->accumAlphaBits; 240 241 setAttrib(NSOpenGLPFAAccumSize, accumBits); 242 } 243 } 244 245 if (fbconfig->redBits != GLFW_DONT_CARE && 246 fbconfig->greenBits != GLFW_DONT_CARE && 247 fbconfig->blueBits != GLFW_DONT_CARE) 248 { 249 int colorBits = fbconfig->redBits + 250 fbconfig->greenBits + 251 fbconfig->blueBits; 252 253 // macOS needs non-zero color size, so set reasonable values 254 if (colorBits == 0) 255 colorBits = 24; 256 else if (colorBits < 15) 257 colorBits = 15; 258 259 setAttrib(NSOpenGLPFAColorSize, colorBits); 260 } 261 262 if (fbconfig->alphaBits != GLFW_DONT_CARE) 263 setAttrib(NSOpenGLPFAAlphaSize, fbconfig->alphaBits); 264 265 if (fbconfig->depthBits != GLFW_DONT_CARE) 266 setAttrib(NSOpenGLPFADepthSize, fbconfig->depthBits); 267 268 if (fbconfig->stencilBits != GLFW_DONT_CARE) 269 setAttrib(NSOpenGLPFAStencilSize, fbconfig->stencilBits); 270 271 if (fbconfig->stereo) 272 { 273 #if MAC_OS_X_VERSION_MAX_ALLOWED >= 101200 274 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 275 "NSGL: Stereo rendering is deprecated"); 276 return GLFW_FALSE; 277 #else 278 addAttrib(NSOpenGLPFAStereo); 279 #endif 280 } 281 282 if (fbconfig->doublebuffer) 283 addAttrib(NSOpenGLPFADoubleBuffer); 284 285 if (fbconfig->samples != GLFW_DONT_CARE) 286 { 287 if (fbconfig->samples == 0) 288 { 289 setAttrib(NSOpenGLPFASampleBuffers, 0); 290 } 291 else 292 { 293 setAttrib(NSOpenGLPFASampleBuffers, 1); 294 setAttrib(NSOpenGLPFASamples, fbconfig->samples); 295 } 296 } 297 298 // NOTE: All NSOpenGLPixelFormats on the relevant cards support sRGB 299 // framebuffer, so there's no need (and no way) to request it 300 301 addAttrib(0); 302 303 #undef addAttrib 304 #undef setAttrib 305 306 window->context.nsgl.pixelFormat = 307 [[NSOpenGLPixelFormat alloc] initWithAttributes:attribs]; 308 if (window->context.nsgl.pixelFormat == nil) 309 { 310 _glfwInputError(GLFW_FORMAT_UNAVAILABLE, 311 "NSGL: Failed to find a suitable pixel format"); 312 return GLFW_FALSE; 313 } 314 315 NSOpenGLContext* share = nil; 316 317 if (ctxconfig->share) 318 share = ctxconfig->share->context.nsgl.object; 319 320 window->context.nsgl.object = 321 [[NSOpenGLContext alloc] initWithFormat:window->context.nsgl.pixelFormat 322 shareContext:share]; 323 if (window->context.nsgl.object == nil) 324 { 325 _glfwInputError(GLFW_VERSION_UNAVAILABLE, 326 "NSGL: Failed to create OpenGL context"); 327 return GLFW_FALSE; 328 } 329 330 if (fbconfig->transparent) 331 { 332 GLint opaque = 0; 333 [window->context.nsgl.object setValues:&opaque 334 forParameter:NSOpenGLContextParameterSurfaceOpacity]; 335 } 336 337 [window->ns.view setWantsBestResolutionOpenGLSurface:window->ns.retina]; 338 339 [window->context.nsgl.object setView:window->ns.view]; 340 341 window->context.makeCurrent = makeContextCurrentNSGL; 342 window->context.swapBuffers = swapBuffersNSGL; 343 window->context.swapInterval = swapIntervalNSGL; 344 window->context.extensionSupported = extensionSupportedNSGL; 345 window->context.getProcAddress = getProcAddressNSGL; 346 window->context.destroy = destroyContextNSGL; 347 348 return GLFW_TRUE; 349 } 350 351 352 ////////////////////////////////////////////////////////////////////////// 353 ////// GLFW native API ////// 354 ////////////////////////////////////////////////////////////////////////// 355 356 GLFWAPI id glfwGetNSGLContext(GLFWwindow* handle) 357 { 358 _GLFWwindow* window = (_GLFWwindow*) handle; 359 _GLFW_REQUIRE_INIT_OR_RETURN(nil); 360 361 if (window->context.client == GLFW_NO_API) 362 { 363 _glfwInputError(GLFW_NO_WINDOW_CONTEXT, NULL); 364 return nil; 365 } 366 367 return window->context.nsgl.object; 368 } 369