Skip to content

Commit b800042

Browse files
authored
Vulkan: Improve descriptor cache management (#3048)
1 parent 3c5f925 commit b800042

File tree

5 files changed

+429
-360
lines changed

5 files changed

+429
-360
lines changed

3rdparty/yasio/yasio/object_pool.hpp

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -38,19 +38,21 @@ struct null_mutex {
3838
void unlock() {}
3939
};
4040
template <typename _Ty, typename _Mutex = ::yasio::null_mutex>
41-
class object_pool : public detail::object_pool {
41+
class object_pool : protected detail::object_pool {
4242
public:
43+
using detail::object_pool::purge;
4344
object_pool(size_t _ElemCount = 128) : detail::object_pool(::tlx::aligned_storage_size<_Ty>::value, _ElemCount) {}
4445

4546
template <typename... _Types>
46-
_Ty* create(_Types&&... args)
47+
_Ty* construt(_Types&&... args)
4748
{
4849
return new (allocate()) _Ty(std::forward<_Types>(args)...);
4950
}
5051

5152
void destroy(void* _Ptr)
5253
{
53-
((_Ty*)_Ptr)->~_Ty(); // call the destructor
54+
if constexpr (!std::is_trivially_destructible<_Ty>::value)
55+
((_Ty*)_Ptr)->~_Ty(); // call the destructor
5456
deallocate(_Ptr);
5557
}
5658

@@ -69,28 +71,19 @@ class object_pool : public detail::object_pool {
6971
_Mutex mutex_;
7072
};
7173

72-
#define DEFINE_OBJECT_POOL_ALLOCATION_ANY(ELEMENT_TYPE, ELEMENT_COUNT, MUTEX_TYPE) \
73-
public: \
74-
using object_pool_type = yasio::object_pool<ELEMENT_TYPE, MUTEX_TYPE>; \
75-
static void* operator new(size_t /*size*/) \
76-
{ \
77-
return get_pool().allocate(); \
78-
} \
79-
\
80-
static void* operator new(size_t /*size*/, std::nothrow_t) \
81-
{ \
82-
return get_pool().allocate(); \
83-
} \
84-
\
85-
static void operator delete(void* p) \
86-
{ \
87-
get_pool().deallocate(p); \
88-
} \
89-
\
90-
static object_pool_type& get_pool() \
91-
{ \
92-
static object_pool_type s_pool(ELEMENT_COUNT); \
93-
return s_pool; \
74+
#define DEFINE_OBJECT_POOL_ALLOCATION_ANY(ELEMENT_TYPE, ELEMENT_COUNT, MUTEX_TYPE) \
75+
public: \
76+
using object_pool_type = yasio::object_pool<ELEMENT_TYPE, MUTEX_TYPE>; \
77+
static void* operator new(size_t /*size*/) { return get_pool().allocate(); } \
78+
\
79+
static void* operator new(size_t /*size*/, std::nothrow_t) { return get_pool().allocate(); } \
80+
\
81+
static void operator delete(void* p) { get_pool().deallocate(p); } \
82+
\
83+
static object_pool_type& get_pool() \
84+
{ \
85+
static object_pool_type s_pool(ELEMENT_COUNT); \
86+
return s_pool; \
9487
}
9588

9689
// The non thread safe edition

axmol/rhi/vulkan/RenderContextVK.cpp

Lines changed: 19 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -144,9 +144,6 @@ RenderContextImpl::RenderContextImpl(DriverImpl* driver, SurfaceHandle surface)
144144
_screenRT = new RenderTargetImpl(_driver, true);
145145

146146
createCommandBuffers();
147-
#if !_AX_USE_DESCRIPTOR_CACHE
148-
createDescriptorPool();
149-
#endif
150147
recreateSwapchain();
151148

152149
// create frame fence objects
@@ -174,6 +171,12 @@ RenderContextImpl::~RenderContextImpl()
174171
vkDeviceWaitIdle(_device);
175172
vkQueueWaitIdle(_presentQueue);
176173

174+
for (auto& descriptorStates : _inFlightDescriptorStates)
175+
{
176+
_renderPipeline->recycleDescriptorStates(descriptorStates, false);
177+
descriptorStates.clear();
178+
}
179+
177180
AX_SAFE_RELEASE_NULL(_screenRT);
178181
_driver->destroyStaleResources();
179182
AX_SAFE_RELEASE_NULL(_renderPipeline);
@@ -186,11 +189,6 @@ RenderContextImpl::~RenderContextImpl()
186189
for (auto fence : _inFlightFences)
187190
vkDestroyFence(_device, fence, nullptr);
188191
_inFlightFences.fill({});
189-
#if !_AX_USE_DESCRIPTOR_CACHE
190-
for (auto pool : _descriptorPools)
191-
vkDestroyDescriptorPool(_device, pool, nullptr);
192-
_descriptorPools.fill(VK_NULL_HANDLE);
193-
#endif
194192
vkFreeCommandBuffers(_device, _commandPool, static_cast<uint32_t>(_commandBuffers.size()), _commandBuffers.data());
195193
_commandBuffers.fill(VK_NULL_HANDLE);
196194

@@ -329,34 +327,6 @@ void RenderContextImpl::createCommandBuffers()
329327
AXASSERT(result == VK_SUCCESS, "vkAllocateCommandBuffers failed");
330328
}
331329

332-
#if !_AX_USE_DESCRIPTOR_CACHE
333-
334-
void RenderContextImpl::createDescriptorPool()
335-
{
336-
// Define the descriptor types and counts supported by the pool
337-
constexpr VkDescriptorPoolSize poolSizes[] = {
338-
{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 64}, {VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 64},
339-
/*{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 32},*/ // SSBO, unused currently
340-
};
341-
342-
constexpr uint32_t MAX_DESCRIPTOR_SETS_PER_FRAME = 1024;
343-
344-
VkDescriptorPoolCreateInfo poolInfo{};
345-
poolInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
346-
poolInfo.poolSizeCount = static_cast<uint32_t>(std::size(poolSizes));
347-
poolInfo.pPoolSizes = poolSizes;
348-
poolInfo.maxSets = MAX_DESCRIPTOR_SETS_PER_FRAME; // Maximum number of descriptor sets that can be allocated
349-
poolInfo.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;
350-
// Allow individual descriptor sets to be freed for flexible management
351-
352-
for (auto i = 0; i < MAX_FRAMES_IN_FLIGHT; ++i)
353-
{
354-
VkResult res = vkCreateDescriptorPool(_device, &poolInfo, nullptr, &_descriptorPools[i]);
355-
AXASSERT(res == VK_SUCCESS, "Failed to create descriptor pool");
356-
}
357-
}
358-
#endif
359-
360330
bool RenderContextImpl::updateSurface(SurfaceHandle surface, uint32_t width, uint32_t height)
361331
{
362332
if (width == _screenWidth && height == _screenHeight && surface == _surface)
@@ -612,15 +582,9 @@ bool RenderContextImpl::beginFrame()
612582
_currentCmdBuffer = _commandBuffers[_frameIndex];
613583
vkResetCommandBuffer(_currentCmdBuffer, 0);
614584

615-
#if _AX_USE_DESCRIPTOR_CACHE
616585
auto& descriptorStates = _inFlightDescriptorStates[_frameIndex];
617-
for (auto& state : descriptorStates)
618-
_renderPipeline->recycleDescriptorState(state);
586+
_renderPipeline->recycleDescriptorStates(descriptorStates, true);
619587
descriptorStates.clear();
620-
#else
621-
auto descriptorPool = _descriptorPools[_frameIndex];
622-
vkResetDescriptorPool(_device, descriptorPool, 0); // safe: only reset current frame pool
623-
#endif
624588

625589
VkCommandBufferBeginInfo const binfo{
626590
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
@@ -1035,34 +999,17 @@ void RenderContextImpl::prepareDrawing()
1035999
for (auto& cb : _programState->getCallbackUniforms())
10361000
cb.second(_programState, cb.first);
10371001

1038-
// Acquire descriptor sets for this frame, matching current pipeline layout
1039-
VkPipelineLayout pipelineLayout = _renderPipeline->getVkPipelineLayout();
1040-
const auto dslState = _renderPipeline->getDescriptorSetLayoutState();
1041-
1042-
#if _AX_USE_DESCRIPTOR_CACHE
1043-
auto& descriptorState = _inFlightDescriptorStates[_frameIndex].emplace_back();
1044-
bool ok = _renderPipeline->acquireDescriptorState(descriptorState, _frameIndex);
1045-
AXASSERT(ok, "Failed to acquire descriptor sets");
1046-
auto& descriptorSets = descriptorState.sets;
1047-
#else
1048-
VkDescriptorSetAllocateInfo allocInfo{};
1049-
allocInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
1050-
allocInfo.descriptorPool = _descriptorPools[_frameIndex];
1051-
auto descriptorSetLayoutState = _renderPipeline->getDescriptorSetLayoutState();
1052-
allocInfo.descriptorSetCount = descriptorSetLayoutState->descriptorSetLayoutCount;
1053-
allocInfo.pSetLayouts = descriptorSetLayoutState->descriptorSetLayouts.data();
1054-
1055-
std::array<VkDescriptorSet, RenderPipelineImpl::MAX_DESCRIPTOR_SETS> descriptorSets{};
1056-
VkResult res = vkAllocateDescriptorSets(_device, &allocInfo, descriptorSets.data());
1057-
AXASSERT(res == VK_SUCCESS, "Failed to allocate descriptor sets");
1058-
#endif
1002+
// Acquire descriptor state, matching current pipeline layout
1003+
auto descriptorState = _renderPipeline->acquireDescriptorState();
1004+
_inFlightDescriptorStates[_frameIndex].emplace_back(descriptorState);
1005+
auto& descriptorSets = descriptorState->sets;
10591006

1060-
assert(descriptorSets[RenderPipelineImpl::SET_INDEX_UBO]);
1007+
assert(descriptorSets[SET_INDEX_UBO]);
10611008

10621009
// Prepare write lists sized to expected UBO + sampler descriptors
10631010
auto& writes = _descriptorWritesPerFrame;
10641011
writes.clear();
1065-
writes.reserve(dslState->uniformDescriptorCount + dslState->samplerDescriptorCount);
1012+
writes.reserve(descriptorState->uniformDescriptorCount + descriptorState->samplerDescriptorCount);
10661013

10671014
_descriptorBufferInfos.clear();
10681015

@@ -1083,7 +1030,7 @@ void RenderContextImpl::prepareDrawing()
10831030
bufferInfo.range = static_cast<VkDeviceSize>(uboInfo.sizeBytes);
10841031

10851032
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1086-
write.dstSet = descriptorSets[RenderPipelineImpl::SET_INDEX_UBO]; // renamed index
1033+
write.dstSet = descriptorSets[SET_INDEX_UBO]; // renamed index
10871034
write.dstBinding = uboInfo.binding;
10881035
write.descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
10891036
write.descriptorCount = 1;
@@ -1094,7 +1041,7 @@ void RenderContextImpl::prepareDrawing()
10941041
// --- Samplers (set=1, binding=N) ---
10951042
auto& imageInfos = _descriptorImageInfosPerFrame;
10961043
imageInfos.clear();
1097-
imageInfos.reserve(dslState->samplerDescriptorCount);
1044+
imageInfos.reserve(descriptorState->samplerDescriptorCount);
10981045

10991046
for (const auto& [bindingIndex, bindingSet] : _programState->getTextureBindingSets())
11001047
{
@@ -1131,7 +1078,7 @@ void RenderContextImpl::prepareDrawing()
11311078

11321079
VkWriteDescriptorSet& write = writes.emplace_back();
11331080
write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
1134-
write.dstSet = descriptorSets[RenderPipelineImpl::SET_INDEX_SAMPLER];
1081+
write.dstSet = descriptorSets[SET_INDEX_SAMPLER];
11351082
write.dstBinding = bindingIndex;
11361083
write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
11371084
write.descriptorCount = static_cast<uint32_t>(texs.size());
@@ -1140,13 +1087,12 @@ void RenderContextImpl::prepareDrawing()
11401087

11411088
// Commit descriptor writes
11421089
if (!writes.empty())
1143-
{
11441090
vkUpdateDescriptorSets(_device, static_cast<uint32_t>(writes.size()), writes.data(), 0, nullptr);
1145-
}
11461091

11471092
// Bind descriptor sets: bind only the sets that exist
1148-
vkCmdBindDescriptorSets(_currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0,
1149-
dslState->descriptorSetLayoutCount, descriptorSets.data(), 0, nullptr);
1093+
auto layoutState = _renderPipeline->getPipelineLayoutState();
1094+
vkCmdBindDescriptorSets(_currentCmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, layoutState->layout, 0,
1095+
layoutState->descriptorSetLayoutCount, descriptorSets.data(), 0, nullptr);
11501096

11511097
// Bind vertex buffers
11521098
_vertexBuffer->setLastFenceValue(_frameFenceValue);

axmol/rhi/vulkan/RenderContextVK.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
#include "axmol/rhi/vulkan/RenderPipelineVK.h"
2727
#include <glad/vulkan.h>
2828

29-
#define _AX_USE_DESCRIPTOR_CACHE 1
30-
3129
namespace ax::rhi::vk
3230
{
3331
class BufferImpl;
@@ -151,10 +149,6 @@ class RenderContextImpl : public RenderContext
151149
};
152150
bool handleSwapchainResult(VkResult result, SwapchainOp op, uint32_t prevSemaphoreIndex);
153151

154-
#if !_AX_USE_DESCRIPTOR_CACHE
155-
void createDescriptorPool();
156-
#endif
157-
158152
void doReadPixels(RenderTarget* rt, bool preserveAxisHint, std::function<void(const PixelBufferDesc&)>& callback);
159153

160154
void markDynamicStateDirty(DynamicStateBits bits) noexcept
@@ -194,10 +188,7 @@ class RenderContextImpl : public RenderContext
194188
uint64_t _completedFenceValue{0};
195189
uint64_t _frameFenceValue{0};
196190

197-
#if !_AX_USE_DESCRIPTOR_CACHE
198-
std::array<VkDescriptorPool, MAX_FRAMES_IN_FLIGHT> _descriptorPools{};
199-
#endif
200-
std::array<tlx::pod_vector<RenderPipelineImpl::DescriptorState>, MAX_FRAMES_IN_FLIGHT> _inFlightDescriptorStates;
191+
std::array<tlx::pod_vector<DescriptorState*>, MAX_FRAMES_IN_FLIGHT> _inFlightDescriptorStates;
201192

202193
tlx::pod_vector<VkSemaphore> _presentCompleteSemaphores;
203194
tlx::pod_vector<VkSemaphore> _renderFinishedSemaphores;

0 commit comments

Comments
 (0)