diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 2fbaf92f9..19efd460b 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -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 buffers; std::array offsets; std::array lengths; + std::array 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(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); } diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index 5f4528124..439170267 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -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); - } } /** diff --git a/src/dxvk/dxvk_context_state.h b/src/dxvk/dxvk_context_state.h index 8fffb5545..c80f93817 100644 --- a/src/dxvk/dxvk_context_state.h +++ b/src/dxvk/dxvk_context_state.h @@ -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 vertexBuffers = { }; std::array vertexStrides = { }; + std::array vertexExtents = { }; }; diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 34ceaea09..16fcf34a9 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -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 dynamicStates; + std::array 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; diff --git a/src/dxvk/dxvk_graphics.h b/src/dxvk/dxvk_graphics.h index 21631d3f4..446c90f38 100644 --- a/src/dxvk/dxvk_graphics.h +++ b/src/dxvk/dxvk_graphics.h @@ -43,6 +43,8 @@ namespace dxvk { std::array viDivisors = { }; std::array viAttributes = { }; + bool useDynamicVertexStrides() const; + bool eq(const DxvkGraphicsPipelineVertexInputState& other) const; size_t hash() const; diff --git a/src/dxvk/dxvk_graphics_state.h b/src/dxvk/dxvk_graphics_state.h index 25fd921a2..1906848ec 100644 --- a/src/dxvk/dxvk_graphics_state.h +++ b/src/dxvk/dxvk_graphics_state.h @@ -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;