2020-01-01 20:44:06 +01:00
|
|
|
// Copyright 2019 yuzu Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
2021-09-09 05:49:08 +02:00
|
|
|
#include <algorithm>
|
2021-04-25 05:15:32 +02:00
|
|
|
#include <mutex>
|
|
|
|
#include <span>
|
2020-01-01 20:44:06 +01:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
|
2020-09-10 08:43:30 +02:00
|
|
|
#include "video_core/renderer_vulkan/vk_resource_pool.h"
|
|
|
|
#include "video_core/renderer_vulkan/vk_scheduler.h"
|
2020-12-26 05:19:46 +01:00
|
|
|
#include "video_core/vulkan_common/vulkan_device.h"
|
2020-12-25 01:30:11 +01:00
|
|
|
#include "video_core/vulkan_common/vulkan_wrapper.h"
|
2020-01-01 20:44:06 +01:00
|
|
|
|
|
|
|
namespace Vulkan {
|
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines
|
|
|
|
constexpr size_t SETS_GROW_RATE = 16;
|
|
|
|
constexpr s32 SCORE_THRESHOLD = 3;
|
2020-01-01 20:44:06 +01:00
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
struct DescriptorBank {
|
|
|
|
DescriptorBankInfo info;
|
|
|
|
std::vector<vk::DescriptorPool> pools;
|
|
|
|
};
|
2020-01-01 20:44:06 +01:00
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
bool DescriptorBankInfo::IsSuperset(const DescriptorBankInfo& subset) const noexcept {
|
|
|
|
return uniform_buffers >= subset.uniform_buffers && storage_buffers >= subset.storage_buffers &&
|
|
|
|
texture_buffers >= subset.texture_buffers && image_buffers >= subset.image_buffers &&
|
|
|
|
textures >= subset.textures && images >= subset.image_buffers;
|
2020-01-01 20:44:06 +01:00
|
|
|
}
|
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
template <typename Descriptors>
|
|
|
|
static u32 Accumulate(const Descriptors& descriptors) {
|
|
|
|
u32 count = 0;
|
|
|
|
for (const auto& descriptor : descriptors) {
|
|
|
|
count += descriptor.count;
|
|
|
|
}
|
|
|
|
return count;
|
2020-01-01 20:44:06 +01:00
|
|
|
}
|
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
static DescriptorBankInfo MakeBankInfo(std::span<const Shader::Info> infos) {
|
|
|
|
DescriptorBankInfo bank;
|
|
|
|
for (const Shader::Info& info : infos) {
|
|
|
|
bank.uniform_buffers += Accumulate(info.constant_buffer_descriptors);
|
|
|
|
bank.storage_buffers += Accumulate(info.storage_buffers_descriptors);
|
|
|
|
bank.texture_buffers += Accumulate(info.texture_buffer_descriptors);
|
|
|
|
bank.image_buffers += Accumulate(info.image_buffer_descriptors);
|
|
|
|
bank.textures += Accumulate(info.texture_descriptors);
|
|
|
|
bank.images += Accumulate(info.image_descriptors);
|
|
|
|
}
|
|
|
|
bank.score = bank.uniform_buffers + bank.storage_buffers + bank.texture_buffers +
|
|
|
|
bank.image_buffers + bank.textures + bank.images;
|
|
|
|
return bank;
|
|
|
|
}
|
2020-07-17 00:19:45 +02:00
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
static void AllocatePool(const Device& device, DescriptorBank& bank) {
|
|
|
|
std::array<VkDescriptorPoolSize, 6> pool_sizes;
|
|
|
|
size_t pool_cursor{};
|
2021-08-29 22:45:17 +02:00
|
|
|
const u32 sets_per_pool = device.GetSetsPerPool();
|
2021-04-25 05:15:32 +02:00
|
|
|
const auto add = [&](VkDescriptorType type, u32 count) {
|
|
|
|
if (count > 0) {
|
|
|
|
pool_sizes[pool_cursor++] = {
|
|
|
|
.type = type,
|
2021-08-29 22:45:17 +02:00
|
|
|
.descriptorCount = count * sets_per_pool,
|
2021-04-25 05:15:32 +02:00
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const auto& info{bank.info};
|
|
|
|
add(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, info.uniform_buffers);
|
|
|
|
add(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, info.storage_buffers);
|
|
|
|
add(VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, info.texture_buffers);
|
|
|
|
add(VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, info.image_buffers);
|
|
|
|
add(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, info.textures);
|
|
|
|
add(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, info.images);
|
|
|
|
bank.pools.push_back(device.GetLogical().CreateDescriptorPool({
|
2020-07-17 00:19:45 +02:00
|
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
|
|
|
.pNext = nullptr,
|
|
|
|
.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
|
2021-08-29 22:45:17 +02:00
|
|
|
.maxSets = sets_per_pool,
|
2021-04-25 05:15:32 +02:00
|
|
|
.poolSizeCount = static_cast<u32>(pool_cursor),
|
2020-07-17 00:19:45 +02:00
|
|
|
.pPoolSizes = std::data(pool_sizes),
|
2021-04-25 05:15:32 +02:00
|
|
|
}));
|
2020-01-01 20:44:06 +01:00
|
|
|
}
|
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
DescriptorAllocator::DescriptorAllocator(const Device& device_, MasterSemaphore& master_semaphore_,
|
|
|
|
DescriptorBank& bank_, VkDescriptorSetLayout layout_)
|
|
|
|
: ResourcePool(master_semaphore_, SETS_GROW_RATE), device{&device_}, bank{&bank_},
|
|
|
|
layout{layout_} {}
|
|
|
|
|
|
|
|
VkDescriptorSet DescriptorAllocator::Commit() {
|
|
|
|
const size_t index = CommitResource();
|
|
|
|
return sets[index / SETS_GROW_RATE][index % SETS_GROW_RATE];
|
|
|
|
}
|
|
|
|
|
|
|
|
void DescriptorAllocator::Allocate(size_t begin, size_t end) {
|
|
|
|
sets.push_back(AllocateDescriptors(end - begin));
|
|
|
|
}
|
|
|
|
|
|
|
|
vk::DescriptorSets DescriptorAllocator::AllocateDescriptors(size_t count) {
|
|
|
|
const std::vector<VkDescriptorSetLayout> layouts(count, layout);
|
|
|
|
VkDescriptorSetAllocateInfo allocate_info{
|
2020-07-17 00:19:45 +02:00
|
|
|
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
|
|
|
.pNext = nullptr,
|
2021-04-25 05:15:32 +02:00
|
|
|
.descriptorPool = *bank->pools.back(),
|
2020-07-17 00:19:45 +02:00
|
|
|
.descriptorSetCount = static_cast<u32>(count),
|
2021-04-25 05:15:32 +02:00
|
|
|
.pSetLayouts = layouts.data(),
|
2020-07-17 00:19:45 +02:00
|
|
|
};
|
2021-04-25 05:15:32 +02:00
|
|
|
vk::DescriptorSets new_sets = bank->pools.back().Allocate(allocate_info);
|
|
|
|
if (!new_sets.IsOutOfPoolMemory()) {
|
|
|
|
return new_sets;
|
2020-01-01 20:44:06 +01:00
|
|
|
}
|
2020-03-27 05:33:21 +01:00
|
|
|
// Our current pool is out of memory. Allocate a new one and retry
|
2021-04-25 05:15:32 +02:00
|
|
|
AllocatePool(*device, *bank);
|
|
|
|
allocate_info.descriptorPool = *bank->pools.back();
|
|
|
|
new_sets = bank->pools.back().Allocate(allocate_info);
|
|
|
|
if (!new_sets.IsOutOfPoolMemory()) {
|
|
|
|
return new_sets;
|
2020-01-01 20:44:06 +01:00
|
|
|
}
|
2020-03-27 05:33:21 +01:00
|
|
|
// After allocating a new pool, we are out of memory again. We can't handle this from here.
|
|
|
|
throw vk::Exception(VK_ERROR_OUT_OF_POOL_MEMORY);
|
2020-01-01 20:44:06 +01:00
|
|
|
}
|
|
|
|
|
2021-04-25 05:15:32 +02:00
|
|
|
DescriptorPool::DescriptorPool(const Device& device_, VKScheduler& scheduler)
|
|
|
|
: device{device_}, master_semaphore{scheduler.GetMasterSemaphore()} {}
|
|
|
|
|
|
|
|
DescriptorPool::~DescriptorPool() = default;
|
|
|
|
|
|
|
|
DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
|
|
|
|
std::span<const Shader::Info> infos) {
|
|
|
|
return Allocator(layout, MakeBankInfo(infos));
|
|
|
|
}
|
|
|
|
|
|
|
|
DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
|
|
|
|
const Shader::Info& info) {
|
|
|
|
return Allocator(layout, MakeBankInfo(std::array{info}));
|
|
|
|
}
|
|
|
|
|
|
|
|
DescriptorAllocator DescriptorPool::Allocator(VkDescriptorSetLayout layout,
|
|
|
|
const DescriptorBankInfo& info) {
|
|
|
|
return DescriptorAllocator(device, master_semaphore, Bank(info), layout);
|
|
|
|
}
|
|
|
|
|
|
|
|
DescriptorBank& DescriptorPool::Bank(const DescriptorBankInfo& reqs) {
|
|
|
|
std::shared_lock read_lock{banks_mutex};
|
|
|
|
const auto it = std::ranges::find_if(bank_infos, [&reqs](const DescriptorBankInfo& bank) {
|
|
|
|
return std::abs(bank.score - reqs.score) < SCORE_THRESHOLD && bank.IsSuperset(reqs);
|
|
|
|
});
|
|
|
|
if (it != bank_infos.end()) {
|
|
|
|
return *banks[std::distance(bank_infos.begin(), it)].get();
|
|
|
|
}
|
|
|
|
read_lock.unlock();
|
|
|
|
|
|
|
|
std::unique_lock write_lock{banks_mutex};
|
|
|
|
bank_infos.push_back(reqs);
|
|
|
|
|
|
|
|
auto& bank = *banks.emplace_back(std::make_unique<DescriptorBank>());
|
|
|
|
bank.info = reqs;
|
|
|
|
AllocatePool(device, bank);
|
|
|
|
return bank;
|
|
|
|
}
|
|
|
|
|
2020-01-01 20:44:06 +01:00
|
|
|
} // namespace Vulkan
|