Support switching between multiple APIs in the same program. Closes #124

* The core class is now responsible for tracking the active window and
  cycling through, so it can cycle through multiple APIs.
* For GL we only set up associations for capturable windows, and only
  those that present (since helper windows are often created). Since
  GL doesn't really have a 'this window is done rendering' call, we just
  decay old windows that haven't presented in >5 seconds.
* Also on GL, legacy/uncapturable windows aren't associated so they will
  not cycle to at all.
* The embedding API now expects a device pointer and window handle to
  set active window or start/end a capture. You can still pass NULLs if
  there is only one API active.
This commit is contained in:
baldurk
2015-04-13 11:31:30 +01:00
parent d91f36281b
commit b41b5242d1
11 changed files with 527 additions and 329 deletions
+12 -7
View File
@@ -226,7 +226,12 @@ enum InAppOverlay
// API breaking change history:
// Version 1 -> 2 - strings changed from wchar_t* to char* (UTF-8)
#define RENDERDOC_API_VERSION 2
// Version 2 -> 3 - StartFrameCapture, EndFrameCapture and SetActiveWindow take
// 'device' pointer as well as window handles.
// This is either ID3D11Device* or the GL context (HGLRC/GLXContext)
// You can still pass NULL to both to capture the default, as long as
// there's only one device/window pair alive.
#define RENDERDOC_API_VERSION 3
//////////////////////////////////////////////////////////////////////////
// In-program functions
@@ -250,17 +255,17 @@ typedef bool32 (RENDERDOC_CC *pRENDERDOC_GetCapture)(uint32_t idx, char *logfile
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_SetActiveWindow(void *wndHandle);
typedef void (RENDERDOC_CC *pRENDERDOC_SetActiveWindow)(void *wndHandle);
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)();
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartFrameCapture(void *wndHandle);
typedef void (RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(void *wndHandle);
extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_StartFrameCapture(void *device, void *wndHandle);
typedef void (RENDERDOC_CC *pRENDERDOC_StartFrameCapture)(void *device, void *wndHandle);
extern "C" RENDERDOC_API bool32 RENDERDOC_CC RENDERDOC_EndFrameCapture(void *wndHandle);
typedef bool32 (RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(void *wndHandle);
extern "C" RENDERDOC_API bool32 RENDERDOC_CC RENDERDOC_EndFrameCapture(void *device, void *wndHandle);
typedef bool32 (RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(void *device, void *wndHandle);
extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_GetOverlayBits();
typedef uint32_t (RENDERDOC_CC *pRENDERDOC_GetOverlayBits)();
+99 -27
View File
@@ -160,7 +160,6 @@ RenderDoc::RenderDoc()
m_Replay = false;
m_Focus = false;
m_Cap = false;
m_FocusKeys.clear();
@@ -311,66 +310,88 @@ void RenderDoc::Shutdown()
}
}
void RenderDoc::StartFrameCapture(void *wnd)
void RenderDoc::StartFrameCapture(void *dev, void *wnd)
{
if(wnd == NULL)
if(dev == NULL || wnd == NULL)
{
// if we have a single window frame capturer, use that in preference
if(m_WindowFrameCapturers.size() == 1)
{
m_WindowFrameCapturers.begin()->second.FrameCapturer->StartFrameCapture(wnd);
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 window");
RDCERR("Multiple frame capture methods registered, can't capture by NULL handles");
}
return;
}
auto it = m_WindowFrameCapturers.find(wnd);
DeviceWnd dw(dev, wnd);
auto it = m_WindowFrameCapturers.find(dw);
if(it == m_WindowFrameCapturers.end())
{
RDCERR("Couldn't find frame capturer for this window %p", wnd);
RDCERR("Couldn't find frame capturer for device %p window %p", dev, wnd);
return;
}
it->second.FrameCapturer->StartFrameCapture(wnd);
it->second.FrameCapturer->StartFrameCapture(dev, wnd);
}
void RenderDoc::SetActiveWindow(void *wnd)
void RenderDoc::SetActiveWindow(void *dev, void *wnd)
{
auto it = m_WindowFrameCapturers.find(wnd);
DeviceWnd dw(dev, wnd);
auto it = m_WindowFrameCapturers.find(dw);
if(it == m_WindowFrameCapturers.end())
{
RDCERR("Couldn't find frame capturer for this window %p", wnd);
RDCERR("Couldn't find frame capturer for device %p window %p", dev, wnd);
return;
}
it->second.FrameCapturer->SetActiveWindow(wnd);
m_ActiveWindow = dw;
}
bool RenderDoc::EndFrameCapture(void *wnd)
bool RenderDoc::EndFrameCapture(void *dev, void *wnd)
{
if(wnd == NULL)
if(dev == NULL || wnd == NULL)
{
// if we have a single window frame capturer, use that in preference
if(m_WindowFrameCapturers.size() == 1)
{
return m_WindowFrameCapturers.begin()->second.FrameCapturer->EndFrameCapture(wnd);
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 window");
RDCERR("Multiple frame capture methods registered, can't capture by NULL handles");
}
return false;
}
auto it = m_WindowFrameCapturers.find(wnd);
DeviceWnd dw(dev, wnd);
auto it = m_WindowFrameCapturers.find(dw);
if(it == m_WindowFrameCapturers.end())
{
RDCERR("Couldn't find frame capturer for this window %p", wnd);
RDCERR("Couldn't find frame capturer for device %p, window %p", dev, wnd);
return false;
}
return it->second.FrameCapturer->EndFrameCapture(wnd);
return it->second.FrameCapturer->EndFrameCapture(dev, wnd);
}
void RenderDoc::Tick()
@@ -385,9 +406,32 @@ void RenderDoc::Tick()
for(size_t i=0; i < m_CaptureKeys.size(); i++) cur_cap |= Keyboard::GetKeyState(m_CaptureKeys[i]);
if(!prev_focus && cur_focus)
FocusToggle();
{
m_Cap = false;
// can only shift focus if we have multiple windows
if(m_WindowFrameCapturers.size() > 1)
{
for(auto it = m_WindowFrameCapturers.begin(); it != m_WindowFrameCapturers.end(); ++it)
{
if(it->first == m_ActiveWindow)
{
auto nextit = it; ++nextit;
if(nextit != m_WindowFrameCapturers.end())
m_ActiveWindow = nextit->first;
else
m_ActiveWindow = m_WindowFrameCapturers.begin()->first;
break;
}
}
}
}
if(!prev_cap && cur_cap)
TriggerCapture();
{
TriggerCapture();
}
prev_focus = cur_focus;
prev_cap = cur_cap;
@@ -729,15 +773,27 @@ void RenderDoc::SuccessfullyWrittenLog()
}
}
void RenderDoc::AddFrameCapturer(void *wnd, IFrameCapturer *cap)
void RenderDoc::AddDefaultFrameCapturer(IFrameCapturer *cap)
{
if(wnd == NULL || cap == NULL)
m_DefaultFrameCapturers.insert(cap);
}
void RenderDoc::RemoveDefaultFrameCapturer(IFrameCapturer *cap)
{
m_DefaultFrameCapturers.erase(cap);
}
void RenderDoc::AddFrameCapturer(void *dev, void *wnd, IFrameCapturer *cap)
{
if(dev == NULL || wnd == NULL || cap == NULL)
{
RDCERR("Invalid FrameCapturer combination: %#p / %#p", wnd, cap);
return;
}
DeviceWnd dw(dev, wnd);
auto it = m_WindowFrameCapturers.find(wnd);
auto it = m_WindowFrameCapturers.find(dw);
if(it != m_WindowFrameCapturers.end())
{
if(it->second.FrameCapturer != cap)
@@ -747,19 +803,35 @@ void RenderDoc::AddFrameCapturer(void *wnd, IFrameCapturer *cap)
}
else
{
m_WindowFrameCapturers[wnd].FrameCapturer = cap;
m_WindowFrameCapturers[dw].FrameCapturer = cap;
}
// the first one we see becomes the default
if(m_ActiveWindow == DeviceWnd())
m_ActiveWindow = dw;
}
void RenderDoc::RemoveFrameCapturer(void *wnd)
void RenderDoc::RemoveFrameCapturer(void *dev, void *wnd)
{
auto it = m_WindowFrameCapturers.find(wnd);
DeviceWnd dw(dev, wnd);
auto it = m_WindowFrameCapturers.find(dw);
if(it != m_WindowFrameCapturers.end())
{
it->second.RefCount--;
if(it->second.RefCount <= 0)
{
if(m_ActiveWindow == dw)
{
if(m_WindowFrameCapturers.size() == 1)
m_ActiveWindow = DeviceWnd();
else
m_ActiveWindow = m_WindowFrameCapturers.begin()->first;
}
m_WindowFrameCapturers.erase(it);
}
}
else
{
+36 -13
View File
@@ -62,9 +62,8 @@ struct ICrashHandler
struct IFrameCapturer
{
virtual void StartFrameCapture(void *wnd) = 0;
virtual void SetActiveWindow(void *wnd) = 0;
virtual bool EndFrameCapture(void *wnd) = 0;
virtual void StartFrameCapture(void *dev, void *wnd) = 0;
virtual bool EndFrameCapture(void *dev, void *wnd) = 0;
};
enum LogState
@@ -236,14 +235,20 @@ class RenderDoc
void Tick();
void AddFrameCapturer(void *wnd, IFrameCapturer *cap);
void RemoveFrameCapturer(void *wnd);
void StartFrameCapture(void *wnd);
void SetActiveWindow(void *wnd);
bool EndFrameCapture(void *wnd);
void AddFrameCapturer(void *dev, void *wnd, IFrameCapturer *cap);
void RemoveFrameCapturer(void *dev, void *wnd);
// 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 StartFrameCapture(void *dev, void *wnd);
void SetActiveWindow(void *dev, void *wnd);
bool EndFrameCapture(void *dev, void *wnd);
bool IsActiveWindow(void *dev, void *wnd) { return dev == m_ActiveWindow.dev && wnd == m_ActiveWindow.wnd; }
void FocusToggle() { m_Focus = true; m_Cap = false; }
void TriggerCapture() { m_Cap = true; }
uint32_t GetOverlayBits() { return m_Overlay; }
@@ -267,7 +272,6 @@ class RenderDoc
const vector<KeyButton> &GetFocusKeys() { return m_FocusKeys; }
const vector<KeyButton> &GetCaptureKeys() { return m_CaptureKeys; }
bool ShouldFocusToggle() { bool ret = m_Focus; m_Focus = false; return ret; }
bool ShouldTriggerCapture(uint32_t frameNumber);
private:
RenderDoc();
@@ -277,7 +281,6 @@ class RenderDoc
bool m_Replay;
bool m_Focus;
bool m_Cap;
vector<KeyButton> m_FocusKeys;
@@ -319,7 +322,27 @@ class RenderDoc
int RefCount;
};
map<void*, FrameCap> m_WindowFrameCapturers;
struct DeviceWnd
{
DeviceWnd() : dev(NULL), wnd(NULL) {}
DeviceWnd(void *d, void *w) : dev(d), wnd(w) {}
void *dev;
void *wnd;
bool operator ==(const DeviceWnd &o) const
{
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;
}
};
map<DeviceWnd, FrameCap> m_WindowFrameCapturers;
DeviceWnd m_ActiveWindow;
set<IFrameCapturer *> m_DefaultFrameCapturers;
volatile bool m_RemoteServerThreadShutdown;
volatile bool m_RemoteClientThreadShutdown;
+18 -72
View File
@@ -288,8 +288,6 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device* realDevice, D3D11InitPara
m_FailedReason = CaptureSucceeded;
m_Failures = 0;
m_SwapChain = NULL;
m_FrameTimer.Restart();
m_AppControlledCapture = false;
@@ -343,7 +341,7 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device* realDevice, D3D11InitPara
m_DeviceRecord->NumSubResources = 0;
m_DeviceRecord->SubResources = NULL;
RenderDoc::Inst().AddFrameCapturer(this, this);
RenderDoc::Inst().AddDefaultFrameCapturer(this);
}
ID3D11DeviceContext *context = NULL;
@@ -417,6 +415,8 @@ WrappedID3D11Device::~WrappedID3D11Device()
if(m_pCurrentWrappedDevice == this)
m_pCurrentWrappedDevice = NULL;
RenderDoc::Inst().RemoveDefaultFrameCapturer(this);
for(auto it = m_CachedStateObjects.begin(); it != m_CachedStateObjects.end(); ++it)
if(*it)
(*it)->Release();
@@ -456,9 +456,6 @@ WrappedID3D11Device::~WrappedID3D11Device()
SAFE_DELETE(it->second);
m_LayoutDXBC.clear();
m_LayoutDescs.clear();
if(!RenderDoc::Inst().IsReplayApp())
RenderDoc::Inst().RemoveFrameCapturer(this);
if(RenderDoc::Inst().GetCrashHandler())
RenderDoc::Inst().GetCrashHandler()->UnregisterMemoryRegion(this);
@@ -2327,6 +2324,8 @@ void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapChain *swap)
swap->GetDesc(&desc);
Keyboard::RemoveInputWindow(desc.OutputWindow);
RenderDoc::Inst().RemoveFrameCapturer(this, desc.OutputWindow);
}
auto it = m_SwapChains.find(swap);
@@ -2335,14 +2334,6 @@ void WrappedID3D11Device::ReleaseSwapchainResources(IDXGISwapChain *swap)
SAFE_RELEASE(it->second);
m_SwapChains.erase(it);
}
if(swap == m_SwapChain)
{
if(m_SwapChains.empty())
m_SwapChain = NULL;
else
m_SwapChain = m_SwapChains.begin()->first;
}
}
bool WrappedID3D11Device::Serialise_SetSwapChainTexture(IDXGISwapChain *swap, DXGI_SWAP_CHAIN_DESC *swapDesc, UINT buffer, ID3D11Texture2D *pTex)
@@ -2442,11 +2433,8 @@ void WrappedID3D11Device::SetSwapChainTexture(IDXGISwapChain *swap, DXGI_SWAP_CH
swap->GetDesc(&sdesc);
Keyboard::AddInputWindow(sdesc.OutputWindow);
}
if(m_SwapChain == NULL)
{
m_SwapChain = swap;
RenderDoc::Inst().AddFrameCapturer(this, sdesc.OutputWindow, this);
}
}
@@ -2474,7 +2462,7 @@ int WrappedID3D11Device::EndEvent()
return m_pCurrentWrappedDevice->m_pImmediateContext->ThreadSafe_EndEvent();
}
void WrappedID3D11Device::StartFrameCapture(void *wnd)
void WrappedID3D11Device::StartFrameCapture(void *dev, void *wnd)
{
if(m_State != WRITING_IDLE) return;
@@ -2526,24 +2514,7 @@ void WrappedID3D11Device::StartFrameCapture(void *wnd)
RDCLOG("Starting capture, frame %u", m_FrameCounter);
}
void WrappedID3D11Device::SetActiveWindow(void *wnd)
{
for(auto it=m_SwapChains.begin(); it!=m_SwapChains.end(); ++it)
{
DXGI_SWAP_CHAIN_DESC swapDesc;
it->first->GetDesc(&swapDesc);
if(swapDesc.OutputWindow == wnd)
{
m_SwapChain = it->first;
return;
}
}
RDCERR("Output window %p provided for active window corresponds with no known swap chain", wnd);
}
bool WrappedID3D11Device::EndFrameCapture(void *wnd)
bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd)
{
if(m_State != WRITING_CAPFRAME) return true;
@@ -2996,7 +2967,6 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI
if((Flags & DXGI_PRESENT_TEST) != 0)
return S_OK;
RenderDoc::Inst().SetCurrentDriver(RDC_D3D11);
m_pCurrentWrappedDevice = this;
if(m_State == WRITING_IDLE)
@@ -3008,6 +2978,10 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI
m_pImmediateContext->BeginFrame();
DXGI_SWAP_CHAIN_DESC swapdesc;
swap->GetDesc(&swapdesc);
bool activeWindow = RenderDoc::Inst().IsActiveWindow(this, swapdesc.OutputWindow);
if(m_State == WRITING_IDLE)
{
D3D11RenderState old = *m_pImmediateContext->GetCurrentPipelineState();
@@ -3055,7 +3029,7 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI
GetDebugManager()->SetOutputDimensions(swapDesc.BufferDesc.Width, swapDesc.BufferDesc.Height);
GetDebugManager()->SetOutputWindow(swapDesc.OutputWindow);
if(swap == m_SwapChain)
if(activeWindow)
{
vector<KeyButton> keys = RenderDoc::Inst().GetCaptureKeys();
@@ -3131,7 +3105,7 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI
{
vector<KeyButton> keys = RenderDoc::Inst().GetFocusKeys();
string str = "Inactive swapchain.";
string str = "D3D11. Inactive swapchain.";
for(size_t i=0; i < keys.size(); i++)
{
@@ -3151,48 +3125,20 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI
old.ApplyState(m_pImmediateContext);
}
if(RenderDoc::Inst().ShouldFocusToggle())
{
IDXGISwapChain *s = m_SwapChain;
m_SwapChain = NULL;
for(auto it=m_SwapChains.begin(); it!=m_SwapChains.end(); ++it)
{
auto next = it; next++;
if(it->first == s)
{
if(next != m_SwapChains.end())
m_SwapChain = next->first;
else
m_SwapChain = m_SwapChains.begin()->first;
break;
}
}
if(m_SwapChain == NULL)
m_SwapChain = swap;
DXGI_SWAP_CHAIN_DESC swapDesc = {0};
m_SwapChain->GetDesc(&swapDesc);
GetDebugManager()->SetOutputDimensions(swapDesc.BufferDesc.Width, swapDesc.BufferDesc.Height);
GetDebugManager()->SetOutputWindow(swapDesc.OutputWindow);
}
}
if(swap != m_SwapChain || m_SwapChain == NULL)
if(!activeWindow)
return S_OK;
DXGI_SWAP_CHAIN_DESC swapDesc = {0};
m_SwapChain->GetDesc(&swapDesc);
RenderDoc::Inst().SetCurrentDriver(RDC_D3D11);
// kill any current capture that isn't application defined
if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture)
EndFrameCapture(swapDesc.OutputWindow);
EndFrameCapture(this, swapdesc.OutputWindow);
if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE)
{
StartFrameCapture(swapDesc.OutputWindow);
StartFrameCapture(this, swapdesc.OutputWindow);
m_AppControlledCapture = false;
}
+2 -4
View File
@@ -238,7 +238,6 @@ private:
static WrappedID3D11Device *m_pCurrentWrappedDevice;
IDXGISwapChain *m_SwapChain;
map<IDXGISwapChain*, ID3D11RenderTargetView*> m_SwapChains;
uint32_t m_FrameCounter;
@@ -300,9 +299,8 @@ public:
void Serialise_CaptureScope(uint64_t offset);
void StartFrameCapture(void *wnd);
void SetActiveWindow(void *wnd);
bool EndFrameCapture(void *wnd);
void StartFrameCapture(void *dev, void *wnd);
bool EndFrameCapture(void *dev, void *wnd);
////////////////////////////////////////////////////////////////
// log replaying
-4
View File
@@ -271,16 +271,12 @@ WrappedIDXGISwapChain2::WrappedIDXGISwapChain2(IDXGISwapChain* real, HWND wnd, W
}
}
if(m_Wnd) RenderDoc::Inst().AddFrameCapturer(m_Wnd, m_pDevice);
SAFE_ADDREF(m_pDevice);
}
WrappedIDXGISwapChain2::~WrappedIDXGISwapChain2()
{
m_pDevice->ReleaseSwapchainResources(this);
if(m_Wnd) RenderDoc::Inst().RemoveFrameCapturer(m_Wnd);
for(int i=0; i < MAX_NUM_BACKBUFFERS; i++)
{
+319 -190
View File
@@ -673,17 +673,19 @@ WrappedOpenGL::WrappedOpenGL(const char *logfile, const GLHookSet &funcs)
m_FrameTimer.Restart();
m_AppControlledCapture = false;
m_TotalTime = m_AvgFrametime = m_MinFrametime = m_MaxFrametime = 0.0;
m_CurFileSize = 0;
m_RealDebugFunc = NULL;
m_RealDebugFuncParam = NULL;
m_DrawcallStack.push_back(&m_ParentDrawcall);
m_CurEventID = 0;
m_CurDrawcallID = 0;
m_CurEventID = 0;
m_CurDrawcallID = 0;
m_FirstEventID = 0;
m_LastEventID = ~0U;
@@ -752,6 +754,8 @@ 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
{
@@ -899,6 +903,8 @@ 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);
@@ -967,12 +973,32 @@ void WrappedOpenGL::DeleteContext(void *contextHandle)
m_ContextData.erase(contextHandle);
}
void WrappedOpenGL::ContextData::UnassociateWindow(void *wndHandle)
{
auto it = windows.find(wndHandle);
if(it != windows.end())
{
windows.erase(wndHandle);
RenderDoc::Inst().RemoveFrameCapturer(ctx, wndHandle);
}
}
void WrappedOpenGL::ContextData::AssociateWindow(WrappedOpenGL *gl, void *wndHandle)
{
auto it = windows.find(wndHandle);
if(it == windows.end())
RenderDoc::Inst().AddFrameCapturer(ctx, wndHandle, gl);
windows[wndHandle] = Timing::GetUnixTimestamp();
}
void WrappedOpenGL::CreateContext(GLWindowingData winData, void *shareContext, GLInitParams initParams, bool core, bool attribsCreate)
{
// TODO: support multiple GL contexts more explicitly
m_InitParams = initParams;
ContextData &ctxdata = m_ContextData[winData.ctx];
ctxdata.ctx = winData.ctx;
ctxdata.isCore = core;
ctxdata.attribsCreate = attribsCreate;
}
@@ -1920,15 +1946,54 @@ void WrappedOpenGL::FreeTargetResource(ResourceId id)
}
}
void WrappedOpenGL::Present(void *windowHandle)
void WrappedOpenGL::SwapBuffers(void *windowHandle)
{
RenderDoc::Inst().SetCurrentDriver(RDC_OpenGL);
if(m_State == WRITING_IDLE)
RenderDoc::Inst().Tick();
m_FrameCounter++; // first present becomes frame #1, this function is at the end of the frame
ContextData &ctxdata = GetCtxData();
// we only handle context-window associations here as it's too common to
// create invisible helper windows while creating contexts, that then
// become the default window.
// Since we only capture windows that do SwapBuffers (i.e. if you're doing
// headless rendering then you must capture via the API anyway), this
// isn't a big problem.
//
// Also we only set up associations for capturable windows.
if(ctxdata.Modern())
{
for(auto it=m_ContextData.begin(); it != m_ContextData.end(); ++it)
if(it->first != ctxdata.ctx)
it->second.UnassociateWindow(windowHandle);
ctxdata.AssociateWindow(this, windowHandle);
}
bool activeWindow = RenderDoc::Inst().IsActiveWindow(ctxdata.ctx, windowHandle);
// look at previous associations and decay any that are too old
uint64_t ref = Timing::GetUnixTimestamp() - 5; // 5 seconds
for(auto cit=m_ContextData.begin(); cit != m_ContextData.end(); ++cit)
{
for(auto wit=cit->second.windows.begin(); wit != cit->second.windows.end(); )
{
if(wit->second < ref)
{
auto remove = wit;
++wit;
cit->second.windows.erase(remove);
}
else
{
++wit;
}
}
}
if(m_State == WRITING_IDLE)
{
m_FrameTimes.push_back(m_FrameTimer.GetMilliseconds());
@@ -1960,15 +2025,13 @@ void WrappedOpenGL::Present(void *windowHandle)
uint32_t overlay = RenderDoc::Inst().GetOverlayBits();
if((overlay & eOverlay_Enabled) && m_Real.glGetIntegerv && m_Real.glReadBuffer && m_Real.glBindFramebuffer && m_Real.glBindBuffer && m_Real.glReadPixels)
if(overlay & eOverlay_Enabled)
{
RenderTextState textState;
ContextData &ctxdata = GetCtxData();
textState.Push(m_Real, ctxdata.Modern());
// TODO: handle selecting active window amongst many
if(activeWindow)
{
vector<KeyButton> keys = RenderDoc::Inst().GetCaptureKeys();
@@ -2046,6 +2109,38 @@ void WrappedOpenGL::Present(void *windowHandle)
y += 1.0f;
#endif
}
else
{
vector<KeyButton> keys = RenderDoc::Inst().GetFocusKeys();
string str = "OpenGL. Inactive window.";
if(ctxdata.Modern())
{
for(size_t i=0; i < keys.size(); i++)
{
if(i == 0)
str += " ";
else
str += ", ";
str += ToStr::Get(keys[i]);
}
if(!keys.empty())
str += " to cycle between windows.";
}
else
{
if(!ctxdata.attribsCreate)
{
str += "\nContext not created via CreateContextAttribs. Capturing disabled.\n";
}
str += "Only OpenGL 3.2+ contexts are supported.";
}
RenderOverlayText(0.0f, 0.0f, str.c_str());
}
textState.Pop(m_Real, ctxdata.Modern());
@@ -2060,195 +2155,229 @@ void WrappedOpenGL::Present(void *windowHandle)
}
}
}
// kill any current capture
if(m_State == WRITING_CAPFRAME)
{
//if(HasSuccessfulCapture())
{
RDCLOG("Finished capture, Frame %u", m_FrameCounter);
EndCaptureFrame();
FinishCapture();
const uint32_t maxSize = 1024;
byte *thpixels = NULL;
uint32_t thwidth = 0;
uint32_t thheight = 0;
if(m_Real.glGetIntegerv && m_Real.glReadBuffer && m_Real.glBindFramebuffer && m_Real.glBindBuffer && m_Real.glReadPixels)
{
RDCGLenum prevReadBuf = eGL_BACK;
GLint prevBuf = 0;
GLint packBufBind = 0;
GLint prevPackRowLen = 0;
GLint prevPackSkipRows = 0;
GLint prevPackSkipPixels = 0;
GLint prevPackAlignment = 0;
m_Real.glGetIntegerv(eGL_READ_BUFFER, (GLint *)&prevReadBuf);
m_Real.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &prevBuf);
m_Real.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, &packBufBind);
m_Real.glGetIntegerv(eGL_PACK_ROW_LENGTH, &prevPackRowLen);
m_Real.glGetIntegerv(eGL_PACK_SKIP_ROWS, &prevPackSkipRows);
m_Real.glGetIntegerv(eGL_PACK_SKIP_PIXELS, &prevPackSkipPixels);
m_Real.glGetIntegerv(eGL_PACK_ALIGNMENT, &prevPackAlignment);
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, 0);
m_Real.glReadBuffer(eGL_BACK);
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0);
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, 0);
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, 0);
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, 0);
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, 1);
thwidth = m_InitParams.width;
thheight = m_InitParams.height;
thpixels = new byte[thwidth*thheight*3];
m_Real.glReadPixels(0, 0, thwidth, thheight, eGL_RGB, eGL_UNSIGNED_BYTE, thpixels);
for(uint32_t y=0; y <= thheight/2; y++)
{
for(uint32_t x=0; x < thwidth; x++)
{
byte save[3];
save[0] = thpixels[y*(thwidth*3) + x*3 + 0];
save[1] = thpixels[y*(thwidth*3) + x*3 + 1];
save[2] = thpixels[y*(thwidth*3) + x*3 + 2];
thpixels[y*(thwidth*3) + x*3 + 0] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0];
thpixels[y*(thwidth*3) + x*3 + 1] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1];
thpixels[y*(thwidth*3) + x*3 + 2] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0] = save[0];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1] = save[1];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2] = save[2];
}
}
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, packBufBind);
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, prevBuf);
m_Real.glReadBuffer(prevReadBuf);
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, prevPackRowLen);
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, prevPackSkipRows);
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, prevPackSkipPixels);
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, prevPackAlignment);
}
byte *jpgbuf = NULL;
int len = thwidth*thheight;
if(len > 0)
{
jpgbuf = new byte[len];
jpge::params p;
p.m_quality = 40;
bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, thpixels, p);
if(!success)
{
RDCERR("Failed to compress to jpg");
SAFE_DELETE_ARRAY(jpgbuf);
thwidth = 0;
thheight = 0;
}
}
Serialiser *m_pFileSerialiser = RenderDoc::Inst().OpenWriteSerialiser(m_FrameCounter, &m_InitParams, jpgbuf, len, thwidth, thheight);
{
SCOPED_SERIALISE_CONTEXT(DEVICE_INIT);
SERIALISE_ELEMENT(ResourceId, immContextId, m_ContextResourceID);
SERIALISE_ELEMENT(ResourceId, vaoId, m_FakeVAOID);
m_pFileSerialiser->Insert(scope.Get(true));
}
RDCDEBUG("Inserting Resource Serialisers");
GetResourceManager()->InsertReferencedChunks(m_pFileSerialiser);
GetResourceManager()->InsertInitialContentsChunks(m_pFileSerialiser);
RDCDEBUG("Creating Capture Scope");
{
SCOPED_SERIALISE_CONTEXT(CAPTURE_SCOPE);
Serialise_CaptureScope(0);
m_pFileSerialiser->Insert(scope.Get(true));
}
{
RDCDEBUG("Getting Resource Record");
GLResourceRecord *record = m_ResourceManager->GetResourceRecord(m_ContextResourceID);
RDCDEBUG("Accumulating context resource list");
map<int32_t, Chunk *> recordlist;
record->Insert(recordlist);
RDCDEBUG("Flushing %u records to file serialiser", (uint32_t)recordlist.size());
for(auto it = recordlist.begin(); it != recordlist.end(); ++it)
m_pFileSerialiser->Insert(it->second);
RDCDEBUG("Done");
}
m_CurFileSize += m_pFileSerialiser->FlushToDisk();
RenderDoc::Inst().SuccessfullyWrittenLog();
SAFE_DELETE(m_pFileSerialiser);
m_State = WRITING_IDLE;
GetResourceManager()->MarkUnwrittenResources();
GetResourceManager()->ClearReferencedResources();
}
}
if(!activeWindow)
return;
RenderDoc::Inst().SetCurrentDriver(RDC_OpenGL);
// only allow capturing on 'modern' created contexts
if(GetCtxData().Modern() && RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE && m_FrameRecord.empty())
if(ctxdata.Legacy())
return;
// kill any current capture that isn't application defined
if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture)
EndFrameCapture(this, windowHandle);
// for now, only allow one captured frame at all
if(!m_FrameRecord.empty())
return;
if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE)
{
m_State = WRITING_CAPFRAME;
StartFrameCapture(this, windowHandle);
GLWindowingData &prevctx = m_ActiveContexts[Threading::GetCurrentID()];
m_AppControlledCapture = false;
}
}
bool switchedContext = false;
if(prevctx.ctx == NULL)
void WrappedOpenGL::StartFrameCapture(void *dev, void *wnd)
{
m_State = WRITING_CAPFRAME;
m_AppControlledCapture = true;
GLWindowingData &prevctx = m_ActiveContexts[Threading::GetCurrentID()];
bool switchedContext = false;
if(prevctx.ctx == NULL)
{
MakeContextCurrent(m_DefaultContexts[Threading::GetCurrentID()]);
switchedContext = true;
}
FetchFrameRecord record;
record.frameInfo.frameNumber = m_FrameCounter+1;
record.frameInfo.captureTime = Timing::GetUnixTimestamp();
m_FrameRecord.push_back(record);
GetResourceManager()->ClearReferencedResources();
GetResourceManager()->MarkResourceFrameReferenced(m_DeviceResourceID, eFrameRef_Write);
GetResourceManager()->PrepareInitialContents();
AttemptCapture();
BeginCaptureFrame();
if(switchedContext)
MakeContextCurrent(prevctx);
RDCLOG("Starting capture, frame %u", m_FrameCounter);
}
bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd)
{
if(m_State != WRITING_CAPFRAME) return true;
CaptureFailReason reason = CaptureSucceeded;
//if(HasSuccessfulCapture(reason))
{
RDCLOG("Finished capture, Frame %u", m_FrameCounter);
ContextEndFrame();
FinishCapture();
const uint32_t maxSize = 1024;
byte *thpixels = NULL;
uint32_t thwidth = 0;
uint32_t thheight = 0;
if(m_Real.glGetIntegerv && m_Real.glReadBuffer && m_Real.glBindFramebuffer && m_Real.glBindBuffer && m_Real.glReadPixels)
{
MakeContextCurrent(m_DefaultContexts[Threading::GetCurrentID()]);
switchedContext = true;
RDCGLenum prevReadBuf = eGL_BACK;
GLint prevBuf = 0;
GLint packBufBind = 0;
GLint prevPackRowLen = 0;
GLint prevPackSkipRows = 0;
GLint prevPackSkipPixels = 0;
GLint prevPackAlignment = 0;
m_Real.glGetIntegerv(eGL_READ_BUFFER, (GLint *)&prevReadBuf);
m_Real.glGetIntegerv(eGL_READ_FRAMEBUFFER_BINDING, &prevBuf);
m_Real.glGetIntegerv(eGL_PIXEL_PACK_BUFFER_BINDING, &packBufBind);
m_Real.glGetIntegerv(eGL_PACK_ROW_LENGTH, &prevPackRowLen);
m_Real.glGetIntegerv(eGL_PACK_SKIP_ROWS, &prevPackSkipRows);
m_Real.glGetIntegerv(eGL_PACK_SKIP_PIXELS, &prevPackSkipPixels);
m_Real.glGetIntegerv(eGL_PACK_ALIGNMENT, &prevPackAlignment);
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, 0);
m_Real.glReadBuffer(eGL_BACK);
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, 0);
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, 0);
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, 0);
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, 0);
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, 1);
thwidth = m_InitParams.width;
thheight = m_InitParams.height;
thpixels = new byte[thwidth*thheight*3];
m_Real.glReadPixels(0, 0, thwidth, thheight, eGL_RGB, eGL_UNSIGNED_BYTE, thpixels);
for(uint32_t y=0; y <= thheight/2; y++)
{
for(uint32_t x=0; x < thwidth; x++)
{
byte save[3];
save[0] = thpixels[y*(thwidth*3) + x*3 + 0];
save[1] = thpixels[y*(thwidth*3) + x*3 + 1];
save[2] = thpixels[y*(thwidth*3) + x*3 + 2];
thpixels[y*(thwidth*3) + x*3 + 0] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0];
thpixels[y*(thwidth*3) + x*3 + 1] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1];
thpixels[y*(thwidth*3) + x*3 + 2] = thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 0] = save[0];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 1] = save[1];
thpixels[(thheight-1-y)*(thwidth*3) + x*3 + 2] = save[2];
}
}
m_Real.glBindBuffer(eGL_PIXEL_PACK_BUFFER, packBufBind);
m_Real.glBindFramebuffer(eGL_READ_FRAMEBUFFER, prevBuf);
m_Real.glReadBuffer(prevReadBuf);
m_Real.glPixelStorei(eGL_PACK_ROW_LENGTH, prevPackRowLen);
m_Real.glPixelStorei(eGL_PACK_SKIP_ROWS, prevPackSkipRows);
m_Real.glPixelStorei(eGL_PACK_SKIP_PIXELS, prevPackSkipPixels);
m_Real.glPixelStorei(eGL_PACK_ALIGNMENT, prevPackAlignment);
}
FetchFrameRecord record;
record.frameInfo.frameNumber = m_FrameCounter+1;
record.frameInfo.captureTime = Timing::GetUnixTimestamp();
m_FrameRecord.push_back(record);
byte *jpgbuf = NULL;
int len = thwidth*thheight;
if(len > 0)
{
jpgbuf = new byte[len];
jpge::params p;
p.m_quality = 40;
bool success = jpge::compress_image_to_jpeg_file_in_memory(jpgbuf, len, thwidth, thheight, 3, thpixels, p);
if(!success)
{
RDCERR("Failed to compress to jpg");
SAFE_DELETE_ARRAY(jpgbuf);
thwidth = 0;
thheight = 0;
}
}
Serialiser *m_pFileSerialiser = RenderDoc::Inst().OpenWriteSerialiser(m_FrameCounter, &m_InitParams, jpgbuf, len, thwidth, thheight);
{
SCOPED_SERIALISE_CONTEXT(DEVICE_INIT);
SERIALISE_ELEMENT(ResourceId, immContextId, m_ContextResourceID);
SERIALISE_ELEMENT(ResourceId, vaoId, m_FakeVAOID);
m_pFileSerialiser->Insert(scope.Get(true));
}
RDCDEBUG("Inserting Resource Serialisers");
GetResourceManager()->InsertReferencedChunks(m_pFileSerialiser);
GetResourceManager()->InsertInitialContentsChunks(m_pFileSerialiser);
RDCDEBUG("Creating Capture Scope");
{
SCOPED_SERIALISE_CONTEXT(CAPTURE_SCOPE);
Serialise_CaptureScope(0);
m_pFileSerialiser->Insert(scope.Get(true));
}
{
RDCDEBUG("Getting Resource Record");
GLResourceRecord *record = m_ResourceManager->GetResourceRecord(m_ContextResourceID);
RDCDEBUG("Accumulating context resource list");
map<int32_t, Chunk *> recordlist;
record->Insert(recordlist);
RDCDEBUG("Flushing %u records to file serialiser", (uint32_t)recordlist.size());
for(auto it = recordlist.begin(); it != recordlist.end(); ++it)
m_pFileSerialiser->Insert(it->second);
RDCDEBUG("Done");
}
m_CurFileSize += m_pFileSerialiser->FlushToDisk();
RenderDoc::Inst().SuccessfullyWrittenLog();
SAFE_DELETE(m_pFileSerialiser);
m_State = WRITING_IDLE;
GetResourceManager()->MarkUnwrittenResources();
GetResourceManager()->ClearReferencedResources();
GetResourceManager()->MarkResourceFrameReferenced(m_DeviceResourceID, eFrameRef_Write);
GetResourceManager()->PrepareInitialContents();
AttemptCapture();
BeginCaptureFrame();
if(switchedContext)
MakeContextCurrent(prevctx);
RDCLOG("Starting capture, frame %u", m_FrameCounter);
return true;
}
//else
{
// failed to capture
}
}
@@ -2273,7 +2402,7 @@ void WrappedOpenGL::Serialise_CaptureScope(uint64_t offset)
}
}
void WrappedOpenGL::EndCaptureFrame()
void WrappedOpenGL::ContextEndFrame()
{
SCOPED_SERIALISE_CONTEXT(CONTEXT_CAPTURE_FOOTER);
@@ -2314,7 +2443,7 @@ void WrappedOpenGL::AttemptCapture()
m_DebugMessages.clear();
{
RDCDEBUG("Immediate Context %llu Attempting capture", GetContextResourceID());
RDCDEBUG("GL Context %llu Attempting capture", GetContextResourceID());
//m_SuccessfulCapture = true;
+33 -4
View File
@@ -61,6 +61,12 @@ struct GLInitParams : public RDCInitParams
uint32_t SerialiseVersion;
};
enum CaptureFailReason
{
CaptureSucceeded = 0,
CaptureFailed_UncappedUnmap,
};
struct DrawcallTreeNode
{
DrawcallTreeNode() {}
@@ -93,7 +99,7 @@ struct Replacement
GLResource res;
};
class WrappedOpenGL
class WrappedOpenGL : public IFrameCapturer
{
private:
const GLHookSet &m_Real;
@@ -124,6 +130,7 @@ class WrappedOpenGL
// internals
Serialiser *m_pSerialiser;
LogState m_State;
bool m_AppControlledCapture;
GLReplay m_Replay;
@@ -311,8 +318,8 @@ class WrappedOpenGL
bool Serialise_BeginCaptureFrame(bool applyInitialState);
void BeginCaptureFrame();
void FinishCapture();
void EndCaptureFrame();
void ContextEndFrame();
struct ContextData
{
ContextData()
@@ -333,6 +340,8 @@ class WrappedOpenGL
m_ProgramPipeline = m_Program = 0;
}
void *ctx;
bool built;
bool ready;
@@ -340,6 +349,18 @@ class WrappedOpenGL
bool attribsCreate;
bool isCore;
// map from window handle void* to uint64_t unix timestamp with
// the last time a window was seen/associated with this context.
// Decays after a few seconds since there's no good explicit
// 'remove' type call for GL, only wglCreateContext/wglMakeCurrent
map<void *, uint64_t> windows;
// a window is only associated with one context at once, so any
// time we associate a window, it broadcasts to all other
// contexts to let them know to remove it
void UnassociateWindow(void *wndHandle);
void AssociateWindow(WrappedOpenGL *gl, void *wndHandle);
bool Legacy() { return !attribsCreate || version < 32; }
bool Modern() { return !Legacy(); }
@@ -445,7 +466,15 @@ class WrappedOpenGL
void DeleteContext(void *contextHandle);
void ActivateContext(GLWindowingData winData);
void WindowSize(void *windowHandle, uint32_t w, uint32_t h);
void Present(void *windowHandle);
void SwapBuffers(void *windowHandle);
void StartFrameCapture();
void EndFrameCapture();
void StartFrameCapture(void *dev, void *wnd);
bool EndFrameCapture(void *dev, void *wnd);
IMPLEMENT_FUNCTION_SERIALISED(void, glBindTexture(GLenum target, GLuint texture));
IMPLEMENT_FUNCTION_SERIALISED(void, glBindTextures(GLuint first, GLsizei count, const GLuint *textures));
@@ -202,7 +202,7 @@ void WrappedOpenGL::glInsertEventMarkerEXT(GLsizei length, const GLchar *marker)
void WrappedOpenGL::glFrameTerminatorGREMEDY()
{
Present(NULL);
SwapBuffers(NULL);
}
void WrappedOpenGL::glStringMarkerGREMEDY(GLsizei len, const void *string)
+1 -1
View File
@@ -518,7 +518,7 @@ class OpenGLHook : LibraryHook
glhooks.GetDriver()->WindowSize(w, r.right-r.left, r.bottom-r.top);
glhooks.GetDriver()->Present(w);
glhooks.GetDriver()->SwapBuffers(w);
return glhooks.SwapBuffers_hook()(dc);
}
+6 -6
View File
@@ -246,9 +246,9 @@ uint32_t RENDERDOC_CC RENDERDOC_InjectIntoProcess(uint32_t pid, const char *logf
}
extern "C" RENDERDOC_API
void RENDERDOC_CC RENDERDOC_SetActiveWindow(void *wndHandle)
void RENDERDOC_CC RENDERDOC_SetActiveWindow(void *device, void *wndHandle)
{
RenderDoc::Inst().SetActiveWindow(wndHandle);
RenderDoc::Inst().SetActiveWindow(device, wndHandle);
}
extern "C" RENDERDOC_API
@@ -258,15 +258,15 @@ void RENDERDOC_CC RENDERDOC_TriggerCapture()
}
extern "C" RENDERDOC_API
void RENDERDOC_CC RENDERDOC_StartFrameCapture(void *wndHandle)
void RENDERDOC_CC RENDERDOC_StartFrameCapture(void *device, void *wndHandle)
{
RenderDoc::Inst().StartFrameCapture(wndHandle);
RenderDoc::Inst().StartFrameCapture(device, wndHandle);
}
extern "C" RENDERDOC_API
bool32 RENDERDOC_CC RENDERDOC_EndFrameCapture(void *wndHandle)
bool32 RENDERDOC_CC RENDERDOC_EndFrameCapture(void *device, void *wndHandle)
{
return RenderDoc::Inst().EndFrameCapture(wndHandle);
return RenderDoc::Inst().EndFrameCapture(device, wndHandle);
}
extern "C" RENDERDOC_API