Files
Renderive/Core/architecture/Update_Completion.cpp
T
2026-08-01 20:09:45 +08:00

127 lines
4.9 KiB
C++

#include "Update_Completion.h"
#include <algorithm>
namespace renderive {
namespace {
std::atomic<Update_Id> next_update_id{1};
int stage_index(Update_Stage stage) {
switch (stage) {
case Update_Stage::Input_Released:
return 0;
case Update_Stage::Committed:
return 1;
case Update_Stage::Rendered:
return 2;
case Update_Stage::Presented:
return 3;
}
ASSERT(false, "Invalid Update_Stage");
return 3;
}
}
Update_State::Stage_Record::Stage_Record(std::pmr::memory_resource* resource) : waiters(resource) {}
Update_State::Update_State(Update_Id id) : update_id(id), input_released(memory_resource(Memory_Domain::Update_Completion)), committed(memory_resource(Memory_Domain::Update_Completion)), rendered(memory_resource(Memory_Domain::Update_Completion)), presented(memory_resource(Memory_Domain::Update_Completion)) {}
Update_Id Update_State::id() const noexcept {
return update_id;
}
bool Update_State::is_complete() const noexcept {
return terminal.load(std::memory_order_acquire);
}
bool Update_State::stage_complete(Update_Stage stage) const noexcept {
std::lock_guard<std::mutex> lock(mutex);
return slot(stage).completed;
}
Update_State::Stage_Record& Update_State::slot(Update_Stage stage) {
switch (stage) {
case Update_Stage::Input_Released:
return input_released;
case Update_Stage::Committed:
return committed;
case Update_Stage::Rendered:
return rendered;
case Update_Stage::Presented:
return presented;
}
ASSERT(false, "Invalid Update_Stage");
return presented;
}
const Update_State::Stage_Record& Update_State::slot(Update_Stage stage) const {
return const_cast<Update_State*>(this)->slot(stage);
}
void Update_State::register_waiter(Update_Stage stage, Update_Waiter waiter) {
std::error_code error;
Update_Outcome outcome = Update_Outcome::Cancelled;
bool ready = false;
{
std::lock_guard<std::mutex> lock(mutex);
Stage_Record& record = slot(stage);
if (record.completed) {
error = record.error;
outcome = record.outcome;
ready = true;
}
else {
record.waiters.push_back(std::move(waiter));
}
}
if (ready)
waiter.complete(error, outcome);
}
void Update_State::drain_completed_waiters(Stage_Record& record, std::pmr::vector<Update_Waiter>& waiters, std::error_code error, Update_Outcome outcome) {
if (record.completed)
return;
record.completed = true;
record.error = error;
record.outcome = outcome;
for (auto& waiter : record.waiters)
waiters.push_back(std::move(waiter));
record.waiters.clear();
}
void Update_State::complete_until(Update_Stage stage, std::error_code error, Update_Outcome outcome) {
std::pmr::vector<Update_Waiter> waiters(memory_resource(Memory_Domain::Update_Completion));
{
std::lock_guard<std::mutex> lock(mutex);
int last = std::min(stage_index(stage), stage_index(Update_Stage::Presented));
if (last >= stage_index(Update_Stage::Input_Released))
drain_completed_waiters(input_released, waiters, error, outcome);
if (last >= stage_index(Update_Stage::Committed))
drain_completed_waiters(committed, waiters, error, outcome);
if (last >= stage_index(Update_Stage::Rendered))
drain_completed_waiters(rendered, waiters, error, outcome);
if (last >= stage_index(Update_Stage::Presented)) {
drain_completed_waiters(presented, waiters, error, outcome);
terminal.store(true, std::memory_order_release);
}
}
for (auto& waiter : waiters)
waiter.complete(error, outcome);
}
void Update_State::complete_all(std::error_code error, Update_Outcome outcome) {
complete_until(Update_Stage::Presented, error, outcome);
}
std::shared_ptr<Update_State> make_update_state() {
std::pmr::polymorphic_allocator<Update_State> allocator(memory_resource(Memory_Domain::Update_Completion));
return std::allocate_shared<Update_State>(allocator, next_update_id.fetch_add(1, std::memory_order_relaxed));
}
void complete_update_states(std::pmr::vector<std::shared_ptr<Update_State>>& states, Update_Stage stage, std::error_code error, Update_Outcome outcome) {
for (auto& state : states) {
if (state)
state->complete_until(stage, error, outcome);
}
states.clear();
}
void cancel_update_states(std::pmr::vector<std::shared_ptr<Update_State>>& states) {
for (auto& state : states) {
if (state)
state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
}
states.clear();
}
void supersede_update_states(std::pmr::vector<std::shared_ptr<Update_State>>& states) {
for (auto& state : states) {
if (state)
state->complete_all({}, Update_Outcome::Superseded);
}
states.clear();
}
}