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