重构出Core2

This commit is contained in:
2026-08-10 19:55:29 +08:00
parent 602e73f3b3
commit cfc0849081
44 changed files with 4977 additions and 341 deletions
+286
View File
@@ -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
+97
View File
@@ -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