Vuo  2.3.2
VuoGlContext.cc
Go to the documentation of this file.
1 
10 #include <vector>
11 #include <algorithm>
12 #include <bitset>
13 #include <map>
14 using 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 
32 static CGLContextObj VuoGlContext_create(CGLContextObj rootContext);
33 dispatch_semaphore_t VuoGlContext_poolSemaphore;
34 static CGLContextObj VuoGlContext_root = NULL;
35 vector<CGLContextObj> VuoGlContext_allSharedContexts;
36 vector<CGLContextObj> VuoGlContext_avaialbleSharedContexts;
37 
38 static dispatch_once_t VuoGlContextPoolCreated = 0;
39 static pthread_key_t VuoGlContextPerformKey;
40 
44 static void VuoGlContext_renderers(void)
45 {
46  VuoList_VuoScreen screensList = VuoScreen_getList();
47  unsigned long screenCount = VuoListGetCount_VuoScreen(screensList);
48  VuoScreen *screens = VuoListGetData_VuoScreen(screensList);
49  VuoLocal(screensList);
50 
51 #pragma clang diagnostic push
52 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
53  // https://developer.apple.com/library/mac/qa/qa1168/_index.html says:
54  // "If you are looking for the VRAM sizes of all the renderers on your system […]
55  // you may specify a -1/0xFFFFFFFF display mask in the CGLQueryRendererInfo() function.
56  CGLRendererInfoObj ri;
57  GLint rendererCount = 0;
58  CGLQueryRendererInfo(-1, &ri, &rendererCount);
59  for (int i = 0; i < rendererCount; ++i)
60  {
61  GLint rendererID;
62  CGLDescribeRenderer(ri, i, kCGLRPRendererID, &rendererID);
63  VUserLog("Renderer %d: %s", i, VuoCglRenderer_getText(rendererID));
64 
65  GLint online;
66  if (CGLDescribeRenderer(ri, i, kCGLRPOnline, &online) == kCGLNoError)
67  VUserLog(" Online : %s", online ? "yes" : "no");
68 
69  GLint accelerated;
70  if (CGLDescribeRenderer(ri, i, kCGLRPAccelerated, &accelerated) == kCGLNoError)
71  VUserLog(" Accelerated : %s", accelerated ? "yes" : "no");
72 
73  GLint videoMegabytes = 0;
74  if (CGLDescribeRenderer(ri, i, kCGLRPVideoMemoryMegabytes, &videoMegabytes) == kCGLNoError
75  && videoMegabytes)
76  VUserLog(" Video memory : %d MB", videoMegabytes);
77 
78  GLint textureMegabytes = 0;
79  if (CGLDescribeRenderer(ri, i, kCGLRPTextureMemoryMegabytes, &textureMegabytes) == kCGLNoError
80  && textureMegabytes)
81  VUserLog(" Texture memory : %d MB", textureMegabytes);
82 
83  GLint displayMask;
84  if (CGLDescribeRenderer(ri, i, kCGLRPDisplayMask, &displayMask) == kCGLNoError)
85  {
86  VUserLog(" Display mask : %s (0x%x)%s",
87  std::bitset<32>(displayMask).to_string().c_str(),
88  displayMask, (displayMask & 0xff) == 0xff ? " (any)" : "");
89  if ((displayMask & 0xff) != 0xff)
90  for (unsigned long i = 0; i < screenCount; ++i)
91  if (displayMask & screens[i].displayMask)
92  VUserLog(" %s %s",
93  std::bitset<32>(screens[i].displayMask).to_string().c_str(),
94  screens[i].name);
95  }
96 
97  GLint glVersion = 0;
98  if (CGLDescribeRenderer(ri, i, /*kCGLRPMajorGLVersion*/ (CGLRendererProperty)133, &glVersion) == kCGLNoError)
99  VUserLog(" OpenGL version : %d", glVersion);
100 
101  CGLPixelFormatObj pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(false, false, displayMask);
102  if (pf != (CGLPixelFormatObj)-1)
103  {
104  CGLContextObj cgl_ctx;
105  CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
106  if (error != kCGLNoError)
107  VUserLog(" Error: %s", CGLErrorString(error));
108  else
109  {
110  GLint maxTextureSize;
111  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
112  VUserLog(" OpenGL 2 : %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
113  CGLDestroyContext(cgl_ctx);
114  }
115  }
116  else
117  VUserLog(" (Can't create an OpenGL 2 context on this renderer.)");
118 
119  pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(false, true, displayMask);
120  if (pf != (CGLPixelFormatObj)-1)
121  {
122  CGLContextObj cgl_ctx;
123  CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
124  if (error != kCGLNoError)
125  VUserLog(" Error: %s", CGLErrorString(error));
126  else
127  {
128  GLint maxTextureSize;
129  glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
130  VUserLog(" OpenGL Core Profile: %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
131  CGLDestroyContext(cgl_ctx);
132  }
133  }
134  else
135  VUserLog(" (Can't create an OpenGL Core Profile context on this renderer.)");
136 
137  GLint cl = 0;
138  if (CGLDescribeRenderer(ri, i, kCGLRPAcceleratedCompute, &cl) == kCGLNoError)
139  VUserLog(" OpenCL supported : %s", cl ? "yes" : "no");
140  }
141  CGLDestroyRendererInfo(ri);
142 #pragma clang diagnostic pop
143 
144 
145  const char *gldriver = "GLDriver";
146  size_t gldriverLen = strlen(gldriver);
147  uint32_t imageCount = _dyld_image_count();
148  for (uint32_t i = 0; i < imageCount; ++i)
149  {
150  const char *dylibPath = _dyld_get_image_name(i);
151  size_t len = strlen(dylibPath);
152  // If the image name ends with "GLDriver"…
153  if (strcmp(dylibPath + len - gldriverLen, gldriver) == 0)
154  {
155  // Trim off the path and the common "GLDriver" suffix.
156  char *z = strdup(strrchr(dylibPath, '/')+1);
157  z[strlen(z)-gldriverLen] = 0;
158  VUserLog("Driver: %s", z);
159  free(z);
160  }
161  }
162 
163  typedef void *(*mtlCopyAllDevicesType)(void);
164  mtlCopyAllDevicesType mtlCopyAllDevices = (mtlCopyAllDevicesType)dlsym(RTLD_DEFAULT, "MTLCopyAllDevices");
165  if (mtlCopyAllDevices)
166  {
167  CFArrayRef mtlDevices = (CFArrayRef)mtlCopyAllDevices();
168  int mtlDeviceCount = CFArrayGetCount(mtlDevices);
169  if (mtlDeviceCount)
170  VUserLog("Metal devices:");
171  for (int i = 0; i < mtlDeviceCount; ++i)
172  {
173  id dev = (id)CFArrayGetValueAtIndex(mtlDevices, i);
174  id devName = ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("name"));
175  const char *devNameZ = ((char * (*)(id, SEL))objc_msgSend)(devName, sel_getUid("UTF8String"));
176  VUserLog(" %s:", devNameZ);
177  if (class_respondsToSelector(object_getClass(dev), sel_getUid("registryID")))
178  VUserLog(" ID : %p", ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("registryID")));
179  if (class_respondsToSelector(object_getClass(dev), sel_getUid("recommendedMaxWorkingSetSize")))
180  VUserLog(" Recommended max working-set size : %lld MiB", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("recommendedMaxWorkingSetSize"))/1048576);
181  if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxBufferLength")))
182  VUserLog(" Max buffer length : %lld MiB", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxBufferLength"))/1048576);
183  if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxThreadgroupMemoryLength")))
184  VUserLog(" Threadgroup memory : %lld B", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxThreadgroupMemoryLength")));
185  VUserLog(" Low-power : %s", ((bool (*)(id, SEL))objc_msgSend) ? "yes" : "no");
186  if (class_respondsToSelector(object_getClass(dev), sel_getUid("isRemovable")))
187  VUserLog(" Removable : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("isRemovable")) ? "yes" : "no");
188  VUserLog(" Headless : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("isHeadless")) ? "yes" : "no");
189 
190  if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10005))
191  VUserLog(" Feature set : GPU Family 2 v1");
192  else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10004))
193  VUserLog(" Feature set : GPU Family 1 v4");
194  else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10003))
195  VUserLog(" Feature set : GPU Family 1 v3");
196  else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10001))
197  VUserLog(" Feature set : GPU Family 1 v2");
198  else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10000))
199  VUserLog(" Feature set : GPU Family 1 v1");
200  else
201  VUserLog(" Feature set : (unknown)");
202 
203  if (class_respondsToSelector(object_getClass(dev), sel_getUid("readWriteTextureSupport")))
204  VUserLog(" Read-write texture support tier : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("readWriteTextureSupport")));
205  if (class_respondsToSelector(object_getClass(dev), sel_getUid("argumentBuffersSupport")))
206  VUserLog(" Argument buffer support tier : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("argumentBuffersSupport")));
207  if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxArgumentBufferSamplerCount")))
208  VUserLog(" Max argument buffers : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxArgumentBufferSamplerCount")));
209  if (class_respondsToSelector(object_getClass(dev), sel_getUid("areProgrammableSamplePositionsSupported")))
210  VUserLog(" Programmable sample position support: %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("areProgrammableSamplePositionsSupported")) ? "yes" : "no");
211  if (class_respondsToSelector(object_getClass(dev), sel_getUid("areRasterOrderGroupsSupported")))
212  VUserLog(" Raster order group support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("areRasterOrderGroupsSupported")) ? "yes" : "no");
213  }
214  CFRelease(mtlDevices);
215  }
216 }
217 
221 void VuoGlContext_reconfig(CGDirectDisplayID display, CGDisplayChangeSummaryFlags flags, void *userInfo)
222 {
223  if (!(flags & kCGDisplaySetModeFlag))
224  return;
225 
226  VuoList_VuoScreen screensList = VuoScreen_getList();
227  unsigned long screenCount = VuoListGetCount_VuoScreen(screensList);
228  VuoScreen *screens = VuoListGetData_VuoScreen(screensList);
229  VuoLocal(screensList);
230 
231  for (unsigned long i = 0; i < screenCount; ++i)
232  if (screens[i].id == display)
233  VUserLog("Display reconfigured: %s", screens[i].name);
234 }
235 
239  static void VuoGlContext_init()
240  {
241  dispatch_once(&VuoGlContextPoolCreated, ^{
242  VuoGlContext_poolSemaphore = dispatch_semaphore_create(1);
243 
244  if (!VuoGlContext_root)
246 
247  int ret = pthread_key_create(&VuoGlContextPerformKey, NULL);
248  if (ret)
249  VUserLog("Couldn't create the key for storing the GL Context state: %s", strerror(errno));
250  });
251  }
252 
260  {
262 
263  CGLContextObj context;
264 
265  dispatch_semaphore_wait(VuoGlContext_poolSemaphore, DISPATCH_TIME_FOREVER);
266  {
267 // VL();
268 // VuoLog_backtrace();
269 
271  {
272  context = VuoGlContext_avaialbleSharedContexts.back();
274 // VLog("Found existing context %p.", context);
275  }
276  else
277  {
279  if (!context)
280  {
281  VUserLog("Error: Couldn't create a context.");
282  return NULL;
283  }
284  VuoGlContext_allSharedContexts.push_back(context);
285 // VLog("Created context %p.", context);
286  }
287  }
288  dispatch_semaphore_signal(VuoGlContext_poolSemaphore);
289 
290  return context;
291  }
292 
298  void VuoGlContext_disuseF(VuoGlContext glContext, const char *file, const unsigned int linenumber, const char *func)
299  {
300  CGLContextObj cgl_ctx = (CGLContextObj)glContext;
301 // VLog("%p", context);
302 
303 #if 0
304  VGL();
305 
306  // Check whether there are any stale bindings (to help identify leaks).
307  // (Some checks are commented because they aren't available in OpenGL 2.1.)
308  VuoGlCheckBinding(GL_ARRAY_BUFFER_BINDING);
309 // VuoGlCheckBinding(GL_ATOMIC_COUNTER_BUFFER_BINDING);
310 // VuoGlCheckBinding(GL_COPY_READ_BUFFER_BINDING);
311 // VuoGlCheckBinding(GL_COPY_WRITE_BUFFER_BINDING);
312 // VuoGlCheckBinding(GL_DRAW_INDIRECT_BUFFER_BINDING);
313 // VuoGlCheckBinding(GL_DISPATCH_INDIRECT_BUFFER_BINDING);
314  VuoGlCheckBinding(GL_DRAW_FRAMEBUFFER_BINDING);
315  VuoGlCheckBinding(GL_ELEMENT_ARRAY_BUFFER_BINDING);
316  VuoGlCheckBinding(GL_FRAMEBUFFER_BINDING);
317  VuoGlCheckBinding(GL_PIXEL_PACK_BUFFER_BINDING);
318  VuoGlCheckBinding(GL_PIXEL_UNPACK_BUFFER_BINDING);
319 // VuoGlCheckBinding(GL_PROGRAM_PIPELINE_BINDING);
320  VuoGlCheckBinding(GL_READ_FRAMEBUFFER_BINDING);
321  VuoGlCheckBinding(GL_RENDERBUFFER_BINDING);
322 // VuoGlCheckBinding(GL_SAMPLER_BINDING);
323 // VuoGlCheckBinding(GL_SHADER_STORAGE_BUFFER_BINDING);
324 
325  GLint textureUnits;
326  glGetIntegerv(GL_MAX_TEXTURE_UNITS, &textureUnits);
327  for (GLint i=0; i<textureUnits; ++i)
328  {
329  glActiveTexture(GL_TEXTURE0+i);
330  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_1D,i);
331  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_1D_ARRAY_EXT,i);
332  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D,i);
333  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_ARRAY_EXT,i);
334 // VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_MULTISAMPLE,i);
335 // VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,i);
336  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_3D,i);
337 // VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_BUFFER,i);
338  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_CUBE_MAP,i);
339  VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_RECTANGLE_ARB,i);
340  }
341 
342  VuoGlCheckBinding(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT);
343  VuoGlCheckBinding(GL_UNIFORM_BUFFER_BINDING_EXT);
344  VuoGlCheckBinding(GL_VERTEX_ARRAY_BINDING_APPLE);
345 #endif
346 
347  dispatch_semaphore_wait(VuoGlContext_poolSemaphore, DISPATCH_TIME_FOREVER);
348  {
350  VuoGlContext_avaialbleSharedContexts.push_back(cgl_ctx);
351  else
352  VUserLog("Error: Disued context %p, which isn't in the global share pool. I'm not going to muddy the waters.", cgl_ctx);
353  }
354  dispatch_semaphore_signal(VuoGlContext_poolSemaphore);
355  }
356 
360  #define VuoGlContext_checkGL(cap, value) \
361  do { \
362  if (glIsEnabled(cap) != value) \
363  { \
364  VUserLog("Warning: Caller incorrectly left %s %s", #cap, value ? "disabled" : "enabled"); \
365  VuoLog_backtrace(); \
366  } \
367  } while (0)
368 
372  #define VuoGlContext_checkGLInt(key, value) \
373  do { \
374  GLint actualValue; \
375  glGetIntegerv(key, &actualValue); \
376  if (actualValue != value) \
377  { \
378  VUserLog("Warning: Caller incorrectly left %s set to something other than %s", #key, #value); \
379  VuoLog_backtrace(); \
380  } \
381  } while (0)
382 
383 
384 
397  void VuoGlContext_perform(void (^function)(CGLContextObj cgl_ctx))
398  {
399 #pragma clang diagnostic push
400 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
401  if (!function)
402  return;
403 
405 
406  bool alreadyLockedOnThisThread = (bool)pthread_getspecific(VuoGlContextPerformKey);
407 
408  if (!alreadyLockedOnThisThread)
409  {
410  pthread_setspecific(VuoGlContextPerformKey, (void *)true);
411  CGLLockContext(VuoGlContext_root);
412  }
413 
414  function(VuoGlContext_root);
415 
416  // Ensure that `function` restored the standard OpenGL state.
417  if (!alreadyLockedOnThisThread && VuoIsDebugEnabled())
418  {
419  CGLContextObj cgl_ctx = VuoGlContext_root;
420 
421  VGL();
422 
423  VuoGlContext_checkGLInt(GL_DEPTH_WRITEMASK, true);
424  VuoGlContext_checkGL (GL_DEPTH_TEST, false);
425 
426  VuoGlContext_checkGL (GL_CULL_FACE, true);
427  VuoGlContext_checkGLInt(GL_CULL_FACE_MODE, GL_BACK);
428 
429  VuoGlContext_checkGL (GL_BLEND, true);
430 
431  VuoGlContext_checkGLInt(GL_BLEND_SRC_RGB, GL_ONE);
432  VuoGlContext_checkGLInt(GL_BLEND_DST_RGB, GL_ONE_MINUS_SRC_ALPHA);
433  VuoGlContext_checkGLInt(GL_BLEND_SRC_ALPHA, GL_ONE);
434  VuoGlContext_checkGLInt(GL_BLEND_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
435 
436  VuoGlContext_checkGLInt(GL_BLEND_EQUATION_RGB, GL_FUNC_ADD);
437  VuoGlContext_checkGLInt(GL_BLEND_EQUATION_ALPHA, GL_FUNC_ADD);
438 
439  VuoGlContext_checkGL(GL_SAMPLE_ALPHA_TO_COVERAGE, false);
440  VuoGlContext_checkGL(GL_SAMPLE_ALPHA_TO_ONE, false);
441 
443  }
444 
445  if (!alreadyLockedOnThisThread)
446  {
447  CGLUnlockContext(VuoGlContext_root);
448  pthread_setspecific(VuoGlContextPerformKey, (void *)false);
449  }
450 #pragma clang diagnostic pop
451  }
452 
456  static CGLContextObj VuoGlContext_create(CGLContextObj rootContext)
457  {
458 #pragma clang diagnostic push
459 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
460  static dispatch_once_t info = 0;
461  dispatch_once(&info, ^{
462  if (VuoIsDebugEnabled())
463  {
465 
466  CGDisplayRegisterReconfigurationCallback(VuoGlContext_reconfig, NULL);
467  }
468  });
469 
470  CGLPixelFormatObj pf;
471  bool shouldDestroyPixelFormat = false;
472  if (rootContext)
473  pf = CGLGetPixelFormat(rootContext);
474  else
475  {
476  Boolean overridden = false;
477  GLint displayMask = (int)CFPreferencesGetAppIntegerValue(CFSTR("displayMask"), CFSTR("org.vuo.Editor"), &overridden);
478  if (!overridden)
479  {
480  // Maybe the preference is a hex string…
481  auto displayMaskString = (CFStringRef)CFPreferencesCopyAppValue(CFSTR("displayMask"), CFSTR("org.vuo.Editor"));
482  if (displayMaskString)
483  {
484  VuoText t = VuoText_makeFromCFString(displayMaskString);
485  CFRelease(displayMaskString);
486  VuoLocal(t);
487  overridden = sscanf(t, "0x%x", &displayMask) == 1;
488  }
489  if (!overridden)
490  // Still no preference, so let macOS automatically choose what it thinks is the best GPU.
491  displayMask = -1;
492  }
493 
494  VDebugLog("displayMask = %s (0x%x)%s",
495  std::bitset<32>(displayMask).to_string().c_str(),
496  displayMask, (displayMask & 0xff) == 0xff ? " (any)" : "");
497 
498  pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(true, false, displayMask);
499  shouldDestroyPixelFormat = true;
500  }
501 
502  CGLContextObj context;
503  {
504  CGLError error = CGLCreateContext(pf, rootContext, &context);
505  if (shouldDestroyPixelFormat)
506  CGLDestroyPixelFormat(pf);
507  if (error != kCGLNoError)
508  {
509  VUserLog("Error: %s", CGLErrorString(error));
510  return NULL;
511  }
512  }
513 
514  if (VuoIsDebugEnabled())
515  {
516  GLint rendererID;
517  CGLGetParameter(context, kCGLCPCurrentRendererID, &rendererID);
518  VUserLog("Created OpenGL context %p%s on %s",
519  context,
520  rootContext ? VuoText_format(" (shared with %p)", rootContext) : " (not shared)",
521  VuoCglRenderer_getText(rendererID));
522  }
523 
524  // https://developer.apple.com/library/content/technotes/tn2085/_index.html
525  // But it doesn't seem to actually improve performance any on the various workloads I tried.
526 // CGLEnable(context, kCGLCEMPEngine);
527 
528  // Set the context's default state to commonly-used values,
529  // to hopefully minimize subsequent state changes.
530  {
531  CGLContextObj cgl_ctx = context;
532  glDepthMask(true);
533  glDisable(GL_DEPTH_TEST);
534  glEnable(GL_CULL_FACE);
535  glEnable(GL_BLEND);
536  glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
537  glBlendEquation(GL_FUNC_ADD);
538  glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
539  glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -.5);
540  }
541 
542  return context;
543 #pragma clang diagnostic pop
544  }
545 
556 void VuoGlContext_setGlobalRootContext(void *rootContext)
557 {
559  {
560  VUserLog("Error: Called after VuoGlContextPool was initialized. Ignoring the new rootContext.");
561  return;
562  }
563 
564  VDebugLog("Setting global root context to %p", rootContext);
565  VuoGlContext_root = VuoGlContext_create((CGLContextObj)rootContext);
566 }
567 
572 #define VuoGlCheckBinding(pname) \
573 { \
574  GLint value; \
575  glGetIntegerv(pname, &value); \
576  if (value) \
577  { \
578  VuoLog(file, linenumber, func, #pname " (value %d) was still active when the context was disused. (This may result in leaks.)", value); \
579  VuoLog_backtrace(); \
580  } \
581 }
582 
587 #define VuoGlCheckTextureBinding(pname, unit) \
588 { \
589  GLint value; \
590  glGetIntegerv(pname, &value); \
591  if (value) \
592  { \
593  VuoLog(file, linenumber, func, #pname " (texture %d on unit %d) was still active when the context was disused. (This may result in leaks.)", value, unit); \
594  VuoLog_backtrace(); \
595  } \
596 }
597 
604 {
605  static GLint supportedSamples = 0;
606  static dispatch_once_t multisamplingCheck = 0;
607  dispatch_once(&multisamplingCheck, ^{
608  GLint rendererID;
609 #pragma clang diagnostic push
610 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
611  CGLGetParameter((CGLContextObj)context, kCGLCPCurrentRendererID, &rendererID);
612 #pragma clang diagnostic pop
613  rendererID &= kCGLRendererIDMatchingMask;
614 
615  CGLContextObj cgl_ctx = (CGLContextObj)context;
616  const char *renderer = (const char *)glGetString(GL_RENDERER);
617 
618  if (rendererID == kCGLRendererIntelHD4000ID // https://b33p.net/kosada/node/8225#comment-31324
619  || rendererID == /*kCGLRendererIntelHD5000ID*/ 0x00024500 // https://b33p.net/kosada/node/10595
620  || strcmp(renderer, "NVIDIA GeForce 320M OpenGL Engine") == 0) // https://b33p.net/kosada/node/13477
621  supportedSamples = 0;
622  else
623  {
624  glGetIntegerv(GL_MAX_SAMPLES, &supportedSamples);
625  if (supportedSamples == 1)
626  supportedSamples = 0;
627  }
628  });
629  return supportedSamples;
630 }
631 
645 void *VuoGlContext_makePlatformPixelFormat(bool hasDepthBuffer, bool openGL32Core, GLint displayMask)
646 {
647 #pragma clang diagnostic push
648 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
649  // Check whether it's OK to use multisampling on this GPU.
650  static dispatch_once_t multisamplingCheck = 0;
651  static int multisample = 0;
652  dispatch_once(&multisamplingCheck, ^{
653  // Create a temporary context so we can get the GPU renderer string.
654  CGLContextObj cgl_ctx;
655  {
656  CGLPixelFormatObj pf;
657  {
658  CGLPixelFormatAttribute pfa[14] = {
659  kCGLPFAAccelerated,
660  kCGLPFAAllowOfflineRenderers,
661 // kCGLPFANoRecovery,
662 // kCGLPFADoubleBuffer,
663  kCGLPFABackingVolatile,
664  kCGLPFAColorSize, (CGLPixelFormatAttribute) 24,
665  kCGLPFADepthSize, (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0),
666  (CGLPixelFormatAttribute) 0
667  };
668  GLint npix;
669  CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
670  if (error != kCGLNoError)
671  {
672  VUserLog("Error: %s", CGLErrorString(error));
673  return;
674  }
675  }
676 
677  CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
678  CGLDestroyPixelFormat(pf);
679  if (error != kCGLNoError)
680  {
681  VUserLog("Error: %s", CGLErrorString(error));
682  return;
683  }
684  }
685 
686 
687  // If the user set the `multisample` preference, use it.
688  Boolean overridden = false;
689  multisample = (int)CFPreferencesGetAppIntegerValue(CFSTR("multisample"), CFSTR("org.vuo.Editor"), &overridden);
690 
691  if (!overridden)
692  {
693  // …otherwise enable 4x multisampling (unless there's a known problem with this GPU model).
694 
695  int supportedSamples = VuoGlContext_getMaximumSupportedMultisampling(cgl_ctx);
696  multisample = MIN(4, supportedSamples);
697  }
698 
699  CGLDestroyContext(cgl_ctx);
700  });
701 
702 
703  CGLPixelFormatAttribute pfa[18];
704  int pfaIndex = 0;
705 
706  // If requesting a specific display, don't require acceleration.
707  if (displayMask == -1)
708  pfa[pfaIndex++] = kCGLPFAAccelerated;
709 
710  pfa[pfaIndex++] = kCGLPFAAllowOfflineRenderers;
711 // pfa[pfaIndex++] = kCGLPFANoRecovery;
712 
713  // https://b33p.net/kosada/node/12525
714 // pfa[pfaIndex++] = kCGLPFADoubleBuffer;
715  pfa[pfaIndex++] = kCGLPFABackingVolatile;
716 
717  pfa[pfaIndex++] = kCGLPFAColorSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 24;
718  pfa[pfaIndex++] = kCGLPFADepthSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0);
719  if (openGL32Core)
720  {
721  pfa[pfaIndex++] = kCGLPFAOpenGLProfile; pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core;
722  }
723 
724  if (multisample)
725  {
726  pfa[pfaIndex++] = kCGLPFAMultisample;
727  pfa[pfaIndex++] = kCGLPFASampleBuffers; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 1;
728  pfa[pfaIndex++] = kCGLPFASamples; pfa[pfaIndex++] = (CGLPixelFormatAttribute) multisample;
729  }
730 
731  if (displayMask >= 0)
732  {
733  pfa[pfaIndex++] = kCGLPFADisplayMask;
734  pfa[pfaIndex++] = (CGLPixelFormatAttribute) displayMask;
735  }
736 
737  // software renderer
738  if ((displayMask & 0xff) == 0xff && displayMask != -1)
739  {
740  pfa[pfaIndex++] = kCGLPFARendererID;
741  pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLRendererGenericFloatID;
742  }
743 
744  pfa[pfaIndex] = (CGLPixelFormatAttribute) 0;
745 
746  CGLPixelFormatObj pf;
747  GLint npix;
748  CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
749  if (error == kCGLBadDisplay)
750  return (CGLPixelFormatObj)-1;
751  else if (error != kCGLNoError)
752  {
753  VUserLog("Error: %s", CGLErrorString(error));
754  return NULL;
755  }
756 
757  return (void *)pf;
758 #pragma clang diagnostic pop
759 }
760 
766 {
767  CGLContextObj cgl_ctx = (CGLContextObj)context;
768 
769  // GL_VERSION is something like:
770  // - Core Profile: `4.1 NVIDIA-10.17.5 355.10.05.45f01`
771  // - Core Profile: `4.1 ATI-2.4.10`
772  // - Compatibility Profile: `2.1 NVIDIA-10.17.5 355.10.05.45f01`
773  // - Compatibility Profile: `2.1 ATI-2.4.10`
774  const unsigned char *contextVersion = glGetString(GL_VERSION);
775  return contextVersion[0] == '3'
776  || contextVersion[0] == '4';
777 }
778 
779 
780 void _VGL_describe(GLenum error, CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func);
781 
785 void _VGL(CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func)
786 {
787 #pragma clang diagnostic push
788 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
789  GLint vertexOnGPU, fragmentOnGPU;
790  CGLGetParameter(cgl_ctx, kCGLCPGPUVertexProcessing, &vertexOnGPU);
791  if (!vertexOnGPU)
792  VuoLog(file, linenumber, func, "OpenGL warning: Falling back to software renderer for vertex shader. This will slow things down.");
793  CGLGetParameter(cgl_ctx, kCGLCPGPUFragmentProcessing, &fragmentOnGPU);
794  if (!fragmentOnGPU)
795  VuoLog(file, linenumber, func, "OpenGL warning: Falling back to software renderer for fragment shader. This will slow things down.");
796 #pragma clang diagnostic pop
797 
798  bool foundError = false;
799  do
800  {
801  GLenum error = glGetError();
802  if (error == GL_NO_ERROR)
803  break;
804  _VGL_describe(error, cgl_ctx, file, linenumber, func);
805  foundError = true;
806  } while (true);
807 
808  if (foundError)
810 }
811 
815 void _VGL_describe(GLenum error, CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func)
816 {
817  // Text from http://www.opengl.org/sdk/docs/man/xhtml/glGetError.xml
818  const char *errorString = "(unknown)";
819  if (error == GL_INVALID_ENUM)
820  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.)";
821  else if (error == GL_INVALID_VALUE)
822  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.)";
823  else if (error == GL_INVALID_OPERATION)
824  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.)";
825  else if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
826  {
827  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.)";
828  VuoLog(file, linenumber, func, "OpenGL error %d: %s", error, errorString);
829 
830  GLenum framebufferError = glCheckFramebufferStatus(GL_FRAMEBUFFER);
831  // Text from http://www.khronos.org/opengles/sdk/docs/man/xhtml/glCheckFramebufferStatus.xml
832  const char *framebufferErrorString = "(unknown)";
833  if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
834  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.)";
835 // else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)
836 // framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS (Not all attached images have the same width and height.)";
837  else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
838  framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (No images are attached to the framebuffer.)";
839  else if (framebufferError == GL_FRAMEBUFFER_UNSUPPORTED)
840  framebufferErrorString = "GL_FRAMEBUFFER_UNSUPPORTED (The combination of internal formats of the attached images violates an implementation-dependent set of restrictions.)";
841  else if (framebufferError == GL_FRAMEBUFFER_COMPLETE)
842  framebufferErrorString = "GL_FRAMEBUFFER_COMPLETE (?)";
843  else if (framebufferError == GL_FRAMEBUFFER_UNDEFINED)
844  framebufferErrorString = "GL_FRAMEBUFFER_UNDEFINED";
845  VuoLog(file, linenumber, func, "OpenGL framebuffer error %d: %s", framebufferError, framebufferErrorString);
846 
847  return;
848  }
849  else if (error == GL_OUT_OF_MEMORY)
850  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.)";
851  else if (error == GL_STACK_UNDERFLOW)
852  errorString = "GL_STACK_UNDERFLOW (An attempt has been made to perform an operation that would cause an internal stack to underflow.)";
853  else if (error == GL_STACK_OVERFLOW)
854  errorString = "GL_STACK_OVERFLOW (An attempt has been made to perform an operation that would cause an internal stack to overflow.)";
855 
856  VuoLog(file, linenumber, func, "OpenGL error %d: %s", error, errorString);
858 }
859 
860 typedef std::map<VuoGlContext, GLuint> VuoShaderContextType;