21 #include <OpenGL/CGLMacro.h>
22 #include <CoreFoundation/CoreFoundation.h>
23 #include <CoreGraphics/CoreGraphics.h>
25 #include <dispatch/dispatch.h>
27 #include <mach-o/dyld.h>
28 #include <objc/objc-runtime.h>
54 CGLRendererInfoObj ri;
55 GLint rendererCount = 0;
56 CGLQueryRendererInfo(-1, &ri, &rendererCount);
57 for (
int i = 0; i < rendererCount; ++i)
60 CGLDescribeRenderer(ri, i, kCGLRPRendererID, &rendererID);
64 if (CGLDescribeRenderer(ri, i, kCGLRPOnline, &online) == kCGLNoError)
65 VUserLog(
" Online : %s", online ?
"yes" :
"no");
68 if (CGLDescribeRenderer(ri, i, kCGLRPAccelerated, &accelerated) == kCGLNoError)
69 VUserLog(
" Accelerated : %s", accelerated ?
"yes" :
"no");
71 GLint videoMegabytes = 0;
72 if (CGLDescribeRenderer(ri, i, kCGLRPVideoMemoryMegabytes, &videoMegabytes) == kCGLNoError
74 VUserLog(
" Video memory : %d MB", videoMegabytes);
76 GLint textureMegabytes = 0;
77 if (CGLDescribeRenderer(ri, i, kCGLRPTextureMemoryMegabytes, &textureMegabytes) == kCGLNoError
79 VUserLog(
" Texture memory : %d MB", textureMegabytes);
82 if (CGLDescribeRenderer(ri, i, kCGLRPDisplayMask, &displayMask) == kCGLNoError)
84 VUserLog(
" Display mask : %s (0x%x)%s",
85 std::bitset<32>(displayMask).to_string().c_str(),
86 displayMask, (displayMask & 0xff) == 0xff ?
" (any)" :
"");
87 if ((displayMask & 0xff) != 0xff)
88 for (
unsigned long i = 0; i < screenCount; ++i)
89 if (displayMask & screens[i].displayMask)
91 std::bitset<32>(screens[i].displayMask).to_string().c_str(),
96 if (CGLDescribeRenderer(ri, i, (CGLRendererProperty)133, &glVersion) == kCGLNoError)
97 VUserLog(
" OpenGL version : %d", glVersion);
100 if (pf != (CGLPixelFormatObj)-1)
102 CGLContextObj cgl_ctx;
103 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
104 if (error != kCGLNoError)
105 VUserLog(
" Error: %s", CGLErrorString(error));
108 GLint maxTextureSize;
109 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
110 VUserLog(
" OpenGL 2 : %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
111 CGLDestroyContext(cgl_ctx);
115 VUserLog(
" (Can't create an OpenGL 2 context on this renderer.)");
118 if (pf != (CGLPixelFormatObj)-1)
120 CGLContextObj cgl_ctx;
121 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
122 if (error != kCGLNoError)
123 VUserLog(
" Error: %s", CGLErrorString(error));
126 GLint maxTextureSize;
127 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
128 VUserLog(
" OpenGL Core Profile: %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
129 CGLDestroyContext(cgl_ctx);
133 VUserLog(
" (Can't create an OpenGL Core Profile context on this renderer.)");
136 if (CGLDescribeRenderer(ri, i, kCGLRPAcceleratedCompute, &cl) == kCGLNoError)
137 VUserLog(
" OpenCL supported : %s", cl ?
"yes" :
"no");
139 CGLDestroyRendererInfo(ri);
142 const char *gldriver =
"GLDriver";
143 size_t gldriverLen = strlen(gldriver);
144 uint32_t imageCount = _dyld_image_count();
145 for (uint32_t i = 0; i < imageCount; ++i)
147 const char *dylibPath = _dyld_get_image_name(i);
148 size_t len = strlen(dylibPath);
150 if (strcmp(dylibPath + len - gldriverLen, gldriver) == 0)
153 char *z = strdup(strrchr(dylibPath,
'/')+1);
154 z[strlen(z)-gldriverLen] = 0;
160 typedef void *(*mtlCopyAllDevicesType)(void);
161 mtlCopyAllDevicesType mtlCopyAllDevices = (mtlCopyAllDevicesType)dlsym(RTLD_DEFAULT,
"MTLCopyAllDevices");
162 if (mtlCopyAllDevices)
164 CFArrayRef mtlDevices = (CFArrayRef)mtlCopyAllDevices();
165 int mtlDeviceCount = CFArrayGetCount(mtlDevices);
168 for (
int i = 0; i < mtlDeviceCount; ++i)
170 id dev = (id)CFArrayGetValueAtIndex(mtlDevices, i);
171 VUserLog(
" %s:", (
char *)objc_msgSend(objc_msgSend(dev, sel_getUid(
"name")), sel_getUid(
"UTF8String")));
172 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"registryID")))
173 VUserLog(
" ID : %p", objc_msgSend(dev, sel_getUid(
"registryID")));
174 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"recommendedMaxWorkingSetSize")))
175 VUserLog(
" Recommended max working-set size : %lld MiB", (int64_t)objc_msgSend(dev, sel_getUid(
"recommendedMaxWorkingSetSize"))/1048576);
176 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"maxBufferLength")))
177 VUserLog(
" Max buffer length : %lld MiB", (int64_t)objc_msgSend(dev, sel_getUid(
"maxBufferLength"))/1048576);
178 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"maxThreadgroupMemoryLength")))
179 VUserLog(
" Threadgroup memory : %lld B", (int64_t)objc_msgSend(dev, sel_getUid(
"maxThreadgroupMemoryLength")));
180 VUserLog(
" Low-power : %s", objc_msgSend(dev, sel_getUid(
"isLowPower")) ?
"yes" :
"no");
181 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"isRemovable")))
182 VUserLog(
" Removable : %s", objc_msgSend(dev, sel_getUid(
"isRemovable")) ?
"yes" :
"no");
183 VUserLog(
" Headless : %s", objc_msgSend(dev, sel_getUid(
"isHeadless")) ?
"yes" :
"no");
185 if (objc_msgSend(dev, sel_getUid(
"supportsFeatureSet:"), 10005))
186 VUserLog(
" Feature set : GPU Family 2 v1");
187 else if (objc_msgSend(dev, sel_getUid(
"supportsFeatureSet:"), 10004))
188 VUserLog(
" Feature set : GPU Family 1 v4");
189 else if (objc_msgSend(dev, sel_getUid(
"supportsFeatureSet:"), 10003))
190 VUserLog(
" Feature set : GPU Family 1 v3");
191 else if (objc_msgSend(dev, sel_getUid(
"supportsFeatureSet:"), 10001))
192 VUserLog(
" Feature set : GPU Family 1 v2");
193 else if (objc_msgSend(dev, sel_getUid(
"supportsFeatureSet:"), 10000))
194 VUserLog(
" Feature set : GPU Family 1 v1");
196 VUserLog(
" Feature set : (unknown)");
198 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"readWriteTextureSupport")))
199 VUserLog(
" Read-write texture support tier : %lld", (int64_t)objc_msgSend(dev, sel_getUid(
"readWriteTextureSupport")));
200 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"argumentBuffersSupport")))
201 VUserLog(
" Argument buffer support tier : %lld", (int64_t)objc_msgSend(dev, sel_getUid(
"argumentBuffersSupport")));
202 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"maxArgumentBufferSamplerCount")))
203 VUserLog(
" Max argument buffers : %lld", (int64_t)objc_msgSend(dev, sel_getUid(
"maxArgumentBufferSamplerCount")));
204 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"areProgrammableSamplePositionsSupported")))
205 VUserLog(
" Programmable sample position support: %s", objc_msgSend(dev, sel_getUid(
"areProgrammableSamplePositionsSupported")) ?
"yes" :
"no");
206 if (class_respondsToSelector(object_getClass(dev), sel_getUid(
"areRasterOrderGroupsSupported")))
207 VUserLog(
" Raster order group support : %s", objc_msgSend(dev, sel_getUid(
"areRasterOrderGroupsSupported")) ?
"yes" :
"no");
209 CFRelease(mtlDevices);
218 if (!(flags & kCGDisplaySetModeFlag))
226 for (
unsigned long i = 0; i < screenCount; ++i)
227 if (screens[i].
id == display)
228 VUserLog(
"Display reconfigured: %s", screens[i].name);
244 VUserLog(
"Couldn't create the key for storing the GL Context state: %s", strerror(errno));
258 CGLContextObj context;
276 VUserLog(
"Error: Couldn't create a context.");
295 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
321 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &textureUnits);
322 for (GLint i=0; i<textureUnits; ++i)
324 glActiveTexture(GL_TEXTURE0+i);
347 VUserLog(
"Error: Disued context %p, which isn't in the global share pool. I'm not going to muddy the waters.", cgl_ctx);
355 #define VuoGlContext_checkGL(cap, value) \
357 if (glIsEnabled(cap) != value) \
359 VUserLog("Warning: Caller incorrectly left %s %s", #cap, value ? "disabled" : "enabled"); \
360 VuoLog_backtrace(); \
367 #define VuoGlContext_checkGLInt(key, value) \
370 glGetIntegerv(key, &actualValue); \
371 if (actualValue != value) \
373 VUserLog("Warning: Caller incorrectly left %s set to something other than %s", #key, #value); \
374 VuoLog_backtrace(); \
401 if (!alreadyLockedOnThisThread)
438 if (!alreadyLockedOnThisThread)
450 static dispatch_once_t info = 0;
451 dispatch_once(&info, ^{
460 CGLPixelFormatObj pf;
461 bool shouldDestroyPixelFormat =
false;
463 pf = CGLGetPixelFormat(rootContext);
466 Boolean overridden =
false;
467 GLint displayMask = (int)CFPreferencesGetAppIntegerValue(CFSTR(
"displayMask"), CFSTR(
"org.vuo.Editor"), &overridden);
471 auto displayMaskString = (CFStringRef)CFPreferencesCopyAppValue(CFSTR(
"displayMask"), CFSTR(
"org.vuo.Editor"));
472 if (displayMaskString)
475 CFRelease(displayMaskString);
477 overridden = sscanf(t,
"0x%x", &displayMask) == 1;
485 std::bitset<32>(displayMask).to_string().c_str(),
486 displayMask, (displayMask & 0xff) == 0xff ?
" (any)" :
"");
489 shouldDestroyPixelFormat =
true;
492 CGLContextObj context;
494 CGLError error = CGLCreateContext(pf, rootContext, &context);
495 if (shouldDestroyPixelFormat)
496 CGLDestroyPixelFormat(pf);
497 if (error != kCGLNoError)
499 VUserLog(
"Error: %s", CGLErrorString(error));
507 CGLGetParameter(context, kCGLCPCurrentRendererID, &rendererID);
508 VUserLog(
"Created OpenGL context %p%s on %s",
510 rootContext ?
VuoText_format(
" (shared with %p)", rootContext) :
" (not shared)",
521 CGLContextObj cgl_ctx = context;
523 glDisable(GL_DEPTH_TEST);
524 glEnable(GL_CULL_FACE);
526 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
527 glBlendEquation(GL_FUNC_ADD);
528 glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
529 glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -.5);
549 VUserLog(
"Error: Called after VuoGlContextPool was initialized. Ignoring the new rootContext.");
553 VDebugLog(
"Setting global root context to %p", rootContext);
561 #define VuoGlCheckBinding(pname) \
564 glGetIntegerv(pname, &value); \
567 VuoLog(file, linenumber, func, #pname " (value %d) was still active when the context was disused. (This may result in leaks.)", value); \
568 VuoLog_backtrace(); \
576 #define VuoGlCheckTextureBinding(pname, unit) \
579 glGetIntegerv(pname, &value); \
582 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); \
583 VuoLog_backtrace(); \
594 static GLint supportedSamples = 0;
595 static dispatch_once_t multisamplingCheck = 0;
596 dispatch_once(&multisamplingCheck, ^{
598 CGLGetParameter((CGLContextObj)context, kCGLCPCurrentRendererID, &rendererID);
599 rendererID &= kCGLRendererIDMatchingMask;
601 CGLContextObj cgl_ctx = (CGLContextObj)context;
602 const char *renderer = (
const char *)glGetString(GL_RENDERER);
604 if (rendererID == kCGLRendererIntelHD4000ID
605 || rendererID == 0x00024500
606 || strcmp(renderer,
"NVIDIA GeForce 320M OpenGL Engine") == 0)
607 supportedSamples = 0;
610 glGetIntegerv(GL_MAX_SAMPLES, &supportedSamples);
611 if (supportedSamples == 1)
612 supportedSamples = 0;
615 return supportedSamples;
634 static dispatch_once_t multisamplingCheck = 0;
635 static int multisample = 0;
636 dispatch_once(&multisamplingCheck, ^{
638 CGLContextObj cgl_ctx;
640 CGLPixelFormatObj pf;
642 CGLPixelFormatAttribute pfa[14] = {
644 kCGLPFAAllowOfflineRenderers,
647 kCGLPFABackingVolatile,
648 kCGLPFAColorSize, (CGLPixelFormatAttribute) 24,
649 kCGLPFADepthSize, (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0),
650 (CGLPixelFormatAttribute) 0
653 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
654 if (error != kCGLNoError)
656 VUserLog(
"Error: %s", CGLErrorString(error));
661 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
662 CGLDestroyPixelFormat(pf);
663 if (error != kCGLNoError)
665 VUserLog(
"Error: %s", CGLErrorString(error));
672 Boolean overridden =
false;
673 multisample = (int)CFPreferencesGetAppIntegerValue(CFSTR(
"multisample"), CFSTR(
"org.vuo.Editor"), &overridden);
680 multisample =
MIN(4, supportedSamples);
683 CGLDestroyContext(cgl_ctx);
687 CGLPixelFormatAttribute pfa[18];
691 if (displayMask == -1)
692 pfa[pfaIndex++] = kCGLPFAAccelerated;
694 pfa[pfaIndex++] = kCGLPFAAllowOfflineRenderers;
699 pfa[pfaIndex++] = kCGLPFABackingVolatile;
701 pfa[pfaIndex++] = kCGLPFAColorSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 24;
702 pfa[pfaIndex++] = kCGLPFADepthSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0);
705 pfa[pfaIndex++] = kCGLPFAOpenGLProfile; pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core;
710 pfa[pfaIndex++] = kCGLPFAMultisample;
711 pfa[pfaIndex++] = kCGLPFASampleBuffers; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 1;
712 pfa[pfaIndex++] = kCGLPFASamples; pfa[pfaIndex++] = (CGLPixelFormatAttribute) multisample;
715 if (displayMask >= 0)
717 pfa[pfaIndex++] = kCGLPFADisplayMask;
718 pfa[pfaIndex++] = (CGLPixelFormatAttribute) displayMask;
722 if ((displayMask & 0xff) == 0xff && displayMask != -1)
724 pfa[pfaIndex++] = kCGLPFARendererID;
725 pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLRendererGenericFloatID;
728 pfa[pfaIndex] = (CGLPixelFormatAttribute) 0;
730 CGLPixelFormatObj pf;
732 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
733 if (error == kCGLBadDisplay)
734 return (CGLPixelFormatObj)-1;
735 else if (error != kCGLNoError)
737 VUserLog(
"Error: %s", CGLErrorString(error));
750 CGLContextObj cgl_ctx = (CGLContextObj)context;
757 const unsigned char *contextVersion = glGetString(GL_VERSION);
758 return contextVersion[0] ==
'3'
759 || contextVersion[0] ==
'4';
763 void _VGL_describe(GLenum error, CGLContextObj cgl_ctx,
const char *file,
const unsigned int linenumber,
const char *func);
768 void _VGL(CGLContextObj cgl_ctx,
const char *file,
const unsigned int linenumber,
const char *func)
770 GLint vertexOnGPU, fragmentOnGPU;
771 CGLGetParameter(cgl_ctx, kCGLCPGPUVertexProcessing, &vertexOnGPU);
773 VuoLog(file, linenumber, func,
"OpenGL warning: Falling back to software renderer for vertex shader. This will slow things down.");
774 CGLGetParameter(cgl_ctx, kCGLCPGPUFragmentProcessing, &fragmentOnGPU);
776 VuoLog(file, linenumber, func,
"OpenGL warning: Falling back to software renderer for fragment shader. This will slow things down.");
778 bool foundError =
false;
781 GLenum error = glGetError();
782 if (error == GL_NO_ERROR)
795 void _VGL_describe(GLenum error, CGLContextObj cgl_ctx,
const char *file,
const unsigned int linenumber,
const char *func)
798 const char *errorString =
"(unknown)";
799 if (error == GL_INVALID_ENUM)
800 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.)";
801 else if (error == GL_INVALID_VALUE)
802 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.)";
803 else if (error == GL_INVALID_OPERATION)
804 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.)";
805 else if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
807 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.)";
808 VuoLog(file, linenumber, func,
"OpenGL error %d: %s", error, errorString);
810 GLenum framebufferError = glCheckFramebufferStatus(GL_FRAMEBUFFER);
812 const char *framebufferErrorString =
"(unknown)";
813 if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
814 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.)";
817 else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
818 framebufferErrorString =
"GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (No images are attached to the framebuffer.)";
819 else if (framebufferError == GL_FRAMEBUFFER_UNSUPPORTED)
820 framebufferErrorString =
"GL_FRAMEBUFFER_UNSUPPORTED (The combination of internal formats of the attached images violates an implementation-dependent set of restrictions.)";
821 else if (framebufferError == GL_FRAMEBUFFER_COMPLETE)
822 framebufferErrorString =
"GL_FRAMEBUFFER_COMPLETE (?)";
823 else if (framebufferError == GL_FRAMEBUFFER_UNDEFINED)
824 framebufferErrorString =
"GL_FRAMEBUFFER_UNDEFINED";
825 VuoLog(file, linenumber, func,
"OpenGL framebuffer error %d: %s", framebufferError, framebufferErrorString);
829 else if (error == GL_OUT_OF_MEMORY)
830 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.)";
831 else if (error == GL_STACK_UNDERFLOW)
832 errorString =
"GL_STACK_UNDERFLOW (An attempt has been made to perform an operation that would cause an internal stack to underflow.)";
833 else if (error == GL_STACK_OVERFLOW)
834 errorString =
"GL_STACK_OVERFLOW (An attempt has been made to perform an operation that would cause an internal stack to overflow.)";
836 VuoLog(file, linenumber, func,
"OpenGL error %d: %s", error, errorString);