策略抽离

This commit is contained in:
2026-08-02 16:02:15 +08:00
parent dda25f9038
commit ffaca5b05f
55 changed files with 1431 additions and 886 deletions
@@ -27,6 +27,14 @@ double Low_Latency_Smoothed_Interval::safe_ms(double jitter_factor) const {
return sample_count ? srtt_ms + jitter_factor * jitter_ms : 0.0;
}
std::uint64_t Low_Latency_Smoothed_Interval::srtt_ns() const {
return sample_count ? static_cast<std::uint64_t>(srtt_ms * 1000000.0) : 0;
}
std::uint64_t Low_Latency_Smoothed_Interval::jitter_ns() const {
return sample_count ? static_cast<std::uint64_t>(jitter_ms * 1000000.0) : 0;
}
bool Low_Latency_Smoothed_Interval::valid() const {
return sample_count > 0;
}
@@ -43,11 +51,16 @@ void Low_Latency_Feedback_Controller::set_max_render_fps(double fps) {
refresh_control_snapshot();
}
void Low_Latency_Feedback_Controller::on_input_changed(std::uint64_t, std::uint64_t input_version) {
void Low_Latency_Feedback_Controller::on_input_changed(std::uint64_t now_ns, std::uint64_t input_version, Dirty_Reason reason) {
if (reason == Dirty_Reason::Core_Data && last_input_change_ns && now_ns > last_input_change_ns)
input_period.update(static_cast<double>(now_ns - last_input_change_ns) / 1000000.0);
if (reason == Dirty_Reason::Core_Data)
last_input_change_ns = now_ns;
if (input_version)
latest_input_version = input_version;
else
++latest_input_version;
refresh_control_snapshot();
}
void Low_Latency_Feedback_Controller::on_render_started(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) {
@@ -62,6 +75,8 @@ void Low_Latency_Feedback_Controller::on_render_completed(Frame_Lifecycle_Record
std::uint64_t render_duration_ns = frame.render_end_ns > frame.render_begin_ns ? frame.render_end_ns - frame.render_begin_ns : 0;
last_render_begin_ns = frame.render_begin_ns;
render_period.update(static_cast<double>(render_duration_ns + frame.render_queue_wait_ns) / 1000000.0);
if (frame.ready_queue_wait_ns)
ready_queue_period.update(static_cast<double>(frame.ready_queue_wait_ns) / 1000000.0);
refresh_control_snapshot();
write_state(frame);
}
@@ -69,18 +84,38 @@ void Low_Latency_Feedback_Controller::on_render_completed(Frame_Lifecycle_Record
void Low_Latency_Feedback_Controller::on_frame_presented(Frame_Lifecycle_Record& frame) {
std::uint64_t paint_duration_ns = frame.paint_end_ns > frame.paint_begin_ns ? frame.paint_end_ns - frame.paint_begin_ns : 0;
paint_period.update(static_cast<double>(paint_duration_ns + frame.present_queue_wait_ns) / 1000000.0);
if (frame.update_request_time_ns && frame.paint_begin_ns > frame.update_request_time_ns)
gui_period.update(static_cast<double>(frame.paint_begin_ns - frame.update_request_time_ns) / 1000000.0);
if (frame.paint_return_wait_ns)
consumption_period.update(static_cast<double>(frame.paint_return_wait_ns) / 1000000.0);
if (frame.core_data_time_ns && frame.paint_end_ns > frame.core_data_time_ns)
frame_age_period.update(static_cast<double>(frame.paint_end_ns - frame.core_data_time_ns) / 1000000.0);
++presented_frame_count;
frame.outcome = Frame_Outcome::Presented;
refresh_control_snapshot();
write_state(frame);
}
void Low_Latency_Feedback_Controller::on_frame_superseded(Frame_Lifecycle_Record& frame) {
++superseded_frame_count;
frame.outcome = Frame_Outcome::Superseded;
refresh_control_snapshot();
write_state(frame);
}
void Low_Latency_Feedback_Controller::on_frame_repeated() {
++repeated_frame_count;
refresh_control_snapshot();
}
void Low_Latency_Feedback_Controller::on_frame_underrun() {
++underrun_count;
refresh_control_snapshot();
}
void Low_Latency_Feedback_Controller::on_frame_dropped(Frame_Lifecycle_Record& frame, Frame_Outcome outcome) {
if (outcome == Frame_Outcome::Paint_Acquire_Dropped || outcome == Frame_Outcome::Render_Acquire_Dropped)
++underrun_count;
frame.outcome = outcome;
refresh_control_snapshot();
write_state(frame);
@@ -105,8 +140,9 @@ void Low_Latency_Feedback_Controller::write_state(Frame_Lifecycle_Record& frame)
void Low_Latency_Feedback_Controller::refresh_control_snapshot() {
std::uint64_t render_period_ns = render_period.valid() ? ms_to_ns(render_period.safe_ms(0.5)) : 0;
std::uint64_t paint_period_ns = paint_period.valid() ? ms_to_ns(paint_period.safe_ms(0.5)) : 0;
std::uint64_t input_period_ns = input_period.valid() ? ms_to_ns(input_period.safe_ms(0.5)) : 0;
std::uint64_t user_period_ns = state_value.user_period_ns;
Refresh_Limit_Source candidate = classify_limit_source(render_period_ns, paint_period_ns, user_period_ns);
Refresh_Limit_Source candidate = classify_limit_source(std::max(render_period_ns, input_period_ns), paint_period_ns, user_period_ns);
std::uint64_t candidate_period = 0;
switch (candidate) {
case Refresh_Limit_Source::Render:
@@ -122,14 +158,43 @@ void Low_Latency_Feedback_Controller::refresh_control_snapshot() {
break;
}
update_limit_source(candidate, candidate_period);
std::uint64_t target_period = std::max({render_period_ns, paint_period_ns, user_period_ns});
std::uint64_t target_period = std::max({render_period_ns, paint_period_ns, input_period_ns, user_period_ns});
Low_Latency_Bottleneck_State bottleneck = Low_Latency_Bottleneck_State::Unknown;
if (candidate == Refresh_Limit_Source::User)
bottleneck = Low_Latency_Bottleneck_State::User_Limited;
else if (paint_period_ns && paint_period_ns == target_period)
bottleneck = Low_Latency_Bottleneck_State::Consumer_Limited;
else if (render_period_ns && render_period_ns == target_period)
bottleneck = Low_Latency_Bottleneck_State::Producer_Limited;
else if (input_period_ns && input_period_ns == target_period)
bottleneck = Low_Latency_Bottleneck_State::Input_Limited;
state_value.render_period_ns = render_period_ns;
state_value.render_jitter_ns = render_period.jitter_ns();
state_value.paint_period_ns = paint_period_ns;
state_value.paint_jitter_ns = paint_period.jitter_ns();
state_value.input_period_ns = input_period_ns;
state_value.input_jitter_ns = input_period.jitter_ns();
state_value.ready_queue_srtt_ns = ready_queue_period.srtt_ns();
state_value.ready_queue_jitter_ns = ready_queue_period.jitter_ns();
state_value.gui_srtt_ns = gui_period.srtt_ns();
state_value.gui_jitter_ns = gui_period.jitter_ns();
state_value.consumption_srtt_ns = consumption_period.srtt_ns();
state_value.consumption_jitter_ns = consumption_period.jitter_ns();
state_value.frame_age_srtt_ns = frame_age_period.srtt_ns();
state_value.frame_age_jitter_ns = frame_age_period.jitter_ns();
state_value.bottleneck = bottleneck;
state_value.production_strategy = bottleneck == Low_Latency_Bottleneck_State::Consumer_Limited ? Low_Latency_Production_Strategy::Cost_Balanced : Low_Latency_Production_Strategy::Latency_Eager;
state_value.target_period_ns = target_period;
state_value.presented_frame_count = presented_frame_count;
state_value.superseded_frame_count = superseded_frame_count;
state_value.repeated_frame_count = repeated_frame_count;
state_value.underrun_count = underrun_count;
std::uint64_t total_display_attempts = presented_frame_count + superseded_frame_count + repeated_frame_count + underrun_count;
state_value.display_efficiency = total_display_attempts ? static_cast<double>(presented_frame_count) / static_cast<double>(total_display_attempts) : 1.0;
if (state_value.source == Refresh_Limit_Source::Unknown)
state_value.source = candidate;
if (state_value.source == Refresh_Limit_Source::User && last_render_begin_ns)
next_render_time = last_render_begin_ns + user_period_ns;
if ((state_value.source == Refresh_Limit_Source::User || state_value.production_strategy == Low_Latency_Production_Strategy::Cost_Balanced) && last_render_begin_ns && state_value.target_period_ns)
next_render_time = last_render_begin_ns + state_value.target_period_ns;
else
next_render_time = 0;
}
@@ -10,6 +10,8 @@ struct Low_Latency_Smoothed_Interval {
std::uint64_t sample_count{};
void update(double sample_ms);
[[nodiscard]] double safe_ms(double jitter_factor = 2.0) const;
[[nodiscard]] std::uint64_t srtt_ns() const;
[[nodiscard]] std::uint64_t jitter_ns() const;
[[nodiscard]] bool valid() const;
};
@@ -23,11 +25,13 @@ class Low_Latency_Feedback_Controller {
public:
void reset();
void set_max_render_fps(double fps);
void on_input_changed(std::uint64_t now_ns, std::uint64_t input_version = 0);
void on_input_changed(std::uint64_t now_ns, std::uint64_t input_version = 0, Dirty_Reason reason = Dirty_Reason::Core_Data);
void on_render_started(Frame_Lifecycle_Record& frame, std::uint64_t now_ns);
void on_render_completed(Frame_Lifecycle_Record& frame);
void on_frame_presented(Frame_Lifecycle_Record& frame);
void on_frame_superseded(Frame_Lifecycle_Record& frame);
void on_frame_repeated();
void on_frame_underrun();
void on_frame_dropped(Frame_Lifecycle_Record& frame, Frame_Outcome outcome);
[[nodiscard]] bool rate_limit_ready(std::uint64_t now_ns) const;
[[nodiscard]] std::uint64_t next_render_time_ns() const;
@@ -43,11 +47,21 @@ private:
Refresh_Control_Snapshot state_value;
Low_Latency_Smoothed_Interval render_period;
Low_Latency_Smoothed_Interval paint_period;
Low_Latency_Smoothed_Interval input_period;
Low_Latency_Smoothed_Interval ready_queue_period;
Low_Latency_Smoothed_Interval gui_period;
Low_Latency_Smoothed_Interval consumption_period;
Low_Latency_Smoothed_Interval frame_age_period;
Refresh_Limit_Source candidate_source = Refresh_Limit_Source::Unknown;
int candidate_count{};
std::uint64_t latest_input_version{};
std::uint64_t last_input_change_ns{};
std::uint64_t last_render_begin_ns{};
std::uint64_t next_render_time{};
std::uint64_t presented_frame_count{};
std::uint64_t superseded_frame_count{};
std::uint64_t repeated_frame_count{};
std::uint64_t underrun_count{};
};
} // namespace renderive
@@ -62,6 +62,17 @@ struct Low_Latency_Frame_Flow::Private {
return;
}
Refresh_Control_Snapshot control = feedback.state();
if (control.production_strategy == Low_Latency_Production_Strategy::Cost_Balanced) {
if (runtime.middle_has_new_frame)
return;
std::uint64_t next_ns = feedback.next_render_time_ns();
if (next_ns && !feedback.rate_limit_ready(runtime.now_ns)) {
host->schedule_render_deadline(next_ns);
return;
}
(void)host->try_render_once();
return;
}
switch (control.source) {
case Refresh_Limit_Source::Paint:
if (!runtime.middle_has_new_frame)
@@ -116,6 +127,10 @@ void Low_Latency_Frame_Flow::attach(Frame_Flow_Host& host) {
d->host = &host;
}
Renderable_Runtime_Strategy Low_Latency_Frame_Flow::renderable_runtime_strategy() const {
return Renderable_Runtime_Strategy::Low_Latency;
}
Frame_Flow_Runtime_Snapshot Low_Latency_Frame_Flow::runtime_snapshot(std::uint64_t now_ns, bool destroying) const {
return {
destroying,
@@ -169,6 +184,8 @@ Render_Surface_Publish_Result Low_Latency_Frame_Flow::publish_rendered_frame(Ren
if (handle->lease.frame && handle->lease.frame->metadata)
handle->lease.frame->version.store(handle->lease.frame->metadata->frame_id, std::memory_order_release);
result = to_surface_result(d->pipeline.try_publish_rendered_frame(std::move(handle->lease), now_ns));
if (result.dropped_frame_record)
d->feedback.on_frame_dropped(*result.dropped_frame_record, result.dropped_frame_record->outcome);
}
d->pipeline.render_job_state = Frame_Job_State::Idle;
return result;
@@ -181,6 +198,10 @@ Render_Surface_Publish_Result Low_Latency_Frame_Flow::request_present(std::uint6
Present_Surface_Lease Low_Latency_Frame_Flow::begin_present(std::uint64_t now_ns, std::shared_ptr<Frame_Lifecycle_Record>& returned_frame_record) {
auto consumed = d->pipeline.try_acquire_paint_frame(now_ns);
returned_frame_record = std::move(consumed.returned_frame_record);
if (consumed.paint_frame_unavailable)
d->feedback.on_frame_underrun();
else if (consumed.lease && !consumed.has_new_frame)
d->feedback.on_frame_repeated();
if (!consumed.lease)
return Present_Surface_Lease_Access::empty(consumed.has_new_frame, consumed.present_request_was_pending);
return Present_Surface_Lease_Access::from_handle<Low_Latency_Present_Surface_Handle>(
@@ -220,8 +241,8 @@ bool Low_Latency_Frame_Flow::view_active() const {
return d->active.load(std::memory_order_acquire);
}
void Low_Latency_Frame_Flow::notify_model_dirty(std::uint64_t now_ns) {
d->feedback.on_input_changed(now_ns);
void Low_Latency_Frame_Flow::notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) {
d->feedback.on_input_changed(now_ns, 0, reason);
d->evaluate(now_ns);
}
@@ -17,6 +17,7 @@ public:
[[nodiscard]] Low_Latency_Diagnostics low_latency_diagnostics() const;
void attach(Frame_Flow_Host& host) override;
[[nodiscard]] Renderable_Runtime_Strategy renderable_runtime_strategy() const override;
[[nodiscard]] Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns, bool destroying) const override;
void advance_edit_version() override;
void cancel_all_update_states() override;
@@ -30,7 +31,10 @@ public:
void activate_view(std::uint64_t now_ns) override;
void deactivate_view() override;
[[nodiscard]] bool view_active() const override;
void notify_model_dirty(std::uint64_t now_ns) override;
void notify_model_dirty(std::uint64_t now_ns) {
notify_model_dirty(Dirty_Reason::Core_Data, now_ns);
}
void notify_model_dirty(Dirty_Reason reason, std::uint64_t now_ns) override;
void notify_viewport_changed(Size viewport, std::uint64_t now_ns) override;
void notify_render_finished(Frame_Lifecycle_Record& frame, std::uint64_t now_ns) override;
void notify_paint_finished(std::uint64_t now_ns) override;
+460 -127
View File
@@ -1,8 +1,16 @@
#include "Render_Data.h"
#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;
@@ -10,70 +18,353 @@ std::ostream& operator<<(std::ostream& stream, const Range& range) {
namespace {
struct Low_Latency_State_Read_Handle final : Render_State_Read_Handle {
Low_Latency_State_Read_Handle(Low_Latency_Render_Data* data, Triple_Buffer_Lease lease)
: data(data), lease(lease) {}
struct Input_Buffer_Slot {
Input_Data* data{};
std::uint64_t version = 0;
bool pending = false;
std::shared_ptr<Update_State> completion_state;
};
~Low_Latency_State_Read_Handle() override {
if (data && lease)
data->unmark_state(lease);
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;
}
Low_Latency_Render_Data* data{};
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(Low_Latency_Render_Data* data, Triple_Buffer_Lease lease)
: data(data), lease(lease) {}
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 (!data || !lease)
if (!runtime || !lease)
return;
++data->state_by_index(lease.index)->version;
auto q = data->q_ptr;
data->unmark_state(lease);
if (q)
q->mark_render_dirty();
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);
}
Low_Latency_Render_Data* data{};
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
Render_Edit_Guard::Render_Edit_Guard(Low_Latency_Render_Data* data, Triple_Buffer_Lease lease) : data(data), lease(lease) {}
Render_Edit_Guard::~Render_Edit_Guard() {
if (!data || !lease)
return;
++data->state_by_index(lease.index)->version;
auto q = data->q_ptr;
data->unmark_state(lease);
if (q)
q->mark_render_dirty();
}
void Low_Latency_Render_Data::mark_input_dirty(Triple_Buffer_Lease lease, bool notify_render) {
Input_Buffer_Slot& slot = input_slot_by_index(lease.index);
slot.version = ++input_version;
slot.pending = true;
if (notify_render && q_ptr)
q_ptr->mark_render_dirty();
}
void Low_Latency_Render_Data::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 Low_Latency_Render_Data::drain_committed_update_states(std::pmr::vector<std::shared_ptr<Update_State>>& output) {
for (auto& state : committed_update_states)
output.push_back(std::move(state));
committed_update_states.clear();
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 Low_Latency_Render_Data::initialize_runtime(Renderable& owner) {
initialize_runtime_buffers(
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(),
@@ -82,105 +373,147 @@ void Low_Latency_Render_Data::initialize_runtime(Renderable& owner) {
owner.create_input_data());
}
void Low_Latency_Render_Data::initialize_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();
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;
Renderable_Runtime_Strategy Strategy_Render_Data::runtime_strategy() const {
return runtime->runtime_strategy_value;
}
Render_State_Read_Guard Low_Latency_Render_Data::acquire_render_state_read() {
Triple_Buffer_Lease lease = try_mark_state_role(State_Render);
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(state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(this, lease));
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 Low_Latency_Render_Data::acquire_edit_state_read() {
Triple_Buffer_Lease lease = mark_state_role(State_Edit);
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(state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(this, lease));
return Render_State_Read_Guard(runtime->state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(runtime.get(), lease));
}
Render_State_Edit_Guard Low_Latency_Render_Data::acquire_edit_state() {
Triple_Buffer_Lease lease = mark_state_role(State_Edit);
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_Edit_Guard(state_by_index(lease.index), std::make_unique<Low_Latency_State_Edit_Handle>(this, lease));
return Render_State_Read_Guard(runtime->state_by_index(lease.index), std::make_unique<Low_Latency_State_Read_Handle>(runtime.get(), lease));
}
void Low_Latency_Render_Data::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();
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)));
}
void Low_Latency_Render_Data::clear_input_role(std::uint8_t role) {
Input_Buffer_Slot& slot = input_slot(role);
if (slot.completion_state) {
if (role == Input_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();
}
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)));
}
if (slot.data)
slot.data->clear();
slot.pending = false;
}
void Low_Latency_Render_Data::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();
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));
}
void Low_Latency_Render_Data::cancel_pending_completions() {
for (Input_Buffer_Slot& slot : input_buffers) {
if (slot.completion_state) {
slot.completion_state->complete_all(std::make_error_code(std::errc::operation_canceled), Update_Outcome::Cancelled);
slot.completion_state.reset();
}
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));
}
cancel_update_states(committed_update_states);
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));
}
Low_Latency_Render_Data::~Low_Latency_Render_Data() {
clear_state_buffers();
clear_input_buffers();
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
+2 -111
View File
@@ -1,117 +1,8 @@
#pragma once
#include "../../architecture/Buffer_Role.h"
#include "../../architecture/Render_Data.h"
#include "../../architecture/Update_Completion.h"
#include "Low_Latency_Roles.h"
#include "Triple_Buffer.h"
#include <array>
#include <atomic>
#include <cstdint>
#include <memory>
#include <memory_resource>
#include "../../architecture/Strategy_Render_Data.h"
namespace renderive {
struct Input_Buffer_Slot {
Input_Data* data{};
std::uint64_t version = 0;
bool pending = false;
std::shared_ptr<Update_State> completion_state;
};
struct Low_Latency_Render_Data : Render_Data {
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;
Render_State* state(std::uint8_t role) const {
return state_by_index(state_control.read_view()[role]);
}
Render_State* state(State_Role src) const {
return state(static_cast<std::uint8_t>(src));
}
Render_State* state_by_index(std::uint8_t index) const {
return state_buffers[index];
}
Triple_Buffer_Lease try_mark_state_role(std::uint8_t role) {
auto record = state_control.try_acquire_role(role);
if (record.result != Triple_Buffer_Result::Success)
return {};
return record.lease;
}
Triple_Buffer_Lease mark_state_role(std::uint8_t role) {
auto record = state_control.acquire_role(role);
if (record.result != Triple_Buffer_Result::Success)
return {};
return record.lease;
}
void unmark_state(Triple_Buffer_Lease lease) {
state_control.unmark_use(lease);
}
void clear_state_buffers();
Input_Data* input_data(std::uint8_t role) const {
return input_slot(role).data;
}
Input_Data* input_data_by_index(std::uint8_t index) const {
return input_buffers[index].data;
}
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_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];
}
void mark_input_dirty(Triple_Buffer_Lease lease, bool notify_render = true);
void set_input_completion(Triple_Buffer_Lease lease, std::shared_ptr<Update_State> state);
void drain_committed_update_states(std::pmr::vector<std::shared_ptr<Update_State>>& output) override;
void initialize_runtime(Renderable& owner) override;
void initialize_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) override;
Render_State_Read_Guard acquire_render_state_read() override;
Render_State_Read_Guard acquire_edit_state_read() override;
Render_State_Edit_Guard acquire_edit_state() override;
void clear_input_role(std::uint8_t role);
void clear_render_input() {
clear_input_role(Input_Render);
}
Triple_Buffer_Lease try_mark_render_state() {
return try_mark_state_role(State_Render);
}
Triple_Buffer_Lease try_mark_edit_state() {
return try_mark_state_role(State_Edit);
}
Triple_Buffer_Lease mark_edit_state() {
return mark_state_role(State_Edit);
}
void clear_input_buffers();
void cancel_pending_completions() override;
~Low_Latency_Render_Data() override;
};
class Plot_Core;
struct LIB_DECL Render_Edit_Guard {
Render_Edit_Guard(Low_Latency_Render_Data* data, Triple_Buffer_Lease lease);
~Render_Edit_Guard();
Low_Latency_Render_Data* data{};
Triple_Buffer_Lease lease{};
};
using Low_Latency_Render_Data = Strategy_Render_Data;
} // namespace renderive
+2 -244
View File
@@ -1,246 +1,4 @@
#pragma once
#include "../../architecture/Buffer_Role.h"
#include "../../architecture/Renderable.h"
#include "Render_Data.h"
#include "../../architecture/Render_Lease.h"
#include "Low_Latency_Roles.h"
#include <utility>
namespace renderive {
template <typename Base, typename Owner, Render_State_Type State, Input_Data_Type Input>
requires std::derived_from<Base, Low_Latency_Render_Data>
struct Typed_Render_Data_Base : Base {
Owner* q() {
return reinterpret_cast<Owner*>(this->q_ptr);
}
State* render_state() override {
return reinterpret_cast<State*>(this->state(State_Render));
}
State* ready_state() override {
return reinterpret_cast<State*>(this->state(State_Ready));
}
State* edit_state() override {
return reinterpret_cast<State*>(this->state(State_Edit));
}
Input* render_input_data() override {
return reinterpret_cast<Input*>(this->input_data(Input_Render));
}
Input* edit_input_data() override {
return reinterpret_cast<Input*>(this->input_data(Input_Edit));
}
State* state_data(State_Role src) {
return reinterpret_cast<State*>(this->state(src));
}
State* state_data_by_index(std::uint8_t index) {
return reinterpret_cast<State*>(this->state_by_index(index));
}
Input* input_data_by_index(std::uint8_t index) {
return reinterpret_cast<Input*>(Base::input_data_by_index(index));
}
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
Field state_value(Field Source_State::* field, State_Role src) {
auto record = this->state_control.acquire_role(static_cast<std::uint8_t>(src));
if (record.result != Triple_Buffer_Result::Success)
return {};
Triple_Buffer_Lease lease = record.lease;
auto state = state_data_by_index(lease.index);
Field result = state->*field;
this->state_control.unmark_use(lease);
return result;
}
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
Field edit_state_value(Field Source_State::* field) {
return state_value(field, State_Edit);
}
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
Field ready_state_value(Field Source_State::* field) {
return state_value(field, State_Ready);
}
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
Field render_state_value(Field Source_State::* field) {
return state_value(field, State_Render);
}
template <typename Field, typename Source_State>
requires std::derived_from<State, Source_State>
void set_state_value(Field Source_State::* field, Field value) {
auto record = this->state_control.acquire_role(State_Edit);
if (record.result != Triple_Buffer_Result::Success)
return;
auto lease = record.lease;
auto state = state_data_by_index(lease.index);
Render_Edit_Guard guard(this, lease);
state->*field = std::move(value);
}
void sync_state_pipeline() override {
auto edit_record = this->state_control.try_acquire_role(State_Edit);
if (edit_record.result != Triple_Buffer_Result::Success)
return;
auto ready_record = this->state_control.try_acquire_role(State_Ready);
if (ready_record.result != Triple_Buffer_Result::Success) {
this->state_control.unmark_use(edit_record.lease);
return;
}
Triple_Buffer_Lease edit_lease = edit_record.lease;
Triple_Buffer_Lease ready_lease = ready_record.lease;
auto ready = reinterpret_cast<State*>(this->state_by_index(ready_lease.index));
auto edit = reinterpret_cast<State*>(this->state_by_index(edit_lease.index));
if (buffer_version_newer(edit->version, ready->version))
*ready = *edit;
this->state_control.unmark_use(ready_lease);
this->state_control.unmark_use(edit_lease);
this->state_control.try_swap_role(State_Ready, State_Render, [this](const Triple_Buffer_View& view) {
auto ready = reinterpret_cast<State*>(this->state_by_index(view[State_Ready]));
auto render = reinterpret_cast<State*>(this->state_by_index(view[State_Render]));
return buffer_version_newer(ready->version, render->version);
});
this->input_control.try_swap_role(Input_Edit, Input_Ready, [this](const Triple_Buffer_View& view) {
const Input_Buffer_Slot& edit = this->input_slot_by_index(view[Input_Edit]);
const Input_Buffer_Slot& ready = this->input_slot_by_index(view[Input_Ready]);
return edit.pending && buffer_version_newer(edit.version, ready.version);
});
this->input_control.try_swap_role(Input_Ready, Input_Render, [this](const Triple_Buffer_View& view) {
const Input_Buffer_Slot& ready = this->input_slot_by_index(view[Input_Ready]);
const Input_Buffer_Slot& render = this->input_slot_by_index(view[Input_Render]);
return ready.pending && buffer_version_newer(ready.version, render.version);
});
}
};
template <typename Owner, Render_State_Type State, Input_Data_Type Input>
using Typed_Render_Data = Typed_Render_Data_Base<Low_Latency_Render_Data, Owner, State, Input>;
template <typename Owner, Render_Data_Type Data, Render_State_Type State, Input_Data_Type Input>
struct Renderable_Binding {
static void init(Owner* owner, const char* name) {
owner->d_owner = make_render_object<Data>();
owner->d_ptr = static_cast<Render_Data*>(owner->d_owner.template get<Data>());
owner->set_object_name(name);
}
static Data* d(Owner* owner) {
return reinterpret_cast<Data*>(owner->d_ptr);
}
static Render_Object_Owner create_render_data() {
return make_render_object<Data>();
}
static Render_Object_Owner create_render_state() {
return make_render_object<State>();
}
static Render_Object_Owner create_input_data() {
if constexpr (std::is_same_v<Input, Input_Data>)
return {};
return make_render_object<Input>();
}
};
#define RENDERIVE_DEFINE_RENDERABLE_BINDING(Owner, Data, State, Input, Name) \
using Owner##_Binding = ::renderive::Renderable_Binding<Owner, Data, State, Input>; \
Owner::Owner() { \
Owner##_Binding::init(this, Name); \
} \
Data* Owner::d() { \
return Owner##_Binding::d(this); \
} \
::renderive::Render_Object_Owner Owner::create_render_data() { \
return Owner##_Binding::create_render_data(); \
} \
::renderive::Render_Object_Owner Owner::create_render_state() { \
return Owner##_Binding::create_render_state(); \
} \
::renderive::Render_Object_Owner Owner::create_input_data() { \
return Owner##_Binding::create_input_data(); \
}
template <typename Data, Render_State_Type State>
requires std::derived_from<Data, Low_Latency_Render_Data>
struct Render_State_Lease : Immovable {
explicit Render_State_Lease(Data* data) : data(data) {
auto record = data->state_control.acquire_role(State_Edit);
if (record.result == Triple_Buffer_Result::Success) {
lease = record.lease;
state = reinterpret_cast<State*>(data->state_by_index(lease.index));
}
}
~Render_State_Lease() {
if (lease)
data->unmark_state(lease);
}
State* operator->() {
return state;
}
State& operator*() {
return *state;
}
explicit operator bool() const {
return state && lease;
}
Data* data{};
Triple_Buffer_Lease lease{};
State* state{};
};
template <typename Data, Render_State_Type State>
requires std::derived_from<Data, Low_Latency_Render_Data>
struct Render_Edit_Lease : Immovable {
explicit Render_Edit_Lease(Data* data) : data(data) {
auto record = data->state_control.acquire_role(State_Edit);
if (record.result == Triple_Buffer_Result::Success) {
lease = record.lease;
state = reinterpret_cast<State*>(data->state_by_index(lease.index));
}
}
~Render_Edit_Lease() {
if (!lease)
return;
++data->state_by_index(lease.index)->version;
auto q = data->q_ptr;
data->unmark_state(lease);
if (q)
q->mark_render_dirty();
}
State* operator->() {
return state;
}
State& operator*() {
return *state;
}
explicit operator bool() const {
return state && lease;
}
Data* data{};
Triple_Buffer_Lease lease{};
State* state{};
};
template <typename Data, Input_Data_Type Input>
requires std::derived_from<Data, Low_Latency_Render_Data>
struct Render_Input_Lease : Immovable {
explicit Render_Input_Lease(Data* data, bool notify_render = true) : data(data), notify_render(notify_render) {
auto record = data->input_control.acquire_role(Input_Edit);
if (record.result == Triple_Buffer_Result::Success) {
lease = record.lease;
input = reinterpret_cast<Input*>(data->input_data_by_index(lease.index));
if (!input) {
data->input_control.unmark_use(lease);
lease = {};
}
}
}
~Render_Input_Lease() {
if (!lease)
return;
data->mark_input_dirty(lease, false);
data->input_control.unmark_use(lease);
if (notify_render && data->q_ptr)
data->q_ptr->mark_render_dirty();
}
Input* operator->() {
return input;
}
Input& operator*() {
return *input;
}
explicit operator bool() const {
return input && lease;
}
Data* data{};
bool notify_render{};
Triple_Buffer_Lease lease{};
Input* input{};
};
} // namespace renderive