1
0
mirror of https://github.com/doitsujin/dxvk.git synced 2024-12-12 04:08:52 +01:00

[dxvk] Refactor descriptor set allocation

With this new approach, descriptor pools are decoupled from the
command list they are used with. Instead, the DXVK context takes
ownership of a single descriptor pool until it runs out of memory.

This reduces the amount of memory wasted for under-utilized pools
and should this reduce an application's memory footprint.
This commit is contained in:
Philip Rebohle 2018-11-27 11:07:23 +01:00
parent eb7a5da975
commit 4057937d2d
No known key found for this signature in database
GPG Key ID: C8CC613427A31C99
8 changed files with 166 additions and 89 deletions

View File

@ -8,7 +8,7 @@ namespace dxvk {
uint32_t queueFamily) uint32_t queueFamily)
: m_vkd (device->vkd()), : m_vkd (device->vkd()),
m_cmdBuffersUsed(0), m_cmdBuffersUsed(0),
m_descAlloc (device->vkd()), m_descriptorPoolTracker(device),
m_stagingAlloc (device) { m_stagingAlloc (device) {
VkFenceCreateInfo fenceInfo; VkFenceCreateInfo fenceInfo;
fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
@ -127,7 +127,7 @@ namespace dxvk {
m_eventTracker.reset(); m_eventTracker.reset();
m_queryTracker.reset(); m_queryTracker.reset();
m_stagingAlloc.reset(); m_stagingAlloc.reset();
m_descAlloc.reset(); m_descriptorPoolTracker.reset();
m_resources.reset(); m_resources.reset();
} }

View File

@ -157,6 +157,14 @@ namespace dxvk {
m_eventTracker.trackEvent(event); m_eventTracker.trackEvent(event);
} }
/**
* \brief Tracks a descriptor pool
* \param [in] pool The descriptor pool
*/
void trackDescriptorPool(Rc<DxvkDescriptorPool> pool) {
m_descriptorPoolTracker.trackDescriptorPool(pool);
}
/** /**
* \brief Signals tracked events * \brief Signals tracked events
* *
@ -188,12 +196,6 @@ namespace dxvk {
*/ */
void reset(); void reset();
VkDescriptorSet allocateDescriptorSet(
VkDescriptorSetLayout descriptorLayout) {
return m_descAlloc.alloc(descriptorLayout);
}
void updateDescriptorSets( void updateDescriptorSets(
uint32_t descriptorWriteCount, uint32_t descriptorWriteCount,
const VkWriteDescriptorSet* pDescriptorWrites) { const VkWriteDescriptorSet* pDescriptorWrites) {
@ -656,7 +658,7 @@ namespace dxvk {
DxvkCmdBufferFlags m_cmdBuffersUsed; DxvkCmdBufferFlags m_cmdBuffersUsed;
DxvkLifetimeTracker m_resources; DxvkLifetimeTracker m_resources;
DxvkDescriptorAlloc m_descAlloc; DxvkDescriptorPoolTracker m_descriptorPoolTracker;
DxvkStagingAlloc m_stagingAlloc; DxvkStagingAlloc m_stagingAlloc;
DxvkQueryTracker m_queryTracker; DxvkQueryTracker m_queryTracker;
DxvkEventTracker m_eventTracker; DxvkEventTracker m_eventTracker;

View File

@ -296,8 +296,7 @@ namespace dxvk {
// Create a descriptor set pointing to the view // Create a descriptor set pointing to the view
VkBufferView viewObject = bufferView->handle(); VkBufferView viewObject = bufferView->handle();
VkDescriptorSet descriptorSet = VkDescriptorSet descriptorSet = allocateDescriptorSet(pipeInfo.dsetLayout);
m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout);
VkWriteDescriptorSet descriptorWrite; VkWriteDescriptorSet descriptorWrite;
descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptorWrite.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
@ -963,7 +962,7 @@ namespace dxvk {
descriptors.srcDepth = dView->getDescriptor(VK_IMAGE_VIEW_TYPE_2D_ARRAY, layout).image; descriptors.srcDepth = dView->getDescriptor(VK_IMAGE_VIEW_TYPE_2D_ARRAY, layout).image;
descriptors.srcStencil = sView->getDescriptor(VK_IMAGE_VIEW_TYPE_2D_ARRAY, layout).image; descriptors.srcStencil = sView->getDescriptor(VK_IMAGE_VIEW_TYPE_2D_ARRAY, layout).image;
VkDescriptorSet dset = m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout); VkDescriptorSet dset = allocateDescriptorSet(pipeInfo.dsetLayout);
m_cmd->updateDescriptorSetWithTemplate(dset, pipeInfo.dsetTemplate, &descriptors); m_cmd->updateDescriptorSetWithTemplate(dset, pipeInfo.dsetTemplate, &descriptors);
// Since this is a meta operation, the image may be // Since this is a meta operation, the image may be
@ -1290,7 +1289,7 @@ namespace dxvk {
// Create descriptor set with the current source view // Create descriptor set with the current source view
descriptorImage.imageView = pass.srcView; descriptorImage.imageView = pass.srcView;
descriptorWrite.dstSet = m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout); descriptorWrite.dstSet = allocateDescriptorSet(pipeInfo.dsetLayout);
m_cmd->updateDescriptorSets(1, &descriptorWrite); m_cmd->updateDescriptorSets(1, &descriptorWrite);
// Set up viewport and scissor rect // Set up viewport and scissor rect
@ -1802,8 +1801,7 @@ namespace dxvk {
imageView->type(), imageFormatInfo(imageView->info().format)->flags); imageView->type(), imageFormatInfo(imageView->info().format)->flags);
// Create a descriptor set pointing to the view // Create a descriptor set pointing to the view
VkDescriptorSet descriptorSet = VkDescriptorSet descriptorSet = allocateDescriptorSet(pipeInfo.dsetLayout);
m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout);
VkDescriptorImageInfo viewInfo; VkDescriptorImageInfo viewInfo;
viewInfo.sampler = VK_NULL_HANDLE; viewInfo.sampler = VK_NULL_HANDLE;
@ -2077,7 +2075,7 @@ namespace dxvk {
descriptorWrite.pBufferInfo = nullptr; descriptorWrite.pBufferInfo = nullptr;
descriptorWrite.pTexelBufferView = nullptr; descriptorWrite.pTexelBufferView = nullptr;
descriptorWrite.dstSet = m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout); descriptorWrite.dstSet = allocateDescriptorSet(pipeInfo.dsetLayout);
m_cmd->updateDescriptorSets(1, &descriptorWrite); m_cmd->updateDescriptorSets(1, &descriptorWrite);
VkViewport viewport; VkViewport viewport;
@ -2272,7 +2270,7 @@ namespace dxvk {
descriptorWrite.pBufferInfo = nullptr; descriptorWrite.pBufferInfo = nullptr;
descriptorWrite.pTexelBufferView = nullptr; descriptorWrite.pTexelBufferView = nullptr;
descriptorWrite.dstSet = m_cmd->allocateDescriptorSet(pipeInfo.dsetLayout); descriptorWrite.dstSet = allocateDescriptorSet(pipeInfo.dsetLayout);
m_cmd->updateDescriptorSets(1, &descriptorWrite); m_cmd->updateDescriptorSets(1, &descriptorWrite);
// Set up viewport and scissor rect // Set up viewport and scissor rect
@ -2814,7 +2812,7 @@ namespace dxvk {
VkDescriptorSet descriptorSet = VK_NULL_HANDLE; VkDescriptorSet descriptorSet = VK_NULL_HANDLE;
if (layout->bindingCount() != 0) { if (layout->bindingCount() != 0) {
descriptorSet = m_cmd->allocateDescriptorSet( descriptorSet = allocateDescriptorSet(
layout->descriptorSetLayout()); layout->descriptorSetLayout());
m_cmd->updateDescriptorSetWithTemplate( m_cmd->updateDescriptorSetWithTemplate(
@ -3259,6 +3257,24 @@ namespace dxvk {
} }
VkDescriptorSet DxvkContext::allocateDescriptorSet(
VkDescriptorSetLayout layout) {
if (m_descPool == nullptr)
m_descPool = m_device->createDescriptorPool();
VkDescriptorSet set = m_descPool->alloc(layout);
if (set == VK_NULL_HANDLE) {
m_cmd->trackDescriptorPool(std::move(m_descPool));
m_descPool = m_device->createDescriptorPool();
set = m_descPool->alloc(layout);
}
return set;
}
void DxvkContext::trackDrawBuffer() { void DxvkContext::trackDrawBuffer() {
if (m_flags.test(DxvkContextFlag::DirtyDrawBuffer)) { if (m_flags.test(DxvkContextFlag::DirtyDrawBuffer)) {
m_flags.clr(DxvkContextFlag::DirtyDrawBuffer); m_flags.clr(DxvkContextFlag::DirtyDrawBuffer);

View File

@ -769,6 +769,8 @@ namespace dxvk {
const Rc<DxvkMetaResolveObjects> m_metaResolve; const Rc<DxvkMetaResolveObjects> m_metaResolve;
Rc<DxvkCommandList> m_cmd; Rc<DxvkCommandList> m_cmd;
Rc<DxvkDescriptorPool> m_descPool;
DxvkContextFlags m_flags; DxvkContextFlags m_flags;
DxvkContextState m_state; DxvkContextState m_state;
@ -903,6 +905,9 @@ namespace dxvk {
VkPipelineStageFlags dstStages, VkPipelineStageFlags dstStages,
VkAccessFlags dstAccess); VkAccessFlags dstAccess);
VkDescriptorSet allocateDescriptorSet(
VkDescriptorSetLayout layout);
void trackDrawBuffer(); void trackDrawBuffer();
}; };

View File

@ -1,50 +1,10 @@
#include "dxvk_descriptor.h" #include "dxvk_descriptor.h"
#include "dxvk_device.h"
namespace dxvk { namespace dxvk {
DxvkDescriptorAlloc::DxvkDescriptorAlloc( DxvkDescriptorPool::DxvkDescriptorPool(const Rc<vk::DeviceFn>& vkd)
const Rc<vk::DeviceFn>& vkd)
: m_vkd(vkd) { : m_vkd(vkd) {
// Allocate one pool right away so that there
// is always at least one pool available when
// allocating a descriptor set
m_pools.push_back(createDescriptorPool());
}
DxvkDescriptorAlloc::~DxvkDescriptorAlloc() {
for (auto p : m_pools) {
m_vkd->vkDestroyDescriptorPool(
m_vkd->device(), p, nullptr);
}
}
VkDescriptorSet DxvkDescriptorAlloc::alloc(VkDescriptorSetLayout layout) {
VkDescriptorSet set = allocFrom(m_pools[m_poolId], layout);
if (set == VK_NULL_HANDLE) {
if (++m_poolId >= m_pools.size())
m_pools.push_back(createDescriptorPool());
set = allocFrom(m_pools[m_poolId], layout);
}
return set;
}
void DxvkDescriptorAlloc::reset() {
for (auto p : m_pools) {
m_vkd->vkResetDescriptorPool(
m_vkd->device(), p, 0);
}
m_poolId = 0;
}
VkDescriptorPool DxvkDescriptorAlloc::createDescriptorPool() {
constexpr uint32_t MaxSets = 2048; constexpr uint32_t MaxSets = 2048;
std::array<VkDescriptorPoolSize, 10> pools = {{ std::array<VkDescriptorPoolSize, 10> pools = {{
@ -67,21 +27,22 @@ namespace dxvk {
info.poolSizeCount = pools.size(); info.poolSizeCount = pools.size();
info.pPoolSizes = pools.data(); info.pPoolSizes = pools.data();
VkDescriptorPool pool = VK_NULL_HANDLE; if (m_vkd->vkCreateDescriptorPool(m_vkd->device(), &info, nullptr, &m_pool) != VK_SUCCESS)
if (m_vkd->vkCreateDescriptorPool(m_vkd->device(), throw DxvkError("DxvkDescriptorPool: Failed to create descriptor pool");
&info, nullptr, &pool) != VK_SUCCESS)
throw DxvkError("DxvkDescriptorAlloc: Failed to create descriptor pool");
return pool;
} }
VkDescriptorSet DxvkDescriptorAlloc::allocFrom( DxvkDescriptorPool::~DxvkDescriptorPool() {
VkDescriptorPool pool, m_vkd->vkDestroyDescriptorPool(
VkDescriptorSetLayout layout) const { m_vkd->device(), m_pool, nullptr);
}
VkDescriptorSet DxvkDescriptorPool::alloc(VkDescriptorSetLayout layout) {
VkDescriptorSetAllocateInfo info; VkDescriptorSetAllocateInfo info;
info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
info.pNext = nullptr; info.pNext = nullptr;
info.descriptorPool = pool; info.descriptorPool = m_pool;
info.descriptorSetCount = 1; info.descriptorSetCount = 1;
info.pSetLayouts = &layout; info.pSetLayouts = &layout;
@ -91,4 +52,38 @@ namespace dxvk {
return set; return set;
} }
void DxvkDescriptorPool::reset() {
m_vkd->vkResetDescriptorPool(
m_vkd->device(), m_pool, 0);
}
DxvkDescriptorPoolTracker::DxvkDescriptorPoolTracker(DxvkDevice* device)
: m_device(device) {
}
DxvkDescriptorPoolTracker::~DxvkDescriptorPoolTracker() {
}
void DxvkDescriptorPoolTracker::trackDescriptorPool(Rc<DxvkDescriptorPool> pool) {
m_pools.push_back(std::move(pool));
}
void DxvkDescriptorPoolTracker::reset() {
for (const auto& pool : m_pools) {
pool->reset();
m_device->recycleDescriptorPool(pool);
}
m_pools.clear();
}
} }

View File

@ -6,6 +6,8 @@
namespace dxvk { namespace dxvk {
class DxvkDevice;
/** /**
* \brief Descriptor info * \brief Descriptor info
* *
@ -20,21 +22,18 @@ namespace dxvk {
/** /**
* \brief Descriptor set allocator * \brief Descriptor pool
* *
* Creates descriptor pools on demand and * Wrapper around a Vulkan descriptor pool that
* allocates descriptor sets from those pools. * descriptor sets can be allocated from.
*/ */
class DxvkDescriptorAlloc { class DxvkDescriptorPool : public RcObject {
public: public:
DxvkDescriptorAlloc( DxvkDescriptorPool(
const Rc<vk::DeviceFn>& vkd); const Rc<vk::DeviceFn>& vkd);
~DxvkDescriptorAlloc(); ~DxvkDescriptorPool();
DxvkDescriptorAlloc (const DxvkDescriptorAlloc&) = delete;
DxvkDescriptorAlloc& operator = (const DxvkDescriptorAlloc&) = delete;
/** /**
* \brief Allocates a descriptor set * \brief Allocates a descriptor set
@ -56,15 +55,45 @@ namespace dxvk {
private: private:
Rc<vk::DeviceFn> m_vkd; Rc<vk::DeviceFn> m_vkd;
VkDescriptorPool m_pool;
std::vector<VkDescriptorPool> m_pools; };
size_t m_poolId = 0;
VkDescriptorPool createDescriptorPool();
VkDescriptorSet allocFrom( /**
VkDescriptorPool pool, * \brief Descriptor pool tracker
VkDescriptorSetLayout layout) const; *
* Tracks descriptor pools that are either full
* or no longer needed by the DXVK context. The
* command list will reset and recycle all pools
* once it has completed execution on the GPU.
*/
class DxvkDescriptorPoolTracker {
public:
DxvkDescriptorPoolTracker(DxvkDevice* device);
~DxvkDescriptorPoolTracker();
/**
* \brief Adds a descriptor pool to track
* \param [in] pool The descriptor pool
*/
void trackDescriptorPool(Rc<DxvkDescriptorPool> pool);
/**
* \brief Resets event tracker
*
* Resets all tracked descriptor pools
* and returns them to the device.
*/
void reset();
private:
DxvkDevice* m_device;
std::vector<Rc<DxvkDescriptorPool>> m_pools;
}; };

View File

@ -116,6 +116,16 @@ namespace dxvk {
} }
Rc<DxvkDescriptorPool> DxvkDevice::createDescriptorPool() {
Rc<DxvkDescriptorPool> pool = m_recycledDescriptorPools.retrieveObject();
if (pool == nullptr)
pool = new DxvkDescriptorPool(m_vkd);
return pool;
}
Rc<DxvkContext> DxvkDevice::createContext() { Rc<DxvkContext> DxvkDevice::createContext() {
return new DxvkContext(this, return new DxvkContext(this,
m_pipelineManager, m_pipelineManager,
@ -296,4 +306,9 @@ namespace dxvk {
m_recycledCommandLists.returnObject(cmdList); m_recycledCommandLists.returnObject(cmdList);
} }
void DxvkDevice::recycleDescriptorPool(const Rc<DxvkDescriptorPool>& pool) {
m_recycledDescriptorPools.returnObject(pool);
}
} }

View File

@ -58,6 +58,7 @@ namespace dxvk {
class DxvkDevice : public RcObject { class DxvkDevice : public RcObject {
friend class DxvkContext; friend class DxvkContext;
friend class DxvkSubmissionQueue; friend class DxvkSubmissionQueue;
friend class DxvkDescriptorPoolTracker;
constexpr static VkDeviceSize DefaultStagingBufferSize = 4 * 1024 * 1024; constexpr static VkDeviceSize DefaultStagingBufferSize = 4 * 1024 * 1024;
public: public:
@ -178,6 +179,16 @@ namespace dxvk {
*/ */
Rc<DxvkCommandList> createCommandList(); Rc<DxvkCommandList> createCommandList();
/**
* \brief Creates a descriptor pool
*
* Returns a previously recycled pool, or creates
* a new one if necessary. The context should take
* ownership of the returned pool.
* \returns Descriptor pool
*/
Rc<DxvkDescriptorPool> createDescriptorPool();
/** /**
* \brief Creates a context * \brief Creates a context
* *
@ -414,6 +425,7 @@ namespace dxvk {
DxvkDeviceQueue m_presentQueue; DxvkDeviceQueue m_presentQueue;
DxvkRecycler<DxvkCommandList, 16> m_recycledCommandLists; DxvkRecycler<DxvkCommandList, 16> m_recycledCommandLists;
DxvkRecycler<DxvkDescriptorPool, 16> m_recycledDescriptorPools;
DxvkRecycler<DxvkStagingBuffer, 4> m_recycledStagingBuffers; DxvkRecycler<DxvkStagingBuffer, 4> m_recycledStagingBuffers;
DxvkSubmissionQueue m_submissionQueue; DxvkSubmissionQueue m_submissionQueue;
@ -421,6 +433,9 @@ namespace dxvk {
void recycleCommandList( void recycleCommandList(
const Rc<DxvkCommandList>& cmdList); const Rc<DxvkCommandList>& cmdList);
void recycleDescriptorPool(
const Rc<DxvkDescriptorPool>& pool);
/** /**
* \brief Dummy buffer handle * \brief Dummy buffer handle
* \returns Use for unbound vertex buffers. * \returns Use for unbound vertex buffers.