mirror of
https://github.com/doitsujin/dxvk.git
synced 2025-01-19 05:52:11 +01:00
[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:
parent
fc525d5b70
commit
35fad0aa6c
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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 = { };
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user