From d3b21741804a7747fc32a462c12bd9709424dd02 Mon Sep 17 00:00:00 2001 From: Philip Rebohle Date: Sat, 16 Dec 2017 13:21:11 +0100 Subject: [PATCH] [dxvk] Implemented buffer renaming --- src/d3d11/d3d11_buffer.cpp | 52 ++++++------------- src/dxvk/dxvk_buffer.cpp | 23 ++++++-- src/dxvk/dxvk_buffer.h | 104 ++++++++++++++++++++++++++++++------- src/dxvk/dxvk_context.cpp | 36 ++++++++++--- src/dxvk/dxvk_context.h | 19 ++++++- src/dxvk/dxvk_device.cpp | 16 +++--- src/dxvk/dxvk_device.h | 11 ++++ 7 files changed, 189 insertions(+), 72 deletions(-) diff --git a/src/d3d11/d3d11_buffer.cpp b/src/d3d11/d3d11_buffer.cpp index 755a72ce6..6ccfe851a 100644 --- a/src/d3d11/d3d11_buffer.cpp +++ b/src/d3d11/d3d11_buffer.cpp @@ -78,47 +78,27 @@ namespace dxvk { if (pMappedSubresource == nullptr) return S_OK; - if (!buffer->isInUse()) { - // Simple case: The buffer is currently not being - // used by the device, we can return the pointer. - pMappedSubresource->pData = buffer->mapPtr(0); - pMappedSubresource->RowPitch = buffer->info().size; - pMappedSubresource->DepthPitch = buffer->info().size; - return S_OK; - } else { + if (buffer->isInUse()) { // Don't wait if the application tells us not to if (MapFlags & D3D11_MAP_FLAG_DO_NOT_WAIT) return DXGI_ERROR_WAS_STILL_DRAWING; - // TODO optimize this. In order to properly cover common use cases - // like frequent constant buffer updates, we must implement buffer - // renaming techniques. The current approach is inefficient as it - // leads to a lot of Flush() and Synchronize() calls. - // - // Possible solution: - // (1) Create buffers with a significantly larger size if they - // can be mapped by the host for writing. If mapping the - // buffer would stall on D3D11_MAP_WRITE_DISCARD, map the - // next slice. on D3D11_MAP_WRITE_NO_OVERWRITE, return the - // current slice. If the buffer is bound, update bindings. - // (2) If no more slices are available, create a new buffer. - // Limit the number of buffers to a small, fixed number. - // (3) If no more buffers are available, flush and synchronize. - // (4) When renaming the buffer internally, all active bindings - // need to be updated internally as well. - // - // In order to support deferred contexts, the immediate context - // must commit all changes to the initial buffer slice prior to - // executing a command list. When mapping on deferred contexts, - // the deferred context shall create local buffer objects. - pContext->Flush(); - pContext->Synchronize(); - - pMappedSubresource->pData = buffer->mapPtr(0); - pMappedSubresource->RowPitch = buffer->info().size; - pMappedSubresource->DepthPitch = buffer->info().size; - return S_OK; + // Invalidate the buffer in order to avoid synchronization + // if the application does not need the buffer contents to + // be preserved. The No Overwrite mode does not require any + // sort of synchronization, but should be used with care. + if (MapType == D3D11_MAP_WRITE_DISCARD) { + pContext->GetDXVKContext()->invalidateBuffer(m_buffer); + } else if (MapType != D3D11_MAP_WRITE_NO_OVERWRITE) { + pContext->Flush(); + pContext->Synchronize(); + } } + + pMappedSubresource->pData = buffer->mapPtr(0); + pMappedSubresource->RowPitch = buffer->info().size; + pMappedSubresource->DepthPitch = buffer->info().size; + return S_OK; } diff --git a/src/dxvk/dxvk_buffer.cpp b/src/dxvk/dxvk_buffer.cpp index 7736182f0..9940f3581 100644 --- a/src/dxvk/dxvk_buffer.cpp +++ b/src/dxvk/dxvk_buffer.cpp @@ -1,13 +1,14 @@ #include "dxvk_buffer.h" +#include "dxvk_device.h" namespace dxvk { - DxvkBuffer::DxvkBuffer( + DxvkBufferResource::DxvkBufferResource( const Rc& vkd, const DxvkBufferCreateInfo& createInfo, DxvkMemoryAllocator& memAlloc, VkMemoryPropertyFlags memFlags) - : m_vkd(vkd), m_info(createInfo) { + : m_vkd(vkd) { VkBufferCreateInfo info; info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; @@ -34,12 +35,28 @@ namespace dxvk { } - DxvkBuffer::~DxvkBuffer() { + DxvkBufferResource::~DxvkBufferResource() { if (m_buffer != VK_NULL_HANDLE) m_vkd->vkDestroyBuffer(m_vkd->device(), m_buffer, nullptr); } + DxvkBuffer::DxvkBuffer( + DxvkDevice* device, + const DxvkBufferCreateInfo& createInfo, + VkMemoryPropertyFlags memoryType) + : m_device (device), + m_info (createInfo), + m_memFlags(memoryType) { + this->allocateResource(); + } + + + void DxvkBuffer::allocateResource() { + m_resource = m_device->allocBufferResource(m_info, m_memFlags); + } + + DxvkBufferView::DxvkBufferView( const Rc& vkd, const Rc& buffer, diff --git a/src/dxvk/dxvk_buffer.h b/src/dxvk/dxvk_buffer.h index 403a5fd14..0fcaafa2e 100644 --- a/src/dxvk/dxvk_buffer.h +++ b/src/dxvk/dxvk_buffer.h @@ -47,30 +47,54 @@ namespace dxvk { /** - * \brief Buffer resource + * \brief Physical buffer resource + * + * + */ + class DxvkBufferResource : public DxvkResource { + + public: + + DxvkBufferResource( + const Rc& vkd, + const DxvkBufferCreateInfo& createInfo, + DxvkMemoryAllocator& memAlloc, + VkMemoryPropertyFlags memFlags); + + ~DxvkBufferResource(); + + VkBuffer handle() const { + return m_buffer; + } + + void* mapPtr(VkDeviceSize offset) const { + return m_memory.mapPtr(offset); + } + + private: + + Rc m_vkd; + DxvkMemory m_memory; + VkBuffer m_buffer; + + }; + + + /** + * \brief Virtual buffer resource * * A simple buffer resource that stores linear, * unformatted data. Can be accessed by the host * if allocated on an appropriate memory type. */ - class DxvkBuffer : public DxvkResource { + class DxvkBuffer : public RcObject { public: DxvkBuffer( - const Rc& vkd, + DxvkDevice* device, const DxvkBufferCreateInfo& createInfo, - DxvkMemoryAllocator& memAlloc, - VkMemoryPropertyFlags memFlags); - ~DxvkBuffer(); - - /** - * \brief Buffer handle - * \returns Buffer handle - */ - VkBuffer handle() const { - return m_buffer; - } + VkMemoryPropertyFlags memoryType); /** * \brief Buffer properties @@ -80,6 +104,14 @@ namespace dxvk { return m_info; } + /** + * \brief Buffer handle + * \returns Buffer handle + */ + VkBuffer handle() const { + return m_resource->handle();; + } + /** * \brief Map pointer * @@ -90,15 +122,49 @@ namespace dxvk { * \returns Pointer to mapped memory region */ void* mapPtr(VkDeviceSize offset) const { - return m_memory.mapPtr(offset); + return m_resource->mapPtr(offset); } + /** + * \brief Checks whether the buffer is in use + * + * Returns \c true if the underlying buffer resource + * is in use. If it is, it should not be accessed by + * the host for reading or writing, but reallocating + * the buffer is a valid strategy to overcome this. + * \returns \c true if the buffer is in use + */ + bool isInUse() const { + return m_resource->isInUse(); + } + + /** + * \brief Underlying buffer resource + * + * Use this for lifetime tracking. + * \returns The resource object + */ + Rc resource() const { + return m_resource; + } + + /** + * \brief Allocates new 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. + */ + void allocateResource(); + private: - Rc m_vkd; - DxvkBufferCreateInfo m_info; - DxvkMemory m_memory; - VkBuffer m_buffer = VK_NULL_HANDLE; + DxvkDevice* m_device; + DxvkBufferCreateInfo m_info; + VkMemoryPropertyFlags m_memFlags; + + Rc m_resource; }; diff --git a/src/dxvk/dxvk_context.cpp b/src/dxvk/dxvk_context.cpp index 8ec676637..d3875feb7 100644 --- a/src/dxvk/dxvk_context.cpp +++ b/src/dxvk/dxvk_context.cpp @@ -310,8 +310,8 @@ namespace dxvk { m_barriers.recordCommands(m_cmd); - m_cmd->trackResource(dstBuffer); - m_cmd->trackResource(srcBuffer); + m_cmd->trackResource(dstBuffer->resource()); + m_cmd->trackResource(srcBuffer->resource()); } } @@ -368,6 +368,29 @@ namespace dxvk { } + void DxvkContext::invalidateBuffer(const Rc& buffer) { + // Allocate new backing resource + buffer->allocateResource(); + + // We also need to update all bindings that the buffer + // may be bound to either directly or through views. + const VkBufferUsageFlags usage = buffer->info().usage; + + if (usage & VK_BUFFER_USAGE_INDEX_BUFFER_BIT) + m_flags.set(DxvkContextFlag::GpDirtyIndexBuffer); + + if (usage & VK_BUFFER_USAGE_VERTEX_BUFFER_BIT) + m_flags.set(DxvkContextFlag::GpDirtyVertexBuffers); + + if (usage & (VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT + | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT + | VK_BUFFER_USAGE_UNIFORM_TEXEL_BUFFER_BIT + | VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT)) + m_flags.set(DxvkContextFlag::GpDirtyResources, + DxvkContextFlag::CpDirtyResources); + } + + void DxvkContext::resolveImage( const Rc& dstImage, const VkImageSubresourceLayers& dstSubresources, @@ -471,7 +494,7 @@ namespace dxvk { buffer->info().access); m_barriers.recordCommands(m_cmd); - m_cmd->trackResource(buffer); + m_cmd->trackResource(buffer->resource()); } } @@ -785,7 +808,6 @@ namespace dxvk { gpState.rsDepthBiasSlope = m_state.rs.depthBiasSlope; gpState.rsViewportCount = m_state.vp.viewportCount; - // TODO implement multisampling support properly gpState.msSampleCount = m_state.om.framebuffer->sampleCount(); gpState.msSampleMask = m_state.ms.sampleMask; gpState.msEnableAlphaToCoverage = m_state.ms.enableAlphaToCoverage; @@ -837,6 +859,8 @@ namespace dxvk { auto layout = m_state.cp.pipeline->layout(); + // TODO refcount used resources + m_cmd->bindResourceDescriptors( VK_PIPELINE_BIND_POINT_COMPUTE, layout->pipelineLayout(), @@ -904,7 +928,7 @@ namespace dxvk { m_state.vi.indexBuffer.offset(), m_state.vi.indexType); m_cmd->trackResource( - m_state.vi.indexBuffer.buffer()); + m_state.vi.indexBuffer.buffer()->resource()); } } } @@ -922,7 +946,7 @@ namespace dxvk { if (handle != VK_NULL_HANDLE) { m_cmd->cmdBindVertexBuffers(i, 1, &handle, &offset); - m_cmd->trackResource(vbo.buffer()); + m_cmd->trackResource(vbo.buffer()->resource()); } } } diff --git a/src/dxvk/dxvk_context.h b/src/dxvk/dxvk_context.h index a66adbf49..601b1ac7e 100644 --- a/src/dxvk/dxvk_context.h +++ b/src/dxvk/dxvk_context.h @@ -244,8 +244,23 @@ namespace dxvk { * \param [in] subresources Image subresources */ void initImage( - const Rc& image, - const VkImageSubresourceRange& subresources); + const Rc& image, + const VkImageSubresourceRange& subresources); + + /** + * \brief Invalidates a buffer's contents + * + * Discards a buffer's contents by allocating a new + * backing resource. This allows the host to access + * the buffer while the GPU is still accessing the + * original backing resource. + * + * \warning If the buffer is used by another context, + * invalidating it will result in undefined behaviour. + * \param [in] buffer The buffer to invalidate + */ + void invalidateBuffer( + const Rc& buffer); /** * \brief Resolves a multisampled image resource diff --git a/src/dxvk/dxvk_device.cpp b/src/dxvk/dxvk_device.cpp index 124534129..72a69eb0b 100644 --- a/src/dxvk/dxvk_device.cpp +++ b/src/dxvk/dxvk_device.cpp @@ -29,6 +29,14 @@ namespace dxvk { } + Rc DxvkDevice::allocBufferResource( + const DxvkBufferCreateInfo& createInfo, + VkMemoryPropertyFlags memoryType) { + return new DxvkBufferResource(m_vkd, + createInfo, *m_memory, memoryType); + } + + Rc DxvkDevice::allocStagingBuffer(VkDeviceSize size) { // In case we need a standard-size staging buffer, try // to recycle an old one that has been returned earlier @@ -102,9 +110,7 @@ namespace dxvk { const DxvkBufferCreateInfo& createInfo, VkMemoryPropertyFlags memoryType) { m_statCounters.increment(DxvkStat::ResBufferCreations, 1); - - return new DxvkBuffer(m_vkd, - createInfo, *m_memory, memoryType); + return new DxvkBuffer(this, createInfo, memoryType); } @@ -119,9 +125,7 @@ namespace dxvk { const DxvkImageCreateInfo& createInfo, VkMemoryPropertyFlags memoryType) { m_statCounters.increment(DxvkStat::ResImageCreations, 1); - - return new DxvkImage(m_vkd, - createInfo, *m_memory, memoryType); + return new DxvkImage(m_vkd, createInfo, *m_memory, memoryType); } diff --git a/src/dxvk/dxvk_device.h b/src/dxvk/dxvk_device.h index 27babc6e1..c85c9c2b8 100644 --- a/src/dxvk/dxvk_device.h +++ b/src/dxvk/dxvk_device.h @@ -75,6 +75,17 @@ namespace dxvk { return m_features; } + /** + * \brief Allocates buffer resource + * + * \param [in] createInfo Buffer create info + * \param [in] memoryType Memory property flags + * \returns The buffer resource object + */ + Rc allocBufferResource( + const DxvkBufferCreateInfo& createInfo, + VkMemoryPropertyFlags memoryType); + /** * \brief Allocates a staging buffer *