|
|
@@ -1,311 +0,0 @@
|
|
|
-// Copyright 2018 yuzu Emulator Project
|
|
|
-// Licensed under GPLv2 or any later version
|
|
|
-// Refer to the license.txt file included.
|
|
|
-
|
|
|
-#include <algorithm>
|
|
|
-#include <optional>
|
|
|
-#include "common/assert.h"
|
|
|
-#include "common/logging/log.h"
|
|
|
-#include "video_core/renderer_vulkan/vk_device.h"
|
|
|
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
|
|
|
-#include "video_core/renderer_vulkan/wrapper.h"
|
|
|
-
|
|
|
-namespace Vulkan {
|
|
|
-
|
|
|
-namespace {
|
|
|
-
|
|
|
-// TODO(Rodrigo): Fine tune these numbers.
|
|
|
-constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
|
|
|
-constexpr std::size_t FENCES_GROW_STEP = 0x40;
|
|
|
-
|
|
|
-constexpr VkFenceCreateInfo BuildFenceCreateInfo() {
|
|
|
- return {
|
|
|
- .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
|
|
|
- .pNext = nullptr,
|
|
|
- .flags = 0,
|
|
|
- };
|
|
|
-}
|
|
|
-
|
|
|
-} // Anonymous namespace
|
|
|
-
|
|
|
-class CommandBufferPool final : public VKFencedPool {
|
|
|
-public:
|
|
|
- explicit CommandBufferPool(const VKDevice& device)
|
|
|
- : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
|
|
|
-
|
|
|
- void Allocate(std::size_t begin, std::size_t end) override {
|
|
|
- // Command buffers are going to be commited, recorded, executed every single usage cycle.
|
|
|
- // They are also going to be reseted when commited.
|
|
|
- Pool& pool = pools.emplace_back();
|
|
|
- pool.handle = device.GetLogical().CreateCommandPool({
|
|
|
- .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
|
|
- .pNext = nullptr,
|
|
|
- .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
|
|
|
- VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
|
|
|
- .queueFamilyIndex = device.GetGraphicsFamily(),
|
|
|
- });
|
|
|
- pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
|
|
|
- }
|
|
|
-
|
|
|
- VkCommandBuffer Commit(VKFence& fence) {
|
|
|
- const std::size_t index = CommitResource(fence);
|
|
|
- const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
|
|
|
- const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
|
|
|
- return pools[pool_index].cmdbufs[sub_index];
|
|
|
- }
|
|
|
-
|
|
|
-private:
|
|
|
- struct Pool {
|
|
|
- vk::CommandPool handle;
|
|
|
- vk::CommandBuffers cmdbufs;
|
|
|
- };
|
|
|
-
|
|
|
- const VKDevice& device;
|
|
|
- std::vector<Pool> pools;
|
|
|
-};
|
|
|
-
|
|
|
-VKResource::VKResource() = default;
|
|
|
-
|
|
|
-VKResource::~VKResource() = default;
|
|
|
-
|
|
|
-VKFence::VKFence(const VKDevice& device)
|
|
|
- : device{device}, handle{device.GetLogical().CreateFence(BuildFenceCreateInfo())} {}
|
|
|
-
|
|
|
-VKFence::~VKFence() = default;
|
|
|
-
|
|
|
-void VKFence::Wait() {
|
|
|
- switch (const VkResult result = handle.Wait()) {
|
|
|
- case VK_SUCCESS:
|
|
|
- return;
|
|
|
- case VK_ERROR_DEVICE_LOST:
|
|
|
- device.ReportLoss();
|
|
|
- [[fallthrough]];
|
|
|
- default:
|
|
|
- throw vk::Exception(result);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void VKFence::Release() {
|
|
|
- ASSERT(is_owned);
|
|
|
- is_owned = false;
|
|
|
-}
|
|
|
-
|
|
|
-void VKFence::Commit() {
|
|
|
- is_owned = true;
|
|
|
- is_used = true;
|
|
|
-}
|
|
|
-
|
|
|
-bool VKFence::Tick(bool gpu_wait, bool owner_wait) {
|
|
|
- if (!is_used) {
|
|
|
- // If a fence is not used it's always free.
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (is_owned && !owner_wait) {
|
|
|
- // The fence is still being owned (Release has not been called) and ownership wait has
|
|
|
- // not been asked.
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- if (gpu_wait) {
|
|
|
- // Wait for the fence if it has been requested.
|
|
|
- (void)handle.Wait();
|
|
|
- } else {
|
|
|
- if (handle.GetStatus() != VK_SUCCESS) {
|
|
|
- // Vulkan fence is not ready, not much it can do here
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // Broadcast resources their free state.
|
|
|
- for (auto* resource : protected_resources) {
|
|
|
- resource->OnFenceRemoval(this);
|
|
|
- }
|
|
|
- protected_resources.clear();
|
|
|
-
|
|
|
- // Prepare fence for reusage.
|
|
|
- handle.Reset();
|
|
|
- is_used = false;
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void VKFence::Protect(VKResource* resource) {
|
|
|
- protected_resources.push_back(resource);
|
|
|
-}
|
|
|
-
|
|
|
-void VKFence::Unprotect(VKResource* resource) {
|
|
|
- const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
|
|
|
- ASSERT(it != protected_resources.end());
|
|
|
-
|
|
|
- resource->OnFenceRemoval(this);
|
|
|
- protected_resources.erase(it);
|
|
|
-}
|
|
|
-
|
|
|
-void VKFence::RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept {
|
|
|
- std::replace(std::begin(protected_resources), std::end(protected_resources), old_resource,
|
|
|
- new_resource);
|
|
|
-}
|
|
|
-
|
|
|
-VKFenceWatch::VKFenceWatch() = default;
|
|
|
-
|
|
|
-VKFenceWatch::VKFenceWatch(VKFence& initial_fence) {
|
|
|
- Watch(initial_fence);
|
|
|
-}
|
|
|
-
|
|
|
-VKFenceWatch::VKFenceWatch(VKFenceWatch&& rhs) noexcept {
|
|
|
- fence = std::exchange(rhs.fence, nullptr);
|
|
|
- if (fence) {
|
|
|
- fence->RedirectProtection(&rhs, this);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-VKFenceWatch& VKFenceWatch::operator=(VKFenceWatch&& rhs) noexcept {
|
|
|
- fence = std::exchange(rhs.fence, nullptr);
|
|
|
- if (fence) {
|
|
|
- fence->RedirectProtection(&rhs, this);
|
|
|
- }
|
|
|
- return *this;
|
|
|
-}
|
|
|
-
|
|
|
-VKFenceWatch::~VKFenceWatch() {
|
|
|
- if (fence) {
|
|
|
- fence->Unprotect(this);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-void VKFenceWatch::Wait() {
|
|
|
- if (fence == nullptr) {
|
|
|
- return;
|
|
|
- }
|
|
|
- fence->Wait();
|
|
|
- fence->Unprotect(this);
|
|
|
-}
|
|
|
-
|
|
|
-void VKFenceWatch::Watch(VKFence& new_fence) {
|
|
|
- Wait();
|
|
|
- fence = &new_fence;
|
|
|
- fence->Protect(this);
|
|
|
-}
|
|
|
-
|
|
|
-bool VKFenceWatch::TryWatch(VKFence& new_fence) {
|
|
|
- if (fence) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- fence = &new_fence;
|
|
|
- fence->Protect(this);
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
-void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) {
|
|
|
- ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence");
|
|
|
- fence = nullptr;
|
|
|
-}
|
|
|
-
|
|
|
-VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {}
|
|
|
-
|
|
|
-VKFencedPool::~VKFencedPool() = default;
|
|
|
-
|
|
|
-std::size_t VKFencedPool::CommitResource(VKFence& fence) {
|
|
|
- const auto Search = [&](std::size_t begin, std::size_t end) -> std::optional<std::size_t> {
|
|
|
- for (std::size_t iterator = begin; iterator < end; ++iterator) {
|
|
|
- if (watches[iterator]->TryWatch(fence)) {
|
|
|
- // The resource is now being watched, a free resource was successfully found.
|
|
|
- return iterator;
|
|
|
- }
|
|
|
- }
|
|
|
- return {};
|
|
|
- };
|
|
|
- // Try to find a free resource from the hinted position to the end.
|
|
|
- auto found = Search(free_iterator, watches.size());
|
|
|
- if (!found) {
|
|
|
- // Search from beginning to the hinted position.
|
|
|
- found = Search(0, free_iterator);
|
|
|
- if (!found) {
|
|
|
- // Both searches failed, the pool is full; handle it.
|
|
|
- const std::size_t free_resource = ManageOverflow();
|
|
|
-
|
|
|
- // Watch will wait for the resource to be free.
|
|
|
- watches[free_resource]->Watch(fence);
|
|
|
- found = free_resource;
|
|
|
- }
|
|
|
- }
|
|
|
- // Free iterator is hinted to the resource after the one that's been commited.
|
|
|
- free_iterator = (*found + 1) % watches.size();
|
|
|
- return *found;
|
|
|
-}
|
|
|
-
|
|
|
-std::size_t VKFencedPool::ManageOverflow() {
|
|
|
- const std::size_t old_capacity = watches.size();
|
|
|
- Grow();
|
|
|
-
|
|
|
- // The last entry is guaranted to be free, since it's the first element of the freshly
|
|
|
- // allocated resources.
|
|
|
- return old_capacity;
|
|
|
-}
|
|
|
-
|
|
|
-void VKFencedPool::Grow() {
|
|
|
- const std::size_t old_capacity = watches.size();
|
|
|
- watches.resize(old_capacity + grow_step);
|
|
|
- std::generate(watches.begin() + old_capacity, watches.end(),
|
|
|
- []() { return std::make_unique<VKFenceWatch>(); });
|
|
|
- Allocate(old_capacity, old_capacity + grow_step);
|
|
|
-}
|
|
|
-
|
|
|
-VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
|
|
|
- GrowFences(FENCES_GROW_STEP);
|
|
|
- command_buffer_pool = std::make_unique<CommandBufferPool>(device);
|
|
|
-}
|
|
|
-
|
|
|
-VKResourceManager::~VKResourceManager() = default;
|
|
|
-
|
|
|
-VKFence& VKResourceManager::CommitFence() {
|
|
|
- const auto StepFences = [&](bool gpu_wait, bool owner_wait) -> VKFence* {
|
|
|
- const auto Tick = [=](auto& fence) { return fence->Tick(gpu_wait, owner_wait); };
|
|
|
- const auto hinted = fences.begin() + fences_iterator;
|
|
|
-
|
|
|
- auto it = std::find_if(hinted, fences.end(), Tick);
|
|
|
- if (it == fences.end()) {
|
|
|
- it = std::find_if(fences.begin(), hinted, Tick);
|
|
|
- if (it == hinted) {
|
|
|
- return nullptr;
|
|
|
- }
|
|
|
- }
|
|
|
- fences_iterator = std::distance(fences.begin(), it) + 1;
|
|
|
- if (fences_iterator >= fences.size())
|
|
|
- fences_iterator = 0;
|
|
|
-
|
|
|
- auto& fence = *it;
|
|
|
- fence->Commit();
|
|
|
- return fence.get();
|
|
|
- };
|
|
|
-
|
|
|
- VKFence* found_fence = StepFences(false, false);
|
|
|
- if (!found_fence) {
|
|
|
- // Try again, this time waiting.
|
|
|
- found_fence = StepFences(true, false);
|
|
|
-
|
|
|
- if (!found_fence) {
|
|
|
- // Allocate new fences and try again.
|
|
|
- LOG_INFO(Render_Vulkan, "Allocating new fences {} -> {}", fences.size(),
|
|
|
- fences.size() + FENCES_GROW_STEP);
|
|
|
-
|
|
|
- GrowFences(FENCES_GROW_STEP);
|
|
|
- found_fence = StepFences(true, false);
|
|
|
- ASSERT(found_fence != nullptr);
|
|
|
- }
|
|
|
- }
|
|
|
- return *found_fence;
|
|
|
-}
|
|
|
-
|
|
|
-VkCommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) {
|
|
|
- return command_buffer_pool->Commit(fence);
|
|
|
-}
|
|
|
-
|
|
|
-void VKResourceManager::GrowFences(std::size_t new_fences_count) {
|
|
|
- const std::size_t previous_size = fences.size();
|
|
|
- fences.resize(previous_size + new_fences_count);
|
|
|
-
|
|
|
- std::generate(fences.begin() + previous_size, fences.end(),
|
|
|
- [this] { return std::make_unique<VKFence>(device); });
|
|
|
-}
|
|
|
-
|
|
|
-} // namespace Vulkan
|