From 8f405a2197f84fc7f4d95bfd088e687e284fdf0e Mon Sep 17 00:00:00 2001 From: baldurk Date: Wed, 24 Jan 2018 16:14:22 +0000 Subject: [PATCH] Render mesh data with instanced properties correctly. --- qrenderdoc/Windows/BufferViewer.cpp | 6 +++++ renderdoc/api/replay/control_types.h | 7 +++++ renderdoc/driver/d3d11/d3d11_rendermesh.cpp | 14 ++++++++-- renderdoc/driver/d3d12/d3d12_rendermesh.cpp | 29 ++++++++++++++++++++- renderdoc/driver/gl/gl_rendermesh.cpp | 13 +++++++-- renderdoc/driver/vulkan/vk_rendermesh.cpp | 29 +++++++++++++++++++-- renderdoc/replay/renderdoc_serialise.inl | 4 ++- 7 files changed, 94 insertions(+), 8 deletions(-) diff --git a/qrenderdoc/Windows/BufferViewer.cpp b/qrenderdoc/Windows/BufferViewer.cpp index 2710cc008..93ebb9e3a 100644 --- a/qrenderdoc/Windows/BufferViewer.cpp +++ b/qrenderdoc/Windows/BufferViewer.cpp @@ -2129,6 +2129,9 @@ void BufferViewer::updatePreviewColumns() { const FormatElement &el = m_ModelVSIn->columns[elIdx]; + m_VSInPosition.instanced = el.perinstance; + m_VSInPosition.instStepRate = el.instancerate; + if(el.buffer < vbs.count()) { m_VSInPosition.vertexResourceId = vbs[el.buffer].resourceId; @@ -2152,6 +2155,9 @@ void BufferViewer::updatePreviewColumns() { const FormatElement &el = m_ModelVSIn->columns[elIdx]; + m_VSInSecondary.instanced = el.perinstance; + m_VSInSecondary.instStepRate = el.instancerate; + if(el.buffer < vbs.count()) { m_VSInSecondary.vertexResourceId = vbs[el.buffer].resourceId; diff --git a/renderdoc/api/replay/control_types.h b/renderdoc/api/replay/control_types.h index d11337a0c..c58cbd958 100644 --- a/renderdoc/api/replay/control_types.h +++ b/renderdoc/api/replay/control_types.h @@ -40,10 +40,12 @@ struct MeshFormat baseVertex = 0; vertexByteOffset = 0; vertexByteStride = 0; + instStepRate = 1; showAlpha = false; topology = Topology::Unknown; numIndices = 0; unproject = false; + instanced = false; nearPlane = farPlane = 0.0f; } @@ -75,6 +77,8 @@ struct MeshFormat Topology topology; DOCUMENT("The number of vertices in the mesh."); uint32_t numIndices; + DOCUMENT("The number of instances to render with the same value. See :data:`instanced`."); + uint32_t instStepRate; DOCUMENT("The near plane for the projection matrix."); float nearPlane; @@ -83,6 +87,9 @@ struct MeshFormat DOCUMENT("``True`` if this mesh element contains post-projection positional data."); bool unproject; + DOCUMENT("``True`` if this mesh element comes from instanced data. See :data:`instStepRate`."); + bool instanced; + DOCUMENT("``True`` if the alpha component of this element should be used."); bool showAlpha; }; diff --git a/renderdoc/driver/d3d11/d3d11_rendermesh.cpp b/renderdoc/driver/d3d11/d3d11_rendermesh.cpp index 0da63a1e9..7f80d8a7c 100644 --- a/renderdoc/driver/d3d11/d3d11_rendermesh.cpp +++ b/renderdoc/driver/d3d11/d3d11_rendermesh.cpp @@ -89,7 +89,8 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const vector &seconda layoutdesc[0].Format = MakeDXGIFormat(resFmt); layoutdesc[0].AlignedByteOffset = 0; // offset will be handled by vertex buffer offset layoutdesc[0].InputSlot = 0; - layoutdesc[0].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + layoutdesc[0].InputSlotClass = + cfg.position.instanced ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; layoutdesc[0].InstanceDataStepRate = 0; layoutdesc[1].SemanticName = "sec"; @@ -99,7 +100,8 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const vector &seconda layoutdesc[1].Format = MakeDXGIFormat(resFmt2); layoutdesc[1].AlignedByteOffset = 0; layoutdesc[1].InputSlot = 1; - layoutdesc[1].InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA; + layoutdesc[1].InputSlotClass = + cfg.second.instanced ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; layoutdesc[1].InstanceDataStepRate = 0; HRESULT hr = m_pDevice->CreateInputLayout(layoutdesc, 2, m_MeshRender.MeshVSBytecode, @@ -221,6 +223,14 @@ void D3D11Replay::RenderMesh(uint32_t eventId, const vector &seconda UINT str[] = {cfg.position.vertexByteStride, cfg.second.vertexByteStride}; UINT offs[] = {(UINT)cfg.position.vertexByteOffset, (UINT)cfg.second.vertexByteOffset}; + // we source all data from the first instanced value in the instanced case, so make sure we + // offset correctly here. + if(cfg.position.instanced) + offs[0] += cfg.position.vertexByteStride * (cfg.curInstance / cfg.position.instStepRate); + + if(cfg.second.instanced) + offs[1] += cfg.second.vertexByteStride * (cfg.curInstance / cfg.second.instStepRate); + { auto it = WrappedID3D11Buffer::m_BufferList.find(cfg.position.vertexResourceId); diff --git a/renderdoc/driver/d3d12/d3d12_rendermesh.cpp b/renderdoc/driver/d3d12/d3d12_rendermesh.cpp index f85db2c9e..79ec6c15c 100644 --- a/renderdoc/driver/d3d12/d3d12_rendermesh.cpp +++ b/renderdoc/driver/d3d12/d3d12_rendermesh.cpp @@ -72,6 +72,17 @@ MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshForm } bit += 16; + if(primary.instanced) + key |= 1ULL << bit; + bit++; + + if(secondary.instanced) + key |= 1ULL << bit; + bit++; + + // only 64 bits, make sure they all fit + RDCASSERT(bit < 64); + MeshDisplayPipelines &cache = m_CachedMeshPipelines[key]; if(cache.pipes[(uint32_t)SolidShade::NoSolid] != NULL) @@ -113,6 +124,8 @@ MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshForm D3D12_INPUT_ELEMENT_DESC ia[2] = {}; ia[0].SemanticName = "pos"; ia[0].Format = primaryFmt; + ia[0].InputSlotClass = primary.instanced ? D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA + : D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; ia[1].SemanticName = "sec"; ia[1].InputSlot = 1; ia[1].Format = secondaryFmt == DXGI_FORMAT_UNKNOWN ? primaryFmt : secondaryFmt; @@ -171,7 +184,8 @@ MeshDisplayPipelines D3D12DebugManager::CacheMeshDisplayPipelines(const MeshForm if(secondary.vertexResourceId != ResourceId()) { // pull secondary information from second vertex buffer - ia[1].InputSlotClass = D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; + ia[1].InputSlotClass = secondary.instanced ? D3D12_INPUT_CLASSIFICATION_PER_INSTANCE_DATA + : D3D12_INPUT_CLASSIFICATION_PER_VERTEX_DATA; RDCASSERT(secondaryFmt != DXGI_FORMAT_UNKNOWN); hr = m_pDevice->CreateGraphicsPipelineState( @@ -336,6 +350,12 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const vector &seconda m_pDevice->GetResourceManager()->GetCurrentAs(cfg.position.vertexResourceId); UINT64 offs = cfg.position.vertexByteOffset; + + // we source all data from the first instanced value in the instanced case, so make sure we + // offset correctly here. + if(cfg.position.instanced) + offs += cfg.position.vertexByteStride * (cfg.curInstance / cfg.position.instStepRate); + D3D12_VERTEX_BUFFER_VIEW view; view.BufferLocation = vb->GetGPUVirtualAddress() + offs; view.StrideInBytes = cfg.position.vertexByteStride; @@ -363,10 +383,17 @@ void D3D12Replay::RenderMesh(uint32_t eventId, const vector &seconda m_pDevice->GetResourceManager()->GetCurrentAs(cfg.position.vertexResourceId); UINT64 offs = cfg.second.vertexByteOffset; + + // we source all data from the first instanced value in the instanced case, so make sure we + // offset correctly here. + if(cfg.second.instanced) + offs += cfg.second.vertexByteStride * (cfg.curInstance / cfg.second.instStepRate); + D3D12_VERTEX_BUFFER_VIEW view; view.BufferLocation = vb->GetGPUVirtualAddress() + offs; view.StrideInBytes = cfg.second.vertexByteStride; view.SizeInBytes = UINT(vb->GetDesc().Width - offs); + list->IASetVertexBuffers(1, 1, &view); } diff --git a/renderdoc/driver/gl/gl_rendermesh.cpp b/renderdoc/driver/gl/gl_rendermesh.cpp index b8cee4923..4dbd7f563 100644 --- a/renderdoc/driver/gl/gl_rendermesh.cpp +++ b/renderdoc/driver/gl/gl_rendermesh.cpp @@ -235,10 +235,19 @@ void GLReplay::RenderMesh(uint32_t eventId, const vector &secondaryD gl.glVertexAttribLFormat(i, meshData[i]->format.compCount, eGL_DOUBLE, 0); } + GLintptr offs = (GLintptr)meshData[i]->vertexByteOffset; + + if(meshData[i]->instanced) + offs += meshData[i]->vertexByteStride * (cfg.curInstance / meshData[i]->instStepRate); + GLuint vb = m_pDriver->GetResourceManager()->GetCurrentResource(meshData[i]->vertexResourceId).name; - gl.glBindVertexBuffer(i, vb, (GLintptr)meshData[i]->vertexByteOffset, - meshData[i]->vertexByteStride); + gl.glBindVertexBuffer(i, vb, offs, meshData[i]->vertexByteStride); + + if(meshData[i]->instanced) + gl.glVertexAttribDivisor(i, 1); + else + gl.glVertexAttribDivisor(i, 0); } // enable position attribute diff --git a/renderdoc/driver/vulkan/vk_rendermesh.cpp b/renderdoc/driver/vulkan/vk_rendermesh.cpp index f24dd4545..eb3d83fd7 100644 --- a/renderdoc/driver/vulkan/vk_rendermesh.cpp +++ b/renderdoc/driver/vulkan/vk_rendermesh.cpp @@ -75,6 +75,17 @@ MeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineLay } bit += 16; + if(primary.instanced) + key |= 1ULL << bit; + bit++; + + if(secondary.instanced) + key |= 1ULL << bit; + bit++; + + // only 64 bits, make sure they all fit + RDCASSERT(bit < 64); + MeshDisplayPipelines &cache = m_CachedMeshPipelines[key]; if(cache.pipes[(uint32_t)SolidShade::NoSolid] != VK_NULL_HANDLE) @@ -88,9 +99,11 @@ MeshDisplayPipelines VulkanDebugManager::CacheMeshDisplayPipelines(VkPipelineLay VkVertexInputBindingDescription binds[] = { // primary - {0, primary.vertexByteStride, VK_VERTEX_INPUT_RATE_VERTEX}, + {0, primary.vertexByteStride, + primary.instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX}, // secondary - {1, secondary.vertexByteStride, VK_VERTEX_INPUT_RATE_VERTEX}}; + {1, secondary.vertexByteStride, + secondary.instanced ? VK_VERTEX_INPUT_RATE_INSTANCE : VK_VERTEX_INPUT_RATE_VERTEX}}; RDCASSERT(primaryFmt != VK_FORMAT_UNDEFINED); @@ -526,6 +539,12 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const vector &second m_pDriver->GetResourceManager()->GetCurrentHandle(cfg.position.vertexResourceId); VkDeviceSize offs = cfg.position.vertexByteOffset; + + // we source all data from the first instanced value in the instanced case, so make sure we + // offset correctly here. + if(cfg.position.instanced) + offs += cfg.position.vertexByteStride * (cfg.curInstance / cfg.position.instStepRate); + vt->CmdBindVertexBuffers(Unwrap(cmd), 0, 1, UnwrapPtr(vb), &offs); } @@ -541,6 +560,12 @@ void VulkanReplay::RenderMesh(uint32_t eventId, const vector &second m_pDriver->GetResourceManager()->GetCurrentHandle(cfg.second.vertexResourceId); VkDeviceSize offs = cfg.second.vertexByteOffset; + + // we source all data from the first instanced value in the instanced case, so make sure we + // offset correctly here. + if(cfg.second.instanced) + offs += cfg.second.vertexByteStride * (cfg.curInstance / cfg.second.instStepRate); + vt->CmdBindVertexBuffers(Unwrap(cmd), 1, 1, UnwrapPtr(vb), &offs); } diff --git a/renderdoc/replay/renderdoc_serialise.inl b/renderdoc/replay/renderdoc_serialise.inl index 8daf7c6a4..11596b160 100644 --- a/renderdoc/replay/renderdoc_serialise.inl +++ b/renderdoc/replay/renderdoc_serialise.inl @@ -706,11 +706,13 @@ void DoSerialise(SerialiserType &ser, MeshFormat &el) SERIALISE_MEMBER(showAlpha); SERIALISE_MEMBER(topology); SERIALISE_MEMBER(numIndices); + SERIALISE_MEMBER(instStepRate); SERIALISE_MEMBER(unproject); + SERIALISE_MEMBER(instanced); SERIALISE_MEMBER(nearPlane); SERIALISE_MEMBER(farPlane); - SIZE_CHECK(88); + SIZE_CHECK(96); } template