重构出Core2
This commit is contained in:
@@ -0,0 +1,286 @@
|
||||
#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/scene/Scene2D_Context.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <utility>
|
||||
|
||||
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>;
|
||||
|
||||
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 paint(detail::Painter&) override {}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
struct Plot_Core::Impl {
|
||||
Impl()
|
||||
: scene(*memory_resource(Memory_Domain::Plot_Frame)) {}
|
||||
|
||||
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()
|
||||
: impl_(std::make_unique<Impl>()) {}
|
||||
|
||||
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");
|
||||
impl_->scene.attach_renderable(root);
|
||||
notify_model_dirty();
|
||||
}
|
||||
|
||||
std::shared_ptr<Renderable> Plot_Core::root_renderable() const {
|
||||
const auto topology = impl_->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();
|
||||
}
|
||||
impl_->scene.attach_renderable(renderable);
|
||||
try {
|
||||
impl_->scene.set_display_parent(*renderable, parent_pointer);
|
||||
impl_->scene.set_dependency_parent(*renderable, parent_pointer);
|
||||
} catch (...) {
|
||||
impl_->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;
|
||||
impl_->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 = impl_->scene.topology_snapshot();
|
||||
for (const auto& renderable : topology.renderables)
|
||||
const_cast<::Renderable_Base&>(*renderable).invalidate_cache();
|
||||
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 topology = impl_->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)) {
|
||||
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::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 {
|
||||
{
|
||||
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();
|
||||
}
|
||||
} 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(), impl_->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;
|
||||
impl_->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");
|
||||
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;
|
||||
}
|
||||
|
||||
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}
|
||||
};
|
||||
}
|
||||
|
||||
Refresh_Control_Snapshot Plot_Core::refresh_feedback_snapshot() const {
|
||||
return diagnostics().refresh;
|
||||
}
|
||||
|
||||
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 impl_->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});
|
||||
notify_model_dirty();
|
||||
}
|
||||
|
||||
} // namespace renderive
|
||||
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include "../base/Types.h"
|
||||
#include "../event/Event.h"
|
||||
|
||||
#include <atomic>
|
||||
#include <concepts>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class Scene_Base;
|
||||
|
||||
namespace renderive {
|
||||
|
||||
class Performance_Overlay;
|
||||
struct Performance_Overlay_Options;
|
||||
class Renderable;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
class Presentation_Sink {
|
||||
public:
|
||||
virtual ~Presentation_Sink() = default;
|
||||
virtual void request_present(Rect dirty_rect) = 0;
|
||||
};
|
||||
|
||||
class LIB_DECL Plot_Core {
|
||||
public:
|
||||
Plot_Core();
|
||||
~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 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);
|
||||
[[nodiscard]] double max_render_fps() const noexcept;
|
||||
[[nodiscard]] Low_Latency_Diagnostics diagnostics() const;
|
||||
[[nodiscard]] Refresh_Control_Snapshot refresh_feedback_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
|
||||
Reference in New Issue
Block a user