mirror of
https://github.com/skyline-emu/skyline.git
synced 2024-12-23 19:51:52 +01:00
Implement Vulkan Descriptor Set Allocator
A fixed descriptor set allocator which manages the size of the pool with automatic reallocations when any allocations run out of descriptors.
This commit is contained in:
parent
9af9f1d41a
commit
492dd47218
@ -159,6 +159,7 @@ add_library(skyline SHARED
|
|||||||
${source_DIR}/skyline/gpu/texture_manager.cpp
|
${source_DIR}/skyline/gpu/texture_manager.cpp
|
||||||
${source_DIR}/skyline/gpu/buffer_manager.cpp
|
${source_DIR}/skyline/gpu/buffer_manager.cpp
|
||||||
${source_DIR}/skyline/gpu/command_scheduler.cpp
|
${source_DIR}/skyline/gpu/command_scheduler.cpp
|
||||||
|
${source_DIR}/skyline/gpu/descriptor_allocator.cpp
|
||||||
${source_DIR}/skyline/gpu/texture/texture.cpp
|
${source_DIR}/skyline/gpu/texture/texture.cpp
|
||||||
${source_DIR}/skyline/gpu/buffer.cpp
|
${source_DIR}/skyline/gpu/buffer.cpp
|
||||||
${source_DIR}/skyline/gpu/presentation_engine.cpp
|
${source_DIR}/skyline/gpu/presentation_engine.cpp
|
||||||
|
@ -214,5 +214,6 @@ namespace skyline::gpu {
|
|||||||
presentation(state, *this),
|
presentation(state, *this),
|
||||||
texture(*this),
|
texture(*this),
|
||||||
buffer(*this),
|
buffer(*this),
|
||||||
|
descriptor(*this),
|
||||||
shader(state, *this) {}
|
shader(state, *this) {}
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "gpu/presentation_engine.h"
|
#include "gpu/presentation_engine.h"
|
||||||
#include "gpu/texture_manager.h"
|
#include "gpu/texture_manager.h"
|
||||||
#include "gpu/buffer_manager.h"
|
#include "gpu/buffer_manager.h"
|
||||||
|
#include "gpu/descriptor_allocator.h"
|
||||||
#include "gpu/shader_manager.h"
|
#include "gpu/shader_manager.h"
|
||||||
|
|
||||||
namespace skyline::gpu {
|
namespace skyline::gpu {
|
||||||
@ -48,6 +49,7 @@ namespace skyline::gpu {
|
|||||||
TextureManager texture;
|
TextureManager texture;
|
||||||
BufferManager buffer;
|
BufferManager buffer;
|
||||||
|
|
||||||
|
DescriptorAllocator descriptor;
|
||||||
ShaderManager shader;
|
ShaderManager shader;
|
||||||
|
|
||||||
GPU(const DeviceState &state);
|
GPU(const DeviceState &state);
|
||||||
|
80
app/src/main/cpp/skyline/gpu/descriptor_allocator.cpp
Normal file
80
app/src/main/cpp/skyline/gpu/descriptor_allocator.cpp
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#include <gpu.h>
|
||||||
|
#include <soc/gm20b/engines/maxwell/types.h>
|
||||||
|
#include "descriptor_allocator.h"
|
||||||
|
|
||||||
|
namespace skyline::gpu {
|
||||||
|
DescriptorAllocator::DescriptorPool::DescriptorPool(const vk::raii::Device &device, const vk::DescriptorPoolCreateInfo &createInfo) : vk::raii::DescriptorPool(device, createInfo), setCount(createInfo.maxSets) {}
|
||||||
|
|
||||||
|
void DescriptorAllocator::AllocateDescriptorPool() {
|
||||||
|
namespace maxwell3d = soc::gm20b::engine::maxwell3d::type; // We use Maxwell3D as reference for base descriptor counts
|
||||||
|
using DescriptorSizes = std::array<vk::DescriptorPoolSize, 1>;
|
||||||
|
constexpr DescriptorSizes BaseDescriptorSizes{
|
||||||
|
vk::DescriptorPoolSize{
|
||||||
|
.descriptorCount = maxwell3d::PipelineStageConstantBufferCount,
|
||||||
|
.type = vk::DescriptorType::eUniformBuffer,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
DescriptorSizes descriptorSizes{BaseDescriptorSizes};
|
||||||
|
for (auto &descriptorSize : descriptorSizes)
|
||||||
|
descriptorSize.descriptorCount *= descriptorMultiplier;
|
||||||
|
|
||||||
|
pool = std::make_shared<DescriptorPool>(gpu.vkDevice, vk::DescriptorPoolCreateInfo{
|
||||||
|
.flags = vk::DescriptorPoolCreateFlagBits::eFreeDescriptorSet,
|
||||||
|
.maxSets = descriptorSetCount,
|
||||||
|
.pPoolSizes = descriptorSizes.data(),
|
||||||
|
.poolSizeCount = descriptorSizes.size(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorAllocator::ActiveDescriptorSet::ActiveDescriptorSet(std::shared_ptr<DescriptorPool> pPool, vk::DescriptorSet set) : pool(std::move(pPool)), DescriptorSet(set) {
|
||||||
|
pool->setCount--;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorAllocator::ActiveDescriptorSet::~ActiveDescriptorSet() {
|
||||||
|
std::scoped_lock lock(*pool);
|
||||||
|
pool->getDevice().freeDescriptorSets(**pool, 1, this, *pool->getDispatcher());
|
||||||
|
pool->setCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorAllocator::DescriptorAllocator(GPU &gpu) : gpu(gpu) {
|
||||||
|
AllocateDescriptorPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
DescriptorAllocator::ActiveDescriptorSet DescriptorAllocator::AllocateSet(vk::DescriptorSetLayout layout) {
|
||||||
|
std::scoped_lock allocatorLock(mutex);
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
std::scoped_lock poolLock(*pool);
|
||||||
|
|
||||||
|
vk::DescriptorSetAllocateInfo allocateInfo{
|
||||||
|
.descriptorPool = **pool,
|
||||||
|
.pSetLayouts = &layout,
|
||||||
|
.descriptorSetCount = 1,
|
||||||
|
};
|
||||||
|
vk::DescriptorSet set{};
|
||||||
|
|
||||||
|
auto result{(*gpu.vkDevice).allocateDescriptorSets(&allocateInfo, &set, *gpu.vkDevice.getDispatcher())};
|
||||||
|
if (result == vk::Result::eSuccess) {
|
||||||
|
return ActiveDescriptorSet(pool, set);
|
||||||
|
} else if (result == vk::Result::eErrorOutOfPoolMemory) {
|
||||||
|
if (pool->setCount == 0)
|
||||||
|
// The amount of maximum descriptor sets is insufficient
|
||||||
|
descriptorSetCount += BaseDescriptorSetCount;
|
||||||
|
else
|
||||||
|
// The amount of maximum descriptors is insufficient
|
||||||
|
descriptorMultiplier++;
|
||||||
|
AllocateDescriptorPool();
|
||||||
|
continue; // Attempt to allocate again with the new pool
|
||||||
|
} else if (result == vk::Result::eErrorFragmentedPool) {
|
||||||
|
AllocateDescriptorPool(); // If the pool is fragmented, we reallocate without increasing the size
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
vk::throwResultException(result, __builtin_FUNCTION());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
63
app/src/main/cpp/skyline/gpu/descriptor_allocator.h
Normal file
63
app/src/main/cpp/skyline/gpu/descriptor_allocator.h
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
// SPDX-License-Identifier: MPL-2.0
|
||||||
|
// Copyright © 2021 Skyline Team and Contributors (https://github.com/skyline-emu/)
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "fence_cycle.h"
|
||||||
|
|
||||||
|
namespace skyline::gpu {
|
||||||
|
/**
|
||||||
|
* @brief A dynamic descriptor set allocator with internal resizing of the descriptor pool to size up to allocation demand
|
||||||
|
*/
|
||||||
|
class DescriptorAllocator {
|
||||||
|
private:
|
||||||
|
GPU &gpu;
|
||||||
|
std::mutex mutex; //!< Synchronizes the creation and replacement of the pool object
|
||||||
|
|
||||||
|
static constexpr u32 BaseDescriptorSetCount{64}; //!< An arbitrary amount of descriptor sets that we allocate in multiples of
|
||||||
|
u32 descriptorSetCount{BaseDescriptorSetCount}; //!< The maximum amount of descriptor sets in the pool
|
||||||
|
u32 descriptorMultiplier{1}; //!< A multiplier for the maximum amount of descriptors in the pool
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief A lockable VkDescriptorPool for maintaining external synchronization requirements
|
||||||
|
*/
|
||||||
|
struct DescriptorPool : public std::mutex, public vk::raii::DescriptorPool {
|
||||||
|
u64 setCount{}; //!< The amount of sets free to allocate from this pool
|
||||||
|
|
||||||
|
DescriptorPool(vk::raii::Device const &device, vk::DescriptorPoolCreateInfo const &createInfo);
|
||||||
|
};
|
||||||
|
|
||||||
|
std::shared_ptr<DescriptorPool> pool; //!< The current pool used by any allocations in the class, replaced when an error is ran into
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief (Re-)Allocates the descriptor pool with the current multiplier applied to the base counts
|
||||||
|
* @note `DescriptorAllocator::mutex` **must** be locked prior to calling this
|
||||||
|
*/
|
||||||
|
void AllocateDescriptorPool();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief A RAII-bound descriptor set that automatically frees of resources into the pool on destruction while respecting external synchronization requirements
|
||||||
|
*/
|
||||||
|
struct ActiveDescriptorSet : public vk::DescriptorSet {
|
||||||
|
private:
|
||||||
|
friend DescriptorAllocator;
|
||||||
|
std::shared_ptr<DescriptorPool> pool;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note The supplied pool **must** be locked prior to calling this
|
||||||
|
*/
|
||||||
|
ActiveDescriptorSet(std::shared_ptr<DescriptorPool> pool, vk::DescriptorSet set);
|
||||||
|
|
||||||
|
public:
|
||||||
|
~ActiveDescriptorSet();
|
||||||
|
};
|
||||||
|
|
||||||
|
DescriptorAllocator(GPU &gpu);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @note It is UB to allocate a set with a descriptor type that isn't in the pool
|
||||||
|
*/
|
||||||
|
ActiveDescriptorSet AllocateSet(vk::DescriptorSetLayout layout);
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user