Files
Renderive/render_3D/render_3D/detail/Gpu_Completion_Service.h
T
2026-08-15 19:58:39 +08:00

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_;
};
}