From 4458ab3205cd62482a0743eb06abdf468b2de564 Mon Sep 17 00:00:00 2001 From: baldurk Date: Thu, 21 Nov 2019 13:52:38 +0000 Subject: [PATCH] Remove assumptions that captures are always frames * Previously we had "Frame X" and "Start of Frame" hardcoded in the event browser, and the end of frame was in many cases assumed to be a present call. However with the in-application API this is not necessarily true. * Presents are now serialised separately in all APIs and displayed wherever they happen in the frame, and if there is no present at the end of the frame an "End of Capture" marker is inserted. Similarly API-defined captures are not given a potentially misleading frame number. --- qrenderdoc/Windows/Dialogs/LiveCapture.cpp | 38 +++++++- qrenderdoc/Windows/EventBrowser.cpp | 10 +- .../PipelineState/PipelineStateViewer.cpp | 4 +- qrenderdoc/Windows/TimelineBar.cpp | 7 +- renderdoc/api/replay/data_types.h | 12 ++- renderdoc/core/core.cpp | 14 ++- renderdoc/driver/d3d11/d3d11_context.cpp | 68 ++++++++------ renderdoc/driver/d3d11/d3d11_context.h | 5 +- renderdoc/driver/d3d11/d3d11_device.cpp | 44 +++++---- renderdoc/driver/d3d11/d3d11_device.h | 10 +- renderdoc/driver/d3d12/d3d12_command_queue.h | 2 - renderdoc/driver/d3d12/d3d12_commands.cpp | 13 ++- renderdoc/driver/d3d12/d3d12_commands.h | 3 + renderdoc/driver/d3d12/d3d12_common.h | 1 + renderdoc/driver/d3d12/d3d12_device.cpp | 85 +++++++++++++---- renderdoc/driver/d3d12/d3d12_device.h | 12 ++- renderdoc/driver/d3d12/d3d12_replay.cpp | 9 +- renderdoc/driver/d3d12/d3d12_stringise.cpp | 3 +- renderdoc/driver/d3d8/d3d8_device.cpp | 2 - renderdoc/driver/d3d8/d3d8_device.h | 2 +- renderdoc/driver/d3d9/d3d9_device.cpp | 2 - renderdoc/driver/d3d9/d3d9_device.h | 2 +- renderdoc/driver/gl/cgl_hooks.cpp | 2 + renderdoc/driver/gl/egl_hooks.cpp | 8 ++ renderdoc/driver/gl/gl_common.h | 9 ++ renderdoc/driver/gl/gl_driver.cpp | 91 +++++++++++++++---- renderdoc/driver/gl/gl_driver.h | 8 +- renderdoc/driver/gl/gl_stringise.cpp | 9 ++ renderdoc/driver/gl/glx_hooks.cpp | 2 + renderdoc/driver/gl/wgl_hooks.cpp | 14 +-- .../driver/gl/wrappers/gl_debug_funcs.cpp | 5 + renderdoc/driver/vulkan/vk_common.h | 1 + renderdoc/driver/vulkan/vk_core.cpp | 33 ++++--- renderdoc/driver/vulkan/vk_core.h | 7 +- renderdoc/driver/vulkan/vk_stringise.cpp | 3 +- .../driver/vulkan/wrappers/vk_wsi_funcs.cpp | 59 +++++++++++- util/test/demos/test_common.h | 2 +- 37 files changed, 454 insertions(+), 147 deletions(-) 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";