删除plot_core

This commit is contained in:
2026-08-13 00:35:00 +08:00
parent 15c3d21f2d
commit 05263cd506
67 changed files with 4154 additions and 1336 deletions
+1 -1
View File
@@ -5,7 +5,7 @@
namespace renderive {
Abs_Axis::Abs_Axis(Plot_Core& plot) : Renderable(plot, true) {}
Abs_Axis::Abs_Axis(::Scene_Base& scene) : Renderable(scene, true) {}
Abs_Axis::~Abs_Axis() = default;
+1 -1
View File
@@ -9,7 +9,7 @@ namespace renderive {
class LIB_DECL Abs_Axis : public Renderable {
public:
explicit Abs_Axis(Plot_Core& plot);
explicit Abs_Axis(::Scene_Base& scene);
~Abs_Axis() override;
[[nodiscard]] virtual Axis_Transform transform() const = 0;
+4 -1
View File
@@ -2,6 +2,7 @@
#include "../renderable/Renderable.h"
#include "Axis_Types.h"
#include <renderive/scene/base/Scene_Base.hpp>
#include <memory>
#include <utility>
@@ -72,7 +73,9 @@ public:
std::shared_ptr<Product> build() const {
if (!parent_)
return {};
return parent_->plot().template make_renderable<Product>(parent_, properties_);
auto result = parent_->scene().template make_renderable<Product>(properties_);
attach_renderable_child(parent_->scene(), result, parent_);
return result;
}
private:
+2 -2
View File
@@ -25,10 +25,10 @@ class Axis_State_Strategy
using Base = Double_State_Strategy<Abs_Axis, State, Atomic_Spin_Mutex, State_Observer>;
public:
Axis_State_Strategy(Plot_Core& plot, State state)
Axis_State_Strategy(::Scene_Base& scene, State state)
: Base(state,
With_Observer<State_Observer>{State_Observer(Renderable_State_Observer(this))},
plot) {}
scene) {}
template <auto Member, Property_Member_Assignable<State, Member> Value>
Axis_State_Strategy& set(Value&& value) {
+2 -2
View File
@@ -6,8 +6,8 @@
namespace renderive::detail {
Frequency_Axis_Control::Frequency_Axis_Control(Plot_Core& plot, const Axis_Properties& properties)
: Axis(plot, properties) {}
Frequency_Axis_Control::Frequency_Axis_Control(::Scene_Base& scene, const Axis_Properties& properties)
: Axis(scene, properties) {}
std::string Frequency_Axis_Control::format_tick_label(double tick,
const Axis_Properties& state) const {
+1 -1
View File
@@ -8,7 +8,7 @@ namespace detail {
class LIB_DECL Frequency_Axis_Control : public Axis {
public:
Frequency_Axis_Control(Plot_Core& plot, const Axis_Properties& properties);
Frequency_Axis_Control(::Scene_Base& scene, const Axis_Properties& properties);
protected:
[[nodiscard]] std::string format_tick_label(double tick, const Axis_Properties& state) const override;
};
+2 -2
View File
@@ -2,8 +2,8 @@
namespace renderive::detail {
Axis_Control::Axis_Control(Plot_Core& plot, const Axis_Properties& properties)
: Axis_State_Strategy(plot, properties) {}
Axis_Control::Axis_Control(::Scene_Base& scene, const Axis_Properties& properties)
: Axis_State_Strategy(scene, properties) {}
void Axis_Control::handle_event(const Event& event) {
if (event.type == Event_Type::Wheel && get<&Axis_Properties::wheel>()) {
+1 -1
View File
@@ -34,7 +34,7 @@ struct Axis_Interaction_State {
class LIB_DECL Axis_Control : public Axis_State_Strategy<Axis_Properties>, public Event_Handler {
public:
Axis_Control(Plot_Core& plot, const Axis_Properties& properties);
Axis_Control(::Scene_Base& scene, const Axis_Properties& properties);
void handle_event(const Event& event) override;
void publish() override;
+2 -2
View File
@@ -16,8 +16,8 @@ Time_Axis_State time_state_from(const Time_Axis_Properties& properties) {
} // namespace
Time_Axis_Control::Time_Axis_Control(Plot_Core& plot, const Time_Axis_Properties& properties)
: Axis_State_Strategy(plot, time_state_from(properties)) {}
Time_Axis_Control::Time_Axis_Control(::Scene_Base& scene, const Time_Axis_Properties& properties)
: Axis_State_Strategy(scene, time_state_from(properties)) {}
std::size_t Time_Axis_Control::time_point_count() const {
return read([](const Time_Axis_State& state) { return state.samples.size(); });
+1 -1
View File
@@ -27,7 +27,7 @@ struct Time_Axis_State : Time_Axis_Properties {
class LIB_DECL Time_Axis_Control : public Axis_State_Strategy<Time_Axis_State> {
public:
Time_Axis_Control(Plot_Core& plot, const Time_Axis_Properties& properties);
Time_Axis_Control(::Scene_Base& scene, const Time_Axis_Properties& properties);
[[nodiscard]] std::size_t time_point_count() const;
int append_time(Time_Of_Day time);
[[nodiscard]] Time_Of_Day tick_to_time(int tick) const;
+1 -1
View File
@@ -3,7 +3,7 @@
#include "base/Types.h"
#include "event/Event.h"
#include "renderable/Renderable.h"
#include "plot/Plot_Core.h"
#include "scene/Scene.h"
#include "axis/Axis.h"
#include "plottable/Hover_Tooltip.h"
#include "plottable/Plottables.h"
-591
View File
@@ -1,591 +0,0 @@
#include "Plot_Core.h"
#include "../plottable/Performance_Overlay.h"
#include "../render/Blend2D_Cache.h"
#include "../renderable/Renderable.h"
#include <renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.hpp>
#include <renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.hpp>
#include <renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp>
#include <renderive/base/observer/Observer.hpp>
#include <renderive/scene/Scene2D_Context.hpp>
#include <renderive/state/base/State_Strategy_Base.hpp>
#include <algorithm>
#include <chrono>
#include <cmath>
#include <limits>
#include <mutex>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <variant>
namespace renderive {
namespace {
struct Frame_Observer_Data {
mutable std::mutex mutex;
std::uint32_t last_event{};
std::uint64_t observation_count{};
std::uint64_t produced_frame_count{};
std::uint64_t consumed_frame_count{};
std::uint64_t dropped_frame_count{};
std::uint64_t failed_operation_count{};
std::uint64_t pending_frame_count{};
std::uint64_t latest_sequence{};
std::uint64_t paint_duration_ns{};
std::uint64_t render_duration_ns{};
std::uint64_t paint_lease_wait_ns{};
std::uint64_t paint_state_wait_ns{};
std::uint64_t publish_state_wait_ns{};
std::uint64_t ready_wait_ns{};
std::uint64_t frame_age_at_render_ns{};
std::uint64_t render_lease_wait_ns{};
std::uint64_t render_state_wait_ns{};
std::uint64_t render_finish_state_wait_ns{};
std::uint64_t queue_wait_ns{};
};
struct Frame_Observer {
static constexpr bool enabled = true;
std::shared_ptr<Frame_Observer_Data> data;
template <class Observation>
void observe(const Observation& observation) noexcept {
std::lock_guard lock(data->mutex);
data->last_event = static_cast<std::uint32_t>(observation.event);
++data->observation_count;
if (observation.statistics.sequence != 0)
data->latest_sequence = observation.statistics.sequence;
if constexpr (requires { observation.statistics.timing.paint_duration_ns; }) {
if (observation.statistics.sequence != 0) {
const auto& timing = observation.statistics.timing;
data->paint_lease_wait_ns = timing.paint_lease_wait_ns;
data->paint_state_wait_ns = timing.paint_state_wait_ns;
data->publish_state_wait_ns = timing.publish_state_wait_ns;
data->ready_wait_ns = timing.ready_wait_ns;
data->frame_age_at_render_ns = timing.frame_age_at_render_ns;
data->render_lease_wait_ns = timing.render_lease_wait_ns;
data->render_state_wait_ns = timing.render_state_wait_ns;
data->render_finish_state_wait_ns = timing.render_finish_state_wait_ns;
}
} else {
if (observation.statistics.sequence != 0) {
data->paint_duration_ns = observation.statistics.paint_duration_ns;
data->render_duration_ns = observation.statistics.render_duration_ns;
}
}
if constexpr (requires { observation.statistics.queue_wait_ns; }) {
if (observation.statistics.sequence != 0)
data->queue_wait_ns = observation.statistics.queue_wait_ns;
}
if constexpr (requires { observation.state.prepared_frame_count; }) {
data->produced_frame_count = observation.state.prepared_frame_count;
data->consumed_frame_count = observation.state.render_count;
data->dropped_frame_count = observation.state.replaced_prepared_frame_count +
observation.state.discarded_prepared_frame_count;
data->failed_operation_count = observation.state.failed_refresh_count;
data->pending_frame_count = observation.state.pending_frame ? 1U : 0U;
} else if constexpr (requires { observation.state.limit_state; }) {
data->produced_frame_count = observation.statistics.counters.published_frame_count;
data->consumed_frame_count = observation.state.completed_lifecycle_count;
data->dropped_frame_count =
observation.statistics.counters.abandoned_frame_count +
observation.statistics.counters.manually_discarded_frame_count;
data->failed_operation_count = observation.statistics.counters.swap_failure_count;
const auto retired_frame_count =
observation.state.completed_lifecycle_count +
observation.statistics.counters.abandoned_frame_count +
observation.statistics.counters.manually_discarded_frame_count;
data->pending_frame_count =
observation.statistics.counters.published_frame_count > retired_frame_count ? 1U : 0U;
} else {
data->produced_frame_count = observation.state.enqueued_frame_count;
data->consumed_frame_count = observation.state.rendered_frame_count;
data->failed_operation_count = observation.state.empty_acquire_count;
data->pending_frame_count = observation.state.pending_frame_count;
}
}
};
using Frame_Observer_State = ::Observer_State<Frame_Observer>;
using Manual_Frame_Control =
::Manual_Refresh_Strategy<::Scene2D_Frame_Data, std::mutex, Frame_Observer_State>;
using Low_Latency_Frame_Control =
::Low_Latency_Strategy<::Scene2D_Frame_Data, std::mutex, Frame_Observer_State>;
using Playback_Frame_Control =
::Flow_Refresh_Strategy<::Scene2D_Frame_Data, std::mutex, Frame_Observer_State>;
using Manual_Plot_Scene =
::Scene2D_Context<Manual_Frame_Control, detail::Blend2D_Color_Cache>;
using Low_Latency_Plot_Scene =
::Scene2D_Context<Low_Latency_Frame_Control, detail::Blend2D_Color_Cache>;
using Playback_Plot_Scene =
::Scene2D_Context<Playback_Frame_Control, detail::Blend2D_Color_Cache>;
using Plot_Scene = std::variant<std::unique_ptr<Manual_Plot_Scene>,
std::unique_ptr<Low_Latency_Plot_Scene>,
std::unique_ptr<Playback_Plot_Scene>>;
template <class Function>
decltype(auto) with_scene(Plot_Scene& scene, Function&& function) {
return std::visit(
[&function](auto& concrete) -> decltype(auto) {
return std::forward<Function>(function)(*concrete);
},
scene);
}
template <class Function>
decltype(auto) with_scene(const Plot_Scene& scene, Function&& function) {
return std::visit(
[&function](const auto& concrete) -> decltype(auto) {
return std::forward<Function>(function)(*concrete);
},
scene);
}
std::string observer_event_name(Frame_Control_Mode mode, std::uint32_t event) {
if (mode == Frame_Control_Mode::Manual) {
static constexpr const char* names[]{"prepared", "prepared_replaced", "refresh_succeeded",
"refresh_failed", "rendered", "real_time_data_updated",
"manually_discarded"};
return event < std::size(names) ? names[event] : "none";
}
if (mode == Frame_Control_Mode::Playback) {
static constexpr const char* names[]{"enqueued", "dequeued", "queue_empty", "rendered",
"real_time_data_updated"};
return event < std::size(names) ? names[event] : "none";
}
static constexpr const char* names[]{"published", "abandoned", "manually_discarded",
"swap_failed", "rendered", "lifecycle_completed",
"real_time_data_updated"};
return event < std::size(names) ? names[event] : "none";
}
std::string limit_state_name(bool available, std::uint32_t state) {
if (!available)
return "not_applicable";
static constexpr const char* names[]{"frequency_limited", "paint_limited", "render_limited",
"consumer_limited", "unlimited"};
return state < std::size(names) ? names[state] : "unknown";
}
Rect full_rect(Size size) {
return {0, 0, std::max(0, size.width), std::max(0, size.height)};
}
class Renderable_Group final : public Renderable {
public:
using Renderable::Renderable;
private:
void prepare_frame(const Render_State_View&,
const Scene_Render_Context&) override {}
void paint(detail::Painter&, const Scene_Render_Context&) override {}
};
} // namespace
struct Plot_Core::Impl {
explicit Impl(Frame_Control_Mode selected_mode)
: mode(selected_mode), observer(std::make_shared<Frame_Observer_Data>()),
scene(make_scene(selected_mode, observer)) {}
static Plot_Scene make_scene(Frame_Control_Mode mode,
const std::shared_ptr<Frame_Observer_Data>& observer) {
auto& resource = *memory_resource(Memory_Domain::Plot_Frame);
Frame_Observer_State observer_state(Frame_Observer{observer});
switch (mode) {
case Frame_Control_Mode::Manual:
return std::make_unique<Manual_Plot_Scene>(resource, std::move(observer_state));
case Frame_Control_Mode::Playback:
return std::make_unique<Playback_Plot_Scene>(resource, std::move(observer_state));
case Frame_Control_Mode::Low_Latency:
return std::make_unique<Low_Latency_Plot_Scene>(resource, std::move(observer_state));
}
throw std::invalid_argument("unknown frame control mode");
}
Frame_Control_Mode mode;
std::shared_ptr<Frame_Observer_Data> observer;
Plot_Scene scene;
mutable std::mutex mutex;
std::mutex initialization_mutex;
std::weak_ptr<Presentation_Sink> presentation_sink;
std::shared_ptr<Performance_Overlay> performance;
Color background = Color::black();
Size viewport;
std::atomic_bool active{};
std::atomic_bool dirty{true};
};
Plot_Core::Plot_Core()
: Plot_Core(Frame_Control_Mode::Low_Latency) {}
Plot_Core::Plot_Core(Frame_Control_Mode mode)
: impl_(std::make_unique<Impl>(mode)) {}
Plot_Core::~Plot_Core() = default;
void Plot_Core::init() {
std::lock_guard initialization_lock(impl_->initialization_mutex);
if (root_renderable())
return;
auto root = renderive::make_shared<Renderable_Group>(Memory_Domain::Renderable, *this, true);
root->set_object_name("root");
with_scene(impl_->scene, [&root](auto& scene) { scene.attach_renderable(root); });
notify_model_dirty();
}
std::shared_ptr<Renderable> Plot_Core::root_renderable() const {
const auto topology = with_scene(impl_->scene, [](const auto& scene) {
return scene.topology_snapshot();
});
for (const auto& relationship : topology.display) {
if (relationship.parent)
continue;
auto base = std::const_pointer_cast<::Renderable_Base>(relationship.child);
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base))
return renderable;
}
return {};
}
std::shared_ptr<Renderable> Plot_Core::create_renderable_node(
const std::shared_ptr<Renderable>& parent, std::string object_name) {
auto renderable = renderive::make_shared<Renderable_Group>(
Memory_Domain::Renderable, *this, true);
renderable->set_object_name(std::move(object_name));
attach_renderable(renderable, parent);
return renderable;
}
void Plot_Core::attach_renderable(const std::shared_ptr<Renderable>& renderable,
const std::shared_ptr<Renderable>& parent) {
if (!renderable)
throw std::invalid_argument("renderable is null");
Renderable* parent_pointer = parent.get();
if (!parent_pointer) {
auto root = root_renderable();
if (!root)
throw std::logic_error("Plot_Core::init must be called before adding renderables");
parent_pointer = root.get();
}
with_scene(impl_->scene, [&renderable](auto& scene) { scene.attach_renderable(renderable); });
try {
with_scene(impl_->scene, [&renderable, parent_pointer](auto& scene) {
scene.set_display_parent(*renderable, parent_pointer);
scene.set_dependency_parent(*renderable, parent_pointer);
});
} catch (...) {
with_scene(impl_->scene, [&renderable](auto& scene) { scene.detach_renderable(*renderable); });
throw;
}
notify_model_dirty();
}
void Plot_Core::remove_renderable(const std::shared_ptr<Renderable>& renderable) {
if (!renderable || renderable == root_renderable())
return;
with_scene(impl_->scene, [&renderable](auto& scene) { scene.detach_renderable(*renderable); });
notify_model_dirty();
}
void Plot_Core::set_background_color(Color color) {
{
std::lock_guard lock(impl_->mutex);
if (impl_->background == color)
return;
impl_->background = color;
}
notify_model_dirty();
}
Color Plot_Core::background_color() const noexcept {
std::lock_guard lock(impl_->mutex);
return impl_->background;
}
void Plot_Core::set_viewport_size(Size size) {
size.width = std::max(0, size.width);
size.height = std::max(0, size.height);
{
std::lock_guard lock(impl_->mutex);
if (impl_->viewport == size)
return;
impl_->viewport = size;
}
const auto topology = with_scene(impl_->scene, [](const auto& scene) {
return scene.topology_snapshot();
});
for (const auto& renderable : topology.renderables)
const_cast<::Renderable_Base&>(*renderable).invalidate_prepare();
notify_model_dirty();
}
Size Plot_Core::viewport_size() const noexcept {
std::lock_guard lock(impl_->mutex);
return impl_->viewport;
}
void Plot_Core::dispatch_event(const Event& event) {
const auto paint_order = with_scene(impl_->scene, [](const auto& scene) {
return scene.paint_order_snapshot();
});
for (auto iterator = paint_order.rbegin(); iterator != paint_order.rend(); ++iterator) {
auto base = std::const_pointer_cast<::Renderable_Base>(*iterator);
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base)) {
if (renderable->is_visible()) {
if (auto handler = std::dynamic_pointer_cast<Event_Handler>(base))
handler->handle_event(event);
}
if (event.is_accepted())
break;
}
}
}
void Plot_Core::notify_model_dirty() noexcept {
impl_->dirty.store(true, std::memory_order_release);
}
bool Plot_Core::prepare_frame() {
return with_scene(impl_->scene, [](auto& scene) {
auto paint_frame = scene.frame_control.acquire_painter();
if (!paint_frame)
return false;
scene.publish_frame_state();
auto& scene_state = static_cast<typename std::remove_reference_t<decltype(scene)>::Scene_State_Strategy&>(scene);
scene_state.template set<&::Scene2D_State::revision>(scene_state.state_revision() + 1);
scene_state.publish();
return true;
});
}
bool Plot_Core::refresh_manual_frame() {
if (impl_->mode != Frame_Control_Mode::Manual)
return false;
return std::get<std::unique_ptr<Manual_Plot_Scene>>(impl_->scene)->frame_control.refresh();
}
bool Plot_Core::render_prepared_frame() {
return with_scene(impl_->scene, [](auto& scene) {
auto render_frame = scene.frame_control.acquire_renderer();
if (!render_frame)
return false;
scene.render(*render_frame);
scene.wait_for_render();
return true;
});
}
bool Plot_Core::discard_pending_frame() {
return with_scene(impl_->scene, [](auto& scene) {
using Scene = std::remove_reference_t<decltype(scene)>;
if constexpr (std::is_same_v<Scene, Playback_Plot_Scene>)
return false;
else
return scene.frame_control.discard_pending_frame();
});
}
bool Plot_Core::render_frame(bool force) {
if (!view_active() && !force)
return false;
const Size viewport = viewport_size();
if (viewport.empty())
return false;
if (!impl_->dirty.exchange(false, std::memory_order_acq_rel) && !force)
return false;
const auto started = std::chrono::steady_clock::now();
try {
if (!prepare_frame())
return false;
if (impl_->mode == Frame_Control_Mode::Manual && !refresh_manual_frame())
return false;
if (!render_prepared_frame())
return false;
} catch (...) {
notify_model_dirty();
throw;
}
const auto finished = std::chrono::steady_clock::now();
const double duration_ms = std::chrono::duration<double, std::milli>(finished - started).count();
std::shared_ptr<Presentation_Sink> sink;
std::shared_ptr<Performance_Overlay> overlay;
{
std::lock_guard lock(impl_->mutex);
sink = impl_->presentation_sink.lock();
overlay = impl_->performance;
}
if (overlay)
overlay->record_frame(duration_ms, diagnostics(),
with_scene(impl_->scene, [](const auto& scene) {
return scene.renderable_count();
}),
viewport);
if (sink)
sink->request_present(full_rect(viewport));
return true;
}
void Plot_Core::with_frame(const std::function<void(Image_View)>& consumer) {
if (!consumer)
return;
with_scene(impl_->scene, [&consumer](auto& scene) {
scene.with_final_color_cache([&consumer](const detail::Blend2D_Color_Cache& cache) {
consumer(cache.view());
});
});
}
void Plot_Core::activate_view() noexcept {
impl_->active.store(true, std::memory_order_release);
notify_model_dirty();
}
void Plot_Core::deactivate_view() noexcept {
impl_->active.store(false, std::memory_order_release);
}
bool Plot_Core::view_active() const noexcept {
return impl_->active.load(std::memory_order_acquire);
}
void Plot_Core::set_max_render_fps(double fps) {
if (!std::isfinite(fps) || fps <= 0.0)
throw std::invalid_argument("maximum render FPS must be finite and positive");
if (impl_->mode != Frame_Control_Mode::Low_Latency)
throw std::logic_error("maximum render FPS is only available in low-latency mode");
std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.set_frequency_hz(fps);
notify_model_dirty();
}
void Plot_Core::clear_max_render_fps() {
if (impl_->mode != Frame_Control_Mode::Low_Latency)
throw std::logic_error("maximum render FPS is only available in low-latency mode");
std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.clear_frequency_limit();
notify_model_dirty();
}
void Plot_Core::set_consumer_feedback(Frame_Consumer_Feedback feedback) {
if (impl_->mode != Frame_Control_Mode::Low_Latency)
throw std::logic_error("consumer feedback is only available in low-latency mode");
std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.set_consumer_feedback(feedback);
}
void Plot_Core::clear_consumer_feedback() {
if (impl_->mode != Frame_Control_Mode::Low_Latency)
throw std::logic_error("consumer feedback is only available in low-latency mode");
std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.clear_consumer_feedback();
}
double Plot_Core::max_render_fps() const noexcept {
if (impl_->mode != Frame_Control_Mode::Low_Latency)
return std::numeric_limits<double>::quiet_NaN();
return std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.state().frequency_hz;
}
Low_Latency_Diagnostics Plot_Core::diagnostics() const {
if (impl_->mode == Frame_Control_Mode::Low_Latency) {
const auto state = std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.state();
return {view_active(),
{state.frequency_hz, state.completed_lifecycle_count,
state.next_refresh_interval_ns}};
}
const auto observed = frame_observer_snapshot();
return {view_active(),
{std::numeric_limits<double>::quiet_NaN(), observed.consumed_frame_count, 0}};
}
Refresh_Control_Snapshot Plot_Core::refresh_feedback_snapshot() const {
return diagnostics().refresh;
}
Frame_Control_Mode Plot_Core::frame_control_mode() const noexcept {
return impl_->mode;
}
Frame_Observer_Snapshot Plot_Core::frame_observer_snapshot() const {
Frame_Observer_Snapshot snapshot;
{
std::lock_guard lock(impl_->observer->mutex);
snapshot.mode = impl_->mode;
snapshot.last_event = impl_->observer->observation_count == 0 ?
std::string("none") :
observer_event_name(impl_->mode, impl_->observer->last_event);
snapshot.limit_state = "not_applicable";
snapshot.observation_count = impl_->observer->observation_count;
snapshot.produced_frame_count = impl_->observer->produced_frame_count;
snapshot.consumed_frame_count = impl_->observer->consumed_frame_count;
snapshot.dropped_frame_count = impl_->observer->dropped_frame_count;
snapshot.failed_operation_count = impl_->observer->failed_operation_count;
snapshot.pending_frame_count = impl_->observer->pending_frame_count;
snapshot.latest_sequence = impl_->observer->latest_sequence;
snapshot.paint_duration_ns = impl_->observer->paint_duration_ns;
snapshot.render_duration_ns = impl_->observer->render_duration_ns;
snapshot.paint_lease_wait_ns = impl_->observer->paint_lease_wait_ns;
snapshot.paint_state_wait_ns = impl_->observer->paint_state_wait_ns;
snapshot.publish_state_wait_ns = impl_->observer->publish_state_wait_ns;
snapshot.ready_wait_ns = impl_->observer->ready_wait_ns;
snapshot.frame_age_at_render_ns = impl_->observer->frame_age_at_render_ns;
snapshot.render_lease_wait_ns = impl_->observer->render_lease_wait_ns;
snapshot.render_state_wait_ns = impl_->observer->render_state_wait_ns;
snapshot.render_finish_state_wait_ns = impl_->observer->render_finish_state_wait_ns;
snapshot.queue_wait_ns = impl_->observer->queue_wait_ns;
}
if (impl_->mode == Frame_Control_Mode::Low_Latency) {
const auto state = std::get<std::unique_ptr<Low_Latency_Plot_Scene>>(impl_->scene)->frame_control.state();
snapshot.limit_state = limit_state_name(true, static_cast<std::uint32_t>(state.limit_state));
snapshot.frequency_hz = state.frequency_hz;
snapshot.paint_duration_ns = state.paint_duration_ns;
snapshot.render_duration_ns = state.render_duration_ns;
snapshot.target_interval_ns = state.target_interval_ns;
snapshot.frequency_limit_enabled = state.frequency_limit_enabled;
snapshot.consumer_feedback_enabled = state.consumer_feedback_enabled;
snapshot.bottleneck_duration_ns = state.bottleneck_duration_ns;
snapshot.consumer_sample_interval_ns = state.consumer_sample_interval_ns;
snapshot.consumer_smoothed_interval_ns = state.consumer_smoothed_interval_ns;
snapshot.consumer_variation_ns = state.consumer_variation_ns;
snapshot.consumer_safety_interval_ns = state.consumer_safety_interval_ns;
snapshot.consumer_interval_ns = state.consumer_interval_ns;
snapshot.next_refresh_interval_ns = state.next_refresh_interval_ns;
snapshot.end_to_end_ns = state.end_to_end_ns;
}
return snapshot;
}
void Plot_Core::set_presentation_sink(std::weak_ptr<Presentation_Sink> sink) {
std::lock_guard lock(impl_->mutex);
impl_->presentation_sink = std::move(sink);
}
std::shared_ptr<Performance_Overlay> Plot_Core::performance_overlay() const {
std::lock_guard lock(impl_->mutex);
return impl_->performance;
}
void Plot_Core::set_performance_overlay(std::shared_ptr<Performance_Overlay> overlay) {
{
std::lock_guard lock(impl_->mutex);
impl_->performance = std::move(overlay);
}
notify_model_dirty();
}
::Scene_Base& Plot_Core::kernel_scene() const noexcept {
return with_scene(impl_->scene, [](auto& scene) -> ::Scene_Base& { return scene; });
}
void Plot_Core::set_renderable_cache(Renderable& renderable, Renderable_Cache_Mode mode) {
with_scene(impl_->scene, [&renderable, mode](auto& scene) {
scene.set_renderable_configuration(
renderable, {.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel});
});
notify_model_dirty();
}
} // namespace renderive
-150
View File
@@ -1,150 +0,0 @@
#pragma once
#include "../base/Types.h"
#include "../event/Event.h"
#include <renderive/frame_control/Frame_Consumer_Feedback.hpp>
#include <atomic>
#include <concepts>
#include <functional>
#include <memory>
#include <string>
class Scene_Base;
namespace renderive {
class Performance_Overlay;
struct Performance_Overlay_Options;
class Renderable;
enum class Frame_Control_Mode : std::uint8_t {
Manual,
Low_Latency,
Playback
};
struct Refresh_Control_Snapshot {
double frequency_hz{};
std::uint64_t frame_count{};
std::uint64_t next_refresh_interval_ns{};
};
struct Low_Latency_Diagnostics {
bool active{};
Refresh_Control_Snapshot refresh;
};
struct Frame_Observer_Snapshot {
Frame_Control_Mode mode = Frame_Control_Mode::Low_Latency;
std::string last_event;
std::string limit_state;
double frequency_hz{};
std::uint64_t observation_count{};
std::uint64_t produced_frame_count{};
std::uint64_t consumed_frame_count{};
std::uint64_t dropped_frame_count{};
std::uint64_t failed_operation_count{};
std::uint64_t pending_frame_count{};
std::uint64_t latest_sequence{};
std::uint64_t paint_duration_ns{};
std::uint64_t render_duration_ns{};
std::uint64_t target_interval_ns{};
bool frequency_limit_enabled{};
bool consumer_feedback_enabled{};
std::uint64_t bottleneck_duration_ns{};
std::uint64_t consumer_sample_interval_ns{};
std::uint64_t consumer_smoothed_interval_ns{};
std::uint64_t consumer_variation_ns{};
std::uint64_t consumer_safety_interval_ns{};
std::uint64_t consumer_interval_ns{};
std::uint64_t next_refresh_interval_ns{};
std::uint64_t paint_lease_wait_ns{};
std::uint64_t paint_state_wait_ns{};
std::uint64_t publish_state_wait_ns{};
std::uint64_t ready_wait_ns{};
std::uint64_t frame_age_at_render_ns{};
std::uint64_t render_lease_wait_ns{};
std::uint64_t render_state_wait_ns{};
std::uint64_t render_finish_state_wait_ns{};
std::uint64_t queue_wait_ns{};
std::uint64_t end_to_end_ns{};
};
class Presentation_Sink {
public:
virtual ~Presentation_Sink() = default;
virtual void request_present(Rect dirty_rect) = 0;
};
class LIB_DECL Plot_Core {
public:
Plot_Core();
explicit Plot_Core(Frame_Control_Mode mode);
~Plot_Core();
Plot_Core(const Plot_Core&) = delete;
Plot_Core& operator=(const Plot_Core&) = delete;
void init();
[[nodiscard]] std::shared_ptr<Renderable> root_renderable() const;
[[nodiscard]] std::shared_ptr<Renderable> create_renderable_node(
const std::shared_ptr<Renderable>& parent,
std::string object_name = {});
template <class T, class... Args>
requires std::derived_from<T, Renderable> && std::constructible_from<T, Plot_Core&, Args&&...>
std::shared_ptr<T> make_renderable(const std::shared_ptr<Renderable>& parent, Args&&... args) {
auto renderable = renderive::make_shared<T>(Memory_Domain::Renderable, *this,
std::forward<Args>(args)...);
attach_renderable(renderable, parent);
return renderable;
}
void remove_renderable(const std::shared_ptr<Renderable>& renderable);
void set_background_color(Color color);
[[nodiscard]] Color background_color() const noexcept;
void set_viewport_size(Size size);
[[nodiscard]] Size viewport_size() const noexcept;
void dispatch_event(const Event& event);
void notify_model_dirty() noexcept;
[[nodiscard]] bool prepare_frame();
[[nodiscard]] bool refresh_manual_frame();
[[nodiscard]] bool render_prepared_frame();
[[nodiscard]] bool discard_pending_frame();
[[nodiscard]] bool render_frame(bool force = false);
void with_frame(const std::function<void(Image_View)>& consumer);
void activate_view() noexcept;
void deactivate_view() noexcept;
[[nodiscard]] bool view_active() const noexcept;
void set_max_render_fps(double fps);
void clear_max_render_fps();
void set_consumer_feedback(Frame_Consumer_Feedback feedback);
void clear_consumer_feedback();
[[nodiscard]] double max_render_fps() const noexcept;
[[nodiscard]] Low_Latency_Diagnostics diagnostics() const;
[[nodiscard]] Refresh_Control_Snapshot refresh_feedback_snapshot() const;
[[nodiscard]] Frame_Control_Mode frame_control_mode() const noexcept;
[[nodiscard]] Frame_Observer_Snapshot frame_observer_snapshot() const;
void set_presentation_sink(std::weak_ptr<Presentation_Sink> sink);
[[nodiscard]] std::shared_ptr<Performance_Overlay> performance_overlay() const;
private:
friend class Renderable;
friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(Plot_Core&);
friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(
Plot_Core&, const Performance_Overlay_Options&);
struct Impl;
std::unique_ptr<Impl> impl_;
[[nodiscard]] ::Scene_Base& kernel_scene() const noexcept;
void attach_renderable(const std::shared_ptr<Renderable>& renderable,
const std::shared_ptr<Renderable>& parent);
void set_renderable_cache(Renderable& renderable, Renderable_Cache_Mode mode);
void set_performance_overlay(std::shared_ptr<Performance_Overlay> overlay);
};
} // namespace renderive
+2 -2
View File
@@ -31,8 +31,8 @@ struct Afterglow_Control::Impl {
Adaptive_Render_Partitioner partitioner;
Afterglow_Prepare_Buffer prepare_buffer;
};
Afterglow_Control::Afterglow_Control(Plot_Core& plot, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Afterglow_Control::Afterglow_Control(::Scene_Base& scene, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Afterglow_Control::~Afterglow_Control() = default;
std::size_t Afterglow_Control::history_count() const {
return impl_->history.size();
+1 -1
View File
@@ -21,7 +21,7 @@ namespace detail {
class LIB_DECL Afterglow_Control : public Plottable_State<Afterglow_Properties>,
public ::Render_Frame_Completion {
public:
Afterglow_Control(Plot_Core& plot, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
Afterglow_Control(::Scene_Base& scene, const Afterglow_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
~Afterglow_Control() override;
[[nodiscard]] std::size_t history_count() const;
[[nodiscard]] std::size_t latest_spectrum_point_count() const;
@@ -28,8 +28,8 @@ struct Constellation_Diagram_Control::Impl {
Constellation_History points;
Prepare_Buffer prepare_buffer;
};
Constellation_Diagram_Control::Constellation_Diagram_Control(Plot_Core& plot, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(i_axis), std::move(q_axis))) {}
Constellation_Diagram_Control::Constellation_Diagram_Control(::Scene_Base& scene, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(i_axis), std::move(q_axis))) {}
Constellation_Diagram_Control::~Constellation_Diagram_Control() = default;
void Constellation_Diagram_Control::append_point(PointF point) {
const auto now = std::chrono::steady_clock::now();
+1 -1
View File
@@ -19,7 +19,7 @@ struct Constellation_Diagram_Properties {
namespace detail {
class LIB_DECL Constellation_Diagram_Control : public Plottable_State<Constellation_Diagram_Properties> {
public:
Constellation_Diagram_Control(Plot_Core& plot, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis);
Constellation_Diagram_Control(::Scene_Base& scene, const Constellation_Diagram_Properties& properties, std::shared_ptr<Axis> i_axis, std::shared_ptr<Axis> q_axis);
~Constellation_Diagram_Control() override;
void append_point(PointF point);
[[nodiscard]] std::size_t point_count() const;
+2 -2
View File
@@ -21,8 +21,8 @@ struct Frequency_Trace_Control::Impl {
Frequency_Trace_History samples;
Prepare_Buffer prepare_buffer;
};
Frequency_Trace_Control::Frequency_Trace_Control(Plot_Core& plot, const Frequency_Trace_Properties& properties, std::shared_ptr<Time_Axis> time_axis, std::shared_ptr<Axis> value_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(time_axis), std::move(value_axis))) {}
Frequency_Trace_Control::Frequency_Trace_Control(::Scene_Base& scene, const Frequency_Trace_Properties& properties, std::shared_ptr<Time_Axis> time_axis, std::shared_ptr<Axis> value_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(time_axis), std::move(value_axis))) {}
Frequency_Trace_Control::~Frequency_Trace_Control() = default;
void Frequency_Trace_Control::append_sample(int tick, double value) {
const int limit = std::max(2, impl_->time_axis->get<&Time_Axis_Properties::visible_count>());
+1 -1
View File
@@ -8,7 +8,7 @@ struct Frequency_Trace_Properties {
namespace detail {
class LIB_DECL Frequency_Trace_Control : public Plottable_State<Frequency_Trace_Properties> {
public:
Frequency_Trace_Control(Plot_Core& plot, const Frequency_Trace_Properties& properties, std::shared_ptr<Time_Axis> time_axis, std::shared_ptr<Axis> value_axis);
Frequency_Trace_Control(::Scene_Base& scene, const Frequency_Trace_Properties& properties, std::shared_ptr<Time_Axis> time_axis, std::shared_ptr<Axis> value_axis);
~Frequency_Trace_Control() override;
void append_sample(int tick, double value);
void append_sample(Time_Of_Day time, double value);
@@ -150,42 +150,4 @@ void Performance_Overlay::record_frame(double render_duration_ms,
}
}
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Plot_Core& plot) {
if (auto existing = plot.performance_overlay())
return existing;
auto overlay = std::make_shared<Performance_Overlay>();
plot.set_performance_overlay(overlay);
return overlay;
}
std::shared_ptr<Performance_Overlay> attach_performance_overlay(
Plot_Core& plot, const Performance_Overlay_Options& options) {
if (auto existing = plot.performance_overlay()) {
existing->set_options(options);
plot.notify_model_dirty();
return existing;
}
auto overlay = std::make_shared<Performance_Overlay>(options);
plot.set_performance_overlay(overlay);
return overlay;
}
void set_performance_plot_name(Plot_Core& plot, std::string name) {
auto overlay = plot.performance_overlay();
if (!overlay)
overlay = attach_performance_overlay(plot);
overlay->set_plot_name(std::move(name));
plot.notify_model_dirty();
}
void set_performance_overlay_enabled(Plot_Core& plot, bool enabled) {
auto overlay = plot.performance_overlay();
if (!overlay && !enabled)
return;
if (!overlay)
overlay = attach_performance_overlay(plot);
overlay->set_enabled(enabled);
plot.notify_model_dirty();
}
} // namespace renderive
+47 -12
View File
@@ -1,7 +1,7 @@
#pragma once
#include "../base/Types.h"
#include "../plot/Plot_Core.h"
#include "../scene/Scene.h"
#include <cstdint>
#include <memory>
@@ -51,12 +51,14 @@ public:
[[nodiscard]] std::shared_ptr<const Performance_Display_Snapshot> display_snapshot() const;
private:
friend class Plot_Core;
friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(Plot_Core&);
friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(
Plot_Core&, const Performance_Overlay_Options&);
friend void set_performance_plot_name(Plot_Core&, std::string);
friend void set_performance_overlay_enabled(Plot_Core&, bool);
template <detail::Scene_Frame_Control Frame_Control>
friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>&);
template <detail::Scene_Frame_Control Frame_Control>
friend std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>&, const Performance_Overlay_Options&);
template <detail::Scene_Frame_Control Frame_Control>
friend void set_performance_plot_name(Basic_Scene2D<Frame_Control>&, std::string);
template <detail::Scene_Frame_Control Frame_Control>
friend void set_performance_overlay_enabled(Basic_Scene2D<Frame_Control>&, bool);
struct Impl;
std::unique_ptr<Impl> impl_;
@@ -64,16 +66,49 @@ private:
void set_enabled(bool enabled);
void set_plot_name(std::string name);
void set_options(Performance_Overlay_Options options);
friend void detail::record_performance_frame(Performance_Overlay&, double, const Low_Latency_Diagnostics&, std::size_t, Size);
void record_frame(double render_duration_ms,
const Low_Latency_Diagnostics& diagnostics,
std::size_t renderable_count,
Size viewport);
};
LIB_DECL std::shared_ptr<Performance_Overlay> attach_performance_overlay(Plot_Core& plot);
LIB_DECL std::shared_ptr<Performance_Overlay> attach_performance_overlay(
Plot_Core& plot, const Performance_Overlay_Options& options);
LIB_DECL void set_performance_plot_name(Plot_Core& plot, std::string name);
LIB_DECL void set_performance_overlay_enabled(Plot_Core& plot, bool enabled);
template <detail::Scene_Frame_Control Frame_Control>
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>& scene) {
if (auto existing = scene.performance_overlay())
return existing;
auto overlay = std::make_shared<Performance_Overlay>();
scene.set_performance_overlay(overlay);
return overlay;
}
template <detail::Scene_Frame_Control Frame_Control>
std::shared_ptr<Performance_Overlay> attach_performance_overlay(Basic_Scene2D<Frame_Control>& scene, const Performance_Overlay_Options& options) {
if (auto existing = scene.performance_overlay()) {
existing->set_options(options);
scene.notify_model_dirty();
return existing;
}
auto overlay = std::make_shared<Performance_Overlay>(options);
scene.set_performance_overlay(overlay);
return overlay;
}
template <detail::Scene_Frame_Control Frame_Control>
void set_performance_plot_name(Basic_Scene2D<Frame_Control>& scene, std::string name) {
auto overlay = scene.performance_overlay();
if (!overlay)
overlay = attach_performance_overlay(scene);
overlay->set_plot_name(std::move(name));
scene.notify_model_dirty();
}
template <detail::Scene_Frame_Control Frame_Control>
void set_performance_overlay_enabled(Basic_Scene2D<Frame_Control>& scene, bool enabled) {
auto overlay = scene.performance_overlay();
if (!overlay && !enabled)
return;
if (!overlay)
overlay = attach_performance_overlay(scene);
overlay->set_enabled(enabled);
scene.notify_model_dirty();
}
} // namespace renderive
+2 -2
View File
@@ -60,11 +60,11 @@ public:
using State_Observer = Observer_State<Renderable_State_Observer>;
using Base = Double_State_Strategy<Renderable, Properties, Atomic_Spin_Mutex,
State_Observer>;
Plottable_State(Plot_Core& plot, const Properties& properties)
Plottable_State(::Scene_Base& scene, const Properties& properties)
: Base(properties,
With_Observer<State_Observer>{
State_Observer(Renderable_State_Observer(this))},
plot) {}
scene) {}
template <auto Member, Property_Member_Assignable<Properties, Member> Value>
Plottable_State& set(Value&& value) {
Base::template set<Member>(std::forward<Value>(value));
@@ -37,8 +37,8 @@ struct Selection_Rectangle_Overlay_Control::Impl {
Selection_Interaction_State interaction;
Prepare_Buffer prepare_buffer;
};
Selection_Rectangle_Overlay_Control::Selection_Rectangle_Overlay_Control(Plot_Core& plot, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr<Abs_Axis> horizontal_axis, std::shared_ptr<Abs_Axis> vertical_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(horizontal_axis), std::move(vertical_axis))) {}
Selection_Rectangle_Overlay_Control::Selection_Rectangle_Overlay_Control(::Scene_Base& scene, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr<Abs_Axis> horizontal_axis, std::shared_ptr<Abs_Axis> vertical_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(horizontal_axis), std::move(vertical_axis))) {}
Selection_Rectangle_Overlay_Control::~Selection_Rectangle_Overlay_Control() = default;
std::vector<RectF> Selection_Rectangle_Overlay_Control::selected_regions() const {
return impl_->regions.snapshot();
@@ -11,7 +11,7 @@ struct Selection_Rectangle_Overlay_Properties {
namespace detail {
class LIB_DECL Selection_Rectangle_Overlay_Control : public Plottable_State<Selection_Rectangle_Overlay_Properties>, public Paint_Overlay, public Event_Handler {
public:
Selection_Rectangle_Overlay_Control(Plot_Core& plot, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr<Abs_Axis> horizontal_axis, std::shared_ptr<Abs_Axis> vertical_axis);
Selection_Rectangle_Overlay_Control(::Scene_Base& scene, const Selection_Rectangle_Overlay_Properties& properties, std::shared_ptr<Abs_Axis> horizontal_axis, std::shared_ptr<Abs_Axis> vertical_axis);
~Selection_Rectangle_Overlay_Control() override;
[[nodiscard]] std::vector<RectF> selected_regions() const;
void clear_selected_regions();
+2 -2
View File
@@ -147,8 +147,8 @@ struct Spectrum_Control::Impl {
Adaptive_Render_Partitioner partitioner;
Spectrum_Prepare_Buffer prepare_buffer;
};
Spectrum_Control::Spectrum_Control(Plot_Core& plot, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Spectrum_Control::Spectrum_Control(::Scene_Base& scene, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Spectrum_Control::~Spectrum_Control() = default;
void Spectrum_Control::update_samples(std::span<const double> values) {
if(get<&Spectrum_Properties::frequency_point_size>() <= 0)
+1 -1
View File
@@ -38,7 +38,7 @@ class LIB_DECL Spectrum_Control : public Plottable_State<Spectrum_Properties>,
public Event_Handler,
public ::Render_Frame_Completion {
public:
Spectrum_Control(Plot_Core& plot, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
Spectrum_Control(::Scene_Base& scene, const Spectrum_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
~Spectrum_Control() override;
void update_samples(std::span<const double> values);
void update_samples(std::pmr::vector<double>&& values);
+2 -2
View File
@@ -30,8 +30,8 @@ struct Sweep_Spectrum_Control::Impl {
Sweep_Spectrum_History blocks;
Prepare_Buffer prepare_buffer;
};
Sweep_Spectrum_Control::Sweep_Spectrum_Control(Plot_Core& plot, const Sweep_Spectrum_Properties& properties, std::shared_ptr<Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Sweep_Spectrum_Control::Sweep_Spectrum_Control(::Scene_Base& scene, const Sweep_Spectrum_Properties& properties, std::shared_ptr<Axis> frequency_axis, std::shared_ptr<Axis> power_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(power_axis))) {}
Sweep_Spectrum_Control::~Sweep_Spectrum_Control() = default;
void Sweep_Spectrum_Control::append_block(std::span<const double> values) {
if(get<&Sweep_Spectrum_Properties::bins_per_block>() <= 0)
+1 -1
View File
@@ -16,7 +16,7 @@ struct Sweep_Spectrum_Properties {
namespace detail {
class LIB_DECL Sweep_Spectrum_Control : public Plottable_State<Sweep_Spectrum_Properties> {
public:
Sweep_Spectrum_Control(Plot_Core& plot, const Sweep_Spectrum_Properties& properties, std::shared_ptr<Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
Sweep_Spectrum_Control(::Scene_Base& scene, const Sweep_Spectrum_Properties& properties, std::shared_ptr<Axis> frequency_axis, std::shared_ptr<Axis> power_axis);
~Sweep_Spectrum_Control() override;
void append_block(std::span<const double> values);
void append_block(std::pmr::vector<double>&& values);
+2 -2
View File
@@ -66,8 +66,8 @@ struct Waterfall_Control::Impl {
Adaptive_Render_Partitioner partitioner;
Waterfall_Prepare_Buffer prepare_buffer;
};
Waterfall_Control::Waterfall_Control(Plot_Core& plot, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis)
: Plottable_State(plot, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(time_axis))) {}
Waterfall_Control::Waterfall_Control(::Scene_Base& scene, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis)
: Plottable_State(scene, properties), impl_(std::make_unique<Impl>(*this, std::move(frequency_axis), std::move(time_axis))) {}
Waterfall_Control::~Waterfall_Control() = default;
void Waterfall_Control::append_row(int tick, std::span<const double> values) {
const std::size_t limit = static_cast<std::size_t>(
+1 -1
View File
@@ -22,7 +22,7 @@ class LIB_DECL Waterfall_Control : public Plottable_State<Waterfall_Properties>,
public Event_Handler,
public ::Render_Frame_Completion {
public:
Waterfall_Control(Plot_Core& plot, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis);
Waterfall_Control(::Scene_Base& scene, const Waterfall_Properties& properties, std::shared_ptr<Frequency_Axis> frequency_axis, std::shared_ptr<Time_Axis> time_axis);
~Waterfall_Control() override;
void append_row(int tick, std::span<const double> values);
void append_row(int tick, std::pmr::vector<double>&& values);
+21 -12
View File
@@ -1,6 +1,6 @@
#include "Renderable.h"
#include <renderive/scene/base/Scene_Base.hpp>
#include "../plot/Plot_Core.h"
#include "../render/Blend2D_Cache.h"
#include <algorithm>
@@ -24,11 +24,23 @@ void Renderable_Observer::observe_state(Renderable_Observer_Event event,
observation_.publish_count = publish_count;
}
Renderable::Renderable(Plot_Core& plot, bool cache_enabled)
: ::Renderable_Base(plot.kernel_scene(), {.cache_enabled = cache_enabled}), plot_(plot) {}
Renderable::Renderable(::Scene_Base& scene, bool cache_enabled)
: ::Renderable_Base(scene, {.cache_enabled = cache_enabled}) {}
Renderable::~Renderable() = default;
void attach_renderable_child(::Scene_Base& scene, const std::shared_ptr<Renderable>& renderable, const std::shared_ptr<Renderable>& parent) {
scene.attach_renderable(renderable);
try {
scene.set_display_parent(*renderable, parent.get());
scene.set_dependency_parent(*renderable, parent.get());
} catch (...) {
scene.detach_renderable(*renderable);
throw;
}
scene.notify_model_dirty();
}
Renderable_Cache_Mode Renderable::get_cache_mode() const noexcept {
return configuration().cache_enabled
? Renderable_Cache_Mode::Local_Pixel
@@ -36,7 +48,8 @@ Renderable_Cache_Mode Renderable::get_cache_mode() const noexcept {
}
void Renderable::set_cache_mode(Renderable_Cache_Mode mode) {
plot_.set_renderable_cache(*this, mode);
scene().set_renderable_configuration(*this, {.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel});
scene().notify_model_dirty();
}
std::string Renderable::object_name() const {
@@ -109,7 +122,7 @@ Renderable_Graph_Builder::Task Renderable::add_paint_task(
auto* cache = dynamic_cast<detail::Blend2D_Color_Cache*>(context.color_cache);
if (!cache)
return;
detail::Painter painter(*cache, viewport_size());
detail::Painter painter(*cache, {context.viewport.width, context.viewport.height});
if (painter)
function(painter, context);
});
@@ -117,21 +130,17 @@ Renderable_Graph_Builder::Task Renderable::add_paint_task(
void Renderable::changed() noexcept {
invalidate_prepare();
plot_.notify_model_dirty();
scene().notify_model_dirty();
}
void Renderable::paint_changed() noexcept {
invalidate_paint();
plot_.notify_model_dirty();
scene().notify_model_dirty();
}
void Renderable::render_graph_changed() noexcept {
rebuild_render_graph();
plot_.notify_model_dirty();
}
Size Renderable::viewport_size() const noexcept {
return plot_.viewport_size();
scene().notify_model_dirty();
}
} // namespace renderive
+5 -5
View File
@@ -8,13 +8,14 @@
#include <atomic>
#include <concepts>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <type_traits>
namespace renderive {
class Plot_Core;
class Renderable;
namespace detail {
class Painter;
class Renderable_State_Observer;
@@ -50,12 +51,13 @@ private:
Renderable_Observation observation_;
};
LIB_DECL void attach_renderable_child(::Scene_Base& scene, const std::shared_ptr<Renderable>& renderable, const std::shared_ptr<Renderable>& parent);
class LIB_DECL Renderable : public ::Renderable_Base {
public:
explicit Renderable(Plot_Core& plot, bool cache_enabled = true);
explicit Renderable(::Scene_Base& scene, bool cache_enabled = true);
~Renderable() override;
[[nodiscard]] Plot_Core& plot() const noexcept { return plot_; }
[[nodiscard]] Renderable_Cache_Mode get_cache_mode() const noexcept;
void set_cache_mode(Renderable_Cache_Mode mode);
@@ -88,14 +90,12 @@ protected:
void changed() noexcept;
void paint_changed() noexcept;
void render_graph_changed() noexcept;
[[nodiscard]] Size viewport_size() const noexcept;
private:
void observe_state(Renderable_Observer_Event event, std::uint64_t time_ns,
std::uint64_t cache_update_count,
std::uint64_t publish_count) noexcept;
friend class detail::Renderable_State_Observer;
Plot_Core& plot_;
mutable std::mutex metadata_mutex_;
std::string object_name_;
std::atomic<bool> visible_{true};
+8 -5
View File
@@ -1,8 +1,8 @@
#pragma once
#include "Renderable.h"
#include "../plot/Plot_Core.h"
#include <renderive/base/property/Concepts.hpp>
#include <renderive/base/property/Validators.hpp>
#include <renderive/scene/base/Scene_Base.hpp>
#include <functional>
#include <memory>
#include <tuple>
@@ -33,15 +33,16 @@ public:
return properties;
}
template <class... Args>
requires std::derived_from<Product, Renderable> && std::constructible_from<Product, Plot_Core&, const Properties&, Args&&...>
requires std::derived_from<Product, Renderable> && std::constructible_from<Product, ::Scene_Base&, const Properties&, Args&&...>
std::shared_ptr<Product> build(const std::shared_ptr<Renderable>& parent, Args&&... args) const {
if(!parent || !(valid_argument(args) && ...))
if (!parent || !(valid_argument(args) && ...))
return {};
validator(properties);
if constexpr (requires(Product& product) { attach_renderable_dependencies(product, args...); } &&
(std::copy_constructible<std::decay_t<Args>> && ...)) {
auto dependency_args = std::make_tuple(args...);
auto result = parent->plot().make_renderable<Product>(parent, properties, std::forward<Args>(args)...);
auto result = parent->scene().template make_renderable<Product>(properties, std::forward<Args>(args)...);
attach_renderable_child(parent->scene(), result, parent);
try {
std::apply([&result](const auto&... values) {
attach_renderable_dependencies(*result, values...);
@@ -52,7 +53,9 @@ public:
}
return result;
}
return parent->plot().make_renderable<Product>(parent, properties, std::forward<Args>(args)...);
auto result = parent->scene().template make_renderable<Product>(properties, std::forward<Args>(args)...);
attach_renderable_child(parent->scene(), result, parent);
return result;
}
private:
template <class T>
+20
View File
@@ -0,0 +1,20 @@
#include "Scene.h"
#include "../plottable/Performance_Overlay.h"
#include "../renderable/Renderable.h"
namespace renderive::detail {
namespace {
class Renderable_Group final : public Renderable {
public:
using Renderable::Renderable;
private:
void prepare_frame(const Render_State_View&, const Scene_Render_Context&) override {}
void paint(Painter&, const Scene_Render_Context&) override {}
};
}
std::shared_ptr<Renderable> make_renderable_group(::Scene_Base& scene, bool cache_enabled) {
return scene.make_renderable<Renderable_Group>(cache_enabled);
}
void record_performance_frame(Performance_Overlay& overlay, double duration_ms, const Low_Latency_Diagnostics& diagnostics, std::size_t renderable_count, Size viewport) {
overlay.record_frame(duration_ms, diagnostics, renderable_count, viewport);
}
}
+509
View File
@@ -0,0 +1,509 @@
#pragma once
#include "../base/Types.h"
#include "../event/Event.h"
#include "../render/Blend2D_Cache.h"
#include "../renderable/Renderable.h"
#include <renderive/base/observer/Observer.hpp>
#include <renderive/frame_control/Frame_Consumer_Feedback.hpp>
#include <renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp>
#include <renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.hpp>
#include <renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.hpp>
#include <renderive/scene/Scene2D_Context.hpp>
#include <algorithm>
#include <atomic>
#include <chrono>
#include <cmath>
#include <concepts>
#include <functional>
#include <limits>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
namespace renderive {
class Performance_Overlay;
class Renderable;
class Event_Handler;
enum class Frame_Control_Mode : std::uint8_t {
Manual,
Low_Latency,
Playback
};
struct Refresh_Control_Snapshot {
double frequency_hz{};
std::uint64_t frame_count{};
std::uint64_t next_refresh_interval_ns{};
};
struct Low_Latency_Diagnostics {
bool active{};
Refresh_Control_Snapshot refresh;
};
struct Frame_Observer_Snapshot {
Frame_Control_Mode mode = Frame_Control_Mode::Low_Latency;
std::string last_event;
std::string limit_state;
double frequency_hz{};
std::uint64_t observation_count{};
std::uint64_t produced_frame_count{};
std::uint64_t consumed_frame_count{};
std::uint64_t dropped_frame_count{};
std::uint64_t failed_operation_count{};
std::uint64_t pending_frame_count{};
std::uint64_t latest_sequence{};
std::uint64_t paint_duration_ns{};
std::uint64_t render_duration_ns{};
std::uint64_t target_interval_ns{};
bool frequency_limit_enabled{};
bool consumer_feedback_enabled{};
std::uint64_t bottleneck_duration_ns{};
std::uint64_t consumer_sample_interval_ns{};
std::uint64_t consumer_smoothed_interval_ns{};
std::uint64_t consumer_variation_ns{};
std::uint64_t consumer_safety_interval_ns{};
std::uint64_t consumer_interval_ns{};
std::uint64_t next_refresh_interval_ns{};
std::uint64_t paint_lease_wait_ns{};
std::uint64_t paint_state_wait_ns{};
std::uint64_t publish_state_wait_ns{};
std::uint64_t ready_wait_ns{};
std::uint64_t frame_age_at_render_ns{};
std::uint64_t render_lease_wait_ns{};
std::uint64_t render_state_wait_ns{};
std::uint64_t render_finish_state_wait_ns{};
std::uint64_t queue_wait_ns{};
std::uint64_t end_to_end_ns{};
};
class Presentation_Sink {
public:
virtual ~Presentation_Sink() = default;
virtual void request_present(Rect dirty_rect) = 0;
};
struct Scene_State : Scene2D_State {
Color background = Color::black();
};
namespace detail {
struct Frame_Observer_Data {
mutable std::mutex mutex;
std::uint32_t last_event{};
std::uint64_t observation_count{};
std::uint64_t produced_frame_count{};
std::uint64_t consumed_frame_count{};
std::uint64_t dropped_frame_count{};
std::uint64_t failed_operation_count{};
std::uint64_t pending_frame_count{};
std::uint64_t latest_sequence{};
std::uint64_t paint_duration_ns{};
std::uint64_t render_duration_ns{};
std::uint64_t paint_lease_wait_ns{};
std::uint64_t paint_state_wait_ns{};
std::uint64_t publish_state_wait_ns{};
std::uint64_t ready_wait_ns{};
std::uint64_t frame_age_at_render_ns{};
std::uint64_t render_lease_wait_ns{};
std::uint64_t render_state_wait_ns{};
std::uint64_t render_finish_state_wait_ns{};
std::uint64_t queue_wait_ns{};
};
struct Frame_Observer {
static constexpr bool enabled = true;
std::shared_ptr<Frame_Observer_Data> data;
template <class Observation>
void observe(const Observation& observation) noexcept {
std::lock_guard lock(data->mutex);
data->last_event = static_cast<std::uint32_t>(observation.event);
++data->observation_count;
if (observation.statistics.sequence != 0)
data->latest_sequence = observation.statistics.sequence;
if constexpr (requires { observation.statistics.timing.paint_duration_ns; }) {
if (observation.statistics.sequence != 0) {
const auto& timing = observation.statistics.timing;
data->paint_lease_wait_ns = timing.paint_lease_wait_ns;
data->paint_state_wait_ns = timing.paint_state_wait_ns;
data->publish_state_wait_ns = timing.publish_state_wait_ns;
data->ready_wait_ns = timing.ready_wait_ns;
data->frame_age_at_render_ns = timing.frame_age_at_render_ns;
data->render_lease_wait_ns = timing.render_lease_wait_ns;
data->render_state_wait_ns = timing.render_state_wait_ns;
data->render_finish_state_wait_ns = timing.render_finish_state_wait_ns;
}
} else if (observation.statistics.sequence != 0) {
data->paint_duration_ns = observation.statistics.paint_duration_ns;
data->render_duration_ns = observation.statistics.render_duration_ns;
}
if constexpr (requires { observation.statistics.queue_wait_ns; }) {
if (observation.statistics.sequence != 0)
data->queue_wait_ns = observation.statistics.queue_wait_ns;
}
if constexpr (requires { observation.state.prepared_frame_count; }) {
data->produced_frame_count = observation.state.prepared_frame_count;
data->consumed_frame_count = observation.state.render_count;
data->dropped_frame_count = observation.state.replaced_prepared_frame_count + observation.state.discarded_prepared_frame_count;
data->failed_operation_count = observation.state.failed_refresh_count;
data->pending_frame_count = observation.state.pending_frame ? 1U : 0U;
} else if constexpr (requires { observation.state.limit_state; }) {
data->produced_frame_count = observation.statistics.counters.published_frame_count;
data->consumed_frame_count = observation.state.completed_lifecycle_count;
data->dropped_frame_count = observation.statistics.counters.abandoned_frame_count + observation.statistics.counters.manually_discarded_frame_count;
data->failed_operation_count = observation.statistics.counters.swap_failure_count;
const auto retired = observation.state.completed_lifecycle_count + observation.statistics.counters.abandoned_frame_count + observation.statistics.counters.manually_discarded_frame_count;
data->pending_frame_count = observation.statistics.counters.published_frame_count > retired ? 1U : 0U;
} else {
data->produced_frame_count = observation.state.enqueued_frame_count;
data->consumed_frame_count = observation.state.rendered_frame_count;
data->failed_operation_count = observation.state.empty_acquire_count;
data->pending_frame_count = observation.state.pending_frame_count;
}
}
};
using Frame_Observer_State = ::Observer_State<Frame_Observer>;
using Manual_Frame_Control = ::Manual_Refresh_Strategy<::Scene2D_Frame_Data, std::mutex, Frame_Observer_State>;
using Low_Latency_Frame_Control = ::Low_Latency_Strategy<::Scene2D_Frame_Data, std::mutex, Frame_Observer_State>;
using Playback_Frame_Control = ::Flow_Refresh_Strategy<::Scene2D_Frame_Data, std::mutex, Frame_Observer_State>;
template <class Frame_Control>
concept Scene_Frame_Control = std::same_as<Frame_Control, Manual_Frame_Control> || std::same_as<Frame_Control, Low_Latency_Frame_Control> || std::same_as<Frame_Control, Playback_Frame_Control>;
template <Scene_Frame_Control Frame_Control>
constexpr Frame_Control_Mode frame_control_mode() noexcept {
if constexpr (std::same_as<Frame_Control, Manual_Frame_Control>)
return Frame_Control_Mode::Manual;
if constexpr (std::same_as<Frame_Control, Low_Latency_Frame_Control>)
return Frame_Control_Mode::Low_Latency;
return Frame_Control_Mode::Playback;
}
LIB_DECL std::shared_ptr<Renderable> make_renderable_group(::Scene_Base& scene, bool cache_enabled);
LIB_DECL void record_performance_frame(Performance_Overlay& overlay, double duration_ms, const Low_Latency_Diagnostics& diagnostics, std::size_t renderable_count, Size viewport);
inline std::string observer_event_name(Frame_Control_Mode mode, std::uint32_t event) {
if (mode == Frame_Control_Mode::Manual) {
static constexpr const char* names[]{"prepared", "prepared_replaced", "refresh_succeeded", "refresh_failed", "rendered", "real_time_data_updated", "manually_discarded"};
return event < std::size(names) ? names[event] : "none";
}
if (mode == Frame_Control_Mode::Playback) {
static constexpr const char* names[]{"enqueued", "dequeued", "queue_empty", "rendered", "real_time_data_updated"};
return event < std::size(names) ? names[event] : "none";
}
static constexpr const char* names[]{"published", "abandoned", "manually_discarded", "swap_failed", "rendered", "lifecycle_completed", "real_time_data_updated"};
return event < std::size(names) ? names[event] : "none";
}
inline std::string limit_state_name(std::uint32_t state) {
static constexpr const char* names[]{"frequency_limited", "paint_limited", "render_limited", "consumer_limited", "unlimited"};
return state < std::size(names) ? names[state] : "unknown";
}
inline Rect full_rect(Size size) {
return {0, 0, std::max(0, size.width), std::max(0, size.height)};
}
}
template <detail::Scene_Frame_Control Frame_Control>
class Basic_Scene2D final : public ::Scene2D_Context<Frame_Control, detail::Blend2D_Color_Cache, Scene_State> {
using Kernel_Scene = ::Scene2D_Context<Frame_Control, detail::Blend2D_Color_Cache, Scene_State>;
using Scene_State_Strategy = typename Kernel_Scene::Scene_State_Strategy;
static constexpr Frame_Control_Mode Mode = detail::frame_control_mode<Frame_Control>();
public:
Basic_Scene2D() : Basic_Scene2D(std::make_shared<detail::Frame_Observer_Data>()) {}
~Basic_Scene2D() override = default;
Basic_Scene2D(const Basic_Scene2D&) = delete;
Basic_Scene2D& operator=(const Basic_Scene2D&) = delete;
void init() {
std::lock_guard lock(initialization_mutex_);
if (root_renderable())
return;
auto root = detail::make_renderable_group(*this, true);
root->set_object_name("root");
this->attach_renderable(root);
this->notify_model_dirty();
}
[[nodiscard]] std::shared_ptr<Renderable> root_renderable() const {
const auto topology = this->topology_snapshot();
for (const auto& relationship : topology.display) {
if (relationship.parent)
continue;
auto base = std::const_pointer_cast<::Renderable_Base>(relationship.child);
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base))
return renderable;
}
return {};
}
[[nodiscard]] std::shared_ptr<Renderable> create_renderable_node(const std::shared_ptr<Renderable>& parent, std::string object_name = {}) {
auto renderable = detail::make_renderable_group(*this, true);
renderable->set_object_name(std::move(object_name));
attach_renderable_node(renderable, parent);
return renderable;
}
template <class T, class... Args>
requires std::derived_from<T, Renderable> && std::constructible_from<T, ::Scene_Base&, Args&&...>
std::shared_ptr<T> make_renderable(const std::shared_ptr<Renderable>& parent, Args&&... args) {
auto renderable = Kernel_Scene::template make_renderable<T>(std::forward<Args>(args)...);
attach_renderable_node(renderable, parent);
return renderable;
}
void remove_renderable(const std::shared_ptr<Renderable>& renderable) {
if (!renderable || renderable == root_renderable())
return;
this->detach_renderable(*renderable);
this->notify_model_dirty();
}
void set_background_color(Color color) {
if (background_color() == color)
return;
static_cast<Scene_State_Strategy&>(*this).template set<&Scene_State::background>(color);
this->notify_model_dirty();
}
[[nodiscard]] Color background_color() const noexcept {
return static_cast<const Scene_State_Strategy&>(*this).template get<&Scene_State::background>();
}
void set_viewport_size(Size size) {
size.width = std::max(0, size.width);
size.height = std::max(0, size.height);
const Frame_Viewport viewport{size.width, size.height};
if (static_cast<const Scene_State_Strategy&>(*this).template get<&Scene_State::viewport>() == viewport)
return;
static_cast<Scene_State_Strategy&>(*this).template set<&Scene_State::viewport>(viewport);
const auto topology = this->topology_snapshot();
for (const auto& renderable : topology.renderables)
const_cast<::Renderable_Base&>(*renderable).invalidate_prepare();
this->notify_model_dirty();
}
[[nodiscard]] Size viewport_size() const noexcept {
const auto viewport = static_cast<const Scene_State_Strategy&>(*this).template get<&Scene_State::viewport>();
return {viewport.width, viewport.height};
}
void dispatch_event(const Event& event) {
const auto paint_order = this->paint_order_snapshot();
for (auto iterator = paint_order.rbegin(); iterator != paint_order.rend(); ++iterator) {
auto base = std::const_pointer_cast<::Renderable_Base>(*iterator);
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base)) {
if (renderable->is_visible()) {
if (auto handler = std::dynamic_pointer_cast<Event_Handler>(base))
handler->handle_event(event);
}
if (event.is_accepted())
break;
}
}
}
[[nodiscard]] bool prepare_frame() {
auto paint_frame = this->frame_control.acquire_painter();
if (!paint_frame)
return false;
this->publish_frame_state();
return true;
}
[[nodiscard]] bool refresh_manual_frame() {
if constexpr (Mode == Frame_Control_Mode::Manual)
return this->frame_control.refresh();
return false;
}
[[nodiscard]] bool render_prepared_frame() {
auto render_frame = this->frame_control.acquire_renderer();
if (!render_frame)
return false;
this->render(*render_frame);
this->wait_for_render();
return true;
}
[[nodiscard]] bool discard_pending_frame() {
if constexpr (Mode == Frame_Control_Mode::Playback)
return false;
else
return this->frame_control.discard_pending_frame();
}
[[nodiscard]] bool render_frame(bool force = false) {
if (!view_active() && !force)
return false;
if (viewport_size().empty())
return false;
if (!this->consume_model_dirty() && !force)
return false;
const auto started = std::chrono::steady_clock::now();
Size viewport;
try {
if (!prepare_frame())
return false;
const auto frame_viewport = this->Scene_State_Strategy::render_state_value().viewport;
viewport = {frame_viewport.width, frame_viewport.height};
if constexpr (Mode == Frame_Control_Mode::Manual) {
if (!refresh_manual_frame())
return false;
}
if (!render_prepared_frame())
return false;
} catch (...) {
this->notify_model_dirty();
throw;
}
const auto finished = std::chrono::steady_clock::now();
const double duration_ms = std::chrono::duration<double, std::milli>(finished - started).count();
std::shared_ptr<Presentation_Sink> sink;
std::shared_ptr<Performance_Overlay> overlay;
{
std::lock_guard lock(control_mutex_);
sink = presentation_sink_.lock();
overlay = performance_;
}
if (overlay)
detail::record_performance_frame(*overlay, duration_ms, diagnostics(), this->renderable_count(), viewport);
if (sink)
sink->request_present(detail::full_rect(viewport));
return true;
}
void with_frame(const std::function<void(Image_View)>& consumer) {
if (!consumer)
return;
this->with_final_color_cache([&consumer](const detail::Blend2D_Color_Cache& cache) {
consumer(cache.view());
});
}
void activate_view() noexcept {
active_.store(true, std::memory_order_release);
this->notify_model_dirty();
}
void deactivate_view() noexcept {
active_.store(false, std::memory_order_release);
}
[[nodiscard]] bool view_active() const noexcept {
return active_.load(std::memory_order_acquire);
}
void set_max_render_fps(double fps) {
if constexpr (Mode != Frame_Control_Mode::Low_Latency) {
throw std::logic_error("maximum render FPS is only available in low-latency mode");
} else {
if (!std::isfinite(fps) || fps <= 0.0)
throw std::invalid_argument("maximum render FPS must be finite and positive");
this->frame_control.set_frequency_hz(fps);
this->notify_model_dirty();
}
}
void clear_max_render_fps() {
if constexpr (Mode != Frame_Control_Mode::Low_Latency) {
throw std::logic_error("maximum render FPS is only available in low-latency mode");
} else {
this->frame_control.clear_frequency_limit();
this->notify_model_dirty();
}
}
void set_consumer_feedback(Frame_Consumer_Feedback feedback) {
if constexpr (Mode != Frame_Control_Mode::Low_Latency)
throw std::logic_error("consumer feedback is only available in low-latency mode");
else
this->frame_control.set_consumer_feedback(feedback);
}
void clear_consumer_feedback() {
if constexpr (Mode != Frame_Control_Mode::Low_Latency)
throw std::logic_error("consumer feedback is only available in low-latency mode");
else
this->frame_control.clear_consumer_feedback();
}
[[nodiscard]] double max_render_fps() const noexcept {
if constexpr (Mode == Frame_Control_Mode::Low_Latency)
return this->frame_control.state().frequency_hz;
return std::numeric_limits<double>::quiet_NaN();
}
[[nodiscard]] Low_Latency_Diagnostics diagnostics() const {
if constexpr (Mode == Frame_Control_Mode::Low_Latency) {
const auto state = this->frame_control.state();
return {view_active(), {state.frequency_hz, state.completed_lifecycle_count, state.next_refresh_interval_ns}};
}
const auto observed = frame_observer_snapshot();
return {view_active(), {std::numeric_limits<double>::quiet_NaN(), observed.consumed_frame_count, 0}};
}
[[nodiscard]] Refresh_Control_Snapshot refresh_feedback_snapshot() const {
return diagnostics().refresh;
}
[[nodiscard]] static constexpr Frame_Control_Mode frame_control_mode() noexcept {
return Mode;
}
[[nodiscard]] Frame_Observer_Snapshot frame_observer_snapshot() const {
Frame_Observer_Snapshot snapshot;
{
std::lock_guard lock(observer_->mutex);
snapshot.mode = Mode;
snapshot.last_event = observer_->observation_count == 0 ? std::string("none") : detail::observer_event_name(Mode, observer_->last_event);
snapshot.limit_state = "not_applicable";
snapshot.observation_count = observer_->observation_count;
snapshot.produced_frame_count = observer_->produced_frame_count;
snapshot.consumed_frame_count = observer_->consumed_frame_count;
snapshot.dropped_frame_count = observer_->dropped_frame_count;
snapshot.failed_operation_count = observer_->failed_operation_count;
snapshot.pending_frame_count = observer_->pending_frame_count;
snapshot.latest_sequence = observer_->latest_sequence;
snapshot.paint_duration_ns = observer_->paint_duration_ns;
snapshot.render_duration_ns = observer_->render_duration_ns;
snapshot.paint_lease_wait_ns = observer_->paint_lease_wait_ns;
snapshot.paint_state_wait_ns = observer_->paint_state_wait_ns;
snapshot.publish_state_wait_ns = observer_->publish_state_wait_ns;
snapshot.ready_wait_ns = observer_->ready_wait_ns;
snapshot.frame_age_at_render_ns = observer_->frame_age_at_render_ns;
snapshot.render_lease_wait_ns = observer_->render_lease_wait_ns;
snapshot.render_state_wait_ns = observer_->render_state_wait_ns;
snapshot.render_finish_state_wait_ns = observer_->render_finish_state_wait_ns;
snapshot.queue_wait_ns = observer_->queue_wait_ns;
}
if constexpr (Mode == Frame_Control_Mode::Low_Latency) {
const auto state = this->frame_control.state();
snapshot.limit_state = detail::limit_state_name(static_cast<std::uint32_t>(state.limit_state));
snapshot.frequency_hz = state.frequency_hz;
snapshot.paint_duration_ns = state.paint_duration_ns;
snapshot.render_duration_ns = state.render_duration_ns;
snapshot.target_interval_ns = state.target_interval_ns;
snapshot.frequency_limit_enabled = state.frequency_limit_enabled;
snapshot.consumer_feedback_enabled = state.consumer_feedback_enabled;
snapshot.bottleneck_duration_ns = state.bottleneck_duration_ns;
snapshot.consumer_sample_interval_ns = state.consumer_sample_interval_ns;
snapshot.consumer_smoothed_interval_ns = state.consumer_smoothed_interval_ns;
snapshot.consumer_variation_ns = state.consumer_variation_ns;
snapshot.consumer_safety_interval_ns = state.consumer_safety_interval_ns;
snapshot.consumer_interval_ns = state.consumer_interval_ns;
snapshot.next_refresh_interval_ns = state.next_refresh_interval_ns;
snapshot.end_to_end_ns = state.end_to_end_ns;
}
return snapshot;
}
void set_presentation_sink(std::weak_ptr<Presentation_Sink> sink) {
std::lock_guard lock(control_mutex_);
presentation_sink_ = std::move(sink);
}
[[nodiscard]] std::shared_ptr<Performance_Overlay> performance_overlay() const {
std::lock_guard lock(control_mutex_);
return performance_;
}
void set_performance_overlay(std::shared_ptr<Performance_Overlay> overlay) {
{
std::lock_guard lock(control_mutex_);
performance_ = std::move(overlay);
}
this->notify_model_dirty();
}
private:
explicit Basic_Scene2D(std::shared_ptr<detail::Frame_Observer_Data> observer)
: Kernel_Scene(*memory_resource(Memory_Domain::Plot_Frame), detail::Frame_Observer_State(detail::Frame_Observer{observer})), observer_(std::move(observer)) {}
void attach_renderable_node(const std::shared_ptr<Renderable>& renderable, const std::shared_ptr<Renderable>& parent) {
if (!renderable)
throw std::invalid_argument("renderable is null");
Renderable* parent_pointer = parent.get();
if (!parent_pointer) {
auto root = root_renderable();
if (!root)
throw std::logic_error("Scene2D::init must be called before adding renderables");
parent_pointer = root.get();
}
this->attach_renderable(renderable);
try {
this->set_display_parent(*renderable, parent_pointer);
this->set_dependency_parent(*renderable, parent_pointer);
} catch (...) {
this->detach_renderable(*renderable);
throw;
}
this->notify_model_dirty();
}
std::shared_ptr<detail::Frame_Observer_Data> observer_;
mutable std::mutex control_mutex_;
std::mutex initialization_mutex_;
std::weak_ptr<Presentation_Sink> presentation_sink_;
std::shared_ptr<Performance_Overlay> performance_;
std::atomic_bool active_{};
};
using Manual_Scene2D = Basic_Scene2D<detail::Manual_Frame_Control>;
using Scene2D = Basic_Scene2D<detail::Low_Latency_Frame_Control>;
using Playback_Scene2D = Basic_Scene2D<detail::Playback_Frame_Control>;
}
@@ -7,7 +7,7 @@
namespace renderive {
namespace {
TEST(Renderive_Core2_Frame_Pipeline, ManualLifecycleSeparatesPrepareRefreshAndRender) {
Plot_Core plot(Frame_Control_Mode::Manual);
Manual_Scene2D plot;
plot.init();
plot.set_viewport_size({96, 54});
ASSERT_TRUE(plot.prepare_frame());
@@ -36,7 +36,7 @@ TEST(Renderive_Core2_Frame_Pipeline, ManualLifecycleSeparatesPrepareRefreshAndRe
EXPECT_EQ(plot.diagnostics().refresh.frame_count, 1u);
}
TEST(Renderive_Core2_Frame_Pipeline, HighFrequencyLowLatencyLifecycleExposesKernelTiming) {
Plot_Core plot(Frame_Control_Mode::Low_Latency);
Scene2D plot;
plot.init();
plot.set_viewport_size({96, 54});
plot.set_max_render_fps(1'000'000'000.0);
@@ -69,7 +69,7 @@ TEST(Renderive_Core2_Frame_Pipeline, HighFrequencyLowLatencyLifecycleExposesKern
EXPECT_EQ(refresh.next_refresh_interval_ns, snapshot.next_refresh_interval_ns);
}
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyConsumerFeedbackFlowsThroughCore2IntoKernelScheduling) {
Plot_Core plot(Frame_Control_Mode::Low_Latency);
Scene2D plot;
plot.init();
plot.set_viewport_size({96, 54});
plot.set_max_render_fps(100.0);
@@ -90,7 +90,7 @@ TEST(Renderive_Core2_Frame_Pipeline, LowLatencyConsumerFeedbackFlowsThroughCore2
EXPECT_EQ(plot.refresh_feedback_snapshot().next_refresh_interval_ns, 40'000'000u);
}
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyLimitsCanBeClearedIndependently) {
Plot_Core plot(Frame_Control_Mode::Low_Latency);
Scene2D plot;
plot.init();
plot.set_viewport_size({96, 54});
plot.set_max_render_fps(100.0);
@@ -108,7 +108,7 @@ TEST(Renderive_Core2_Frame_Pipeline, LowLatencyLimitsCanBeClearedIndependently)
EXPECT_TRUE(snapshot.limit_state == "paint_limited" || snapshot.limit_state == "render_limited");
}
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyReplacementRetiresOldFrameWithoutPhantomPendingFrame) {
Plot_Core plot(Frame_Control_Mode::Low_Latency);
Scene2D plot;
plot.init();
plot.set_viewport_size({64, 40});
ASSERT_TRUE(plot.prepare_frame());
@@ -129,7 +129,7 @@ TEST(Renderive_Core2_Frame_Pipeline, LowLatencyReplacementRetiresOldFrameWithout
EXPECT_EQ(snapshot.latest_sequence, 2u);
}
TEST(Renderive_Core2_Frame_Pipeline, LowLatencyDiscardAndEmptyRenderRemainObservable) {
Plot_Core plot(Frame_Control_Mode::Low_Latency);
Scene2D plot;
plot.init();
plot.set_viewport_size({64, 40});
ASSERT_TRUE(plot.prepare_frame());
@@ -149,7 +149,7 @@ TEST(Renderive_Core2_Frame_Pipeline, LowLatencyDiscardAndEmptyRenderRemainObserv
EXPECT_EQ(snapshot.latest_sequence, 1u);
}
TEST(Renderive_Core2_Frame_Pipeline, ManualDiscardAndFailedRefreshUpdateObserverState) {
Plot_Core plot(Frame_Control_Mode::Manual);
Manual_Scene2D plot;
plot.init();
plot.set_viewport_size({64, 40});
ASSERT_TRUE(plot.prepare_frame());
@@ -170,7 +170,7 @@ TEST(Renderive_Core2_Frame_Pipeline, ManualDiscardAndFailedRefreshUpdateObserver
EXPECT_EQ(snapshot.paint_duration_ns, last_paint_duration_ns);
}
TEST(Renderive_Core2_Frame_Pipeline, PlaybackPreservesQueueDepthAndEmptyAcquireFailure) {
Plot_Core plot(Frame_Control_Mode::Playback);
Playback_Scene2D plot;
plot.init();
plot.set_viewport_size({64, 40});
ASSERT_TRUE(plot.prepare_frame());
@@ -202,7 +202,7 @@ TEST(Renderive_Core2_Frame_Pipeline, PlaybackPreservesQueueDepthAndEmptyAcquireF
EXPECT_EQ(snapshot.queue_wait_ns, last_queue_wait_ns);
}
TEST(Renderive_Core2_Frame_Pipeline, PlaybackFullCyclePreservesPreviouslyQueuedFrames) {
Plot_Core plot(Frame_Control_Mode::Playback);
Playback_Scene2D plot;
plot.init();
plot.set_viewport_size({96, 54});
ASSERT_TRUE(plot.prepare_frame());
@@ -224,34 +224,32 @@ TEST(Renderive_Core2_Frame_Pipeline, PlaybackFullCyclePreservesPreviouslyQueuedF
EXPECT_EQ(plot.frame_observer_snapshot().pending_frame_count, 3u);
}
TEST(Renderive_Core2_Frame_Pipeline, EveryModeHonorsActiveDirtyAndForceRenderGates) {
constexpr std::array modes{
Frame_Control_Mode::Manual,
Frame_Control_Mode::Low_Latency,
Frame_Control_Mode::Playback
};
for (const auto mode : modes) {
Plot_Core plot(mode);
plot.init();
plot.set_viewport_size({80, 45});
EXPECT_FALSE(plot.view_active());
EXPECT_FALSE(plot.render_frame(false));
EXPECT_EQ(plot.frame_observer_snapshot().observation_count, 0u);
plot.activate_view();
ASSERT_TRUE(plot.render_frame(false));
const auto completed = plot.frame_observer_snapshot();
const auto verify = []<class Scene_Type>(Frame_Control_Mode mode) {
Scene_Type scene;
scene.init();
scene.set_viewport_size({80, 45});
EXPECT_FALSE(scene.view_active());
EXPECT_FALSE(scene.render_frame(false));
EXPECT_EQ(scene.frame_observer_snapshot().observation_count, 0u);
scene.activate_view();
ASSERT_TRUE(scene.render_frame(false));
const auto completed = scene.frame_observer_snapshot();
EXPECT_EQ(completed.mode, mode);
EXPECT_EQ(completed.consumed_frame_count, 1u);
EXPECT_FALSE(plot.render_frame(false));
EXPECT_EQ(plot.frame_observer_snapshot().consumed_frame_count, 1u);
plot.deactivate_view();
plot.set_background_color(Color{8, 16, 24, 255});
EXPECT_FALSE(plot.render_frame(false));
ASSERT_TRUE(plot.render_frame(true));
EXPECT_EQ(plot.frame_observer_snapshot().consumed_frame_count, 2u);
}
EXPECT_FALSE(scene.render_frame(false));
EXPECT_EQ(scene.frame_observer_snapshot().consumed_frame_count, 1u);
scene.deactivate_view();
scene.set_background_color(Color{8, 16, 24, 255});
EXPECT_FALSE(scene.render_frame(false));
ASSERT_TRUE(scene.render_frame(true));
EXPECT_EQ(scene.frame_observer_snapshot().consumed_frame_count, 2u);
};
verify.template operator()<Manual_Scene2D>(Frame_Control_Mode::Manual);
verify.template operator()<Scene2D>(Frame_Control_Mode::Low_Latency);
verify.template operator()<Playback_Scene2D>(Frame_Control_Mode::Playback);
}
TEST(Renderive_Core2_Frame_Pipeline, FrequencyConfigurationRejectsInvalidValuesAndWrongModes) {
Plot_Core low_latency(Frame_Control_Mode::Low_Latency);
Scene2D low_latency;
EXPECT_THROW(low_latency.set_max_render_fps(0.0), std::invalid_argument);
EXPECT_THROW(low_latency.set_max_render_fps(-1.0), std::invalid_argument);
EXPECT_THROW(low_latency.set_max_render_fps(std::numeric_limits<double>::quiet_NaN()),
@@ -260,8 +258,8 @@ TEST(Renderive_Core2_Frame_Pipeline, FrequencyConfigurationRejectsInvalidValuesA
std::invalid_argument);
low_latency.set_max_render_fps(1'000.0);
EXPECT_DOUBLE_EQ(low_latency.max_render_fps(), 1'000.0);
Plot_Core manual(Frame_Control_Mode::Manual);
Plot_Core playback(Frame_Control_Mode::Playback);
Manual_Scene2D manual;
Playback_Scene2D playback;
EXPECT_THROW(manual.set_max_render_fps(60.0), std::logic_error);
EXPECT_THROW(playback.set_max_render_fps(60.0), std::logic_error);
EXPECT_FALSE(playback.refresh_manual_frame());
+19 -19
View File
@@ -33,7 +33,7 @@ static_assert(!Legacy_Axis_Property_Api<Time_Axis>);
static_assert(!Legacy_Time_Axis_Property_Api<Time_Axis>);
TEST(Renderive_Core2, RootIdentityAndRefreshDiagnosticsComeFromKernelState) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
ASSERT_TRUE(root);
@@ -115,7 +115,7 @@ TEST(Renderive_Core2, BicubicHeatmapUsesRealCubicResampling) {
EXPECT_NE(bilinear, bicubic);
}
TEST(Renderive_Core2, KernelSceneRendersBusinessObjectsIntoBlend2DFrame) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({320, 180});
plot.activate_view();
@@ -184,7 +184,7 @@ TEST(Renderive_Core2, KernelSceneRendersBusinessObjectsIntoBlend2DFrame) {
EXPECT_FALSE(plot.render_frame());
}
TEST(Renderive_Core2, TimeAxisUsesOneFontStateAndFormatsConfiguredLabels) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
auto axis = Time_Axis::Builder(root, Orientation::Horizontal)
@@ -199,7 +199,7 @@ TEST(Renderive_Core2, TimeAxisUsesOneFontStateAndFormatsConfiguredLabels) {
EXPECT_EQ(axis->get<&Time_Axis_Properties::tick_label_spacing_px>(), 17);
}
TEST(Renderive_Core2, PerformanceOverlayConsumesKernelFrameDiagnostics) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({160, 90});
plot.activate_view();
@@ -220,7 +220,7 @@ TEST(Renderive_Core2, PerformanceOverlayConsumesKernelFrameDiagnostics) {
EXPECT_DOUBLE_EQ(plot.max_render_fps(), 120.0);
}
TEST(Renderive_Core2, AllFrameControlModesUseKernelStrategiesAndExposeObservers) {
Plot_Core manual(Frame_Control_Mode::Manual);
Manual_Scene2D manual;
manual.init();
manual.set_viewport_size({64, 40});
EXPECT_EQ(manual.frame_control_mode(), Frame_Control_Mode::Manual);
@@ -233,7 +233,7 @@ TEST(Renderive_Core2, AllFrameControlModesUseKernelStrategiesAndExposeObservers)
manual_observer = manual.frame_observer_snapshot();
EXPECT_EQ(manual_observer.last_event, "rendered");
EXPECT_EQ(manual_observer.consumed_frame_count, 1u);
Plot_Core low_latency(Frame_Control_Mode::Low_Latency);
Scene2D low_latency;
low_latency.init();
low_latency.set_viewport_size({64, 40});
low_latency.activate_view();
@@ -242,7 +242,7 @@ TEST(Renderive_Core2, AllFrameControlModesUseKernelStrategiesAndExposeObservers)
EXPECT_EQ(low_latency.frame_control_mode(), Frame_Control_Mode::Low_Latency);
EXPECT_EQ(low_observer.last_event, "lifecycle_completed");
EXPECT_EQ(low_observer.consumed_frame_count, 1u);
Plot_Core playback(Frame_Control_Mode::Playback);
Playback_Scene2D playback;
playback.init();
playback.set_viewport_size({64, 40});
ASSERT_TRUE(playback.prepare_frame());
@@ -259,7 +259,7 @@ TEST(Renderive_Core2, AllFrameControlModesUseKernelStrategiesAndExposeObservers)
EXPECT_EQ(playback_observer.consumed_frame_count, 1u);
}
TEST(Renderive_Core2, RetainedAxisAndTimeAxisApisRoundTripWithoutWebAdapters) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
ASSERT_TRUE(root);
@@ -336,7 +336,7 @@ TEST(Renderive_Core2, RetainedAxisAndTimeAxisApisRoundTripWithoutWebAdapters) {
EXPECT_EQ(time_axis->tick_label(first), "01:02:03.004");
}
TEST(Renderive_Core2, RetainedSpectrumApisRoundTripAndMarkersRemainObservable) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({360, 240});
plot.activate_view();
@@ -414,7 +414,7 @@ TEST(Renderive_Core2, RetainedSpectrumApisRoundTripAndMarkersRemainObservable) {
EXPECT_TRUE(plot.render_frame(true));
}
TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({420, 280});
plot.activate_view();
@@ -507,7 +507,7 @@ TEST(Renderive_Core2, RetainedHeatmapSweepAndTraceApisPreserveDataShapes) {
EXPECT_TRUE(plot.render_frame(true));
}
TEST(Renderive_Core2, RetainedSelectionAndConstellationApisDriveInteractionAndLayout) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({360, 260});
plot.activate_view();
@@ -573,7 +573,7 @@ TEST(Renderive_Core2, RetainedSelectionAndConstellationApisDriveInteractionAndLa
EXPECT_TRUE(plot.render_frame(true));
}
TEST(Renderive_Core2, PlottablePropertiesPublishOnlyAtFrameBoundary) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
@@ -591,7 +591,7 @@ TEST(Renderive_Core2, PlottablePropertiesPublishOnlyAtFrameBoundary) {
ASSERT_TRUE(plot.render_prepared_frame());
}
TEST(Renderive_Core2, EveryStatefulRenderableOwnsItsStateObserver) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
@@ -635,7 +635,7 @@ TEST(Renderive_Core2, EveryStatefulRenderableOwnsItsStateObserver) {
EXPECT_EQ(spectrum->observation().event, Renderable_Observer_Event::Published);
}
TEST(Renderive_Core2, PlottableDataUpdatesReachKernelRealTimeDataStrategy) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
@@ -700,7 +700,7 @@ TEST(Renderive_Core2, AxisRasterLayoutCoversAxisSwapAndEveryReversal) {
EXPECT_DOUBLE_EQ(points.back().y, 30.0);
}
TEST(Renderive_Core2, PartitionedPlotsBuildPropertyDrivenRenderGraphs) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({320, 180});
const auto root = plot.root_renderable();
@@ -815,7 +815,7 @@ TEST(Renderive_Core2, AutomaticPartitioningSplitsOnlyForMeasuredBottlenecks) {
}
TEST(Renderive_Core2, DynamicWaterfallCaptureStressPreservesPlansSlotsAndExactSessions) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({640, 360});
const auto root = plot.root_renderable();
@@ -873,7 +873,7 @@ TEST(Renderive_Core2, DynamicWaterfallCaptureStressPreservesPlansSlotsAndExactSe
}
TEST(Renderive_Core2, PaintOnlyStyleChangesPreservePrepareCache) {
Plot_Core plot;
Scene2D plot;
plot.init();
plot.set_viewport_size({320, 180});
const auto root = plot.root_renderable();
@@ -921,7 +921,7 @@ TEST(Renderive_Core2, PaintOnlyStyleChangesPreservePrepareCache) {
}));
}
TEST(Renderive_Core2, PlottableAxesAreDataDependenciesAndPaintOverlays) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto frequency = Frequency_Axis::Builder(root, Orientation::Horizontal).build();
@@ -955,7 +955,7 @@ TEST(Renderive_Core2, PlottableAxesAreDataDependenciesAndPaintOverlays) {
EXPECT_LT(spectrum_position, power_position);
}
TEST(Renderive_Core2, InteractionOverlayPaintsAboveItsAxes) {
Plot_Core plot;
Scene2D plot;
plot.init();
const auto root = plot.root_renderable();
const auto horizontal = Axis::Builder(root, Orientation::Horizontal).build();