From 0d40c20aef8ea084ab059a531cb7412089ae2a0c Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Wed, 8 May 2019 03:37:49 +0200 Subject: [PATCH] [dxvk] Compact vertex buffer bindings This way, we can always update all vertex buffer bindings with one single API call, without having to deal with any gaps in binding IDs. The previous optimization triggers a bug in some drivers when no vertex buffer is ever bound to a given binding point, and may also trigger inefficient behaviour if the binding range is assumed to be contiguous. --- src/dxvk/dxvk_context.cpp | 35 ++++++++++++----------------------- src/dxvk/dxvk_graphics.cpp | 20 ++++++++++++++++++-- 2 files changed, 30 insertions(+), 25 deletions(-) diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 1c91992ab..9f9a4c251 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -3672,46 +3672,35 @@ namespace dxvk { void DxvkContext::updateVertexBufferBindings() { if (m_flags.test(DxvkContextFlag::GpDirtyVertexBuffers)) { m_flags.clr(DxvkContextFlag::GpDirtyVertexBuffers); + + if (unlikely(!m_state.gp.state.ilBindingCount)) + return; std::array buffers; std::array offsets; // Set buffer handles and offsets for active bindings - uint32_t bindingMask = 0; - for (uint32_t i = 0; i < m_state.gp.state.ilBindingCount; i++) { uint32_t binding = m_state.gp.state.ilBindings[i].binding; - bindingMask |= 1u << binding; if (likely(m_state.vi.vertexBuffers[binding].defined())) { auto vbo = m_state.vi.vertexBuffers[binding].getDescriptor(); - buffers[binding] = vbo.buffer.buffer; - offsets[binding] = vbo.buffer.offset; + buffers[i] = vbo.buffer.buffer; + offsets[i] = vbo.buffer.offset; m_cmd->trackResource(m_state.vi.vertexBuffers[binding].buffer()); } else { - buffers[binding] = m_device->dummyBufferHandle(); - offsets[binding] = 0; + buffers[i] = m_device->dummyBufferHandle(); + offsets[i] = 0; } } - // Actually bind all the vertex buffers. - uint32_t bindingIndex = 0; - - while (bindingMask) { - uint32_t shift = bit::tzcnt(bindingMask); - uint32_t count = bit::tzcnt(~bindingMask >> shift); - uint32_t index = bindingIndex + shift; - - m_cmd->cmdBindVertexBuffers( - index, count, - &buffers[index], - &offsets[index]); - - bindingIndex += shift + count; - bindingMask >>= shift + count; - } + // Vertex bindigs get remapped when compiling the + // pipeline, so this actually does the right thing + m_cmd->cmdBindVertexBuffers( + 0, m_state.gp.state.ilBindingCount, + buffers.data(), offsets.data()); } } diff --git a/src/dxvk/dxvk_graphics.cpp b/src/dxvk/dxvk_graphics.cpp index 23784a65f..d50fb209f 100644 --- a/src/dxvk/dxvk_graphics.cpp +++ b/src/dxvk/dxvk_graphics.cpp @@ -258,6 +258,22 @@ namespace dxvk { int32_t rasterizedStream = m_gs != nullptr ? m_gs->shaderOptions().rasterizedStream : 0; + + // Compact vertex bindings so that we can more easily update vertex buffers + std::array viAttribs; + std::array viBindings; + std::array viBindingMap = { }; + + for (uint32_t i = 0; i < state.ilBindingCount; i++) { + viBindings[i] = state.ilBindings[i]; + viBindings[i].binding = i; + viBindingMap[state.ilBindings[i].binding] = i; + } + + for (uint32_t i = 0; i < state.ilAttributeCount; i++) { + viAttribs[i] = state.ilAttributes[i]; + viAttribs[i].binding = viBindingMap[state.ilAttributes[i].binding]; + } VkPipelineVertexInputDivisorStateCreateInfoEXT viDivisorInfo; viDivisorInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_DIVISOR_STATE_CREATE_INFO_EXT; @@ -270,9 +286,9 @@ namespace dxvk { viInfo.pNext = &viDivisorInfo; viInfo.flags = 0; viInfo.vertexBindingDescriptionCount = state.ilBindingCount; - viInfo.pVertexBindingDescriptions = state.ilBindings; + viInfo.pVertexBindingDescriptions = viBindings.data(); viInfo.vertexAttributeDescriptionCount = state.ilAttributeCount; - viInfo.pVertexAttributeDescriptions = state.ilAttributes; + viInfo.pVertexAttributeDescriptions = viAttribs.data(); if (viDivisorCount == 0) viInfo.pNext = viDivisorInfo.pNext;