From 546a1fbb31d5dd355ea139833dd2f45ebe45061f Mon Sep 17 00:00:00 2001 From: baldurk Date: Tue, 2 Sep 2014 00:21:12 +0100 Subject: [PATCH] Add functionality for overlay tweaking & manual capturing by apps * The functionality is exposed via dllexported functions so that apps can call it to tweak renderdoc's behaviour when injects. 1) Can now turn bits of the overlay off, or hide it entirely. 2) Can also manually start and stop a capture on a given window. This skips the need for swapping between active swapchains, and allows the app to define the exact region of D3D11 calls to be captured (could in fact be many frames). The window provided determines the swapchain used for thumbnail, and also implicitly specifies the device to be used for capture. --- renderdoc/core/core.cpp | 38 + renderdoc/core/core.h | 32 + renderdoc/driver/d3d11/d3d11_device.cpp | 1020 ++++++++++++----------- renderdoc/driver/d3d11/d3d11_device.h | 6 + renderdoc/driver/dxgi/dxgi_wrapped.cpp | 6 + renderdoc/replay/entry_points.cpp | 30 + renderdoc/replay/renderdoc.h | 15 + 7 files changed, 676 insertions(+), 471 deletions(-) diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index b59ebc129..01fa3a57b 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -88,6 +88,8 @@ RenderDoc::RenderDoc() m_ProgressPtr = NULL; m_ExHandler = NULL; + + m_Overlay = eOverlay_Default; m_RemoteServerThreadShutdown = false; m_RemoteClientThreadShutdown = false; @@ -203,6 +205,42 @@ RenderDoc::~RenderDoc() RDCLOGDELETE(); } +void RenderDoc::StartFrameCapture(void *wnd) +{ + auto it = m_WindowFrameCapturers.find(wnd); + if(it == m_WindowFrameCapturers.end()) + { + RDCERR("Couldn't find frame capturer for this window %p", wnd); + return; + } + + it->second->StartFrameCapture(wnd); +} + +void RenderDoc::SetActiveWindow(void *wnd) +{ + auto it = m_WindowFrameCapturers.find(wnd); + if(it == m_WindowFrameCapturers.end()) + { + RDCERR("Couldn't find frame capturer for this window %p", wnd); + return; + } + + it->second->SetActiveWindow(wnd); +} + +bool RenderDoc::EndFrameCapture(void *wnd) +{ + auto it = m_WindowFrameCapturers.find(wnd); + if(it == m_WindowFrameCapturers.end()) + { + RDCERR("Couldn't find frame capturer for this window %p", wnd); + return false; + } + + return it->second->EndFrameCapture(wnd); +} + void RenderDoc::Tick() { static bool prev_f11 = false; diff --git a/renderdoc/core/core.h b/renderdoc/core/core.h index 5074b8bfc..172cb90a0 100644 --- a/renderdoc/core/core.h +++ b/renderdoc/core/core.h @@ -55,6 +55,13 @@ struct ICrashHandler virtual void UnregisterMemoryRegion(void *mem) = 0; }; +struct IFrameCapturer +{ + virtual void StartFrameCapture(void *wnd) = 0; + virtual void SetActiveWindow(void *wnd) = 0; + virtual bool EndFrameCapture(void *wnd) = 0; +}; + enum LogState { READING = 0, @@ -97,6 +104,18 @@ enum RDCDriver RDC_Custom9, }; +enum InAppOverlay +{ + eOverlay_Enabled = 0x1, + eOverlay_FrameRate = 0x2, + eOverlay_FrameNumber = 0x4, + eOverlay_CaptureList = 0x8, + + eOverlay_Default = (eOverlay_Enabled|eOverlay_FrameRate|eOverlay_FrameNumber|eOverlay_CaptureList), + eOverlay_All = ~0U, + eOverlay_None = 0, +}; + namespace DXBC { class DXBCFile; } namespace Callstack { class StackResolver; } @@ -201,9 +220,19 @@ class RenderDoc void Tick(); + void AddFrameCapturer(void *wnd, IFrameCapturer *cap) { if(wnd != NULL && cap != NULL) m_WindowFrameCapturers[wnd] = cap; } + void RemoveFrameCapturer(void *wnd) { m_WindowFrameCapturers.erase(wnd); } + + void StartFrameCapture(void *wnd); + void SetActiveWindow(void *wnd); + bool EndFrameCapture(void *wnd); + void FocusToggle() { m_Focus = true; m_Cap = false; } void TriggerCapture() { m_Cap = true; } + uint32_t GetOverlayBits() { return m_Overlay; } + void MaskOverlayBits(uint32_t and, uint32_t or) { m_Overlay = (m_Overlay & and) | or; } + void QueueCapture(uint32_t frameNumber) { m_QueuedFrameCaptures.insert(frameNumber); } bool ShouldFocusToggle() { bool ret = m_Focus; m_Focus = false; return ret; } @@ -223,6 +252,7 @@ class RenderDoc wstring m_LogFile; wstring m_CurrentLogFile; CaptureOptions m_Options; + uint32_t m_Overlay; set m_QueuedFrameCaptures; @@ -243,6 +273,8 @@ class RenderDoc map m_ReplayDriverProviders; map m_RemoteDriverProviders; + map m_WindowFrameCapturers; + volatile bool m_RemoteServerThreadShutdown; volatile bool m_RemoteClientThreadShutdown; Threading::CriticalSection m_SingleClientLock; diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index 361ff2c05..7ca753d44 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -265,6 +265,8 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device* realDevice, D3D11InitPara m_FrameTimer.Restart(); + m_AppControlledCapture = false; + m_TotalTime = m_AvgFrametime = m_MinFrametime = m_MaxFrametime = 0.0; m_CurFileSize = 0; @@ -2336,6 +2338,495 @@ int WrappedID3D11Device::EndEvent() return m_pCurrentWrappedDevice->m_pImmediateContext->ThreadSafe_EndEvent(); } +void WrappedID3D11Device::StartFrameCapture(void *wnd) +{ + if(m_State != WRITING_IDLE) return; + + SCOPED_LOCK(m_D3DLock); + + m_State = WRITING_CAPFRAME; + + m_AppControlledCapture = true; + + m_Failures = 0; + m_FailedFrame = 0; + m_FailedReason = CaptureSucceeded; + + FetchFrameRecord record; + record.frameInfo.frameNumber = m_FrameCounter+1; + m_FrameRecord.push_back(record); + + GetResourceManager()->ClearReferencedResources(); + + GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Write); + + m_pImmediateContext->AttemptCapture(); + m_pImmediateContext->BeginCaptureFrame(); + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + { + context->AttemptCapture(); + } + else + { + RDCERR("NULL deferred context in resource record!"); + } + } + + GetResourceManager()->PrepareInitialContents(); + + if(m_pInfoQueue) + m_pInfoQueue->ClearStoredMessages(); + + 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) +{ + if(m_State != WRITING_CAPFRAME) return true; + + CaptureFailReason reason; + + IDXGISwapChain *swap = NULL; + + for(auto it=m_SwapChains.begin(); it!=m_SwapChains.end(); ++it) + { + DXGI_SWAP_CHAIN_DESC swapDesc; + it->first->GetDesc(&swapDesc); + + if(swapDesc.OutputWindow == wnd) + { + swap = it->first; + break; + } + } + + if(swap == NULL) + { + RDCERR("Output window %p provided for frame capture corresponds with no known swap chain", wnd); + return false; + } + + if(m_pImmediateContext->HasSuccessfulCapture(reason)) + { + SCOPED_LOCK(m_D3DLock); + + RDCLOG("Finished capture, Frame %u", m_FrameCounter); + + m_Failures = 0; + m_FailedFrame = 0; + m_FailedReason = CaptureSucceeded; + + m_pImmediateContext->EndCaptureFrame(); + m_pImmediateContext->FinishCapture(); + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + { + context->FinishCapture(); + } + else + { + RDCERR("NULL deferred context in resource record!"); + } + } + + const uint32_t maxSize = 1024; + + byte *thpixels = NULL; + uint32_t thwidth = 0; + uint32_t thheight = 0; + + { + ID3D11RenderTargetView *rtv = m_SwapChains[swap]; + + ID3D11Resource *res = NULL; + + rtv->GetResource(&res); res->Release(); + + ID3D11Texture2D *tex = (ID3D11Texture2D *)res; + + D3D11_TEXTURE2D_DESC desc; + tex->GetDesc(&desc); + + desc.BindFlags = 0; + desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; + desc.MiscFlags = 0; + desc.Usage = D3D11_USAGE_STAGING; + + bool msaa = (desc.SampleDesc.Count > 1) || (desc.SampleDesc.Quality > 0); + + desc.SampleDesc.Count = 1; + desc.SampleDesc.Quality = 0; + + ID3D11Texture2D *stagingTex = NULL; + + HRESULT hr = m_pDevice->CreateTexture2D(&desc, NULL, &stagingTex); + + if(FAILED(hr)) + { + RDCERR("Couldn't create staging texture to create thumbnail. %08x", hr); + } + else + { + if(msaa) + { + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.CPUAccessFlags = 0; + desc.Usage = D3D11_USAGE_DEFAULT; + + ID3D11Texture2D *resolveTex = NULL; + + HRESULT hr = m_pDevice->CreateTexture2D(&desc, NULL, &resolveTex); + + if(FAILED(hr)) + { + RDCERR("Couldn't create resolve texture to create thumbnail. %08x", hr); + tex = NULL; + } + else + { + m_pImmediateContext->GetReal()->ResolveSubresource(resolveTex, 0, tex, 0, desc.Format); + m_pImmediateContext->GetReal()->CopyResource(stagingTex, resolveTex); + resolveTex->Release(); + } + } + else + { + m_pImmediateContext->GetReal()->CopyResource(stagingTex, tex); + } + + if(tex) + { + ResourceFormat fmt = MakeResourceFormat(desc.Format); + + D3D11_MAPPED_SUBRESOURCE mapped; + hr = m_pImmediateContext->GetReal()->Map(stagingTex, 0, D3D11_MAP_READ, 0, &mapped); + + if(FAILED(hr)) + { + RDCERR("Couldn't map staging texture to create thumbnail. %08x", hr); + } + else + { + byte *data = (byte *)mapped.pData; + + float aspect = float(desc.Width)/float(desc.Height); + + thwidth = RDCMIN(maxSize, desc.Width); + thwidth &= ~0x7; // align down to multiple of 8 + thheight = uint32_t(float(thwidth)/aspect); + + thpixels = new byte[3*thwidth*thheight]; + + float widthf = float(desc.Width); + float heightf = float(desc.Height); + + uint32_t stride = fmt.compByteWidth*fmt.compCount; + + bool buf1010102 = false; + bool bufBGRA = false; + + if(fmt.special && fmt.specialFormat == eSpecial_R10G10B10A2) + { + stride = 4; + buf1010102 = true; + } + if(fmt.special && fmt.specialFormat == eSpecial_B8G8R8A8) + { + stride = 4; + bufBGRA = true; + } + + byte *dst = thpixels; + + for(uint32_t y=0; y < thheight; y++) + { + for(uint32_t x=0; x < thwidth; x++) + { + float xf = float(x)/float(thwidth); + float yf = float(y)/float(thheight); + + byte *src = &data[ stride*uint32_t(xf*widthf) + mapped.RowPitch*uint32_t(yf*heightf) ]; + + if(buf1010102) + { + uint32_t *src1010102 = (uint32_t *)src; + Vec4f unorm = ConvertFromR10G10B10A2(*src1010102); + dst[0] = (byte)(unorm.x*255.0f); + dst[1] = (byte)(unorm.y*255.0f); + dst[2] = (byte)(unorm.z*255.0f); + } + else if(bufBGRA) + { + dst[0] = src[2]; + dst[1] = src[1]; + dst[2] = src[0]; + } + else + { + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[2]; + } + + dst += 3; + } + } + + m_pImmediateContext->GetReal()->Unmap(stagingTex, 0); + } + } + + stagingTex->Release(); + } + + } + + byte *jpgbuf = NULL; + int len = thwidth*thheight; + + { + 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); + + SAFE_DELETE_ARRAY(jpgbuf); + SAFE_DELETE(thpixels); + + { + SCOPED_SERIALISE_CONTEXT(DEVICE_INIT); + + SERIALISE_ELEMENT(ResourceId, immContextId, m_pImmediateContext->GetResourceID()); + + m_pFileSerialiser->Insert(scope.Get(true)); + } + + RDCDEBUG("Inserting Resource Serialisers"); + + GetResourceManager()->InsertReferencedChunks(m_pFileSerialiser); + + GetResourceManager()->InsertInitialContentsChunks(m_pSerialiser, m_pFileSerialiser); + + RDCDEBUG("Creating Capture Scope"); + + { + SCOPED_SERIALISE_CONTEXT(CAPTURE_SCOPE); + + Serialise_CaptureScope(0); + + m_pFileSerialiser->Insert(scope.Get(true)); + } + + { + RDCDEBUG("Getting Resource Record"); + + D3D11ResourceRecord *record = m_ResourceManager->GetResourceRecord(m_pImmediateContext->GetResourceID()); + + RDCDEBUG("Accumulating context resource list"); + + map 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(); + + SAFE_DELETE(m_pFileSerialiser); + + RenderDoc::Inst().SuccessfullyWrittenLog(); + + m_State = WRITING_IDLE; + + m_pImmediateContext->CleanupCapture(); + + m_pImmediateContext->FreeCaptureData(); + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + context->CleanupCapture(); + else + RDCERR("NULL deferred context in resource record!"); + } + + GetResourceManager()->MarkUnwrittenResources(); + + GetResourceManager()->ClearReferencedResources(); + + return true; + } + else + { + RDCLOG("Failed to capture, frame %u", m_FrameCounter); + + m_Failures++; + + if(RenderDoc::Inst().GetOverlayBits() & eOverlay_Enabled) + { + D3D11RenderState old = *m_pImmediateContext->GetCurrentPipelineState(); + + ID3D11RenderTargetView *rtv = m_SwapChains[swap]; + + if(rtv) + { + m_pImmediateContext->GetReal()->OMSetRenderTargets(1, &rtv, NULL); + + int w = GetDebugManager()->GetWidth(); + int h = GetDebugManager()->GetHeight(); + + DXGI_SWAP_CHAIN_DESC swapDesc = {0}; + swap->GetDesc(&swapDesc); + GetDebugManager()->SetOutputDimensions(swapDesc.BufferDesc.Width, swapDesc.BufferDesc.Height); + + const char *reasonString = "Unknown reason"; + switch(reason) + { + case CaptureFailed_UncappedCmdlist: reasonString = "Uncapped command list"; break; + case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break; + default: break; + } + + GetDebugManager()->RenderText(0.0f, 0.0f, 1.0f, "Failed to capture frame %u: %hs", m_FrameCounter, reasonString); + + GetDebugManager()->SetOutputDimensions(w, h); + } + + old.ApplyState(m_pImmediateContext); + } + + m_FrameRecord.back().frameInfo.frameNumber = m_FrameCounter+1; + + m_pImmediateContext->CleanupCapture(); + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + context->CleanupCapture(); + else + RDCERR("NULL deferred context in resource record!"); + } + + GetResourceManager()->ClearReferencedResources(); + + if(m_Failures > 5) // failed too many times + { + m_pImmediateContext->FinishCapture(); + + m_FrameRecord.pop_back(); + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + { + context->FinishCapture(); + } + else + { + RDCERR("NULL deferred context in resource record!"); + } + } + + m_pImmediateContext->FreeCaptureData(); + + m_FailedFrame = m_FrameCounter; + m_FailedReason = reason; + + m_State = WRITING_IDLE; + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + context->CleanupCapture(); + else + RDCERR("NULL deferred context in resource record!"); + } + + GetResourceManager()->MarkUnwrittenResources(); + } + else + { + GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Write); + GetResourceManager()->PrepareInitialContents(); + + m_pImmediateContext->AttemptCapture(); + m_pImmediateContext->BeginCaptureFrame(); + + for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) + { + WrappedID3D11DeviceContext *context = *it; + + if(context) + { + context->AttemptCapture(); + } + else + { + RDCERR("NULL deferred context in resource record!"); + } + } + } + + if(m_pInfoQueue) + m_pInfoQueue->ClearStoredMessages(); + + return false; + } +} + HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UINT Flags) { if((Flags & DXGI_PRESENT_TEST) != 0) @@ -2383,52 +2874,69 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI m_FrameTimes.clear(); } + + uint32_t overlay = RenderDoc::Inst().GetOverlayBits(); - ID3D11RenderTargetView *rtv = m_SwapChains[swap]; - - m_pImmediateContext->GetReal()->OMSetRenderTargets(1, &rtv, NULL); - - int w = GetDebugManager()->GetWidth(); - int h = GetDebugManager()->GetHeight(); - - DXGI_SWAP_CHAIN_DESC swapDesc = {0}; - swap->GetDesc(&swapDesc); - GetDebugManager()->SetOutputDimensions(swapDesc.BufferDesc.Width, swapDesc.BufferDesc.Height); - - if(swap == m_SwapChain) + if(overlay & eOverlay_Enabled) { - GetDebugManager()->RenderText(0.0f, 0.0f, 1.0f, "Frame: %d. F12/PrtScr to capture. %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)", - m_FrameCounter, m_AvgFrametime, m_MinFrametime, m_MaxFrametime, 1000.0f/m_AvgFrametime); - size_t i=0; - for(i=0; i < m_FrameRecord.size(); i++) - GetDebugManager()->RenderText(0.0f, (float)(i+1)*18.0f, 1.0f, "Captured frame %d.\n", m_FrameRecord[i].frameInfo.frameNumber); + ID3D11RenderTargetView *rtv = m_SwapChains[swap]; - if(m_FailedFrame > 0) + m_pImmediateContext->GetReal()->OMSetRenderTargets(1, &rtv, NULL); + + int w = GetDebugManager()->GetWidth(); + int h = GetDebugManager()->GetHeight(); + + DXGI_SWAP_CHAIN_DESC swapDesc = {0}; + swap->GetDesc(&swapDesc); + GetDebugManager()->SetOutputDimensions(swapDesc.BufferDesc.Width, swapDesc.BufferDesc.Height); + + if(swap == m_SwapChain) { - const char *reasonString = "Unknown reason"; - switch(m_FailedReason) + string overlayText = "F12/PrtScr to capture."; + + if(overlay & eOverlay_FrameNumber) + overlayText += StringFormat::Fmt(" Frame: %d.", m_FrameCounter); + if(overlay & eOverlay_FrameRate) + overlayText += StringFormat::Fmt(" %.2lf ms (%.2lf .. %.2lf) (%.0lf FPS)", + m_AvgFrametime, m_MinFrametime, m_MaxFrametime, 1000.0f/m_AvgFrametime); + + GetDebugManager()->RenderText(0.0f, 0.0f, 1.0f, overlayText.c_str()); + + size_t i=0; + + if(overlay & eOverlay_CaptureList) { + for(i=0; i < m_FrameRecord.size(); i++) + GetDebugManager()->RenderText(0.0f, (float)(i+1)*18.0f, 1.0f, "Captured frame %d.\n", m_FrameRecord[i].frameInfo.frameNumber); + } + + if(m_FailedFrame > 0) + { + const char *reasonString = "Unknown reason"; + switch(m_FailedReason) + { case CaptureFailed_UncappedCmdlist: reasonString = "Uncapped command list"; break; case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break; default: break; + } + + GetDebugManager()->RenderText(0.0f, (float)(++i)*18.0f, 1.0f, "Failed capture at frame %d:\n", m_FailedFrame); + GetDebugManager()->RenderText(0.0f, (float)(++i)*18.0f, 1.0f, " %hs\n", reasonString); } - GetDebugManager()->RenderText(0.0f, (float)(++i)*18.0f, 1.0f, "Failed capture at frame %d:\n", m_FailedFrame); - GetDebugManager()->RenderText(0.0f, (float)(++i)*18.0f, 1.0f, " %hs\n", reasonString); +#if !defined(RELEASE) + GetDebugManager()->RenderText(0.0f, float(++i)*18.0f, 1.0f, "%llu chunks - %.2f MB", Chunk::NumLiveChunks(), float(Chunk::TotalMem())/1024.0f/1024.0f); +#endif + } + else + { + GetDebugManager()->RenderText(0.0f, 0.0f, 1.0f, "Inactive swapchain, F11 to cycle"); } -#if !defined(RELEASE) - GetDebugManager()->RenderText(0.0f, float(++i)*18.0f, 1.0f, "%llu chunks - %.2f MB", Chunk::NumLiveChunks(), float(Chunk::TotalMem())/1024.0f/1024.0f); -#endif - } - else - { - GetDebugManager()->RenderText(0.0f, 0.0f, 1.0f, "Inactive swapchain, F11 to cycle"); - } - - GetDebugManager()->SetOutputDimensions(w, h); + GetDebugManager()->SetOutputDimensions(w, h); - old.ApplyState(m_pImmediateContext); + old.ApplyState(m_pImmediateContext); + } if(RenderDoc::Inst().ShouldFocusToggle()) { @@ -2457,451 +2965,21 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapChain *swap, UINT SyncInterval, UI } } - if(swap != m_SwapChain) - { + if(swap != m_SwapChain || m_SwapChain == NULL) return S_OK; - } - // kill any current capture - if(m_State == WRITING_CAPFRAME) - { - CaptureFailReason reason; + DXGI_SWAP_CHAIN_DESC swapDesc = {0}; + m_SwapChain->GetDesc(&swapDesc); - if(m_pImmediateContext->HasSuccessfulCapture(reason)) - { - SCOPED_LOCK(m_D3DLock); - - RDCLOG("Finished capture, Frame %u", m_FrameCounter); - - m_Failures = 0; - m_FailedFrame = 0; - m_FailedReason = CaptureSucceeded; - - m_pImmediateContext->EndCaptureFrame(); - m_pImmediateContext->FinishCapture(); - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - { - context->FinishCapture(); - } - else - { - RDCERR("NULL deferred context in resource record!"); - } - } - - const uint32_t maxSize = 1024; - - byte *thpixels = NULL; - uint32_t thwidth = 0; - uint32_t thheight = 0; - - { - ID3D11RenderTargetView *rtv = m_SwapChains[swap]; - - ID3D11Resource *res = NULL; - - rtv->GetResource(&res); res->Release(); - - ID3D11Texture2D *tex = (ID3D11Texture2D *)res; - - D3D11_TEXTURE2D_DESC desc; - tex->GetDesc(&desc); - - desc.BindFlags = 0; - desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ; - desc.MiscFlags = 0; - desc.Usage = D3D11_USAGE_STAGING; - - bool msaa = (desc.SampleDesc.Count > 1) || (desc.SampleDesc.Quality > 0); - - desc.SampleDesc.Count = 1; - desc.SampleDesc.Quality = 0; - - ID3D11Texture2D *stagingTex = NULL; - - HRESULT hr = m_pDevice->CreateTexture2D(&desc, NULL, &stagingTex); - - if(FAILED(hr)) - { - RDCERR("Couldn't create staging texture to create thumbnail. %08x", hr); - } - else - { - if(msaa) - { - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.CPUAccessFlags = 0; - desc.Usage = D3D11_USAGE_DEFAULT; - - ID3D11Texture2D *resolveTex = NULL; - - HRESULT hr = m_pDevice->CreateTexture2D(&desc, NULL, &resolveTex); - - if(FAILED(hr)) - { - RDCERR("Couldn't create resolve texture to create thumbnail. %08x", hr); - tex = NULL; - } - else - { - m_pImmediateContext->GetReal()->ResolveSubresource(resolveTex, 0, tex, 0, desc.Format); - m_pImmediateContext->GetReal()->CopyResource(stagingTex, resolveTex); - resolveTex->Release(); - } - } - else - { - m_pImmediateContext->GetReal()->CopyResource(stagingTex, tex); - } - - if(tex) - { - ResourceFormat fmt = MakeResourceFormat(desc.Format); - - D3D11_MAPPED_SUBRESOURCE mapped; - hr = m_pImmediateContext->GetReal()->Map(stagingTex, 0, D3D11_MAP_READ, 0, &mapped); - - if(FAILED(hr)) - { - RDCERR("Couldn't map staging texture to create thumbnail. %08x", hr); - } - else - { - byte *data = (byte *)mapped.pData; - - float aspect = float(desc.Width)/float(desc.Height); - - thwidth = RDCMIN(maxSize, desc.Width); - thwidth &= ~0x7; // align down to multiple of 8 - thheight = uint32_t(float(thwidth)/aspect); - - thpixels = new byte[3*thwidth*thheight]; - - float widthf = float(desc.Width); - float heightf = float(desc.Height); - - uint32_t stride = fmt.compByteWidth*fmt.compCount; - - bool buf1010102 = false; - bool bufBGRA = false; - - if(fmt.special && fmt.specialFormat == eSpecial_R10G10B10A2) - { - stride = 4; - buf1010102 = true; - } - if(fmt.special && fmt.specialFormat == eSpecial_B8G8R8A8) - { - stride = 4; - bufBGRA = true; - } - - byte *dst = thpixels; - - for(uint32_t y=0; y < thheight; y++) - { - for(uint32_t x=0; x < thwidth; x++) - { - float xf = float(x)/float(thwidth); - float yf = float(y)/float(thheight); - - byte *src = &data[ stride*uint32_t(xf*widthf) + mapped.RowPitch*uint32_t(yf*heightf) ]; - - if(buf1010102) - { - uint32_t *src1010102 = (uint32_t *)src; - Vec4f unorm = ConvertFromR10G10B10A2(*src1010102); - dst[0] = (byte)(unorm.x*255.0f); - dst[1] = (byte)(unorm.y*255.0f); - dst[2] = (byte)(unorm.z*255.0f); - } - else if(bufBGRA) - { - dst[0] = src[2]; - dst[1] = src[1]; - dst[2] = src[0]; - } - else - { - dst[0] = src[0]; - dst[1] = src[1]; - dst[2] = src[2]; - } - - dst += 3; - } - } - - m_pImmediateContext->GetReal()->Unmap(stagingTex, 0); - } - } - - stagingTex->Release(); - } - - } - - byte *jpgbuf = NULL; - int len = thwidth*thheight; - - { - 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); - - SAFE_DELETE_ARRAY(jpgbuf); - SAFE_DELETE(thpixels); - - { - SCOPED_SERIALISE_CONTEXT(DEVICE_INIT); - - SERIALISE_ELEMENT(ResourceId, immContextId, m_pImmediateContext->GetResourceID()); - - m_pFileSerialiser->Insert(scope.Get(true)); - } - - RDCDEBUG("Inserting Resource Serialisers"); - - GetResourceManager()->InsertReferencedChunks(m_pFileSerialiser); - - GetResourceManager()->InsertInitialContentsChunks(m_pSerialiser, m_pFileSerialiser); - - RDCDEBUG("Creating Capture Scope"); - - { - SCOPED_SERIALISE_CONTEXT(CAPTURE_SCOPE); - - Serialise_CaptureScope(0); - - m_pFileSerialiser->Insert(scope.Get(true)); - } - - { - RDCDEBUG("Getting Resource Record"); - - D3D11ResourceRecord *record = m_ResourceManager->GetResourceRecord(m_pImmediateContext->GetResourceID()); - - RDCDEBUG("Accumulating context resource list"); - - map 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(); - - SAFE_DELETE(m_pFileSerialiser); - - RenderDoc::Inst().SuccessfullyWrittenLog(); - - m_State = WRITING_IDLE; - - m_pImmediateContext->CleanupCapture(); - - m_pImmediateContext->FreeCaptureData(); - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - context->CleanupCapture(); - else - RDCERR("NULL deferred context in resource record!"); - } - - GetResourceManager()->MarkUnwrittenResources(); - - GetResourceManager()->ClearReferencedResources(); - } - else - { - RDCLOG("Failed to capture, frame %u", m_FrameCounter); - - m_Failures++; - - { - D3D11RenderState old = *m_pImmediateContext->GetCurrentPipelineState(); - - ID3D11RenderTargetView *rtv = m_SwapChains[swap]; - - if(rtv) - { - m_pImmediateContext->GetReal()->OMSetRenderTargets(1, &rtv, NULL); - - int w = GetDebugManager()->GetWidth(); - int h = GetDebugManager()->GetHeight(); - - DXGI_SWAP_CHAIN_DESC swapDesc = {0}; - swap->GetDesc(&swapDesc); - GetDebugManager()->SetOutputDimensions(swapDesc.BufferDesc.Width, swapDesc.BufferDesc.Height); - - const char *reasonString = "Unknown reason"; - switch(reason) - { - case CaptureFailed_UncappedCmdlist: reasonString = "Uncapped command list"; break; - case CaptureFailed_UncappedUnmap: reasonString = "Uncapped Map()/Unmap()"; break; - default: break; - } - - GetDebugManager()->RenderText(0.0f, 0.0f, 1.0f, "Failed to capture frame %u: %hs", m_FrameCounter, reasonString); - - GetDebugManager()->SetOutputDimensions(w, h); - } - - old.ApplyState(m_pImmediateContext); - } - - m_FrameRecord.back().frameInfo.frameNumber = m_FrameCounter+1; - - m_pImmediateContext->CleanupCapture(); - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - context->CleanupCapture(); - else - RDCERR("NULL deferred context in resource record!"); - } - - GetResourceManager()->ClearReferencedResources(); - - if(m_Failures > 5) // failed too many times - { - m_pImmediateContext->FinishCapture(); - - m_FrameRecord.pop_back(); - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - { - context->FinishCapture(); - } - else - { - RDCERR("NULL deferred context in resource record!"); - } - } - - m_pImmediateContext->FreeCaptureData(); - - m_FailedFrame = m_FrameCounter; - m_FailedReason = reason; - - m_State = WRITING_IDLE; - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - context->CleanupCapture(); - else - RDCERR("NULL deferred context in resource record!"); - } - - GetResourceManager()->MarkUnwrittenResources(); - } - else - { - GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Write); - GetResourceManager()->PrepareInitialContents(); - - m_pImmediateContext->AttemptCapture(); - m_pImmediateContext->BeginCaptureFrame(); - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - { - context->AttemptCapture(); - } - else - { - RDCERR("NULL deferred context in resource record!"); - } - } - } - - if(m_pInfoQueue) - m_pInfoQueue->ClearStoredMessages(); - } - } + // kill any current capture that isn't application defined + if(m_State == WRITING_CAPFRAME && !m_AppControlledCapture) + EndFrameCapture(swapDesc.OutputWindow); if(RenderDoc::Inst().ShouldTriggerCapture(m_FrameCounter) && m_State == WRITING_IDLE) { - SCOPED_LOCK(m_D3DLock); + StartFrameCapture(swapDesc.OutputWindow); - m_State = WRITING_CAPFRAME; - - m_Failures = 0; - m_FailedFrame = 0; - m_FailedReason = CaptureSucceeded; - - FetchFrameRecord record; - record.frameInfo.frameNumber = m_FrameCounter+1; - m_FrameRecord.push_back(record); - - GetResourceManager()->ClearReferencedResources(); - - GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Write); - - m_pImmediateContext->AttemptCapture(); - m_pImmediateContext->BeginCaptureFrame(); - - for(auto it = m_DeferredContexts.begin(); it != m_DeferredContexts.end(); ++it) - { - WrappedID3D11DeviceContext *context = *it; - - if(context) - { - context->AttemptCapture(); - } - else - { - RDCERR("NULL deferred context in resource record!"); - } - } - - GetResourceManager()->PrepareInitialContents(); - - if(m_pInfoQueue) - m_pInfoQueue->ClearStoredMessages(); - - RDCLOG("Starting capture, frame %u", m_FrameCounter); + m_AppControlledCapture = false; } return S_OK; diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 525ce0dce..9ca5c0c36 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -129,6 +129,7 @@ class WrappedID3D11ClassLinkage; enum CaptureFailReason; class WrappedID3D11Device : + public IFrameCapturer, #if defined(INCLUDE_D3D_11_1) public ID3D11Device1 #else @@ -177,6 +178,7 @@ private: Serialiser *m_pSerialiser; Serialiser *m_pDebugSerialiser; LogState m_State; + bool m_AppControlledCapture; set m_CachedStateObjects; set m_DeferredContexts; @@ -245,6 +247,10 @@ public: void Serialise_CaptureScope(uint64_t offset); + void StartFrameCapture(void *wnd); + void SetActiveWindow(void *wnd); + bool EndFrameCapture(void *wnd); + //////////////////////////////////////////////////////////////// // log replaying diff --git a/renderdoc/driver/dxgi/dxgi_wrapped.cpp b/renderdoc/driver/dxgi/dxgi_wrapped.cpp index a3707fccb..3d2c4e810 100644 --- a/renderdoc/driver/dxgi/dxgi_wrapped.cpp +++ b/renderdoc/driver/dxgi/dxgi_wrapped.cpp @@ -146,12 +146,18 @@ WrappedIDXGISwapChain::WrappedIDXGISwapChain(IDXGISwapChain* real, WrappedID3D11 m_bFullscreen = desc.Windowed == FALSE; + RenderDoc::Inst().AddFrameCapturer(desc.OutputWindow, m_pDevice); + SAFE_ADDREF(m_pDevice); } WrappedIDXGISwapChain::~WrappedIDXGISwapChain() { m_pDevice->ReleaseSwapchainResources(this); + + DXGI_SWAP_CHAIN_DESC desc; + m_pReal->GetDesc(&desc); + RenderDoc::Inst().RemoveFrameCapturer(desc.OutputWindow); SAFE_RELEASE(m_pReal); for(int i=0; i < MAX_NUM_BACKBUFFERS; i++) diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index de4b6456e..9e6c76e3c 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -194,12 +194,42 @@ uint32_t RENDERDOC_CC RENDERDOC_InjectIntoProcess(uint32_t pid, const wchar_t *l return Process::InjectIntoProcess(pid, logfile, opts, waitForExit); } +extern "C" RENDERDOC_API +void RENDERDOC_CC RENDERDOC_SetActiveWindow(void *wndHandle) +{ + RenderDoc::Inst().SetActiveWindow(wndHandle); +} + extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_TriggerCapture() { RenderDoc::Inst().TriggerCapture(); } +extern "C" RENDERDOC_API +void RENDERDOC_CC RENDERDOC_StartFrameCapture(void *wndHandle) +{ + RenderDoc::Inst().StartFrameCapture(wndHandle); +} + +extern "C" RENDERDOC_API +bool RENDERDOC_CC RENDERDOC_EndFrameCapture(void *wndHandle) +{ + return RenderDoc::Inst().EndFrameCapture(wndHandle); +} + +extern "C" RENDERDOC_API +uint32_t RENDERDOC_CC RENDERDOC_GetOverlayBits() +{ + return RenderDoc::Inst().GetOverlayBits(); +} + +extern "C" RENDERDOC_API +void RENDERDOC_CC RENDERDOC_MaskOverlayBits(uint32_t and, uint32_t or) +{ + RenderDoc::Inst().MaskOverlayBits(and, or); +} + extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_QueueCapture(uint32_t frameNumber) { diff --git a/renderdoc/replay/renderdoc.h b/renderdoc/replay/renderdoc.h index bdc4be7f1..e8d04e585 100644 --- a/renderdoc/replay/renderdoc.h +++ b/renderdoc/replay/renderdoc.h @@ -224,9 +224,24 @@ typedef uint32_t (RENDERDOC_CC *pRENDERDOC_ExecuteAndInject)(const wchar_t *app, extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_InjectIntoProcess(uint32_t pid, const wchar_t *logfile, const CaptureOptions *opts, bool waitForExit); typedef uint32_t (RENDERDOC_CC *pRENDERDOC_InjectIntoProcess)(uint32_t pid, const wchar_t *logfile, const CaptureOptions *opts, bool waitForExit); +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_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 bool RENDERDOC_CC RENDERDOC_EndFrameCapture(void *wndHandle); +typedef bool (RENDERDOC_CC *pRENDERDOC_EndFrameCapture)(void *wndHandle); + +extern "C" RENDERDOC_API uint32_t RENDERDOC_CC RENDERDOC_GetOverlayBits(); +typedef uint32_t (RENDERDOC_CC *pRENDERDOC_GetOverlayBits)(); + +extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_MaskOverlayBits(uint32_t and, uint32_t or); +typedef void (RENDERDOC_CC *pRENDERDOC_MaskOverlayBits)(uint32_t and, uint32_t or); + ////////////////////////////////////////////////////////////////////////// // Remote access and control //////////////////////////////////////////////////////////////////////////