删除plot_core
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,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>()) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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(); });
|
||||
|
||||
@@ -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
@@ -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"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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>());
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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>(
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user