[dxvk] Use dynamic vertex strides whenever possible

May reduce the number of pipeline permutations, as well as the overhead
of the bindVertexBuffer call.
This commit is contained in:
Philip Rebohle 2022-07-15 12:18:51 +02:00
parent fc525d5b70
commit 35fad0aa6c
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
6 changed files with 94 additions and 24 deletions

View File

@ -2392,25 +2392,30 @@ namespace dxvk {
m_flags.set(
DxvkContextFlag::GpDirtyPipelineState,
DxvkContextFlag::GpDirtyVertexBuffers);
for (uint32_t i = 0; i < bindingCount; i++) {
m_state.gp.state.ilBindings[i] = DxvkIlBinding(
bindings[i].binding, 0, bindings[i].inputRate,
bindings[i].fetchRate);
m_state.vi.vertexExtents[bindings[i].binding] = 0;
}
for (uint32_t i = bindingCount; i < m_state.gp.state.il.bindingCount(); i++)
m_state.gp.state.ilBindings[i] = DxvkIlBinding();
for (uint32_t i = 0; i < attributeCount; i++) {
m_state.gp.state.ilAttributes[i] = DxvkIlAttribute(
attributes[i].location, attributes[i].binding,
attributes[i].format, attributes[i].offset);
uint32_t extent = attributes[i].offset + lookupFormatInfo(attributes[i].format)->elementSize;
m_state.vi.vertexExtents[attributes[i].binding] = std::max(extent,
m_state.vi.vertexExtents[attributes[i].binding]);
}
for (uint32_t i = attributeCount; i < m_state.gp.state.il.attributeCount(); i++)
m_state.gp.state.ilAttributes[i] = DxvkIlAttribute();
for (uint32_t i = 0; i < bindingCount; i++) {
m_state.gp.state.ilBindings[i] = DxvkIlBinding(
bindings[i].binding, 0, bindings[i].inputRate,
bindings[i].fetchRate);
}
for (uint32_t i = bindingCount; i < m_state.gp.state.il.bindingCount(); i++)
m_state.gp.state.ilBindings[i] = DxvkIlBinding();
m_state.gp.state.il = DxvkIlInfo(attributeCount, bindingCount);
}
@ -4484,12 +4489,6 @@ namespace dxvk {
bool DxvkContext::updateGraphicsPipelineState(DxvkGlobalPipelineBarrier srcBarrier) {
bool oldIndependentSets = m_flags.test(DxvkContextFlag::GpIndependentSets);
// Set up vertex buffer strides for active bindings
for (uint32_t i = 0; i < m_state.gp.state.il.bindingCount(); i++) {
const uint32_t binding = m_state.gp.state.ilBindings[i].binding();
m_state.gp.state.ilBindings[i].setStride(m_state.vi.vertexStrides[binding]);
}
// Check which dynamic states need to be active. States that
// are not dynamic will be invalidated in the command buffer.
m_flags.clr(DxvkContextFlag::GpDynamicBlendConstants,
@ -5074,7 +5073,11 @@ namespace dxvk {
std::array<VkBuffer, MaxNumVertexBindings> buffers;
std::array<VkDeviceSize, MaxNumVertexBindings> offsets;
std::array<VkDeviceSize, MaxNumVertexBindings> lengths;
std::array<VkDeviceSize, MaxNumVertexBindings> strides;
bool oldDynamicStrides = m_flags.test(DxvkContextFlag::GpDynamicVertexStrides);
bool newDynamicStrides = true;
// Set buffer handles and offsets for active bindings
for (uint32_t i = 0; i < m_state.gp.state.il.bindingCount(); i++) {
uint32_t binding = m_state.gp.state.ilBindings[i].binding();
@ -5085,20 +5088,48 @@ namespace dxvk {
buffers[i] = vbo.buffer.buffer;
offsets[i] = vbo.buffer.offset;
lengths[i] = vbo.buffer.range;
strides[i] = m_state.vi.vertexStrides[binding];
if (strides[i]) {
// Dynamic strides are only allowed if the stride is not smaller
// than highest attribute offset + format size for given binding
newDynamicStrides &= strides[i] >= m_state.vi.vertexExtents[binding];
}
if (m_vbTracked.set(binding))
m_cmd->trackResource<DxvkAccess::Read>(m_state.vi.vertexBuffers[binding].buffer());
} else {
buffers[i] = VK_NULL_HANDLE;
offsets[i] = 0;
lengths[i] = 0;
strides[i] = 0;
}
}
// If vertex strides are static or if we are switching between static or
// dynamic strides, we'll have to apply them to the pipeline state and
// also sort out our state flags
if (unlikely(!oldDynamicStrides) || unlikely(!newDynamicStrides)) {
m_flags.clr(DxvkContextFlag::GpDynamicVertexStrides);
for (uint32_t i = 0; i < m_state.gp.state.il.bindingCount(); i++) {
uint32_t stride = newDynamicStrides ? 0 : strides[i];
if (m_state.gp.state.ilBindings[i].stride() != stride) {
m_state.gp.state.ilBindings[i].setStride(stride);
m_flags.set(DxvkContextFlag::GpDirtyPipelineState);
}
}
if (newDynamicStrides)
m_flags.set(DxvkContextFlag::GpDynamicVertexStrides);
}
// Vertex bindigs get remapped when compiling the
// pipeline, so this actually does the right thing
m_cmd->cmdBindVertexBuffers(0, m_state.gp.state.il.bindingCount(),
buffers.data(), offsets.data(), lengths.data(), nullptr);
buffers.data(), offsets.data(), lengths.data(),
newDynamicStrides ? strides.data() : nullptr);
}

View File

@ -252,12 +252,8 @@ namespace dxvk {
m_vbTracked.clr(binding);
m_state.vi.vertexBuffers[binding] = buffer;
m_state.vi.vertexStrides[binding] = stride;
m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers);
if (unlikely(m_state.vi.vertexStrides[binding] != stride)) {
m_state.vi.vertexStrides[binding] = stride;
m_flags.set(DxvkContextFlag::GpDirtyPipelineState);
}
}
/**

View File

@ -41,6 +41,7 @@ namespace dxvk {
GpDynamicDepthBounds, ///< Depth bounds are dynamic
GpDynamicStencilRef, ///< Stencil reference is dynamic
GpDynamicRasterizerState, ///< Cull mode and front face are dynamic
GpDynamicVertexStrides, ///< Vertex buffer strides are dynamic
GpIndependentSets, ///< Graphics pipeline layout was created with independent sets
CpDirtyPipeline, ///< Compute pipeline binding are out of date
@ -89,6 +90,7 @@ namespace dxvk {
std::array<DxvkBufferSlice, DxvkLimits::MaxNumVertexBindings> vertexBuffers = { };
std::array<uint32_t, DxvkLimits::MaxNumVertexBindings> vertexStrides = { };
std::array<uint32_t, DxvkLimits::MaxNumVertexBindings> vertexExtents = { };
};

View File

@ -67,6 +67,21 @@ namespace dxvk {
}
bool DxvkGraphicsPipelineVertexInputState::useDynamicVertexStrides() const {
if (!viInfo.vertexBindingDescriptionCount)
return false;
// The backend will set all strides to 0 if dynamic strides are
// allowed, since the restrictions only apply to non-zero strides
bool dynamicStride = true;
for (uint32_t i = 0; i < viInfo.vertexBindingDescriptionCount && dynamicStride; i++)
dynamicStride = !viBindings[i].stride;
return dynamicStride;
}
bool DxvkGraphicsPipelineVertexInputState::eq(const DxvkGraphicsPipelineVertexInputState& other) const {
bool eq = iaInfo.topology == other.iaInfo.topology
&& iaInfo.primitiveRestartEnable == other.iaInfo.primitiveRestartEnable
@ -141,6 +156,14 @@ namespace dxvk {
: m_device(device) {
auto vk = m_device->vkd();
VkDynamicState dynamicState = VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE;
VkPipelineDynamicStateCreateInfo dyInfo = { VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO };
if (state.useDynamicVertexStrides()) {
dyInfo.dynamicStateCount = 1;
dyInfo.pDynamicStates = &dynamicState;
}
VkGraphicsPipelineLibraryCreateInfoEXT libInfo = { VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_LIBRARY_CREATE_INFO_EXT };
libInfo.flags = VK_GRAPHICS_PIPELINE_LIBRARY_VERTEX_INPUT_INTERFACE_BIT_EXT;
@ -148,6 +171,7 @@ namespace dxvk {
info.flags = VK_PIPELINE_CREATE_LIBRARY_BIT_KHR;
info.pVertexInputState = &state.viInfo;
info.pInputAssemblyState = &state.iaInfo;
info.pDynamicState = &dyInfo;
info.basePipelineIndex = -1;
VkResult vr = vk->vkCreateGraphicsPipelines(vk->device(),
@ -766,12 +790,15 @@ namespace dxvk {
auto vk = m_device->vkd();
// Set up dynamic states as needed
std::array<VkDynamicState, 8> dynamicStates;
std::array<VkDynamicState, 9> dynamicStates;
uint32_t dynamicStateCount = 0;
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT_WITH_COUNT;
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR_WITH_COUNT;
if (state.useDynamicVertexStrides())
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE;
if (state.useDynamicDepthBias())
dynamicStates[dynamicStateCount++] = VK_DYNAMIC_STATE_DEPTH_BIAS;

View File

@ -43,6 +43,8 @@ namespace dxvk {
std::array<VkVertexInputBindingDivisorDescriptionEXT, MaxNumVertexBindings> viDivisors = { };
std::array<VkVertexInputAttributeDescription, MaxNumVertexAttributes> viAttributes = { };
bool useDynamicVertexStrides() const;
bool eq(const DxvkGraphicsPipelineVertexInputState& other) const;
size_t hash() const;

View File

@ -731,6 +731,18 @@ namespace dxvk {
return ds.enableDepthBoundsTest();
}
bool useDynamicVertexStrides() const {
if (!il.bindingCount())
return false;
bool result = true;
for (uint32_t i = 0; i < il.bindingCount() && result; i++)
result = !ilBindings[i].stride();
return result;
}
bool useDynamicBlendConstants() const {
bool result = false;