网页升级
This commit is contained in:
+318
-38
@@ -5,20 +5,185 @@
|
||||
#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 <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
namespace renderive {
|
||||
namespace {
|
||||
|
||||
using Plot_Frame_Control = ::Low_Latency_Strategy<::Scene2D_Frame_Data>;
|
||||
using Plot_Scene = ::Scene2D_Context<Plot_Frame_Control, detail::Blend2D_Color_Cache>;
|
||||
struct Frame_Observer_Data {
|
||||
mutable std::mutex mutex;
|
||||
std::uint32_t last_event{};
|
||||
std::uint32_t limit_state{};
|
||||
bool has_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{};
|
||||
std::uint64_t bottleneck_duration_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{};
|
||||
};
|
||||
|
||||
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->has_limit_state = true;
|
||||
data->limit_state = static_cast<std::uint32_t>(observation.state.limit_state);
|
||||
data->frequency_hz = observation.state.frequency_hz;
|
||||
data->target_interval_ns = observation.state.target_interval_ns;
|
||||
data->paint_duration_ns = observation.state.paint_duration_ns;
|
||||
data->render_duration_ns = observation.state.render_duration_ns;
|
||||
data->bottleneck_duration_ns = observation.state.bottleneck_duration_ns;
|
||||
data->next_refresh_interval_ns = observation.state.next_refresh_interval_ns;
|
||||
data->end_to_end_ns = observation.state.end_to_end_ns;
|
||||
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"};
|
||||
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)};
|
||||
@@ -35,9 +200,27 @@ private:
|
||||
} // namespace
|
||||
|
||||
struct Plot_Core::Impl {
|
||||
Impl()
|
||||
: scene(*memory_resource(Memory_Domain::Plot_Frame)) {}
|
||||
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;
|
||||
@@ -50,7 +233,10 @@ struct Plot_Core::Impl {
|
||||
};
|
||||
|
||||
Plot_Core::Plot_Core()
|
||||
: impl_(std::make_unique<Impl>()) {}
|
||||
: 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;
|
||||
|
||||
@@ -60,12 +246,14 @@ void Plot_Core::init() {
|
||||
return;
|
||||
auto root = renderive::make_shared<Renderable_Group>(Memory_Domain::Renderable, *this, true);
|
||||
root->set_object_name("root");
|
||||
impl_->scene.attach_renderable(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 = impl_->scene.topology_snapshot();
|
||||
const auto topology = with_scene(impl_->scene, [](const auto& scene) {
|
||||
return scene.topology_snapshot();
|
||||
});
|
||||
for (const auto& relationship : topology.display) {
|
||||
if (relationship.parent)
|
||||
continue;
|
||||
@@ -96,12 +284,14 @@ void Plot_Core::attach_renderable(const std::shared_ptr<Renderable>& renderable,
|
||||
throw std::logic_error("Plot_Core::init must be called before adding renderables");
|
||||
parent_pointer = root.get();
|
||||
}
|
||||
impl_->scene.attach_renderable(renderable);
|
||||
with_scene(impl_->scene, [&renderable](auto& scene) { scene.attach_renderable(renderable); });
|
||||
try {
|
||||
impl_->scene.set_display_parent(*renderable, parent_pointer);
|
||||
impl_->scene.set_dependency_parent(*renderable, parent_pointer);
|
||||
with_scene(impl_->scene, [&renderable, parent_pointer](auto& scene) {
|
||||
scene.set_display_parent(*renderable, parent_pointer);
|
||||
scene.set_dependency_parent(*renderable, parent_pointer);
|
||||
});
|
||||
} catch (...) {
|
||||
impl_->scene.detach_renderable(*renderable);
|
||||
with_scene(impl_->scene, [&renderable](auto& scene) { scene.detach_renderable(*renderable); });
|
||||
throw;
|
||||
}
|
||||
notify_model_dirty();
|
||||
@@ -110,7 +300,7 @@ void Plot_Core::attach_renderable(const std::shared_ptr<Renderable>& renderable,
|
||||
void Plot_Core::remove_renderable(const std::shared_ptr<Renderable>& renderable) {
|
||||
if (!renderable || renderable == root_renderable())
|
||||
return;
|
||||
impl_->scene.detach_renderable(*renderable);
|
||||
with_scene(impl_->scene, [&renderable](auto& scene) { scene.detach_renderable(*renderable); });
|
||||
notify_model_dirty();
|
||||
}
|
||||
|
||||
@@ -138,7 +328,9 @@ void Plot_Core::set_viewport_size(Size size) {
|
||||
return;
|
||||
impl_->viewport = size;
|
||||
}
|
||||
const auto topology = impl_->scene.topology_snapshot();
|
||||
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_cache();
|
||||
notify_model_dirty();
|
||||
@@ -150,7 +342,9 @@ Size Plot_Core::viewport_size() const noexcept {
|
||||
}
|
||||
|
||||
void Plot_Core::dispatch_event(const Event& event) {
|
||||
const auto topology = impl_->scene.topology_snapshot();
|
||||
const auto topology = with_scene(impl_->scene, [](const auto& scene) {
|
||||
return scene.topology_snapshot();
|
||||
});
|
||||
for (auto iterator = topology.display.rbegin(); iterator != topology.display.rend(); ++iterator) {
|
||||
auto base = std::const_pointer_cast<::Renderable_Base>(iterator->child);
|
||||
if (auto renderable = std::dynamic_pointer_cast<Renderable>(base)) {
|
||||
@@ -168,6 +362,45 @@ 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;
|
||||
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();
|
||||
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;
|
||||
@@ -179,17 +412,12 @@ bool Plot_Core::render_frame(bool force) {
|
||||
|
||||
const auto started = std::chrono::steady_clock::now();
|
||||
try {
|
||||
{
|
||||
auto paint_frame = impl_->scene.frame_control.acquire_painter();
|
||||
auto& scene_state = static_cast<Plot_Scene::Scene_State_Strategy&>(impl_->scene);
|
||||
scene_state.template set<&::Scene2D_State::revision>(scene_state.state_revision() + 1);
|
||||
scene_state.publish();
|
||||
}
|
||||
{
|
||||
auto render_frame = impl_->scene.frame_control.acquire_renderer();
|
||||
impl_->scene.render();
|
||||
impl_->scene.wait_for_render();
|
||||
}
|
||||
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;
|
||||
@@ -205,7 +433,11 @@ bool Plot_Core::render_frame(bool force) {
|
||||
overlay = impl_->performance;
|
||||
}
|
||||
if (overlay)
|
||||
overlay->record_frame(duration_ms, diagnostics(), impl_->scene.renderable_count(), viewport);
|
||||
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;
|
||||
@@ -214,8 +446,10 @@ bool Plot_Core::render_frame(bool force) {
|
||||
void Plot_Core::with_frame(const std::function<void(Image_View)>& consumer) {
|
||||
if (!consumer)
|
||||
return;
|
||||
impl_->scene.with_final_color_cache([&consumer](const detail::Blend2D_Color_Cache& cache) {
|
||||
consumer(cache.view());
|
||||
with_scene(impl_->scene, [&consumer](auto& scene) {
|
||||
scene.with_final_color_cache([&consumer](const detail::Blend2D_Color_Cache& cache) {
|
||||
consumer(cache.view());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -235,26 +469,70 @@ bool Plot_Core::view_active() const noexcept {
|
||||
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");
|
||||
impl_->scene.frame_control.set_frequency_hz(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.set_frequency_hz(fps);
|
||||
notify_model_dirty();
|
||||
}
|
||||
|
||||
double Plot_Core::max_render_fps() const noexcept {
|
||||
return impl_->scene.frame_control.state().frequency_hz;
|
||||
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 {
|
||||
const auto state = impl_->scene.frame_control.state();
|
||||
return {
|
||||
view_active(),
|
||||
{state.frequency_hz, state.completed_lifecycle_count, state.next_refresh_interval_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();
|
||||
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 {
|
||||
std::lock_guard lock(impl_->observer->mutex);
|
||||
return {impl_->mode,
|
||||
impl_->observer->observation_count == 0
|
||||
? std::string("none")
|
||||
: observer_event_name(impl_->mode, impl_->observer->last_event),
|
||||
limit_state_name(impl_->observer->has_limit_state, impl_->observer->limit_state),
|
||||
impl_->observer->frequency_hz,
|
||||
impl_->observer->observation_count,
|
||||
impl_->observer->produced_frame_count,
|
||||
impl_->observer->consumed_frame_count,
|
||||
impl_->observer->dropped_frame_count,
|
||||
impl_->observer->failed_operation_count,
|
||||
impl_->observer->pending_frame_count,
|
||||
impl_->observer->latest_sequence,
|
||||
impl_->observer->paint_duration_ns,
|
||||
impl_->observer->render_duration_ns,
|
||||
impl_->observer->target_interval_ns,
|
||||
impl_->observer->bottleneck_duration_ns,
|
||||
impl_->observer->next_refresh_interval_ns,
|
||||
impl_->observer->paint_lease_wait_ns,
|
||||
impl_->observer->paint_state_wait_ns,
|
||||
impl_->observer->publish_state_wait_ns,
|
||||
impl_->observer->ready_wait_ns,
|
||||
impl_->observer->frame_age_at_render_ns,
|
||||
impl_->observer->render_lease_wait_ns,
|
||||
impl_->observer->render_state_wait_ns,
|
||||
impl_->observer->render_finish_state_wait_ns,
|
||||
impl_->observer->queue_wait_ns,
|
||||
impl_->observer->end_to_end_ns};
|
||||
}
|
||||
|
||||
void Plot_Core::set_presentation_sink(std::weak_ptr<Presentation_Sink> sink) {
|
||||
std::lock_guard lock(impl_->mutex);
|
||||
impl_->presentation_sink = std::move(sink);
|
||||
@@ -274,12 +552,14 @@ void Plot_Core::set_performance_overlay(std::shared_ptr<Performance_Overlay> ove
|
||||
}
|
||||
|
||||
::Scene_Base& Plot_Core::kernel_scene() const noexcept {
|
||||
return impl_->scene;
|
||||
return with_scene(impl_->scene, [](auto& scene) -> ::Scene_Base& { return scene; });
|
||||
}
|
||||
|
||||
void Plot_Core::set_renderable_cache(Renderable& renderable, Renderable_Cache_Mode mode) {
|
||||
impl_->scene.set_renderable_configuration(
|
||||
renderable, {.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel});
|
||||
with_scene(impl_->scene, [&renderable, mode](auto& scene) {
|
||||
scene.set_renderable_configuration(
|
||||
renderable, {.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel});
|
||||
});
|
||||
notify_model_dirty();
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,12 @@ 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{};
|
||||
@@ -28,6 +34,35 @@ struct Low_Latency_Diagnostics {
|
||||
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{};
|
||||
std::uint64_t bottleneck_duration_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;
|
||||
@@ -37,6 +72,7 @@ public:
|
||||
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;
|
||||
@@ -64,6 +100,10 @@ public:
|
||||
|
||||
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);
|
||||
|
||||
@@ -75,6 +115,8 @@ public:
|
||||
[[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;
|
||||
|
||||
Reference in New Issue
Block a user