Vuo  2.4.0
VuoGlContext.cc
Go to the documentation of this file.
1
10#include <vector>
11#include <algorithm>
12#include <bitset>
13#include <map>
14using namespace std;
15
16#include "module.h"
17#include "VuoGlContext.h"
18#include "VuoCglPixelFormat.h"
19#include "VuoScreenCommon.h"
20
21#include <OpenGL/CGLMacro.h>
22#include <CoreFoundation/CoreFoundation.h>
23#include <CoreGraphics/CoreGraphics.h>
24
25#include <dispatch/dispatch.h>
26#include <dlfcn.h>
27#include <mach-o/dyld.h>
28#include <objc/objc-runtime.h>
29#include <pthread.h>
30
31
32static CGLContextObj VuoGlContext_create(CGLContextObj rootContext);
33dispatch_semaphore_t VuoGlContext_poolSemaphore;
34static CGLContextObj VuoGlContext_root = NULL;
35vector<CGLContextObj> VuoGlContext_allSharedContexts;
37
38static dispatch_once_t VuoGlContextPoolCreated = 0;
39static pthread_key_t VuoGlContextPerformKey;
40
41static bool VuoGlContext_infoLogging = true;
42
48{
50}
51
55static void VuoGlContext_renderers(void)
56{
58 return;
59
61 unsigned long screenCount = VuoListGetCount_VuoScreen(screensList);
62 VuoScreen *screens = VuoListGetData_VuoScreen(screensList);
63 VuoLocal(screensList);
64
65#pragma clang diagnostic push
66#pragma clang diagnostic ignored "-Wdeprecated-declarations"
67 // https://developer.apple.com/library/mac/qa/qa1168/_index.html says:
68 // "If you are looking for the VRAM sizes of all the renderers on your system […]
69 // you may specify a -1/0xFFFFFFFF display mask in the CGLQueryRendererInfo() function.
70 CGLRendererInfoObj ri;
71 GLint rendererCount = 0;
72 CGLQueryRendererInfo(-1, &ri, &rendererCount);
73 for (int i = 0; i < rendererCount; ++i)
74 {
75 GLint rendererID;
76 CGLDescribeRenderer(ri, i, kCGLRPRendererID, &rendererID);
77 VUserLog("Renderer %d: %s", i, VuoCglRenderer_getText(rendererID));
78
79 GLint online;
80 if (CGLDescribeRenderer(ri, i, kCGLRPOnline, &online) == kCGLNoError)
81 VUserLog(" Online : %s", online ? "yes" : "no");
82
83 GLint accelerated;
84 if (CGLDescribeRenderer(ri, i, kCGLRPAccelerated, &accelerated) == kCGLNoError)
85 VUserLog(" Accelerated : %s", accelerated ? "yes" : "no");
86
87 GLint videoMegabytes = 0;
88 if (CGLDescribeRenderer(ri, i, kCGLRPVideoMemoryMegabytes, &videoMegabytes) == kCGLNoError
89 && videoMegabytes)
90 VUserLog(" Video memory : %d MB", videoMegabytes);
91
92 GLint textureMegabytes = 0;
93 if (CGLDescribeRenderer(ri, i, kCGLRPTextureMemoryMegabytes, &textureMegabytes) == kCGLNoError
94 && textureMegabytes)
95 VUserLog(" Texture memory : %d MB", textureMegabytes);
96
97 GLint displayMask;
98 if (CGLDescribeRenderer(ri, i, kCGLRPDisplayMask, &displayMask) == kCGLNoError)
99 {
100 VUserLog(" Display mask : %s (0x%x)%s",
101 std::bitset<32>(displayMask).to_string().c_str(),
102 displayMask, (displayMask & 0xff) == 0xff ? " (any)" : "");
103 if ((displayMask & 0xff) != 0xff)
104 for (unsigned long i = 0; i < screenCount; ++i)
105 if (displayMask & screens[i].displayMask)
106 VUserLog(" %s %s",
107 std::bitset<32>(screens[i].displayMask).to_string().c_str(),
108 screens[i].name);
109 }
110
111 GLint glVersion = 0;
112 if (CGLDescribeRenderer(ri, i, /*kCGLRPMajorGLVersion*/ (CGLRendererProperty)133, &glVersion) == kCGLNoError)
113 VUserLog(" OpenGL version : %d", glVersion);
114
115 CGLPixelFormatObj pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(false, false, displayMask);
116 if (pf != (CGLPixelFormatObj)-1)
117 {
118 CGLContextObj cgl_ctx;
119 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
120 if (error != kCGLNoError)
121 VUserLog(" Error: %s", CGLErrorString(error));
122 else
123 {
124 GLint maxTextureSize;
125 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
126 VUserLog(" OpenGL 2 : %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
127 CGLDestroyContext(cgl_ctx);
128 }
129 }
130 else
131 VUserLog(" (Can't create an OpenGL 2 context on this renderer.)");
132
133 pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(false, true, displayMask);
134 if (pf != (CGLPixelFormatObj)-1)
135 {
136 CGLContextObj cgl_ctx;
137 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
138 if (error != kCGLNoError)
139 VUserLog(" Error: %s", CGLErrorString(error));
140 else
141 {
142 GLint maxTextureSize;
143 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
144 VUserLog(" OpenGL Core Profile: %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
145 CGLDestroyContext(cgl_ctx);
146 }
147 }
148 else
149 VUserLog(" (Can't create an OpenGL Core Profile context on this renderer.)");
150 }
151 CGLDestroyRendererInfo(ri);
152#pragma clang diagnostic pop
153
154
155 VUserLog("OpenGL driver binaries:");
156 const char *gldriver = "GLDriver";
157 size_t gldriverLen = strlen(gldriver);
158 uint32_t imageCount = _dyld_image_count();
159 for (uint32_t i = 0; i < imageCount; ++i)
160 {
161 const char *dylibPath = _dyld_get_image_name(i);
162 size_t len = strlen(dylibPath);
163 // If the image name ends with "GLDriver"…
164 if (strcmp(dylibPath + len - gldriverLen, gldriver) == 0)
165 {
166 // Trim off the path and the common "GLDriver" suffix.
167 char *z = strdup(strrchr(dylibPath, '/')+1);
168 z[strlen(z)-gldriverLen] = 0;
169 VUserLog(" %s", z);
170 free(z);
171 }
172 }
173
174 typedef void *(*mtlCopyAllDevicesType)(void);
175 mtlCopyAllDevicesType mtlCopyAllDevices = (mtlCopyAllDevicesType)dlsym(RTLD_DEFAULT, "MTLCopyAllDevices");
176 if (mtlCopyAllDevices)
177 {
178 CFArrayRef mtlDevices = (CFArrayRef)mtlCopyAllDevices();
179 int mtlDeviceCount = CFArrayGetCount(mtlDevices);
180 if (mtlDeviceCount)
181 VUserLog("Metal devices:");
182 for (int i = 0; i < mtlDeviceCount; ++i)
183 {
184 id dev = (id)CFArrayGetValueAtIndex(mtlDevices, i);
185 id devName = ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("name"));
186 const char *devNameZ = ((char * (*)(id, SEL))objc_msgSend)(devName, sel_getUid("UTF8String"));
187 VUserLog(" %s:", devNameZ);
188 if (class_respondsToSelector(object_getClass(dev), sel_getUid("registryID")))
189 VUserLog(" ID : %p", ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("registryID")));
190 if (class_respondsToSelector(object_getClass(dev), sel_getUid("peerGroupID")))
191 VUserLog(" Peer group : %llu", ((uint64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("peerGroupID")));
192
193 if (class_respondsToSelector(object_getClass(dev), sel_getUid("recommendedMaxWorkingSetSize")))
194 VUserLog(" Recommended max working-set size : %lld MiB", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("recommendedMaxWorkingSetSize"))/1048576);
195 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxBufferLength")))
196 VUserLog(" Max buffer length : %lld MiB", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxBufferLength"))/1048576);
197 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxThreadgroupMemoryLength")))
198 VUserLog(" Threadgroup memory : %lld B", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxThreadgroupMemoryLength")));
199 if (class_respondsToSelector(object_getClass(dev), sel_getUid("sparseTileSizeInBytes")))
200 VUserLog(" Sparse tile size : %llu B", ((uint64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("sparseTileSizeInBytes")));
201
202 VUserLog(" Low-power : %s", ((bool (*)(id, SEL))objc_msgSend) ? "yes" : "no");
203 if (class_respondsToSelector(object_getClass(dev), sel_getUid("isRemovable")))
204 VUserLog(" Removable : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("isRemovable")) ? "yes" : "no");
205 VUserLog(" Headless : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("isHeadless")) ? "yes" : "no");
206
207 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10005))
208 VUserLog(" Feature set : GPU Family 2 v1");
209 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10004))
210 VUserLog(" Feature set : GPU Family 1 v4");
211 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10003))
212 VUserLog(" Feature set : GPU Family 1 v3");
213 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10001))
214 VUserLog(" Feature set : GPU Family 1 v2");
215 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10000))
216 VUserLog(" Feature set : GPU Family 1 v1");
217 else
218 VUserLog(" Feature set : (unknown)");
219
220 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsFamily:")))
221 {
222 // "A higher GPU version is always a superset of an earlier version in the same GPU family."
223 // https://developer.apple.com/documentation/metal/mtldevice/detecting_gpu_features_and_metal_software_versions
224 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1008))
225 VUserLog(" Family : Apple 8");
226 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1007))
227 VUserLog(" Family : Apple 7");
228 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1006))
229 VUserLog(" Family : Apple 6");
230 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1005))
231 VUserLog(" Family : Apple 5");
232 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1004))
233 VUserLog(" Family : Apple 4");
234 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1003))
235 VUserLog(" Family : Apple 3");
236 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1002))
237 VUserLog(" Family : Apple 2");
238 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1001))
239 VUserLog(" Family : Apple 1");
240
241 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 2002))
242 VUserLog(" Family : Mac 2");
243 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 2001))
244 VUserLog(" Family : Mac 1");
245
246 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 3003))
247 VUserLog(" Family : Common 3");
248 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 3002))
249 VUserLog(" Family : Common 2");
250 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 3001))
251 VUserLog(" Family : Common 1");
252 }
253
254 if (class_respondsToSelector(object_getClass(dev), sel_getUid("readWriteTextureSupport")))
255 VUserLog(" Read-write texture support tier : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("readWriteTextureSupport")));
256 if (class_respondsToSelector(object_getClass(dev), sel_getUid("argumentBuffersSupport")))
257 VUserLog(" Argument buffer support tier : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("argumentBuffersSupport")));
258 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxArgumentBufferSamplerCount")))
259 VUserLog(" Max argument buffers : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxArgumentBufferSamplerCount")));
260 if (class_respondsToSelector(object_getClass(dev), sel_getUid("areProgrammableSamplePositionsSupported")))
261 VUserLog(" Programmable sample position support: %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("areProgrammableSamplePositionsSupported")) ? "yes" : "no");
262 if (class_respondsToSelector(object_getClass(dev), sel_getUid("areRasterOrderGroupsSupported")))
263 VUserLog(" Raster order group support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("areRasterOrderGroupsSupported")) ? "yes" : "no");
264
265 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsDynamicLibraries")))
266 VUserLog(" Dynamic library support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsDynamicLibraries")) ? "yes" : "no");
267 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsFunctionPointers")))
268 VUserLog(" Function pointer support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsFunctionPointers")) ? "yes" : "no");
269 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsRaytracing")))
270 VUserLog(" Raytracing support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsRaytracing")) ? "yes" : "no");
271 }
272 CFRelease(mtlDevices);
273 }
274}
275
279void VuoGlContext_reconfig(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo)
280{
281 if (!(flags & kCGDisplaySetModeFlag))
282 return;
283
284 VuoList_VuoScreen screensList = VuoScreen_getList();
285 unsigned long screenCount = VuoListGetCount_VuoScreen(screensList);
286 VuoScreen *screens = VuoListGetData_VuoScreen(screensList);
287 VuoLocal(screensList);
288
289 for (unsigned long i = 0; i < screenCount; ++i)
290 if (screens[i].id == display)
291 VUserLog("Display reconfigured: %s", screens[i].name);
292}
293
297 static void VuoGlContext_init()
298 {
299 dispatch_once(&VuoGlContextPoolCreated, ^{
300 VuoGlContext_poolSemaphore = dispatch_semaphore_create(1);
301
304
305 int ret = pthread_key_create(&VuoGlContextPerformKey, NULL);
306 if (ret)
307 VUserLog("Couldn't create the key for storing the GL Context state: %s", strerror(errno));
308 });
309 }
310
318 {
320
321 CGLContextObj context;
322
323 dispatch_semaphore_wait(VuoGlContext_poolSemaphore, DISPATCH_TIME_FOREVER);
324 {
325// VL();
326// VuoLog_backtrace();
327
329 {
332// VLog("Found existing context %p.", context);
333 }
334 else
335 {
337 if (!context)
338 {
339 VUserLog("Error: Couldn't create a context.");
340 return NULL;
341 }
342 VuoGlContext_allSharedContexts.push_back(context);
343// VLog("Created context %p.", context);
344 }
345 }
346 dispatch_semaphore_signal(VuoGlContext_poolSemaphore);
347
348 return context;
349 }
350
356 void VuoGlContext_disuseF(VuoGlContext glContext, const char *file, const unsigned int linenumber, const char *func)
357 {
358 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
359// VLog("%p", context);
360
361#if 0
362 VGL();
363
364 // Check whether there are any stale bindings (to help identify leaks).
365 // (Some checks are commented because they aren't available in OpenGL 2.1.)
366 VuoGlCheckBinding(GL_ARRAY_BUFFER_BINDING);
367// VuoGlCheckBinding(GL_ATOMIC_COUNTER_BUFFER_BINDING);
368// VuoGlCheckBinding(GL_COPY_READ_BUFFER_BINDING);
369// VuoGlCheckBinding(GL_COPY_WRITE_BUFFER_BINDING);
370// VuoGlCheckBinding(GL_DRAW_INDIRECT_BUFFER_BINDING);
371// VuoGlCheckBinding(GL_DISPATCH_INDIRECT_BUFFER_BINDING);
372 VuoGlCheckBinding(GL_DRAW_FRAMEBUFFER_BINDING);
373 VuoGlCheckBinding(GL_ELEMENT_ARRAY_BUFFER_BINDING);
374 VuoGlCheckBinding(GL_FRAMEBUFFER_BINDING);
375 VuoGlCheckBinding(GL_PIXEL_PACK_BUFFER_BINDING);
376 VuoGlCheckBinding(GL_PIXEL_UNPACK_BUFFER_BINDING);
377// VuoGlCheckBinding(GL_PROGRAM_PIPELINE_BINDING);
378 VuoGlCheckBinding(GL_READ_FRAMEBUFFER_BINDING);
379 VuoGlCheckBinding(GL_RENDERBUFFER_BINDING);
380// VuoGlCheckBinding(GL_SAMPLER_BINDING);
381// VuoGlCheckBinding(GL_SHADER_STORAGE_BUFFER_BINDING);
382
383 GLint textureUnits;
384 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &textureUnits);
385 for (GLint i=0; i<textureUnits; ++i)
386 {
387 glActiveTexture(GL_TEXTURE0+i);
388 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_1D,i);
389 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_1D_ARRAY_EXT,i);
390 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D,i);
391 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_ARRAY_EXT,i);
392// VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_MULTISAMPLE,i);
393// VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,i);
394 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_3D,i);
395// VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_BUFFER,i);
396 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_CUBE_MAP,i);
397 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_RECTANGLE_ARB,i);
398 }
399
400 VuoGlCheckBinding(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT);
401 VuoGlCheckBinding(GL_UNIFORM_BUFFER_BINDING_EXT);
402 VuoGlCheckBinding(GL_VERTEX_ARRAY_BINDING_APPLE);
403#endif
404
405 dispatch_semaphore_wait(VuoGlContext_poolSemaphore, DISPATCH_TIME_FOREVER);
406 {
408 VuoGlContext_avaialbleSharedContexts.push_back(cgl_ctx);
409 else
410 VUserLog("Error: Disued context %p, which isn't in the global share pool. I'm not going to muddy the waters.", cgl_ctx);
411 }
412 dispatch_semaphore_signal(VuoGlContext_poolSemaphore);
413 }
414
418 #define VuoGlContext_checkGL(cap, value) \
419 do { \
420 if (glIsEnabled(cap) != value) \
421 { \
422 VUserLog("Warning: Caller incorrectly left %s %s", #cap, value ? "disabled" : "enabled"); \
423 VuoLog_backtrace(); \
424 } \
425 } while (0)
426
430 #define VuoGlContext_checkGLInt(key, value) \
431 do { \
432 GLint actualValue; \
433 glGetIntegerv(key, &actualValue); \
434 if (actualValue != value) \
435 { \
436 VUserLog("Warning: Caller incorrectly left %s set to something other than %s", #key, #value); \
437 VuoLog_backtrace(); \
438 } \
439 } while (0)
440
441
442
455 void VuoGlContext_perform(void (^function)(CGLContextObj cgl_ctx))
456 {
457#pragma clang diagnostic push
458#pragma clang diagnostic ignored "-Wdeprecated-declarations"
459 if (!function)
460 return;
461
463
464 bool alreadyLockedOnThisThread = (bool)pthread_getspecific(VuoGlContextPerformKey);
465
466 if (!alreadyLockedOnThisThread)
467 {
468 pthread_setspecific(VuoGlContextPerformKey, (void *)true);
469 CGLLockContext(VuoGlContext_root);
470 }
471
472 function(VuoGlContext_root);
473
474 // Ensure that `function` restored the standard OpenGL state.
475 if (!alreadyLockedOnThisThread && VuoIsDebugEnabled())
476 {
477 CGLContextObj cgl_ctx = VuoGlContext_root;
478
479 VGL();
480
481 VuoGlContext_checkGLInt(GL_DEPTH_WRITEMASK, true);
482 VuoGlContext_checkGL (GL_DEPTH_TEST, false);
483
484 VuoGlContext_checkGL (GL_CULL_FACE, true);
485 VuoGlContext_checkGLInt(GL_CULL_FACE_MODE, GL_BACK);
486
487 VuoGlContext_checkGL (GL_BLEND, true);
488
489 VuoGlContext_checkGLInt(GL_BLEND_SRC_RGB, GL_ONE);
490 VuoGlContext_checkGLInt(GL_BLEND_DST_RGB, GL_ONE_MINUS_SRC_ALPHA);
491 VuoGlContext_checkGLInt(GL_BLEND_SRC_ALPHA, GL_ONE);
492 VuoGlContext_checkGLInt(GL_BLEND_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
493
494 VuoGlContext_checkGLInt(GL_BLEND_EQUATION_RGB, GL_FUNC_ADD);
495 VuoGlContext_checkGLInt(GL_BLEND_EQUATION_ALPHA, GL_FUNC_ADD);
496
497 VuoGlContext_checkGL(GL_SAMPLE_ALPHA_TO_COVERAGE, false);
498 VuoGlContext_checkGL(GL_SAMPLE_ALPHA_TO_ONE, false);
499
501 }
502
503 if (!alreadyLockedOnThisThread)
504 {
505 CGLUnlockContext(VuoGlContext_root);
506 pthread_setspecific(VuoGlContextPerformKey, (void *)false);
507 }
508#pragma clang diagnostic pop
509 }
510
514 static CGLContextObj VuoGlContext_create(CGLContextObj rootContext)
515 {
516#pragma clang diagnostic push
517#pragma clang diagnostic ignored "-Wdeprecated-declarations"
518 static dispatch_once_t info = 0;
519 dispatch_once(&info, ^{
521 CGDisplayRegisterReconfigurationCallback(VuoGlContext_reconfig, NULL);
522 });
523
524 CGLPixelFormatObj pf;
525 bool shouldDestroyPixelFormat = false;
526 GLint displayMask;
527 if (rootContext)
528 pf = CGLGetPixelFormat(rootContext);
529 else
530 {
531 Boolean overridden = false;
532 displayMask = (int)CFPreferencesGetAppIntegerValue(CFSTR("displayMask"), CFSTR("org.vuo.Editor"), &overridden);
533 if (!overridden)
534 {
535 // Maybe the preference is a hex string…
536 auto displayMaskString = (CFStringRef)CFPreferencesCopyAppValue(CFSTR("displayMask"), CFSTR("org.vuo.Editor"));
537 if (displayMaskString)
538 {
539 VuoText t = VuoText_makeFromCFString(displayMaskString);
540 CFRelease(displayMaskString);
541 VuoLocal(t);
542 overridden = sscanf(t, "0x%x", &displayMask) == 1;
543 }
544 if (!overridden)
545 // Still no preference, so let macOS automatically choose what it thinks is the best GPU.
546 displayMask = -1;
547 }
548
549 pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(true, false, displayMask);
550 shouldDestroyPixelFormat = true;
551 }
552
553 CGLContextObj context;
554 {
555 CGLError error = CGLCreateContext(pf, rootContext, &context);
556 if (shouldDestroyPixelFormat)
557 CGLDestroyPixelFormat(pf);
558 if (error != kCGLNoError)
559 {
560 VUserLog("Error: %s", CGLErrorString(error));
561 return NULL;
562 }
563 }
564
566 {
567 GLint rendererID;
568 CGLGetParameter(context, kCGLCPCurrentRendererID, &rendererID);
569 char *sharingText = rootContext ? VuoText_format(" (shared with %p)", rootContext) : strdup(" (not shared)");
570 char *displayMaskText = rootContext ? nullptr : (displayMask == -1 ? strdup(" (macOS default)") : VuoText_format(" (selected using displayMask %s)", std::bitset<32>(displayMask).to_string().c_str()));
571 VUserLog("Created OpenGL context %p%s on %s%s", context, sharingText, VuoCglRenderer_getText(rendererID), displayMaskText);
572 free(sharingText);
573 free(displayMaskText);
574 }
575
576 // https://developer.apple.com/library/content/technotes/tn2085/_index.html
577 // But it doesn't seem to actually improve performance any on the various workloads I tried.
578// CGLEnable(context, kCGLCEMPEngine);
579
580 // Set the context's default state to commonly-used values,
581 // to hopefully minimize subsequent state changes.
582 {
583 CGLContextObj cgl_ctx = context;
584 glDepthMask(true);
585 glDisable(GL_DEPTH_TEST);
586 glEnable(GL_CULL_FACE);
587 glEnable(GL_BLEND);
588 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
589 glBlendEquation(GL_FUNC_ADD);
590 glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
591 glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -.5);
592 }
593
594 return context;
595#pragma clang diagnostic pop
596 }
597
609{
611 {
612 VUserLog("Error: Called after VuoGlContextPool was initialized. Ignoring the new rootContext.");
613 return;
614 }
615
616 VUserLog("Setting global root context to %p", rootContext);
617 VuoGlContext_root = VuoGlContext_create((CGLContextObj)rootContext);
618}
619
624#define VuoGlCheckBinding(pname) \
625{ \
626 GLint value; \
627 glGetIntegerv(pname, &value); \
628 if (value) \
629 { \
630 VuoLog(VuoLog_moduleName, file, linenumber, func, #pname " (value %d) was still active when the context was disused. (This may result in leaks.)", value); \
631 VuoLog_backtrace(); \
632 } \
633}
634
639#define VuoGlCheckTextureBinding(pname, unit) \
640{ \
641 GLint value; \
642 glGetIntegerv(pname, &value); \
643 if (value) \
644 { \
645 VuoLog(VuoLog_moduleName, file, linenumber, func, #pname " (texture %d on unit %d) was still active when the context was disused. (This may result in leaks.)", value, unit); \
646 VuoLog_backtrace(); \
647 } \
648}
649
656{
657 static GLint supportedSamples = 0;
658 static dispatch_once_t multisamplingCheck = 0;
659 dispatch_once(&multisamplingCheck, ^{
660 GLint rendererID;
661#pragma clang diagnostic push
662#pragma clang diagnostic ignored "-Wdeprecated-declarations"
663 CGLGetParameter((CGLContextObj)context, kCGLCPCurrentRendererID, &rendererID);
664#pragma clang diagnostic pop
665 rendererID &= kCGLRendererIDMatchingMask;
666
667 CGLContextObj cgl_ctx = (CGLContextObj)context;
668 const char *renderer = (const char *)glGetString(GL_RENDERER);
669
670 if (rendererID == kCGLRendererIntelHD4000ID // https://b33p.net/kosada/node/8225#comment-31324
671 || rendererID == /*kCGLRendererIntelHD5000ID*/ 0x00024500 // https://b33p.net/kosada/node/10595
672 || strcmp(renderer, "NVIDIA GeForce 320M OpenGL Engine") == 0) // https://b33p.net/kosada/node/13477
673 supportedSamples = 0;
674 else
675 {
676 glGetIntegerv(GL_MAX_SAMPLES, &supportedSamples);
677 if (supportedSamples == 1)
678 supportedSamples = 0;
679 }
680 });
681 return supportedSamples;
682}
683
697void *VuoGlContext_makePlatformPixelFormat(bool hasDepthBuffer, bool openGL32Core, GLint displayMask)
698{
699#pragma clang diagnostic push
700#pragma clang diagnostic ignored "-Wdeprecated-declarations"
701 // Check whether it's OK to use multisampling on this GPU.
702 static dispatch_once_t multisamplingCheck = 0;
703 static int multisample = 0;
704 dispatch_once(&multisamplingCheck, ^{
705 // Create a temporary context so we can get the GPU renderer string.
706 CGLContextObj cgl_ctx;
707 {
708 CGLPixelFormatObj pf;
709 {
710 CGLPixelFormatAttribute pfa[14] = {
711 kCGLPFAAccelerated,
712 kCGLPFAAllowOfflineRenderers,
713// kCGLPFANoRecovery,
714// kCGLPFADoubleBuffer,
715 kCGLPFABackingVolatile,
716 kCGLPFAColorSize, (CGLPixelFormatAttribute) 24,
717 kCGLPFADepthSize, (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0),
718 (CGLPixelFormatAttribute) 0
719 };
720 GLint npix;
721 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
722 if (error != kCGLNoError)
723 {
724 VUserLog("Error: %s", CGLErrorString(error));
725 return;
726 }
727 }
728
729 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
730 CGLDestroyPixelFormat(pf);
731 if (error != kCGLNoError)
732 {
733 VUserLog("Error: %s", CGLErrorString(error));
734 return;
735 }
736 }
737
738
739 // If the user set the `multisample` preference, use it.
740 Boolean overridden = false;
741 multisample = (int)CFPreferencesGetAppIntegerValue(CFSTR("multisample"), CFSTR("org.vuo.Editor"), &overridden);
742
743 if (!overridden)
744 {
745 // …otherwise enable 4x multisampling (unless there's a known problem with this GPU model).
746
747 int supportedSamples = VuoGlContext_getMaximumSupportedMultisampling(cgl_ctx);
748 multisample = MIN(4, supportedSamples);
749 }
750
751 CGLDestroyContext(cgl_ctx);
752 });
753
754
755 CGLPixelFormatAttribute pfa[18];
756 int pfaIndex = 0;
757
758 // If requesting a specific display, don't require acceleration.
759 if (displayMask == -1)
760 pfa[pfaIndex++] = kCGLPFAAccelerated;
761
762 pfa[pfaIndex++] = kCGLPFAAllowOfflineRenderers;
763// pfa[pfaIndex++] = kCGLPFANoRecovery;
764
765 // https://b33p.net/kosada/node/12525
766// pfa[pfaIndex++] = kCGLPFADoubleBuffer;
767 pfa[pfaIndex++] = kCGLPFABackingVolatile;
768
769 pfa[pfaIndex++] = kCGLPFAColorSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 24;
770 pfa[pfaIndex++] = kCGLPFADepthSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0);
771 if (openGL32Core)
772 {
773 pfa[pfaIndex++] = kCGLPFAOpenGLProfile; pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core;
774 }
775
776 if (multisample)
777 {
778 pfa[pfaIndex++] = kCGLPFAMultisample;
779 pfa[pfaIndex++] = kCGLPFASampleBuffers; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 1;
780 pfa[pfaIndex++] = kCGLPFASamples; pfa[pfaIndex++] = (CGLPixelFormatAttribute) multisample;
781 }
782
783 if (displayMask >= 0)
784 {
785 pfa[pfaIndex++] = kCGLPFADisplayMask;
786 pfa[pfaIndex++] = (CGLPixelFormatAttribute) displayMask;
787 }
788
789 // software renderer
790 if ((displayMask & 0xff) == 0xff && displayMask != -1)
791 {
792 pfa[pfaIndex++] = kCGLPFARendererID;
793 pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLRendererGenericFloatID;
794 }
795
796 pfa[pfaIndex] = (CGLPixelFormatAttribute) 0;
797
798 CGLPixelFormatObj pf;
799 GLint npix;
800 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
801 if (error == kCGLBadDisplay)
802 return (CGLPixelFormatObj)-1;
803 else if (error != kCGLNoError)
804 {
805 VUserLog("Error: %s", CGLErrorString(error));
806 return NULL;
807 }
808
809 return (void *)pf;
810#pragma clang diagnostic pop
811}
812
818{
819 CGLContextObj cgl_ctx = (CGLContextObj)context;
820
821 // GL_VERSION is something like:
822 // - Core Profile: `4.1 NVIDIA-10.17.5 355.10.05.45f01`
823 // - Core Profile: `4.1 ATI-2.4.10`
824 // - Compatibility Profile: `2.1 NVIDIA-10.17.5 355.10.05.45f01`
825 // - Compatibility Profile: `2.1 ATI-2.4.10`
826 const unsigned char *contextVersion = glGetString(GL_VERSION);
827 return contextVersion[0] == '3'
828 || contextVersion[0] == '4';
829}
830
831
832void _VGL_describe(GLenum error, CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func);
833
837void _VGL(CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func)
838{
839#pragma clang diagnostic push
840#pragma clang diagnostic ignored "-Wdeprecated-declarations"
841 GLint vertexOnGPU, fragmentOnGPU;
842 CGLGetParameter(cgl_ctx, kCGLCPGPUVertexProcessing, &vertexOnGPU);
843 if (!vertexOnGPU)
844 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL warning: Falling back to software renderer for vertex shader. This will slow things down.");
845 CGLGetParameter(cgl_ctx, kCGLCPGPUFragmentProcessing, &fragmentOnGPU);
846 if (!fragmentOnGPU)
847 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL warning: Falling back to software renderer for fragment shader. This will slow things down.");
848#pragma clang diagnostic pop
849
850 bool foundError = false;
851 do
852 {
853 GLenum error = glGetError();
854 if (error == GL_NO_ERROR)
855 break;
856 _VGL_describe(error, cgl_ctx, file, linenumber, func);
857 foundError = true;
858 } while (true);
859
860 if (foundError)
862}
863
867void _VGL_describe(GLenum error, CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func)
868{
869 // Text from http://www.opengl.org/sdk/docs/man/xhtml/glGetError.xml
870 const char *errorString = "(unknown)";
871 if (error == GL_INVALID_ENUM)
872 errorString = "GL_INVALID_ENUM (An unacceptable value is specified for an enumerated argument. The offending command is ignored and has no other side effect than to set the error flag.)";
873 else if (error == GL_INVALID_VALUE)
874 errorString = "GL_INVALID_VALUE (A numeric argument is out of range. The offending command is ignored and has no other side effect than to set the error flag.)";
875 else if (error == GL_INVALID_OPERATION)
876 errorString = "GL_INVALID_OPERATION (The specified operation is not allowed in the current state. The offending command is ignored and has no other side effect than to set the error flag.)";
877 else if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
878 {
879 errorString = "GL_INVALID_FRAMEBUFFER_OPERATION (The framebuffer object is not complete. The offending command is ignored and has no other side effect than to set the error flag.)";
880 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL error %d: %s", error, errorString);
881
882 GLenum framebufferError = glCheckFramebufferStatus(GL_FRAMEBUFFER);
883 // Text from http://www.khronos.org/opengles/sdk/docs/man/xhtml/glCheckFramebufferStatus.xml
884 const char *framebufferErrorString = "(unknown)";
885 if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
886 framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT (Not all framebuffer attachment points are framebuffer attachment complete. This means that at least one attachment point with a renderbuffer or texture attached has its attached object no longer in existence or has an attached image with a width or height of zero, or the color attachment point has a non-color-renderable image attached, or the depth attachment point has a non-depth-renderable image attached, or the stencil attachment point has a non-stencil-renderable image attached.)";
887// else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)
888// framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS (Not all attached images have the same width and height.)";
889 else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
890 framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (No images are attached to the framebuffer.)";
891 else if (framebufferError == GL_FRAMEBUFFER_UNSUPPORTED)
892 framebufferErrorString = "GL_FRAMEBUFFER_UNSUPPORTED (The combination of internal formats of the attached images violates an implementation-dependent set of restrictions.)";
893 else if (framebufferError == GL_FRAMEBUFFER_COMPLETE)
894 framebufferErrorString = "GL_FRAMEBUFFER_COMPLETE (?)";
895 else if (framebufferError == GL_FRAMEBUFFER_UNDEFINED)
896 framebufferErrorString = "GL_FRAMEBUFFER_UNDEFINED";
897 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL framebuffer error %d: %s", framebufferError, framebufferErrorString);
898
899 return;
900 }
901 else if (error == GL_OUT_OF_MEMORY)
902 errorString = "GL_OUT_OF_MEMORY (There is not enough memory left to execute the command. The state of the GL is undefined, except for the state of the error flags, after this error is recorded.)";
903 else if (error == GL_STACK_UNDERFLOW)
904 errorString = "GL_STACK_UNDERFLOW (An attempt has been made to perform an operation that would cause an internal stack to underflow.)";
905 else if (error == GL_STACK_OVERFLOW)
906 errorString = "GL_STACK_OVERFLOW (An attempt has been made to perform an operation that would cause an internal stack to overflow.)";
907
908 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL error %d: %s", error, errorString);
910}
911
912typedef std::map<VuoGlContext, GLuint> VuoShaderContextType;