改结构
This commit is contained in:
+573
-568
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,6 @@
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace aethera::render_3d::detail {
|
||||
namespace {
|
||||
std::uint64_t elapsed_nanoseconds(
|
||||
@@ -14,20 +13,15 @@ std::uint64_t elapsed_nanoseconds(
|
||||
std::chrono::steady_clock::now() - started).count();
|
||||
return elapsed > 0 ? static_cast<std::uint64_t>(elapsed) : 0ULL;
|
||||
}
|
||||
|
||||
template <typename Value>
|
||||
void update_peak(Value& peak, Value value) noexcept {
|
||||
peak = std::max(peak, value);
|
||||
}
|
||||
}
|
||||
|
||||
Gpu_Completion_Service::Private::Private() = default;
|
||||
|
||||
Gpu_Completion_Service::Private::~Private() = default;
|
||||
|
||||
Gpu_Completion_Service::Gpu_Completion_Service() = default;
|
||||
Gpu_Completion_Service::~Gpu_Completion_Service() = default;
|
||||
|
||||
Gpu_Completion_Service& Gpu_Completion_Service::instance() {
|
||||
/*
|
||||
* GPU completion 域与进程同寿命。主动泄放所有权可避免静态析构阶段
|
||||
@@ -43,26 +37,20 @@ Gpu_Completion_Service& Gpu_Completion_Service::instance() {
|
||||
}();
|
||||
return *service;
|
||||
}
|
||||
|
||||
Gpu_Completion_Service::Reservation::Reservation() = default;
|
||||
|
||||
Gpu_Completion_Service::Reservation::Reservation(
|
||||
std::shared_ptr<Gpu_Completion_Pending_Fence> pending) noexcept
|
||||
: pending_(std::move(pending)) {}
|
||||
|
||||
std::shared_ptr<Gpu_Completion_Pending_Fence> pending) noexcept : pending_(std::move(pending)) {}
|
||||
Gpu_Completion_Service::Reservation::~Reservation() noexcept {
|
||||
try { cancel(); }
|
||||
try {
|
||||
cancel();
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
Gpu_Completion_Service::Reservation::Reservation(
|
||||
Reservation&& other) noexcept
|
||||
: pending_(std::exchange(other.pending_, {})) {}
|
||||
|
||||
Reservation&& other) noexcept : pending_(std::exchange(other.pending_, {})) {}
|
||||
Gpu_Completion_Service::Prepare_Result::operator bool() const noexcept {
|
||||
return result == Admission_Result::none;
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::Reservation::watch(std::uintptr_t device,
|
||||
std::uintptr_t fence) {
|
||||
if (!pending_ || device == 0 || fence == 0)
|
||||
@@ -71,41 +59,32 @@ void Gpu_Completion_Service::Reservation::watch(std::uintptr_t device,
|
||||
pending_->service->watch(pending_, device, fence);
|
||||
pending_.reset();
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::Reservation::cancel() {
|
||||
if (!pending_) return;
|
||||
pending_->service->cancel(pending_);
|
||||
pending_.reset();
|
||||
}
|
||||
|
||||
Gpu_Completion_Service::Prepare_Result Gpu_Completion_Service::prepare(
|
||||
Completion completion, Exception_Handler on_exception, bool observe) {
|
||||
return static_cast<Private&>(*d).prepare(
|
||||
std::move(completion), std::move(on_exception), observe);
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::watch(
|
||||
const std::shared_ptr<Gpu_Completion_Pending_Fence>& pending,
|
||||
std::uintptr_t device, std::uintptr_t fence) {
|
||||
static_cast<Private&>(*d).watch(pending, device, fence);
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::cancel(
|
||||
const std::shared_ptr<Gpu_Completion_Pending_Fence>& pending) {
|
||||
static_cast<Private&>(*d).cancel(pending);
|
||||
}
|
||||
|
||||
Gpu_Completion_Service::Prepare_Result
|
||||
Gpu_Completion_Service::Private::prepare(
|
||||
Gpu_Completion_Service::Prepare_Result Gpu_Completion_Service::Private::prepare(
|
||||
Completion completion, Exception_Handler on_exception, bool observe) {
|
||||
if (!completion)
|
||||
throw std::invalid_argument("GPU completion callback is empty");
|
||||
if (!completion) throw std::invalid_argument("GPU completion callback is empty");
|
||||
if (!on_exception)
|
||||
throw std::invalid_argument(
|
||||
"GPU completion exception handler is empty");
|
||||
if (stopping.load(std::memory_order_acquire))
|
||||
return {{}, Admission_Result::stopping};
|
||||
|
||||
if (stopping.load(std::memory_order_acquire)) return {{}, Admission_Result::stopping};
|
||||
auto pending = std::make_shared<Gpu_Completion_Pending_Fence>();
|
||||
pending->completion = std::move(completion);
|
||||
pending->on_exception = std::move(on_exception);
|
||||
@@ -113,11 +92,11 @@ Gpu_Completion_Service::Private::prepare(
|
||||
pending->service = object;
|
||||
Admission_Result admission{Admission_Result::none};
|
||||
double_buffer::detail::Internal_Access::update_state<
|
||||
&Gpu_Completion_State::State::in_flight, &Gpu_Completion_State::State::peak_in_flight,
|
||||
&Gpu_Completion_State::State::reservation_count, &Gpu_Completion_State::State::backpressure_count>(
|
||||
&State::in_flight, &State::peak_in_flight,
|
||||
&State::reservation_count, &State::backpressure_count>(
|
||||
object,
|
||||
[&](State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
if (state.in_flight >= state.capacity) {
|
||||
++state.backpressure_count;
|
||||
admission = Admission_Result::capacity_exhausted;
|
||||
@@ -127,23 +106,21 @@ Gpu_Completion_Service::Private::prepare(
|
||||
update_peak(state.peak_in_flight, state.in_flight);
|
||||
++state.reservation_count;
|
||||
});
|
||||
if (admission != Admission_Result::none)
|
||||
return {{}, admission};
|
||||
if (admission != Admission_Result::none) return {{}, admission};
|
||||
try {
|
||||
double_buffer::detail::Internal_Access::submit_stream<GPU_Task_Tag>(object, pending);
|
||||
}
|
||||
catch (...) {
|
||||
double_buffer::detail::Internal_Access::update_state<&Gpu_Completion_State::State::in_flight>(object,
|
||||
[](State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
if (state.in_flight != 0) --state.in_flight;
|
||||
});
|
||||
double_buffer::detail::Internal_Access::update_state<&State::in_flight>(object,
|
||||
[](State_Access<State> states) {
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
if (state.in_flight != 0) --state.in_flight;
|
||||
});
|
||||
throw;
|
||||
}
|
||||
request_poll(std::chrono::nanoseconds{1});
|
||||
return {Reservation(std::move(pending)), Admission_Result::none};
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::Private::watch(
|
||||
const std::shared_ptr<Gpu_Completion_Pending_Fence>& pending,
|
||||
std::uintptr_t device, std::uintptr_t fence) {
|
||||
@@ -158,42 +135,39 @@ void Gpu_Completion_Service::Private::watch(
|
||||
pending->watched_at = std::chrono::steady_clock::now();
|
||||
pending->status = Gpu_Completion_Pending_Fence::Status::watched;
|
||||
}
|
||||
double_buffer::detail::Internal_Access::update_state<&Gpu_Completion_State::State::watched, &Gpu_Completion_State::State::peak_watched>(object,
|
||||
[](State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
++state.watched;
|
||||
update_peak(state.peak_watched, state.watched);
|
||||
});
|
||||
double_buffer::detail::Internal_Access::update_state<&State::watched, &State::peak_watched>(object,
|
||||
[](State_Access<State> states) {
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
++state.watched;
|
||||
update_peak(state.peak_watched, state.watched);
|
||||
});
|
||||
request_poll(std::chrono::nanoseconds{1});
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::Private::cancel(
|
||||
const std::shared_ptr<Gpu_Completion_Pending_Fence>& pending) {
|
||||
{
|
||||
std::lock_guard lock(service_mutex);
|
||||
if (!pending || pending->service != object) return;
|
||||
if (pending->status == Gpu_Completion_Pending_Fence::Status::reserved)
|
||||
pending->status = Gpu_Completion_Pending_Fence::Status::canceled;
|
||||
if (pending->status == Gpu_Completion_Pending_Fence::Status::reserved) pending->status = Gpu_Completion_Pending_Fence::Status::canceled;
|
||||
}
|
||||
request_poll(std::chrono::nanoseconds{1});
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::Private::request_poll(
|
||||
std::chrono::nanoseconds delay) noexcept {
|
||||
if (stopping.load(std::memory_order_acquire)) return;
|
||||
try { poll_timer.start_once(delay); }
|
||||
try {
|
||||
poll_timer.start_once(delay);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
|
||||
void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
if (stopping.load(std::memory_order_acquire)) return;
|
||||
try {
|
||||
double_buffer::detail::Internal_Access::exchange_stream<GPU_Task_Tag>(object);
|
||||
double_buffer::detail::Internal_Access::access_rendering_stream<GPU_Task_Tag>(object,
|
||||
[&](std::span<std::shared_ptr<Gpu_Completion_Pending_Fence>> pending) {
|
||||
active.insert(active.end(), pending.begin(), pending.end());
|
||||
});
|
||||
|
||||
[&](std::span<std::shared_ptr<Gpu_Completion_Pending_Fence>> pending) {
|
||||
active.insert(active.end(), pending.begin(), pending.end());
|
||||
});
|
||||
const auto finish = [this](
|
||||
const std::shared_ptr<Gpu_Completion_Pending_Fence>& pending,
|
||||
VkResult vulkan_result, Completion_Error error) noexcept {
|
||||
@@ -202,41 +176,41 @@ void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
Result result{};
|
||||
{
|
||||
std::lock_guard lock(service_mutex);
|
||||
if (pending->status != Gpu_Completion_Pending_Fence::Status::watched)
|
||||
return;
|
||||
if (pending->status != Gpu_Completion_Pending_Fence::Status::watched) return;
|
||||
completion = std::move(pending->completion);
|
||||
on_exception = std::move(pending->on_exception);
|
||||
result.error = error;
|
||||
result.vulkan_result = static_cast<std::int32_t>(vulkan_result);
|
||||
if (pending->observe)
|
||||
result.wait_duration_ns = elapsed_nanoseconds(pending->watched_at);
|
||||
if (pending->observe) result.wait_duration_ns = elapsed_nanoseconds(pending->watched_at);
|
||||
pending->status = Gpu_Completion_Pending_Fence::Status::canceled;
|
||||
}
|
||||
double_buffer::detail::Internal_Access::update_state<
|
||||
&Gpu_Completion_State::State::watched, &Gpu_Completion_State::State::fault_count, &Gpu_Completion_State::State::abandoned_count>(
|
||||
&State::watched, &State::fault_count, &State::abandoned_count>(
|
||||
object,
|
||||
[error](State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
if (state.watched != 0) --state.watched;
|
||||
if (error != Completion_Error::none) {
|
||||
++state.fault_count;
|
||||
if (error == Completion_Error::fence_abandoned)
|
||||
++state.abandoned_count;
|
||||
if (error == Completion_Error::fence_abandoned) ++state.abandoned_count;
|
||||
}
|
||||
});
|
||||
|
||||
const auto callback_started = std::chrono::steady_clock::now();
|
||||
std::exception_ptr callback_failure;
|
||||
try { completion(std::move(result)); }
|
||||
catch (...) { callback_failure = std::current_exception(); }
|
||||
try {
|
||||
completion(std::move(result));
|
||||
}
|
||||
catch (...) {
|
||||
callback_failure = std::current_exception();
|
||||
}
|
||||
const auto callback_ns = elapsed_nanoseconds(callback_started);
|
||||
double_buffer::detail::Internal_Access::update_state<
|
||||
&Gpu_Completion_State::State::callback_total_ns, &Gpu_Completion_State::State::callback_max_ns,
|
||||
&Gpu_Completion_State::State::callback_failure_count, &Gpu_Completion_State::State::completion_count,
|
||||
&Gpu_Completion_State::State::in_flight>(
|
||||
&State::callback_total_ns, &State::callback_max_ns,
|
||||
&State::callback_failure_count, &State::completion_count,
|
||||
&State::in_flight>(
|
||||
object,
|
||||
[callback_ns, &callback_failure](State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
state.callback_total_ns += callback_ns;
|
||||
update_peak(state.callback_max_ns, callback_ns);
|
||||
if (callback_failure) ++state.callback_failure_count;
|
||||
@@ -250,7 +224,6 @@ void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
}
|
||||
catch (...) {}
|
||||
};
|
||||
|
||||
std::size_t reserved_count{};
|
||||
bool needs_more{};
|
||||
for (auto iterator = active.begin(); iterator != active.end();) {
|
||||
@@ -268,10 +241,10 @@ void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
if (status == Gpu_Completion_Pending_Fence::Status::canceled) {
|
||||
iterator = active.erase(iterator);
|
||||
double_buffer::detail::Internal_Access::update_state<
|
||||
&Gpu_Completion_State::State::cancellation_count, &Gpu_Completion_State::State::in_flight>(
|
||||
&State::cancellation_count, &State::in_flight>(
|
||||
object,
|
||||
[](State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
++state.cancellation_count;
|
||||
if (state.in_flight != 0) --state.in_flight;
|
||||
});
|
||||
@@ -288,10 +261,10 @@ void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
finish(pending, VK_TIMEOUT, Completion_Error::fence_abandoned);
|
||||
continue;
|
||||
}
|
||||
double_buffer::detail::Internal_Access::update_state<&Gpu_Completion_State::State::fence_probe_count>(object,
|
||||
[](State_Access<State> states) {
|
||||
++states.template get<Gpu_Completion_State::Base_Tag>().fence_probe_count;
|
||||
});
|
||||
double_buffer::detail::Internal_Access::update_state<&State::fence_probe_count>(object,
|
||||
[](State_Access<State> states) {
|
||||
++states.template get<Base_Tag>().fence_probe_count;
|
||||
});
|
||||
const VkResult result = vkGetFenceStatus(
|
||||
reinterpret_cast<VkDevice>(device),
|
||||
reinterpret_cast<VkFence>(fence));
|
||||
@@ -303,26 +276,25 @@ void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
auto pending = *iterator;
|
||||
iterator = active.erase(iterator);
|
||||
finish(pending, result,
|
||||
result == VK_SUCCESS ? Completion_Error::none
|
||||
: Completion_Error::vulkan_failure);
|
||||
result == VK_SUCCESS
|
||||
? Completion_Error::none
|
||||
: Completion_Error::vulkan_failure);
|
||||
}
|
||||
|
||||
const auto now = std::chrono::steady_clock::now();
|
||||
if (last_state_publication == std::chrono::steady_clock::time_point{} ||
|
||||
now - last_state_publication >= state_publication_interval) {
|
||||
double_buffer::detail::Internal_Access::update_state<
|
||||
&Gpu_Completion_State::State::active_fences, &Gpu_Completion_State::State::pending_fences>(
|
||||
&State::active_fences, &State::pending_fences>(
|
||||
object,
|
||||
[active_count = active.size(), reserved_count](
|
||||
State_Access<State> states) {
|
||||
auto& state = states.template get<Gpu_Completion_State::Base_Tag>();
|
||||
auto& state = states.template get<Base_Tag>();
|
||||
state.active_fences = active_count;
|
||||
state.pending_fences = reserved_count;
|
||||
});
|
||||
double_buffer::detail::Internal_Access::publish_state<Gpu_Completion_State::Base_Tag>(object);
|
||||
double_buffer::detail::Internal_Access::publish_state<Base_Tag>(object);
|
||||
last_state_publication = now;
|
||||
}
|
||||
|
||||
if (needs_more) request_poll(probe_interval);
|
||||
}
|
||||
catch (...) {
|
||||
@@ -332,13 +304,14 @@ void Gpu_Completion_Service::Private::poll() noexcept {
|
||||
std::lock_guard lock(service_mutex);
|
||||
for (auto& pending : active) {
|
||||
pending->status = Gpu_Completion_Pending_Fence::Status::canceled;
|
||||
if (pending->on_exception)
|
||||
handlers.push_back(std::move(pending->on_exception));
|
||||
if (pending->on_exception) handlers.push_back(std::move(pending->on_exception));
|
||||
}
|
||||
active.clear();
|
||||
}
|
||||
for (auto& handler : handlers) {
|
||||
try { handler(failure); }
|
||||
try {
|
||||
handler(failure);
|
||||
}
|
||||
catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
#include <memory>
|
||||
namespace aethera::render_3d::detail {
|
||||
struct GPU_Task_Tag {};
|
||||
|
||||
/* GPU completion 统计是独立状态领域;Service 只负责行为与任务流。 */
|
||||
struct Gpu_Completion_State : Def<Gpu_Completion_State, Root> {
|
||||
struct Gpu_Completion_Pending_Fence;
|
||||
struct Gpu_Completion_Service : Def<Gpu_Completion_Service, Root,
|
||||
Mpmc_Triple_Buffer<GPU_Task_Tag,
|
||||
std::shared_ptr<Gpu_Completion_Pending_Fence>>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
/* GPU completion 统计是独立状态领域;Service 只负责行为与任务流。 */
|
||||
struct State : Prev_State {
|
||||
std::size_t capacity{}; /* 可同时监视的最大 fence 数量。 */
|
||||
std::size_t in_flight{}; /* 已准入且尚未完成的 reservation 数量。 */
|
||||
@@ -35,15 +37,6 @@ struct Gpu_Completion_State : Def<Gpu_Completion_State, Root> {
|
||||
std::uint64_t abandoned_count{};
|
||||
bool operator==(const State&) const = default;
|
||||
};
|
||||
struct Private : Prev_Private {};
|
||||
};
|
||||
|
||||
struct Gpu_Completion_Pending_Fence;
|
||||
struct Gpu_Completion_Service : Def<Gpu_Completion_Service, Gpu_Completion_State,
|
||||
Mpmc_Triple_Buffer<GPU_Task_Tag,
|
||||
std::shared_ptr<Gpu_Completion_Pending_Fence>>> {
|
||||
struct Prop : Prev_Prop {};
|
||||
struct State : Prev_State {};
|
||||
struct Private;
|
||||
enum struct Admission_Result : std::uint8_t {
|
||||
none,
|
||||
|
||||
@@ -35,12 +35,14 @@ struct Gpu_Completion_Service::Private : Prev_Private {
|
||||
void bind_private_crtp(Attached_Object* attached) {
|
||||
Prev_Private::bind_private_crtp(attached);
|
||||
object = not_null{static_cast<Object*>(attached)};
|
||||
double_buffer::detail::Internal_Access::update_state<&Gpu_Completion_State::State::capacity>(object,
|
||||
static_cast<std::size_t>(default_capacity));
|
||||
double_buffer::detail::Internal_Access::publish_state<Gpu_Completion_State::Base_Tag>(object);
|
||||
double_buffer::detail::Internal_Access::update_state<&State::capacity>(object,
|
||||
static_cast<std::size_t>(default_capacity));
|
||||
double_buffer::detail::Internal_Access::publish_state<Base_Tag>(object);
|
||||
active.reserve(static_cast<std::size_t>(default_capacity));
|
||||
poll_timer = Frame_Scheduler::instance().make_timer(
|
||||
[this](Frame_Scheduler::Tick) { poll(); });
|
||||
[this](Frame_Scheduler::Tick) {
|
||||
poll();
|
||||
});
|
||||
}
|
||||
[[nodiscard]] Prepare_Result prepare(Completion completion,
|
||||
Exception_Handler on_exception,
|
||||
@@ -54,6 +56,6 @@ struct Gpu_Completion_Service::Private : Prev_Private {
|
||||
static constexpr auto probe_interval = std::chrono::milliseconds(1);
|
||||
static constexpr std::uint64_t maximum_fence_age_ns = 30'000'000'000ULL;
|
||||
static constexpr auto state_publication_interval =
|
||||
std::chrono::milliseconds(100);
|
||||
std::chrono::milliseconds(100);
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user