diff --git a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp index 8b09c71cb..1c42f9a30 100644 --- a/qrenderdoc/Windows/Dialogs/LiveCapture.cpp +++ b/qrenderdoc/Windows/Dialogs/LiveCapture.cpp @@ -355,10 +355,30 @@ void LiveCapture::saveCapture_triggered() if(path.endsWith(lit(".rdc"))) path.chop(4); + // don't save duplicates if we have multiple captures from the same frame (possible if the + // application is not presenting at all and using the API to capture) + QMap existingFiles; + for(QListWidgetItem *item : ui->captures->selectedItems()) { Capture *cap = GetCapture(item); - saveCapture(cap, QFormatStr("%1-frame%2.rdc").arg(path).arg(cap->frameNumber)); + + QString filename = QFormatStr("%1-frame%2").arg(path).arg(cap->frameNumber); + if(cap->frameNumber == ~0U) + filename = QFormatStr("%1-capture").arg(path); + + if(existingFiles.contains(cap->frameNumber)) + { + filename += QFormatStr("_%1").arg(existingFiles[cap->frameNumber]); + existingFiles[cap->frameNumber]++; + } + else + { + // start on 2 next time + existingFiles[cap->frameNumber] = 2; + } + + saveCapture(cap, QFormatStr("%1.rdc").arg(filename)); } } } @@ -652,8 +672,12 @@ QString LiveCapture::MakeText(Capture *cap) text += tr(" (Remote)"); text += lit("\n") + cap->api; - text += tr("\nFrame #%1 ").arg(cap->frameNumber) + - cap->timestamp.toString(lit("yyyy-MM-dd HH:mm:ss")); + if(cap->frameNumber == ~0U) + text += tr("\nUser-defined Capture "); + else + text += tr("\nFrame #%1 ").arg(cap->frameNumber); + + text += cap->timestamp.toString(lit("yyyy-MM-dd HH:mm:ss")); return text; } @@ -687,10 +711,14 @@ bool LiveCapture::checkAllowClose() if(!suppressRemoteWarning && !notoall) { + QString frameName = tr("Frame #%1").arg(cap->frameNumber); + if(cap->frameNumber == ~0U) + frameName = tr("User-defined Capture"); + res = RDDialog::question(this, tr("Unsaved capture"), - tr("Save this capture '%1 Frame #%2' at %3?") + tr("Save this capture '%1 %2' at %3?") .arg(cap->name) - .arg(cap->frameNumber) + .arg(frameName) .arg(cap->timestamp.toString(lit("HH:mm:ss"))), msgFlags); diff --git a/qrenderdoc/Windows/EventBrowser.cpp b/qrenderdoc/Windows/EventBrowser.cpp index cb912801a..975a3626b 100644 --- a/qrenderdoc/Windows/EventBrowser.cpp +++ b/qrenderdoc/Windows/EventBrowser.cpp @@ -238,11 +238,15 @@ EventBrowser::~EventBrowser() void EventBrowser::OnCaptureLoaded() { - RDTreeWidgetItem *frame = new RDTreeWidgetItem( - {QFormatStr("Frame #%1").arg(m_Ctx.FrameInfo().frameNumber), QString(), QString(), QString()}); + uint32_t frameNumber = m_Ctx.FrameInfo().frameNumber; + + QString rootName = + frameNumber == ~0U ? tr("User-defined Capture") : tr("Frame #%1").arg(frameNumber); + + RDTreeWidgetItem *frame = new RDTreeWidgetItem({rootName, QString(), QString(), QString()}); RDTreeWidgetItem *framestart = - new RDTreeWidgetItem({tr("Frame Start"), lit("0"), lit("0"), QString()}); + new RDTreeWidgetItem({tr("Capture Start"), lit("0"), lit("0"), QString()}); framestart->setTag(QVariant::fromValue(EventItemTag(0, 0))); frame->addChild(framestart); diff --git a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp index 8058f89d5..6f43efe49 100644 --- a/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp +++ b/qrenderdoc/Windows/PipelineState/PipelineStateViewer.cpp @@ -369,7 +369,9 @@ div.stage table tr td { border-right: 1px solid #AAAAAA; background-color: #EEEE xml.writeStartElement(lit("h3")); { - QString context = tr("Frame %1").arg(m_Ctx.FrameInfo().frameNumber); + uint32_t frameNumber = m_Ctx.FrameInfo().frameNumber; + + QString context = frameNumber == ~0U ? tr("Capture") : tr("Frame %1").arg(frameNumber); const DrawcallDescription *draw = m_Ctx.CurDrawcall(); diff --git a/qrenderdoc/Windows/TimelineBar.cpp b/qrenderdoc/Windows/TimelineBar.cpp index e746eb086..1f24f2f3c 100644 --- a/qrenderdoc/Windows/TimelineBar.cpp +++ b/qrenderdoc/Windows/TimelineBar.cpp @@ -187,7 +187,12 @@ void TimelineBar::OnCaptureClosed() void TimelineBar::OnCaptureLoaded() { - setWindowTitle(tr("Timeline - Frame #%1").arg(m_Ctx.FrameInfo().frameNumber)); + uint32_t frameNumber = m_Ctx.FrameInfo().frameNumber; + + if(frameNumber != ~0U) + setWindowTitle(tr("Timeline - Frame #%1").arg(frameNumber)); + else + setWindowTitle(tr("Timeline - Capture")); processDraws(m_RootMarkers, m_RootDraws, m_Ctx.CurDrawcalls()); diff --git a/renderdoc/api/replay/data_types.h b/renderdoc/api/replay/data_types.h index e4e408ca0..c95923fac 100644 --- a/renderdoc/api/replay/data_types.h +++ b/renderdoc/api/replay/data_types.h @@ -1060,7 +1060,12 @@ struct FrameStatistics DECLARE_REFLECTION_STRUCT(FrameStatistics); -DOCUMENT("Contains frame-level global information"); +DOCUMENT(R"(Contains frame-level global information + +.. data:: NoFrameNumber + + No frame number is available. +)"); struct FrameDescription { DOCUMENT(""); @@ -1080,7 +1085,8 @@ struct FrameDescription this counts the frame number when the capture was made. .. note:: This value is only accurate if the capture was triggered through the default mechanism, if - it was triggered from the application API it doesn't correspond to anything. + it was triggered from the application API it doesn't correspond to anything and will be set to + :data:`NoFrameNumber`. )"); uint32_t frameNumber; @@ -1111,6 +1117,8 @@ this counts the frame number when the capture was made. DOCUMENT("A list of debug messages that are not associated with any particular event."); rdcarray debugMessages; + + static const uint32_t NoFrameNumber = ~0U; }; DECLARE_REFLECTION_STRUCT(FrameDescription); diff --git a/renderdoc/core/core.cpp b/renderdoc/core/core.cpp index 326bc9a29..dc8415767 100644 --- a/renderdoc/core/core.cpp +++ b/renderdoc/core/core.cpp @@ -857,7 +857,10 @@ std::string RenderDoc::GetOverlayText(RDCDriver driver, uint32_t frameNumber, in { if(now - m_Captures[i].timestamp < 20) { - overlayText += StringFormat::Fmt("Captured frame %d.\n", m_Captures[i].frameNumber); + if(m_Captures[i].frameNumber == ~0U) + overlayText += "Captured user-defined capture.\n"; + else + overlayText += StringFormat::Fmt("Captured frame %d.\n", m_Captures[i].frameNumber); } } } @@ -1061,7 +1064,12 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, const FramePi { RDCFile *ret = new RDCFile; - m_CurrentLogFile = StringFormat::Fmt("%s_frame%u.rdc", m_CaptureFileTemplate.c_str(), frameNum); + std::string suffix = StringFormat::Fmt("_frame%u", frameNum); + + if(frameNum == ~0U) + suffix = "_capture"; + + m_CurrentLogFile = StringFormat::Fmt("%s%s.rdc", m_CaptureFileTemplate.c_str(), suffix.c_str()); // make sure we don't stomp another capture if we make multiple captures in the same frame. { @@ -1072,7 +1080,7 @@ RDCFile *RenderDoc::CreateRDC(RDCDriver driver, uint32_t frameNum, const FramePi }) != m_Captures.end()) { m_CurrentLogFile = - StringFormat::Fmt("%s_frame%u_%d.rdc", m_CaptureFileTemplate.c_str(), frameNum, altnum); + StringFormat::Fmt("%s%s_%d.rdc", m_CaptureFileTemplate.c_str(), suffix.c_str(), altnum); altnum++; } } diff --git a/renderdoc/driver/d3d11/d3d11_context.cpp b/renderdoc/driver/d3d11/d3d11_context.cpp index d04ae0315..f7cc28c9a 100644 --- a/renderdoc/driver/d3d11/d3d11_context.cpp +++ b/renderdoc/driver/d3d11/d3d11_context.cpp @@ -162,8 +162,6 @@ WrappedID3D11DeviceContext::WrappedID3D11DeviceContext(WrappedID3D11Device *real m_FailureReason = CaptureSucceeded; m_EmptyCommandList = true; - m_PresentChunk = false; - m_DrawcallStack.push_back(&m_ParentDrawcall); m_CurEventID = 0; @@ -543,15 +541,47 @@ void WrappedID3D11DeviceContext::EndCaptureFrame() m_ContextRecord->AddChunk(scope.Get()); } -void WrappedID3D11DeviceContext::Present(UINT SyncInterval, UINT Flags) +template +bool WrappedID3D11DeviceContext::Serialise_Present(SerialiserType &ser, UINT SyncInterval, UINT Flags) { - WriteSerialiser &ser = m_ScratchSerialiser; - SCOPED_SERIALISE_CHUNK(D3D11Chunk::SwapchainPresent); - SERIALISE_ELEMENT(m_ResourceID).Named("Context"_lit).TypedAs("ID3D11DeviceContext *"_lit); + // we don't do anything with these parameters, they're just here to store + // them for user benefits SERIALISE_ELEMENT(SyncInterval); SERIALISE_ELEMENT(Flags); - m_ContextRecord->AddChunk(scope.Get()); + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading() && IsLoading(m_State)) + { + AddEvent(); + + DrawcallDescription draw; + + draw.copyDestination = m_pDevice->GetBackbufferResourceID(); + + draw.name = StringFormat::Fmt("Present(%s)", ToStr(draw.copyDestination).c_str()); + draw.flags |= DrawFlags::Present; + + AddDrawcall(draw, true); + } + + return true; +} + +void WrappedID3D11DeviceContext::Present(UINT SyncInterval, UINT Flags) +{ + SERIALISE_TIME_CALL(); + + if(IsActiveCapturing(m_State)) + { + USE_SCRATCH_SERIALISER(); + GET_SERIALISER.SetDrawChunk(); + SCOPED_SERIALISE_CHUNK(D3D11Chunk::SwapchainPresent); + SERIALISE_ELEMENT(m_ResourceID).Named("Context"_lit).TypedAs("ID3D11DeviceContext *"_lit); + Serialise_Present(ser, SyncInterval, Flags); + + m_ContextRecord->AddChunk(scope.Get()); + } } bool WrappedID3D11DeviceContext::ShadowStorageInUse(D3D11ResourceRecord *record) @@ -827,34 +857,17 @@ bool WrappedID3D11DeviceContext::ProcessChunk(ReadSerialiser &ser, D3D11Chunk ch ret = Serialise_SwapDeviceContextState(ser, NULL, NULL); break; - case D3D11Chunk::SwapchainPresent: - { - // we don't do anything with these parameters, they're just here to store - // them for user benefits - UINT SyncInterval = 0, Flags = 0; - SERIALISE_ELEMENT(SyncInterval); - SERIALISE_ELEMENT(Flags); - - SERIALISE_CHECK_READ_ERRORS(); - - ret = true; - - m_PresentChunk = true; - break; - } + case D3D11Chunk::SwapchainPresent: ret = Serialise_Present(ser, 0, 0); break; default: { SystemChunk system = (SystemChunk)chunk; if(system == SystemChunk::CaptureEnd) { - if(IsLoading(m_State)) + if(IsLoading(m_State) && m_LastChunk != D3D11Chunk::SwapchainPresent) { - if(!m_PresentChunk) - AddEvent(); - DrawcallDescription draw; - draw.name = "End of Frame"; + draw.name = "End of Capture"; draw.flags |= DrawFlags::Present; draw.copyDestination = m_pDevice->GetBackbufferResourceID(); @@ -1200,6 +1213,7 @@ ReplayStatus WrappedID3D11DeviceContext::ReplayLog(CaptureState readType, uint32 if((SystemChunk)chunktype == SystemChunk::CaptureEnd) break; + m_LastChunk = chunktype; m_CurEventID++; } diff --git a/renderdoc/driver/d3d11/d3d11_context.h b/renderdoc/driver/d3d11/d3d11_context.h index d085f6e70..e8efa1005 100644 --- a/renderdoc/driver/d3d11/d3d11_context.h +++ b/renderdoc/driver/d3d11/d3d11_context.h @@ -176,8 +176,6 @@ private: RenderDoc::Inst().AddActiveDriver(RDCDriver::D3D11, false); } - bool m_PresentChunk; - ResourceId m_FakeContext; bool m_DoStateVerify; @@ -212,6 +210,7 @@ private: uint64_t m_CurChunkOffset; SDChunkMetaData m_ChunkMetadata; uint32_t m_CurEventID, m_CurDrawcallID; + D3D11Chunk m_LastChunk; ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; @@ -287,7 +286,7 @@ public: void MarkDirtyResource(ResourceId id); // insert a fake chunk just to store these parameters - void Present(UINT SyncInterval, UINT Flags); + IMPLEMENT_FUNCTION_SERIALISED(void, Present, UINT SyncInterval, UINT Flags); void CleanupCapture(); bool ShadowStorageInUse(D3D11ResourceRecord *record); diff --git a/renderdoc/driver/d3d11/d3d11_device.cpp b/renderdoc/driver/d3d11/d3d11_device.cpp index a65babeee..74b90a33f 100644 --- a/renderdoc/driver/d3d11/d3d11_device.cpp +++ b/renderdoc/driver/d3d11/d3d11_device.cpp @@ -101,15 +101,10 @@ WrappedID3D11Device::WrappedID3D11Device(ID3D11Device *realDevice, D3D11InitPara m_WrappedMultithread.m_pDevice = this; m_WrappedVideo.m_pDevice = this; - m_FrameCounter = 0; - m_FailedFrame = 0; m_FailedReason = CaptureSucceeded; - m_Failures = 0; m_ChunkAtomic = 0; - m_AppControlledCapture = false; - if(RenderDoc::Inst().IsReplayApp()) { m_State = CaptureState::LoadingReplaying; @@ -1084,13 +1079,13 @@ bool WrappedID3D11Device::ProcessChunk(ReadSerialiser &ser, D3D11Chunk context) template bool WrappedID3D11Device::Serialise_CaptureScope(SerialiserType &ser) { - SERIALISE_ELEMENT(m_FrameCounter); + SERIALISE_ELEMENT_LOCAL(frameNumber, m_CapturedFrames.back().frameNumber); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayMode(m_State)) { - m_FrameRecord.frameInfo.frameNumber = m_FrameCounter; + m_FrameRecord.frameInfo.frameNumber = frameNumber; FrameStatistics &stats = m_FrameRecord.frameInfo.stats; RDCEraseEl(stats); @@ -1566,10 +1561,10 @@ void WrappedID3D11Device::StartFrameCapture(void *dev, void *wnd) m_FailedFrame = 0; m_FailedReason = CaptureSucceeded; - m_FrameCounter = RDCMAX((uint32_t)m_CapturedFrames.size(), m_FrameCounter); - FrameDescription frame; - frame.frameNumber = m_FrameCounter; + // for app-controlled captures the frame number is meaningless, so set it here here first. We'll + // update it with the actual frame counter when we un-set m_AppControlledCapture. + frame.frameNumber = ~0U; frame.captureTime = Timing::GetUnixTimestamp(); m_CapturedFrames.push_back(frame); @@ -1603,7 +1598,7 @@ void WrappedID3D11Device::StartFrameCapture(void *dev, void *wnd) if(m_pInfoQueue) m_pInfoQueue->ClearStoredMessages(); - RDCLOG("Starting capture, frame %u", m_FrameCounter); + RDCLOG("Starting capture, frame %u", m_CapturedFrames.back().frameNumber); } bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) @@ -1637,7 +1632,10 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) if(m_pImmediateContext->HasSuccessfulCapture(reason)) { - RDCLOG("Finished capture, Frame %u", m_FrameCounter); + RDCLOG("Finished capture, Frame %u", m_CapturedFrames.back().frameNumber); + + if(swapper == NULL) + swapper = m_LastSwap; m_Failures = 0; m_FailedFrame = 0; @@ -1906,7 +1904,7 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) default: break; } - RDCLOG("Failed to capture, frame %u: %s", m_FrameCounter, reasonString); + RDCLOG("Failed to capture, frame %u: %s", m_CapturedFrames.back().frameNumber, reasonString); m_Failures++; @@ -1923,14 +1921,16 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) m_TextRenderer->SetOutputDimensions(swapper->GetWidth(), swapper->GetHeight()); m_TextRenderer->SetOutputWindow(swapper->GetHWND()); - m_TextRenderer->RenderText(0.0f, 0.0f, "Failed to capture frame %u: %s", m_FrameCounter, - reasonString); + m_TextRenderer->RenderText(0.0f, 0.0f, "Failed to capture frame %u: %s", + m_CapturedFrames.back().frameNumber, reasonString); } old.ApplyState(m_pImmediateContext); } - m_CapturedFrames.back().frameNumber = m_FrameCounter; + uint32_t failedFrame = m_CapturedFrames.back().frameNumber; + + m_CapturedFrames.back().frameNumber = m_AppControlledCapture ? ~0U : m_FrameCounter; m_pImmediateContext->CleanupCapture(); @@ -1973,7 +1973,7 @@ bool WrappedID3D11Device::EndFrameCapture(void *dev, void *wnd) GetResourceManager()->FreeCaptureData(); - m_FailedFrame = m_FrameCounter; + m_FailedFrame = failedFrame; m_FailedReason = reason; m_State = CaptureState::BackgroundCapturing; @@ -2180,6 +2180,7 @@ void WrappedID3D11Device::FirstFrame(IDXGISwapper *swapper) RenderDoc::Inst().StartFrameCapture((ID3D11Device *)this, swapper->GetHWND()); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = 0; } } @@ -2201,6 +2202,8 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapper *swapper, UINT SyncInterval, U bool activeWindow = RenderDoc::Inst().IsActiveWindow((ID3D11Device *)this, swapper->GetHWND()); + m_LastSwap = swapper; + if(IsBackgroundCapturing(m_State)) { D3D11RenderState old = *m_pImmediateContext->GetCurrentPipelineState(); @@ -2243,14 +2246,16 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapper *swapper, UINT SyncInterval, U RenderDoc::Inst().AddActiveDriver(RDCDriver::D3D11, true); + // serialise the present call, even for inactive windows + if(IsActiveCapturing(m_State)) + m_pImmediateContext->Present(SyncInterval, Flags); + if(!activeWindow) return S_OK; // kill any current capture that isn't application defined if(IsActiveCapturing(m_State) && !m_AppControlledCapture) { - m_pImmediateContext->Present(SyncInterval, Flags); - RenderDoc::Inst().EndFrameCapture((ID3D11Device *)this, swapper->GetHWND()); } @@ -2259,6 +2264,7 @@ HRESULT WrappedID3D11Device::Present(IDXGISwapper *swapper, UINT SyncInterval, U RenderDoc::Inst().StartFrameCapture((ID3D11Device *)this, swapper->GetHWND()); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = m_FrameCounter; } return S_OK; diff --git a/renderdoc/driver/d3d11/d3d11_device.h b/renderdoc/driver/d3d11/d3d11_device.h index 809eddf00..48e77df77 100644 --- a/renderdoc/driver/d3d11/d3d11_device.h +++ b/renderdoc/driver/d3d11/d3d11_device.h @@ -345,7 +345,7 @@ private: D3D11ResourceRecord *m_DeviceRecord; CaptureState m_State; - bool m_AppControlledCapture; + bool m_AppControlledCapture = false; ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; @@ -374,10 +374,12 @@ private: std::map m_SwapChains; - uint32_t m_FrameCounter; - uint32_t m_FailedFrame; + IDXGISwapper *m_LastSwap = NULL; + + uint32_t m_FrameCounter = 0; + uint32_t m_FailedFrame = 0; CaptureFailReason m_FailedReason; - uint32_t m_Failures; + uint32_t m_Failures = 0; SDFile *m_StructuredFile = NULL; SDFile m_StoredStructuredData; diff --git a/renderdoc/driver/d3d12/d3d12_command_queue.h b/renderdoc/driver/d3d12/d3d12_command_queue.h index ab98c733f..66d818a73 100644 --- a/renderdoc/driver/d3d12/d3d12_command_queue.h +++ b/renderdoc/driver/d3d12/d3d12_command_queue.h @@ -133,7 +133,6 @@ class WrappedID3D12CommandQueue : public ID3D12CommandQueue, D3D12CommandData m_Cmd; ResourceId m_PrevQueueId; - ResourceId m_BackbufferID; bool ProcessChunk(ReadSerialiser &ser, D3D12Chunk context); @@ -155,7 +154,6 @@ public: D3D12DrawcallTreeNode &GetParentDrawcall() { return m_Cmd.m_ParentDrawcall; } const APIEvent &GetEvent(uint32_t eventId); uint32_t GetMaxEID() { return m_Cmd.m_Events.back().eventId; } - ResourceId GetBackbufferResourceID() { return m_BackbufferID; } void ClearAfterCapture(); ReplayStatus ReplayLog(CaptureState readType, uint32_t startEventID, uint32_t endEventID, diff --git a/renderdoc/driver/d3d12/d3d12_commands.cpp b/renderdoc/driver/d3d12/d3d12_commands.cpp index 1d3f96d48..808598231 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.cpp +++ b/renderdoc/driver/d3d12/d3d12_commands.cpp @@ -706,6 +706,8 @@ bool WrappedID3D12CommandQueue::ProcessChunk(ReadSerialiser &ser, D3D12Chunk chu // Just in case it gets exported and imported, completely ignore it. return true; + case D3D12Chunk::Swapchain_Present: ret = m_pDevice->Serialise_Present(ser, NULL, 0, 0); break; + default: { SystemChunk system = (SystemChunk)chunk; @@ -716,17 +718,18 @@ bool WrappedID3D12CommandQueue::ProcessChunk(ReadSerialiser &ser, D3D12Chunk chu SERIALISE_CHECK_READ_ERRORS(); - m_BackbufferID = PresentedImage; + if(PresentedImage != ResourceId()) + m_Cmd.m_LastPresentedImage = PresentedImage; - if(IsLoading(m_State)) + if(IsLoading(m_State) && m_Cmd.m_LastChunk != D3D12Chunk::Swapchain_Present) { m_Cmd.AddEvent(); DrawcallDescription draw; - draw.name = "Present()"; + draw.name = "End of Capture"; draw.flags |= DrawFlags::Present; - draw.copyDestination = m_BackbufferID; + draw.copyDestination = m_Cmd.m_LastPresentedImage; m_Cmd.AddDrawcall(draw, true); } @@ -895,6 +898,8 @@ ReplayStatus WrappedID3D12CommandQueue::ReplayLog(CaptureState readType, uint32_ if(IsActiveReplaying(m_State) && startEventID == endEventID) break; + m_Cmd.m_LastChunk = context; + // increment root event ID either if we didn't just replay a cmd // buffer event, OR if we are doing a frame sub-section replay, // in which case it's up to the calling code to make sure we only diff --git a/renderdoc/driver/d3d12/d3d12_commands.h b/renderdoc/driver/d3d12/d3d12_commands.h index 8c70b0009..83ba1dbf4 100644 --- a/renderdoc/driver/d3d12/d3d12_commands.h +++ b/renderdoc/driver/d3d12/d3d12_commands.h @@ -338,6 +338,9 @@ struct D3D12CommandData SDChunkMetaData m_ChunkMetadata; uint32_t m_RootEventID, m_RootDrawcallID; uint32_t m_FirstEventID, m_LastEventID; + D3D12Chunk m_LastChunk; + + ResourceId m_LastPresentedImage; SDFile *m_StructuredFile; diff --git a/renderdoc/driver/d3d12/d3d12_common.h b/renderdoc/driver/d3d12/d3d12_common.h index a9db2404c..01b13c475 100644 --- a/renderdoc/driver/d3d12/d3d12_common.h +++ b/renderdoc/driver/d3d12/d3d12_common.h @@ -827,5 +827,6 @@ enum class D3D12Chunk : uint32_t List_RSSetShadingRate, List_RSSetShadingRateImage, Device_ExternalDXGIResource, + Swapchain_Present, Max, }; diff --git a/renderdoc/driver/d3d12/d3d12_device.cpp b/renderdoc/driver/d3d12/d3d12_device.cpp index 37db44208..8d9223ea3 100644 --- a/renderdoc/driver/d3d12/d3d12_device.cpp +++ b/renderdoc/driver/d3d12/d3d12_device.cpp @@ -264,13 +264,9 @@ WrappedID3D12Device::WrappedID3D12Device(ID3D12Device *realDevice, D3D12InitPara m_Replay.SetDevice(this); - m_AppControlledCapture = false; - threadSerialiserTLSSlot = Threading::AllocateTLSSlot(); tempMemoryTLSSlot = Threading::AllocateTLSSlot(); - m_FrameCounter = 0; - m_HeaderChunk = NULL; m_Alloc = m_DataUploadAlloc = NULL; @@ -354,7 +350,6 @@ WrappedID3D12Device::WrappedID3D12Device(ID3D12Device *realDevice, D3D12InitPara m_DeviceRecord = NULL; m_Queue = NULL; - m_LastSwap = NULL; if(!RenderDoc::Inst().IsReplayApp()) { @@ -947,6 +942,7 @@ void WrappedID3D12Device::FirstFrame(IDXGISwapper *swapper) RenderDoc::Inst().StartFrameCapture((ID3D12Device *)this, swapper ? swapper->GetHWND() : NULL); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = 0; } } @@ -1613,6 +1609,24 @@ HRESULT WrappedID3D12Device::Present(ID3D12GraphicsCommandList *pOverlayCommandL RenderDoc::Inst().AddActiveDriver(RDCDriver::D3D12, true); + // serialise the present call, even for inactive windows + if(IsActiveCapturing(m_State)) + { + SERIALISE_TIME_CALL(); + + ID3D12Resource *backbuffer = NULL; + + if(swapper != NULL) + backbuffer = (ID3D12Resource *)swapper->GetBackbuffers()[swapper->GetLastPresentedBuffer()]; + + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(D3D12Chunk::Swapchain_Present); + Serialise_Present(ser, backbuffer, SyncInterval, Flags); + + m_FrameCaptureRecord->AddChunk(scope.Get()); + } + if(!activeWindow) return S_OK; @@ -1629,21 +1643,62 @@ HRESULT WrappedID3D12Device::Present(ID3D12GraphicsCommandList *pOverlayCommandL RenderDoc::Inst().StartFrameCapture((ID3D12Device *)this, swapper->GetHWND()); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = m_FrameCounter; } return S_OK; } +template +bool WrappedID3D12Device::Serialise_Present(SerialiserType &ser, ID3D12Resource *PresentedImage, + UINT SyncInterval, UINT Flags) +{ + SERIALISE_ELEMENT_LOCAL(PresentedBackbuffer, GetResID(PresentedImage)) + .TypedAs("ID3D12Resource *"_lit); + + // we don't do anything with these parameters, they're just here to store + // them for user benefits + SERIALISE_ELEMENT(SyncInterval); + SERIALISE_ELEMENT(Flags); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading() && IsLoading(m_State)) + { + D3D12CommandData &cmd = *m_Queue->GetCommandData(); + cmd.AddEvent(); + + DrawcallDescription draw; + + draw.name = StringFormat::Fmt("Present(%s)", ToStr(PresentedBackbuffer).c_str()); + draw.flags |= DrawFlags::Present; + + cmd.m_LastPresentedImage = PresentedBackbuffer; + draw.copyDestination = PresentedBackbuffer; + + cmd.AddDrawcall(draw, true); + } + + return true; +} + +template bool WrappedID3D12Device::Serialise_Present(ReadSerialiser &ser, + ID3D12Resource *PresentedImage, + UINT SyncInterval, UINT Flags); +template bool WrappedID3D12Device::Serialise_Present(WriteSerialiser &ser, + ID3D12Resource *PresentedImage, + UINT SyncInterval, UINT Flags); + template bool WrappedID3D12Device::Serialise_CaptureScope(SerialiserType &ser) { - SERIALISE_ELEMENT(m_FrameCounter); + SERIALISE_ELEMENT_LOCAL(frameNumber, m_CapturedFrames.back().frameNumber); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayMode(m_State)) { - m_FrameRecord.frameInfo.frameNumber = m_FrameCounter; + m_FrameRecord.frameInfo.frameNumber = frameNumber; RDCEraseEl(m_FrameRecord.frameInfo.stats); } @@ -1681,14 +1736,14 @@ bool WrappedID3D12Device::Serialise_BeginCaptureFrame(SerialiserType &ser) template bool WrappedID3D12Device::Serialise_BeginCaptureFrame(ReadSerialiser &ser); template bool WrappedID3D12Device::Serialise_BeginCaptureFrame(WriteSerialiser &ser); -void WrappedID3D12Device::EndCaptureFrame(ID3D12Resource *presentImage) +void WrappedID3D12Device::EndCaptureFrame() { WriteSerialiser &ser = GetThreadSerialiser(); ser.SetDrawChunk(); SCOPED_SERIALISE_CHUNK(SystemChunk::CaptureEnd); - SERIALISE_ELEMENT_LOCAL(PresentedBackbuffer, GetResID(presentImage)) - .TypedAs("ID3D12Resource *"_lit); + // here for compatibility reasons, this used to store the presented Resource. + SERIALISE_ELEMENT_LOCAL(PresentedBackbuffer, ResourceId()).Hidden(); m_FrameCaptureRecord->AddChunk(scope.Get()); } @@ -1702,10 +1757,8 @@ void WrappedID3D12Device::StartFrameCapture(void *dev, void *wnd) m_SubmitCounter = 0; - m_FrameCounter = RDCMAX((uint32_t)m_CapturedFrames.size(), m_FrameCounter); - FrameDescription frame; - frame.frameNumber = m_FrameCounter; + frame.frameNumber = m_AppControlledCapture ? ~0U : m_FrameCounter; frame.captureTime = Timing::GetUnixTimestamp(); RDCEraseEl(frame.stats); m_CapturedFrames.push_back(frame); @@ -1765,7 +1818,7 @@ void WrappedID3D12Device::StartFrameCapture(void *dev, void *wnd) GetResourceManager()->MarkResourceFrameReferenced(m_ResourceID, eFrameRef_Read); - RDCLOG("Starting capture, frame %u", m_FrameCounter); + RDCLOG("Starting capture, frame %u", m_CapturedFrames.back().frameNumber); } bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) @@ -1795,7 +1848,7 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) } } - RDCLOG("Finished capture, Frame %u", m_FrameCounter); + RDCLOG("Finished capture, Frame %u", m_CapturedFrames.back().frameNumber); ID3D12Resource *backbuffer = NULL; @@ -1813,7 +1866,7 @@ bool WrappedID3D12Device::EndFrameCapture(void *dev, void *wnd) // transition back to IDLE and readback initial states atomically { SCOPED_WRITELOCK(m_CapTransitionLock); - EndCaptureFrame(backbuffer); + EndCaptureFrame(); m_State = CaptureState::BackgroundCapturing; diff --git a/renderdoc/driver/d3d12/d3d12_device.h b/renderdoc/driver/d3d12/d3d12_device.h index 41f1bf39c..98ce3c11b 100644 --- a/renderdoc/driver/d3d12/d3d12_device.h +++ b/renderdoc/driver/d3d12/d3d12_device.h @@ -431,14 +431,14 @@ private: SDFile *m_StructuredFile = NULL; SDFile m_StoredStructuredData; - uint32_t m_FrameCounter; + uint32_t m_FrameCounter = 0; std::vector m_CapturedFrames; FrameRecord m_FrameRecord; std::vector m_Drawcalls; ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; - bool m_AppControlledCapture; + bool m_AppControlledCapture = false; bool m_InvalidPSO = false; @@ -492,7 +492,7 @@ private: std::map m_SwapChains; std::map m_BackbufferFormat; - IDXGISwapper *m_LastSwap; + IDXGISwapper *m_LastSwap = NULL; D3D12_FEATURE_DATA_D3D12_OPTIONS m_D3D12Opts; D3D12_FEATURE_DATA_D3D12_OPTIONS1 m_D3D12Opts1; @@ -502,7 +502,7 @@ private: template bool Serialise_CaptureScope(SerialiserType &ser); - void EndCaptureFrame(ID3D12Resource *presentImage); + void EndCaptureFrame(); bool m_debugLayerEnabled; @@ -653,6 +653,10 @@ public: bool EndFrameCapture(void *dev, void *wnd); bool DiscardFrameCapture(void *dev, void *wnd); + template + bool Serialise_Present(SerialiserType &ser, ID3D12Resource *PresentedImage, UINT SyncInterval, + UINT Flags); + template bool Serialise_BeginCaptureFrame(SerialiserType &ser); diff --git a/renderdoc/driver/d3d12/d3d12_replay.cpp b/renderdoc/driver/d3d12/d3d12_replay.cpp index a53e0c096..217b56a4c 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.cpp +++ b/renderdoc/driver/d3d12/d3d12_replay.cpp @@ -424,10 +424,13 @@ TextureDescription D3D12Replay::GetTexture(ResourceId id) if(desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) ret.creationFlags |= TextureCategory::ShaderReadWrite; - if(ret.resourceId == m_pDevice->GetQueue()->GetBackbufferResourceID()) { - ret.format = MakeResourceFormat(GetTypedFormat(desc.Format, CompType::UNorm)); - ret.creationFlags |= TextureCategory::SwapBuffer; + auto resit = m_ResourceIdx.find(ret.resourceId); + if(resit != m_ResourceIdx.end() && m_Resources[resit->second].type == ResourceType::SwapchainImage) + { + ret.format = MakeResourceFormat(GetTypedFormat(desc.Format, CompType::UNorm)); + ret.creationFlags |= TextureCategory::SwapBuffer; + } } return ret; diff --git a/renderdoc/driver/d3d12/d3d12_stringise.cpp b/renderdoc/driver/d3d12/d3d12_stringise.cpp index 5bfd1af57..43f02ba5d 100644 --- a/renderdoc/driver/d3d12/d3d12_stringise.cpp +++ b/renderdoc/driver/d3d12/d3d12_stringise.cpp @@ -28,7 +28,7 @@ template <> rdcstr DoStringise(const D3D12Chunk &el) { - RDCCOMPILE_ASSERT((uint32_t)D3D12Chunk::Max == 1105, "Chunks changed without updating names"); + RDCCOMPILE_ASSERT((uint32_t)D3D12Chunk::Max == 1106, "Chunks changed without updating names"); BEGIN_ENUM_STRINGISE(D3D12Chunk) { @@ -191,6 +191,7 @@ rdcstr DoStringise(const D3D12Chunk &el) STRINGISE_ENUM_CLASS_NAMED(List_RSSetShadingRateImage, "ID3D12GraphicsCommandList5::RSSetShadingRateImage"); STRINGISE_ENUM_CLASS_NAMED(Device_ExternalDXGIResource, "External DXGI Resource import"); + STRINGISE_ENUM_CLASS_NAMED(Swapchain_Present, "IDXGISwapChain::Present"); STRINGISE_ENUM_CLASS_NAMED(Max, "Max Chunk"); } END_ENUM_STRINGISE() diff --git a/renderdoc/driver/d3d8/d3d8_device.cpp b/renderdoc/driver/d3d8/d3d8_device.cpp index 331f22222..616be0b32 100644 --- a/renderdoc/driver/d3d8/d3d8_device.cpp +++ b/renderdoc/driver/d3d8/d3d8_device.cpp @@ -37,8 +37,6 @@ WrappedD3DDevice8::WrappedD3DDevice8(IDirect3DDevice8 *device, HWND wnd, m_DebugManager(NULL), m_PresentParameters(*pPresentationParameters) { - m_FrameCounter = 0; - // refcounters implicitly construct with one reference, but we don't start with any soft // references. m_SoftRefCounter.Release(); diff --git a/renderdoc/driver/d3d8/d3d8_device.h b/renderdoc/driver/d3d8/d3d8_device.h index 345c7ff67..3f0ba3d14 100644 --- a/renderdoc/driver/d3d8/d3d8_device.h +++ b/renderdoc/driver/d3d8/d3d8_device.h @@ -228,7 +228,7 @@ private: RefCounter8 m_SoftRefCounter; bool m_Alive; - uint32_t m_FrameCounter; + uint32_t m_FrameCounter = 0; }; //////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/driver/d3d9/d3d9_device.cpp b/renderdoc/driver/d3d9/d3d9_device.cpp index f70f80548..b278e8b78 100644 --- a/renderdoc/driver/d3d9/d3d9_device.cpp +++ b/renderdoc/driver/d3d9/d3d9_device.cpp @@ -34,8 +34,6 @@ WrappedD3DDevice9::WrappedD3DDevice9(IDirect3DDevice9 *device, HWND wnd) m_device(device), m_DebugManager(NULL) { - m_FrameCounter = 0; - // refcounters implicitly construct with one reference, but we don't start with any soft // references. m_SoftRefCounter.Release(); diff --git a/renderdoc/driver/d3d9/d3d9_device.h b/renderdoc/driver/d3d9/d3d9_device.h index ee27c42f8..3ec9846bd 100644 --- a/renderdoc/driver/d3d9/d3d9_device.h +++ b/renderdoc/driver/d3d9/d3d9_device.h @@ -258,7 +258,7 @@ private: RefCounter9 m_SoftRefCounter; bool m_Alive; - uint32_t m_FrameCounter; + uint32_t m_FrameCounter = 0; }; //////////////////////////////////////////////////////////////////////////// diff --git a/renderdoc/driver/gl/cgl_hooks.cpp b/renderdoc/driver/gl/cgl_hooks.cpp index f1c2adc8c..1d052a5ce 100644 --- a/renderdoc/driver/gl/cgl_hooks.cpp +++ b/renderdoc/driver/gl/cgl_hooks.cpp @@ -178,6 +178,8 @@ CGLError GL_EXPORT_NAME(CGLFlushDrawable)(CGLContextObj ctx) CGL.CGLGetSurface(ctx, &conn, &window, &surface); + gl_CurChunk = GLChunk::CGLFlushDrawable; + cglhook.driver.SwapBuffers(WindowingSystem::MacOS, (void *)(uintptr_t)window); } diff --git a/renderdoc/driver/gl/egl_hooks.cpp b/renderdoc/driver/gl/egl_hooks.cpp index d84d14c20..fe65a62d1 100644 --- a/renderdoc/driver/gl/egl_hooks.cpp +++ b/renderdoc/driver/gl/egl_hooks.cpp @@ -502,6 +502,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffers_renderdoc_hooked(EGLDisplay dp SurfaceConfig cfg = eglhook.windows[surface]; + gl_CurChunk = GLChunk::eglSwapBuffers; + eglhook.driver.SwapBuffers(cfg.system, cfg.wnd); } @@ -535,6 +537,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglPostSubBufferNV_renderdoc_hooked(EGLDispla { SurfaceConfig cfg = eglhook.windows[surface]; + gl_CurChunk = GLChunk::eglPostSubBufferNV; + eglhook.driver.SwapBuffers(cfg.system, cfg.wnd); } @@ -568,6 +572,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageEXT_renderdoc_hooked( { SurfaceConfig cfg = eglhook.windows[surface]; + gl_CurChunk = GLChunk::eglSwapBuffersWithDamageEXT; + eglhook.driver.SwapBuffers(cfg.system, cfg.wnd); } @@ -601,6 +607,8 @@ HOOK_EXPORT EGLBoolean EGLAPIENTRY eglSwapBuffersWithDamageKHR_renderdoc_hooked( { SurfaceConfig cfg = eglhook.windows[surface]; + gl_CurChunk = GLChunk::eglSwapBuffersWithDamageKHR; + eglhook.driver.SwapBuffers(cfg.system, cfg.wnd); } diff --git a/renderdoc/driver/gl/gl_common.h b/renderdoc/driver/gl/gl_common.h index 7ea36390c..2780442a2 100644 --- a/renderdoc/driver/gl/gl_common.h +++ b/renderdoc/driver/gl/gl_common.h @@ -2163,6 +2163,15 @@ enum class GLChunk : uint32_t glBlendEquationARB, glPrimitiveBoundingBoxARB, + SwapBuffers, + wglSwapBuffers, + glXSwapBuffers, + CGLFlushDrawable, + eglSwapBuffers, + eglPostSubBufferNV, + eglSwapBuffersWithDamageEXT, + eglSwapBuffersWithDamageKHR, + Max, }; diff --git a/renderdoc/driver/gl/gl_driver.cpp b/renderdoc/driver/gl/gl_driver.cpp index 841017dd5..6fd77bdad 100644 --- a/renderdoc/driver/gl/gl_driver.cpp +++ b/renderdoc/driver/gl/gl_driver.cpp @@ -630,7 +630,6 @@ WrappedOpenGL::WrappedOpenGL(GLPlatform &platform) m_SectionVersion = GLInitParams::CurrentVersion; - m_FrameCounter = 0; m_NoCtxFrames = 0; m_FailedFrame = 0; m_FailedReason = CaptureSucceeded; @@ -638,8 +637,6 @@ WrappedOpenGL::WrappedOpenGL(GLPlatform &platform) m_SuccessfulCapture = true; m_FailureReason = CaptureSucceeded; - m_AppControlledCapture = false; - m_UsesVRMarkers = false; m_SuppressDebugMessages = false; @@ -2056,6 +2053,18 @@ void WrappedOpenGL::SwapBuffers(WindowingSystem winSystem, void *windowHandle) m_BackbufferImages[windowHandle] = SaveBackbufferImage(); } + if(IsActiveCapturing(m_State) && gl_CurChunk != GLChunk::Max) + { + SERIALISE_TIME_CALL(); + + USE_SCRATCH_SERIALISER(); + ser.SetDrawChunk(); + SCOPED_SERIALISE_CHUNK(gl_CurChunk); + Serialise_Present(ser); + + GetContextRecord()->AddChunk(scope.Get()); + } + RenderDoc::Inst().AddActiveDriver(GetDriverType(), true); if(!activeWindow) @@ -2074,6 +2083,7 @@ void WrappedOpenGL::SwapBuffers(WindowingSystem winSystem, void *windowHandle) RenderDoc::Inst().StartFrameCapture(ctxdata.ctx, windowHandle); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = m_FrameCounter; } } @@ -2136,10 +2146,8 @@ void WrappedOpenGL::StartFrameCapture(void *dev, void *wnd) GLWindowingData switchctx = prevctx; MakeValidContextCurrent(switchctx, wnd); - m_FrameCounter = RDCMAX((uint32_t)m_CapturedFrames.size(), m_FrameCounter); - FrameDescription frame; - frame.frameNumber = m_FrameCounter; + frame.frameNumber = m_AppControlledCapture ? ~0U : m_FrameCounter; frame.captureTime = Timing::GetUnixTimestamp(); RDCEraseEl(frame.stats); m_CapturedFrames.push_back(frame); @@ -2169,7 +2177,7 @@ void WrappedOpenGL::StartFrameCapture(void *dev, void *wnd) m_ActiveContexts[Threading::GetCurrentID()] = prevctx; } - RDCLOG("Starting capture, frame %u", m_FrameCounter); + RDCLOG("Starting capture, frame %u", m_CapturedFrames.back().frameNumber); } bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) @@ -2187,7 +2195,7 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) if(HasSuccessfulCapture(reason)) { - RDCLOG("Finished capture, Frame %u", m_FrameCounter); + RDCLOG("Finished capture, Frame %u", m_CapturedFrames.back().frameNumber); m_Failures = 0; m_FailedFrame = 0; @@ -2366,7 +2374,7 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) default: break; } - RDCLOG("Failed to capture, frame %u: %s", m_FrameCounter, reasonString); + RDCLOG("Failed to capture, frame %u: %s", m_CapturedFrames.back().frameNumber, reasonString); m_Failures++; @@ -2374,7 +2382,8 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) { ContextData &ctxdata = GetCtxData(); - RenderOverlayText(0.0f, 0.0f, "Failed to capture frame %u: %s", m_FrameCounter, reasonString); + RenderOverlayText(0.0f, 0.0f, "Failed to capture frame %u: %s", + m_CapturedFrames.back().frameNumber, reasonString); // swallow all errors we might have inadvertantly caused. This is // better than letting an error propagate and maybe screw up the @@ -2384,7 +2393,9 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) ClearGLErrors(); } - m_CapturedFrames.back().frameNumber = m_FrameCounter; + uint32_t failedFrame = m_CapturedFrames.back().frameNumber; + + m_CapturedFrames.back().frameNumber = m_AppControlledCapture ? ~0U : m_FrameCounter; CleanupCapture(); @@ -2410,7 +2421,7 @@ bool WrappedOpenGL::EndFrameCapture(void *dev, void *wnd) FreeCaptureData(); - m_FailedFrame = m_FrameCounter; + m_FailedFrame = failedFrame; m_FailedReason = reason; m_State = CaptureState::BackgroundCapturing; @@ -2494,6 +2505,7 @@ void WrappedOpenGL::FirstFrame(void *ctx, void *wndHandle) RenderDoc::Inst().StartFrameCapture(ctx, NULL); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = 0; } } @@ -2555,16 +2567,45 @@ RenderDoc::FramePixels *WrappedOpenGL::SaveBackbufferImage() return fp; } +template +bool WrappedOpenGL::Serialise_Present(SerialiserType &ser) +{ + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading() && IsLoading(m_State)) + { + AddEvent(); + + DrawcallDescription draw; + + GLuint col = 0; + GL.glGetNamedFramebufferAttachmentParameterivEXT(m_CurrentDefaultFBO, eGL_COLOR_ATTACHMENT0, + eGL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME, + (GLint *)&col); + + draw.copyDestination = + GetResourceManager()->GetOriginalID(GetResourceManager()->GetID(TextureRes(GetCtx(), col))); + + draw.name = + StringFormat::Fmt("%s(%s)", ToStr(gl_CurChunk).c_str(), ToStr(draw.copyDestination).c_str()); + draw.flags |= DrawFlags::Present; + + AddDrawcall(draw, true); + } + + return true; +} + template bool WrappedOpenGL::Serialise_CaptureScope(SerialiserType &ser) { - SERIALISE_ELEMENT(m_FrameCounter); + SERIALISE_ELEMENT_LOCAL(frameNumber, m_CapturedFrames.back().frameNumber); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { - m_FrameRecord.frameInfo.frameNumber = m_FrameCounter; + m_FrameRecord.frameInfo.frameNumber = frameNumber; RDCEraseEl(m_FrameRecord.frameInfo.stats); } @@ -3190,12 +3231,19 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) } else if(system == SystemChunk::CaptureEnd) { - if(IsLoading(m_State)) + bool lastSwap = + m_LastChunk == GLChunk::SwapBuffers || m_LastChunk == GLChunk::wglSwapBuffers || + m_LastChunk == GLChunk::glXSwapBuffers || m_LastChunk == GLChunk::CGLFlushDrawable || + m_LastChunk == GLChunk::eglSwapBuffers || m_LastChunk == GLChunk::eglPostSubBufferNV || + m_LastChunk == GLChunk::eglSwapBuffersWithDamageEXT || + m_LastChunk == GLChunk::eglSwapBuffersWithDamageKHR; + + if(IsLoading(m_State) && !lastSwap) { AddEvent(); DrawcallDescription draw; - draw.name = "SwapBuffers()"; + draw.name = "End of Capture"; draw.flags |= DrawFlags::Present; GLuint col = 0; @@ -4386,6 +4434,16 @@ bool WrappedOpenGL::ProcessChunk(ReadSerialiser &ser, GLChunk chunk) case GLChunk::glReleaseKeyedMutexWin32EXT: return Serialise_glReleaseKeyedMutexWin32EXT(ser, 0, 0); + case GLChunk::SwapBuffers: + case GLChunk::wglSwapBuffers: + case GLChunk::glXSwapBuffers: + case GLChunk::CGLFlushDrawable: + case GLChunk::eglSwapBuffers: + case GLChunk::eglPostSubBufferNV: + case GLChunk::eglSwapBuffersWithDamageEXT: + case GLChunk::eglSwapBuffersWithDamageKHR: + return Serialise_Present(ser); + // these functions are not currently serialised - they do nothing on replay and are not // serialised for information (it would be harmless and perhaps useful for the user to see // where and how they're called). @@ -4854,6 +4912,7 @@ ReplayStatus WrappedOpenGL::ContextReplayLog(CaptureState readType, uint32_t sta if((SystemChunk)chunktype == SystemChunk::CaptureEnd) break; + m_LastChunk = chunktype; m_CurEventID++; } diff --git a/renderdoc/driver/gl/gl_driver.h b/renderdoc/driver/gl/gl_driver.h index 70d901c7f..9b82d052a 100644 --- a/renderdoc/driver/gl/gl_driver.h +++ b/renderdoc/driver/gl/gl_driver.h @@ -119,7 +119,7 @@ private: // internals CaptureState m_State; - bool m_AppControlledCapture; + bool m_AppControlledCapture = false; bool m_MarkedActive = false; @@ -182,7 +182,7 @@ private: void AddResourceCurChunk(ResourceId id); void AddResourceInitChunk(GLResource res); - uint32_t m_FrameCounter; + uint32_t m_FrameCounter = 0; uint32_t m_NoCtxFrames; uint32_t m_FailedFrame; CaptureFailReason m_FailedReason; @@ -240,6 +240,7 @@ private: uint32_t m_CurEventID, m_CurDrawcallID; uint32_t m_FirstEventID; uint32_t m_LastEventID; + GLChunk m_LastChunk; ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; @@ -288,6 +289,9 @@ private: void AddDrawcall(const DrawcallDescription &d, bool hasEvents); void AddEvent(); + template + bool Serialise_Present(SerialiserType &ser); + template bool Serialise_CaptureScope(SerialiserType &ser); diff --git a/renderdoc/driver/gl/gl_stringise.cpp b/renderdoc/driver/gl/gl_stringise.cpp index e7a431e93..34db8f19c 100644 --- a/renderdoc/driver/gl/gl_stringise.cpp +++ b/renderdoc/driver/gl/gl_stringise.cpp @@ -45,6 +45,15 @@ rdcstr DoStringise(const GLChunk &el) STRINGISE_ENUM_CLASS_NAMED(CoherentMapWrite, "Internal: Coherent Mapped Memory Write"); + STRINGISE_ENUM_CLASS(SwapBuffers); + STRINGISE_ENUM_CLASS(wglSwapBuffers); + STRINGISE_ENUM_CLASS(glXSwapBuffers); + STRINGISE_ENUM_CLASS(CGLFlushDrawable); + STRINGISE_ENUM_CLASS(eglSwapBuffers); + STRINGISE_ENUM_CLASS(eglPostSubBufferNV); + STRINGISE_ENUM_CLASS(eglSwapBuffersWithDamageEXT); + STRINGISE_ENUM_CLASS(eglSwapBuffersWithDamageKHR); + // re-use list of GL functions as chunks. Many of these will be aliased. This may not appear in the // same order as the definition, but that's OK. #define StringiseFunction(function, alias) STRINGISE_ENUM_CLASS_NAMED(alias, STRINGIZE(alias)); diff --git a/renderdoc/driver/gl/glx_hooks.cpp b/renderdoc/driver/gl/glx_hooks.cpp index 73b0c0146..d041ef32d 100644 --- a/renderdoc/driver/gl/glx_hooks.cpp +++ b/renderdoc/driver/gl/glx_hooks.cpp @@ -444,6 +444,8 @@ HOOK_EXPORT void glXSwapBuffers_renderdoc_hooked(Display *dpy, GLXDrawable drawa glxhook.UpdateWindowSize(data, dpy, drawable); } + gl_CurChunk = GLChunk::glXSwapBuffers; + glxhook.driver.SwapBuffers(WindowingSystem::Xlib, (void *)drawable); GLX.glXSwapBuffers(dpy, drawable); diff --git a/renderdoc/driver/gl/wgl_hooks.cpp b/renderdoc/driver/gl/wgl_hooks.cpp index 08df221bb..56d0c2a50 100644 --- a/renderdoc/driver/gl/wgl_hooks.cpp +++ b/renderdoc/driver/gl/wgl_hooks.cpp @@ -52,7 +52,7 @@ public: std::set contexts; void RefreshWindowParameters(const GLWindowingData &data); - void ProcessSwapBuffers(HDC dc); + void ProcessSwapBuffers(GLChunk src, HDC dc); void PopulateFromContext(HDC dc, HGLRC rc); GLInitParams GetInitParamsForDC(HDC dc); } wglhook; @@ -161,7 +161,7 @@ void WGLHook::RefreshWindowParameters(const GLWindowingData &data) } } -void WGLHook::ProcessSwapBuffers(HDC dc) +void WGLHook::ProcessSwapBuffers(GLChunk src, HDC dc) { if(eglDisabled) return; @@ -179,6 +179,8 @@ void WGLHook::ProcessSwapBuffers(HDC dc) RefreshWindowParameters(data); + gl_CurChunk = src; + { SCOPED_LOCK(glLock); driver.SwapBuffers(WindowingSystem::Win32, w); @@ -448,7 +450,7 @@ static BOOL WINAPI SwapBuffers_hooked(HDC dc) { SCOPED_LOCK(glLock); - wglhook.ProcessSwapBuffers(dc); + wglhook.ProcessSwapBuffers(GLChunk::SwapBuffers, dc); wglhook.swapRecurse = true; BOOL ret = WGL.SwapBuffers(dc); @@ -461,7 +463,7 @@ static BOOL WINAPI wglSwapBuffers_hooked(HDC dc) { SCOPED_LOCK(glLock); - wglhook.ProcessSwapBuffers(dc); + wglhook.ProcessSwapBuffers(GLChunk::wglSwapBuffers, dc); wglhook.swapRecurse = true; BOOL ret = WGL.wglSwapBuffers(dc); @@ -474,7 +476,7 @@ static BOOL WINAPI wglSwapLayerBuffers_hooked(HDC dc, UINT planes) { SCOPED_LOCK(glLock); - wglhook.ProcessSwapBuffers(dc); + wglhook.ProcessSwapBuffers(GLChunk::wglSwapBuffers, dc); wglhook.swapRecurse = true; BOOL ret = WGL.wglSwapLayerBuffers(dc, planes); @@ -488,7 +490,7 @@ static BOOL WINAPI wglSwapMultipleBuffers_hooked(UINT numSwaps, CONST WGLSWAP *p SCOPED_LOCK(glLock); for(UINT i = 0; pSwaps && i < numSwaps; i++) - wglhook.ProcessSwapBuffers(pSwaps[i].hdc); + wglhook.ProcessSwapBuffers(GLChunk::wglSwapBuffers, pSwaps[i].hdc); wglhook.swapRecurse = true; BOOL ret = WGL.wglSwapMultipleBuffers(numSwaps, pSwaps); diff --git a/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp b/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp index 4833f794b..5e5ba605b 100644 --- a/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp +++ b/renderdoc/driver/gl/wrappers/gl_debug_funcs.cpp @@ -293,6 +293,11 @@ void WrappedOpenGL::HandleVRFrameMarkers(const GLchar *buf, GLsizei length) { if(strstr(buf, "vr-marker,frame_end,type,application") != NULL) { + PUSH_CURRENT_CHUNK; + + // don't serialise this present as a separate chunk + gl_CurChunk = GLChunk::Max; + SwapBuffers(WindowingSystem::Headless, (void *)m_ActiveContexts[Threading::GetCurrentID()].wnd); m_UsesVRMarkers = true; diff --git a/renderdoc/driver/vulkan/vk_common.h b/renderdoc/driver/vulkan/vk_common.h index f80e2c3ee..975eadf93 100644 --- a/renderdoc/driver/vulkan/vk_common.h +++ b/renderdoc/driver/vulkan/vk_common.h @@ -590,6 +590,7 @@ enum class VulkanChunk : uint32_t vkGetSemaphoreCounterValueKHR, vkWaitSemaphoresKHR, vkSignalSemaphoreKHR, + vkQueuePresentKHR, Max, }; diff --git a/renderdoc/driver/vulkan/vk_core.cpp b/renderdoc/driver/vulkan/vk_core.cpp index 89225d5fe..59821d7b8 100644 --- a/renderdoc/driver/vulkan/vk_core.cpp +++ b/renderdoc/driver/vulkan/vk_core.cpp @@ -116,10 +116,6 @@ WrappedVulkan::WrappedVulkan() : m_RenderState(this, &m_CreationInfo) m_Replay.SetDriver(this); - m_FrameCounter = 0; - - m_AppControlledCapture = false; - threadSerialiserTLSSlot = Threading::AllocateTLSSlot(); tempMemoryTLSSlot = Threading::AllocateTLSSlot(); debugMessageSinkTLSSlot = Threading::AllocateTLSSlot(); @@ -1334,13 +1330,13 @@ VkResult WrappedVulkan::GetProvidedInstanceExtensionProperties(uint32_t *pProper template bool WrappedVulkan::Serialise_CaptureScope(SerialiserType &ser) { - SERIALISE_ELEMENT(m_FrameCounter); + SERIALISE_ELEMENT_LOCAL(frameNumber, m_CapturedFrames.back().frameNumber); SERIALISE_CHECK_READ_ERRORS(); if(IsReplayingAndReading()) { - m_FrameRecord.frameInfo.frameNumber = m_FrameCounter; + m_FrameRecord.frameInfo.frameNumber = frameNumber; RDCEraseEl(m_FrameRecord.frameInfo.stats); } @@ -1366,6 +1362,7 @@ void WrappedVulkan::FirstFrame() RenderDoc::Inst().StartFrameCapture(LayerDisp(m_Instance), NULL); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = 0; } } @@ -1481,10 +1478,8 @@ void WrappedVulkan::StartFrameCapture(void *dev, void *wnd) m_SubmitCounter = 0; - m_FrameCounter = RDCMAX((uint32_t)m_CapturedFrames.size(), m_FrameCounter); - FrameDescription frame; - frame.frameNumber = m_FrameCounter; + frame.frameNumber = m_AppControlledCapture ? ~0U : m_FrameCounter; frame.captureTime = Timing::GetUnixTimestamp(); RDCEraseEl(frame.stats); m_CapturedFrames.push_back(frame); @@ -1566,7 +1561,7 @@ void WrappedVulkan::StartFrameCapture(void *dev, void *wnd) (*it)->memSize, eFrameRef_ReadBeforeWrite); } - RDCLOG("Starting capture, frame %u", m_FrameCounter); + RDCLOG("Starting capture, frame %u", m_CapturedFrames.back().frameNumber); } bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) @@ -1592,7 +1587,7 @@ bool WrappedVulkan::EndFrameCapture(void *dev, void *wnd) } } - RDCLOG("Finished capture, Frame %u", m_FrameCounter); + RDCLOG("Finished capture, Frame %u", m_CapturedFrames.back().frameNumber); VkImage backbuffer = VK_NULL_HANDLE; VkResourceRecord *swaprecord = NULL; @@ -2029,6 +2024,7 @@ void WrappedVulkan::Present(void *dev, void *wnd) bool activeWindow = wnd == NULL || RenderDoc::Inst().IsActiveWindow(dev, wnd); RenderDoc::Inst().AddActiveDriver(RDCDriver::Vulkan, true); + if(!activeWindow) return; @@ -2040,6 +2036,7 @@ void WrappedVulkan::Present(void *dev, void *wnd) RenderDoc::Inst().StartFrameCapture(dev, wnd); m_AppControlledCapture = false; + m_CapturedFrames.back().frameNumber = m_FrameCounter; } } @@ -2365,6 +2362,8 @@ ReplayStatus WrappedVulkan::ContextReplayLog(CaptureState readType, uint32_t sta if(IsActiveReplaying(m_State) && startEventID == endEventID) break; + m_LastChunk = chunktype; + // increment root event ID either if we didn't just replay a cmd // buffer event, OR if we are doing a frame sub-section replay, // in which case it's up to the calling code to make sure we only @@ -2992,6 +2991,9 @@ bool WrappedVulkan::ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk) std::vector data; return GetResourceManager()->Serialise_ImageRefs(ser, data); } + case VulkanChunk::vkQueuePresentKHR: + return Serialise_vkQueuePresentKHR(ser, VK_NULL_HANDLE, NULL); + default: { SystemChunk system = (SystemChunk)chunk; @@ -3024,15 +3026,18 @@ bool WrappedVulkan::ProcessChunk(ReadSerialiser &ser, VulkanChunk chunk) SERIALISE_CHECK_READ_ERRORS(); - if(IsLoading(m_State)) + if(PresentedImage != ResourceId()) + m_LastPresentedImage = PresentedImage; + + if(IsLoading(m_State) && m_LastChunk != VulkanChunk::vkQueuePresentKHR) { AddEvent(); DrawcallDescription draw; - draw.name = "vkQueuePresentKHR()"; + draw.name = "End of Capture"; draw.flags |= DrawFlags::Present; - draw.copyDestination = PresentedImage; + draw.copyDestination = m_LastPresentedImage; AddDrawcall(draw, true); } diff --git a/renderdoc/driver/vulkan/vk_core.h b/renderdoc/driver/vulkan/vk_core.h index 2fc47509f..52aa61900 100644 --- a/renderdoc/driver/vulkan/vk_core.h +++ b/renderdoc/driver/vulkan/vk_core.h @@ -278,7 +278,7 @@ private: void AddDebugMessage(MessageCategory c, MessageSeverity sv, MessageSource src, std::string d); CaptureState m_State; - bool m_AppControlledCapture; + bool m_AppControlledCapture = false; bool m_MarkedActive = false; uint32_t m_SubmitCounter = 0; @@ -350,7 +350,7 @@ private: std::vector m_SupportedWindowSystems; - uint32_t m_FrameCounter; + uint32_t m_FrameCounter = 0; std::vector m_CapturedFrames; FrameRecord m_FrameRecord; @@ -861,6 +861,9 @@ private: SDChunkMetaData m_ChunkMetadata; uint32_t m_RootEventID, m_RootDrawcallID; uint32_t m_FirstEventID, m_LastEventID; + VulkanChunk m_LastChunk; + + ResourceId m_LastPresentedImage; ReplayStatus m_FailedReplayStatus = ReplayStatus::APIReplayFailed; diff --git a/renderdoc/driver/vulkan/vk_stringise.cpp b/renderdoc/driver/vulkan/vk_stringise.cpp index edeafd551..c7f776adc 100644 --- a/renderdoc/driver/vulkan/vk_stringise.cpp +++ b/renderdoc/driver/vulkan/vk_stringise.cpp @@ -28,7 +28,7 @@ template <> rdcstr DoStringise(const VulkanChunk &el) { - RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1139, "Chunks changed without updating names"); + RDCCOMPILE_ASSERT((uint32_t)VulkanChunk::Max == 1140, "Chunks changed without updating names"); BEGIN_ENUM_STRINGISE(VulkanChunk) { @@ -171,6 +171,7 @@ rdcstr DoStringise(const VulkanChunk &el) STRINGISE_ENUM_CLASS(vkGetSemaphoreCounterValueKHR); STRINGISE_ENUM_CLASS(vkWaitSemaphoresKHR); STRINGISE_ENUM_CLASS(vkSignalSemaphoreKHR); + STRINGISE_ENUM_CLASS(vkQueuePresentKHR); STRINGISE_ENUM_CLASS_NAMED(Max, "Max Chunk"); } END_ENUM_STRINGISE() diff --git a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp index 0b08dafd6..11e5e796b 100644 --- a/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp +++ b/renderdoc/driver/vulkan/wrappers/vk_wsi_funcs.cpp @@ -670,6 +670,49 @@ VkResult WrappedVulkan::vkCreateSwapchainKHR(VkDevice device, return ret; } +template +bool WrappedVulkan::Serialise_vkQueuePresentKHR(SerialiserType &ser, VkQueue queue, + const VkPresentInfoKHR *pPresentInfo) +{ + SERIALISE_ELEMENT(queue); + SERIALISE_ELEMENT_LOCAL(PresentInfo, *pPresentInfo); + + ResourceId PresentedImage; + + if(ser.IsWriting()) + { + VkResourceRecord *swaprecord = GetRecord(pPresentInfo->pSwapchains[0]); + + SwapchainInfo &swapInfo = *swaprecord->swapInfo; + + PresentedImage = GetResID(swapInfo.images[pPresentInfo->pImageIndices[0]].im); + } + + // we don't have all the information we need about swapchains on replay to get the presented image + // just from the PresentInfo struct, so we hide it away here + SERIALISE_ELEMENT(PresentedImage).Hidden(); + + Serialise_DebugMessages(ser); + + SERIALISE_CHECK_READ_ERRORS(); + + if(IsReplayingAndReading()) + { + AddEvent(); + + DrawcallDescription draw; + + draw.name = StringFormat::Fmt("vkQueuePresentKHR(%s)", ToStr(PresentedImage).c_str()); + draw.flags |= DrawFlags::Present; + + m_LastPresentedImage = draw.copyDestination = PresentedImage; + + AddDrawcall(draw, true); + } + + return true; +} + VkResult WrappedVulkan::vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR *pPresentInfo) { AdvanceFrame(); @@ -831,7 +874,18 @@ VkResult WrappedVulkan::vkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR } } - VkResult vkr = ObjDisp(queue)->QueuePresentKHR(Unwrap(queue), &unwrappedInfo); + VkResult vkr; + SERIALISE_TIME_CALL(vkr = ObjDisp(queue)->QueuePresentKHR(Unwrap(queue), &unwrappedInfo)); + + if(IsActiveCapturing(m_State)) + { + CACHE_THREAD_SERIALISER(); + + SCOPED_SERIALISE_CHUNK(VulkanChunk::vkQueuePresentKHR); + Serialise_vkQueuePresentKHR(ser, queue, pPresentInfo); + + m_FrameCaptureRecord->AddChunk(scope.Get()); + } Present(LayerDisp(m_Instance), swapInfo.wndHandle); @@ -1130,3 +1184,6 @@ INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkCreateSwapchainKHR, VkDevice device, INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkGetSwapchainImagesKHR, VkDevice device, VkSwapchainKHR swapchain, uint32_t *pSwapchainImageCount, VkImage *pSwapchainImages); + +INSTANTIATE_FUNCTION_SERIALISED(VkResult, vkQueuePresentKHR, VkQueue queue, + const VkPresentInfoKHR *pPresentInfo); diff --git a/util/test/demos/test_common.h b/util/test/demos/test_common.h index bc97a5442..983d80494 100644 --- a/util/test/demos/test_common.h +++ b/util/test/demos/test_common.h @@ -172,7 +172,7 @@ struct GraphicsTest bool FrameLimit(); - int curFrame = 0; + int curFrame = -1; const char *screenTitle = "RenderDoc test program";