diff --git a/renderdoc/driver/gl/cgl_platform.cpp b/renderdoc/driver/gl/cgl_platform.cpp index 97d5786e3..0ab06ba3e 100644 --- a/renderdoc/driver/gl/cgl_platform.cpp +++ b/renderdoc/driver/gl/cgl_platform.cpp @@ -30,19 +30,14 @@ #include "apple_gl_hook_defs.h" // helpers defined in cgl_platform.mm -extern "C" int NSGL_getLayerWidth(void *layer); -extern "C" int NSGL_getLayerHeight(void *layer); -extern "C" void *NSGL_createContext(void *view, void *shareContext); -extern "C" void NSGL_makeCurrentContext(void *context); -extern "C" void NSGL_update(void *context); -extern "C" void NSGL_flushBuffer(void *context); -extern "C" void NSGL_destroyContext(void *context); - -// helper for cgl_platform.mm -extern "C" void NSGL_LogText(const char *text) -{ - RDCLOG("CGL: %s", text); -} +void Apple_getWindowSize(void *view, int &width, int &height); +void Apple_stopTrackingWindowSize(void *view); +void NSGL_init(); +void *NSGL_createContext(void *view, void *shareContext); +void NSGL_makeCurrentContext(void *context); +void NSGL_update(void *context); +void NSGL_flushBuffer(void *context); +void NSGL_destroyContext(void *context); // gl functions (used for quad rendering on legacy contexts) extern "C" void glPushMatrix(); @@ -161,15 +156,15 @@ class CGLPlatform : public GLPlatform { RDCASSERT(context.nsgl_ctx); NSGL_destroyContext(context.nsgl_ctx); + Apple_stopTrackingWindowSize(context.wnd); } void SwapBuffers(GLWindowingData context) { NSGL_flushBuffer(context.nsgl_ctx); } void WindowResized(GLWindowingData context) { NSGL_update(context.nsgl_ctx); } void GetOutputWindowDimensions(GLWindowingData context, int32_t &w, int32_t &h) { - if(context.layer) + if(context.wnd) { - w = NSGL_getLayerWidth(context.layer); - h = NSGL_getLayerHeight(context.layer); + Apple_getWindowSize(context.wnd, w, h); } else { @@ -201,7 +196,6 @@ class CGLPlatform : public GLPlatform ret.nsgl_ctx = NSGL_createContext(window.macOS.view, share_context.nsgl_ctx); ret.wnd = window.macOS.view; - ret.layer = window.macOS.layer; return ret; } @@ -223,6 +217,7 @@ class CGLPlatform : public GLPlatform { RDCASSERT(api == RDCDriver::OpenGL); + NSGL_init(); replayContext.nsgl_ctx = NSGL_createContext(NULL, NULL); return ReplayStatus::Succeeded; diff --git a/renderdoc/driver/gl/cgl_platform.mm b/renderdoc/driver/gl/cgl_platform.mm index 3acff0317..8137b0780 100644 --- a/renderdoc/driver/gl/cgl_platform.mm +++ b/renderdoc/driver/gl/cgl_platform.mm @@ -1,27 +1,412 @@ +/****************************************************************************** + * The MIT License (MIT) + * + * Copyright (c) 2019-2021 Baldur Karlsson + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + ******************************************************************************/ + +#include "api/replay/rdcstr.h" +#include "common/common.h" +#include "common/threading.h" +#include "os/os_specific.h" + #import -extern "C" void NSGL_LogText(const char *text); +#define RD_THREAD_RANDOM_SLEEP (0) +#define RD_USE_CONTEXT_LOCK_COUNTS (0) #pragma GCC diagnostic ignored "-Wdeprecated-declarations" -extern "C" int NSGL_getLayerWidth(void *layer) -{ - CALayer *caLayer = (CALayer *)layer; - assert([caLayer isKindOfClass:[CALayer class]]); +static uint64_t s_currentContextTLSkey; - return caLayer.bounds.size.width; +static Threading::CriticalSection s_WindowPtrsArrayLock; +static rdcarray s_WindowPtrs; +static rdcarray s_WindowWidths; +static rdcarray s_WindowHeights; +static int s_ReplaySalt = 0; + +static Threading::CriticalSection s_ContextPtrsArrayLock; +static rdcarray s_ContextPtrs; +static rdcarray s_ContextLocks; +#if RD_USE_CONTEXT_LOCK_COUNTS +static rdcarray s_ContextLocksCount; +#endif // #if RD_USE_CONTEXT_LOCK_COUNTS + +static void scheduleContextSetView(int contextIndex, int replaySalt, NSView *view); +static void scheduleContextUpdate(int contextIndex, int replaySalt); + +static void RandomSleep() +{ +#if RD_THREAD_RANDOM_SLEEP + usleep(rand() % 1000); +#endif // #if RD_THREAD_RANDOM_SLEEP } -extern "C" int NSGL_getLayerHeight(void *layer) +static NSOpenGLContext *GetNSOpenGLContext(void *context) { - CALayer *caLayer = (CALayer *)layer; - assert([caLayer isKindOfClass:[CALayer class]]); - - return caLayer.bounds.size.height; + NSOpenGLContext *nsglContext = (NSOpenGLContext *)context; + RDCASSERT([nsglContext isKindOfClass:[NSOpenGLContext class]]); + return nsglContext; } -extern "C" void *NSGL_createContext(void *view, void *shareContext) +static NSView *GetNSView(void *view) { + NSView *nsView = (NSView *)view; + RDCASSERT([nsView isKindOfClass:[NSView class]]); + return nsView; +} + +static void IncrementContextLockCount(int contextIndex) +{ +#if RD_USE_CONTEXT_LOCK_COUNTS + Atomic::Inc32(s_ContextLocksCount.data() + contextIndex); +#endif //#if RD_USE_CONTEXT_LOCK_COUNTS +} + +static void DecrementContextLockCount(int contextIndex) +{ +#if RD_USE_CONTEXT_LOCK_COUNTS + Atomic::Dec32(s_ContextLocksCount.data() + contextIndex); +#endif //#if RD_USE_CONTEXT_LOCK_COUNTS +} + +static void LockContext(int contextIndex) +{ + s_ContextLocks[contextIndex]->Lock(); + IncrementContextLockCount(contextIndex); +} + +static bool TryLockContext(int contextIndex) +{ + if(s_ContextLocks[contextIndex]->Trylock()) + { + IncrementContextLockCount(contextIndex); + return true; + } + return false; +} + +static void UnLockContext(int contextIndex) +{ + DecrementContextLockCount(contextIndex); + s_ContextLocks[contextIndex]->Unlock(); +} + +// s_WindowPtrsArrayLock must be locked by the caller +static bool findWindowIndex(NSView *nsView, int &index) +{ + const int windowCount = s_WindowPtrs.count(); + int firstFreeIndex = windowCount; + for(int i = 0; i < windowCount; ++i) + { + if(s_WindowPtrs[i] == nsView) + { + index = i; + return true; + } + if((s_WindowPtrs[i] == nil) && (i < firstFreeIndex)) + { + firstFreeIndex = i; + } + } + index = firstFreeIndex; + return false; +} + +static int getWindowIndex(NSView *nsView) +{ + SCOPED_LOCK(s_WindowPtrsArrayLock); + int index = -1; + if(findWindowIndex(nsView, index)) + { + return index; + } + const int windowCount = s_WindowPtrs.count(); + if(index < windowCount) + { + RDCASSERT(index >= 0); + RDCASSERT(index < s_WindowWidths.count()); + RDCASSERT(index < s_WindowHeights.count()); + s_WindowPtrs[index] = nsView; + s_WindowWidths[index] = 0; + s_WindowHeights[index] = 0; + } + else + { + RDCASSERT(windowCount == s_WindowWidths.count()); + RDCASSERT(windowCount == s_WindowHeights.count()); + s_WindowPtrs.push_back(nsView); + s_WindowWidths.push_back(0); + s_WindowHeights.push_back(0); + } + return index; +} + +static void SetCurrentContextIndexTLS(int contextIndex) +{ + Threading::SetTLSValue(s_currentContextTLSkey, (void *)(uintptr_t)contextIndex); +} + +static int GetCurrentContextIndexTLS() +{ + return (int)(uintptr_t)Threading::GetTLSValue(s_currentContextTLSkey); +} + +static int getContextIndex(NSOpenGLContext *nsglContext) +{ + SCOPED_LOCK(s_ContextPtrsArrayLock); + const int contextCount = s_ContextPtrs.count(); + int firstFreeIndex = contextCount; + for(int i = 0; i < contextCount; ++i) + { + if(s_ContextPtrs[i] == nsglContext) + { + return i; + } + if((s_ContextPtrs[i] == nil) && (i < firstFreeIndex)) + { + firstFreeIndex = i; + } + } + int index = firstFreeIndex; + if(index < contextCount) + { + RDCASSERT(index >= 0); + RDCASSERT(index < s_ContextLocks.count()); + s_ContextPtrs[index] = nsglContext; + RDCASSERT(s_ContextLocks[index]); +#if RD_USE_CONTEXT_LOCK_COUNTS + RDCASSERT(0 == s_ContextLocksCount[index]); + RDCASSERT(index < s_ContextLocksCount.count()); + s_ContextLocksCount[index] = 0; +#endif // #if RD_USE_CONTEXT_LOCK_COUNTS + } + else + { + RDCASSERT(contextCount == s_ContextLocks.count()); +#if RD_USE_CONTEXT_LOCK_COUNTS + RDCASSERT(contextCount == s_ContextLocksCount.count()); +#endif // #if RD_USE_CONTEXT_LOCK_COUNTS + s_ContextPtrs.push_back(nsglContext); + s_ContextLocks.push_back(new Threading::CriticalSection); +#if RD_USE_CONTEXT_LOCK_COUNTS + s_ContextLocksCount.push_back(0); +#endif // #if RD_USE_CONTEXT_LOCK_COUNTS + } + return index; +} + +static NSOpenGLContext *getLockedContext(int contextIndex) +{ + SCOPED_LOCK(s_ContextPtrsArrayLock); + RDCASSERT(contextIndex >= 0 && contextIndex < s_ContextPtrs.count()); + if(contextIndex >= 0 && contextIndex < s_ContextPtrs.count()) + { + NSOpenGLContext *context = s_ContextPtrs[contextIndex]; + if(context && TryLockContext(contextIndex)) + { + return context; + } + } + return nil; +} + +static void viewSetWantBestResolutionMT(NSView *view) +{ + RandomSleep(); + [view setWantsBestResolutionOpenGLSurface:true]; +} + +static void viewGetWindowSizeMT(int windowIndex, int replaySalt) +{ + RandomSleep(); + const int currentReplaySalt = s_ReplaySalt; + if(replaySalt != currentReplaySalt) + { + return; + } + SCOPED_LOCK(s_WindowPtrsArrayLock); + RDCASSERT(windowIndex >= 0 && windowIndex < s_WindowPtrs.count()); + if(windowIndex >= 0 && windowIndex < s_WindowPtrs.count()) + { + NSView *view = s_WindowPtrs[windowIndex]; + if(view) + { + const NSRect contentRect = [view frame]; + CGSize viewSize = [view convertSizeToBacking:contentRect.size]; + + s_WindowWidths[windowIndex] = viewSize.width; + s_WindowHeights[windowIndex] = viewSize.height; + } + } +} + +static void contextUpdateMT(int contextIndex, int replaySalt) +{ + RandomSleep(); + const int currentReplaySalt = s_ReplaySalt; + if(replaySalt != currentReplaySalt) + { + return; + } + NSOpenGLContext *lockedContext = getLockedContext(contextIndex); + if(lockedContext) + { + [lockedContext update]; + UnLockContext(contextIndex); + } + else + { + scheduleContextUpdate(contextIndex, replaySalt); + } +} + +static void contextSetViewMT(int contextIndex, int replaySalt, NSView *view) +{ + RandomSleep(); + const int currentReplaySalt = s_ReplaySalt; + if(replaySalt != currentReplaySalt) + { + return; + } + NSOpenGLContext *lockedContext = getLockedContext(contextIndex); + if(lockedContext) + { + [lockedContext setView:view]; + [lockedContext update]; + UnLockContext(contextIndex); + } + else + { + scheduleContextSetView(contextIndex, replaySalt, view); + } +} + +static void scheduleContextSetView(int contextIndex, int replaySalt, NSView *view) +{ + RandomSleep(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + contextSetViewMT(contextIndex, replaySalt, view); + }); +} + +static void scheduleContextUpdate(int contextIndex, int replaySalt) +{ + RandomSleep(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + contextUpdateMT(contextIndex, replaySalt); + }); +} + +static void scheduleViewSetWantBestResolution(NSView *view) +{ + RandomSleep(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + viewSetWantBestResolutionMT(view); + }); +} + +static void scheduleViewGetWindowSizeMT(int windowIndex, int replaySalt) +{ + RandomSleep(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + viewGetWindowSizeMT(windowIndex, replaySalt); + }); +} + +void Apple_getWindowSize(void *view, int &width, int &height) +{ + RandomSleep(); + if(!view) + { + width = 0; + height = 0; + return; + } + NSView *nsView = GetNSView(view); + const int windowIndex = getWindowIndex(nsView); + width = s_WindowWidths[windowIndex]; + height = s_WindowHeights[windowIndex]; + scheduleViewGetWindowSizeMT(windowIndex, s_ReplaySalt); +} + +void Apple_stopTrackingWindowSize(void *view) +{ + RandomSleep(); + if(!view) + return; + NSView *nsView = (NSView *)view; + { + SCOPED_LOCK(s_WindowPtrsArrayLock); + int windowIndex = -1; + if(findWindowIndex(nsView, windowIndex)) + { + s_WindowPtrs[windowIndex] = nil; + s_WindowWidths[windowIndex] = 0; + s_WindowHeights[windowIndex] = 0; + } + } +} + +void NSGL_init() +{ + RandomSleep(); + static bool s_allocatedTLSKey = false; + if(!s_allocatedTLSKey) + { + s_ReplaySalt = 0; + s_allocatedTLSKey = true; + s_currentContextTLSkey = Threading::AllocateTLSSlot(); + const int initialWindowCountMax = 8; + s_WindowPtrs.reserve(initialWindowCountMax); + s_WindowWidths.reserve(initialWindowCountMax); + s_WindowHeights.reserve(initialWindowCountMax); + + const int initialContextCountMax = 8; + s_ContextPtrs.reserve(initialContextCountMax); + s_ContextLocks.reserve(initialContextCountMax); +#if RD_USE_CONTEXT_LOCK_COUNTS + s_ContextLocksCount.reserve(initialContextCountMax); +#endif // #if RD_USE_CONTEXT_LOCK_COUNTS + } + Atomic::Inc32(&s_ReplaySalt); + SetCurrentContextIndexTLS(-1); + { + SCOPED_LOCK(s_WindowPtrsArrayLock); + s_WindowPtrs.resize(0); + s_WindowWidths.resize(0); + s_WindowHeights.resize(0); + } + { + SCOPED_LOCK(s_ContextPtrsArrayLock); + s_ContextPtrs.resize(0); + s_ContextLocks.resize(0); +#if RD_USE_CONTEXT_LOCK_COUNTS + s_ContextLocksCount.resize(0); +#endif // #if RD_USE_CONTEXT_LOCK_COUNTS + } +} + +void *NSGL_createContext(void *view, void *shareContext) +{ + RandomSleep(); NSView *nsView = (NSView *)view; assert(nsView == nil || [nsView isKindOfClass:[NSView class]]); @@ -29,17 +414,21 @@ extern "C" void *NSGL_createContext(void *view, void *shareContext) assert(share == nil || [share isKindOfClass:[NSOpenGLContext class]]); NSOpenGLPixelFormatAttribute attr[] = { - NSOpenGLPFANoRecovery, - NSOpenGLPFADoubleBuffer, NSOpenGLPFAAccelerated, - NSOpenGLPFAAllowOfflineRenderers, - + NSOpenGLPFAClosestPolicy, NSOpenGLPFAOpenGLProfile, NSOpenGLProfileVersion4_1Core, - NSOpenGLPFAColorSize, - 32, - + 24, + NSOpenGLPFAAlphaSize, + 8, + NSOpenGLPFADepthSize, + 24, + NSOpenGLPFAStencilSize, + 8, + NSOpenGLPFADoubleBuffer, + NSOpenGLPFASampleBuffers, + 0, 0, }; @@ -47,7 +436,7 @@ extern "C" void *NSGL_createContext(void *view, void *shareContext) if(pix == nil) { - NSGL_LogText("Failed to create NSOpenGLPixelFormat"); + RDCERR("CGL: Failed to create NSOpenGLPixelFormat"); return nil; } @@ -56,53 +445,82 @@ extern "C" void *NSGL_createContext(void *view, void *shareContext) if(context == nil) { - NSGL_LogText("Failed to create NSOpenGLContext"); + RDCERR("CGL: Failed to create NSOpenGLContext"); return nil; } GLint aboveWindow = 1; [context setValues:&aboveWindow forParameter:NSOpenGLCPSurfaceOrder]; - [context setView:nsView]; - [context update]; + scheduleViewSetWantBestResolution(nsView); + + NSOpenGLContext *nsglContext = GetNSOpenGLContext(context); + const int contextIndex = getContextIndex(nsglContext); + scheduleContextSetView(contextIndex, s_ReplaySalt, nsView); return context; } -extern "C" void NSGL_makeCurrentContext(void *context) +void NSGL_makeCurrentContext(void *context) { - NSOpenGLContext *nsglContext = (NSOpenGLContext *)context; - assert([nsglContext isKindOfClass:[NSOpenGLContext class]]); + RandomSleep(); + const int currentThreadContextIndex = GetCurrentContextIndexTLS(); + if(currentThreadContextIndex >= 0) + UnLockContext(currentThreadContextIndex); + NSOpenGLContext *nsglContext = GetNSOpenGLContext(context); + const int contextIndex = getContextIndex(nsglContext); + SetCurrentContextIndexTLS(contextIndex); + LockContext(contextIndex); [nsglContext makeCurrentContext]; + scheduleContextUpdate(contextIndex, s_ReplaySalt); } -extern "C" void NSGL_update(void *context) +void NSGL_update(void *context) { - NSOpenGLContext *nsglContext = (NSOpenGLContext *)context; - assert([nsglContext isKindOfClass:[NSOpenGLContext class]]); - - [nsglContext update]; + RandomSleep(); + NSOpenGLContext *nsglContext = GetNSOpenGLContext(context); + const int contextIndex = getContextIndex(nsglContext); + scheduleContextUpdate(contextIndex, s_ReplaySalt); } -extern "C" void NSGL_flushBuffer(void *context) +void NSGL_flushBuffer(void *context) { - NSOpenGLContext *nsglContext = (NSOpenGLContext *)context; - assert([nsglContext isKindOfClass:[NSOpenGLContext class]]); - + RandomSleep(); + NSOpenGLContext *nsglContext = GetNSOpenGLContext(context); + const int contextIndex = getContextIndex(nsglContext); + LockContext(contextIndex); [nsglContext flushBuffer]; + UnLockContext(contextIndex); } -extern "C" void NSGL_destroyContext(void *context) +void NSGL_destroyContext(void *context) { + RandomSleep(); @autoreleasepool { - NSOpenGLContext *nsglContext = (NSOpenGLContext *)context; - assert([nsglContext isKindOfClass:[NSOpenGLContext class]]); + NSOpenGLContext *nsglContext = GetNSOpenGLContext(context); + const int contextIndex = getContextIndex(nsglContext); + const int currentThreadContextIndex = GetCurrentContextIndexTLS(); + if(currentThreadContextIndex == contextIndex) + { + SetCurrentContextIndexTLS(-1); + UnLockContext(contextIndex); + } + LockContext(contextIndex); [nsglContext makeCurrentContext]; [nsglContext clearDrawable]; [nsglContext update]; [nsglContext release]; + UnLockContext(contextIndex); + + { + SCOPED_LOCK(s_ContextPtrsArrayLock); + s_ContextPtrs[contextIndex] = nil; +#if RD_USE_CONTEXT_LOCK_COUNTS + RDCASSERT(0 == s_ContextLocksCount[contextIndex]); +#endif //#if RD_USE_CONTEXT_LOCK_COUNTS + } } } diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index c122f5e17..0b05f911b 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -198,8 +198,6 @@ struct GLWindowingData ctx = NULL; wnd = NULL; pix = NULL; - - layer = NULL; } union @@ -210,8 +208,6 @@ struct GLWindowingData void *wnd; // during capture, this is the CGL window ID. During replay, it's the NSView CGLPixelFormatObj pix; - - void *layer; // during replay only, this is the CALayer }; #define DECL_HOOK_EXPORT(function) \