Vuo 2.4.4
Loading...
Searching...
No Matches
VuoGlContext.cc
Go to the documentation of this file.
1
10#include <vector>
11#include <algorithm>
12#include <bitset>
13#include <map>
14using namespace std;
15
16#include "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
31static CGLContextObj VuoGlContext_create(CGLContextObj rootContext);
32dispatch_semaphore_t VuoGlContext_poolSemaphore;
33static CGLContextObj VuoGlContext_root = NULL;
34vector<CGLContextObj> VuoGlContext_allSharedContexts;
36
37static dispatch_once_t VuoGlContextPoolCreated = 0;
38static pthread_key_t VuoGlContextPerformKey;
39
40static bool VuoGlContext_infoLogging = true;
41
47{
49}
50
54static void VuoGlContext_renderers(void)
55{
57 return;
58
60 unsigned long screenCount = VuoListGetCount_VuoScreen(screensList);
61 VuoScreen *screens = VuoListGetData_VuoScreen(screensList);
62 VuoLocal(screensList);
63
64#pragma clang diagnostic push
65#pragma clang diagnostic ignored "-Wdeprecated-declarations"
66 // https://developer.apple.com/library/mac/qa/qa1168/_index.html says:
67 // "If you are looking for the VRAM sizes of all the renderers on your system […]
68 // you may specify a -1/0xFFFFFFFF display mask in the CGLQueryRendererInfo() function.
69 CGLRendererInfoObj ri;
70 GLint rendererCount = 0;
71 CGLQueryRendererInfo(-1, &ri, &rendererCount);
72 for (int i = 0; i < rendererCount; ++i)
73 {
74 GLint rendererID;
75 CGLDescribeRenderer(ri, i, kCGLRPRendererID, &rendererID);
76 VUserLog("Renderer %d: %s", i, VuoCglRenderer_getText(rendererID));
77
78 GLint online;
79 if (CGLDescribeRenderer(ri, i, kCGLRPOnline, &online) == kCGLNoError)
80 VUserLog(" Online : %s", online ? "yes" : "no");
81
82 GLint accelerated;
83 if (CGLDescribeRenderer(ri, i, kCGLRPAccelerated, &accelerated) == kCGLNoError)
84 VUserLog(" Accelerated : %s", accelerated ? "yes" : "no");
85
86 GLint videoMegabytes = 0;
87 if (CGLDescribeRenderer(ri, i, kCGLRPVideoMemoryMegabytes, &videoMegabytes) == kCGLNoError
88 && videoMegabytes)
89 VUserLog(" Video memory : %d MB", videoMegabytes);
90
91 GLint textureMegabytes = 0;
92 if (CGLDescribeRenderer(ri, i, kCGLRPTextureMemoryMegabytes, &textureMegabytes) == kCGLNoError
93 && textureMegabytes)
94 VUserLog(" Texture memory : %d MB", textureMegabytes);
95
96 GLint displayMask;
97 if (CGLDescribeRenderer(ri, i, kCGLRPDisplayMask, &displayMask) == kCGLNoError)
98 {
99 VUserLog(" Display mask : %s (0x%x)%s",
100 std::bitset<32>(displayMask).to_string().c_str(),
101 displayMask, (displayMask & 0xff) == 0xff ? " (any)" : "");
102 if ((displayMask & 0xff) != 0xff)
103 for (unsigned long i = 0; i < screenCount; ++i)
104 if (displayMask & screens[i].displayMask)
105 VUserLog(" %s %s",
106 std::bitset<32>(screens[i].displayMask).to_string().c_str(),
107 screens[i].name);
108 }
109
110 GLint glVersion = 0;
111 if (CGLDescribeRenderer(ri, i, /*kCGLRPMajorGLVersion*/ (CGLRendererProperty)133, &glVersion) == kCGLNoError)
112 VUserLog(" OpenGL version : %d", glVersion);
113
114 CGLPixelFormatObj pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(false, false, displayMask);
115 if (pf != (CGLPixelFormatObj)-1)
116 {
117 CGLContextObj cgl_ctx;
118 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
119 if (error != kCGLNoError)
120 VUserLog(" Error: %s", CGLErrorString(error));
121 else
122 {
123 GLint maxTextureSize;
124 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
125 VUserLog(" OpenGL 2 : %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
126 CGLDestroyContext(cgl_ctx);
127 }
128 }
129 else
130 VUserLog(" (Can't create an OpenGL 2 context on this renderer.)");
131
132 pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(false, true, displayMask);
133 if (pf != (CGLPixelFormatObj)-1)
134 {
135 CGLContextObj cgl_ctx;
136 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
137 if (error != kCGLNoError)
138 VUserLog(" Error: %s", CGLErrorString(error));
139 else
140 {
141 GLint maxTextureSize;
142 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
143 VUserLog(" OpenGL Core Profile: %s (%s) maxTextureSize=%d", glGetString(GL_RENDERER), glGetString(GL_VERSION), maxTextureSize);
144 CGLDestroyContext(cgl_ctx);
145 }
146 }
147 else
148 VUserLog(" (Can't create an OpenGL Core Profile context on this renderer.)");
149 }
150 CGLDestroyRendererInfo(ri);
151#pragma clang diagnostic pop
152
153
154 typedef void *(*mtlCopyAllDevicesType)(void);
155 mtlCopyAllDevicesType mtlCopyAllDevices = (mtlCopyAllDevicesType)dlsym(RTLD_DEFAULT, "MTLCopyAllDevices");
156 if (mtlCopyAllDevices)
157 {
158 CFArrayRef mtlDevices = (CFArrayRef)mtlCopyAllDevices();
159 int mtlDeviceCount = CFArrayGetCount(mtlDevices);
160 if (mtlDeviceCount)
161 VUserLog("Metal devices:");
162 for (int i = 0; i < mtlDeviceCount; ++i)
163 {
164 id dev = (id)CFArrayGetValueAtIndex(mtlDevices, i);
165 id devName = ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("name"));
166 const char *devNameZ = ((char * (*)(id, SEL))objc_msgSend)(devName, sel_getUid("UTF8String"));
167 VUserLog(" %s (%s):", devNameZ, class_getName(object_getClass(dev)));
168 if (class_respondsToSelector(object_getClass(dev), sel_getUid("registryID")))
169 VUserLog(" ID : %p", ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("registryID")));
170 if (class_respondsToSelector(object_getClass(dev), sel_getUid("location")))
171 {
172 unsigned long location = ((unsigned long (*)(id, SEL))objc_msgSend)(dev, sel_getUid("location"));
173 VUserLog(" Location : %s (ID %lu)",
174 location == 0 ? "built-in" : (location == 1 ? "slot" : (location == 2 ? "external" : "unspecified")),
175 ((unsigned long (*)(id, SEL))objc_msgSend)(dev, sel_getUid("locationNumber")));
176 }
177 if (class_respondsToSelector(object_getClass(dev), sel_getUid("peerGroupID")))
178 VUserLog(" Peer group : %llu (ID %u of %u)",
179 ((uint64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("peerGroupID")),
180 ((uint32_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("peerIndex")),
181 ((uint32_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("peerCount")));
182
183 if (class_respondsToSelector(object_getClass(dev), sel_getUid("recommendedMaxWorkingSetSize")))
184 VUserLog(" Recommended max working-set size : %lld MiB", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("recommendedMaxWorkingSetSize"))/1048576);
185 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxBufferLength")))
186 VUserLog(" Max buffer length : %lld MiB", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxBufferLength"))/1048576);
187 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxThreadgroupMemoryLength")))
188 VUserLog(" Threadgroup memory : %lld B", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxThreadgroupMemoryLength")));
189 if (class_respondsToSelector(object_getClass(dev), sel_getUid("sparseTileSizeInBytes")))
190 VUserLog(" Sparse tile size : %llu B", ((uint64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("sparseTileSizeInBytes")));
191
192 VUserLog(" Low-power : %s", ((bool (*)(id, SEL))objc_msgSend) ? "yes" : "no");
193 if (class_respondsToSelector(object_getClass(dev), sel_getUid("isRemovable")))
194 VUserLog(" Removable : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("isRemovable")) ? "yes" : "no");
195 if (class_respondsToSelector(object_getClass(dev), sel_getUid("hasUnifiedMemory")))
196 VUserLog(" Unified memory : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("hasUnifiedMemory")) ? "yes" : "no");
197 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxTransferRate")))
198 {
199 uint64_t maxTransferRate = ((uint64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxTransferRate"));
200 if (maxTransferRate)
201 VUserLog(" Max CPU-GPU RAM transfer rate : %llu MiB/sec", maxTransferRate/1024/1024);
202 }
203 VUserLog(" Headless : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("isHeadless")) ? "yes" : "no");
204
205 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10005))
206 VUserLog(" Feature set : GPU Family 2 v1");
207 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10004))
208 VUserLog(" Feature set : GPU Family 1 v4");
209 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10003))
210 VUserLog(" Feature set : GPU Family 1 v3");
211 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10001))
212 VUserLog(" Feature set : GPU Family 1 v2");
213 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFeatureSet:"), 10000))
214 VUserLog(" Feature set : GPU Family 1 v1");
215 else
216 VUserLog(" Feature set : (unknown)");
217
218 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsFamily:")))
219 {
220 // "A higher GPU version is always a superset of an earlier version in the same GPU family."
221 // https://developer.apple.com/documentation/metal/mtldevice/detecting_gpu_features_and_metal_software_versions
222 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1008))
223 VUserLog(" Family : Apple 8");
224 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1007))
225 VUserLog(" Family : Apple 7");
226 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1006))
227 VUserLog(" Family : Apple 6");
228 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1005))
229 VUserLog(" Family : Apple 5");
230 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1004))
231 VUserLog(" Family : Apple 4");
232 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1003))
233 VUserLog(" Family : Apple 3");
234 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1002))
235 VUserLog(" Family : Apple 2");
236 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 1001))
237 VUserLog(" Family : Apple 1");
238
239 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 2002))
240 VUserLog(" Family : Mac 2");
241 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 2001))
242 VUserLog(" Family : Mac 1");
243
244 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 3003))
245 VUserLog(" Family : Common 3");
246 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 3002))
247 VUserLog(" Family : Common 2");
248 else if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 3001))
249 VUserLog(" Family : Common 1");
250
251 if (((bool (*)(id, SEL, int))objc_msgSend)(dev, sel_getUid("supportsFamily:"), 5001)) // MTLGPUFamilyMetal3
252 VUserLog(" Family : Metal 3");
253 }
254
255 if (class_respondsToSelector(object_getClass(dev), sel_getUid("readWriteTextureSupport")))
256 VUserLog(" Read-write texture support tier : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("readWriteTextureSupport")));
257 if (class_respondsToSelector(object_getClass(dev), sel_getUid("argumentBuffersSupport")))
258 VUserLog(" Argument buffer support tier : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("argumentBuffersSupport")));
259 if (class_respondsToSelector(object_getClass(dev), sel_getUid("maxArgumentBufferSamplerCount")))
260 VUserLog(" Max argument buffers : %lld", ((int64_t (*)(id, SEL))objc_msgSend)(dev, sel_getUid("maxArgumentBufferSamplerCount")));
261 if (class_respondsToSelector(object_getClass(dev), sel_getUid("areProgrammableSamplePositionsSupported")))
262 VUserLog(" Programmable sample position support: %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("areProgrammableSamplePositionsSupported")) ? "yes" : "no");
263 if (class_respondsToSelector(object_getClass(dev), sel_getUid("areRasterOrderGroupsSupported")))
264 VUserLog(" Raster order group support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("areRasterOrderGroupsSupported")) ? "yes" : "no");
265
266 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsDynamicLibraries")))
267 VUserLog(" Dynamic library support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsDynamicLibraries")) ? "yes" : "no");
268 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsRenderDynamicLibraries")))
269 VUserLog(" Dynamic render library support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsRenderDynamicLibraries")) ? "yes" : "no");
270 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsFunctionPointers")))
271 VUserLog(" Function pointer support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsFunctionPointers")) ? "yes" : "no");
272 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsFunctionPointersFromRender")))
273 VUserLog(" Render function pointer support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsFunctionPointersFromRender")) ? "yes" : "no");
274 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsRaytracing")))
275 VUserLog(" Raytracing support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsRaytracing")) ? "yes" : "no");
276 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsRaytracingFromRender")))
277 VUserLog(" Render raytracing support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsRaytracingFromRender")) ? "yes" : "no");
278 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsShaderBarycentricCoordinates")))
279 VUserLog(" Barycentric support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsShaderBarycentricCoordinates")) ? "yes" : "no");
280 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsPrimitiveMotionBlur")))
281 VUserLog(" Motion blur support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsPrimitiveMotionBlur")) ? "yes" : "no");
282 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsPullModelInterpolation")))
283 VUserLog(" Multiple interpolation support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsPullModelInterpolation")) ? "yes" : "no");
284 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supports32BitFloatFiltering")))
285 VUserLog(" 32-bit float support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supports32BitFloatFiltering")) ? "yes" : "no");
286 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supports32BitMSAA")))
287 VUserLog(" 32-bit MSAA support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supports32BitMSAA")) ? "yes" : "no");
288 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsQueryTextureLOD")))
289 VUserLog(" LOD query support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsQueryTextureLOD")) ? "yes" : "no");
290 if (class_respondsToSelector(object_getClass(dev), sel_getUid("supportsBCTextureCompression")))
291 VUserLog(" BC texture support : %s", ((bool (*)(id, SEL))objc_msgSend)(dev, sel_getUid("supportsBCTextureCompression")) ? "yes" : "no");
292
293 if (class_respondsToSelector(object_getClass(dev), sel_getUid("sparseTileSizeInBytesForSparsePageSize:")))
294 {
295 VUserLog(" 16-byte sparse page tile size : %llu", ((uint64_t (*)(id, SEL, uint64_t))objc_msgSend)(dev, sel_getUid("sparseTileSizeInBytesForSparsePageSize:"), 101));
296 VUserLog(" 64-byte sparse page tile size : %llu", ((uint64_t (*)(id, SEL, uint64_t))objc_msgSend)(dev, sel_getUid("sparseTileSizeInBytesForSparsePageSize:"), 102));
297 VUserLog(" 256-byte sparse page tile size : %llu", ((uint64_t (*)(id, SEL, uint64_t))objc_msgSend)(dev, sel_getUid("sparseTileSizeInBytesForSparsePageSize:"), 103));
298 }
299
300 if (class_respondsToSelector(object_getClass(dev), sel_getUid("counterSets")))
301 {
302 id counterSets = ((id (*)(id, SEL))objc_msgSend)(dev, sel_getUid("counterSets"));
303 id description = ((id (*)(id, SEL))objc_msgSend)(counterSets, sel_getUid("description"));
304 if (description)
305 {
306 string descriptionS{((const char * (*)(id, SEL))objc_msgSend)(description, sel_getUid("UTF8String"))};
307 std::replace(descriptionS.begin(), descriptionS.end(), '\n', ' ');
308 VUserLog(" Counters : %s", descriptionS.c_str());
309 }
310 }
311 }
312 CFRelease(mtlDevices);
313 }
314}
315
319 static void VuoGlContext_init()
320 {
321 dispatch_once(&VuoGlContextPoolCreated, ^{
322 VuoGlContext_poolSemaphore = dispatch_semaphore_create(1);
323
326
327 int ret = pthread_key_create(&VuoGlContextPerformKey, NULL);
328 if (ret)
329 VUserLog("Couldn't create the key for storing the GL Context state: %s", strerror(errno));
330 });
331 }
332
340 {
342
343 CGLContextObj context;
344
345 dispatch_semaphore_wait(VuoGlContext_poolSemaphore, DISPATCH_TIME_FOREVER);
346 {
347// VL();
348// VuoLog_backtrace();
349
351 {
354// VLog("Found existing context %p.", context);
355 }
356 else
357 {
359 if (!context)
360 {
361 VUserLog("Error: Couldn't create a context.");
362 return NULL;
363 }
364 VuoGlContext_allSharedContexts.push_back(context);
365// VLog("Created context %p.", context);
366 }
367 }
368 dispatch_semaphore_signal(VuoGlContext_poolSemaphore);
369
370 return context;
371 }
372
378 void VuoGlContext_disuseF(VuoGlContext glContext, const char *file, const unsigned int linenumber, const char *func)
379 {
380 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
381// VLog("%p", context);
382
383#if 0
384 VGL();
385
386 // Check whether there are any stale bindings (to help identify leaks).
387 // (Some checks are commented because they aren't available in OpenGL 2.1.)
388 VuoGlCheckBinding(GL_ARRAY_BUFFER_BINDING);
389// VuoGlCheckBinding(GL_ATOMIC_COUNTER_BUFFER_BINDING);
390// VuoGlCheckBinding(GL_COPY_READ_BUFFER_BINDING);
391// VuoGlCheckBinding(GL_COPY_WRITE_BUFFER_BINDING);
392// VuoGlCheckBinding(GL_DRAW_INDIRECT_BUFFER_BINDING);
393// VuoGlCheckBinding(GL_DISPATCH_INDIRECT_BUFFER_BINDING);
394 VuoGlCheckBinding(GL_DRAW_FRAMEBUFFER_BINDING);
395 VuoGlCheckBinding(GL_ELEMENT_ARRAY_BUFFER_BINDING);
396 VuoGlCheckBinding(GL_FRAMEBUFFER_BINDING);
397 VuoGlCheckBinding(GL_PIXEL_PACK_BUFFER_BINDING);
398 VuoGlCheckBinding(GL_PIXEL_UNPACK_BUFFER_BINDING);
399// VuoGlCheckBinding(GL_PROGRAM_PIPELINE_BINDING);
400 VuoGlCheckBinding(GL_READ_FRAMEBUFFER_BINDING);
401 VuoGlCheckBinding(GL_RENDERBUFFER_BINDING);
402// VuoGlCheckBinding(GL_SAMPLER_BINDING);
403// VuoGlCheckBinding(GL_SHADER_STORAGE_BUFFER_BINDING);
404
405 GLint textureUnits;
406 glGetIntegerv(GL_MAX_TEXTURE_UNITS, &textureUnits);
407 for (GLint i=0; i<textureUnits; ++i)
408 {
409 glActiveTexture(GL_TEXTURE0+i);
410 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_1D,i);
411 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_1D_ARRAY_EXT,i);
412 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D,i);
413 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_ARRAY_EXT,i);
414// VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_MULTISAMPLE,i);
415// VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_2D_MULTISAMPLE_ARRAY,i);
416 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_3D,i);
417// VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_BUFFER,i);
418 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_CUBE_MAP,i);
419 VuoGlCheckTextureBinding(GL_TEXTURE_BINDING_RECTANGLE_ARB,i);
420 }
421
422 VuoGlCheckBinding(GL_TRANSFORM_FEEDBACK_BUFFER_BINDING_EXT);
423 VuoGlCheckBinding(GL_UNIFORM_BUFFER_BINDING_EXT);
424 VuoGlCheckBinding(GL_VERTEX_ARRAY_BINDING_APPLE);
425#endif
426
427 dispatch_semaphore_wait(VuoGlContext_poolSemaphore, DISPATCH_TIME_FOREVER);
428 {
430 VuoGlContext_avaialbleSharedContexts.push_back(cgl_ctx);
431 else
432 VUserLog("Error: Disued context %p, which isn't in the global share pool. I'm not going to muddy the waters.", cgl_ctx);
433 }
434 dispatch_semaphore_signal(VuoGlContext_poolSemaphore);
435 }
436
440 #define VuoGlContext_checkGL(cap, value) \
441 do { \
442 if (glIsEnabled(cap) != value) \
443 { \
444 VUserLog("Warning: Caller incorrectly left %s %s", #cap, value ? "disabled" : "enabled"); \
445 VuoLog_backtrace(); \
446 } \
447 } while (0)
448
452 #define VuoGlContext_checkGLInt(key, value) \
453 do { \
454 GLint actualValue; \
455 glGetIntegerv(key, &actualValue); \
456 if (actualValue != value) \
457 { \
458 VUserLog("Warning: Caller incorrectly left %s set to something other than %s", #key, #value); \
459 VuoLog_backtrace(); \
460 } \
461 } while (0)
462
463
464
477 void VuoGlContext_perform(void (^function)(CGLContextObj cgl_ctx))
478 {
479#pragma clang diagnostic push
480#pragma clang diagnostic ignored "-Wdeprecated-declarations"
481 if (!function)
482 return;
483
485
486 bool alreadyLockedOnThisThread = (bool)pthread_getspecific(VuoGlContextPerformKey);
487
488 if (!alreadyLockedOnThisThread)
489 {
490 pthread_setspecific(VuoGlContextPerformKey, (void *)true);
491 CGLLockContext(VuoGlContext_root);
492 }
493
494 function(VuoGlContext_root);
495
496 // Ensure that `function` restored the standard OpenGL state.
497 if (!alreadyLockedOnThisThread && VuoIsDebugEnabled())
498 {
499 CGLContextObj cgl_ctx = VuoGlContext_root;
500
501 VGL();
502
503 VuoGlContext_checkGLInt(GL_DEPTH_WRITEMASK, true);
504 VuoGlContext_checkGL (GL_DEPTH_TEST, false);
505
506 VuoGlContext_checkGL (GL_CULL_FACE, true);
507 VuoGlContext_checkGLInt(GL_CULL_FACE_MODE, GL_BACK);
508
509 VuoGlContext_checkGL (GL_BLEND, true);
510
511 VuoGlContext_checkGLInt(GL_BLEND_SRC_RGB, GL_ONE);
512 VuoGlContext_checkGLInt(GL_BLEND_DST_RGB, GL_ONE_MINUS_SRC_ALPHA);
513 VuoGlContext_checkGLInt(GL_BLEND_SRC_ALPHA, GL_ONE);
514 VuoGlContext_checkGLInt(GL_BLEND_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
515
516 VuoGlContext_checkGLInt(GL_BLEND_EQUATION_RGB, GL_FUNC_ADD);
517 VuoGlContext_checkGLInt(GL_BLEND_EQUATION_ALPHA, GL_FUNC_ADD);
518
519 VuoGlContext_checkGL(GL_SAMPLE_ALPHA_TO_COVERAGE, false);
520 VuoGlContext_checkGL(GL_SAMPLE_ALPHA_TO_ONE, false);
521
523 }
524
525 if (!alreadyLockedOnThisThread)
526 {
527 CGLUnlockContext(VuoGlContext_root);
528 pthread_setspecific(VuoGlContextPerformKey, (void *)false);
529 }
530#pragma clang diagnostic pop
531 }
532
536 static CGLContextObj VuoGlContext_create(CGLContextObj rootContext)
537 {
538#pragma clang diagnostic push
539#pragma clang diagnostic ignored "-Wdeprecated-declarations"
540 static dispatch_once_t info = 0;
541 dispatch_once(&info, ^{
543
544 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
545 // Start logging display changes.
547 });
548 });
549
550 CGLPixelFormatObj pf;
551 bool shouldDestroyPixelFormat = false;
552 GLint displayMask;
553 if (rootContext)
554 pf = CGLGetPixelFormat(rootContext);
555 else
556 {
557 Boolean overridden = false;
558 displayMask = (int)CFPreferencesGetAppIntegerValue(CFSTR("displayMask"), CFSTR("org.vuo.Editor"), &overridden);
559 if (!overridden)
560 {
561 // Maybe the preference is a hex string…
562 auto displayMaskString = (CFStringRef)CFPreferencesCopyAppValue(CFSTR("displayMask"), CFSTR("org.vuo.Editor"));
563 if (displayMaskString)
564 {
565 VuoText t = VuoText_makeFromCFString(displayMaskString);
566 CFRelease(displayMaskString);
567 VuoLocal(t);
568 overridden = sscanf(t, "0x%x", &displayMask) == 1;
569 }
570 if (!overridden)
571 // Still no preference, so let macOS automatically choose what it thinks is the best GPU.
572 displayMask = -1;
573 }
574
575 pf = (CGLPixelFormatObj)VuoGlContext_makePlatformPixelFormat(true, false, displayMask);
576 shouldDestroyPixelFormat = true;
577 }
578
579 CGLContextObj context;
580 {
581 CGLError error = CGLCreateContext(pf, rootContext, &context);
582 if (shouldDestroyPixelFormat)
583 CGLDestroyPixelFormat(pf);
584 if (error != kCGLNoError)
585 {
586 VUserLog("Error: %s", CGLErrorString(error));
587 return NULL;
588 }
589 }
590
592 {
593 GLint rendererID;
594 CGLGetParameter(context, kCGLCPCurrentRendererID, &rendererID);
595 char *sharingText = rootContext ? VuoText_format(" (shared with %p)", rootContext) : strdup(" (not shared)");
596 char *displayMaskText = rootContext ? nullptr : (displayMask == -1 ? strdup(" (macOS default)") : VuoText_format(" (selected using displayMask %s)", std::bitset<32>(displayMask).to_string().c_str()));
597 VUserLog("Created OpenGL context %p%s on %s%s", context, sharingText, VuoCglRenderer_getText(rendererID), displayMaskText);
598 free(sharingText);
599 free(displayMaskText);
600 }
601
602 // https://developer.apple.com/library/content/technotes/tn2085/_index.html
603 // But it doesn't seem to actually improve performance any on the various workloads I tried.
604// CGLEnable(context, kCGLCEMPEngine);
605
606 // Set the context's default state to commonly-used values,
607 // to hopefully minimize subsequent state changes.
608 {
609 CGLContextObj cgl_ctx = context;
610 glDepthMask(true);
611 glDisable(GL_DEPTH_TEST);
612 glEnable(GL_CULL_FACE);
613 glEnable(GL_BLEND);
614 glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
615 glBlendEquation(GL_FUNC_ADD);
616 glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST);
617 glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -.5);
618 }
619
620 return context;
621#pragma clang diagnostic pop
622 }
623
635{
637 {
638 VUserLog("Error: Called after VuoGlContextPool was initialized. Ignoring the new rootContext.");
639 return;
640 }
641
642 VUserLog("Setting global root context to %p", rootContext);
643 VuoGlContext_root = VuoGlContext_create((CGLContextObj)rootContext);
644}
645
650#define VuoGlCheckBinding(pname) \
651{ \
652 GLint value; \
653 glGetIntegerv(pname, &value); \
654 if (value) \
655 { \
656 VuoLog(VuoLog_moduleName, file, linenumber, func, #pname " (value %d) was still active when the context was disused. (This may result in leaks.)", value); \
657 VuoLog_backtrace(); \
658 } \
659}
660
665#define VuoGlCheckTextureBinding(pname, unit) \
666{ \
667 GLint value; \
668 glGetIntegerv(pname, &value); \
669 if (value) \
670 { \
671 VuoLog(VuoLog_moduleName, file, linenumber, func, #pname " (texture %d on unit %d) was still active when the context was disused. (This may result in leaks.)", value, unit); \
672 VuoLog_backtrace(); \
673 } \
674}
675
682{
683 static GLint supportedSamples = 0;
684 static dispatch_once_t multisamplingCheck = 0;
685 dispatch_once(&multisamplingCheck, ^{
686 GLint rendererID;
687#pragma clang diagnostic push
688#pragma clang diagnostic ignored "-Wdeprecated-declarations"
689 CGLGetParameter((CGLContextObj)context, kCGLCPCurrentRendererID, &rendererID);
690#pragma clang diagnostic pop
691 rendererID &= kCGLRendererIDMatchingMask;
692
693 CGLContextObj cgl_ctx = (CGLContextObj)context;
694 const char *renderer = (const char *)glGetString(GL_RENDERER);
695
696 if (rendererID == kCGLRendererIntelHD4000ID // https://b33p.net/kosada/node/8225#comment-31324
697 || rendererID == /*kCGLRendererIntelHD5000ID*/ 0x00024500 // https://b33p.net/kosada/node/10595
698 || strcmp(renderer, "NVIDIA GeForce 320M OpenGL Engine") == 0) // https://b33p.net/kosada/node/13477
699 supportedSamples = 0;
700 else
701 {
702 glGetIntegerv(GL_MAX_SAMPLES, &supportedSamples);
703 if (supportedSamples == 1)
704 supportedSamples = 0;
705 }
706 });
707 return supportedSamples;
708}
709
723void *VuoGlContext_makePlatformPixelFormat(bool hasDepthBuffer, bool openGL32Core, GLint displayMask)
724{
725#pragma clang diagnostic push
726#pragma clang diagnostic ignored "-Wdeprecated-declarations"
727 // Check whether it's OK to use multisampling on this GPU.
728 static dispatch_once_t multisamplingCheck = 0;
729 static int multisample = 0;
730 dispatch_once(&multisamplingCheck, ^{
731 // Create a temporary context so we can get the GPU renderer string.
732 CGLContextObj cgl_ctx;
733 {
734 CGLPixelFormatObj pf;
735 {
736 CGLPixelFormatAttribute pfa[14] = {
737 kCGLPFAAccelerated,
738 kCGLPFAAllowOfflineRenderers,
739// kCGLPFANoRecovery,
740// kCGLPFADoubleBuffer,
741 kCGLPFABackingVolatile,
742 kCGLPFAColorSize, (CGLPixelFormatAttribute) 24,
743 kCGLPFADepthSize, (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0),
744 (CGLPixelFormatAttribute) 0
745 };
746 GLint npix;
747 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
748 if (error != kCGLNoError)
749 {
750 VUserLog("Error: %s", CGLErrorString(error));
751 return;
752 }
753 }
754
755 CGLError error = CGLCreateContext(pf, NULL, &cgl_ctx);
756 CGLDestroyPixelFormat(pf);
757 if (error != kCGLNoError)
758 {
759 VUserLog("Error: %s", CGLErrorString(error));
760 return;
761 }
762 }
763
764
765 // If the user set the `multisample` preference, use it.
766 Boolean overridden = false;
767 multisample = (int)CFPreferencesGetAppIntegerValue(CFSTR("multisample"), CFSTR("org.vuo.Editor"), &overridden);
768
769 if (!overridden)
770 {
771 // …otherwise enable 4x multisampling (unless there's a known problem with this GPU model).
772
773 int supportedSamples = VuoGlContext_getMaximumSupportedMultisampling(cgl_ctx);
774 multisample = MIN(4, supportedSamples);
775 }
776
777 CGLDestroyContext(cgl_ctx);
778 });
779
780
781 CGLPixelFormatAttribute pfa[18];
782 int pfaIndex = 0;
783
784 // If requesting a specific display, don't require acceleration.
785 if (displayMask == -1)
786 pfa[pfaIndex++] = kCGLPFAAccelerated;
787
788 pfa[pfaIndex++] = kCGLPFAAllowOfflineRenderers;
789// pfa[pfaIndex++] = kCGLPFANoRecovery;
790
791 // https://b33p.net/kosada/node/12525
792// pfa[pfaIndex++] = kCGLPFADoubleBuffer;
793 pfa[pfaIndex++] = kCGLPFABackingVolatile;
794
795 pfa[pfaIndex++] = kCGLPFAColorSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 24;
796 pfa[pfaIndex++] = kCGLPFADepthSize; pfa[pfaIndex++] = (CGLPixelFormatAttribute) (hasDepthBuffer ? 16 : 0);
797 if (openGL32Core)
798 {
799 pfa[pfaIndex++] = kCGLPFAOpenGLProfile; pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLOGLPVersion_3_2_Core;
800 }
801
802 if (multisample)
803 {
804 pfa[pfaIndex++] = kCGLPFAMultisample;
805 pfa[pfaIndex++] = kCGLPFASampleBuffers; pfa[pfaIndex++] = (CGLPixelFormatAttribute) 1;
806 pfa[pfaIndex++] = kCGLPFASamples; pfa[pfaIndex++] = (CGLPixelFormatAttribute) multisample;
807 }
808
809 if (displayMask >= 0)
810 {
811 pfa[pfaIndex++] = kCGLPFADisplayMask;
812 pfa[pfaIndex++] = (CGLPixelFormatAttribute) displayMask;
813 }
814
815 // software renderer
816 if ((displayMask & 0xff) == 0xff && displayMask != -1)
817 {
818 pfa[pfaIndex++] = kCGLPFARendererID;
819 pfa[pfaIndex++] = (CGLPixelFormatAttribute) kCGLRendererGenericFloatID;
820 }
821
822 pfa[pfaIndex] = (CGLPixelFormatAttribute) 0;
823
824 CGLPixelFormatObj pf;
825 GLint npix;
826 CGLError error = CGLChoosePixelFormat(pfa, &pf, &npix);
827 if (error == kCGLBadDisplay)
828 return (CGLPixelFormatObj)-1;
829 else if (error != kCGLNoError)
830 {
831 VUserLog("Error: %s", CGLErrorString(error));
832 return NULL;
833 }
834
835 return (void *)pf;
836#pragma clang diagnostic pop
837}
838
844{
845 CGLContextObj cgl_ctx = (CGLContextObj)context;
846
847 // GL_VERSION is something like:
848 // - Core Profile: `4.1 NVIDIA-10.17.5 355.10.05.45f01`
849 // - Core Profile: `4.1 ATI-2.4.10`
850 // - Compatibility Profile: `2.1 NVIDIA-10.17.5 355.10.05.45f01`
851 // - Compatibility Profile: `2.1 ATI-2.4.10`
852 const unsigned char *contextVersion = glGetString(GL_VERSION);
853 return contextVersion[0] == '3'
854 || contextVersion[0] == '4';
855}
856
857
858void _VGL_describe(GLenum error, CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func);
859
863void _VGL(CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func)
864{
865#pragma clang diagnostic push
866#pragma clang diagnostic ignored "-Wdeprecated-declarations"
867 GLint vertexOnGPU, fragmentOnGPU;
868 CGLGetParameter(cgl_ctx, kCGLCPGPUVertexProcessing, &vertexOnGPU);
869 if (!vertexOnGPU)
870 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL warning: Falling back to software renderer for vertex shader. This will slow things down.");
871 CGLGetParameter(cgl_ctx, kCGLCPGPUFragmentProcessing, &fragmentOnGPU);
872 if (!fragmentOnGPU)
873 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL warning: Falling back to software renderer for fragment shader. This will slow things down.");
874#pragma clang diagnostic pop
875
876 bool foundError = false;
877 do
878 {
879 GLenum error = glGetError();
880 if (error == GL_NO_ERROR)
881 break;
882 _VGL_describe(error, cgl_ctx, file, linenumber, func);
883 foundError = true;
884 } while (true);
885
886 if (foundError)
888}
889
893void _VGL_describe(GLenum error, CGLContextObj cgl_ctx, const char *file, const unsigned int linenumber, const char *func)
894{
895 // Text from http://www.opengl.org/sdk/docs/man/xhtml/glGetError.xml
896 const char *errorString = "(unknown)";
897 if (error == GL_INVALID_ENUM)
898 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.)";
899 else if (error == GL_INVALID_VALUE)
900 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.)";
901 else if (error == GL_INVALID_OPERATION)
902 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.)";
903 else if (error == GL_INVALID_FRAMEBUFFER_OPERATION)
904 {
905 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.)";
906 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL error %d: %s", error, errorString);
907
908 GLenum framebufferError = glCheckFramebufferStatus(GL_FRAMEBUFFER);
909 // Text from http://www.khronos.org/opengles/sdk/docs/man/xhtml/glCheckFramebufferStatus.xml
910 const char *framebufferErrorString = "(unknown)";
911 if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT)
912 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.)";
913// else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS)
914// framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS (Not all attached images have the same width and height.)";
915 else if (framebufferError == GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT)
916 framebufferErrorString = "GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT (No images are attached to the framebuffer.)";
917 else if (framebufferError == GL_FRAMEBUFFER_UNSUPPORTED)
918 framebufferErrorString = "GL_FRAMEBUFFER_UNSUPPORTED (The combination of internal formats of the attached images violates an implementation-dependent set of restrictions.)";
919 else if (framebufferError == GL_FRAMEBUFFER_COMPLETE)
920 framebufferErrorString = "GL_FRAMEBUFFER_COMPLETE (?)";
921 else if (framebufferError == GL_FRAMEBUFFER_UNDEFINED)
922 framebufferErrorString = "GL_FRAMEBUFFER_UNDEFINED";
923 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL framebuffer error %d: %s", framebufferError, framebufferErrorString);
924
925 return;
926 }
927 else if (error == GL_OUT_OF_MEMORY)
928 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.)";
929 else if (error == GL_STACK_UNDERFLOW)
930 errorString = "GL_STACK_UNDERFLOW (An attempt has been made to perform an operation that would cause an internal stack to underflow.)";
931 else if (error == GL_STACK_OVERFLOW)
932 errorString = "GL_STACK_OVERFLOW (An attempt has been made to perform an operation that would cause an internal stack to overflow.)";
933
934 VuoLog(VuoLog_moduleName, file, linenumber, func, "OpenGL error %d: %s", error, errorString);
936}
937
938typedef std::map<VuoGlContext, GLuint> VuoShaderContextType;