[dxvk] Implemented recycling of command buffers and staging buffers

This commit is contained in:
Philip Rebohle 2017-12-11 19:17:08 +01:00
parent 037e9a643d
commit 68ca71d8a4
6 changed files with 112 additions and 12 deletions

View File

@ -22,7 +22,7 @@ namespace dxvk {
// Create swap chain for the surface
DxvkSwapchainProperties swapchainProperties;
swapchainProperties.preferredSurfaceFormat = this->pickFormat(bufferFormat);
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
swapchainProperties.preferredBufferSize.width = bufferWidth;
swapchainProperties.preferredBufferSize.height = bufferHeight;
@ -207,7 +207,7 @@ namespace dxvk {
DXGI_FORMAT bufferFormat) {
DxvkSwapchainProperties swapchainProperties;
swapchainProperties.preferredSurfaceFormat = this->pickFormat(bufferFormat);
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_FIFO_KHR;
swapchainProperties.preferredPresentMode = VK_PRESENT_MODE_IMMEDIATE_KHR;
swapchainProperties.preferredBufferSize.width = bufferWidth;
swapchainProperties.preferredBufferSize.height = bufferHeight;

View File

@ -33,12 +33,20 @@ namespace dxvk {
Rc<DxvkStagingBuffer> DxvkDevice::allocStagingBuffer(VkDeviceSize size) {
// TODO actually recycle old buffers
const VkDeviceSize baseSize = 64 * 1024 * 1024;
const VkDeviceSize bufferSize = std::max(baseSize, size);
// In case we need a standard-size staging buffer, try
// to recycle an old one that has been returned earlier
if (size <= DefaultStagingBufferSize) {
const Rc<DxvkStagingBuffer> buffer
= m_recycledStagingBuffers.retrieveObject();
if (buffer != nullptr)
return buffer;
}
// Staging buffers only need to be able to handle transfer
// operations, and they need to be in host-visible memory.
DxvkBufferCreateInfo info;
info.size = bufferSize;
info.size = size;
info.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT;
info.stages = VK_PIPELINE_STAGE_TRANSFER_BIT
| VK_PIPELINE_STAGE_HOST_BIT;
@ -49,18 +57,32 @@ namespace dxvk {
= VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT
| VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
// Don't create buffers that are too small. A staging
// buffer should be able to serve multiple uploads.
if (info.size < DefaultStagingBufferSize)
info.size = DefaultStagingBufferSize;
return new DxvkStagingBuffer(this->createBuffer(info, memFlags));
}
void DxvkDevice::recycleStagingBuffer(const Rc<DxvkStagingBuffer>& buffer) {
// TODO implement
// Drop staging buffers that are bigger than the
// standard ones to save memory, recycle the rest
if (buffer->size() == DefaultStagingBufferSize)
m_recycledStagingBuffers.returnObject(buffer);
}
Rc<DxvkCommandList> DxvkDevice::createCommandList() {
return new DxvkCommandList(m_vkd, this,
m_adapter->graphicsQueueFamily());
Rc<DxvkCommandList> cmdList = m_recycledCommandLists.retrieveObject();
if (cmdList == nullptr) {
cmdList = new DxvkCommandList(m_vkd,
this, m_adapter->graphicsQueueFamily());
}
return cmdList;
}
@ -176,6 +198,9 @@ namespace dxvk {
// TODO Delay synchronization by putting these into a ring buffer
fence->wait(std::numeric_limits<uint64_t>::max());
commandList->reset();
// FIXME this must go away once the ring buffer is implemented
m_recycledCommandLists.returnObject(commandList);
return fence;
}

View File

@ -9,6 +9,7 @@
#include "dxvk_image.h"
#include "dxvk_memory.h"
#include "dxvk_pipemanager.h"
#include "dxvk_recycler.h"
#include "dxvk_renderpass.h"
#include "dxvk_sampler.h"
#include "dxvk_shader.h"
@ -28,7 +29,7 @@ namespace dxvk {
* contexts. Multiple contexts can be created for a device.
*/
class DxvkDevice : public RcObject {
constexpr static VkDeviceSize DefaultStagingBufferSize = 64 * 1024 * 1024;
public:
DxvkDevice(
@ -266,8 +267,12 @@ namespace dxvk {
Rc<DxvkRenderPassPool> m_renderPassPool;
Rc<DxvkPipelineManager> m_pipelineManager;
VkQueue m_graphicsQueue;
VkQueue m_presentQueue;
VkQueue m_graphicsQueue = VK_NULL_HANDLE;
VkQueue m_presentQueue = VK_NULL_HANDLE;
// TODO fine-tune buffer sizes
DxvkRecycler<DxvkCommandList, 16> m_recycledCommandLists;
DxvkRecycler<DxvkStagingBuffer, 4> m_recycledStagingBuffers;
};

63
src/dxvk/dxvk_recycler.h Normal file
View File

@ -0,0 +1,63 @@
#pragma once
#include <mutex>
#include <vector>
namespace dxvk {
/**
* \brief Object recycler
*
* Implements a thread-safe buffer that can store up to
* a given number of objects of a certain type. This way,
* DXVK can efficiently reuse and reset objects instead
* of destroying them and creating them anew.
* \tparam T Type of the objects to store
* \tparam N Maximum number of objects to store
*/
template<typename T, size_t N>
class DxvkRecycler {
public:
/**
* \brief Retrieves an object if possible
*
* Returns an object that was returned to the recycler
* earier. In case no objects are available, this will
* return \c nullptr and a new object has to be created.
* \return An object, or \c nullptr
*/
Rc<T> retrieveObject() {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_objectId == 0)
return nullptr;
return m_objects.at(--m_objectId);
}
/**
* \brief Returns an object to the recycler
*
* If the buffer is full, the object will be destroyed
* once the last reference runs out of scope. No further
* action needs to be taken in this case.
* \param [in] object The object to return
*/
void returnObject(const Rc<T>& object) {
std::lock_guard<std::mutex> lock(m_mutex);
if (m_objectId < N)
m_objects.at(m_objectId++) = object;
}
private:
std::mutex m_mutex;
std::array<Rc<T>, N> m_objects;
size_t m_objectId = 0;
};
}

View File

@ -15,6 +15,11 @@ namespace dxvk {
}
VkDeviceSize DxvkStagingBuffer::size() const {
return m_bufferSize;
}
VkDeviceSize DxvkStagingBuffer::freeBytes() const {
return m_bufferSize - m_bufferOffset;
}

View File

@ -22,6 +22,8 @@ namespace dxvk {
~DxvkStagingBuffer();
VkDeviceSize size() const;
VkDeviceSize freeBytes() const;
bool alloc(