vk_resource_manager: Implement VKResourceManager and fence allocator
CommitFence iterates a pool of fences until one is found. If all fences are being used at the same time, allocate more.
This commit is contained in:
		@@ -4,12 +4,16 @@
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include "common/assert.h"
 | 
			
		||||
#include "common/logging/log.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/declarations.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_device.h"
 | 
			
		||||
#include "video_core/renderer_vulkan/vk_resource_manager.h"
 | 
			
		||||
 | 
			
		||||
namespace Vulkan {
 | 
			
		||||
 | 
			
		||||
// TODO(Rodrigo): Fine tune these numbers.
 | 
			
		||||
constexpr std::size_t FENCES_GROW_STEP = 0x40;
 | 
			
		||||
 | 
			
		||||
VKResource::VKResource() = default;
 | 
			
		||||
 | 
			
		||||
VKResource::~VKResource() = default;
 | 
			
		||||
@@ -117,4 +121,62 @@ void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) {
 | 
			
		||||
    fence = nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
 | 
			
		||||
    GrowFences(FENCES_GROW_STEP);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void VKResourceManager::GrowFences(std::size_t new_fences_count) {
 | 
			
		||||
    const auto dev = device.GetLogical();
 | 
			
		||||
    const auto& dld = device.GetDispatchLoader();
 | 
			
		||||
    const vk::FenceCreateInfo fence_ci;
 | 
			
		||||
 | 
			
		||||
    const std::size_t previous_size = fences.size();
 | 
			
		||||
    fences.resize(previous_size + new_fences_count);
 | 
			
		||||
 | 
			
		||||
    std::generate(fences.begin() + previous_size, fences.end(), [&]() {
 | 
			
		||||
        return std::make_unique<VKFence>(device, dev.createFenceUnique(fence_ci, nullptr, dld));
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
 
 | 
			
		||||
@@ -4,6 +4,8 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <cstddef>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include "video_core/renderer_vulkan/declarations.h"
 | 
			
		||||
 | 
			
		||||
@@ -116,4 +118,25 @@ private:
 | 
			
		||||
    VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * The resource manager handles all resources that can be protected with a fence avoiding
 | 
			
		||||
 * driver-side or GPU-side concurrent usage. Usage is documented in VKFence.
 | 
			
		||||
 */
 | 
			
		||||
class VKResourceManager final {
 | 
			
		||||
public:
 | 
			
		||||
    explicit VKResourceManager(const VKDevice& device);
 | 
			
		||||
    ~VKResourceManager();
 | 
			
		||||
 | 
			
		||||
    /// Commits a fence. It has to be sent to a queue and released.
 | 
			
		||||
    VKFence& CommitFence();
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
    /// Allocates new fences.
 | 
			
		||||
    void GrowFences(std::size_t new_fences_count);
 | 
			
		||||
 | 
			
		||||
    const VKDevice& device;          ///< Device handler.
 | 
			
		||||
    std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
 | 
			
		||||
    std::vector<std::unique_ptr<VKFence>> fences; ///< Pool of fences.
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Vulkan
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user