diff --git a/renderdoc/api/app/renderdoc_app.h b/renderdoc/api/app/renderdoc_app.h index 81ffe9e70..77c17848e 100644 --- a/renderdoc/api/app/renderdoc_app.h +++ b/renderdoc/api/app/renderdoc_app.h @@ -244,11 +244,26 @@ typedef uint32_t (RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *logfi extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetCaptureOptions(const CaptureOptions *opts); typedef void (RENDERDOC_CC *pRENDERDOC_SetCaptureOptions)(const CaptureOptions *opts); +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_TriggerCapture(); +typedef void (RENDERDOC_CC *pRENDERDOC_TriggerCapture)(); + +// In the below functions 'device pointer' corresponds to the API specific handle, e.g. +// ID3D11Device, or the GL context pointer. +// The 'window handle' is the OS's native window handle (HWND or GLXDrawable). + +// This must match precisely to a pair, and it sets the RenderDoc in-app overlay to select that +// window as 'active' and respond to keypresses. extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_SetActiveWindow(void *device, void *wndHandle); typedef void (RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(void *device, void *wndHandle); -extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_TriggerCapture(); -typedef void (RENDERDOC_CC *pRENDERDOC_TriggerCapture)(); +// Either parameter can be NULL to wild-card match, such that you can capture from any +// device to a particular window, or a particular device to any window. +// In either case, if there are two or more possible matching (device,window) pairs it +// is undefined which one will be captured. +// You can pass (NULL, NULL) if you know you only have one device and one window, and +// it will match. Likewise if you have not created a window at all (only off-screen +// rendering), then NULL window pointer will capture, whether you pass a NULL device +// or specify a device among multiple. extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartFrameCapture(void *device, void *wndHandle); typedef void (RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(void *device, void *wndHandle); diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index a5b4f3675..9f0c97c54 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -30,6 +30,8 @@ #include +#include + #include "data/version.h" #include "crash_handler.h" @@ -310,39 +312,51 @@ void RenderDoc::Shutdown() } } -void RenderDoc::StartFrameCapture(void *dev, void *wnd) +IFrameCapturer *RenderDoc::MatchFrameCapturer(void *dev, void *wnd) { - if(dev == NULL || wnd == NULL) - { - // if we have a single window frame capturer, use that in preference - if(m_WindowFrameCapturers.size() == 1) - { - auto it = m_WindowFrameCapturers.begin(); - it->second.FrameCapturer->StartFrameCapture(it->first.dev, it->first.wnd); - } - // otherwise, see if we only have one default capturer - else if(m_DefaultFrameCapturers.size() == 1) - { - (*m_DefaultFrameCapturers.begin())->StartFrameCapture(dev, wnd); - } - // otherwise we can't capture with NULL handles - else - { - RDCERR("Multiple frame capture methods registered, can't capture by NULL handles"); - } - return; - } - DeviceWnd dw(dev, wnd); - auto it = m_WindowFrameCapturers.find(dw); - if(it == m_WindowFrameCapturers.end()) + // lower_bound and the DeviceWnd ordering (pointer compares, dev over wnd) means that if either + // element in dw is NULL we can go forward from this iterator and find the first wildcardMatch + // note that if dev is specified and wnd is NULL, this will actually point at the first + // wildcardMatch already and we can use it immediately (since which window of multiple we + // choose is undefined, so up to us). If dev is NULL there is no window ordering (since dev is + // the primary sorting value) so we just iterate through the whole map. It should be small in + // the majority of cases + auto it = m_WindowFrameCapturers.lower_bound(dw); + + while(it != m_WindowFrameCapturers.end()) { - RDCERR("Couldn't find frame capturer for device %p window %p", dev, wnd); - return; + if(it->first.wildcardMatch(dw)) break; + ++it; } - it->second.FrameCapturer->StartFrameCapture(dev, wnd); + if(it == m_WindowFrameCapturers.end()) + { + // handle off-screen rendering where there are no device/window pairs in + // m_WindowFrameCapturers, instead we use the first matching device frame capturer + if(wnd == NULL) + { + auto defaultit = m_DeviceFrameCapturers.find(dev); + if(defaultit == m_DeviceFrameCapturers.end() && !m_DeviceFrameCapturers.empty()) + defaultit = m_DeviceFrameCapturers.begin(); + + if(defaultit != m_DeviceFrameCapturers.end()) + return defaultit->second; + } + + RDCERR("Couldn't find matching frame capturer for device %p window %p", dev, wnd); + return NULL; + } + + return it->second.FrameCapturer; +} + +void RenderDoc::StartFrameCapture(void *dev, void *wnd) +{ + IFrameCapturer *frameCap = MatchFrameCapturer(dev, wnd); + if(frameCap) + frameCap->StartFrameCapture(dev, wnd); } void RenderDoc::SetActiveWindow(void *dev, void *wnd) @@ -361,37 +375,10 @@ void RenderDoc::SetActiveWindow(void *dev, void *wnd) bool RenderDoc::EndFrameCapture(void *dev, void *wnd) { - if(dev == NULL || wnd == NULL) - { - // if we have a single window frame capturer, use that in preference - if(m_WindowFrameCapturers.size() == 1) - { - auto it = m_WindowFrameCapturers.begin(); - return it->second.FrameCapturer->EndFrameCapture(it->first.dev, it->first.wnd); - } - // otherwise, see if we only have one default capturer - else if(m_DefaultFrameCapturers.size() == 1) - { - (*m_DefaultFrameCapturers.begin())->EndFrameCapture(dev, wnd); - } - // otherwise we can't capture with NULL handles - else - { - RDCERR("Multiple frame capture methods registered, can't capture by NULL handles"); - } - return false; - } - - DeviceWnd dw(dev, wnd); - - auto it = m_WindowFrameCapturers.find(dw); - if(it == m_WindowFrameCapturers.end()) - { - RDCERR("Couldn't find frame capturer for device %p, window %p", dev, wnd); - return false; - } - - return it->second.FrameCapturer->EndFrameCapture(dev, wnd); + IFrameCapturer *frameCap = MatchFrameCapturer(dev, wnd); + if(frameCap) + return frameCap->EndFrameCapture(dev, wnd); + return false; } bool RenderDoc::IsRemoteAccessConnected() @@ -779,14 +766,26 @@ void RenderDoc::SuccessfullyWrittenLog() } } -void RenderDoc::AddDefaultFrameCapturer(IFrameCapturer *cap) +void RenderDoc::AddDeviceFrameCapturer(void *dev, IFrameCapturer *cap) { - m_DefaultFrameCapturers.insert(cap); + if(dev == NULL || cap == NULL) + { + RDCERR("Invalid FrameCapturer combination: %#p / %#p", dev, cap); + return; + } + + m_DeviceFrameCapturers[dev] = cap; } -void RenderDoc::RemoveDefaultFrameCapturer(IFrameCapturer *cap) +void RenderDoc::RemoveDeviceFrameCapturer(void *dev) { - m_DefaultFrameCapturers.erase(cap); + if(dev == NULL) + { + RDCERR("Invalid device pointer: %#p / %#p", dev); + return; + } + + m_DeviceFrameCapturers.erase(dev); } void RenderDoc::AddFrameCapturer(void *dev, void *wnd, IFrameCapturer *cap) @@ -803,7 +802,7 @@ void RenderDoc::AddFrameCapturer(void *dev, void *wnd, IFrameCapturer *cap) if(it != m_WindowFrameCapturers.end()) { if(it->second.FrameCapturer != cap) - RDCERR("New different FrameCapturer being registered for known window!"); + RDCERR("New different FrameCapturer being registered for known device/window pair!"); it->second.RefCount++; } diff --git a/renderdoc/core/core.h b/renderdoc/core/core.h index e85d7d25d..063fc2faf 100644 --- a/renderdoc/core/core.h +++ b/renderdoc/core/core.h @@ -241,8 +241,8 @@ class RenderDoc // add window-less frame capturers for use via users capturing // manually through the renderdoc API with NULL device/window handles - void AddDefaultFrameCapturer(IFrameCapturer *cap); - void RemoveDefaultFrameCapturer(IFrameCapturer *cap); + void AddDeviceFrameCapturer(void *dev, IFrameCapturer *cap); + void RemoveDeviceFrameCapturer(void *dev); void StartFrameCapture(void *dev, void *wnd); void SetActiveWindow(void *dev, void *wnd); @@ -334,16 +334,30 @@ class RenderDoc { return dev == o.dev && wnd == o.wnd; } + bool operator <(const DeviceWnd &o) const { if(dev != o.dev) return dev < o.dev; return wnd < o.wnd; } + + bool wildcardMatch(const DeviceWnd &o) const + { + if(dev == NULL || o.dev == NULL) + return wnd == NULL || o.wnd == NULL || wnd == o.wnd; + + if(wnd == NULL || o.wnd == NULL) + return dev == NULL || o.dev == NULL || dev == o.dev; + + return *this == o; + } }; map m_WindowFrameCapturers; DeviceWnd m_ActiveWindow; - set m_DefaultFrameCapturers; + map m_DeviceFrameCapturers; + + IFrameCapturer *MatchFrameCapturer(void *dev, void *wnd); volatile bool m_RemoteServerThreadShutdown; volatile bool m_RemoteClientThreadShutdown; diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index ef5d38523..838ebea98 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -342,7 +342,7 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device* realDevice, D3D11InitPara m_DeviceRecord->NumSubResources = 0; m_DeviceRecord->SubResources = NULL; - RenderDoc::Inst().AddDefaultFrameCapturer(this); + RenderDoc::Inst().AddDeviceFrameCapturer((ID3D11Device *)this, this); } ID3D11DeviceContext *context = NULL; @@ -416,7 +416,7 @@ WrappedID3D11Device::~WrappedID3D11Device() if(m_pCurrentWrappedDevice == this) m_pCurrentWrappedDevice = NULL; - RenderDoc::Inst().RemoveDefaultFrameCapturer(this); + RenderDoc::Inst().RemoveDeviceFrameCapturer((ID3D11Device *)this); for(auto it = m_CachedStateObjects.begin(); it != m_CachedStateObjects.end(); ++it) if(*it) diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index dec90ce7f..2c2ecd62c 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -760,8 +760,6 @@ WrappedOpenGL::WrappedOpenGL(const char *logfile, const GLHookSet &funcs) m_FakeVAOID = GetResourceManager()->RegisterResource(VertexArrayRes(NULL, 0)); GetResourceManager()->AddResourceRecord(m_FakeVAOID); GetResourceManager()->MarkDirtyResource(m_FakeVAOID); - - RenderDoc::Inst().AddDefaultFrameCapturer(this); } else { @@ -909,8 +907,6 @@ WrappedOpenGL::~WrappedOpenGL() if(m_FakeBB_Color) m_Real.glDeleteTextures(1, &m_FakeBB_Color); if(m_FakeBB_DepthStencil) m_Real.glDeleteTextures(1, &m_FakeBB_DepthStencil); - RenderDoc::Inst().RemoveDefaultFrameCapturer(this); - SAFE_DELETE(m_pSerialiser); GetResourceManager()->ReleaseCurrentResource(m_DeviceResourceID); @@ -962,6 +958,8 @@ void WrappedOpenGL::DeleteContext(void *contextHandle) { ContextData &ctxdata = m_ContextData[contextHandle]; + RenderDoc::Inst().RemoveDeviceFrameCapturer(ctxdata.ctx); + if(ctxdata.built && ctxdata.ready) { if(ctxdata.Program) @@ -1007,6 +1005,8 @@ void WrappedOpenGL::CreateContext(GLWindowingData winData, void *shareContext, G ctxdata.ctx = winData.ctx; ctxdata.isCore = core; ctxdata.attribsCreate = attribsCreate; + + RenderDoc::Inst().AddDeviceFrameCapturer(ctxdata.ctx, this); } void WrappedOpenGL::RegisterContext(GLWindowingData winData, void *shareContext, bool core, bool attribsCreate) @@ -2186,7 +2186,7 @@ void WrappedOpenGL::SwapBuffers(void *windowHandle) // kill any current capture that isn't application defined if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture) - EndFrameCapture(this, windowHandle); + EndFrameCapture(ctxdata.ctx, windowHandle); // for now, only allow one captured frame at all if(!m_FrameRecord.empty()) @@ -2194,7 +2194,7 @@ void WrappedOpenGL::SwapBuffers(void *windowHandle) if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE) { - StartFrameCapture(this, windowHandle); + StartFrameCapture(ctxdata.ctx, windowHandle); m_AppControlledCapture = false; }