127 lines
4.9 KiB
C++
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();
|
|
}
|
|
}
|