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>
51 #pragma clang diagnostic push
52 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
56 CGLRendererInfoObj ri;
57 GLint rendererCount = 0;
58 CGLQueryRendererInfo(-1, &ri, &rendererCount);
59 for (
int i = 0; i < rendererCount; ++i)
62 CGLDescribeRenderer(ri, i, kCGLRPRendererID, &rendererID);
66 if (CGLDescribeRenderer(ri, i, kCGLRPOnline, &online) == kCGLNoError)
67 VUserLog(
" Online : %s", online ?
"yes" :
"no");
70 if (CGLDescribeRenderer(ri, i, kCGLRPAccelerated, &accelerated) == kCGLNoError)
71 VUserLog(
" Accelerated : %s", accelerated ?
"yes" :
"no");
73 GLint videoMegabytes = 0;
74 if (CGLDescribeRenderer(ri, i, kCGLRPVideoMemoryMegabytes, &videoMegabytes) == kCGLNoError
76 VUserLog(
" Video memory : %d MB", videoMegabytes);
78 GLint textureMegabytes = 0;
79 if (CGLDescribeRenderer(ri, i, kCGLRPTextureMemoryMegabytes, &textureMegabytes) == kCGLNoError
81 VUserLog(
" Texture memory : %d MB", textureMegabytes);
84 if (CGLDescribeRenderer(ri, i, kCGLRPDisplayMask, &displayMask) == kCGLNoError)
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)
93 std::bitset<32>(screens[i].displayMask).to_string().c_str(),
98 if (CGLDescribeRenderer(ri, i, (CGLRendererProperty)133, &glVersion) == kCGLNoError)
99 VUserLog(
" OpenGL version : %d", glVersion);
102 if (pf != (CGLPixelFormatObj)-1)
104 CGLContextObj cgl_ctx;
105 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
106 if (error != kCGLNoError)
107 VUserLog(
" Error: %s", CGLErrorString(error));
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);
117 VUserLog(
" (Can't create an OpenGL 2 context on this renderer.)");
120 if (pf != (CGLPixelFormatObj)-1)
122 CGLContextObj cgl_ctx;
123 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
124 if (error != kCGLNoError)
125 VUserLog(
" Error: %s", CGLErrorString(error));
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);
135 VUserLog(
" (Can't create an OpenGL Core Profile context on this renderer.)");
138 if (CGLDescribeRenderer(ri, i, kCGLRPAcceleratedCompute, &cl) == kCGLNoError)
139 VUserLog(
" OpenCL supported : %s", cl ?
"yes" :
"no");
141 CGLDestroyRendererInfo(ri);
142 #pragma clang diagnostic pop
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)
150 const char *dylibPath = _dyld_get_image_name(i);
151 size_t len = strlen(dylibPath);
153 if (strcmp(dylibPath + len - gldriverLen, gldriver) == 0)
156 char *z = strdup(strrchr(dylibPath,
'/')+1);
157 z[strlen(z)-gldriverLen] = 0;
163 typedef void *(*mtlCopyAllDevicesType)(void);
164 mtlCopyAllDevicesType mtlCopyAllDevices = (mtlCopyAllDevicesType)dlsym(RTLD_DEFAULT,
"MTLCopyAllDevices");
165 if (mtlCopyAllDevices)
167 CFArrayRef mtlDevices = (CFArrayRef)mtlCopyAllDevices();
168 int mtlDeviceCount = CFArrayGetCount(mtlDevices);
171 for (
int i = 0; i < mtlDeviceCount; ++i)
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"));
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");
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");
201 VUserLog(
" Feature set : (unknown)");
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");
214 CFRelease(mtlDevices);
223 if (!(flags & kCGDisplaySetModeFlag))
231 for (
unsigned long i = 0; i < screenCount; ++i)
232 if (screens[i].
id == display)
233 VUserLog(
"Display reconfigured: %s", screens[i].name);
249 VUserLog(
"Couldn't create the key for storing the GL Context state: %s", strerror(errno));
263 CGLContextObj context;
281 VUserLog(
"Error: Couldn't create a context.");
300 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
326 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &textureUnits);
327 for (GLint i=0; i<textureUnits; ++i)
329 glActiveTexture(GL_TEXTURE0+i);
352 VUserLog(
"Error: Disued context %p, which isn't in the global share pool. I'm not going to muddy the waters.", cgl_ctx);
360 #define VuoGlContext_checkGL(cap, value) \
362 if (glIsEnabled(cap) != value) \
364 VUserLog("Warning: Caller incorrectly left %s %s", #cap, value ? "disabled" : "enabled"); \
365 VuoLog_backtrace(); \
372 #define VuoGlContext_checkGLInt(key, value) \
375 glGetIntegerv(key, &actualValue); \
376 if (actualValue != value) \
378 VUserLog("Warning: Caller incorrectly left %s set to something other than %s", #key, #value); \
379 VuoLog_backtrace(); \
399 #pragma clang diagnostic push
400 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
408 if (!alreadyLockedOnThisThread)
445 if (!alreadyLockedOnThisThread)
450 #pragma clang diagnostic pop
458 #pragma clang diagnostic push
459 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
460 static dispatch_once_t info = 0;
461 dispatch_once(&info, ^{
470 CGLPixelFormatObj pf;
471 bool shouldDestroyPixelFormat =
false;
473 pf = CGLGetPixelFormat(rootContext);
476 Boolean overridden =
false;
477 GLint displayMask = (int)CFPreferencesGetAppIntegerValue(CFSTR(
"displayMask"), CFSTR(
"org.vuo.Editor"), &overridden);
481 auto displayMaskString = (CFStringRef)CFPreferencesCopyAppValue(CFSTR(
"displayMask"), CFSTR(
"org.vuo.Editor"));
482 if (displayMaskString)
485 CFRelease(displayMaskString);
487 overridden = sscanf(t,
"0x%x", &displayMask) == 1;
495 std::bitset<32>(displayMask).to_string().c_str(),
496 displayMask, (displayMask & 0xff) == 0xff ?
" (any)" :
"");
499 shouldDestroyPixelFormat =
true;
502 CGLContextObj context;
504 CGLError error = CGLCreateContext(pf, rootContext, &context);
505 if (shouldDestroyPixelFormat)
506 CGLDestroyPixelFormat(pf);
507 if (error != kCGLNoError)
509 VUserLog(
"Error: %s", CGLErrorString(error));
517 CGLGetParameter(context, kCGLCPCurrentRendererID, &rendererID);
518 VUserLog(
"Created OpenGL context %p%s on %s",
520 rootContext ?
VuoText_format(
" (shared with %p)", rootContext) :
" (not shared)",
531 CGLContextObj cgl_ctx = context;
533 glDisable(GL_DEPTH_TEST);
534 glEnable(GL_CULL_FACE);
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);
543 #pragma clang diagnostic pop
560 VUserLog(
"Error: Called after VuoGlContextPool was initialized. Ignoring the new rootContext.");
564 VDebugLog(
"Setting global root context to %p", rootContext);
572 #define VuoGlCheckBinding(pname) \
575 glGetIntegerv(pname, &value); \
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(); \
587 #define VuoGlCheckTextureBinding(pname, unit) \
590 glGetIntegerv(pname, &value); \
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(); \
605 static GLint supportedSamples = 0;
606 static dispatch_once_t multisamplingCheck = 0;
607 dispatch_once(&multisamplingCheck, ^{
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;
615 CGLContextObj cgl_ctx = (CGLContextObj)context;
616 const char *renderer = (
const char *)glGetString(GL_RENDERER);
618 if (rendererID == kCGLRendererIntelHD4000ID
619 || rendererID == 0x00024500
620 || strcmp(renderer,
"NVIDIA GeForce 320M OpenGL Engine") == 0)
621 supportedSamples = 0;
624 glGetIntegerv(GL_MAX_SAMPLES, &supportedSamples);
625 if (supportedSamples == 1)
626 supportedSamples = 0;
629 return supportedSamples;
647 #pragma clang diagnostic push
648 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
650 static dispatch_once_t multisamplingCheck = 0;
651 static int multisample = 0;
652 dispatch_once(&multisamplingCheck, ^{
654 CGLContextObj cgl_ctx;
656 CGLPixelFormatObj pf;
658 CGLPixelFormatAttribute pfa[14] = {
660 kCGLPFAAllowOfflineRenderers,
663 kCGLPFABackingVolatile,
664 kCGLPFAColorSize, (CGLPixelFormatAttribute) 24,
665 kCGLPFADepthSize, (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0),
666 (CGLPixelFormatAttribute) 0
669 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
670 if (error != kCGLNoError)
672 VUserLog(
"Error: %s", CGLErrorString(error));
677 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
678 CGLDestroyPixelFormat(pf);
679 if (error != kCGLNoError)
681 VUserLog(
"Error: %s", CGLErrorString(error));
688 Boolean overridden =
false;
689 multisample = (int)CFPreferencesGetAppIntegerValue(CFSTR(
"multisample"), CFSTR(
"org.vuo.Editor"), &overridden);
696 multisample =
MIN(4, supportedSamples);
699 CGLDestroyContext(cgl_ctx);
703 CGLPixelFormatAttribute pfa[18];
707 if (displayMask == -1)
708 pfa[pfaIndex++] = kCGLPFAAccelerated;
710 pfa[pfaIndex++] = kCGLPFAAllowOfflineRenderers;
715 pfa[pfaIndex++] = kCGLPFABackingVolatile;
717 pfa[pfaIndex++] = kCGLPFAColorSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 24;
718 pfa[pfaIndex++] = kCGLPFADepthSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0);
721 pfa[pfaIndex++] = kCGLPFAOpenGLProfile; pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core;
726 pfa[pfaIndex++] = kCGLPFAMultisample;
727 pfa[pfaIndex++] = kCGLPFASampleBuffers; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 1;
728 pfa[pfaIndex++] = kCGLPFASamples; pfa[pfaIndex++] = (CGLPixelFormatAttribute) multisample;
731 if (displayMask >= 0)
733 pfa[pfaIndex++] = kCGLPFADisplayMask;
734 pfa[pfaIndex++] = (CGLPixelFormatAttribute) displayMask;
738 if ((displayMask & 0xff) == 0xff && displayMask != -1)
740 pfa[pfaIndex++] = kCGLPFARendererID;
741 pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLRendererGenericFloatID;
744 pfa[pfaIndex] = (CGLPixelFormatAttribute) 0;
746 CGLPixelFormatObj pf;
748 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
749 if (error == kCGLBadDisplay)
750 return (CGLPixelFormatObj)-1;
751 else if (error != kCGLNoError)
753 VUserLog(
"Error: %s", CGLErrorString(error));
758 #pragma clang diagnostic pop
767 CGLContextObj cgl_ctx = (CGLContextObj)context;
774 const unsigned char *contextVersion = glGetString(GL_VERSION);
775 return contextVersion[0] ==
'3'
776 || contextVersion[0] ==
'4';
780 void _VGL_describe(GLenum error, CGLContextObj cgl_ctx,
const char *file,
const unsigned int linenumber,
const char *func);
785 void _VGL(CGLContextObj cgl_ctx,
const char *file,
const unsigned int linenumber,
const char *func)
787 #pragma clang diagnostic push
788 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
789 GLint vertexOnGPU, fragmentOnGPU;
790 CGLGetParameter(cgl_ctx, kCGLCPGPUVertexProcessing, &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);
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
798 bool foundError =
false;
801 GLenum error = glGetError();
802 if (error == GL_NO_ERROR)
804 _VGL_describe(error, cgl_ctx, file, linenumber, func);
815 void _VGL_describe(GLenum error, CGLContextObj cgl_ctx,
const char *file,
const unsigned int linenumber,
const char *func)
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)
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);
830 GLenum framebufferError = glCheckFramebufferStatus(GL_FRAMEBUFFER);
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.)";
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);
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.)";
856 VuoLog(file, linenumber, func,
"OpenGL error %d: %s", error, errorString);