策略抽离
This commit is contained in:
@@ -1,25 +0,0 @@
|
||||
#include "Explicit_Refresh_Strategy.h"
|
||||
|
||||
namespace renderive {
|
||||
|
||||
void Explicit_Refresh_Strategy::request_render() {
|
||||
manual_request_pending.store(true, std::memory_order_release);
|
||||
}
|
||||
|
||||
Refresh_Action Explicit_Refresh_Strategy::on_trigger(
|
||||
Refresh_Trigger,
|
||||
const Refresh_Runtime_Snapshot& runtime,
|
||||
Frame_Lifecycle_Record*) {
|
||||
Refresh_Action action;
|
||||
if (!manual_request_pending.load(std::memory_order_acquire))
|
||||
return action;
|
||||
if (!runtime.enabled || runtime.destroying || runtime.render_job_active)
|
||||
return action;
|
||||
manual_request_pending.store(false, std::memory_order_release);
|
||||
action.try_render = true;
|
||||
if (runtime.middle_has_new_frame && !runtime.paint_active)
|
||||
action.request_paint = true;
|
||||
return action;
|
||||
}
|
||||
|
||||
} // namespace renderive
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
#include "Refresh_Strategy.h"
|
||||
#include <atomic>
|
||||
|
||||
namespace renderive {
|
||||
|
||||
class Explicit_Refresh_Strategy final : public Refresh_Strategy {
|
||||
public:
|
||||
void request_render();
|
||||
|
||||
private:
|
||||
[[nodiscard]] Refresh_Action on_trigger(
|
||||
Refresh_Trigger trigger,
|
||||
const Refresh_Runtime_Snapshot& runtime,
|
||||
Frame_Lifecycle_Record* frame) override;
|
||||
|
||||
std::atomic_bool manual_request_pending{false};
|
||||
};
|
||||
|
||||
} // namespace renderive
|
||||
@@ -1,90 +0,0 @@
|
||||
#include "Latency_Eager_Refresh_Strategy.h"
|
||||
#include "../architecture/Render_Time.h"
|
||||
|
||||
namespace renderive {
|
||||
|
||||
struct Latency_Eager_Refresh_Strategy::Private {
|
||||
std::atomic<double> max_render_fps{60.0};
|
||||
Frame_Feedback_Controller feedback;
|
||||
};
|
||||
|
||||
static constexpr double k_default_max_fps = 60.0;
|
||||
|
||||
Latency_Eager_Refresh_Strategy::Latency_Eager_Refresh_Strategy()
|
||||
: d(std::make_unique<Private>()) {}
|
||||
|
||||
Latency_Eager_Refresh_Strategy::~Latency_Eager_Refresh_Strategy() = default;
|
||||
|
||||
double Latency_Eager_Refresh_Strategy::max_render_fps() const {
|
||||
return d->max_render_fps.load(std::memory_order_acquire);
|
||||
}
|
||||
|
||||
void Latency_Eager_Refresh_Strategy::set_max_render_fps(double fps) {
|
||||
if (fps <= 0.0)
|
||||
fps = k_default_max_fps;
|
||||
d->max_render_fps.store(fps, std::memory_order_release);
|
||||
}
|
||||
|
||||
Refresh_Control_Snapshot Latency_Eager_Refresh_Strategy::feedback_state() const {
|
||||
return d->feedback.state();
|
||||
}
|
||||
|
||||
Refresh_Action Latency_Eager_Refresh_Strategy::on_trigger(
|
||||
Refresh_Trigger trigger,
|
||||
const Refresh_Runtime_Snapshot& runtime,
|
||||
Frame_Lifecycle_Record* frame) {
|
||||
d->feedback.set_max_render_fps(max_render_fps());
|
||||
|
||||
if (trigger == Refresh_Trigger::Started)
|
||||
d->feedback.reset();
|
||||
if (trigger == Refresh_Trigger::Input_Dirty)
|
||||
d->feedback.on_input_changed(runtime.now_ns);
|
||||
if (frame) {
|
||||
switch (trigger) {
|
||||
case Refresh_Trigger::Render_Finished:
|
||||
d->feedback.on_render_completed(*frame);
|
||||
break;
|
||||
case Refresh_Trigger::Frame_Returned:
|
||||
if (frame->outcome == Frame_Outcome::Superseded)
|
||||
d->feedback.on_frame_superseded(*frame);
|
||||
else
|
||||
d->feedback.on_frame_presented(*frame);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Refresh_Action action;
|
||||
if (!runtime.enabled || runtime.destroying || trigger == Refresh_Trigger::Paused)
|
||||
return action;
|
||||
|
||||
if (runtime.middle_has_new_frame && !runtime.paint_active)
|
||||
action.request_paint = true;
|
||||
|
||||
if (!runtime.dirty || runtime.render_job_active)
|
||||
return action;
|
||||
|
||||
Refresh_Control_Snapshot control = d->feedback.state();
|
||||
switch (control.source) {
|
||||
case Refresh_Limit_Source::Paint:
|
||||
if (!runtime.middle_has_new_frame)
|
||||
action.try_render = true;
|
||||
break;
|
||||
case Refresh_Limit_Source::User: {
|
||||
std::uint64_t next_ns = d->feedback.next_render_time_ns();
|
||||
if (next_ns && !d->feedback.rate_limit_ready(runtime.now_ns))
|
||||
action.render_deadline_ns = next_ns;
|
||||
else
|
||||
action.try_render = true;
|
||||
break;
|
||||
}
|
||||
case Refresh_Limit_Source::Render:
|
||||
case Refresh_Limit_Source::Unknown:
|
||||
action.try_render = true;
|
||||
break;
|
||||
}
|
||||
return action;
|
||||
}
|
||||
|
||||
} // namespace renderive
|
||||
@@ -1,28 +0,0 @@
|
||||
#pragma once
|
||||
#include "Refresh_Strategy.h"
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
|
||||
namespace renderive {
|
||||
|
||||
class Latency_Eager_Refresh_Strategy final : public Refresh_Strategy {
|
||||
public:
|
||||
Latency_Eager_Refresh_Strategy();
|
||||
~Latency_Eager_Refresh_Strategy() override;
|
||||
|
||||
[[nodiscard]] double max_render_fps() const;
|
||||
void set_max_render_fps(double fps);
|
||||
[[nodiscard]] Refresh_Control_Snapshot feedback_state() const;
|
||||
|
||||
private:
|
||||
[[nodiscard]] Refresh_Action on_trigger(
|
||||
Refresh_Trigger trigger,
|
||||
const Refresh_Runtime_Snapshot& runtime,
|
||||
Frame_Lifecycle_Record* frame) override;
|
||||
|
||||
struct Private;
|
||||
std::unique_ptr<Private> d;
|
||||
};
|
||||
|
||||
} // namespace renderive
|
||||
+112
-145
@@ -1,5 +1,4 @@
|
||||
#include "Plot_Core.h"
|
||||
#include "Plot_Frame_Pipeline.h"
|
||||
#include "../architecture/Renderable.h"
|
||||
#include "../architecture/Render_Time.h"
|
||||
#include "../architecture/Update_Completion.h"
|
||||
@@ -13,37 +12,26 @@ class Plot_Core_Private;
|
||||
struct Frame_Render_Job {
|
||||
std::shared_ptr<Plot_Core_Private> owner;
|
||||
std::uint64_t generation{};
|
||||
Plot_Frame_Lease frame_lease;
|
||||
Plot_Frame* buffer{};
|
||||
Render_Frame_Surface frame_surface;
|
||||
Render_Output* buffer{};
|
||||
std::shared_ptr<Frame_Lifecycle_Record> lifecycle;
|
||||
Plot_Render_Snapshot render_snapshot;
|
||||
Renderable_Tree_Snapshot renderables;
|
||||
Render_Frame_Snapshot render_snapshot;
|
||||
Renderable_Frame_Tree renderables;
|
||||
std::shared_ptr<Frame_Worker_Stats> worker_stat;
|
||||
};
|
||||
class Plot_Core_Private final : public std::enable_shared_from_this<Plot_Core_Private> {
|
||||
class Plot_Core_Private final : public std::enable_shared_from_this<Plot_Core_Private>, public Frame_Flow_Host {
|
||||
public:
|
||||
explicit Plot_Core_Private(std::unique_ptr<Refresh_Strategy> s)
|
||||
: strategy(std::move(s)) {}
|
||||
explicit Plot_Core_Private(std::unique_ptr<Frame_Flow> selected_flow)
|
||||
: flow(std::move(selected_flow)) {}
|
||||
~Plot_Core_Private() = default;
|
||||
bool render_enabled() const {
|
||||
return rendering.load(std::memory_order_acquire);
|
||||
}
|
||||
bool destroying() const {
|
||||
return !context || context->destroying.load(std::memory_order_acquire);
|
||||
}
|
||||
Refresh_Runtime_Snapshot refresh_runtime_snapshot(std::uint64_t now_ns = steady_now_ns()) const {
|
||||
return {
|
||||
render_enabled(),
|
||||
destroying(),
|
||||
pipeline.has_unrendered_edit(),
|
||||
pipeline.render_job_state != Frame_Job_State::Idle,
|
||||
pipeline.middle_frame_ready(),
|
||||
pipeline.painting_frame_in_use(),
|
||||
now_ns
|
||||
};
|
||||
Frame_Flow_Runtime_Snapshot runtime_snapshot(std::uint64_t now_ns) const override {
|
||||
return flow ? flow->runtime_snapshot(now_ns, destroying()) : Frame_Flow_Runtime_Snapshot{destroying(), false, false, false, false, now_ns};
|
||||
}
|
||||
bool submit_render_once() {
|
||||
if (!rendering.load(std::memory_order_acquire) || destroying())
|
||||
if (destroying())
|
||||
return false;
|
||||
if (!root)
|
||||
return false;
|
||||
@@ -53,18 +41,20 @@ public:
|
||||
};
|
||||
if (size.empty())
|
||||
return false;
|
||||
auto frame_lease = pipeline.try_acquire_render_frame();
|
||||
if (!frame_lease)
|
||||
auto frame_surface = flow ? flow->begin_render_frame() : Render_Frame_Surface{};
|
||||
if (!frame_surface)
|
||||
return false;
|
||||
Plot_Frame* buffer = frame_lease.frame;
|
||||
Render_Output* buffer = frame_surface.output;
|
||||
cancel_update_states(buffer->presented_update_states);
|
||||
if (flow)
|
||||
flow->drain_frame_update_states(buffer->presented_update_states);
|
||||
std::uint64_t render_role_enter_ns = buffer->metadata ? buffer->metadata->render_role_enter_ns : 0;
|
||||
buffer->metadata = std::make_shared<Frame_Lifecycle_Record>();
|
||||
auto frame_record = buffer->metadata;
|
||||
Frame_Lifecycle_Record& frame = *frame_record;
|
||||
std::uint64_t submit_time = steady_now_ns();
|
||||
frame.frame_id = ++next_frame_id;
|
||||
frame.input_version = pipeline.edit_version.load(std::memory_order_acquire);
|
||||
frame.input_version = frame_surface.input_version;
|
||||
frame.render_request_ns = submit_time;
|
||||
frame.render_role_enter_ns = render_role_enter_ns ? render_role_enter_ns : submit_time;
|
||||
frame.core_data_time_ns = latest_input_time_ns.load(std::memory_order_acquire);
|
||||
@@ -72,15 +62,12 @@ public:
|
||||
frame.pixel_width = static_cast<std::uint64_t>(size.width);
|
||||
frame.pixel_height = static_cast<std::uint64_t>(size.height);
|
||||
frame.viewport_version = context->viewport_version.load(std::memory_order_acquire);
|
||||
frame.wait_21_ns = submit_time > frame.render_role_enter_ns ? submit_time - frame.render_role_enter_ns : 0;
|
||||
frame.render_queue_wait_ns = submit_time > frame.render_role_enter_ns ? submit_time - frame.render_role_enter_ns : 0;
|
||||
frame.render_begin_ns = submit_time;
|
||||
std::uint64_t previous_render_state_version = pipeline.rendered_edit_version;
|
||||
pipeline.render_job_state = Frame_Job_State::Running;
|
||||
pipeline.rendered_edit_version = frame.input_version;
|
||||
auto job = std::make_shared<Frame_Render_Job>();
|
||||
job->owner = shared_from_this();
|
||||
job->generation = generation.load(std::memory_order_acquire);
|
||||
job->frame_lease = std::move(frame_lease);
|
||||
job->frame_surface = std::move(frame_surface);
|
||||
job->buffer = buffer;
|
||||
job->lifecycle = frame_record;
|
||||
job->worker_stat = std::make_shared<Frame_Worker_Stats>();
|
||||
@@ -127,11 +114,10 @@ public:
|
||||
return true;
|
||||
frame.outcome = Frame_Outcome::Worker_Budget_Dropped;
|
||||
frame.worker.rejected_task_count++;
|
||||
job->frame_lease.reset();
|
||||
pipeline.rendered_edit_version = previous_render_state_version;
|
||||
pipeline.render_job_state = Frame_Job_State::Idle;
|
||||
if (flow)
|
||||
flow->discard_render_frame(std::move(job->frame_surface));
|
||||
collect_frame(frame_record);
|
||||
trigger_refresh_strategy(Refresh_Trigger::Render_Finished, &frame);
|
||||
notify_render_finished(frame);
|
||||
return false;
|
||||
}
|
||||
static bool frame_job_valid(const std::shared_ptr<Frame_Render_Job>& job) {
|
||||
@@ -169,7 +155,6 @@ public:
|
||||
if (!frame_job_valid(job) || !job->lifecycle->render_end_ns)
|
||||
return;
|
||||
Frame_Lifecycle_Record& frame = *job->lifecycle;
|
||||
job->buffer->version.store(frame.frame_id, std::memory_order_release);
|
||||
for (const auto& update_state : job->buffer->presented_update_states) {
|
||||
if (update_state)
|
||||
update_state->complete_until(Update_Stage::Rendered);
|
||||
@@ -191,25 +176,23 @@ public:
|
||||
bool valid_generation = job->generation == generation.load(std::memory_order_acquire);
|
||||
bool valid_context = !destroying();
|
||||
if (!valid_generation || !valid_context || !frame.render_end_ns) {
|
||||
job->frame_lease.reset();
|
||||
if (flow)
|
||||
flow->cancel_render_frame(std::move(job->frame_surface));
|
||||
frame.outcome = Frame_Outcome::Cancelled;
|
||||
pipeline.render_job_state = Frame_Job_State::Idle;
|
||||
supersede_update_states(job->buffer->presented_update_states);
|
||||
trigger_refresh_strategy(Refresh_Trigger::Render_Finished, &frame);
|
||||
notify_render_finished(frame);
|
||||
return;
|
||||
}
|
||||
auto publish_result = pipeline.try_publish_rendered_frame(std::move(job->frame_lease), steady_now_ns());
|
||||
auto publish_result = flow ? flow->publish_rendered_frame(std::move(job->frame_surface), steady_now_ns()) : Render_Surface_Publish_Result{};
|
||||
if (publish_result.superseded_frame_record) {
|
||||
trigger_refresh_strategy(Refresh_Trigger::Frame_Returned, publish_result.superseded_frame_record.get());
|
||||
collect_frame(publish_result.superseded_frame_record);
|
||||
notify_frame_returned(publish_result.superseded_frame_record);
|
||||
}
|
||||
if (publish_result.dropped_frame_record) {
|
||||
collect_frame(publish_result.dropped_frame_record);
|
||||
}
|
||||
pipeline.render_job_state = Frame_Job_State::Idle;
|
||||
if (publish_result.request_present)
|
||||
request_present(publish_result.dirty_rect);
|
||||
trigger_refresh_strategy(Refresh_Trigger::Render_Finished, &frame);
|
||||
notify_render_finished(frame);
|
||||
}
|
||||
void post_scheduler_action(Task task) {
|
||||
if (!task)
|
||||
@@ -227,85 +210,72 @@ public:
|
||||
}
|
||||
scheduler.post(std::move(action));
|
||||
}
|
||||
void trigger_refresh_strategy(Refresh_Trigger trigger, Frame_Lifecycle_Record* frame = nullptr) {
|
||||
if (!strategy)
|
||||
return;
|
||||
std::uint64_t now_ns = steady_now_ns();
|
||||
Refresh_Runtime_Snapshot runtime = refresh_runtime_snapshot(now_ns);
|
||||
Refresh_Action action = strategy->on_trigger(trigger, runtime, frame);
|
||||
apply_refresh_action(action, runtime);
|
||||
bool try_render_once() override {
|
||||
cancel_render_deadline();
|
||||
return submit_render_once();
|
||||
}
|
||||
void notify_input_changed(std::uint64_t now_ns) {
|
||||
if (!strategy)
|
||||
return;
|
||||
Refresh_Runtime_Snapshot runtime = refresh_runtime_snapshot(now_ns);
|
||||
Refresh_Action action = strategy->on_trigger(Refresh_Trigger::Input_Dirty, runtime, nullptr);
|
||||
apply_refresh_action(action, runtime);
|
||||
void request_paint_from_middle() override {
|
||||
auto update_result = flow ? flow->request_present(steady_now_ns()) : Render_Surface_Publish_Result{};
|
||||
if (update_result.request_present)
|
||||
request_present(update_result.dirty_rect);
|
||||
}
|
||||
void notify_model_dirty(std::uint64_t now_ns) {
|
||||
if (flow)
|
||||
flow->notify_model_dirty(now_ns);
|
||||
}
|
||||
void notify_viewport_changed(Size viewport, std::uint64_t now_ns) {
|
||||
if (flow)
|
||||
flow->notify_viewport_changed(viewport, now_ns);
|
||||
}
|
||||
void notify_render_finished(Frame_Lifecycle_Record& frame) {
|
||||
if (flow)
|
||||
flow->notify_render_finished(frame, steady_now_ns());
|
||||
}
|
||||
void notify_started() {
|
||||
if (!strategy_started) {
|
||||
strategy_started = true;
|
||||
trigger_refresh_strategy(Refresh_Trigger::Started);
|
||||
}
|
||||
else {
|
||||
trigger_refresh_strategy(Refresh_Trigger::Input_Dirty);
|
||||
}
|
||||
if (flow)
|
||||
flow->activate_view(steady_now_ns());
|
||||
}
|
||||
void notify_paused() {
|
||||
invalidate_refresh_deadline();
|
||||
if (!strategy_started)
|
||||
return;
|
||||
strategy_started = false;
|
||||
trigger_refresh_strategy(Refresh_Trigger::Paused);
|
||||
if (flow)
|
||||
flow->deactivate_view();
|
||||
}
|
||||
void notify_frame_returned(std::shared_ptr<Frame_Lifecycle_Record> frame) {
|
||||
if (!frame)
|
||||
return;
|
||||
trigger_refresh_strategy(Refresh_Trigger::Frame_Returned, frame.get());
|
||||
if (flow)
|
||||
flow->notify_frame_returned(*frame, steady_now_ns());
|
||||
collect_frame(frame);
|
||||
}
|
||||
void apply_refresh_action(Refresh_Action action, const Refresh_Runtime_Snapshot& runtime) {
|
||||
if (action.request_paint) {
|
||||
auto update_result = pipeline.request_present(steady_now_ns());
|
||||
if (update_result.request_present)
|
||||
request_present(update_result.dirty_rect);
|
||||
}
|
||||
if (action.try_render) {
|
||||
invalidate_refresh_deadline();
|
||||
submit_render_once();
|
||||
return;
|
||||
}
|
||||
if (action.render_deadline_ns) {
|
||||
schedule_refresh_deadline(action.render_deadline_ns);
|
||||
return;
|
||||
}
|
||||
if (!runtime.enabled || runtime.destroying || !runtime.dirty)
|
||||
invalidate_refresh_deadline();
|
||||
void notify_paint_finished() {
|
||||
if (flow)
|
||||
flow->notify_paint_finished(steady_now_ns());
|
||||
}
|
||||
void schedule_refresh_deadline(std::uint64_t deadline_ns) {
|
||||
void schedule_render_deadline(std::uint64_t deadline_ns) override {
|
||||
if (deadline_ns <= steady_now_ns()) {
|
||||
invalidate_refresh_deadline();
|
||||
trigger_refresh_strategy(Refresh_Trigger::Timer_Fired);
|
||||
cancel_render_deadline();
|
||||
if (flow)
|
||||
flow->notify_timer_fired(steady_now_ns());
|
||||
return;
|
||||
}
|
||||
if (scheduled_refresh_deadline_ns && scheduled_refresh_deadline_ns <= deadline_ns)
|
||||
if (scheduled_flow_deadline_ns && scheduled_flow_deadline_ns <= deadline_ns)
|
||||
return;
|
||||
scheduled_refresh_deadline_ns = deadline_ns;
|
||||
std::uint64_t generation = ++refresh_deadline_generation;
|
||||
scheduled_flow_deadline_ns = deadline_ns;
|
||||
std::uint64_t generation = ++flow_deadline_generation;
|
||||
auto guard = context;
|
||||
auto self = shared_from_this();
|
||||
Global::instance()->render_scheduler().post_at(deadline_ns, [guard, self, generation, deadline_ns]() {
|
||||
if (!guard || guard->destroying.load(std::memory_order_acquire))
|
||||
return;
|
||||
if (self->refresh_deadline_generation != generation || self->scheduled_refresh_deadline_ns != deadline_ns)
|
||||
if (self->flow_deadline_generation != generation || self->scheduled_flow_deadline_ns != deadline_ns)
|
||||
return;
|
||||
self->scheduled_refresh_deadline_ns = 0;
|
||||
self->trigger_refresh_strategy(Refresh_Trigger::Timer_Fired);
|
||||
self->scheduled_flow_deadline_ns = 0;
|
||||
if (self->flow)
|
||||
self->flow->notify_timer_fired(steady_now_ns());
|
||||
});
|
||||
}
|
||||
void invalidate_refresh_deadline() {
|
||||
scheduled_refresh_deadline_ns = 0;
|
||||
++refresh_deadline_generation;
|
||||
void cancel_render_deadline() override {
|
||||
scheduled_flow_deadline_ns = 0;
|
||||
++flow_deadline_generation;
|
||||
}
|
||||
void dispatch_event(const Event& event) {
|
||||
if (!root)
|
||||
@@ -346,8 +316,8 @@ public:
|
||||
append_renderable_snapshot(result, root);
|
||||
return result;
|
||||
}
|
||||
Renderable_Tree_Snapshot renderable_tree_snapshot() const {
|
||||
Renderable_Tree_Snapshot result;
|
||||
Renderable_Frame_Tree renderable_tree_snapshot() const {
|
||||
Renderable_Frame_Tree result;
|
||||
if (root)
|
||||
root->append_tree_snapshot(result);
|
||||
return result;
|
||||
@@ -413,18 +383,15 @@ public:
|
||||
Pixel p = context->background_rgba.load(std::memory_order_acquire);
|
||||
return premultiplied_to_color(p);
|
||||
}
|
||||
std::unique_ptr<Refresh_Strategy> strategy;
|
||||
std::unique_ptr<Frame_Flow> flow;
|
||||
std::shared_ptr<Plot_Render_Context> context = std::make_shared<Plot_Render_Context>();
|
||||
std::shared_ptr<Renderable> root;
|
||||
std::vector<std::weak_ptr<Renderable>> hover_targets;
|
||||
Plot_Frame_Pipeline pipeline;
|
||||
std::weak_ptr<Presentation_Sink> sink;
|
||||
std::atomic_bool rendering{false};
|
||||
std::atomic<std::uint64_t> latest_input_time_ns{0};
|
||||
std::atomic<std::uint64_t> pending_input_count{0};
|
||||
bool strategy_started{};
|
||||
std::uint64_t refresh_deadline_generation{};
|
||||
std::uint64_t scheduled_refresh_deadline_ns{};
|
||||
std::uint64_t flow_deadline_generation{};
|
||||
std::uint64_t scheduled_flow_deadline_ns{};
|
||||
std::uint64_t next_frame_id{};
|
||||
std::uint64_t previous_frame_time_ns{};
|
||||
Plot_Execution_Id plot_execution_id{};
|
||||
@@ -436,17 +403,22 @@ Plot_Execution_Id next_plot_execution_id() {
|
||||
return next_id.fetch_add(1, std::memory_order_acq_rel);
|
||||
}
|
||||
} // namespace
|
||||
Plot_Core::Plot_Core(std::unique_ptr<Refresh_Strategy> strategy)
|
||||
: d(std::make_shared<Plot_Core_Private>(std::move(strategy))) {
|
||||
Plot_Core::Plot_Core(std::unique_ptr<Frame_Flow> flow)
|
||||
: d(std::make_shared<Plot_Core_Private>(std::move(flow))) {
|
||||
d->plot_execution_id = next_plot_execution_id();
|
||||
d->context->owner.store(this, std::memory_order_release);
|
||||
if (d->flow)
|
||||
d->flow->attach(*d);
|
||||
Global::instance()->render_scheduler().register_plot(this);
|
||||
}
|
||||
Plot_Core::~Plot_Core() {
|
||||
d->context->destroying.store(true, std::memory_order_release);
|
||||
d->generation.fetch_add(1, std::memory_order_acq_rel);
|
||||
if (d->flow)
|
||||
d->flow->shutdown();
|
||||
Global::instance()->render_scheduler().remove_plot(this);
|
||||
d->pipeline.cancel_all_update_states();
|
||||
if (d->flow)
|
||||
d->flow->cancel_all_update_states();
|
||||
d->context->owner.store(nullptr, std::memory_order_release);
|
||||
}
|
||||
void Plot_Core::init() {
|
||||
@@ -479,11 +451,11 @@ void Plot_Core::remove_renderable(const std::shared_ptr<Renderable>& renderable)
|
||||
d->root.reset();
|
||||
else
|
||||
renderable->detach_from_parent();
|
||||
mark_render_state_dirty();
|
||||
notify_model_dirty();
|
||||
}
|
||||
void Plot_Core::set_background_color(Color color) {
|
||||
d->context->background_rgba.store(premultiply(color), std::memory_order_release);
|
||||
mark_render_state_dirty();
|
||||
notify_model_dirty();
|
||||
}
|
||||
Color Plot_Core::background_color() const {
|
||||
return d->background_color();
|
||||
@@ -499,6 +471,11 @@ void Plot_Core::set_viewport_size(Size size) {
|
||||
d->context->viewport_version.fetch_add(1, std::memory_order_acq_rel);
|
||||
if (!size.empty())
|
||||
d->context->first_resize.store(false, std::memory_order_release);
|
||||
if (previous != size) {
|
||||
d->post_scheduler_action([data = d, size]() {
|
||||
data->notify_viewport_changed(size, steady_now_ns());
|
||||
});
|
||||
}
|
||||
}
|
||||
Size Plot_Core::viewport_size() const {
|
||||
return {
|
||||
@@ -509,78 +486,68 @@ Size Plot_Core::viewport_size() const {
|
||||
void Plot_Core::dispatch_event(const Event& event) {
|
||||
d->dispatch_event(event);
|
||||
}
|
||||
void Plot_Core::mark_render_state_dirty() {
|
||||
void Plot_Core::notify_model_dirty() {
|
||||
if (d->destroying())
|
||||
return;
|
||||
std::uint64_t now_ns = steady_now_ns();
|
||||
d->pipeline.advance_edit_version();
|
||||
if (d->flow)
|
||||
d->flow->advance_edit_version();
|
||||
d->latest_input_time_ns.store(now_ns, std::memory_order_release);
|
||||
d->pending_input_count.fetch_add(1, std::memory_order_relaxed);
|
||||
d->post_scheduler_action([data = d, now_ns]() {
|
||||
data->notify_input_changed(now_ns);
|
||||
data->notify_model_dirty(now_ns);
|
||||
});
|
||||
}
|
||||
void Plot_Core::request_manual_refresh() {
|
||||
void Plot_Core::request_explicit_render() {
|
||||
if (d->destroying())
|
||||
return;
|
||||
d->post_scheduler_action([data = d]() {
|
||||
data->trigger_refresh_strategy(Refresh_Trigger::Manual_Request);
|
||||
data->notify_model_dirty(steady_now_ns());
|
||||
});
|
||||
}
|
||||
void Plot_Core::start_render() {
|
||||
void Plot_Core::activate_view() {
|
||||
if (d->destroying())
|
||||
return;
|
||||
d->rendering.store(true, std::memory_order_release);
|
||||
d->post_scheduler_action([data = d]() {
|
||||
data->notify_started();
|
||||
});
|
||||
}
|
||||
void Plot_Core::pause_render() {
|
||||
d->rendering.store(false, std::memory_order_release);
|
||||
void Plot_Core::deactivate_view() {
|
||||
d->post_scheduler_action([data = d]() {
|
||||
data->notify_paused();
|
||||
});
|
||||
}
|
||||
bool Plot_Core::is_rendering() const {
|
||||
return d->rendering.load(std::memory_order_acquire);
|
||||
bool Plot_Core::view_active() const {
|
||||
return d->flow && d->flow->view_active();
|
||||
}
|
||||
void Plot_Core::set_presentation_sink(std::weak_ptr<Presentation_Sink> sink) {
|
||||
d->sink = std::move(sink);
|
||||
}
|
||||
Present_Frame_Lease Plot_Core::begin_present() {
|
||||
Present_Frame_Lease result;
|
||||
if (!d->root)
|
||||
Present_Surface_Lease Plot_Core::begin_present() {
|
||||
Present_Surface_Lease result;
|
||||
if (!d->root || !d->flow)
|
||||
return result;
|
||||
auto consumed = d->pipeline.try_acquire_paint_frame(steady_now_ns());
|
||||
if (consumed.returned_frame_record) {
|
||||
d->post_scheduler_action([data = d, frame = consumed.returned_frame_record]() mutable {
|
||||
std::shared_ptr<Frame_Lifecycle_Record> returned_frame;
|
||||
result = d->flow->begin_present(steady_now_ns(), returned_frame);
|
||||
if (returned_frame) {
|
||||
d->post_scheduler_action([data = d, frame = std::move(returned_frame)]() mutable {
|
||||
data->notify_frame_returned(frame);
|
||||
});
|
||||
}
|
||||
result.is_new_frame = consumed.has_new_frame;
|
||||
result.update_was_pending = consumed.present_request_was_pending;
|
||||
if (consumed.lease)
|
||||
result.lease_.emplace(std::move(consumed.lease));
|
||||
if (!result.new_frame() && result.request_was_pending()) {
|
||||
d->post_scheduler_action([data = d]() {
|
||||
data->trigger_refresh_strategy(Refresh_Trigger::Paint_Finished);
|
||||
data->notify_paint_finished();
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}
|
||||
void Plot_Core::end_present(Present_Frame_Lease&& frame, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) {
|
||||
if (frame.lease_ && frame.lease_->frame) {
|
||||
auto* rendered_frame = frame.lease_->frame;
|
||||
if (rendered_frame->metadata) {
|
||||
rendered_frame->metadata->paint_begin_ns = paint_begin_ns;
|
||||
rendered_frame->metadata->paint_end_ns = paint_end_ns;
|
||||
rendered_frame->metadata->wait_23_ns = paint_begin_ns > rendered_frame->metadata->transition_12_ns ? paint_begin_ns - rendered_frame->metadata->transition_12_ns : 0;
|
||||
}
|
||||
complete_update_states(rendered_frame->presented_update_states, Update_Stage::Presented);
|
||||
void Plot_Core::end_present(Present_Surface_Lease&& frame, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns) {
|
||||
if (d->flow) {
|
||||
if (auto returned = d->flow->end_present(std::move(frame), paint_begin_ns, paint_end_ns))
|
||||
d->collect_frame(returned);
|
||||
}
|
||||
frame.lease_.reset();
|
||||
d->post_scheduler_action([data = d]() mutable {
|
||||
data->trigger_refresh_strategy(Refresh_Trigger::Paint_Finished);
|
||||
data->notify_paint_finished();
|
||||
});
|
||||
}
|
||||
} // namespace renderive
|
||||
|
||||
+10
-10
@@ -4,9 +4,9 @@
|
||||
#include "../base/Color.h"
|
||||
#include "../base/Geometry.h"
|
||||
#include "../event/Event.h"
|
||||
#include "Present_Frame_Lease.h"
|
||||
#include "../flow/Frame_Flow.h"
|
||||
#include "../flow/Present_Surface_Lease.h"
|
||||
#include "Presentation_Sink.h"
|
||||
#include "Refresh_Strategy.h"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -16,7 +16,7 @@ class Plot_Core_Private;
|
||||
|
||||
class Plot_Core {
|
||||
public:
|
||||
explicit Plot_Core(std::unique_ptr<Refresh_Strategy> strategy);
|
||||
explicit Plot_Core(std::unique_ptr<Frame_Flow> flow);
|
||||
~Plot_Core();
|
||||
|
||||
void init();
|
||||
@@ -35,17 +35,17 @@ public:
|
||||
[[nodiscard]] Size viewport_size() const;
|
||||
|
||||
void dispatch_event(const Event& event);
|
||||
void mark_render_state_dirty();
|
||||
void request_manual_refresh();
|
||||
void notify_model_dirty();
|
||||
void request_explicit_render();
|
||||
|
||||
void start_render();
|
||||
void pause_render();
|
||||
[[nodiscard]] bool is_rendering() const;
|
||||
void activate_view();
|
||||
void deactivate_view();
|
||||
[[nodiscard]] bool view_active() const;
|
||||
|
||||
void set_presentation_sink(std::weak_ptr<Presentation_Sink> sink);
|
||||
|
||||
Present_Frame_Lease begin_present();
|
||||
void end_present(Present_Frame_Lease&& frame, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns);
|
||||
Present_Surface_Lease begin_present();
|
||||
void end_present(Present_Surface_Lease&& frame, std::uint64_t paint_begin_ns, std::uint64_t paint_end_ns);
|
||||
|
||||
private:
|
||||
std::shared_ptr<Plot_Core_Private> d;
|
||||
|
||||
@@ -1,212 +0,0 @@
|
||||
#include "Plot_Frame_Pipeline.h"
|
||||
#include "../architecture/Update_Completion.h"
|
||||
#include <utility>
|
||||
|
||||
namespace renderive {
|
||||
|
||||
Plot_Frame::Plot_Frame()
|
||||
: presented_update_states(memory_resource(Memory_Domain::Update_Completion)),
|
||||
metadata(std::make_shared<Frame_Lifecycle_Record>()) {}
|
||||
|
||||
Plot_Frame_Lease::Plot_Frame_Lease(Plot_Frame* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease)
|
||||
: frame(buffer), control(control), lease(lease) {}
|
||||
|
||||
Plot_Frame_Lease::Plot_Frame_Lease(Plot_Frame_Lease&& other) noexcept
|
||||
: frame(std::exchange(other.frame, nullptr)),
|
||||
control(std::exchange(other.control, nullptr)),
|
||||
lease(std::exchange(other.lease, Triple_Buffer_Lease{})) {}
|
||||
|
||||
Plot_Frame_Lease& Plot_Frame_Lease::operator=(Plot_Frame_Lease&& other) noexcept {
|
||||
if (this == &other)
|
||||
return *this;
|
||||
reset();
|
||||
frame = std::exchange(other.frame, nullptr);
|
||||
control = std::exchange(other.control, nullptr);
|
||||
lease = std::exchange(other.lease, Triple_Buffer_Lease{});
|
||||
return *this;
|
||||
}
|
||||
|
||||
Plot_Frame_Lease::~Plot_Frame_Lease() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Plot_Frame_Lease::reset() {
|
||||
if (!control)
|
||||
return;
|
||||
control->unmark_use(lease);
|
||||
frame = nullptr;
|
||||
control = nullptr;
|
||||
lease = {};
|
||||
}
|
||||
|
||||
Plot_Frame_Pipeline::Plot_Frame_Pipeline() {
|
||||
frame_control.reset();
|
||||
}
|
||||
|
||||
Plot_Frame_Pipeline::~Plot_Frame_Pipeline() {
|
||||
cancel_all_update_states();
|
||||
}
|
||||
|
||||
void Plot_Frame_Pipeline::advance_edit_version() {
|
||||
edit_version.fetch_add(1, std::memory_order_release);
|
||||
}
|
||||
|
||||
bool Plot_Frame_Pipeline::has_unrendered_edit() const {
|
||||
return buffer_version_newer(edit_version.load(std::memory_order_acquire), rendered_edit_version);
|
||||
}
|
||||
|
||||
Plot_Frame* Plot_Frame_Pipeline::frame_by_index(std::uint8_t index) {
|
||||
return &frames[index];
|
||||
}
|
||||
|
||||
const Plot_Frame* Plot_Frame_Pipeline::frame_by_index(std::uint8_t index) const {
|
||||
return &frames[index];
|
||||
}
|
||||
|
||||
Plot_Frame* Plot_Frame_Pipeline::frame_by_role(std::uint8_t role) {
|
||||
return frame_by_index(frame_control.read_view()[role]);
|
||||
}
|
||||
|
||||
const Plot_Frame* Plot_Frame_Pipeline::frame_by_role(std::uint8_t role) const {
|
||||
return frame_by_index(frame_control.read_view()[role]);
|
||||
}
|
||||
|
||||
Plot_Frame_Lease Plot_Frame_Pipeline::try_acquire_render_frame() {
|
||||
auto record = frame_control.try_acquire_role(Frame_Rendering);
|
||||
if (record.result != Triple_Buffer_Result::Success) {
|
||||
++attempt_counters.render_role_busy_count;
|
||||
return {};
|
||||
}
|
||||
return {frame_by_index(record.lease.index), &frame_control, record.lease};
|
||||
}
|
||||
|
||||
Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::try_publish_rendered_frame(Plot_Frame_Lease rendering_lease, std::uint64_t now_ns) {
|
||||
Frame_Publish_Result result;
|
||||
if (!rendering_lease)
|
||||
return result;
|
||||
Plot_Frame* rendered = rendering_lease.frame;
|
||||
|
||||
auto swap = frame_control.try_swap_role_with_left_lease(Frame_Rendering, Frame_Middle, rendering_lease.lease, [this](const Triple_Buffer_View& view) {
|
||||
const Plot_Frame* rendering = frame_by_index(view[Frame_Rendering]);
|
||||
const Plot_Frame* middle = frame_by_index(view[Frame_Middle]);
|
||||
return buffer_version_newer(rendering->version.load(std::memory_order_acquire), middle->version.load(std::memory_order_acquire));
|
||||
}, [this, &result, now_ns](const Triple_Buffer_View& old_view, const Triple_Buffer_View& new_view) {
|
||||
Plot_Frame* rendered = frame_by_index(old_view[Frame_Rendering]);
|
||||
Plot_Frame* old_middle = frame_by_index(old_view[Frame_Middle]);
|
||||
const Plot_Frame* painting = frame_by_index(old_view[Frame_Painting]);
|
||||
bool supersede_middle = buffer_version_newer(old_middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire));
|
||||
|
||||
rendered->metadata->transition_12_ns = now_ns;
|
||||
rendered->metadata->wait_12_ns = now_ns > rendered->metadata->render_end_ns ? now_ns - rendered->metadata->render_end_ns : 0;
|
||||
|
||||
if (supersede_middle && old_middle->metadata && old_middle->metadata->frame_id) {
|
||||
old_middle->metadata->superseded_time_ns = now_ns;
|
||||
old_middle->metadata->outcome = Frame_Outcome::Superseded;
|
||||
result.superseded_frame_record = old_middle->metadata;
|
||||
supersede_update_states(old_middle->presented_update_states);
|
||||
}
|
||||
|
||||
Plot_Frame* next_rendering = frame_by_index(new_view[Frame_Rendering]);
|
||||
next_rendering->metadata = std::make_shared<Frame_Lifecycle_Record>();
|
||||
next_rendering->metadata->render_role_enter_ns = now_ns;
|
||||
});
|
||||
if (swap.result != Triple_Buffer_Result::Success) {
|
||||
rendered->metadata->outcome = Frame_Outcome::Publish_Dropped;
|
||||
result.dropped_frame_record = rendered->metadata;
|
||||
supersede_update_states(rendered->presented_update_states);
|
||||
++attempt_counters.publish_dropped_count;
|
||||
return result;
|
||||
}
|
||||
|
||||
rendering_lease.reset();
|
||||
Frame_Publish_Result update_result = request_present(now_ns);
|
||||
update_result.superseded_frame_record = result.superseded_frame_record;
|
||||
update_result.dropped_frame_record = result.dropped_frame_record;
|
||||
return update_result;
|
||||
}
|
||||
|
||||
Plot_Frame_Pipeline::Frame_Publish_Result Plot_Frame_Pipeline::request_present(std::uint64_t now_ns) {
|
||||
Frame_Publish_Result result;
|
||||
auto record = frame_control.try_acquire_role(Frame_Middle);
|
||||
if (record.result != Triple_Buffer_Result::Success)
|
||||
return result;
|
||||
|
||||
Plot_Frame_Lease middle_lease{frame_by_index(record.lease.index), &frame_control, record.lease};
|
||||
Plot_Frame* middle = middle_lease.frame;
|
||||
const Plot_Frame* painting = frame_by_role(Frame_Painting);
|
||||
if (!buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire)))
|
||||
return result;
|
||||
|
||||
if (paint_request_pending.load(std::memory_order_acquire)) {
|
||||
middle->metadata->update_request_id = pending_present_request_id.load(std::memory_order_acquire);
|
||||
middle->metadata->update_request_time_ns = pending_present_request_time_ns.load(std::memory_order_acquire);
|
||||
return result;
|
||||
}
|
||||
result.update_request_id = ++next_present_request_id;
|
||||
result.update_request_time = now_ns;
|
||||
middle->metadata->update_request_id = result.update_request_id;
|
||||
middle->metadata->update_request_time_ns = result.update_request_time;
|
||||
pending_present_request_id.store(result.update_request_id, std::memory_order_release);
|
||||
pending_present_request_time_ns.store(result.update_request_time, std::memory_order_release);
|
||||
paint_request_pending.store(true, std::memory_order_release);
|
||||
Size size = middle->image.size();
|
||||
result.dirty_rect = Rect{0, 0, size.width, size.height};
|
||||
result.request_present = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
Plot_Frame_Pipeline::Frame_Consume_Result Plot_Frame_Pipeline::try_acquire_paint_frame(std::uint64_t now_ns) {
|
||||
Frame_Consume_Result result;
|
||||
bool pending = paint_request_pending.load(std::memory_order_acquire);
|
||||
|
||||
if (middle_frame_ready()) {
|
||||
auto swap = frame_control.try_swap_role(Frame_Middle, Frame_Painting, [this](const Triple_Buffer_View& view) {
|
||||
const Plot_Frame* middle = frame_by_index(view[Frame_Middle]);
|
||||
const Plot_Frame* painting = frame_by_index(view[Frame_Painting]);
|
||||
return buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire));
|
||||
}, [this, &result, now_ns](const Triple_Buffer_View& old_view, const Triple_Buffer_View&) {
|
||||
Plot_Frame* old_painting = frame_by_index(old_view[Frame_Painting]);
|
||||
Plot_Frame* new_painting = frame_by_index(old_view[Frame_Middle]);
|
||||
result.has_new_frame = true;
|
||||
if (old_painting->metadata && old_painting->metadata->frame_id && old_painting->metadata->paint_end_ns) {
|
||||
old_painting->metadata->transition_32_ns = now_ns;
|
||||
old_painting->metadata->wait_32_ns = now_ns > old_painting->metadata->paint_end_ns ? now_ns - old_painting->metadata->paint_end_ns : 0;
|
||||
result.returned_frame_record = old_painting->metadata;
|
||||
}
|
||||
new_painting->metadata->transition_23_ns = now_ns;
|
||||
new_painting->metadata->wait_23_ns = now_ns > new_painting->metadata->transition_12_ns ? now_ns - new_painting->metadata->transition_12_ns : 0;
|
||||
});
|
||||
if (swap.result != Triple_Buffer_Result::Success) {
|
||||
result.paint_frame_unavailable = true;
|
||||
++attempt_counters.paint_role_busy_count;
|
||||
}
|
||||
}
|
||||
|
||||
auto record = frame_control.try_acquire_role(Frame_Painting);
|
||||
if (record.result == Triple_Buffer_Result::Success) {
|
||||
result.lease = {frame_by_index(record.lease.index), &frame_control, record.lease};
|
||||
result.present_request_was_pending = pending && paint_request_pending.exchange(false, std::memory_order_acq_rel);
|
||||
}
|
||||
else {
|
||||
result.paint_frame_unavailable = true;
|
||||
++attempt_counters.paint_role_busy_count;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Plot_Frame_Pipeline::middle_frame_ready() const {
|
||||
const Plot_Frame* middle = frame_by_role(Frame_Middle);
|
||||
const Plot_Frame* painting = frame_by_role(Frame_Painting);
|
||||
return buffer_version_newer(middle->version.load(std::memory_order_acquire), painting->version.load(std::memory_order_acquire));
|
||||
}
|
||||
|
||||
bool Plot_Frame_Pipeline::painting_frame_in_use() const {
|
||||
return frame_control.role_busy(Frame_Painting);
|
||||
}
|
||||
|
||||
void Plot_Frame_Pipeline::cancel_all_update_states() {
|
||||
for (Plot_Frame& frame : frames)
|
||||
cancel_update_states(frame.presented_update_states);
|
||||
}
|
||||
|
||||
} // namespace renderive
|
||||
@@ -1,83 +0,0 @@
|
||||
#pragma once
|
||||
#include "../architecture/Frame_Scheduler.h"
|
||||
#include "../architecture/Render_Config.h"
|
||||
#include "../architecture/Triple_Buffer.h"
|
||||
#include "../base/Geometry.h"
|
||||
#include "../base/Object_Semantics.h"
|
||||
#include "../render/Image.h"
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
namespace renderive {
|
||||
struct Update_State;
|
||||
enum class Frame_Job_State {
|
||||
Idle,
|
||||
Running
|
||||
};
|
||||
struct Plot_Frame {
|
||||
Plot_Frame();
|
||||
std::atomic<std::uint64_t> version{0};
|
||||
Image image;
|
||||
std::pmr::vector<std::shared_ptr<Update_State>> presented_update_states;
|
||||
std::shared_ptr<Frame_Lifecycle_Record> metadata;
|
||||
};
|
||||
struct Plot_Frame_Lease : Move_Only {
|
||||
Plot_Frame_Lease() = default;
|
||||
Plot_Frame_Lease(Plot_Frame* buffer, Triple_Role_Buffer_Control* control, Triple_Buffer_Lease lease);
|
||||
Plot_Frame_Lease(Plot_Frame_Lease&& other) noexcept;
|
||||
Plot_Frame_Lease& operator=(Plot_Frame_Lease&& other) noexcept;
|
||||
~Plot_Frame_Lease();
|
||||
void reset();
|
||||
Plot_Frame* frame{};
|
||||
Triple_Role_Buffer_Control* control{};
|
||||
Triple_Buffer_Lease lease{};
|
||||
explicit operator bool() const {
|
||||
return frame && lease;
|
||||
}
|
||||
};
|
||||
class Plot_Frame_Pipeline {
|
||||
public:
|
||||
struct Frame_Publish_Result {
|
||||
Rect dirty_rect;
|
||||
std::shared_ptr<Frame_Lifecycle_Record> superseded_frame_record;
|
||||
std::shared_ptr<Frame_Lifecycle_Record> dropped_frame_record;
|
||||
std::uint64_t update_request_id{};
|
||||
std::uint64_t update_request_time{};
|
||||
bool request_present{};
|
||||
};
|
||||
struct Frame_Consume_Result {
|
||||
Plot_Frame_Lease lease;
|
||||
std::shared_ptr<Frame_Lifecycle_Record> returned_frame_record;
|
||||
bool has_new_frame{};
|
||||
bool present_request_was_pending{};
|
||||
bool paint_frame_unavailable{};
|
||||
};
|
||||
Plot_Frame_Pipeline();
|
||||
~Plot_Frame_Pipeline();
|
||||
void advance_edit_version();
|
||||
[[nodiscard]] bool has_unrendered_edit() const;
|
||||
[[nodiscard]] Plot_Frame* frame_by_index(std::uint8_t index);
|
||||
[[nodiscard]] const Plot_Frame* frame_by_index(std::uint8_t index) const;
|
||||
[[nodiscard]] Plot_Frame* frame_by_role(std::uint8_t role);
|
||||
[[nodiscard]] const Plot_Frame* frame_by_role(std::uint8_t role) const;
|
||||
[[nodiscard]] Plot_Frame_Lease try_acquire_render_frame();
|
||||
[[nodiscard]] Frame_Publish_Result try_publish_rendered_frame(Plot_Frame_Lease rendering_lease, std::uint64_t now_ns);
|
||||
[[nodiscard]] Frame_Publish_Result request_present(std::uint64_t now_ns);
|
||||
[[nodiscard]] Frame_Consume_Result try_acquire_paint_frame(std::uint64_t now_ns);
|
||||
[[nodiscard]] bool middle_frame_ready() const;
|
||||
[[nodiscard]] bool painting_frame_in_use() const;
|
||||
void cancel_all_update_states();
|
||||
Triple_Role_Buffer_Control frame_control;
|
||||
std::array<Plot_Frame, 3> frames;
|
||||
std::atomic<std::uint64_t> edit_version{1};
|
||||
std::uint64_t rendered_edit_version = 0;
|
||||
Frame_Job_State render_job_state = Frame_Job_State::Idle;
|
||||
Frame_Attempt_Accumulator attempt_counters;
|
||||
std::atomic_bool paint_request_pending{false};
|
||||
std::atomic<std::uint64_t> pending_present_request_id{0};
|
||||
std::atomic<std::uint64_t> pending_present_request_time_ns{0};
|
||||
std::uint64_t next_present_request_id{};
|
||||
};
|
||||
} // namespace renderive
|
||||
@@ -1,21 +0,0 @@
|
||||
#include "Present_Frame_Lease.h"
|
||||
namespace renderive {
|
||||
Present_Frame_Lease::Present_Frame_Lease() = default;
|
||||
Present_Frame_Lease::Present_Frame_Lease(Present_Frame_Lease&&) noexcept = default;
|
||||
Present_Frame_Lease& Present_Frame_Lease::operator=(Present_Frame_Lease&&) noexcept = default;
|
||||
Present_Frame_Lease::~Present_Frame_Lease() = default;
|
||||
Present_Frame_Lease::operator bool() const {
|
||||
return lease_ && lease_->operator bool();
|
||||
}
|
||||
bool Present_Frame_Lease::new_frame() const {
|
||||
return is_new_frame;
|
||||
}
|
||||
bool Present_Frame_Lease::request_was_pending() const {
|
||||
return update_was_pending;
|
||||
}
|
||||
Image_View Present_Frame_Lease::image() const {
|
||||
if (!lease_ || !lease_->frame)
|
||||
return {};
|
||||
return lease_->frame->image.view();
|
||||
}
|
||||
} // namespace renderive
|
||||
@@ -1,23 +0,0 @@
|
||||
#pragma once
|
||||
#include "Plot_Frame_Pipeline.h"
|
||||
#include "../render/Image_View.h"
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
namespace renderive {
|
||||
class Present_Frame_Lease {
|
||||
public:
|
||||
Present_Frame_Lease();
|
||||
Present_Frame_Lease(Present_Frame_Lease&&) noexcept;
|
||||
Present_Frame_Lease& operator=(Present_Frame_Lease&&) noexcept;
|
||||
~Present_Frame_Lease();
|
||||
[[nodiscard]] explicit operator bool() const;
|
||||
[[nodiscard]] bool new_frame() const;
|
||||
[[nodiscard]] bool request_was_pending() const;
|
||||
[[nodiscard]] Image_View image() const;
|
||||
private:
|
||||
friend class Plot_Core;
|
||||
std::optional<Plot_Frame_Lease> lease_;
|
||||
bool is_new_frame{};
|
||||
bool update_was_pending{};
|
||||
};
|
||||
} // namespace renderive
|
||||
@@ -1,43 +0,0 @@
|
||||
#pragma once
|
||||
#include "../architecture/Frame_Scheduler.h"
|
||||
#include <cstdint>
|
||||
|
||||
namespace renderive {
|
||||
|
||||
enum class Refresh_Trigger : std::uint8_t {
|
||||
Started,
|
||||
Input_Dirty,
|
||||
Render_Finished,
|
||||
Paint_Finished,
|
||||
Frame_Returned,
|
||||
Timer_Fired,
|
||||
Manual_Request,
|
||||
Paused
|
||||
};
|
||||
|
||||
struct Refresh_Runtime_Snapshot {
|
||||
bool enabled{};
|
||||
bool destroying{};
|
||||
bool dirty{};
|
||||
bool render_job_active{};
|
||||
bool middle_has_new_frame{};
|
||||
bool paint_active{};
|
||||
std::uint64_t now_ns{};
|
||||
};
|
||||
|
||||
struct Refresh_Action {
|
||||
bool try_render{};
|
||||
bool request_paint{};
|
||||
std::uint64_t render_deadline_ns{};
|
||||
};
|
||||
|
||||
class Refresh_Strategy {
|
||||
public:
|
||||
virtual ~Refresh_Strategy() = default;
|
||||
[[nodiscard]] virtual Refresh_Action on_trigger(
|
||||
Refresh_Trigger trigger,
|
||||
const Refresh_Runtime_Snapshot& runtime,
|
||||
Frame_Lifecycle_Record* frame) = 0;
|
||||
};
|
||||
|
||||
} // namespace renderive
|
||||
Reference in New Issue
Block a user