diff --git a/src/dxvk/dxvk_buffer.cpp b/src/dxvk/dxvk_buffer.cpp index 240e8d00d..925723a1f 100644 --- a/src/dxvk/dxvk_buffer.cpp +++ b/src/dxvk/dxvk_buffer.cpp @@ -7,21 +7,50 @@ namespace dxvk { DxvkDevice* device, const DxvkBufferCreateInfo& createInfo, VkMemoryPropertyFlags memoryType) - : m_device (device), - m_info (createInfo), - m_memFlags(memoryType), - m_resource(allocateResource()) { + : m_device (device), + m_info (createInfo), + m_memFlags (memoryType) { + // Initialize a single backing bufer with one slice + m_physBuffers[0] = this->allocPhysicalBuffer(1); + m_physSlice = this->allocPhysicalSlice(); + } + + + void DxvkBuffer::rename( + const DxvkPhysicalBufferSlice& slice) { + m_physSlice = slice; + } + + + DxvkPhysicalBufferSlice DxvkBuffer::allocPhysicalSlice() { + if (m_physSliceId >= m_physBuffers[m_physBufferId]->sliceCount()) { + m_physBufferId = (m_physBufferId + 1) % m_physBuffers.size(); + m_physSliceId = 0; + + if ((m_physBuffers[m_physBufferId] == nullptr) + || (m_physBuffers[m_physBufferId]->sliceCount() < m_physSliceCount)) { + // Make sure that all buffers have the same size. If we don't do this, + // one of the physical buffers may grow indefinitely while the others + // remain small, depending on the usage pattern of the application. + m_physBuffers[m_physBufferId] = this->allocPhysicalBuffer(m_physSliceCount); + } else if (m_physBuffers[m_physBufferId]->isInUse()) { + // Allocate a new physical buffer if the current one is still in use. + // This also indicates that the buffer gets updated frequently, so we + // will double the size of the physical buffers to accomodate for it. + if (m_physBufferId == 0) + m_physSliceCount *= 2; + + m_physBuffers[m_physBufferId] = this->allocPhysicalBuffer(m_physSliceCount); + } + } + return m_physBuffers[m_physBufferId]->slice(m_physSliceId++); } - void DxvkBuffer::renameResource(const DxvkPhysicalBufferSlice& resource) { - m_resource = resource; - } - - - DxvkPhysicalBufferSlice DxvkBuffer::allocateResource() { - return m_device->allocBufferResource(m_info, m_memFlags)->slice(0); + Rc DxvkBuffer::allocPhysicalBuffer(VkDeviceSize sliceCount) const { + TRACE(this, sliceCount); + return m_device->allocPhysicalBuffer(m_info, sliceCount, m_memFlags); } diff --git a/src/dxvk/dxvk_buffer.h b/src/dxvk/dxvk_buffer.h index f37da4c28..40b9b9833 100644 --- a/src/dxvk/dxvk_buffer.h +++ b/src/dxvk/dxvk_buffer.h @@ -49,7 +49,7 @@ namespace dxvk { * \returns Pointer to mapped memory region */ void* mapPtr(VkDeviceSize offset) const { - return m_resource.mapPtr(offset); + return m_physSlice.mapPtr(offset); } /** @@ -62,7 +62,7 @@ namespace dxvk { * \returns \c true if the buffer is in use */ bool isInUse() const { - return m_resource.resource()->isInUse(); + return m_physSlice.resource()->isInUse(); } /** @@ -72,27 +72,9 @@ namespace dxvk { * \returns The resource object */ Rc resource() const { - return m_resource.resource(); + return m_physSlice.resource(); } - /** - * \brief Replaces backing resource - * - * Replaces the underlying buffer and implicitly marks - * any buffer views using this resource as dirty. Do - * not call this directly as this is called implicitly - * by the context's \c invalidateBuffer method. - * \param [in] resource The new backing resource - */ - void renameResource( - const DxvkPhysicalBufferSlice& resource); - - /** - * \brief Allocates new backing resource - * \returns The new buffer - */ - DxvkPhysicalBufferSlice allocateResource(); - /** * \brief Physical buffer slice * @@ -101,7 +83,7 @@ namespace dxvk { * \returns The backing slice */ DxvkPhysicalBufferSlice slice() const { - return m_resource; + return m_physSlice; } /** @@ -113,16 +95,48 @@ namespace dxvk { * \returns The sub slice */ DxvkPhysicalBufferSlice subSlice(VkDeviceSize offset, VkDeviceSize length) const { - return m_resource.subSlice(offset, length); + return m_physSlice.subSlice(offset, length); } + /** + * \brief Replaces backing resource + * + * Replaces the underlying buffer and implicitly marks + * any buffer views using this resource as dirty. Do + * not call this directly as this is called implicitly + * by the context's \c invalidateBuffer method. + * \param [in] slice The new backing resource + */ + void rename( + const DxvkPhysicalBufferSlice& slice); + + /** + * \brief Allocates new physical resource + * + * This method must not be called from multiple threads + * simultaneously, but it can be called in parallel with + * \ref rename and other methods of this class. + * \returns The new backing buffer slice + */ + DxvkPhysicalBufferSlice allocPhysicalSlice(); + private: DxvkDevice* m_device; DxvkBufferCreateInfo m_info; VkMemoryPropertyFlags m_memFlags; + DxvkPhysicalBufferSlice m_physSlice; - DxvkPhysicalBufferSlice m_resource; + // TODO maybe align this to a cache line in order + // to avoid false sharing once CSMT is implemented + VkDeviceSize m_physBufferId = 0; + VkDeviceSize m_physSliceId = 0; + VkDeviceSize m_physSliceCount = 1; + + std::array, 2> m_physBuffers; + + Rc allocPhysicalBuffer( + VkDeviceSize sliceCount) const; }; diff --git a/src/dxvk/dxvk_buffer_res.cpp b/src/dxvk/dxvk_buffer_res.cpp index c666a5ba5..fd69f1031 100644 --- a/src/dxvk/dxvk_buffer_res.cpp +++ b/src/dxvk/dxvk_buffer_res.cpp @@ -8,9 +8,10 @@ namespace dxvk { VkDeviceSize sliceCount, DxvkMemoryAllocator& memAlloc, VkMemoryPropertyFlags memFlags) - : m_vkd(vkd), - m_sliceLength(createInfo.size), - m_sliceStride(align(createInfo.size, 256)) { + : m_vkd (vkd), + m_sliceCount (sliceCount), + m_sliceLength (createInfo.size), + m_sliceStride (align(createInfo.size, 256)) { VkBufferCreateInfo info; info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; diff --git a/src/dxvk/dxvk_buffer_res.h b/src/dxvk/dxvk_buffer_res.h index d3b050ccd..8d3703c41 100644 --- a/src/dxvk/dxvk_buffer_res.h +++ b/src/dxvk/dxvk_buffer_res.h @@ -78,6 +78,14 @@ namespace dxvk { return m_handle; } + /** + * \brief Number of slices + * \returns Total slice count + */ + VkDeviceSize sliceCount() const { + return m_sliceCount; + } + /** * \brief Map pointer * @@ -108,6 +116,7 @@ namespace dxvk { DxvkMemory m_memory; VkBuffer m_handle; + VkDeviceSize m_sliceCount; VkDeviceSize m_sliceLength; VkDeviceSize m_sliceStride; diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 9593ff60b..c467c9011 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -541,8 +541,7 @@ namespace dxvk { void DxvkContext::invalidateBuffer(const Rc& buffer) { // Allocate new backing resource - buffer->renameResource( - buffer->allocateResource()); + buffer->rename(buffer->allocPhysicalSlice()); // We also need to update all bindings that the buffer // may be bound to either directly or through views. diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index fddbdc269..404f80207 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -37,11 +37,12 @@ namespace dxvk { } - Rc DxvkDevice::allocBufferResource( + Rc DxvkDevice::allocPhysicalBuffer( const DxvkBufferCreateInfo& createInfo, + VkDeviceSize sliceCount, VkMemoryPropertyFlags memoryType) { return new DxvkPhysicalBuffer(m_vkd, - createInfo, 1, *m_memory, memoryType); + createInfo, sliceCount, *m_memory, memoryType); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 04bcb2c30..d81f93be7 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -101,14 +101,16 @@ namespace dxvk { } /** - * \brief Allocates buffer resource + * \brief Allocates a physical buffer * * \param [in] createInfo Buffer create info + * \param [in] sliceCount Buffer slice count * \param [in] memoryType Memory property flags * \returns The buffer resource object */ - Rc allocBufferResource( + Rc allocPhysicalBuffer( const DxvkBufferCreateInfo& createInfo, + VkDeviceSize sliceCount, VkMemoryPropertyFlags memoryType); /**