94 lines
3.2 KiB
C++
94 lines
3.2 KiB
C++
#pragma once
|
|
#include <volk.h>
|
|
#include <atomic>
|
|
#include <chrono>
|
|
#include <condition_variable>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <exception>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <semaphore>
|
|
#include <thread>
|
|
#include <oneapi/tbb/concurrent_queue.h>
|
|
namespace renderive::render_3d::detail {
|
|
class Gpu_Completion_Service final {
|
|
struct Pending_Fence;
|
|
public:
|
|
struct Result {
|
|
std::exception_ptr error;
|
|
std::uint64_t wait_duration_ns{};
|
|
};
|
|
struct Statistics {
|
|
std::size_t capacity{};
|
|
std::size_t in_flight{};
|
|
std::size_t peak_in_flight{};
|
|
std::size_t watched{};
|
|
std::size_t peak_watched{};
|
|
std::uint64_t backpressure_count{};
|
|
std::uint64_t backpressure_wait_ns{};
|
|
};
|
|
using Completion = std::function<void(Result)>;
|
|
class Reservation final {
|
|
public:
|
|
Reservation() = default;
|
|
~Reservation();
|
|
Reservation(const Reservation&) = delete;
|
|
Reservation& operator=(const Reservation&) = delete;
|
|
Reservation(Reservation&& other) noexcept;
|
|
Reservation& operator=(Reservation&&) = delete;
|
|
void watch(VkDevice device, VkFence fence) noexcept;
|
|
private:
|
|
explicit Reservation(std::shared_ptr<Pending_Fence> pending) noexcept;
|
|
void cancel() noexcept;
|
|
std::shared_ptr<Pending_Fence> pending_;
|
|
friend class Gpu_Completion_Service;
|
|
};
|
|
static Gpu_Completion_Service& instance();
|
|
Gpu_Completion_Service(const Gpu_Completion_Service&) = delete;
|
|
Gpu_Completion_Service& operator=(const Gpu_Completion_Service&) = delete;
|
|
[[nodiscard]] Reservation prepare(Completion completion, bool observe);
|
|
[[nodiscard]] Statistics statistics() const noexcept;
|
|
private:
|
|
struct Pending_Fence {
|
|
enum class Status {
|
|
reserved,
|
|
watched,
|
|
canceled
|
|
};
|
|
std::mutex mutex;
|
|
VkDevice device{VK_NULL_HANDLE};
|
|
VkFence fence{VK_NULL_HANDLE};
|
|
Completion completion;
|
|
std::chrono::steady_clock::time_point watched_at{};
|
|
Gpu_Completion_Service* service{};
|
|
Status status{Status::reserved};
|
|
bool observe{};
|
|
};
|
|
Gpu_Completion_Service();
|
|
~Gpu_Completion_Service();
|
|
static void update_peak(std::atomic_size_t& peak, std::size_t value) noexcept;
|
|
static void cancel_reserved(const std::shared_ptr<Pending_Fence>& pending) noexcept;
|
|
void acquire_slot();
|
|
void release_slot() noexcept;
|
|
void wake() noexcept;
|
|
void run() noexcept;
|
|
static constexpr std::ptrdiff_t default_capacity = 1024;
|
|
static constexpr auto poll_interval = std::chrono::microseconds(200);
|
|
std::counting_semaphore<default_capacity> slots_{default_capacity};
|
|
oneapi::tbb::concurrent_bounded_queue<std::shared_ptr<Pending_Fence>> pending_;
|
|
std::mutex wait_mutex_;
|
|
std::condition_variable wake_condition_;
|
|
std::atomic_uint64_t wake_generation_{};
|
|
std::atomic_size_t in_flight_{};
|
|
std::atomic_size_t peak_in_flight_{};
|
|
std::atomic_size_t watched_{};
|
|
std::atomic_size_t peak_watched_{};
|
|
std::atomic_uint64_t backpressure_count_{};
|
|
std::atomic_uint64_t backpressure_wait_ns_{};
|
|
std::atomic_bool stopping_{};
|
|
std::thread thread_;
|
|
};
|
|
}
|