Add support for VK_EXT_vertex_attribute_divisor

This commit is contained in:
baldurk
2018-04-25 17:58:39 +01:00
parent 5695f3e76f
commit f87338dc0e
14 changed files with 158 additions and 28 deletions
@@ -917,9 +917,12 @@ rdcarray<VertexInputAttribute> CommonPipelineState::GetVertexInputs()
ret[a].vertexBuffer = (int)attrs[i].binding;
ret[a].byteOffset = attrs[i].byteOffset;
ret[a].perInstance = false;
if(attrs[i].binding < (uint32_t)m_Vulkan->vertexInput.bindings.count())
ret[a].perInstance = m_Vulkan->vertexInput.bindings[attrs[i].binding].perInstance;
ret[a].instanceRate = 1;
if(attrs[i].binding < (uint32_t)m_Vulkan->vertexInput.bindings.count())
{
ret[a].perInstance = m_Vulkan->vertexInput.bindings[attrs[i].binding].perInstance;
ret[a].instanceRate = m_Vulkan->vertexInput.bindings[attrs[i].binding].instanceDivisor;
}
ret[a].format = attrs[i].format;
ret[a].used = true;
ret[a].genericEnabled = false;
@@ -180,9 +180,9 @@ VulkanPipelineStateViewer::VulkanPipelineStateViewer(ICaptureContext &ctx,
RDHeaderView *header = new RDHeaderView(Qt::Horizontal, this);
ui->viBuffers->setHeader(header);
ui->viBuffers->setColumns({tr("Slot"), tr("Buffer"), tr("Rate"), tr("Offset"), tr("Stride"),
tr("Byte Length"), tr("Go")});
header->setColumnStretchHints({1, 4, 2, 2, 2, 3, -1});
ui->viBuffers->setColumns({tr("Slot"), tr("Buffer"), tr("Rate"), tr("Divisor"), tr("Offset"),
tr("Stride"), tr("Byte Length"), tr("Go")});
header->setColumnStretchHints({1, 4, 2, 2, 2, 2, 3, -1});
ui->viBuffers->setHoverIconColumn(6, action, action_hover);
ui->viBuffers->setClearSelectionOnFocusLoss(true);
@@ -1595,7 +1595,7 @@ void VulkanPipelineStateViewer::setState()
length = buf->length;
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{tr("Index"), state.inputAssembly.indexBuffer.resourceId, tr("Index"),
{tr("Index"), state.inputAssembly.indexBuffer.resourceId, tr("Index"), lit("-"),
(qulonglong)state.inputAssembly.indexBuffer.byteOffset,
draw != NULL ? draw->indexByteWidth : 0, (qulonglong)length, QString()});
@@ -1616,8 +1616,8 @@ void VulkanPipelineStateViewer::setState()
{
if(ibufferUsed || showEmpty)
{
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{tr("Index"), ResourceId(), tr("Index"), lit("-"), lit("-"), lit("-"), QString()});
RDTreeWidgetItem *node = new RDTreeWidgetItem({tr("Index"), ResourceId(), tr("Index"), lit("-"),
lit("-"), lit("-"), lit("-"), QString()});
node->setTag(QVariant::fromValue(
VulkanVBIBTag(state.inputAssembly.indexBuffer.resourceId,
@@ -1657,6 +1657,7 @@ void VulkanPipelineStateViewer::setState()
uint64_t length = 1;
uint64_t offset = 0;
uint32_t stride = 0;
uint32_t divisor = 1;
if(vbuff != NULL)
{
@@ -1671,6 +1672,8 @@ void VulkanPipelineStateViewer::setState()
{
stride = bind->byteStride;
rate = bind->perInstance ? tr("Instance") : tr("Vertex");
if(bind->perInstance)
divisor = bind->instanceDivisor;
}
else
{
@@ -1680,11 +1683,11 @@ void VulkanPipelineStateViewer::setState()
RDTreeWidgetItem *node = NULL;
if(filledSlot)
node = new RDTreeWidgetItem({i, vbuff->resourceId, rate, (qulonglong)offset, stride,
(qulonglong)length, QString()});
node = new RDTreeWidgetItem({i, vbuff->resourceId, rate, divisor, (qulonglong)offset,
stride, (qulonglong)length, QString()});
else
node = new RDTreeWidgetItem(
{i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
{i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
node->setTag(QVariant::fromValue(VulkanVBIBTag(
vbuff != NULL ? vbuff->resourceId : ResourceId(), vbuff != NULL ? vbuff->byteOffset : 0)));
@@ -1706,7 +1709,7 @@ void VulkanPipelineStateViewer::setState()
if(usedBindings[i])
{
RDTreeWidgetItem *node = new RDTreeWidgetItem(
{i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
{i, tr("No Binding"), lit("-"), lit("-"), lit("-"), lit("-"), lit("-"), QString()});
node->setTag(QVariant::fromValue(VulkanVBIBTag(ResourceId(), 0)));
+11 -1
View File
@@ -320,7 +320,7 @@ struct VertexBinding
bool operator==(const VertexBinding &o) const
{
return vertexBufferBinding == o.vertexBufferBinding && byteStride == o.byteStride &&
perInstance == o.perInstance;
perInstance == o.perInstance && instanceDivisor == o.instanceDivisor;
}
bool operator<(const VertexBinding &o) const
{
@@ -330,6 +330,8 @@ struct VertexBinding
return byteStride < o.byteStride;
if(!(perInstance == o.perInstance))
return perInstance < o.perInstance;
if(!(instanceDivisor == o.instanceDivisor))
return instanceDivisor < o.instanceDivisor;
return false;
}
DOCUMENT("The vertex binding where data will be sourced from.");
@@ -338,6 +340,14 @@ struct VertexBinding
uint32_t byteStride = 0;
DOCUMENT("``True`` if the vertex data is instance-rate.");
bool perInstance = false;
DOCUMENT(R"(The instance rate divisor.
If this is ``0`` then every vertex gets the same value.
If it's ``1`` then one element is read for each instance, and for ``N`` greater than ``1`` then
``N`` instances read the same element before advancing.
)");
uint32_t instanceDivisor = 1;
};
DOCUMENT("Describes a single Vulkan vertex buffer binding.")
+4
View File
@@ -214,6 +214,7 @@ enum
{
VkCheckExt_AMD_neg_viewport,
VkCheckExt_EXT_conserv_rast,
VkCheckExt_EXT_vertex_divisor,
VkCheckExt_Max,
};
@@ -624,6 +625,8 @@ DECLARE_REFLECTION_STRUCT(VkPipelineTessellationDomainOriginStateCreateInfoKHR);
DECLARE_REFLECTION_STRUCT(VkImageViewUsageCreateInfoKHR);
DECLARE_REFLECTION_STRUCT(VkInputAttachmentAspectReference);
DECLARE_REFLECTION_STRUCT(VkRenderPassInputAttachmentAspectCreateInfoKHR);
DECLARE_REFLECTION_STRUCT(VkVertexInputBindingDivisorDescriptionEXT);
DECLARE_REFLECTION_STRUCT(VkPipelineVertexInputDivisorStateCreateInfoEXT);
DECLARE_DESERIALISE_TYPE(VkDeviceCreateInfo);
DECLARE_DESERIALISE_TYPE(VkBufferCreateInfo);
@@ -667,6 +670,7 @@ DECLARE_DESERIALISE_TYPE(VkPipelineRasterizationConservativeStateCreateInfoEXT);
DECLARE_DESERIALISE_TYPE(VkPipelineTessellationDomainOriginStateCreateInfoKHR);
DECLARE_DESERIALISE_TYPE(VkImageViewUsageCreateInfoKHR);
DECLARE_DESERIALISE_TYPE(VkRenderPassInputAttachmentAspectCreateInfoKHR);
DECLARE_DESERIALISE_TYPE(VkPipelineVertexInputDivisorStateCreateInfoEXT);
DECLARE_REFLECTION_ENUM(VkFlagWithNoBits);
DECLARE_REFLECTION_ENUM(VkQueueFlagBits);
+3
View File
@@ -564,6 +564,9 @@ static const VkExtensionProperties supportedExtensions[] = {
{
VK_EXT_VALIDATION_FLAGS_EXTENSION_NAME, VK_EXT_VALIDATION_FLAGS_SPEC_VERSION,
},
{
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME, VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_SPEC_VERSION,
},
#ifdef VK_IMG_format_pvrtc
{
VK_IMG_FORMAT_PVRTC_EXTENSION_NAME, VK_IMG_FORMAT_PVRTC_SPEC_VERSION,
+2 -1
View File
@@ -324,7 +324,8 @@
CheckExt(KHR_bind_memory2, VK11); \
CheckExt(EXT_conservative_rasterization, VKXX); \
CheckExt(EXT_global_priority, VKXX); \
CheckExt(AMD_buffer_marker, VKXX);
CheckExt(AMD_buffer_marker, VKXX); \
CheckExt(EXT_vertex_attribute_divisor, VKXX);
#define HookInitVulkanInstanceExts() \
HookInitExtension(KHR_surface, DestroySurfaceKHR); \
+17
View File
@@ -205,6 +205,23 @@ void VulkanCreationInfo::Pipeline::Init(VulkanResourceManager *resourceMan, Vulk
vertexBindings[i].perInstance =
pCreateInfo->pVertexInputState->pVertexBindingDescriptions[i].inputRate ==
VK_VERTEX_INPUT_RATE_INSTANCE;
vertexBindings[i].instanceDivisor = 1;
}
// if there's a divisors struct, apply them now
const VkPipelineVertexInputDivisorStateCreateInfoEXT *divisors =
(const VkPipelineVertexInputDivisorStateCreateInfoEXT *)FindNextStruct(
pCreateInfo->pVertexInputState,
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT);
if(divisors)
{
for(uint32_t b = 0; b < divisors->vertexBindingDivisorCount; b++)
{
const VkVertexInputBindingDivisorDescriptionEXT &div = divisors->pVertexBindingDivisors[b];
if(div.binding < vertexBindings.size())
vertexBindings[div.binding].instanceDivisor = div.divisor;
}
}
vertexAttrs.resize(pCreateInfo->pVertexInputState->vertexAttributeDescriptionCount);
+3
View File
@@ -155,6 +155,9 @@ struct VulkanCreationInfo
uint32_t vbufferBinding;
uint32_t bytestride;
bool perInstance;
// VkVertexInputBindingDivisorDescriptionEXT
uint32_t instanceDivisor;
};
vector<Binding> vertexBindings;
+42 -12
View File
@@ -35,7 +35,7 @@ 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<bool> isInstanced,
const char *entryName, std::vector<uint32_t> instDivisor,
uint32_t &descSet, const DrawcallDescription *draw,
int32_t indexOffset, uint64_t numFetchVerts, uint32_t numVerts,
std::vector<uint32_t> &modSpirv, uint32_t &bufStride)
@@ -849,14 +849,41 @@ static void ConvertToMeshOutputCompute(const ShaderReflection &refl, const SPIRV
ops.push_back(SPIRVOperation(spv::OpImage, {tb.imageTypeID, rawimg, loaded}));
// vec4 result = texelFetch(rawimg, vtxID or instID);
uint32_t idx = location < isInstanced.size() && isInstanced[location] ? instanceLookup
: vertexLookup;
uint32_t idx = vertexLookup;
if(location < instDivisor.size())
{
uint32_t divisor = instDivisor[location];
if(divisor == ~0U)
{
// this magic value indicates vertex-rate data
idx = vertexLookup;
}
else if(divisor == 0)
{
// if the divisor is 0, all instances read the first value.
idx = editor.AddConstantImmediate<int32_t>(0);
}
else if(divisor == 1)
{
// if the divisor is 1, it's just regular instancing
idx = instanceLookup;
}
else
{
// otherwise we divide by the divisor
idx = editor.MakeId();
divisor = editor.AddConstantImmediate(int32_t(divisor & 0x7fffffff));
ops.push_back(SPIRVOperation(spv::OpSDiv, {sint32ID, idx, instanceLookup, divisor}));
}
}
uint32_t result = editor.MakeId();
ops.push_back(SPIRVOperation(spv::OpImageFetch, {ins[i].vec4ID, result, rawimg, idx}));
// for one component, extract x, for less than 4, extract the sub-vector, otherwise
// leave
// alone (4 components)
// leave alone (4 components)
if(refl.inputSignature[i].compCount == 1)
{
uint32_t swizzleIn = result;
@@ -1240,7 +1267,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
VkBufferView view;
};
std::vector<bool> attrIsInstanced;
std::vector<uint32_t> attrInstDivisor;
CompactedAttrBuffer vbuffers[64];
RDCEraseEl(vbuffers);
@@ -1292,7 +1319,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
continue;
}
bool isInstanced = false;
uint32_t instDivisor = ~0U;
size_t stride = 1;
const byte *origVBBegin = NULL;
@@ -1306,7 +1333,10 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
origVBBegin = origVBs[vb].data() + attrDesc.offset;
origVBEnd = origVBs[vb].data() + origVBs[vb].size();
stride = vbDesc.stride;
isInstanced = (vbDesc.inputRate == VK_VERTEX_INPUT_RATE_INSTANCE);
if(vbDesc.inputRate == VK_VERTEX_INPUT_RATE_INSTANCE)
instDivisor = pipeInfo.vertexBindings[vbDesc.binding].instanceDivisor;
else
instDivisor = ~0U;
break;
}
}
@@ -1360,7 +1390,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT,
};
if(isInstanced)
if(instDivisor != ~0U)
bufInfo.size = elemSize * (maxInstance + 1);
vkr = m_pDriver->vkCreateBuffer(dev, &bufInfo, NULL, &vbuffers[attr].buf);
@@ -1505,8 +1535,8 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
m_pDriver->vkCreateBufferView(dev, &info, NULL, &vbuffers[attr].view);
attrIsInstanced.resize(RDCMAX(attrIsInstanced.size(), size_t(attr + 1)));
attrIsInstanced[attr] = isInstanced;
attrInstDivisor.resize(RDCMAX(attrInstDivisor.size(), size_t(attr + 1)));
attrInstDivisor[attr] = instDivisor;
descWrites[numWrites].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
descWrites[numWrites].dstSet = m_MeshFetchDescSet;
@@ -1540,7 +1570,7 @@ void VulkanReplay::InitPostVSBuffers(uint32_t eventId)
}
ConvertToMeshOutputCompute(*refl, *pipeInfo.shaders[0].patchData,
pipeInfo.shaders[0].entryPoint.c_str(), attrIsInstanced, descSet,
pipeInfo.shaders[0].entryPoint.c_str(), attrInstDivisor, descSet,
drawcall, baseVertex, numFetchVerts, numVerts, modSpirv, bufStride);
VkComputePipelineCreateInfo compPipeInfo = {VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
+2
View File
@@ -874,6 +874,8 @@ void VulkanReplay::SavePipelineState()
m_VulkanPipelineState.vertexInput.bindings[i].vertexBufferBinding =
p.vertexBindings[i].vbufferBinding;
m_VulkanPipelineState.vertexInput.bindings[i].perInstance = p.vertexBindings[i].perInstance;
m_VulkanPipelineState.vertexInput.bindings[i].instanceDivisor =
p.vertexBindings[i].instanceDivisor;
}
m_VulkanPipelineState.vertexInput.vertexBuffers.resize(state.vbuffers.size());
+31 -1
View File
@@ -178,7 +178,11 @@ SERIALISE_VK_HANDLES();
VkPipelineTessellationDomainOriginStateCreateInfoKHR) \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_IMAGE_VIEW_USAGE_CREATE_INFO_KHR, VkImageViewUsageCreateInfoKHR) \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_RENDER_PASS_INPUT_ATTACHMENT_ASPECT_CREATE_INFO_KHR, \
VkRenderPassInputAttachmentAspectCreateInfoKHR)
VkRenderPassInputAttachmentAspectCreateInfoKHR) \
\
/* VK_EXT_vertex_attribute_divisor */ \
PNEXT_STRUCT(VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT, \
VkPipelineVertexInputDivisorStateCreateInfoEXT)
template <typename SerialiserType>
static void SerialiseNext(SerialiserType &ser, VkStructureType &sType, const void *&pNext)
@@ -2301,6 +2305,31 @@ void Deserialise(const VkRenderPassInputAttachmentAspectCreateInfoKHR &el)
delete[] el.pAspectReferences;
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VkVertexInputBindingDivisorDescriptionEXT &el)
{
SERIALISE_MEMBER(binding);
SERIALISE_MEMBER(divisor);
}
template <typename SerialiserType>
void DoSerialise(SerialiserType &ser, VkPipelineVertexInputDivisorStateCreateInfoEXT &el)
{
RDCASSERT(ser.IsReading() ||
el.sType == VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT);
SerialiseNext(ser, el.sType, el.pNext);
SERIALISE_MEMBER(vertexBindingDivisorCount);
SERIALISE_MEMBER_ARRAY(pVertexBindingDivisors, vertexBindingDivisorCount);
}
template <>
void Deserialise(const VkPipelineVertexInputDivisorStateCreateInfoEXT &el)
{
DeserialiseNext(el.pNext);
delete[] el.pVertexBindingDivisors;
}
INSTANTIATE_SERIALISE_TYPE(VkOffset2D);
INSTANTIATE_SERIALISE_TYPE(VkExtent2D);
INSTANTIATE_SERIALISE_TYPE(VkMemoryType);
@@ -2402,6 +2431,7 @@ INSTANTIATE_SERIALISE_TYPE(VkDescriptorUpdateTemplateCreateInfoKHR);
INSTANTIATE_SERIALISE_TYPE(VkBindBufferMemoryInfoKHR);
INSTANTIATE_SERIALISE_TYPE(VkBindImageMemoryInfoKHR);
INSTANTIATE_SERIALISE_TYPE(VkPipelineRasterizationConservativeStateCreateInfoEXT);
INSTANTIATE_SERIALISE_TYPE(VkPipelineVertexInputDivisorStateCreateInfoEXT);
INSTANTIATE_SERIALISE_TYPE(DescriptorSetSlot);
INSTANTIATE_SERIALISE_TYPE(ImageRegionState);
@@ -356,6 +356,25 @@ void VulkanShaderCache::MakeGraphicsPipelineInfo(VkGraphicsPipelineCreateInfo &p
: VK_VERTEX_INPUT_RATE_VERTEX;
}
static VkPipelineVertexInputDivisorStateCreateInfoEXT vertexDivisor = {
VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT,
};
static VkVertexInputBindingDivisorDescriptionEXT vibindDivisors[128] = {};
if(m_pDriver->m_ExtensionsEnabled[VkCheckExt_EXT_vertex_divisor])
{
vertexDivisor.pVertexBindingDivisors = vibindDivisors;
vertexDivisor.vertexBindingDivisorCount = vi.vertexBindingDescriptionCount;
for(uint32_t i = 0; i < vi.vertexBindingDescriptionCount; i++)
{
vibindDivisors[i].binding = i;
vibindDivisors[i].divisor = pipeInfo.vertexBindings[i].instanceDivisor;
}
vi.pNext = &vertexDivisor;
}
RDCASSERT(ARRAY_COUNT(viattr) >= pipeInfo.vertexAttrs.size());
RDCASSERT(ARRAY_COUNT(vibind) >= pipeInfo.vertexBindings.size());
@@ -989,6 +989,10 @@ bool WrappedVulkan::Serialise_vkCreateDevice(SerialiserType &ser, VkPhysicalDevi
VK_EXT_CONSERVATIVE_RASTERIZATION_EXTENSION_NAME) != Extensions.end())
m_ExtensionsEnabled[VkCheckExt_EXT_conserv_rast] = true;
if(std::find(Extensions.begin(), Extensions.end(),
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME) != Extensions.end())
m_ExtensionsEnabled[VkCheckExt_EXT_vertex_divisor] = true;
std::vector<string> Layers;
for(uint32_t i = 0; i < createInfo.enabledLayerCount; i++)
Layers.push_back(createInfo.ppEnabledLayerNames[i]);
+2 -1
View File
@@ -1819,8 +1819,9 @@ void DoSerialise(SerialiserType &ser, VKPipe::VertexBinding &el)
SERIALISE_MEMBER(vertexBufferBinding);
SERIALISE_MEMBER(byteStride);
SERIALISE_MEMBER(perInstance);
SERIALISE_MEMBER(instanceDivisor);
SIZE_CHECK(12);
SIZE_CHECK(16);
}
template <typename SerialiserType>