mirror of
https://github.com/baldurk/renderdoc.git
synced 2026-07-10 01:27:15 +00:00
Do index buffer remapping in vulkan
* This allows vulkan to handle index buffers with huge differences between minimum and maximum indices without over-allocating. * It does mean we can no longer use the original index buffer as-is without changes, but this is a fair trade-off.
This commit is contained in:
@@ -35,11 +35,10 @@ 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<uint32_t> instDivisor, uint32_t &descSet,
|
||||
const DrawcallDescription *draw, int32_t indexOffset,
|
||||
uint64_t numFetchVerts, uint32_t numVerts, uint32_t numViews,
|
||||
static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRVPatchData &patchData,
|
||||
const char *entryName, std::vector<uint32_t> instDivisor,
|
||||
uint32_t &descSet, const DrawcallDescription *draw,
|
||||
uint32_t numVerts, uint32_t numViews,
|
||||
std::vector<uint32_t> &modSpirv, uint32_t &bufStride)
|
||||
{
|
||||
SPIRVEditor editor(modSpirv);
|
||||
@@ -511,12 +510,10 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl,
|
||||
}
|
||||
|
||||
SPIRVId outBufferVarID = 0;
|
||||
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");
|
||||
@@ -680,12 +677,15 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl,
|
||||
uint32_t intInvocationID = editor.MakeId();
|
||||
ops.push_back(SPIRVOperation(spv::OpBitcast, {sint32ID, intInvocationID, invocationID}));
|
||||
|
||||
editor.SetName(intInvocationID, "invocation");
|
||||
// arraySlotID = intInvocationID;
|
||||
uint32_t arraySlotID = intInvocationID;
|
||||
|
||||
// int viewinst = intInvocationID / numFetchVerts
|
||||
editor.SetName(intInvocationID, "arraySlot");
|
||||
|
||||
// int viewinst = intInvocationID / numVerts
|
||||
uint32_t viewinstID = editor.MakeId();
|
||||
ops.push_back(SPIRVOperation(spv::OpSDiv,
|
||||
{sint32ID, viewinstID, intInvocationID, numFetchVertsConstID}));
|
||||
ops.push_back(
|
||||
SPIRVOperation(spv::OpSDiv, {sint32ID, viewinstID, intInvocationID, numVertsConstID}));
|
||||
|
||||
editor.SetName(viewinstID, "viewInstance");
|
||||
|
||||
@@ -745,23 +745,6 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl,
|
||||
ops.push_back(SPIRVOperation(spv::OpBitcast, {sint32ID, vertexIndex, uintIndex}));
|
||||
}
|
||||
|
||||
// int arraySlotID = viewinst * numVerts;
|
||||
uint32_t arraySlotTempID = editor.MakeId();
|
||||
ops.push_back(
|
||||
SPIRVOperation(spv::OpIMul, {sint32ID, arraySlotTempID, viewinstID, numVertsConstID}));
|
||||
|
||||
// arraySlotID = arraySlotID + vertexIndex;
|
||||
uint32_t arraySlotTemp2ID = editor.MakeId();
|
||||
ops.push_back(
|
||||
SPIRVOperation(spv::OpIAdd, {sint32ID, arraySlotTemp2ID, arraySlotTempID, vertexIndex}));
|
||||
|
||||
// arraySlotID = arraySlotID + indexOffset;
|
||||
uint32_t arraySlotID = editor.MakeId();
|
||||
ops.push_back(SPIRVOperation(spv::OpIAdd, {sint32ID, arraySlotID, arraySlotTemp2ID,
|
||||
editor.AddConstantImmediate(indexOffset)}));
|
||||
|
||||
editor.SetName(arraySlotID, "arraySlot");
|
||||
|
||||
// we use the current value of vertexIndex and use instID, to lookup per-vertex and
|
||||
// per-instance attributes. This is because when we fetched the vertex data, we advanced by
|
||||
// (in non-indexed draws) vertexOffset, and by instanceOffset. Rather than fetching data
|
||||
@@ -1085,6 +1068,11 @@ void VulkanReplay::ClearPostVSCache()
|
||||
|
||||
for(auto it = m_PostVSData.begin(); it != m_PostVSData.end(); ++it)
|
||||
{
|
||||
if(it->second.vsout.idxbuf != VK_NULL_HANDLE)
|
||||
{
|
||||
m_pDriver->vkDestroyBuffer(dev, it->second.vsout.idxbuf, NULL);
|
||||
m_pDriver->vkFreeMemory(dev, it->second.vsout.idxbufmem, NULL);
|
||||
}
|
||||
m_pDriver->vkDestroyBuffer(dev, it->second.vsout.buf, NULL);
|
||||
m_pDriver->vkFreeMemory(dev, it->second.vsout.bufmem, NULL);
|
||||
}
|
||||
@@ -1124,6 +1112,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
// empty vertex output signature
|
||||
m_PostVSData[eventId].vsin.topo = pipeInfo.topology;
|
||||
m_PostVSData[eventId].vsout.buf = VK_NULL_HANDLE;
|
||||
m_PostVSData[eventId].vsout.bufmem = VK_NULL_HANDLE;
|
||||
m_PostVSData[eventId].vsout.instStride = 0;
|
||||
m_PostVSData[eventId].vsout.vertStride = 0;
|
||||
m_PostVSData[eventId].vsout.numViews = 1;
|
||||
@@ -1131,7 +1120,8 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
m_PostVSData[eventId].vsout.farPlane = 0.0f;
|
||||
m_PostVSData[eventId].vsout.useIndices = false;
|
||||
m_PostVSData[eventId].vsout.hasPosOut = false;
|
||||
m_PostVSData[eventId].vsout.idxBuf = ResourceId();
|
||||
m_PostVSData[eventId].vsout.idxbuf = VK_NULL_HANDLE;
|
||||
m_PostVSData[eventId].vsout.idxbufmem = VK_NULL_HANDLE;
|
||||
|
||||
m_PostVSData[eventId].vsout.topo = pipeInfo.topology;
|
||||
|
||||
@@ -1171,8 +1161,10 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
VkDeviceMemory uniqIdxBufMem = VK_NULL_HANDLE;
|
||||
VkBufferView uniqIdxBufView = VK_NULL_HANDLE;
|
||||
|
||||
VkBuffer rebasedIdxBuf = VK_NULL_HANDLE;
|
||||
VkDeviceMemory rebasedIdxBufMem = VK_NULL_HANDLE;
|
||||
|
||||
uint32_t numVerts = drawcall->numIndices;
|
||||
uint64_t numFetchVerts = drawcall->numIndices;
|
||||
VkDeviceSize bufSize = 0;
|
||||
|
||||
uint32_t numViews = 1;
|
||||
@@ -1192,9 +1184,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
|
||||
uint32_t idxsize = state.ibuffer.bytewidth;
|
||||
|
||||
int32_t baseVertex = 0;
|
||||
|
||||
uint32_t minIndex = 0, maxIndex = RDCMAX(drawcall->baseVertex, 0) + numVerts - 1;
|
||||
uint32_t maxIndex = RDCMAX(drawcall->baseVertex, 0) + numVerts - 1;
|
||||
|
||||
uint32_t maxInstance = drawcall->instanceOffset + drawcall->numInstances - 1;
|
||||
|
||||
@@ -1291,18 +1281,30 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
if(numIndices < drawcall->numIndices && (indices.empty() || indices[0] != 0))
|
||||
indices.insert(indices.begin(), 0);
|
||||
|
||||
minIndex = indices[0];
|
||||
maxIndex = indices[indices.size() - 1];
|
||||
maxIndex = indices.back();
|
||||
|
||||
// set numVerts
|
||||
numVerts = maxIndex - minIndex + 1;
|
||||
numFetchVerts = (uint64_t)indices.size();
|
||||
numVerts = (uint32_t)indices.size();
|
||||
|
||||
// An index buffer could be something like: 500, 520, 518, 553, 554, 556
|
||||
// but in our vertex buffer that will be: 0, 20, 18, 53, 54, 56
|
||||
// so we add -minIndex as the baseVertex when rendering. The existing baseVertex was 'applied'
|
||||
// when we fetched the mesh output so it can be discarded.
|
||||
baseVertex = -(int32_t)minIndex;
|
||||
// An index buffer could be something like: 500, 501, 502, 501, 503, 502
|
||||
// in which case we can't use the existing index buffer without filling 499 slots of vertex
|
||||
// data with padding. Instead we rebase the indices based on the smallest vertex so it becomes
|
||||
// 0, 1, 2, 1, 3, 2 and then that matches our stream-out'd buffer.
|
||||
//
|
||||
// Note that there could also be gaps, like: 500, 501, 502, 510, 511, 512
|
||||
// which would become 0, 1, 2, 3, 4, 5 and so the old index buffer would no longer be valid.
|
||||
// We just stream-out a tightly packed list of unique indices, and then remap the index buffer
|
||||
// so that what did point to 500 points to 0 (accounting for rebasing), and what did point
|
||||
// to 510 now points to 3 (accounting for the unique sort).
|
||||
|
||||
// we use a map here since the indices may be sparse. Especially considering if an index
|
||||
// is 'invalid' like 0xcccccccc then we don't want an array of 3.4 billion entries.
|
||||
map<uint32_t, size_t> indexRemap;
|
||||
for(size_t i = 0; i < indices.size(); i++)
|
||||
{
|
||||
// by definition, this index will only appear once in indices[]
|
||||
indexRemap[indices[i]] = i;
|
||||
}
|
||||
|
||||
// create buffer with unique 0-based indices
|
||||
VkBufferCreateInfo bufInfo = {
|
||||
@@ -1350,6 +1352,54 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
memcpy(idxData, &indices[0], indices.size() * sizeof(uint32_t));
|
||||
|
||||
m_pDriver->vkUnmapMemory(m_Device, uniqIdxBufMem);
|
||||
|
||||
// rebase existing index buffer to point to the right elements in our stream-out'd
|
||||
// vertex buffer
|
||||
for(uint32_t i = 0; i < numIndices; i++)
|
||||
{
|
||||
uint32_t i32 = index16 ? uint32_t(idx16[i]) : idx32[i];
|
||||
|
||||
// preserve primitive restart indices
|
||||
if(i32 == (index16 ? 0xffff : 0xffffffff))
|
||||
continue;
|
||||
|
||||
// apply baseVertex but clamp to 0 (don't allow index to become negative)
|
||||
if(i32 < idxclamp)
|
||||
i32 = 0;
|
||||
else if(drawcall->baseVertex < 0)
|
||||
i32 -= idxclamp;
|
||||
else if(drawcall->baseVertex > 0)
|
||||
i32 += drawcall->baseVertex;
|
||||
|
||||
if(index16)
|
||||
idx16[i] = uint16_t(indexRemap[i32]);
|
||||
else
|
||||
idx32[i] = uint32_t(indexRemap[i32]);
|
||||
}
|
||||
|
||||
bufInfo.size = (VkDeviceSize)idxdata.size();
|
||||
bufInfo.usage = VK_BUFFER_USAGE_INDEX_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
|
||||
|
||||
vkr = m_pDriver->vkCreateBuffer(dev, &bufInfo, NULL, &rebasedIdxBuf);
|
||||
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
||||
|
||||
m_pDriver->vkGetBufferMemoryRequirements(dev, rebasedIdxBuf, &mrq);
|
||||
|
||||
allocInfo.allocationSize = mrq.size;
|
||||
allocInfo.memoryTypeIndex = m_pDriver->GetUploadMemoryIndex(mrq.memoryTypeBits);
|
||||
|
||||
vkr = m_pDriver->vkAllocateMemory(dev, &allocInfo, NULL, &rebasedIdxBufMem);
|
||||
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
||||
|
||||
vkr = m_pDriver->vkBindBufferMemory(dev, rebasedIdxBuf, rebasedIdxBufMem, 0);
|
||||
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
||||
|
||||
vkr = m_pDriver->vkMapMemory(m_Device, rebasedIdxBufMem, 0, VK_WHOLE_SIZE, 0, (void **)&idxData);
|
||||
RDCASSERTEQUAL(vkr, VK_SUCCESS);
|
||||
|
||||
memcpy(idxData, idxdata.data(), idxdata.size());
|
||||
|
||||
m_pDriver->vkUnmapMemory(m_Device, rebasedIdxBufMem);
|
||||
}
|
||||
|
||||
uint32_t bufStride = 0;
|
||||
@@ -1695,9 +1745,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, numViews, modSpirv, bufStride);
|
||||
ConvertToMeshOutputCompute(*refl, *pipeInfo.shaders[0].patchData,
|
||||
pipeInfo.shaders[0].entryPoint.c_str(), attrInstDivisor, descSet,
|
||||
drawcall, numVerts, numViews, modSpirv, bufStride);
|
||||
|
||||
VkComputePipelineCreateInfo compPipeInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
|
||||
|
||||
@@ -1870,7 +1920,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(numViews);
|
||||
uint64_t totalVerts = numVerts * 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
|
||||
@@ -2005,7 +2055,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
m_PostVSData[eventId].vsout.buf = meshBuffer;
|
||||
m_PostVSData[eventId].vsout.bufmem = meshMem;
|
||||
|
||||
m_PostVSData[eventId].vsout.baseVertex = baseVertex + drawcall->baseVertex;
|
||||
m_PostVSData[eventId].vsout.baseVertex = 0;
|
||||
|
||||
m_PostVSData[eventId].vsout.numViews = numViews;
|
||||
|
||||
@@ -2020,11 +2070,11 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
|
||||
if(drawcall->flags & DrawFlags::Instanced)
|
||||
m_PostVSData[eventId].vsout.instStride = uint32_t(bufSize / (drawcall->numInstances * numViews));
|
||||
|
||||
m_PostVSData[eventId].vsout.idxBuf = ResourceId();
|
||||
m_PostVSData[eventId].vsout.idxbuf = VK_NULL_HANDLE;
|
||||
if(m_PostVSData[eventId].vsout.useIndices && state.ibuffer.buf != ResourceId())
|
||||
{
|
||||
m_PostVSData[eventId].vsout.idxBuf = GetResourceManager()->GetOriginalID(state.ibuffer.buf);
|
||||
m_PostVSData[eventId].vsout.idxOffset = state.ibuffer.offs + drawcall->indexOffset * idxsize;
|
||||
m_PostVSData[eventId].vsout.idxbuf = rebasedIdxBuf;
|
||||
m_PostVSData[eventId].vsout.idxbufmem = rebasedIdxBufMem;
|
||||
m_PostVSData[eventId].vsout.idxFmt = idxsize == 2 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32;
|
||||
}
|
||||
|
||||
@@ -2119,9 +2169,9 @@ MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uin
|
||||
|
||||
MeshFormat ret;
|
||||
|
||||
if(s.useIndices && s.idxBuf != ResourceId())
|
||||
if(s.useIndices && s.idxbuf != VK_NULL_HANDLE)
|
||||
{
|
||||
ret.indexResourceId = s.idxBuf;
|
||||
ret.indexResourceId = GetResID(s.idxbuf);
|
||||
ret.indexByteStride = s.idxFmt == VK_INDEX_TYPE_UINT16 ? 2 : 4;
|
||||
}
|
||||
else
|
||||
@@ -2129,7 +2179,7 @@ MeshFormat VulkanReplay::GetPostVSBuffers(uint32_t eventId, uint32_t instID, uin
|
||||
ret.indexResourceId = ResourceId();
|
||||
ret.indexByteStride = 0;
|
||||
}
|
||||
ret.indexByteOffset = s.idxOffset;
|
||||
ret.indexByteOffset = 0;
|
||||
ret.baseVertex = s.baseVertex;
|
||||
|
||||
if(s.buf != VK_NULL_HANDLE)
|
||||
|
||||
@@ -143,8 +143,8 @@ struct VulkanPostVSData
|
||||
uint32_t numViews;
|
||||
|
||||
bool useIndices;
|
||||
ResourceId idxBuf;
|
||||
VkDeviceSize idxOffset;
|
||||
VkBuffer idxbuf;
|
||||
VkDeviceMemory idxbufmem;
|
||||
VkIndexType idxFmt;
|
||||
|
||||
bool hasPosOut;
|
||||
|
||||
Reference in New Issue
Block a user