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;