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 //////////////////////////////////////////////////////////////////////////