From ec17ee5999f28af5803e27bb4dc16f2aed1933d6 Mon Sep 17 00:00:00 2001 From: baldurk Date: Fri, 23 Feb 2024 15:00:28 +0000 Subject: [PATCH] Batch calls to GetPostVSBuffers to work around Android being awful * On Android the end-to-end latency of a single call is large enough that doing a call per-instance in a draw with a high number of instances is a real problem --- renderdoc/core/replay_proxy.cpp | 23 ++++--- renderdoc/core/replay_proxy.h | 10 ++- renderdoc/driver/d3d11/d3d11_replay.h | 12 +++- renderdoc/driver/d3d12/d3d12_replay.h | 18 ++++- renderdoc/driver/gl/gl_replay.h | 12 +++- renderdoc/driver/vulkan/vk_replay.h | 18 ++++- renderdoc/replay/replay_driver.h | 14 ++++ renderdoc/replay/replay_output.cpp | 95 +++++++++++++-------------- 8 files changed, 131 insertions(+), 71 deletions(-) diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index 9fe24093b..0bab3b523 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -1092,18 +1092,20 @@ void ReplayProxy::InitPostVSBuffers(const rdcarray &events) } template -MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, ReturnSerialiser &retser, - uint32_t eventId, uint32_t instID, uint32_t viewID, - MeshDataStage stage) +rdcarray ReplayProxy::Proxied_GetBatchPostVSBuffers(ParamSerialiser ¶mser, + ReturnSerialiser &retser, + uint32_t eventId, + const rdcarray &instIDs, + uint32_t viewID, MeshDataStage stage) { const ReplayProxyPacket expectedPacket = eReplayProxy_GetPostVS; ReplayProxyPacket packet = eReplayProxy_GetPostVS; - MeshFormat ret = {}; + rdcarray ret = {}; { BEGIN_PARAMS(); SERIALISE_ELEMENT(eventId); - SERIALISE_ELEMENT(instID); + SERIALISE_ELEMENT(instIDs); SERIALISE_ELEMENT(viewID); SERIALISE_ELEMENT(stage); END_PARAMS(); @@ -1112,7 +1114,7 @@ MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, Retu { REMOTE_EXECUTION(); if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) - ret = m_Remote->GetPostVSBuffers(eventId, instID, viewID, stage); + ret = m_Remote->GetBatchPostVSBuffers(eventId, instIDs, viewID, stage); } SERIALISE_RETURN(ret); @@ -1120,10 +1122,11 @@ MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, Retu return ret; } -MeshFormat ReplayProxy::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, - MeshDataStage stage) +rdcarray ReplayProxy::GetBatchPostVSBuffers(uint32_t eventId, + const rdcarray &instIDs, + uint32_t viewID, MeshDataStage stage) { - PROXY_FUNCTION(GetPostVSBuffers, eventId, instID, viewID, stage); + PROXY_FUNCTION(GetBatchPostVSBuffers, eventId, instIDs, viewID, stage); } template @@ -2932,7 +2935,7 @@ bool ReplayProxy::Tick(int type) InitPostVSBuffers(dummy); break; } - case eReplayProxy_GetPostVS: GetPostVSBuffers(0, 0, 0, MeshDataStage::VSIn); break; + case eReplayProxy_GetPostVS: GetBatchPostVSBuffers(0, {}, 0, MeshDataStage::VSIn); break; case eReplayProxy_BuildTargetShader: { rdcstr entry; diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 1f9d23fb0..e942822c7 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -504,8 +504,14 @@ public: IMPLEMENT_FUNCTION_PROXIED(void, InitPostVSBuffers, uint32_t eventId); IMPLEMENT_FUNCTION_PROXIED(void, InitPostVSBuffers, const rdcarray &passEvents); - IMPLEMENT_FUNCTION_PROXIED(MeshFormat, GetPostVSBuffers, uint32_t eventId, uint32_t instID, - uint32_t viewID, MeshDataStage stage); + IMPLEMENT_FUNCTION_PROXIED(rdcarray, GetBatchPostVSBuffers, uint32_t eventId, + const rdcarray &instIDs, uint32_t viewID, MeshDataStage stage); + + // if this gets called individually, just forward to the batch implementation for simplicity + MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, MeshDataStage stage) + { + return GetBatchPostVSBuffers(eventId, {instID}, viewID, stage)[0]; + } IMPLEMENT_FUNCTION_PROXIED(ResourceId, RenderOverlay, ResourceId texid, FloatVector clearCol, DebugOverlay overlay, uint32_t eventId, diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index 2bd8a829c..a27805444 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -82,8 +82,16 @@ struct D3D11PostVSData return vsout; else if(type == MeshDataStage::GSOut) return gsout; - else - RDCERR("Unexpected mesh data stage!"); + + if(type == MeshDataStage::Count) + { + if(gsout.buf) + return gsout; + + return vsout; + } + + RDCERR("Unexpected mesh data stage!"); return vsin; } diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 0797e72b7..5a8db51ea 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -366,8 +366,22 @@ private: return ampout; else if(type == MeshDataStage::MeshOut) return meshout; - else - RDCERR("Unexpected mesh data stage!"); + + if(type == MeshDataStage::Count) + { + if(gsout.buf) + return gsout; + + if(vsout.buf) + return vsout; + + if(meshout.buf) + return meshout; + + return vsout; + } + + RDCERR("Unexpected mesh data stage!"); return vsout; } diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index f4b5fd97c..73d5ebe5f 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -79,8 +79,16 @@ struct GLPostVSData return vsout; else if(type == MeshDataStage::GSOut) return gsout; - else - RDCERR("Unexpected mesh data stage!"); + + if(type == MeshDataStage::Count) + { + if(gsout.buf) + return gsout; + + return vsout; + } + + RDCERR("Unexpected mesh data stage!"); return vsin; } diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 841d7e606..a2b93bbb1 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -231,8 +231,22 @@ struct VulkanPostVSData return taskout; else if(type == MeshDataStage::MeshOut) return meshout; - else - RDCERR("Unexpected mesh data stage!"); + + if(type == MeshDataStage::Count) + { + if(gsout.buf != VK_NULL_HANDLE) + return gsout; + + if(vsout.buf != VK_NULL_HANDLE) + return vsout; + + if(meshout.buf != VK_NULL_HANDLE) + return meshout; + + return vsout; + } + + RDCERR("Unexpected mesh data stage!"); return vsout; } diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index 38932eef2..7a0a27c53 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -178,6 +178,20 @@ public: virtual MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, MeshDataStage stage) = 0; + // this is a helper/batch query for the above, that's only necessary because Android is a shit + // platform and its proxying has significant per-call overhead. This is overridden in the proxy, + // but otherwise will call to this default implementation + virtual rdcarray GetBatchPostVSBuffers(uint32_t eventId, + const rdcarray &instIDs, + uint32_t viewID, MeshDataStage stage) + { + rdcarray ret; + ret.reserve(instIDs.size()); + for(uint32_t instID : instIDs) + ret.push_back(GetPostVSBuffers(eventId, instID, viewID, stage)); + return ret; + } + virtual void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData) = 0; virtual void GetTextureData(ResourceId tex, const Subresource &sub, const GetTextureDataParams ¶ms, bytebuf &data) = 0; diff --git a/renderdoc/replay/replay_output.cpp b/renderdoc/replay/replay_output.cpp index f298713c5..1d6a9b491 100644 --- a/renderdoc/replay/replay_output.cpp +++ b/renderdoc/replay/replay_output.cpp @@ -595,23 +595,26 @@ rdcpair ReplayOutput::PickVertex(uint32_t x, uint32_t y) maxInst = RDCMAX(1U, action->numInstances); } - // used for post-VS output, calculate the offset of the element we're using as position, - // relative to 0 - MeshFormat fmt = - m_pDevice->GetPostVSBuffers(action->eventId, m_RenderData.meshDisplay.curInstance, - m_RenderData.meshDisplay.curView, m_RenderData.meshDisplay.type); + rdcarray instIDs; + instIDs.reserve(maxInst - firstInst + 1); + for(uint32_t inst = firstInst; inst < maxInst; inst++) + instIDs.push_back(inst); + instIDs.push_back(m_RenderData.meshDisplay.curInstance); + rdcarray fmts = m_pDevice->GetBatchPostVSBuffers( + action->eventId, instIDs, m_RenderData.meshDisplay.curView, m_RenderData.meshDisplay.type); m_pController->FatalErrorCheck(); - uint64_t elemOffset = cfg.position.vertexByteOffset - fmt.vertexByteOffset; + // used for post-VS output, calculate the offset of the element we're using as position, + // relative to 0 + // the last element in fmts corresponds to curInstance + const uint64_t elemOffset = cfg.position.vertexByteOffset - fmts.back().vertexByteOffset; + fmts.pop_back(); - for(uint32_t inst = firstInst; inst < maxInst; inst++) + uint32_t inst = firstInst; + for(MeshFormat &fmt : fmts) { // find the start of this buffer, and apply the element offset, then pick in that instance - fmt = m_pDevice->GetPostVSBuffers(action->eventId, inst, m_RenderData.meshDisplay.curView, - m_RenderData.meshDisplay.type); - m_pController->FatalErrorCheck(); - if(fmt.vertexResourceId != ResourceId()) cfg.position.vertexByteOffset = fmt.vertexByteOffset + elemOffset; @@ -619,9 +622,9 @@ rdcpair ReplayOutput::PickVertex(uint32_t x, uint32_t y) m_pController->FatalErrorCheck(); if(vert != ~0U) - { return make_rdcpair(vert, inst); - } + + inst++; } return errorReturn; @@ -1072,34 +1075,22 @@ void ReplayOutput::DisplayMesh() if(d) { + rdcarray instIDs; + instIDs.reserve(RDCMAX(1U, d->numInstances)); for(uint32_t inst = 0; inst < RDCMAX(1U, d->numInstances); inst++) - { - // get the 'most final' stage - MeshFormat fmt = m_pDevice->GetPostVSBuffers( - passEvents[i], inst, m_RenderData.meshDisplay.curView, MeshDataStage::GSOut); - m_pController->FatalErrorCheck(); - if(fmt.vertexResourceId == ResourceId()) - { - fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, m_RenderData.meshDisplay.curView, - MeshDataStage::VSOut); - m_pController->FatalErrorCheck(); - } - if(fmt.vertexResourceId == ResourceId()) - { - fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, m_RenderData.meshDisplay.curView, - MeshDataStage::MeshOut); - m_pController->FatalErrorCheck(); - } + instIDs.push_back(inst); - fmt.meshColor = passDraws; - - // if unproject is marked, this output had a 'real' system position output - if(fmt.unproject) - secondaryDraws.push_back(fmt); - } + // pass MeshDataStage::Count to get the 'most final' stage + secondaryDraws.append(m_pDevice->GetBatchPostVSBuffers( + passEvents[i], instIDs, m_RenderData.meshDisplay.curView, MeshDataStage::Count)); + m_pController->FatalErrorCheck(); } } + // all secondary draws so far are previous pass elements + for(MeshFormat &fmt : secondaryDraws) + fmt.meshColor = passDraws; + // action previous instances in the current action if(action->flags & ActionFlags::Instanced) { @@ -1109,26 +1100,28 @@ void ReplayOutput::DisplayMesh() if(m_RenderData.meshDisplay.showAllInstances) maxInst = RDCMAX(1U, action->numInstances); - for(uint32_t inst = 0; inst < maxInst; inst++) + if(maxInst > 0) { - // get the 'most final' stage - MeshFormat fmt = m_pDevice->GetPostVSBuffers( - action->eventId, inst, m_RenderData.meshDisplay.curView, MeshDataStage::GSOut); + rdcarray instIDs; + instIDs.reserve(maxInst); + for(uint32_t inst = 0; inst < maxInst; inst++) + instIDs.push_back(inst); + + size_t prevInstancesStart = secondaryDraws.size(); + + // pass MeshDataStage::Count to get the 'most final' stage + secondaryDraws.append(m_pDevice->GetBatchPostVSBuffers( + action->eventId, instIDs, m_RenderData.meshDisplay.curView, MeshDataStage::Count)); m_pController->FatalErrorCheck(); - if(fmt.vertexResourceId == ResourceId()) - { - fmt = m_pDevice->GetPostVSBuffers(action->eventId, inst, m_RenderData.meshDisplay.curView, - MeshDataStage::VSOut); - m_pController->FatalErrorCheck(); - } - fmt.meshColor = otherInstances; - - // if unproject is marked, this output had a 'real' system position output - if(fmt.unproject) - secondaryDraws.push_back(fmt); + // set the colour for other instances + for(size_t i = prevInstancesStart; i < secondaryDraws.size(); i++) + secondaryDraws[i].meshColor = otherInstances; } } + + // only if unproject is marked did this output have a 'real' system position output + secondaryDraws.removeIf([](const MeshFormat &fmt) { return fmt.unproject == false; }); } mesh.position.meshColor = drawItself;