Vuo 2.4.4
Loading...
Searching...
No Matches
VuoGlPool.cc
Go to the documentation of this file.
1
10#include "VuoGlPool.h"
11#include "VuoGlContext.h"
12#include "VuoEventLoop.h"
13
14#include <vector>
15#include <utility>
16#include <queue>
17#include <set>
18#include <deque>
19#include <map>
20#include <locale>
21#include <sstream>
22using namespace std;
23
24#include "VuoStringUtilities.hh"
25#include "VuoShaderIssues.hh"
26
27#include <dispatch/dispatch.h>
28
29#include <CoreFoundation/CoreFoundation.h>
30#include <IOSurface/IOSurface.h>
31
32#include <OpenGL/CGLIOSurface.h>
33#include <OpenGL/CGLMacro.h>
34#include <ApplicationServices/ApplicationServices.h>
35
36
37static map<VuoGlPoolType, map<unsigned long, vector<GLuint> > > VuoGlPool __attribute__((init_priority(101)));
38static dispatch_semaphore_t VuoGlPool_semaphore;
39
50GLuint VuoGlPool_use(VuoGlContext glContext, VuoGlPoolType type, unsigned long size)
51{
52 GLuint name = 0;
53
54 dispatch_semaphore_wait(VuoGlPool_semaphore, DISPATCH_TIME_FOREVER);
55 {
56 if (VuoGlPool[type][size].size())
57 {
58 name = VuoGlPool[type][size].back();
59 VuoGlPool[type][size].pop_back();
60// VLog("using recycled type=%d name=%d",type,name);
61 }
62 else
63 {
64 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
65
66 if (type == VuoGlPool_ArrayBuffer || type == VuoGlPool_ElementArrayBuffer)
67 {
68 glGenBuffers(1, &name);
69 GLenum bufferType = type == VuoGlPool_ArrayBuffer ? GL_ARRAY_BUFFER : GL_ELEMENT_ARRAY_BUFFER;
70 glBindBuffer(bufferType, name);
71 glBufferData(bufferType, size, NULL, GL_STREAM_DRAW);
72 glBindBuffer(bufferType, 0);
74// VLog("allocated type=%d name=%d",type,name);
75 }
76 else
77 VUserLog("Unknown pool type %d.", type);
78 }
79 }
80 dispatch_semaphore_signal(VuoGlPool_semaphore);
81
82 return name;
83}
84
93void VuoGlPool_disuse(VuoGlPoolType type, unsigned long size, GLuint name)
94{
95 dispatch_semaphore_wait(VuoGlPool_semaphore, DISPATCH_TIME_FOREVER);
96 {
97 if (type == VuoGlPool_ArrayBuffer || type == VuoGlPool_ElementArrayBuffer)
98 VuoGlPool[type][size].push_back(name);
99 else
100 VUserLog("Unknown pool type %d.", type);
101 }
102 dispatch_semaphore_signal(VuoGlPool_semaphore);
103}
104
105typedef map<GLuint, unsigned int> VuoGlPoolReferenceCounts;
106static VuoGlPoolReferenceCounts VuoGlPool_referenceCounts __attribute__((init_priority(101)));
107static dispatch_semaphore_t VuoGlPool_referenceCountsSemaphore = NULL;
108static void __attribute__((constructor)) VuoGlPool_referenceCountsInit(void)
109{
110 VuoGlPool_referenceCountsSemaphore = dispatch_semaphore_create(1);
111}
112
116void VuoGlPool_retainF(GLuint glBufferName, const char *file, unsigned int linenumber, const char *func)
117{
118 if (glBufferName == 0)
119 return;
120
121 dispatch_semaphore_wait(VuoGlPool_referenceCountsSemaphore, DISPATCH_TIME_FOREVER);
122
123 VuoGlPoolReferenceCounts::iterator it = VuoGlPool_referenceCounts.find(glBufferName);
124 if (it == VuoGlPool_referenceCounts.end())
125 VuoGlPool_referenceCounts[glBufferName] = 1;
126 else
127 ++VuoGlPool_referenceCounts[glBufferName];
128
129 dispatch_semaphore_signal(VuoGlPool_referenceCountsSemaphore);
130// VuoLog(VuoLog_moduleName, file, linenumber, func, "VuoGlPool_retain(%d)", glBufferName);
131}
132
136void VuoGlPool_releaseF(VuoGlPoolType type, unsigned long size, GLuint glBufferName, const char *file, unsigned int linenumber, const char *func)
137{
138 if (glBufferName == 0)
139 return;
140
141 dispatch_semaphore_wait(VuoGlPool_referenceCountsSemaphore, DISPATCH_TIME_FOREVER);
142
143 VuoGlPoolReferenceCounts::iterator it = VuoGlPool_referenceCounts.find(glBufferName);
144 if (it == VuoGlPool_referenceCounts.end())
145 VuoLog(VuoLog_moduleName, file, linenumber, func, "Error: VuoGlPool_release() was called with OpenGL Buffer Object %d, which was never retained.", glBufferName);
146 else
147 {
148 if (--VuoGlPool_referenceCounts[glBufferName] == 0)
149 VuoGlPool_disuse(type, size, glBufferName);
150 }
151
152 dispatch_semaphore_signal(VuoGlPool_referenceCountsSemaphore);
153// VuoLog(VuoLog_moduleName, file, linenumber, func, "VuoGlPool_release(%d)", glBufferName);
154}
155
156
157
158
159
164{
165public:
166 GLenum target;
168 unsigned short width;
169 unsigned short height;
170
174 VuoGlTextureDescriptor(GLenum target, GLenum internalFormat, unsigned short width, unsigned short height)
175 : target(target),
177 width(width),
179 {
180 }
181
193
197 string toString() const
198 {
199 char *z;
200 asprintf(&z, "%-24s %-21s %5dx%-5d", VuoGl_stringForConstant(target), VuoGl_stringForConstant(internalFormat), width, height);
201 string s(z);
202 free(z);
203 return s;
204 }
205};
206typedef pair<queue<GLuint>,double> VuoGlTextureLastUsed;
207typedef map<VuoGlTextureDescriptor, VuoGlTextureLastUsed> VuoGlTexturePoolType;
209static dispatch_semaphore_t VuoGlTexturePool_semaphore;
210
211
215GLuint VuoGlTexture_getType(GLuint format)
216{
217 if (format == GL_YCBCR_422_APPLE)
218 return GL_UNSIGNED_SHORT_8_8_APPLE;
219 else if (format == GL_RGB
220 || format == GL_LUMINANCE
221 || format == GL_LUMINANCE_ALPHA
222 || format == GL_BGR_EXT)
223 return GL_UNSIGNED_BYTE;
224 else if (format == GL_RGBA
225 || format == GL_RGBA8
226 || format == GL_BGRA)
227 return GL_UNSIGNED_INT_8_8_8_8_REV;
228 else if (format == GL_DEPTH_COMPONENT)
229 // Don't use GL_UNSIGNED_SHORT or GL_UNSIGNED_INT; they cause glBlitFramebufferEXT to output garbage on some GPUs.
230 // https://b33p.net/kosada/node/11305
231 return GL_UNSIGNED_BYTE;
232
233 char *formatString = VuoGl_stringForConstant(format);
234 VUserLog("Unknown format %s", formatString);
235 free(formatString);
237 return 0;
238}
239
243unsigned char VuoGlTexture_getChannelCount(GLuint format)
244{
245 if (format == GL_LUMINANCE
246 || format == GL_LUMINANCE8
247 || format == GL_LUMINANCE16F_ARB
248 || format == GL_LUMINANCE32F_ARB
249 || format == GL_DEPTH_COMPONENT)
250 return 1;
251 else if (format == GL_YCBCR_422_APPLE
252 || format == GL_LUMINANCE_ALPHA
253 || format == GL_LUMINANCE_ALPHA16F_ARB
254 || format == GL_LUMINANCE_ALPHA32F_ARB)
255 return 2;
256 else if (format == GL_RGB
257 || format == GL_RGB16F_ARB
258 || format == GL_RGB32F_ARB
259 || format == GL_BGR_EXT)
260 return 3;
261 else if (format == GL_RGBA
262 || format == GL_RGBA8
263 || format == GL_RGBA16F_ARB
264 || format == GL_RGBA32F_ARB
265 || format == GL_BGRA)
266 return 4;
267
268 char *formatString = VuoGl_stringForConstant(format);
269 VUserLog("Unknown format %s", formatString);
270 free(formatString);
272 return 1;
273}
274
278unsigned char VuoGlTexture_getBytesPerPixel(GLuint internalformat, GLuint format)
279{
280 if (internalformat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
281 return 1;
282 if (internalformat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT
283 || internalformat == GL_COMPRESSED_RED_RGTC1)
284 return 1; // Actually 0.5
285
286 unsigned char bytes = VuoGlTexture_getChannelCount(format);
287 if (internalformat == GL_RGB
288 || internalformat == GL_RGBA
289 || internalformat == GL_RGBA8
290 || internalformat == GL_LUMINANCE8
291 || internalformat == GL_LUMINANCE8_ALPHA8)
292 return bytes;
293 else if (internalformat == GL_RGB16F_ARB
294 || internalformat == GL_RGBA16F_ARB
295 || internalformat == GL_DEPTH_COMPONENT
296 || internalformat == GL_LUMINANCE16F_ARB
297 || internalformat == GL_LUMINANCE_ALPHA16F_ARB)
298 return bytes * 2;
299 else if (internalformat == GL_RGB32F_ARB
300 || internalformat == GL_RGBA32F_ARB
301 || internalformat == GL_LUMINANCE32F_ARB
302 || internalformat == GL_LUMINANCE_ALPHA32F_ARB)
303 return bytes * 4;
304
305 VUserLog("Unknown internalformat %s", VuoGl_stringForConstant(internalformat));
307 return 0;
308}
309
314unsigned char VuoGlTexture_getBytesPerPixelForInternalFormat(GLuint internalformat)
315{
316 if (internalformat == GL_COMPRESSED_RGB_S3TC_DXT1_EXT
317 || internalformat == GL_COMPRESSED_RED_RGTC1)
318 return 1; // Actually 0.5
319 if (internalformat == GL_LUMINANCE8
320 || internalformat == GL_LUMINANCE
321 || internalformat == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
322 return 1;
323 if (internalformat == GL_LUMINANCE8_ALPHA8
324 || internalformat == GL_LUMINANCE16F_ARB
325 || internalformat == GL_DEPTH_COMPONENT)
326 return 2;
327 if (internalformat == GL_RGB
328 || internalformat == GL_BGR)
329 return 3;
330 if (internalformat == GL_RGBA
331 || internalformat == GL_RGBA8
332 || internalformat == GL_BGRA
333 || internalformat == GL_LUMINANCE_ALPHA16F_ARB
334 || internalformat == GL_LUMINANCE32F_ARB)
335 return 4;
336 if (internalformat == GL_RGB16F_ARB)
337 return 6;
338 if (internalformat == GL_RGBA16F_ARB
339 || internalformat == GL_LUMINANCE_ALPHA32F_ARB)
340 return 8;
341 if (internalformat == GL_RGB32F_ARB)
342 return 12;
343 if (internalformat == GL_RGBA32F_ARB)
344 return 16;
345
346 char *formatString = VuoGl_stringForConstant(internalformat);
347 VUserLog("Unknown internalformat %s", formatString);
348 free(formatString);
350 return 0;
351}
352
358{
359 if (format == GL_BGRA
360 || format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT
361 || format == GL_LUMINANCE8_ALPHA8
362 || format == GL_LUMINANCE_ALPHA
363 || format == GL_LUMINANCE_ALPHA16F_ARB
364 || format == GL_LUMINANCE_ALPHA32F_ARB
365 || format == GL_RGBA
366 || format == GL_RGBA16F_ARB
367 || format == GL_RGBA32F_ARB
368 || format == GL_RGBA8)
369 return true;
370
371 return false;
372}
373
378{
379#pragma clang diagnostic push
380#pragma clang diagnostic ignored "-Wdeprecated-declarations"
381 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
382 static unsigned long maximumTextureBytes = 0;
383 static dispatch_once_t maximumTextureBytesQuery = 0;
384 dispatch_once(&maximumTextureBytesQuery, ^{
385 GLint contextRendererID;
386 CGLGetParameter(cgl_ctx, kCGLCPCurrentRendererID, &contextRendererID);
387
388 // https://b33p.net/kosada/node/12177
389 // https://developer.apple.com/library/mac/qa/qa1168/_index.html says:
390 // "If you are looking for the VRAM sizes of all the renderers on your system […]
391 // you may specify a -1/0xFFFFFFFF display mask in the CGLQueryRendererInfo() function.
392 CGLRendererInfoObj ri;
393 GLint rendererCount = 0;
394 CGLQueryRendererInfo(-1, &ri, &rendererCount);
395 for (int i = 0; i < rendererCount; ++i)
396 {
397 GLint rendererID;
398 CGLDescribeRenderer(ri, i, kCGLRPRendererID, &rendererID);
399 if (rendererID == contextRendererID)
400 {
401 GLint textureMegabytes = 0;
402 if (CGLDescribeRenderer(ri, i, kCGLRPTextureMemoryMegabytes, &textureMegabytes) == kCGLNoError)
403 {
404 // In OS X, the GPU seems to often crash with individual textures that occupy more than a certain amount of memory.
405 // See https://b33p.net/kosada/node/10791
406 // See https://b33p.net/kosada/node/12030
407 double fudge = .9;
408 if ((rendererID & kCGLRendererIDMatchingMask) == kCGLRendererIntelHD4000ID)
409 // Reduce on Intel 4000, since on macOS 10.14, textures larger than this fail with GL_OUT_OF_MEMORY.
410 fudge = .74;
411
412 maximumTextureBytes = (textureMegabytes - 85) * 1048576UL * fudge;
413 VUserLog("%ld MB", maximumTextureBytes / 1024 / 1024);
414 break;
415 }
416 }
417 }
418 CGLDestroyRendererInfo(ri);
419 });
420 return maximumTextureBytes;
421#pragma clang diagnostic pop
422}
423
428{
429 static GLint maximumTextureDimension = 0;
430 static dispatch_once_t maximumTextureDimensionQuery = 0;
431 dispatch_once(&maximumTextureDimensionQuery, ^{
432 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
433 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maximumTextureDimension);
434 });
435 return maximumTextureDimension;
436}
437
438#pragma clang diagnostic push
439#pragma clang diagnostic ignored "-Wunused-function"
444{
445 return allocation == VuoGlTexturePool_NoAllocation ? "NoAlloc" :
446 (allocation == VuoGlTexturePool_Allocate ? "Alloc" :
447 allocation == VuoGlTexturePool_AllocateIOSurface ? "AllocIOSurf" : "?");
448}
449#pragma clang diagnostic pop
450
475GLuint VuoGlTexturePool_use(VuoGlContext glContext, VuoGlTexturePoolAllocation allocation, GLenum target, GLenum internalformat, unsigned short width, unsigned short height, GLenum format, void *ioSurfaceRef)
476{
477 if (allocation == VuoGlTexturePool_AllocateIOSurface
478 && target != GL_TEXTURE_RECTANGLE_ARB)
479 {
480 VUserLog("Error: To use VuoGlTexturePool_AllocateIOSurface, target must be GL_TEXTURE_RECTANGLE_ARB.");
481 return 0;
482 }
483
484 unsigned char bytesPerPixel = VuoGlTexture_getBytesPerPixel(internalformat, format);
485 unsigned long requiredBytes = width * height * bytesPerPixel;
486 VuoGlTextureDescriptor descriptor(target, internalformat, width, height);
487// VLog(" %-11s %s: Requested",VuoGlTexturePool_stringForAllocation(allocation), descriptor.toString().c_str());
488
489 GLuint name = 0;
490 if (allocation != VuoGlTexturePool_AllocateIOSurface)
491 {
492 dispatch_semaphore_wait(VuoGlTexturePool_semaphore, DISPATCH_TIME_FOREVER);
493 auto it = VuoGlTexturePool->find(descriptor);
494 if (it != VuoGlTexturePool->end() && it->second.first.size())
495 {
496 name = it->second.first.front();
497 it->second.first.pop();
498// if (name) VLog(" %s: Used recycled texture %d", descriptor.toString().c_str(), name);
499 it->second.second = VuoLogGetTime();
500 }
501 dispatch_semaphore_signal(VuoGlTexturePool_semaphore);
502 }
503
504
505 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
506
507 if (name == 0)
508 {
509 // Check texture size against available Texture Video RAM (to avoid GPU crashes / kernel panics).
510 unsigned long maximumTextureBytes = VuoGlTexture_getMaximumTextureBytes(glContext);
511 if (maximumTextureBytes > 0)
512 {
513 if (requiredBytes > maximumTextureBytes)
514 {
516 VUserLog("Not enough graphics memory for a %dx%d (%d bytes/pixel) texture. Requires %lu MB, have %lu MB.", width, height, bytesPerPixel, requiredBytes/1024/1024, maximumTextureBytes/1024/1024);
517 return 0;
518 }
519 }
520
521 // Check texture size against the GPU's allowed texture size.
522 GLint maxDimension = VuoGlTexture_getMaximumTextureDimension(glContext);
523 if (width > maxDimension || height > maxDimension)
524 {
526 VUserLog("This GPU doesn't support textures of size %dx%d (GPU's limit is %dx%d).", width, height, maxDimension, maxDimension);
527 return 0;
528 }
529
530 glGenTextures(1, &name);
531// VLog(" %-11s %s: Generated new texture %d", VuoGlTexturePool_stringForAllocation(allocation), descriptor.toString().c_str(), name);
532 glBindTexture(target, name);
533 if (allocation == VuoGlTexturePool_Allocate)
534 {
535 glTexImage2D(target, 0, internalformat, width, height, 0, format, VuoGlTexture_getType(format), NULL);
536// VLog("glTexImage2D(%s, 0, %s, %d, %d, 0, %s, %s, NULL); -> %d", VuoGl_stringForConstant(target), VuoGl_stringForConstant(internalformat), width, height, VuoGl_stringForConstant(format), VuoGl_stringForConstant(VuoGlTexture_getType(format)), name);
537 }
538 else if (allocation == VuoGlTexturePool_AllocateIOSurface)
539 {
540#pragma clang diagnostic push
541#pragma clang diagnostic ignored "-Wdeprecated-declarations"
542 CGLError err = CGLTexImageIOSurface2D(cgl_ctx, GL_TEXTURE_RECTANGLE_ARB, internalformat, width, height, format, VuoGlTexture_getType(format), (IOSurfaceRef)ioSurfaceRef, 0);
543 if (err != kCGLNoError)
544 {
545 char *internalFormatString = VuoGl_stringForConstant(internalformat);
546 char *formatString = VuoGl_stringForConstant(format);
547 char *typeString = VuoGl_stringForConstant(VuoGlTexture_getType(format));
548 VUserLog("Error in CGLTexImageIOSurface2D(context, GL_TEXTURE_RECTANGLE_ARB, %s, %hu, %hu, %s, %s, iosurface, 0): %s",
549 internalFormatString, width, height, formatString, typeString, CGLErrorString(err));
550 free(internalFormatString);
551 free(formatString);
552 free(typeString);
553 VGL();
554 return false;
555 }
556#pragma clang diagnostic pop
557 }
558 VuoGlPool_logVRAMAllocated(requiredBytes);
559 }
560 else
561 glBindTexture(target, name);
562
563
564 glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
565 glTexParameteri(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
566
567 glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
568 glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
569
570 glBindTexture(target, 0);
571
572 return name;
573}
574
585void VuoGlTexturePool_disuse(VuoGlTexturePoolAllocation allocation, GLenum target, GLenum internalformat, unsigned short width, unsigned short height, GLuint name)
586{
587 if (internalformat == 0)
588 {
589 VUserLog("Error: Can't recycle texture %d since we don't know its internalformat. Deleting.", name);
590 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
591 glDeleteTextures(1, &name);
592 });
593 VuoGlPool_logVRAMFreed(width * height * 1 /* unknown BPP */);
594 return;
595 }
596
597 if (allocation == VuoGlTexturePool_AllocateIOSurface)
598 {
599 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
600 glDeleteTextures(1, &name);
601 });
602// VuoGlTextureDescriptor descriptor(target, internalformat, width, height);
603// VLog(" %s: Deleted texture %d (can't recycle IOSurface backing textures)", descriptor.toString().c_str(), name);
604 }
605 else
606 {
607 VuoGlTextureDescriptor descriptor(target, internalformat, width, height);
608 dispatch_semaphore_wait(VuoGlTexturePool_semaphore, DISPATCH_TIME_FOREVER);
609 {
610 auto it = &(*VuoGlTexturePool)[descriptor];
611 it->first.push(name);
612 it->second = VuoLogGetTime();
613 }
614 dispatch_semaphore_signal(VuoGlTexturePool_semaphore);
615// VLog(" %s: Recycled texture %d", descriptor.toString().c_str(), name);
616 }
617}
618
619
620
621
622
626typedef struct
627{
628 unsigned int referenceCount;
629 VuoImage_freeCallback freeCallback;
630 void *freeCallbackContext;
632typedef map<GLuint, VuoGlTexture> VuoGlTextureReferenceCounts;
634static dispatch_semaphore_t VuoGlTexture_referenceCountsSemaphore = NULL;
638static void __attribute__((constructor(101))) VuoGlTexture_init(void)
639{
641 VuoGlTexture_referenceCountsSemaphore = dispatch_semaphore_create(1);
642}
643
649void VuoGlTexture_retain(GLuint glTextureName, VuoImage_freeCallback freeCallback, void *freeCallbackContext)
650{
651// VLog("%d", glTextureName);
652 if (glTextureName == 0)
653 return;
654
655 dispatch_semaphore_wait(VuoGlTexture_referenceCountsSemaphore, DISPATCH_TIME_FOREVER);
656
657 VuoGlTextureReferenceCounts::iterator it = VuoGlTexture_referenceCounts->find(glTextureName);
658 if (it == VuoGlTexture_referenceCounts->end())
659 (*VuoGlTexture_referenceCounts)[glTextureName] = (VuoGlTexture){1, freeCallback, freeCallbackContext};
660 else
661 ++(*it).second.referenceCount;
662
663 dispatch_semaphore_signal(VuoGlTexture_referenceCountsSemaphore);
664}
665
672void VuoGlTexture_release(VuoGlTexturePoolAllocation allocation, GLuint glTextureTarget, GLenum internalformat, unsigned short width, unsigned short height, GLuint glTextureName)
673{
674// VLog(" %-11s %d (%s %s %dx%d)", VuoGlTexturePool_stringForAllocation(allocation), glTextureName, VuoGl_stringForConstant(glTextureTarget), VuoGl_stringForConstant(internalformat), width, height);
675 if (glTextureName == 0)
676 return;
677
678 dispatch_semaphore_wait(VuoGlTexture_referenceCountsSemaphore, DISPATCH_TIME_FOREVER);
679
680 VuoGlTextureReferenceCounts::iterator it = VuoGlTexture_referenceCounts->find(glTextureName);
681 if (it == VuoGlTexture_referenceCounts->end())
682 VUserLog("Error: VuoGlTexture_release() was called with OpenGL Texture Object %d, which was never retained.", glTextureName);
683 else
684 {
685 VuoGlTexture t = (*VuoGlTexture_referenceCounts)[glTextureName];
686 if (--t.referenceCount == 0)
687 {
688 if (t.freeCallback)
689 {
690 // Client-owned texture
691 struct _VuoImage i = (struct _VuoImage){glTextureName, internalformat, glTextureTarget, width, height, 1, t.freeCallbackContext, NULL, NULL, NULL};
692 t.freeCallback(&i);
693 }
694 else
695 {
696 // Vuo-owned texture
697 VuoGlTexturePool_disuse(allocation, glTextureTarget, internalformat, width, height, glTextureName);
698 }
700 }
701 else
702 (*VuoGlTexture_referenceCounts)[glTextureName] = t;
703 }
704
705 dispatch_semaphore_signal(VuoGlTexture_referenceCountsSemaphore);
706}
707
717void VuoGlTexture_disown(GLuint glTextureName)
718{
719// VLog("%d", glTextureName);
720 if (glTextureName == 0)
721 return;
722
723 dispatch_semaphore_wait(VuoGlTexture_referenceCountsSemaphore, DISPATCH_TIME_FOREVER);
724
725 VuoGlTextureReferenceCounts::iterator it = VuoGlTexture_referenceCounts->find(glTextureName);
726 if (it == VuoGlTexture_referenceCounts->end())
727 VUserLog("Error: VuoGlTexture_disown() was called with OpenGL Texture Object %d, which was never retained.", glTextureName);
728 else
729 {
730 VuoGlTexture t = (*VuoGlTexture_referenceCounts)[glTextureName];
731 if (t.referenceCount != 1)
732 VUserLog("Error: VuoGlTexture_disown() was called with OpenGL Texture Object %d, which has a reference count of %d (but it should be 1).", glTextureName, t.referenceCount);
733 else
735 }
736
737 dispatch_semaphore_signal(VuoGlTexture_referenceCountsSemaphore);
738}
739
740
741
745typedef struct
746{
747 IOSurfaceRef ioSurface;
748 GLuint texture;
749 unsigned short pixelsWide;
750 unsigned short pixelsHigh;
751 double lastUsedTime;
753typedef pair<unsigned short,unsigned short> VuoGlTextureDimensionsType;
754typedef map<VuoGlTextureDimensionsType, deque<VuoIoSurfacePoolEntryType> > VuoIoSurfacePoolType;
757static dispatch_semaphore_t VuoIoSurfacePool_semaphore;
758static CFStringRef receiverFinishedWithIoSurfaceKey = CFSTR("VuoReceiverFinished");
759
760const double VuoGlPool_cleanupInterval = 0.1;
761static dispatch_source_t VuoGlPool_timer;
762static dispatch_semaphore_t VuoGlPool_canceledAndCompleted;
763
764static unsigned long VuoGlPool_allocatedBytes = 0;
765static unsigned long VuoGlPool_allocatedBytesMax = 0;
766
776void VuoGlPool_logVRAMAllocated(unsigned long bytesAllocated)
777{
778// VLog("Alloc %lu", bytesAllocated);
779 __sync_add_and_fetch(&VuoGlPool_allocatedBytes, bytesAllocated);
780
781 // Not thread-safe, but the worst that could happen is that,
782 // if multiple threads exceed the limit simultaneously,
783 // we might not get the largest of the new maximums.
786}
787
797void VuoGlPool_logVRAMFreed(unsigned long bytesFreed)
798{
799// VLog("Freed %lu", bytesFreed);
800 __sync_sub_and_fetch(&VuoGlPool_allocatedBytes, bytesFreed);
801}
802
806static void VuoGlPool_cleanup(void *blah)
807{
808// VLog("{");
809 unsigned long totalTextureCount = 0;
810 static unsigned long totalTextureCountMax = 0;
811
812 vector<GLuint> texturesToDelete;
813 vector<VuoIoSurfacePoolEntryType> iosurfaceTexturesToDisuse;
814
815 dispatch_semaphore_wait(VuoGlTexturePool_semaphore, DISPATCH_TIME_FOREVER);
816 {
817 double now = VuoLogGetTime();
818 for (VuoGlTexturePoolType::iterator poolItem = VuoGlTexturePool->begin(); poolItem != VuoGlTexturePool->end(); )
819 {
820 unsigned long textureCount = poolItem->second.first.size();
821 double timeSinceLastUsed = now - poolItem->second.second;
822// VLog(" %s: %lu unused texture%s (last used %.02gs ago)", poolItem->first.toString().c_str(), textureCount, textureCount == 1 ? "" : "s", timeSinceLastUsed);
823 if (timeSinceLastUsed > VuoGlPool_cleanupInterval)
824 {
825 if (textureCount)
826 {
827// VLog(" Purging %lu expired texture%s", textureCount, textureCount == 1 ? "" : "s");
828 while (!poolItem->second.first.empty())
829 {
830 GLuint textureName = poolItem->second.first.front();
831 poolItem->second.first.pop();
832 texturesToDelete.push_back(textureName);
833 VuoGlPool_logVRAMFreed(poolItem->first.width * poolItem->first.height * VuoGlTexture_getBytesPerPixelForInternalFormat(poolItem->first.internalFormat));
834 }
835 }
836
837 VuoGlTexturePool->erase(poolItem++);
838 }
839 else
840 {
841 totalTextureCount += textureCount;
842 ++poolItem;
843 }
844 }
845 }
846 dispatch_semaphore_signal(VuoGlTexturePool_semaphore);
847
848 if (totalTextureCount > totalTextureCountMax)
849 totalTextureCountMax = totalTextureCount;
850
851
852 unsigned long totalIOSurfaceCount = 0;
853 static unsigned long totalIOSurfaceCountMax = 0;
854
855 dispatch_semaphore_wait(VuoIoSurfacePool_semaphore, DISPATCH_TIME_FOREVER);
856 {
857 // Promote IOSurfaces from the quarantine to the pool, if the receiver has finished using them.
858 for (VuoIoSurfacePoolType::iterator quarantinedQueue = VuoIoSurfaceQuarantine->begin(); quarantinedQueue != VuoIoSurfaceQuarantine->end(); ++quarantinedQueue)
859 {
860 for (deque<VuoIoSurfacePoolEntryType>::iterator quarantinedIoSurfaceEntry = quarantinedQueue->second.begin(); quarantinedIoSurfaceEntry != quarantinedQueue->second.end();)
861 {
862 VuoIoSurfacePoolEntryType e = *quarantinedIoSurfaceEntry;
863 CFBooleanRef finished = (CFBooleanRef)IOSurfaceCopyValue(e.ioSurface, receiverFinishedWithIoSurfaceKey);
864 if (finished)
865 {
866 IOSurfaceRemoveValue(e.ioSurface, receiverFinishedWithIoSurfaceKey);
867
868 (*VuoIoSurfacePool)[quarantinedQueue->first].push_back(e);
869 quarantinedIoSurfaceEntry = quarantinedQueue->second.erase(quarantinedIoSurfaceEntry);
870
871// VLog(" Promoted IOSurface %d + GL Texture %d (%dx%d) from quarantine to pool", IOSurfaceGetID(e.ioSurface), e.texture, quarantinedQueue->first.first, quarantinedQueue->first.second);
872 }
873 else
874 {
875 ++totalIOSurfaceCount;
876 ++quarantinedIoSurfaceEntry;
877 }
878 }
879 }
880
881
882 double now = VuoLogGetTime();
883 for (VuoIoSurfacePoolType::iterator poolQueue = VuoIoSurfacePool->begin(); poolQueue != VuoIoSurfacePool->end(); ++poolQueue)
884 {
885 for (deque<VuoIoSurfacePoolEntryType>::iterator poolIoSurfaceEntry = poolQueue->second.begin(); poolIoSurfaceEntry != poolQueue->second.end();)
886 {
887 VuoIoSurfacePoolEntryType e = *poolIoSurfaceEntry;
888 if (now - e.lastUsedTime > VuoGlPool_cleanupInterval * 2.)
889 {
890// VLog(" Purging expired IOSurface %d + GL Texture %d (%dx%d) — it's %gs old", IOSurfaceGetID(e.ioSurface), e.texture, poolQueue->first.first, poolQueue->first.second, now - e.lastUsedTime);
891
892 CFRelease(e.ioSurface);
893 iosurfaceTexturesToDisuse.push_back(e);
894 poolIoSurfaceEntry = poolQueue->second.erase(poolIoSurfaceEntry);
895 }
896 else
897 {
898 ++totalIOSurfaceCount;
899 ++poolIoSurfaceEntry;
900 }
901 }
902 }
903 }
904 dispatch_semaphore_signal(VuoIoSurfacePool_semaphore);
905
906 if (totalIOSurfaceCount > totalIOSurfaceCountMax)
907 totalIOSurfaceCountMax = totalIOSurfaceCount;
908
909
910 // Delete textures after we've released the pool semaphores, to avoid deadlock, and to avoid hogging the shared GL context.
911 if (texturesToDelete.size())
912 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
913 for (vector<GLuint>::const_iterator i = texturesToDelete.begin(); i != texturesToDelete.end(); ++i)
914 {
915 GLuint t = *i;
916 glDeleteTextures(1, &t);
917 // VuoGlPool_logVRAMFreed was called above when texturesToDelete was being built.
918 }
919 });
920 for (auto e : iosurfaceTexturesToDisuse)
921 VuoGlTexturePool_disuse(VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, e.pixelsWide, e.pixelsHigh, e.texture);
922// VLog("}");
923
924
925 // Log VRAM use every 10 seconds.
926 static unsigned long cleanupCount = 0;
927 if ((++cleanupCount % (long)(10. / VuoGlPool_cleanupInterval) == 0)
930 || totalTextureCount > 0 || totalTextureCountMax > 0
931 || totalIOSurfaceCount > 0 || totalIOSurfaceCountMax > 0))
932 {
933 __block unsigned long maximumTextureBytes;
934 VuoGlContext_perform(^(CGLContextObj cgl_ctx){
935 maximumTextureBytes = VuoGlTexture_getMaximumTextureBytes(cgl_ctx);
936 });
937
938 if (maximumTextureBytes > 0)
939 VUserLog("VRAM used: %5lu MB (%5lu MB max, %3lu%%). Textures in pool: %3lu (%3lu max). IOSurfaces in pool: %3lu (%3lu max).",
940 VuoGlPool_allocatedBytes/1024/1024,
942 VuoGlPool_allocatedBytesMax*100/maximumTextureBytes,
943 totalTextureCount, totalTextureCountMax,
944 totalIOSurfaceCount, totalIOSurfaceCountMax);
945 else
946 VUserLog("VRAM used: %5lu MB (%5lu MB max). Textures in pool: %3lu (%3lu max). IOSurfaces in pool: %3lu (%3lu max).",
947 VuoGlPool_allocatedBytes/1024/1024,
949 totalTextureCount, totalTextureCountMax,
950 totalIOSurfaceCount, totalIOSurfaceCountMax);
951 }
952}
953static void __attribute__((constructor)) VuoGlPool_init(void)
954{
955 VuoGlPool_semaphore = dispatch_semaphore_create(1);
956
957 VuoGlTexturePool_semaphore = dispatch_semaphore_create(1);
959
960 VuoIoSurfacePool_semaphore = dispatch_semaphore_create(1);
963
964 VuoGlPool_canceledAndCompleted = dispatch_semaphore_create(0);
965 VuoGlPool_timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, DISPATCH_TIMER_STRICT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
966 dispatch_source_set_timer(VuoGlPool_timer, dispatch_walltime(NULL,0), NSEC_PER_SEC * VuoGlPool_cleanupInterval, NSEC_PER_SEC * VuoGlPool_cleanupInterval);
967 dispatch_source_set_event_handler_f(VuoGlPool_timer, VuoGlPool_cleanup);
968 dispatch_source_set_cancel_handler(VuoGlPool_timer, ^{
969 dispatch_semaphore_signal(VuoGlPool_canceledAndCompleted);
970 });
971 dispatch_resume(VuoGlPool_timer);
972}
976static void __attribute__((destructor)) VuoGlPool_fini(void)
977{
978 dispatch_source_cancel(VuoGlPool_timer);
979
980 // Wait for the last cleanup to complete.
981 dispatch_semaphore_wait(VuoGlPool_canceledAndCompleted, DISPATCH_TIME_FOREVER);
982
983 delete VuoGlTexturePool;
984
985 delete VuoIoSurfacePool;
987}
988
993VuoIoSurface VuoIoSurfacePool_use(VuoGlContext glContext, unsigned short pixelsWide, unsigned short pixelsHigh, GLuint *outputTexture)
994{
995 VuoIoSurface ioSurface = NULL;
996 VuoGlTextureDimensionsType dimensions(pixelsWide,pixelsHigh);
997
998 dispatch_semaphore_wait(VuoIoSurfacePool_semaphore, DISPATCH_TIME_FOREVER);
999 {
1000 if ((*VuoIoSurfacePool)[dimensions].size())
1001 {
1002 VuoIoSurfacePoolEntryType e = (*VuoIoSurfacePool)[dimensions].front();
1003 ioSurface = malloc(sizeof(VuoIoSurfacePoolEntryType));
1004 memcpy(ioSurface, &e, sizeof(VuoIoSurfacePoolEntryType));
1005 *outputTexture = e.texture;
1006 (*VuoIoSurfacePool)[dimensions].pop_front();
1007// VLog(" Using recycled IOSurface %d + GL Texture %d (%dx%d) from pool", IOSurfaceGetID(e.ioSurface), *outputTexture, pixelsWide, pixelsHigh);
1008 }
1009 }
1010 dispatch_semaphore_signal(VuoIoSurfacePool_semaphore);
1011
1012 if (!ioSurface)
1013 {
1014 CFMutableDictionaryRef properties = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, NULL, NULL);
1015
1016#pragma clang diagnostic push
1017#pragma clang diagnostic ignored "-Wdeprecated-declarations"
1020 CFDictionaryAddValue(properties, kIOSurfaceIsGlobal, kCFBooleanTrue);
1021#pragma clang diagnostic pop
1022
1023 long long pixelsWideLL = pixelsWide;
1024 CFNumberRef pixelsWideCF = CFNumberCreate(NULL, kCFNumberLongLongType, &pixelsWideLL);
1025 CFDictionaryAddValue(properties, kIOSurfaceWidth, pixelsWideCF);
1026
1027 long long pixelsHighLL = pixelsHigh;
1028 CFNumberRef pixelsHighCF = CFNumberCreate(NULL, kCFNumberLongLongType, &pixelsHighLL);
1029 CFDictionaryAddValue(properties, kIOSurfaceHeight, pixelsHighCF);
1030
1031 long long bytesPerElement = 4;
1032 CFNumberRef bytesPerElementCF = CFNumberCreate(NULL, kCFNumberLongLongType, &bytesPerElement);
1033 CFDictionaryAddValue(properties, kIOSurfaceBytesPerElement, bytesPerElementCF);
1034
1035 uint32_t pixelFormat = 'BGRA'; // kCVPixelFormatType_32BGRA;
1036 CFNumberRef pixelFormatCF = CFNumberCreate(NULL, kCFNumberSInt32Type, &pixelFormat);
1037 CFDictionaryAddValue(properties, kIOSurfacePixelFormat, pixelFormatCF);
1038
1039 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1040
1042 ioSurface = e;
1043 e->ioSurface = IOSurfaceCreate(properties);
1044 e->pixelsWide = pixelsWide;
1045 e->pixelsHigh = pixelsHigh;
1046 e->lastUsedTime = -1;
1047 CFRelease(pixelsWideCF);
1048 CFRelease(pixelsHighCF);
1049 CFRelease(bytesPerElementCF);
1050 CFRelease(properties);
1051
1052 *outputTexture = VuoGlTexturePool_use(cgl_ctx, VuoGlTexturePool_AllocateIOSurface, GL_TEXTURE_RECTANGLE_ARB, GL_RGBA, pixelsWide, pixelsHigh, GL_BGRA, e->ioSurface);
1053 e->texture = *outputTexture;
1054
1055// VLog(" Created IOSurface %d (%dx%d)", IOSurfaceGetID(e->ioSurface), pixelsWide, pixelsHigh);
1056 }
1057
1058 return ioSurface;
1059}
1060
1065{
1067 return IOSurfaceGetID(e->ioSurface);
1068}
1069
1074{
1076 return e->ioSurface;
1077}
1078
1083{
1085 return e->pixelsWide;
1086}
1087
1092{
1094 return e->pixelsHigh;
1095}
1096
1101{
1103 return e->texture;
1104}
1105
1118void VuoIoSurfacePool_disuse(VuoIoSurface vis, bool quarantine)
1119{
1121// VLog(" Sender disusing IOSurface %d + GL Texture %d (%dx%d)", IOSurfaceGetID(e->ioSurface), e->texture, e->pixelsWide, e->pixelsHigh);
1122 VuoGlTextureDimensionsType dimensions(e->pixelsWide, e->pixelsHigh);
1123 e->lastUsedTime = VuoLogGetTime();
1124
1125 dispatch_semaphore_wait(VuoIoSurfacePool_semaphore, DISPATCH_TIME_FOREVER);
1126 {
1127 if (quarantine)
1128 (*VuoIoSurfaceQuarantine)[dimensions].push_back(*e);
1129 else
1130 (*VuoIoSurfacePool)[dimensions].push_back(*e);
1131 }
1132 dispatch_semaphore_signal(VuoIoSurfacePool_semaphore);
1133
1134 free(e);
1135}
1136
1142{
1143 IOSurfaceRef ioSurface = (IOSurfaceRef)ios;
1144 IOSurfaceSetValue(ioSurface, receiverFinishedWithIoSurfaceKey, kCFBooleanTrue);
1145}
1146
1147
1148
1154void VuoGlShader_parseShaderInfoLog(CGLContextObj cgl_ctx, GLenum type, GLuint obj, const GLchar *source, VuoShaderFile::Stage stage, VuoShaderIssues *outIssues)
1155{
1156 int infologLength = 0;
1157 glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
1158 if (infologLength > 0)
1159 {
1160 char *infoLog = (char *)malloc(infologLength);
1161 int charsWritten = 0;
1162 glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
1163
1164 if (outIssues)
1165 {
1166 istringstream iss(infoLog);
1167 string ln;
1168 while (getline(iss, ln))
1169 {
1170 int lineNumber = 0;
1171 char *message = (char *)malloc(ln.length() + 1);
1172 char *quotation = (char *)malloc(ln.length() + 1);
1173 if (sscanf(ln.c_str(), "ERROR: 0:%d: '' : %[^\n]", &lineNumber, message) == 2)
1174 outIssues->addIssue(stage, lineNumber, message);
1175 else if (sscanf(ln.c_str(), "ERROR: 0:%d: '%[^']' : syntax error: %[^\n]", &lineNumber, quotation, message) == 3)
1176 outIssues->addIssue(stage, lineNumber, message + string(": '") + quotation + "'");
1177 else if (sscanf(ln.c_str(), "ERROR: 0:%d: %[^\n]", &lineNumber, message) == 2)
1178 outIssues->addIssue(stage, lineNumber, message);
1179 else if (sscanf(ln.c_str(), "WARNING: 0:%d: %[^\n]", &lineNumber, message) == 2)
1180 outIssues->addIssue(stage, lineNumber, message);
1181 else
1182 {
1183 VUserLog("Warning: Couldn't parse GLSL log message: \"%s\"", ln.c_str());
1184 outIssues->addIssue(stage, VuoShaderIssues::NoLine, ln);
1185 }
1186 free(message);
1187 free(quotation);
1188 }
1189 }
1190 else
1191 {
1192 VUserLog("%s", infoLog);
1193 VDebugLog("Source code:\n%s", source);
1194 }
1195
1196 free(infoLog);
1197 }
1198}
1199
1200map<GLenum, map<long, GLuint> > VuoGlShaderPool __attribute__((init_priority(101)));
1201dispatch_semaphore_t VuoGlShaderPool_semaphore = NULL;
1202
1206__attribute__((constructor)) static void VuoGlShaderPool_init(void)
1207{
1208 VuoGlShaderPool_semaphore = dispatch_semaphore_create(1);
1209}
1210
1216static bool VuoGlShader_resolveIncludes(string &source, VuoShaderFile::Stage stage, VuoShaderIssues *outIssues)
1217{
1218 const char *cwd = VuoGetWorkingDirectory();
1219 const string vuoFrameworkPath = VuoFileUtilities::getVuoFrameworkPath();
1220 const string vuoRunnerFrameworkPath = VuoFileUtilities::getVuoRunnerFrameworkPath();
1221 string userModulesPath = VuoFileUtilities::getUserModulesPath();
1222 if (!VuoFileUtilities::dirExists(userModulesPath))
1223 userModulesPath = "";
1224 string systemModulesPath = VuoFileUtilities::getSystemModulesPath();
1225 if (!VuoFileUtilities::dirExists(systemModulesPath))
1226 systemModulesPath = "";
1227
1228 set<string> includedFiles;
1229
1230 while (1)
1231 {
1232 const string includeKeyword = "#include";
1233
1234 size_t includeStart = source.find(includeKeyword);
1235 if (includeStart == string::npos)
1236 return true;
1237
1238 size_t offset = includeStart + includeKeyword.length();
1239
1240 while (source[offset] == ' ')
1241 ++offset;
1242
1243 if (source[offset] != '"'
1244 && source[offset] != '<')
1245 {
1247 if (outIssues)
1248 outIssues->addIssue(stage, VuoShaderIssues::NoLine, "Syntax error in #include statement. Expected syntax: #include \"filename.glsl\"");
1249 else
1250 VUserLog("Error: Syntax error in #include statement. Expected syntax: #include \"filename.glsl\"");
1251 return false;
1252 }
1253 ++offset;
1254
1255 size_t filenameStart = offset;
1256
1257 while (source[offset] != '"'
1258 && source[offset] != '>')
1259 ++offset;
1260
1261 size_t filenameEnd = offset;
1262
1263 string filename = source.substr(filenameStart, filenameEnd - filenameStart);
1264
1265 vector<string> pathsToTest;
1266 if (strcmp(cwd, "/") != 0)
1267 pathsToTest.push_back(cwd + string("/") + filename);
1268 if (!vuoFrameworkPath.empty())
1269 pathsToTest.push_back(vuoFrameworkPath + "/Resources/shaders/" + filename);
1270 if (!vuoRunnerFrameworkPath.empty())
1271 pathsToTest.push_back(vuoRunnerFrameworkPath + "/Resources/shaders/" + filename);
1272 if (!userModulesPath.empty())
1273 pathsToTest.push_back(userModulesPath + "/" + filename);
1274 if (!systemModulesPath.empty())
1275 pathsToTest.push_back(systemModulesPath + "/" + filename);
1276 bool found = false;
1277 for (vector<string>::iterator i = pathsToTest.begin(); i != pathsToTest.end(); ++i)
1278 {
1280 {
1281 found = true;
1282
1283 source.erase(includeStart, offset - includeStart + 1);
1284
1285 // Prevent recursive includes.
1286 if (includedFiles.count(*i))
1287 {
1288 source.insert(includeStart,
1289 "\n"
1290 "// ============================================================\n"
1291 "// Skipped including file \"" + *i + "\" since it was already included.\n"
1292 "// ============================================================\n"
1293 "\n"
1294 );
1295 break;
1296 }
1297 includedFiles.insert(*i);
1298
1299 source.insert(includeStart,
1300 "\n"
1301 "// ============================================================\n"
1302 "// Begin included file \"" + *i + "\"\n"
1304 + "\n"
1305 "// End included file \"" + *i + "\"\n"
1306 "// ============================================================\n"
1307 "\n"
1308 );
1309 break;
1310 }
1311 }
1312
1313 if (!found)
1314 {
1316 if (outIssues)
1317 outIssues->addIssue(stage, VuoShaderIssues::NoLine, "Couldn't find include file \"" + filename + "\"");
1318 else
1319 VUserLog("Error: Couldn't find include file \"%s\".", filename.c_str());
1320 return false;
1321 }
1322 }
1323}
1324
1325static const std::locale VuoGlPool_locale;
1326static const std::collate<char> &VuoGlPool_collate = std::use_facet<std::collate<char> >(VuoGlPool_locale);
1327
1345GLuint VuoGlShader_use(VuoGlContext glContext, GLenum type, const char *source, void *outIssues)
1346{
1347 long hash = VuoGlPool_collate.hash(source, source+strlen(source));
1348
1349 dispatch_semaphore_wait(VuoGlShaderPool_semaphore, DISPATCH_TIME_FOREVER);
1350
1351 GLuint shader;
1352 if (!outIssues && VuoGlShaderPool[type].find(hash) != VuoGlShaderPool[type].end())
1353 shader = VuoGlShaderPool[type][hash];
1354 else
1355 {
1356 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1357
1358 string combinedSource = source;
1359
1360 VuoShaderIssues *oi = static_cast<VuoShaderIssues *>(outIssues);
1361 VuoShaderFile::Stage stage;
1362 if (type == GL_VERTEX_SHADER)
1363 stage = VuoShaderFile::Vertex;
1364 else if (type == GL_GEOMETRY_SHADER_EXT)
1365 stage = VuoShaderFile::Geometry;
1366 else // if (type == GL_FRAGMENT_SHADER)
1367 stage = VuoShaderFile::Fragment;
1368
1369 if (!VuoGlShader_resolveIncludes(combinedSource, stage, oi))
1370 {
1371 shader = 0;
1372 goto done;
1373 }
1374
1375// VLog("\n%s\n\n", combinedSource.c_str());
1376
1377 shader = glCreateShader(type);
1378 GLint length = combinedSource.length();
1379 const GLchar *combinedSourceCString = combinedSource.c_str();
1380 glShaderSource(shader, 1, (const GLchar**)&combinedSourceCString, &length);
1381 glCompileShader(shader);
1382 VuoGlShader_parseShaderInfoLog(cgl_ctx, type, shader, combinedSourceCString, stage, oi);
1383
1384 GLint status = GL_FALSE;
1385 glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
1386 if (status == GL_FALSE)
1387 {
1388 glDeleteShader(shader);
1389 shader = 0;
1390 goto done;
1391 }
1392
1393 VuoGlShaderPool[type][hash] = shader;
1394 }
1395
1396done:
1397 dispatch_semaphore_signal(VuoGlShaderPool_semaphore);
1398
1399 return shader;
1400}
1401
1402// Too bad we can't use std::tuple yet…
1403typedef pair<GLuint, pair<GLuint, pair<GLuint, pair<VuoMesh_ElementAssemblyMethod, unsigned int> > > > VuoGlProgramDescriptorType;
1404typedef map<VuoGlProgramDescriptorType, VuoGlProgram> VuoGlProgramPoolType;
1406static dispatch_semaphore_t VuoGlProgramPool_semaphore;
1407static void __attribute__((constructor)) VuoGlProgramPool_init(void)
1408{
1409 VuoGlProgramPool_semaphore = dispatch_semaphore_create(1);
1410}
1411
1412typedef std::map<long, GLuint> VuoGlUniformMap;
1413
1431VuoGlProgram VuoGlProgram_use(VuoGlContext glContext, const char *description, GLuint vertexShaderName, GLuint geometryShaderName, GLuint fragmentShaderName, VuoMesh_ElementAssemblyMethod assemblyMethod, unsigned int expectedOutputPrimitiveCount, void *outIssues)
1432{
1433// VLog("looking for %d %d %d %d %d", vertexShaderName, geometryShaderName, fragmentShaderName, assemblyMethod, expectedOutputPrimitiveCount);
1434 VuoGlProgram program;
1435 VuoGlProgramDescriptorType e(vertexShaderName, std::make_pair(geometryShaderName, std::make_pair(fragmentShaderName, std::make_pair(assemblyMethod, expectedOutputPrimitiveCount))));
1436 dispatch_semaphore_wait(VuoGlProgramPool_semaphore, DISPATCH_TIME_FOREVER);
1437 VuoGlProgramPoolType::iterator it = VuoGlProgramPool.find(e);
1438 if (!outIssues && it != VuoGlProgramPool.end())
1439 program = it->second;
1440 else
1441 {
1442 CGLContextObj cgl_ctx = (CGLContextObj)glContext;
1443
1444 GLuint programName = glCreateProgram();
1445 glAttachShader(programName, vertexShaderName);
1446 if (geometryShaderName)
1447 glAttachShader(programName, geometryShaderName);
1448 if (fragmentShaderName)
1449 glAttachShader(programName, fragmentShaderName);
1450
1451 bool transformFeedback = false;
1452 // If there's no fragment shader, this program is being used for transform feedback.
1453 if (!fragmentShaderName)
1454 transformFeedback = true;
1455
1456 // Make sure `position` is at location 0, since location 0 is required in order for glDraw*() to work.
1457 glBindAttribLocation(programName, 0, "position");
1458
1459 if (geometryShaderName)
1460 {
1461 GLuint inputPrimitiveGlMode = GL_TRIANGLES;
1462 if (assemblyMethod == VuoMesh_IndividualLines
1463 || assemblyMethod == VuoMesh_LineStrip)
1464 inputPrimitiveGlMode = GL_LINES;
1465 else if (assemblyMethod == VuoMesh_Points)
1466 inputPrimitiveGlMode = GL_POINTS;
1467 glProgramParameteriEXT(programName, GL_GEOMETRY_INPUT_TYPE_EXT, inputPrimitiveGlMode);
1468
1469 GLuint outputPrimitiveGlMode = GL_TRIANGLE_STRIP;
1470 if (transformFeedback)
1471 {
1472 // In transform feedback, the output primitive mode needs to match the input primitive mode.
1473 if (assemblyMethod == VuoMesh_IndividualLines
1474 || assemblyMethod == VuoMesh_LineStrip)
1475 outputPrimitiveGlMode = GL_LINE_STRIP;
1476 else if (assemblyMethod == VuoMesh_Points)
1477 outputPrimitiveGlMode = GL_POINTS;
1478 }
1479 glProgramParameteriEXT(programName, GL_GEOMETRY_OUTPUT_TYPE_EXT, outputPrimitiveGlMode);
1480
1481 unsigned int expectedVertexCount = expectedOutputPrimitiveCount;
1482 if (outputPrimitiveGlMode == GL_TRIANGLE_STRIP)
1483 expectedVertexCount *= 3;
1484 else if (outputPrimitiveGlMode == GL_LINE_STRIP)
1485 expectedVertexCount *= 2;
1486 glProgramParameteriEXT(programName, GL_GEOMETRY_VERTICES_OUT_EXT, expectedVertexCount);
1487 }
1488
1489 if (transformFeedback)
1490 {
1491 const GLchar *varyings[] = { "outPosition", "outNormal", "outTextureCoordinate", "outVertexColor" };
1492 glTransformFeedbackVaryingsEXT(programName, 4, varyings, GL_SEPARATE_ATTRIBS_EXT);
1493 }
1494
1495 glLinkProgram(programName);
1496
1497 {
1498 int infologLength = 0;
1499 glGetProgramiv(programName, GL_INFO_LOG_LENGTH, &infologLength);
1500 if (infologLength > 0)
1501 {
1502 char *infoLog = (char *)malloc(infologLength);
1503 int charsWritten = 0;
1504 glGetProgramInfoLog(programName, infologLength, &charsWritten, infoLog);
1505
1506 if (outIssues)
1507 {
1508 VuoShaderIssues *oi = static_cast<VuoShaderIssues *>(outIssues);
1509
1510 istringstream iss(infoLog);
1511 string ln;
1512 while (getline(iss, ln))
1513 {
1514 string s;
1515 if ( !(s = VuoStringUtilities::substrAfter(ln, "ERROR: ")).empty() )
1516 oi->addIssue(VuoShaderFile::Program, VuoShaderIssues::NoLine, s);
1517 else if ( !(s = VuoStringUtilities::substrAfter(ln, "WARNING: ")).empty() )
1518 oi->addIssue(VuoShaderFile::Program, VuoShaderIssues::NoLine, s);
1519 else
1520 {
1521 VUserLog("Warning: Couldn't parse GLSL log message: \"%s\"", ln.c_str());
1522 oi->addIssue(VuoShaderFile::Program, VuoShaderIssues::NoLine, ln);
1523 }
1524 }
1525 }
1526 else
1527 VUserLog("%s: %s", description, infoLog);
1528
1529 free(infoLog);
1530 }
1531 }
1532
1533 int linkStatus = 0;
1534 glGetProgramiv(programName, GL_LINK_STATUS, &linkStatus);
1535 if (linkStatus == GL_FALSE)
1536 {
1537 glDeleteProgram(programName);
1538 dispatch_semaphore_signal(VuoGlProgramPool_semaphore);
1539 return (VuoGlProgram){0,NULL};
1540 }
1541
1542 program.programName = programName;
1543
1544 GLint uniformCount;
1545 glGetProgramiv(programName, GL_ACTIVE_UNIFORMS, &uniformCount);
1546
1547 GLint maxNameLength;
1548 glGetProgramiv(programName, GL_ACTIVE_UNIFORM_MAX_LENGTH, &maxNameLength);
1549 char *name = (char *)malloc(maxNameLength + 1);
1550
1551 VuoGlUniformMap *uniforms = new VuoGlUniformMap;
1552 program.uniforms = (void *)uniforms;
1553
1554 for (GLuint i = 0; i < uniformCount; ++i)
1555 {
1556 GLint size;
1557 glGetActiveUniform(programName, i, maxNameLength+1, NULL, &size, NULL, name);
1558
1559 // The uniform location is _not_ the same as the active uniform index!
1560 size_t nameLen = strlen(name);
1561 long hash = VuoGlPool_collate.hash(name, name+nameLen);
1562 (*uniforms)[hash] = glGetUniformLocation(programName, name);
1563
1564 if (size > 1)
1565 {
1566 // For arrays, glGetActiveUniform() returns only the first array index. Take care of the rest of the indices.
1567 // E.g., `uniform float offset[3];` would return `offset[0]` with size 3, so we need to synthesize `offset[1]` and `offset[2]`.
1568 if (name[nameLen-2] == '0' && name[nameLen-1] == ']')
1569 {
1570 name[nameLen-2] = 0;
1571 for (int i = 1; i < size; ++i)
1572 {
1573 std::stringstream ss;
1574 ss << name << i << "]";
1575 string sss = ss.str();
1576 long sHash = VuoGlPool_collate.hash(sss.data(), sss.data()+sss.length());
1577 (*uniforms)[sHash] = glGetUniformLocation(programName, sss.c_str());
1578 }
1579 }
1580 }
1581 }
1582
1583 VuoGlProgramPool[e] = program;
1584 }
1585
1586 dispatch_semaphore_signal(VuoGlProgramPool_semaphore);
1587 return program;
1588}
1589
1593int VuoGlProgram_getUniformLocation(VuoGlProgram program, const char *uniformIdentifier)
1594{
1595 VuoGlUniformMap *uniforms = (VuoGlUniformMap *)program.uniforms;
1596
1597 long hash = VuoGlPool_collate.hash(uniformIdentifier, uniformIdentifier+strlen(uniformIdentifier));
1598
1599 VuoGlUniformMap::iterator i = uniforms->find(hash);
1600 if (i != uniforms->end())
1601 return i->second;
1602
1603 return -1;
1604}
1605
1607#define RETURN_STRING_IF_EQUAL(value) if (constant == value) return strdup(#value)
1608
1614char *VuoGl_stringForConstant(GLenum constant)
1615{
1616 if (constant == 0)
1617 return strdup("(GL_ZERO or GL_POINTS)");
1618 if (constant == 1)
1619 return strdup("(GL_ONE or GL_LINES)");
1620 RETURN_STRING_IF_EQUAL(GL_LINE_LOOP);
1621 RETURN_STRING_IF_EQUAL(GL_LINE_STRIP);
1622 RETURN_STRING_IF_EQUAL(GL_TRIANGLES);
1623 RETURN_STRING_IF_EQUAL(GL_TRIANGLE_STRIP);
1624 RETURN_STRING_IF_EQUAL(GL_TRIANGLE_FAN);
1625 RETURN_STRING_IF_EQUAL(GL_QUADS);
1626 RETURN_STRING_IF_EQUAL(GL_QUAD_STRIP);
1627 RETURN_STRING_IF_EQUAL(GL_POLYGON);
1628 RETURN_STRING_IF_EQUAL(GL_BACK);
1629 RETURN_STRING_IF_EQUAL(GL_FRONT);
1631 RETURN_STRING_IF_EQUAL(GL_RED);
1632 RETURN_STRING_IF_EQUAL(GL_RGB);
1633 RETURN_STRING_IF_EQUAL(GL_RGB16);
1634 RETURN_STRING_IF_EQUAL(GL_RGB16F_ARB);
1635 RETURN_STRING_IF_EQUAL(GL_RGB32F_ARB);
1636 RETURN_STRING_IF_EQUAL(GL_RGBA);
1637 RETURN_STRING_IF_EQUAL(GL_RGBA8);
1638 RETURN_STRING_IF_EQUAL(GL_RGBA16);
1639 RETURN_STRING_IF_EQUAL(GL_RGBA16F_ARB);
1640 RETURN_STRING_IF_EQUAL(GL_RGBA32F_ARB);
1641 RETURN_STRING_IF_EQUAL(GL_BGRA);
1642 RETURN_STRING_IF_EQUAL(GL_BGR_EXT);
1643 RETURN_STRING_IF_EQUAL(GL_LUMINANCE);
1644 RETURN_STRING_IF_EQUAL(GL_LUMINANCE8);
1645 RETURN_STRING_IF_EQUAL(GL_LUMINANCE16);
1646 RETURN_STRING_IF_EQUAL(GL_LUMINANCE16F_ARB);
1647 RETURN_STRING_IF_EQUAL(GL_LUMINANCE16I_EXT);
1648 RETURN_STRING_IF_EQUAL(GL_LUMINANCE32F_ARB);
1649 RETURN_STRING_IF_EQUAL(GL_LUMINANCE8_ALPHA8);
1650 RETURN_STRING_IF_EQUAL(GL_LUMINANCE16_ALPHA16);
1651 RETURN_STRING_IF_EQUAL(GL_LUMINANCE_ALPHA16F_ARB);
1652 RETURN_STRING_IF_EQUAL(GL_LUMINANCE_ALPHA32F_ARB);
1653 RETURN_STRING_IF_EQUAL(GL_LUMINANCE_ALPHA);
1654 RETURN_STRING_IF_EQUAL(GL_DEPTH_COMPONENT);
1655 RETURN_STRING_IF_EQUAL(GL_DEPTH_COMPONENT16);
1656 RETURN_STRING_IF_EQUAL(GL_TEXTURE_2D);
1657 RETURN_STRING_IF_EQUAL(GL_TEXTURE_RECTANGLE_ARB);
1658 RETURN_STRING_IF_EQUAL(GL_FLOAT);
1659 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_BYTE);
1660 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_BYTE_3_3_2);
1661 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT);
1662 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_4_4_4_4);
1663 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_5_5_5_1);
1664 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_8_8_APPLE);
1665 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_INT_8_8_8_8);
1666 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_INT_10_10_10_2);
1667 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_BYTE_2_3_3_REV);
1668 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_5_6_5);
1669 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_5_6_5_REV);
1670 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_4_4_4_4_REV);
1671 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_SHORT_1_5_5_5_REV);
1672 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_INT_8_8_8_8_REV);
1673 RETURN_STRING_IF_EQUAL(GL_UNSIGNED_INT_2_10_10_10_REV);
1674 RETURN_STRING_IF_EQUAL(GL_COMPRESSED_RGB_S3TC_DXT1_EXT);
1675 RETURN_STRING_IF_EQUAL(GL_COMPRESSED_RGBA_S3TC_DXT5_EXT);
1676 RETURN_STRING_IF_EQUAL(GL_COMPRESSED_RED_RGTC1);
1677 RETURN_STRING_IF_EQUAL(GL_YCBCR_422_APPLE);
1678 RETURN_STRING_IF_EQUAL(GL_ALREADY_SIGNALED);
1679 RETURN_STRING_IF_EQUAL(GL_TIMEOUT_EXPIRED);
1680 RETURN_STRING_IF_EQUAL(GL_CONDITION_SATISFIED);
1681 RETURN_STRING_IF_EQUAL(GL_WAIT_FAILED);
1682
1683 return VuoText_format("(unknown: %x)", constant);
1684}