Files
Renderive/Core/flow/low_latency/Render_Data.cpp
T
2026-08-02 16:02:15 +08:00

520 lines
22 KiB
C++

#include "../../architecture/Strategy_Render_Data.h"
#include "../../architecture/Renderable.h"
#include "../../base/Memory.h"
#include "Low_Latency_Roles.h"
#include "Triple_Buffer.h"
#include <array>
#include <mutex>
#include <ostream>
#include <system_error>
#include <utility>
namespace renderive {
std::ostream& operator<<(std::ostream& stream, const Range& range) {
stream << "Range(" << range.origin << ", " << range.target << ")";
return stream;
}
namespace {
struct Input_Buffer_Slot {
Input_Data* data{};
std::uint64_t version = 0;
bool pending = false;
std::shared_ptr<Update_State> completion_state;
};
std::uint8_t state_role_index(Renderable_State_Role role) {
switch (role) {
case Renderable_State_Role::Edit:
return State_Edit;
case Renderable_State_Role::Ready:
return State_Ready;
case Renderable_State_Role::Render:
return State_Render;
}
return State_Render;
}
std::uint8_t input_role_index(Renderable_Input_Role role) {
switch (role) {
case Renderable_Input_Role::Edit:
return Input_Edit;
case Renderable_Input_Role::Ready:
return Input_Ready;
case Renderable_Input_Role::Render:
return Input_Render;
}
return Input_Render;
}
} // namespace
struct Strategy_Render_Data_Private {
Renderable_Runtime_Strategy runtime_strategy_value = Renderable_Runtime_Strategy::Low_Latency;
Triple_Role_Buffer_Control state_control;
std::array<Render_State*, 3> state_buffers{};
std::array<Render_Object_Owner, 3> state_buffer_owners{};
Triple_Role_Buffer_Control input_control;
std::array<Input_Buffer_Slot, 3> input_buffers{};
std::array<Render_Object_Owner, 3> input_buffer_owners{};
std::pmr::vector<std::shared_ptr<Update_State>> committed_update_states{memory_resource(Memory_Domain::Update_Completion)};
std::uint64_t input_version = 0;
mutable std::mutex explicit_state_mutex;
mutable std::mutex explicit_input_mutex;
Render_Object_Owner explicit_state_owner;
Render_State* explicit_state{};
Render_Object_Owner explicit_input_owner;
Input_Buffer_Slot explicit_input;
std::uint64_t explicit_input_version = 0;
Render_State* state_by_index(std::uint8_t index) const {
return index < state_buffers.size() ? state_buffers[index] : nullptr;
}
Render_State* state(std::uint8_t role) const {
if (runtime_strategy_value == Renderable_Runtime_Strategy::Explicit)
return explicit_state;
return state_by_index(state_control.read_view()[role]);
}
Input_Data* input_data_by_index(std::uint8_t index) const {
return index < input_buffers.size() ? input_buffers[index].data : nullptr;
}
Input_Buffer_Slot& input_slot_by_index(std::uint8_t index) {
return input_buffers[index];
}
const Input_Buffer_Slot& input_slot_by_index(std::uint8_t index) const {
return input_buffers[index];
}
Input_Buffer_Slot& input_slot(std::uint8_t role) {
return input_slot_by_index(input_control.read_view()[role]);
}
const Input_Buffer_Slot& input_slot(std::uint8_t role) const {
return input_slot_by_index(input_control.read_view()[role]);
}
Input_Data* input_data(std::uint8_t role) const {
if (runtime_strategy_value == Renderable_Runtime_Strategy::Explicit)
return explicit_input.data;
return input_slot(role).data;
}
Triple_Buffer_Lease try_mark_state_role(std::uint8_t role) {
auto record = state_control.try_acquire_role(role);
return record.result == Triple_Buffer_Result::Success ? record.lease : Triple_Buffer_Lease{};
}
Triple_Buffer_Lease mark_state_role(std::uint8_t role) {
auto record = state_control.acquire_role(role);
return record.result == Triple_Buffer_Result::Success ? record.lease : Triple_Buffer_Lease{};
}
void mark_input_dirty(Triple_Buffer_Lease lease) {
Input_Buffer_Slot& slot = input_slot_by_index(lease.index);
slot.version = ++input_version;
slot.pending = true;
}
void set_input_completion(Triple_Buffer_Lease lease, std::shared_ptr<Update_State> state) {
Input_Buffer_Slot& slot = input_slot_by_index(lease.index);
if (slot.completion_state && slot.completion_state != state)
slot.completion_state->complete_all({}, Update_Outcome::Superseded);
slot.completion_state = std::move(state);
}
void cancel_pending_completions() {
for (Input_Buffer_Slot& slot : input_buffers) {
if (!slot.completion_state)
continue;
slot.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
slot.completion_state.reset();
}
{
std::lock_guard<std::mutex> lock(explicit_input_mutex);
if (explicit_input.completion_state) {
explicit_input.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
explicit_input.completion_state.reset();
}
}
cancel_update_states(committed_update_states);
}
void clear_state_buffers() {
state_control.close_and_wait();
for (std::size_t i = 0; i < state_buffers.size(); ++i) {
state_buffer_owners[i].reset();
state_buffers[i] = nullptr;
}
state_control.reset();
}
void clear_input_buffers() {
input_control.close_and_wait();
cancel_pending_completions();
for (std::size_t i = 0; i < input_buffers.size(); ++i) {
input_buffer_owners[i].reset();
input_buffers[i].data = nullptr;
input_buffers[i].version = 0;
input_buffers[i].pending = false;
input_buffers[i].completion_state.reset();
}
input_version = 0;
input_control.reset();
}
void clear_explicit_runtime() {
{
std::lock_guard<std::mutex> input_lock(explicit_input_mutex);
if (explicit_input.completion_state) {
explicit_input.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
explicit_input.completion_state.reset();
}
explicit_input.data = nullptr;
explicit_input.version = 0;
explicit_input.pending = false;
explicit_input_owner.reset();
explicit_input_version = 0;
}
{
std::lock_guard<std::mutex> state_lock(explicit_state_mutex);
explicit_state = nullptr;
explicit_state_owner.reset();
}
}
void initialize_low_latency_runtime_buffers(
Render_Object_Owner edit_state,
Render_Object_Owner ready_state,
Render_Object_Owner render_state,
Render_Object_Owner edit_input,
Render_Object_Owner ready_input,
Render_Object_Owner render_input) {
clear_state_buffers();
clear_input_buffers();
clear_explicit_runtime();
state_buffer_owners[State_Edit] = std::move(edit_state);
state_buffer_owners[State_Ready] = std::move(ready_state);
state_buffer_owners[State_Render] = std::move(render_state);
state_buffers[State_Edit] = state_buffer_owners[State_Edit].get<Render_State>();
state_buffers[State_Ready] = state_buffer_owners[State_Ready].get<Render_State>();
state_buffers[State_Render] = state_buffer_owners[State_Render].get<Render_State>();
input_buffer_owners[Input_Edit] = std::move(edit_input);
input_buffer_owners[Input_Ready] = std::move(ready_input);
input_buffer_owners[Input_Render] = std::move(render_input);
if (input_buffer_owners[Input_Edit])
input_buffers[Input_Edit].data = input_buffer_owners[Input_Edit].get<Input_Data>();
if (input_buffer_owners[Input_Ready])
input_buffers[Input_Ready].data = input_buffer_owners[Input_Ready].get<Input_Data>();
if (input_buffer_owners[Input_Render])
input_buffers[Input_Render].data = input_buffer_owners[Input_Render].get<Input_Data>();
if (state_buffers[State_Edit])
state_buffers[State_Edit]->version = 1;
}
void initialize_explicit_runtime_buffers(Render_Object_Owner state, Render_Object_Owner input) {
clear_state_buffers();
clear_input_buffers();
clear_explicit_runtime();
explicit_state_owner = std::move(state);
explicit_state = explicit_state_owner.get<Render_State>();
if (explicit_state)
explicit_state->version = 1;
explicit_input_owner = std::move(input);
explicit_input.data = explicit_input_owner.get<Input_Data>();
explicit_input.version = 0;
explicit_input.pending = false;
explicit_input.completion_state.reset();
}
void clear_input_role(Renderable_Input_Role role) {
if (runtime_strategy_value == Renderable_Runtime_Strategy::Explicit) {
std::lock_guard<std::mutex> lock(explicit_input_mutex);
if (explicit_input.completion_state) {
if (role == Renderable_Input_Role::Render) {
explicit_input.completion_state->complete_until(Update_Stage::Committed);
committed_update_states.push_back(std::move(explicit_input.completion_state));
}
else {
explicit_input.completion_state->complete_all({}, Update_Outcome::Superseded);
explicit_input.completion_state.reset();
}
}
if (explicit_input.data)
explicit_input.data->clear();
explicit_input.pending = false;
return;
}
Input_Buffer_Slot& slot = input_slot(input_role_index(role));
if (slot.completion_state) {
if (role == Renderable_Input_Role::Render) {
slot.completion_state->complete_until(Update_Stage::Committed);
committed_update_states.push_back(std::move(slot.completion_state));
}
else {
slot.completion_state->complete_all({}, Update_Outcome::Superseded);
slot.completion_state.reset();
}
}
if (slot.data)
slot.data->clear();
slot.pending = false;
}
};
namespace {
struct Low_Latency_State_Read_Handle final : Render_State_Read_Handle {
Low_Latency_State_Read_Handle(Strategy_Render_Data_Private* runtime, Triple_Buffer_Lease lease)
: runtime(runtime), lease(lease) {}
~Low_Latency_State_Read_Handle() override {
if (runtime && lease)
runtime->state_control.unmark_use(lease);
}
Strategy_Render_Data_Private* runtime{};
Triple_Buffer_Lease lease{};
};
struct Low_Latency_State_Edit_Handle final : Render_State_Edit_Handle {
Low_Latency_State_Edit_Handle(Strategy_Render_Data_Private* runtime, Renderable* owner, Triple_Buffer_Lease lease)
: runtime(runtime), owner(owner), lease(lease) {}
~Low_Latency_State_Edit_Handle() override {
if (!runtime || !lease)
return;
if (auto* state = runtime->state_by_index(lease.index))
++state->version;
runtime->state_control.unmark_use(lease);
if (owner)
owner->mark_render_dirty(Dirty_Reason::Config);
}
Strategy_Render_Data_Private* runtime{};
Renderable* owner{};
Triple_Buffer_Lease lease{};
};
struct Low_Latency_Input_Edit_Handle final : Render_Input_Edit_Handle {
Low_Latency_Input_Edit_Handle(Strategy_Render_Data_Private* runtime, Renderable* owner, Triple_Buffer_Lease lease, bool notify_render)
: runtime(runtime), owner(owner), lease(lease), notify_render(notify_render) {}
~Low_Latency_Input_Edit_Handle() override {
if (!runtime || !lease)
return;
runtime->mark_input_dirty(lease);
runtime->input_control.unmark_use(lease);
if (notify_render && owner)
owner->mark_render_dirty();
}
void set_completion(std::shared_ptr<Update_State> state) override {
if (runtime && lease)
runtime->set_input_completion(lease, std::move(state));
}
Strategy_Render_Data_Private* runtime{};
Renderable* owner{};
Triple_Buffer_Lease lease{};
bool notify_render{};
};
struct Explicit_State_Read_Handle final : Render_State_Read_Handle {
explicit Explicit_State_Read_Handle(std::unique_lock<std::mutex> lock)
: lock(std::move(lock)) {}
std::unique_lock<std::mutex> lock;
};
struct Explicit_State_Edit_Handle final : Render_State_Edit_Handle {
Explicit_State_Edit_Handle(Strategy_Render_Data_Private* runtime, Renderable* owner, std::unique_lock<std::mutex> lock)
: runtime(runtime), owner(owner), lock(std::move(lock)) {}
~Explicit_State_Edit_Handle() override {
if (!runtime || !runtime->explicit_state)
return;
++runtime->explicit_state->version;
lock.unlock();
if (owner)
owner->mark_render_dirty(Dirty_Reason::Config);
}
Strategy_Render_Data_Private* runtime{};
Renderable* owner{};
std::unique_lock<std::mutex> lock;
};
struct Explicit_Input_Edit_Handle final : Render_Input_Edit_Handle {
Explicit_Input_Edit_Handle(Strategy_Render_Data_Private* runtime, Renderable* owner, std::unique_lock<std::mutex> lock, bool notify_render)
: runtime(runtime), owner(owner), lock(std::move(lock)), notify_render(notify_render) {}
~Explicit_Input_Edit_Handle() override {
if (!runtime)
return;
runtime->explicit_input.version = ++runtime->explicit_input_version;
runtime->explicit_input.pending = true;
lock.unlock();
if (notify_render && owner)
owner->mark_render_dirty();
}
void set_completion(std::shared_ptr<Update_State> state) override {
if (!runtime)
return;
if (runtime->explicit_input.completion_state && runtime->explicit_input.completion_state != state)
runtime->explicit_input.completion_state->complete_all({}, Update_Outcome::Superseded);
runtime->explicit_input.completion_state = std::move(state);
}
Strategy_Render_Data_Private* runtime{};
Renderable* owner{};
std::unique_lock<std::mutex> lock;
bool notify_render{};
};
} // namespace
Strategy_Render_Data::Strategy_Render_Data()
: runtime(std::make_unique<Strategy_Render_Data_Private>()) {}
Strategy_Render_Data::~Strategy_Render_Data() {
runtime->clear_state_buffers();
runtime->clear_input_buffers();
runtime->clear_explicit_runtime();
}
void Strategy_Render_Data::initialize_runtime(Renderable& owner, Renderable_Runtime_Strategy strategy) {
runtime->runtime_strategy_value = strategy;
if (strategy == Renderable_Runtime_Strategy::Explicit) {
runtime->initialize_explicit_runtime_buffers(owner.create_render_state(), owner.create_input_data());
return;
}
runtime->initialize_low_latency_runtime_buffers(
owner.create_render_state(),
owner.create_render_state(),
owner.create_render_state(),
owner.create_input_data(),
owner.create_input_data(),
owner.create_input_data());
}
Renderable_Runtime_Strategy Strategy_Render_Data::runtime_strategy() const {
return runtime->runtime_strategy_value;
}
void Strategy_Render_Data::cancel_pending_completions() {
runtime->cancel_pending_completions();
}
void Strategy_Render_Data::drain_committed_update_states(std::pmr::vector<std::shared_ptr<Update_State>>& output) {
for (auto& state : runtime->committed_update_states)
output.push_back(std::move(state));
runtime->committed_update_states.clear();
}
Render_State_Read_Guard Strategy_Render_Data::acquire_state_read(Renderable_State_Role role) {
if (runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit) {
std::unique_lock<std::mutex> lock(runtime->explicit_state_mutex);
if (!runtime->explicit_state)
return {};
return Render_State_Read_Guard(runtime->explicit_state, std::make_unique<Explicit_State_Read_Handle>(std::move(lock)));
}
Triple_Buffer_Lease lease = runtime->mark_state_role(state_role_index(role));
if (!lease)
return {};
return Render_State_Read_Guard(runtime->state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(runtime.get(), lease));
}
Render_State_Read_Guard Strategy_Render_Data::acquire_render_state_read() {
if (runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit) {
std::unique_lock<std::mutex> lock(runtime->explicit_state_mutex);
if (!runtime->explicit_state)
return {};
return Render_State_Read_Guard(runtime->explicit_state, std::make_unique<Explicit_State_Read_Handle>(std::move(lock)));
}
Triple_Buffer_Lease lease = runtime->try_mark_state_role(State_Render);
if (!lease)
return {};
return Render_State_Read_Guard(runtime->state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(runtime.get(), lease));
}
Render_State_Read_Guard Strategy_Render_Data::acquire_edit_state_read() {
if (runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit) {
std::unique_lock<std::mutex> lock(runtime->explicit_state_mutex);
if (!runtime->explicit_state)
return {};
return Render_State_Read_Guard(runtime->explicit_state, std::make_unique<Explicit_State_Read_Handle>(std::move(lock)));
}
Triple_Buffer_Lease lease = runtime->mark_state_role(State_Edit);
if (!lease)
return {};
return Render_State_Read_Guard(runtime->state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(runtime.get(), lease));
}
Render_State_Read_Guard Strategy_Render_Data::acquire_prepare_state_read() {
if (runtime->runtime_strategy_value != Renderable_Runtime_Strategy::Explicit)
return {};
std::unique_lock<std::mutex> lock(runtime->explicit_state_mutex);
if (!runtime->explicit_state)
return {};
return Render_State_Read_Guard(runtime->explicit_state, std::make_unique<Explicit_State_Read_Handle>(std::move(lock)));
}
Render_State_Edit_Guard Strategy_Render_Data::acquire_edit_state() {
if (runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit) {
std::unique_lock<std::mutex> lock(runtime->explicit_state_mutex);
if (!runtime->explicit_state)
return {};
return Render_State_Edit_Guard(runtime->explicit_state, std::make_unique<Explicit_State_Edit_Handle>(runtime.get(), q_ptr, std::move(lock)));
}
Triple_Buffer_Lease lease = runtime->mark_state_role(State_Edit);
if (!lease)
return {};
return Render_State_Edit_Guard(runtime->state_by_index(lease.index), std::make_unique<Low_Latency_State_Edit_Handle>(runtime.get(), q_ptr, lease));
}
Render_Input_Edit_Guard Strategy_Render_Data::acquire_edit_input(bool notify_render) {
if (runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit) {
std::unique_lock<std::mutex> lock(runtime->explicit_input_mutex);
if (!runtime->explicit_input.data)
return {};
return Render_Input_Edit_Guard(runtime->explicit_input.data, std::make_unique<Explicit_Input_Edit_Handle>(runtime.get(), q_ptr, std::move(lock), notify_render));
}
auto record = runtime->input_control.acquire_role(Input_Edit);
if (record.result != Triple_Buffer_Result::Success)
return {};
Input_Data* input = runtime->input_data_by_index(record.lease.index);
if (!input) {
runtime->input_control.unmark_use(record.lease);
return {};
}
return Render_Input_Edit_Guard(input, std::make_unique<Low_Latency_Input_Edit_Handle>(runtime.get(), q_ptr, record.lease, notify_render));
}
void Strategy_Render_Data::sync_state_pipeline() {
if (runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit)
return;
auto edit_record = runtime->state_control.try_acquire_role(State_Edit);
if (edit_record.result != Triple_Buffer_Result::Success)
return;
auto ready_record = runtime->state_control.try_acquire_role(State_Ready);
if (ready_record.result != Triple_Buffer_Result::Success) {
runtime->state_control.unmark_use(edit_record.lease);
return;
}
Render_State* ready = runtime->state_by_index(ready_record.lease.index);
Render_State* edit = runtime->state_by_index(edit_record.lease.index);
if (ready && edit && buffer_version_newer(edit->version, ready->version))
copy_render_state_object(*ready, *edit);
runtime->state_control.unmark_use(ready_record.lease);
runtime->state_control.unmark_use(edit_record.lease);
runtime->state_control.try_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) {
auto* ready = runtime->state_by_index(view[State_Ready]);
auto* render = runtime->state_by_index(view[State_Render]);
return ready && render && buffer_version_newer(ready->version, render->version);
});
runtime->input_control.try_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) {
const Input_Buffer_Slot& edit = runtime->input_slot_by_index(view[Input_Edit]);
const Input_Buffer_Slot& ready = runtime->input_slot_by_index(view[Input_Ready]);
return edit.pending && buffer_version_newer(edit.version, ready.version);
});
runtime->input_control.try_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) {
const Input_Buffer_Slot& ready = runtime->input_slot_by_index(view[Input_Ready]);
const Input_Buffer_Slot& render = runtime->input_slot_by_index(view[Input_Render]);
return ready.pending && buffer_version_newer(ready.version, render.version);
});
}
Render_State* Strategy_Render_Data::state(Renderable_State_Role role) {
return runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit
? runtime->explicit_state
: runtime->state(state_role_index(role));
}
Input_Data* Strategy_Render_Data::input_data(Renderable_Input_Role role) {
return runtime->runtime_strategy_value == Renderable_Runtime_Strategy::Explicit
? runtime->explicit_input.data
: runtime->input_data(input_role_index(role));
}
void Strategy_Render_Data::clear_render_input() {
runtime->clear_input_role(Renderable_Input_Role::Render);
}
} // namespace renderive