diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 92084efbe..9c65a9f9f 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -1020,6 +1020,7 @@ BufferViewer::BufferViewer(ICaptureContext &ctx, bool meshview, QWidget *parent) ui->rowOffset->setFont(Formatter::PreferredFont()); ui->instance->setFont(Formatter::PreferredFont()); + ui->viewIndex->setFont(Formatter::PreferredFont()); ui->camSpeed->setFont(Formatter::PreferredFont()); ui->fovGuess->setFont(Formatter::PreferredFont()); ui->aspectGuess->setFont(Formatter::PreferredFont()); @@ -1147,6 +1148,8 @@ void BufferViewer::SetupRawView() ui->syncViews->setVisible(false); ui->instanceLabel->setVisible(false); ui->instance->setVisible(false); + ui->viewLabel->setVisible(false); + ui->viewIndex->setVisible(false); ui->vsinData->setWindowTitle(tr("Buffer Contents")); ui->vsinData->setFrameShape(QFrame::NoFrame); @@ -1460,6 +1463,19 @@ void BufferViewer::OnEventChanged(uint32_t eventId) if(draw) ui->instance->setMaximum(qMax(0, int(draw->numInstances) - 1)); + uint32_t numViews = m_Ctx.CurPipelineState().MultiviewBroadcastCount(); + + if(draw && numViews > 1) + { + ui->viewIndex->setEnabled(true); + ui->viewIndex->setMaximum(qMax(0, int(numViews) - 1)); + } + else + { + ui->viewIndex->setEnabled(false); + ui->viewIndex->setValue(0); + } + if(m_MeshView) { configureMeshColumns(); @@ -1716,7 +1732,7 @@ void BufferViewer::RT_FetchMeshData(IReplayController *r) m_ModelVSIn->buffers.push_back(buf); } - m_PostVS = r->GetPostVSData(m_Config.curInstance, MeshDataStage::VSOut); + m_PostVS = r->GetPostVSData(m_Config.curInstance, m_Config.curView, MeshDataStage::VSOut); m_ModelVSOut->numRows = m_PostVS.numIndices; m_ModelVSOut->unclampedNumRows = 0; @@ -1778,7 +1794,7 @@ void BufferViewer::RT_FetchMeshData(IReplayController *r) m_ModelVSOut->buffers.push_back(postvs); } - m_PostGS = r->GetPostVSData(m_Config.curInstance, MeshDataStage::GSOut); + m_PostGS = r->GetPostVSData(m_Config.curInstance, m_Config.curView, MeshDataStage::GSOut); m_ModelGSOut->numRows = m_PostGS.numIndices; m_ModelGSOut->unclampedNumRows = 0; @@ -3572,6 +3588,12 @@ void BufferViewer::on_instance_valueChanged(int value) OnEventChanged(m_Ctx.CurEvent()); } +void BufferViewer::on_viewIndex_valueChanged(int value) +{ + m_Config.curView = value; + OnEventChanged(m_Ctx.CurEvent()); +} + void BufferViewer::on_rowOffset_valueChanged(int value) { ScrollToRow(m_ModelVSIn, value); diff --git a/qrenderdoc/Windows/BufferViewer.h b/qrenderdoc/Windows/BufferViewer.h index b16673aa5..2568d3da8 100644 --- a/qrenderdoc/Windows/BufferViewer.h +++ b/qrenderdoc/Windows/BufferViewer.h @@ -105,6 +105,7 @@ private slots: void on_controlType_currentIndexChanged(int index); void on_camSpeed_valueChanged(double value); void on_instance_valueChanged(int value); + void on_viewIndex_valueChanged(int value); void on_rowOffset_valueChanged(int value); void on_byteRangeStart_valueChanged(int value); void on_byteRangeLength_valueChanged(int value); diff --git a/qrenderdoc/Windows/BufferViewer.ui b/qrenderdoc/Windows/BufferViewer.ui index ff6535146..473170afa 100644 --- a/qrenderdoc/Windows/BufferViewer.ui +++ b/qrenderdoc/Windows/BufferViewer.ui @@ -787,6 +787,20 @@ + + + + View + + + + + + + 16 + + + diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h index f50f8b87a..66ab936e4 100644 --- a/renderdoc/api/replay/control_types.h +++ b/renderdoc/api/replay/control_types.h @@ -134,6 +134,8 @@ struct MeshDisplay bool showWholePass; DOCUMENT("The index of the currently selected instance in the drawcall."); uint32_t curInstance; + DOCUMENT("The index of the currently selected multiview view in the drawcall."); + uint32_t curView; DOCUMENT("The index of the vertex to highlight, or :data:`NoHighlight` to select no vertex."); uint32_t highlightVert; diff --git a/renderdoc/api/replay/pipestate.h b/renderdoc/api/replay/pipestate.h index 8be3a7a5e..26993938b 100644 --- a/renderdoc/api/replay/pipestate.h +++ b/renderdoc/api/replay/pipestate.h @@ -133,6 +133,9 @@ public: return false; } + DOCUMENT("Returns the number of views being broadcast to simultaneously during rendering."); + uint32_t MultiviewBroadcastCount() const; + DOCUMENT(R"(Determines whether or not the current capture supports binding arrays of resources. :return: A boolean indicating if binding arrays of resources is supported. diff --git a/renderdoc/api/replay/pipestate.inl b/renderdoc/api/replay/pipestate.inl index 28a9e09f8..2104cb708 100644 --- a/renderdoc/api/replay/pipestate.inl +++ b/renderdoc/api/replay/pipestate.inl @@ -382,6 +382,16 @@ ResourceId PipeState::GetGraphicsPipelineObject() const return ResourceId(); } +uint32_t PipeState::MultiviewBroadcastCount() const +{ + if(IsCaptureLoaded() && IsCaptureVK()) + { + return std::max((uint32_t)m_Vulkan->currentPass.renderpass.multiviews.size(), 1U); + } + + return 1; +} + rdcstr PipeState::GetShaderEntryPoint(ShaderStage stage) const { if(IsCaptureLoaded() && IsCaptureVK()) diff --git a/renderdoc/api/replay/renderdoc_replay.h b/renderdoc/api/replay/renderdoc_replay.h index 889b9f350..1fd19cca4 100644 --- a/renderdoc/api/replay/renderdoc_replay.h +++ b/renderdoc/api/replay/renderdoc_replay.h @@ -1124,12 +1124,13 @@ texture to something compatible with the target file format. DOCUMENT(R"(Retrieve the generated data from one of the geometry processing shader stages. -:param int instance: The index of the instance to retrieve data for. +:param int instance: The index of the instance to retrieve data for, or 0 for non-instanced draws. +:param int view: The index of the multiview view to retrieve data for, or 0 if multiview is disabled. :param MeshDataStage stage: The stage of the geometry processing pipeline to retrieve data from. :return: The information describing where the post-transform data is stored. :rtype: MeshFormat )"); - virtual MeshFormat GetPostVSData(uint32_t instance, MeshDataStage stage) = 0; + virtual MeshFormat GetPostVSData(uint32_t instance, uint32_t view, MeshDataStage stage) = 0; DOCUMENT(R"(Retrieve the contents of a range of a buffer as a ``bytes``. diff --git a/renderdoc/api/replay/vk_pipestate.h b/renderdoc/api/replay/vk_pipestate.h index 5de70bcfe..53e52c1e0 100644 --- a/renderdoc/api/replay/vk_pipestate.h +++ b/renderdoc/api/replay/vk_pipestate.h @@ -573,6 +573,13 @@ struct RenderPass If there is no depth-stencil attachment, this index is ``-1``. )"); int32_t depthstencilAttachment = -1; + + DOCUMENT(R"(If multiview is enabled, contains a list of view indices to be broadcast to during +rendering. + +If the list is empty, multiview is disabled and rendering is as normal. +)"); + rdcarray multiviews; }; DOCUMENT("Describes a single attachment in a framebuffer object."); diff --git a/renderdoc/core/image_viewer.cpp b/renderdoc/core/image_viewer.cpp index 603772586..cd6212949 100644 --- a/renderdoc/core/image_viewer.cpp +++ b/renderdoc/core/image_viewer.cpp @@ -196,7 +196,7 @@ public: void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData) {} void InitPostVSBuffers(uint32_t eventId) {} void InitPostVSBuffers(const vector &eventId) {} - MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) + MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, MeshDataStage stage) { MeshFormat ret; RDCEraseEl(ret); diff --git a/renderdoc/core/replay_proxy.cpp b/renderdoc/core/replay_proxy.cpp index e0847f774..5f5425585 100644 --- a/renderdoc/core/replay_proxy.cpp +++ b/renderdoc/core/replay_proxy.cpp @@ -729,9 +729,9 @@ void ReplayProxy::InitPostVSBuffers(const std::vector &events) } template -MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, - ReturnSerialiser &retser, uint32_t eventId, - uint32_t instID, MeshDataStage stage) +MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, ReturnSerialiser &retser, + uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) { const ReplayProxyPacket packet = eReplayProxy_GetPostVS; MeshFormat ret = {}; @@ -740,21 +740,23 @@ MeshFormat ReplayProxy::Proxied_GetPostVSBuffers(ParamSerialiser ¶mser, BEGIN_PARAMS(); SERIALISE_ELEMENT(eventId); SERIALISE_ELEMENT(instID); + SERIALISE_ELEMENT(viewID); SERIALISE_ELEMENT(stage); END_PARAMS(); } if(paramser.IsReading() && !paramser.IsErrored() && !m_IsErrored) - ret = m_Remote->GetPostVSBuffers(eventId, instID, stage); + ret = m_Remote->GetPostVSBuffers(eventId, instID, viewID, stage); SERIALISE_RETURN(ret); return ret; } -MeshFormat ReplayProxy::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) +MeshFormat ReplayProxy::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) { - PROXY_FUNCTION(GetPostVSBuffers, eventId, instID, stage); + PROXY_FUNCTION(GetPostVSBuffers, eventId, instID, viewID, stage); } template @@ -2058,7 +2060,7 @@ bool ReplayProxy::Tick(int type) InitPostVSBuffers(dummy); break; } - case eReplayProxy_GetPostVS: GetPostVSBuffers(0, 0, MeshDataStage::Unknown); break; + case eReplayProxy_GetPostVS: GetPostVSBuffers(0, 0, 0, MeshDataStage::Unknown); break; case eReplayProxy_BuildTargetShader: BuildTargetShader("", "", ShaderCompileFlags(), ShaderStage::Vertex, NULL, NULL); break; diff --git a/renderdoc/core/replay_proxy.h b/renderdoc/core/replay_proxy.h index 123053605..4b9222fd1 100644 --- a/renderdoc/core/replay_proxy.h +++ b/renderdoc/core/replay_proxy.h @@ -466,7 +466,7 @@ public: IMPLEMENT_FUNCTION_PROXIED(void, InitPostVSBuffers, uint32_t eventId); IMPLEMENT_FUNCTION_PROXIED(void, InitPostVSBuffers, const std::vector &passEvents); IMPLEMENT_FUNCTION_PROXIED(MeshFormat, GetPostVSBuffers, uint32_t eventId, uint32_t instID, - MeshDataStage stage); + uint32_t viewID, MeshDataStage stage); IMPLEMENT_FUNCTION_PROXIED(ResourceId, RenderOverlay, ResourceId texid, CompType typeHint, DebugOverlay overlay, uint32_t eventId, diff --git a/renderdoc/driver/d3d11/d3d11_overlay.cpp b/renderdoc/driver/d3d11/d3d11_overlay.cpp index ddb035e25..26321a7dc 100644 --- a/renderdoc/driver/d3d11/d3d11_overlay.cpp +++ b/renderdoc/driver/d3d11/d3d11_overlay.cpp @@ -681,9 +681,9 @@ ResourceId D3D11Replay::RenderOverlay(ResourceId texid, CompType typeHint, Debug for(uint32_t inst = 0; draw && inst < RDCMAX(1U, draw->numInstances); inst++) { - MeshFormat fmt = GetPostVSBuffers(events[i], inst, MeshDataStage::GSOut); + MeshFormat fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::GSOut); if(fmt.vertexResourceId == ResourceId()) - fmt = GetPostVSBuffers(events[i], inst, MeshDataStage::VSOut); + fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::VSOut); if(fmt.vertexResourceId != ResourceId()) { diff --git a/renderdoc/driver/d3d11/d3d11_postvs.cpp b/renderdoc/driver/d3d11/d3d11_postvs.cpp index ad18bec0e..bd618ccb6 100644 --- a/renderdoc/driver/d3d11/d3d11_postvs.cpp +++ b/renderdoc/driver/d3d11/d3d11_postvs.cpp @@ -97,11 +97,15 @@ void D3D11Replay::ClearPostVSCache() m_PostVSData.clear(); } -MeshFormat D3D11Replay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) +MeshFormat D3D11Replay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) { D3D11PostVSData postvs; RDCEraseEl(postvs); + // no multiview support + (void)viewID; + if(m_PostVSData.find(eventId) != m_PostVSData.end()) postvs = m_PostVSData[eventId]; diff --git a/renderdoc/driver/d3d11/d3d11_replay.h b/renderdoc/driver/d3d11/d3d11_replay.h index db7e66e16..27a329f9f 100644 --- a/renderdoc/driver/d3d11/d3d11_replay.h +++ b/renderdoc/driver/d3d11/d3d11_replay.h @@ -169,7 +169,8 @@ public: CompType typeHint, float minval, float maxval, bool channels[4], vector &histogram); - MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage); + MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage); void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData); void GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/driver/d3d12/d3d12_overlay.cpp b/renderdoc/driver/d3d12/d3d12_overlay.cpp index 939c2e583..362d5f07c 100644 --- a/renderdoc/driver/d3d12/d3d12_overlay.cpp +++ b/renderdoc/driver/d3d12/d3d12_overlay.cpp @@ -906,9 +906,9 @@ ResourceId D3D12Replay::RenderOverlay(ResourceId texid, CompType typeHint, Debug for(uint32_t inst = 0; draw && inst < RDCMAX(1U, draw->numInstances); inst++) { - MeshFormat fmt = GetPostVSBuffers(events[i], inst, MeshDataStage::GSOut); + MeshFormat fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::GSOut); if(fmt.vertexResourceId == ResourceId()) - fmt = GetPostVSBuffers(events[i], inst, MeshDataStage::VSOut); + fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::VSOut); if(fmt.vertexResourceId != ResourceId()) { diff --git a/renderdoc/driver/d3d12/d3d12_postvs.cpp b/renderdoc/driver/d3d12/d3d12_postvs.cpp index c26c508de..9a99f9de4 100644 --- a/renderdoc/driver/d3d12/d3d12_postvs.cpp +++ b/renderdoc/driver/d3d12/d3d12_postvs.cpp @@ -1338,7 +1338,8 @@ void D3D12Replay::InitPostVSBuffers(const vector &events) m_pDevice->ReplayLog(events.front(), events.back(), eReplay_Full); } -MeshFormat D3D12Replay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) +MeshFormat D3D12Replay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) { // go through any aliasing if(m_PostVSAlias.find(eventId) != m_PostVSAlias.end()) @@ -1347,6 +1348,9 @@ MeshFormat D3D12Replay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, Mesh D3D12PostVSData postvs; RDCEraseEl(postvs); + // no multiview support + (void)viewID; + if(m_PostVSData.find(eventId) != m_PostVSData.end()) postvs = m_PostVSData[eventId]; diff --git a/renderdoc/driver/d3d12/d3d12_replay.h b/renderdoc/driver/d3d12/d3d12_replay.h index 3eb4113a6..210ba2791 100644 --- a/renderdoc/driver/d3d12/d3d12_replay.h +++ b/renderdoc/driver/d3d12/d3d12_replay.h @@ -129,7 +129,8 @@ public: CompType typeHint, float minval, float maxval, bool channels[4], vector &histogram); - MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage); + MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage); void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData); void GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/driver/gl/gl_overlay.cpp b/renderdoc/driver/gl/gl_overlay.cpp index 1a8f7f09d..e23e38ec0 100644 --- a/renderdoc/driver/gl/gl_overlay.cpp +++ b/renderdoc/driver/gl/gl_overlay.cpp @@ -975,9 +975,9 @@ ResourceId GLReplay::RenderOverlay(ResourceId texid, CompType typeHint, DebugOve for(uint32_t inst = 0; draw && inst < RDCMAX(1U, draw->numInstances); inst++) { - MeshFormat postvs = GetPostVSBuffers(events[i], inst, MeshDataStage::GSOut); + MeshFormat postvs = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::GSOut); if(postvs.vertexResourceId == ResourceId()) - postvs = GetPostVSBuffers(events[i], inst, MeshDataStage::VSOut); + postvs = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::VSOut); if(postvs.vertexResourceId != ResourceId()) { diff --git a/renderdoc/driver/gl/gl_postvs.cpp b/renderdoc/driver/gl/gl_postvs.cpp index 0cfdd52f8..a443dca6c 100644 --- a/renderdoc/driver/gl/gl_postvs.cpp +++ b/renderdoc/driver/gl/gl_postvs.cpp @@ -1420,11 +1420,15 @@ void GLReplay::InitPostVSBuffers(const vector &passEvents) } } -MeshFormat GLReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) +MeshFormat GLReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) { GLPostVSData postvs; RDCEraseEl(postvs); + // no multiview support + (void)viewID; + ContextPair ctx = {m_ReplayCtx.ctx, m_pDriver->ShareCtx(m_ReplayCtx.ctx)}; if(m_PostVSData.find(eventId) != m_PostVSData.end()) diff --git a/renderdoc/driver/gl/gl_replay.h b/renderdoc/driver/gl/gl_replay.h index f124dd48e..6a373e053 100644 --- a/renderdoc/driver/gl/gl_replay.h +++ b/renderdoc/driver/gl/gl_replay.h @@ -162,7 +162,8 @@ public: CompType typeHint, float minval, float maxval, bool channels[4], vector &histogram); - MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage); + MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage); void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &ret); void GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/driver/vulkan/vk_info.cpp b/renderdoc/driver/vulkan/vk_info.cpp index 4e89b9fa4..0551b85d2 100644 --- a/renderdoc/driver/vulkan/vk_info.cpp +++ b/renderdoc/driver/vulkan/vk_info.cpp @@ -510,6 +510,11 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, for(uint32_t i = 0; i < pCreateInfo->attachmentCount; i++) attachments.push_back(pCreateInfo->pAttachments[i]); + // VK_KHR_multiview + const VkRenderPassMultiviewCreateInfo *multiview = + (const VkRenderPassMultiviewCreateInfo *)FindNextStruct( + pCreateInfo, VK_STRUCTURE_TYPE_RENDER_PASS_MULTIVIEW_CREATE_INFO); + subpasses.resize(pCreateInfo->subpassCount); for(uint32_t subp = 0; subp < pCreateInfo->subpassCount; subp++) { @@ -544,6 +549,16 @@ void VulkanCreationInfo::RenderPass::Init(VulkanResourceManager *resourceMan, src.pDepthStencilAttachment->attachment != VK_ATTACHMENT_UNUSED ? src.pDepthStencilAttachment->layout : VK_IMAGE_LAYOUT_UNDEFINED); + + if(multiview && multiview->subpassCount > 0) + { + uint32_t mask = multiview->pViewMasks[subp]; + for(uint32_t i = 0; i < 32; i++) + { + if(mask & (1 << i)) + dst.multiviews.push_back(i); + } + } } } diff --git a/renderdoc/driver/vulkan/vk_info.h b/renderdoc/driver/vulkan/vk_info.h index b0a28988b..827e94e29 100644 --- a/renderdoc/driver/vulkan/vk_info.h +++ b/renderdoc/driver/vulkan/vk_info.h @@ -274,6 +274,8 @@ struct VulkanCreationInfo vector inputLayouts; vector colorLayouts; VkImageLayout depthstencilLayout; + + std::vector multiviews; }; vector subpasses; diff --git a/renderdoc/driver/vulkan/vk_overlay.cpp b/renderdoc/driver/vulkan/vk_overlay.cpp index a666e4957..9b355d06a 100644 --- a/renderdoc/driver/vulkan/vk_overlay.cpp +++ b/renderdoc/driver/vulkan/vk_overlay.cpp @@ -2095,9 +2095,9 @@ ResourceId VulkanReplay::RenderOverlay(ResourceId texid, CompType typeHint, Debu for(uint32_t inst = 0; draw && inst < RDCMAX(1U, draw->numInstances); inst++) { - MeshFormat fmt = GetPostVSBuffers(events[i], inst, MeshDataStage::GSOut); + MeshFormat fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::GSOut); if(fmt.vertexResourceId == ResourceId()) - fmt = GetPostVSBuffers(events[i], inst, MeshDataStage::VSOut); + fmt = GetPostVSBuffers(events[i], inst, 0, MeshDataStage::VSOut); if(fmt.vertexResourceId != ResourceId()) { diff --git a/renderdoc/driver/vulkan/vk_postvs.cpp b/renderdoc/driver/vulkan/vk_postvs.cpp index 369f3640f..2f7452fa4 100644 --- a/renderdoc/driver/vulkan/vk_postvs.cpp +++ b/renderdoc/driver/vulkan/vk_postvs.cpp @@ -35,10 +35,11 @@ static const char *PatchedMeshOutputEntryPoint = "rdc"; static const uint32_t MeshOutputDispatchWidth = 128; static const uint32_t MeshOutputTBufferArraySize = 16; -static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRVPatchData &patchData, - const char *entryName, std::vector instDivisor, - uint32_t &descSet, const DrawcallDescription *draw, - int32_t indexOffset, uint64_t numFetchVerts, uint32_t numVerts, +static void ConvertToMeshOutputCompute(const ShaderReflection &refl, + const SPIRVPatchData &patchData, const char *entryName, + std::vector instDivisor, uint32_t &descSet, + const DrawcallDescription *draw, int32_t indexOffset, + uint64_t numFetchVerts, uint32_t numVerts, uint32_t numViews, std::vector &modSpirv, uint32_t &bufStride) { SPIRVEditor editor(modSpirv); @@ -550,10 +551,12 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV SPIRVId numFetchVertsConstID = editor.AddConstantImmediate((int32_t)numFetchVerts); SPIRVId numVertsConstID = editor.AddConstantImmediate((int32_t)numVerts); SPIRVId numInstConstID = editor.AddConstantImmediate((int32_t)draw->numInstances); + SPIRVId numViewsConstID = editor.AddConstantImmediate((int32_t)numViews); editor.SetName(numFetchVertsConstID, "numFetchVerts"); editor.SetName(numVertsConstID, "numVerts"); editor.SetName(numInstConstID, "numInsts"); + editor.SetName(numViewsConstID, "numViews"); // declare the output buffer and its type { @@ -716,17 +719,27 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV editor.SetName(intInvocationID, "invocation"); - // int inst = intInvocationID / numFetchVerts + // int viewinst = intInvocationID / numFetchVerts + uint32_t viewinstID = editor.MakeId(); + ops.push_back(SPIRVOperation(spv::OpSDiv, + {sint32ID, viewinstID, intInvocationID, numFetchVertsConstID})); + + editor.SetName(viewinstID, "viewInstance"); + uint32_t instID = editor.MakeId(); - ops.push_back( - SPIRVOperation(spv::OpSDiv, {sint32ID, instID, intInvocationID, numFetchVertsConstID})); + ops.push_back(SPIRVOperation(spv::OpSMod, {sint32ID, instID, viewinstID, numInstConstID})); editor.SetName(instID, "instanceID"); - // bool inBounds = inst < numInstances; + uint32_t viewID = editor.MakeId(); + ops.push_back(SPIRVOperation(spv::OpSDiv, {sint32ID, viewID, viewinstID, numInstConstID})); + + editor.SetName(viewID, "viewID"); + + // bool inBounds = viewID < numViews; uint32_t inBounds = editor.MakeId(); - ops.push_back(SPIRVOperation( - spv::OpULessThan, {editor.DeclareType(scalar()), inBounds, instID, numInstConstID})); + ops.push_back(SPIRVOperation(spv::OpULessThan, {editor.DeclareType(scalar()), inBounds, + viewID, numViewsConstID})); // if(inBounds) goto continueLabel; else goto killLabel; uint32_t killLabel = editor.MakeId(); @@ -769,9 +782,10 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV ops.push_back(SPIRVOperation(spv::OpBitcast, {sint32ID, vertexIndex, uintIndex})); } - // int arraySlotID = inst * numVerts; + // int arraySlotID = viewinst * numVerts; uint32_t arraySlotTempID = editor.MakeId(); - ops.push_back(SPIRVOperation(spv::OpIMul, {sint32ID, arraySlotTempID, instID, numVertsConstID})); + ops.push_back( + SPIRVOperation(spv::OpIMul, {sint32ID, arraySlotTempID, viewinstID, numVertsConstID})); // arraySlotID = arraySlotID + vertexIndex; uint32_t arraySlotTemp2ID = editor.MakeId(); @@ -826,8 +840,7 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV } else if(builtin == ShaderBuiltin::ViewportIndex) { - ops.push_back(SPIRVOperation( - spv::OpStore, {ins[i].variableID, editor.AddConstantImmediate(int32_t(0))})); + ops.push_back(SPIRVOperation(spv::OpStore, {ins[i].variableID, viewID})); } else if(builtin == ShaderBuiltin::BaseVertex) { @@ -1150,6 +1163,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) m_PostVSData[eventId].vsout.buf = VK_NULL_HANDLE; m_PostVSData[eventId].vsout.instStride = 0; m_PostVSData[eventId].vsout.vertStride = 0; + m_PostVSData[eventId].vsout.numViews = 1; m_PostVSData[eventId].vsout.nearPlane = 0.0f; m_PostVSData[eventId].vsout.farPlane = 0.0f; m_PostVSData[eventId].vsout.useIndices = false; @@ -1198,6 +1212,21 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) uint64_t numFetchVerts = drawcall->numIndices; VkDeviceSize bufSize = 0; + uint32_t numViews = 1; + + { + const VulkanCreationInfo::RenderPass &rp = creationInfo.m_RenderPass[state.renderPass]; + + if(state.subpass < rp.subpasses.size()) + { + numViews = RDCMAX(numViews, (uint32_t)rp.subpasses[state.subpass].multiviews.size()); + } + else + { + RDCERR("Subpass is out of bounds to renderpass creation info"); + } + } + uint32_t idxsize = state.ibuffer.bytewidth; int32_t baseVertex = 0; @@ -1703,9 +1732,9 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) m_pDriver->vkUpdateDescriptorSets(dev, numWrites, descWrites, 0, NULL); } - ConvertToMeshOutputCompute(*refl, *pipeInfo.shaders[0].patchData, - pipeInfo.shaders[0].entryPoint.c_str(), attrInstDivisor, descSet, - drawcall, baseVertex, numFetchVerts, numVerts, modSpirv, bufStride); + ConvertToMeshOutputCompute( + *refl, *pipeInfo.shaders[0].patchData, pipeInfo.shaders[0].entryPoint.c_str(), attrInstDivisor, + descSet, drawcall, baseVertex, numFetchVerts, numVerts, numViews, modSpirv, bufStride); VkComputePipelineCreateInfo compPipeInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO}; @@ -1790,10 +1819,11 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) // have a compact 0-based index to index into the buffer. We must use // index-minIndex which is 0-based but potentially sparse, so this buffer may // be more or less wasteful - VkBufferCreateInfo bufInfo = { - VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, NULL, 0, - numVerts * drawcall->numInstances * bufStride, 0, - }; + VkBufferCreateInfo bufInfo = {VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO}; + + // set bufSize + bufSize = bufInfo.size = uint64_t(numVerts) * uint64_t(drawcall->numInstances) * + uint64_t(bufStride) * uint64_t(numViews); bufInfo.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; bufInfo.usage |= VK_BUFFER_USAGE_TRANSFER_DST_BIT; @@ -1864,9 +1894,6 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) // finish. DoPipelineBarrier(cmd, 1, &globalbarrier); - // set bufSize - bufSize = numVerts * drawcall->numInstances * bufStride; - // vkUpdateDescriptorSet desc set to point to buffer VkDescriptorBufferInfo fetchdesc = {0}; fetchdesc.buffer = meshBuffer; @@ -1880,7 +1907,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) // do single draw modifiedstate.BindPipeline(cmd, VulkanRenderState::BindCompute, true); - uint64_t totalVerts = numFetchVerts * uint64_t(drawcall->numInstances); + uint64_t totalVerts = numFetchVerts * uint64_t(drawcall->numInstances) * uint64_t(numViews); // the validation layers will probably complain about this dispatch saying some arrays aren't // fully updated. That's because they don't statically analyse that only fixed indices are @@ -2017,6 +2044,8 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) m_PostVSData[eventId].vsout.baseVertex = baseVertex + drawcall->baseVertex; + m_PostVSData[eventId].vsout.numViews = numViews; + m_PostVSData[eventId].vsout.vertStride = bufStride; m_PostVSData[eventId].vsout.nearPlane = nearp; m_PostVSData[eventId].vsout.farPlane = farp; @@ -2026,7 +2055,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId) m_PostVSData[eventId].vsout.instStride = 0; if(drawcall->flags & DrawFlags::Instanced) - m_PostVSData[eventId].vsout.instStride = uint32_t(bufSize / drawcall->numInstances); + m_PostVSData[eventId].vsout.instStride = uint32_t(bufSize / (drawcall->numInstances * numViews)); m_PostVSData[eventId].vsout.idxBuf = ResourceId(); if(m_PostVSData[eventId].vsout.useIndices && state.ibuffer.buf != ResourceId()) @@ -2098,7 +2127,8 @@ void VulkanReplay::InitPostVSBuffers(const vector &events) m_pDriver->ReplayLog(events.front(), events.back(), eReplay_Full); } -MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) +MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) { // go through any aliasing if(m_PostVSAlias.find(eventId) != m_PostVSAlias.end()) @@ -2110,8 +2140,20 @@ MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, Mes if(m_PostVSData.find(eventId) != m_PostVSData.end()) postvs = m_PostVSData[eventId]; + const DrawcallDescription *drawcall = m_pDriver->GetDrawcall(eventId); + + uint32_t numInstances = 1; + if(drawcall && (drawcall->flags & DrawFlags::Instanced)) + numInstances = drawcall->numInstances; + VulkanPostVSData::StageData s = postvs.GetStage(stage); + // clamp viewID + if(s.numViews > 1) + viewID = RDCMIN(viewID, s.numViews - 1); + else + viewID = 0; + MeshFormat ret; if(s.useIndices && s.idxBuf != ResourceId()) @@ -2132,7 +2174,7 @@ MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, Mes else ret.vertexResourceId = ResourceId(); - ret.vertexByteOffset = s.instStride * instID; + ret.vertexByteOffset = s.instStride * (instID + viewID * numInstances); ret.vertexByteStride = s.vertStride; ret.format.compCount = 4; diff --git a/renderdoc/driver/vulkan/vk_replay.cpp b/renderdoc/driver/vulkan/vk_replay.cpp index 15c10e729..46e5aaff3 100644 --- a/renderdoc/driver/vulkan/vk_replay.cpp +++ b/renderdoc/driver/vulkan/vk_replay.cpp @@ -1124,6 +1124,9 @@ void VulkanReplay::SavePipelineState() c.m_RenderPass[state.renderPass].subpasses[state.subpass].resolveAttachments; m_VulkanPipelineState.currentPass.renderpass.depthstencilAttachment = c.m_RenderPass[state.renderPass].subpasses[state.subpass].depthstencilAttachment; + + m_VulkanPipelineState.currentPass.renderpass.multiviews = + c.m_RenderPass[state.renderPass].subpasses[state.subpass].multiviews; } m_VulkanPipelineState.currentPass.framebuffer.resourceId = rm->GetOriginalID(state.framebuffer); diff --git a/renderdoc/driver/vulkan/vk_replay.h b/renderdoc/driver/vulkan/vk_replay.h index 603f2cc29..ba1839c48 100644 --- a/renderdoc/driver/vulkan/vk_replay.h +++ b/renderdoc/driver/vulkan/vk_replay.h @@ -140,6 +140,8 @@ struct VulkanPostVSData uint32_t vertStride; uint32_t instStride; + uint32_t numViews; + bool useIndices; ResourceId idxBuf; VkDeviceSize idxOffset; @@ -251,7 +253,8 @@ public: void AliasPostVSBuffers(uint32_t eventId, uint32_t alias) { m_PostVSAlias[alias] = eventId; } void ClearPostVSCache(); - MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage); + MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage); void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData); void GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 6c91190ae..c77fe00be 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -1969,8 +1969,9 @@ void DoSerialise(SerialiserType &ser, VKPipe::RenderPass &el) SERIALISE_MEMBER(colorAttachments); SERIALISE_MEMBER(resolveAttachments); SERIALISE_MEMBER(depthstencilAttachment); + SERIALISE_MEMBER(multiviews); - SIZE_CHECK(72); + SIZE_CHECK(88); } template @@ -2020,7 +2021,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::CurrentPass &el) SERIALISE_MEMBER(framebuffer); SERIALISE_MEMBER(renderArea); - SIZE_CHECK(128); + SIZE_CHECK(144); } template @@ -2073,7 +2074,7 @@ void DoSerialise(SerialiserType &ser, VKPipe::State &el) SERIALISE_MEMBER(images); - SIZE_CHECK(1328); + SIZE_CHECK(1344); } #pragma endregion Vulkan pipeline state diff --git a/renderdoc/replay/replay_controller.cpp b/renderdoc/replay/replay_controller.cpp index fa514f35d..e68d58ea4 100644 --- a/renderdoc/replay/replay_controller.cpp +++ b/renderdoc/replay/replay_controller.cpp @@ -557,7 +557,7 @@ rdcarray ReplayController::GetUsage(ResourceId id) return m_pDevice->GetUsage(id); } -MeshFormat ReplayController::GetPostVSData(uint32_t instID, MeshDataStage stage) +MeshFormat ReplayController::GetPostVSData(uint32_t instID, uint32_t viewID, MeshDataStage stage) { DrawcallDescription *draw = GetDrawcallByEID(m_EventID); @@ -571,7 +571,7 @@ MeshFormat ReplayController::GetPostVSData(uint32_t instID, MeshDataStage stage) m_pDevice->InitPostVSBuffers(draw->eventId); - return m_pDevice->GetPostVSBuffers(draw->eventId, instID, stage); + return m_pDevice->GetPostVSBuffers(draw->eventId, instID, viewID, stage); } bytebuf ReplayController::GetBufferData(ResourceId buff, uint64_t offset, uint64_t len) diff --git a/renderdoc/replay/replay_controller.h b/renderdoc/replay/replay_controller.h index 24db80a20..20539dbdb 100644 --- a/renderdoc/replay/replay_controller.h +++ b/renderdoc/replay/replay_controller.h @@ -180,7 +180,7 @@ public: ShaderDebugTrace *DebugThread(const uint32_t groupid[3], const uint32_t threadid[3]); void FreeTrace(ShaderDebugTrace *trace); - MeshFormat GetPostVSData(uint32_t instID, MeshDataStage stage); + MeshFormat GetPostVSData(uint32_t instID, uint32_t viewID, MeshDataStage stage); rdcarray GetUsage(ResourceId id); diff --git a/renderdoc/replay/replay_driver.h b/renderdoc/replay/replay_driver.h index a351a5b66..8bb4028e1 100644 --- a/renderdoc/replay/replay_driver.h +++ b/renderdoc/replay/replay_driver.h @@ -130,7 +130,8 @@ public: virtual ResourceId GetLiveID(ResourceId id) = 0; - virtual MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, MeshDataStage stage) = 0; + virtual MeshFormat GetPostVSBuffers(uint32_t eventId, uint32_t instID, uint32_t viewID, + MeshDataStage stage) = 0; virtual void GetBufferData(ResourceId buff, uint64_t offset, uint64_t len, bytebuf &retData) = 0; virtual void GetTextureData(ResourceId tex, uint32_t arrayIdx, uint32_t mip, diff --git a/renderdoc/replay/replay_output.cpp b/renderdoc/replay/replay_output.cpp index 03385a9ab..1bcb8c4e4 100644 --- a/renderdoc/replay/replay_output.cpp +++ b/renderdoc/replay/replay_output.cpp @@ -449,14 +449,16 @@ rdcpair ReplayOutput::PickVertex(uint32_t eventId, uint32_t // used for post-VS output, calculate the offset of the element we're using as position, // relative to 0 - MeshFormat fmt = m_pDevice->GetPostVSBuffers( - draw->eventId, m_RenderData.meshDisplay.curInstance, m_RenderData.meshDisplay.type); + MeshFormat fmt = + m_pDevice->GetPostVSBuffers(draw->eventId, m_RenderData.meshDisplay.curInstance, + m_RenderData.meshDisplay.curView, m_RenderData.meshDisplay.type); uint64_t elemOffset = cfg.position.vertexByteOffset - fmt.vertexByteOffset; for(uint32_t inst = firstInst; inst < maxInst; inst++) { // find the start of this buffer, and apply the element offset, then pick in that instance - fmt = m_pDevice->GetPostVSBuffers(draw->eventId, inst, m_RenderData.meshDisplay.type); + fmt = m_pDevice->GetPostVSBuffers(draw->eventId, inst, m_RenderData.meshDisplay.curView, + m_RenderData.meshDisplay.type); if(fmt.vertexResourceId != ResourceId()) cfg.position.vertexByteOffset = fmt.vertexByteOffset + elemOffset; @@ -819,9 +821,11 @@ void ReplayOutput::DisplayMesh() for(uint32_t inst = 0; inst < RDCMAX(1U, d->numInstances); inst++) { // get the 'most final' stage - MeshFormat fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, MeshDataStage::GSOut); + MeshFormat fmt = m_pDevice->GetPostVSBuffers( + passEvents[i], inst, m_RenderData.meshDisplay.curView, MeshDataStage::GSOut); if(fmt.vertexResourceId == ResourceId()) - fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, MeshDataStage::VSOut); + fmt = m_pDevice->GetPostVSBuffers(passEvents[i], inst, m_RenderData.meshDisplay.curView, + MeshDataStage::VSOut); fmt.meshColor = passDraws; @@ -844,9 +848,11 @@ void ReplayOutput::DisplayMesh() for(uint32_t inst = 0; inst < maxInst; inst++) { // get the 'most final' stage - MeshFormat fmt = m_pDevice->GetPostVSBuffers(draw->eventId, inst, MeshDataStage::GSOut); + MeshFormat fmt = m_pDevice->GetPostVSBuffers( + draw->eventId, inst, m_RenderData.meshDisplay.curView, MeshDataStage::GSOut); if(fmt.vertexResourceId == ResourceId()) - fmt = m_pDevice->GetPostVSBuffers(draw->eventId, inst, MeshDataStage::VSOut); + fmt = m_pDevice->GetPostVSBuffers(draw->eventId, inst, m_RenderData.meshDisplay.curView, + MeshDataStage::VSOut); fmt.meshColor = otherInstances;