From 7c6b0f3e1e0121ed6a289a01d353e773728c4eae Mon Sep 17 00:00:00 2001 From: wyc <1104749580@qq.com> Date: Mon, 17 Aug 2026 11:50:07 +0800 Subject: [PATCH] =?UTF-8?q?=E6=94=B9=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 1 + Kernel/CMakeLists.txt | 9 +- Kernel/src/renderive/base/adapter/Enum.hpp | 42 + .../src/renderive/base/adapter/Expected.hpp | 23 + .../base/observer/Observer_State.hpp | 6 + Kernel/src/renderive/event/Event.hpp | 46 +- .../renderive/frame_control/Inheritance.hpp | 51 + .../base/Frame_Control_Strategy_Base.hpp | 66 +- .../concept/Frame_Control_Strategy.hpp | 7 +- .../concept/Frame_Control_Strategy_Base.hpp | 1 - .../concept/Frame_Refresh_Strategy.hpp | 1 - .../strategy/flow/Flow_Refresh_Strategy.hpp | 38 +- .../strategy/flow/Flow_Refresh_Strategy.inl | 261 ++-- .../low_latency/Low_Latency_Strategy.hpp | 51 +- .../low_latency/Low_Latency_Strategy.inl | 453 +++--- .../manual/Manual_Refresh_Strategy.hpp | 40 +- .../manual/Manual_Refresh_Strategy.inl | 267 ++-- .../real_time_data/Attach_Real_Time_Data.hpp | 6 +- .../Frame_Strategy_Observer.cpp | 13 +- .../src/renderive/renderable/Inheritance.hpp | 29 +- .../renderable/base/Renderable_Base.cpp | 80 +- .../renderable/base/Renderable_Base_p.hpp | 21 +- Kernel/src/renderive/scene/Inheritance.hpp | 103 +- .../src/renderive/scene/base/Scene_Base.cpp | 1280 ++++++++--------- .../src/renderive/scene/base/Scene_Base.hpp | 335 +---- .../src/renderive/scene/base/Scene_Base.inl | 212 +++ Kernel/src/renderive/scene/concept/Scene.hpp | 4 +- .../state/Render_Acquired_State_Storage.hpp | 36 + Qt/plot/Latency_Eager_Plot.cpp | 4 +- Qt/plot/Latency_Eager_Plot.h | 4 +- render_2D/CMakeLists.txt | 6 +- render_2D/render_2D/axis/Frequency_Axis.cpp | 4 +- render_2D/render_2D/axis/Frequency_Axis.h | 2 + render_2D/render_2D/axis/Numeric_Axis.cpp | 4 +- render_2D/render_2D/axis/Numeric_Axis.h | 2 + render_2D/render_2D/axis/Time_Axis.cpp | 4 +- render_2D/render_2D/axis/Time_Axis.h | 3 + .../frame_control/Plot_Frame_Control.cpp | 186 +++ .../frame_control/Plot_Frame_Control.h | 40 + render_2D/render_2D/plottable/Afterglow.h | 3 + .../plottable/Constellation_Diagram.h | 3 + .../render_2D/plottable/Frequency_Trace.h | 3 + .../plottable/Performance_Overlay.cpp | 18 +- .../render_2D/plottable/Performance_Overlay.h | 11 +- .../plottable/Selection_Rectangle_Overlay.cpp | 14 +- .../plottable/Selection_Rectangle_Overlay.h | 8 + render_2D/render_2D/plottable/Spectrum.cpp | 18 +- render_2D/render_2D/plottable/Spectrum.h | 8 + .../render_2D/plottable/Sweep_Spectrum.cpp | 23 +- .../render_2D/plottable/Sweep_Spectrum.h | 8 + render_2D/render_2D/plottable/Waterfall.cpp | 14 +- render_2D/render_2D/plottable/Waterfall.h | 8 + render_2D/render_2D/renderable/Renderable.cpp | 26 - render_2D/render_2D/renderable/Renderable.h | 17 +- .../render_2D/renderable/Renderable_Builder.h | 6 +- render_2D/render_2D/renderable/Renderable_p.h | 48 +- render_2D/render_2D/scene/Plot_Frame_Mode.h | 13 + render_2D/render_2D/scene/Plot_Frame_Status.h | 63 - render_2D/render_2D/scene/Render_Scene_2D.cpp | 405 +++--- render_2D/render_2D/scene/Render_Scene_2D.h | 79 +- render_2D/render_2D/scene/Scene_Context.hpp | 148 -- .../scene/detail/Plot_Scene_Model.hpp | 475 ------ render_3D/render_3D/Point_Visual.cpp | 26 +- render_3D/render_3D/Point_Visual.h | 8 + render_3D/render_3D/Render_Scene_3D.cpp | 653 ++++----- render_3D/render_3D/Render_Scene_3D.h | 117 +- render_3D/render_3D/Scene_Context.hpp | 105 -- render_3D/render_3D/detail/Point_Core.h | 7 +- .../frame_control/Point_Frame_Control.cpp | 190 +++ .../frame_control/Point_Frame_Control.h | 37 + render_3D/render_3D/renderable/Renderable.cpp | 20 - render_3D/render_3D/renderable/Renderable.h | 33 +- .../render_3D/renderable/Renderable_Builder.h | 7 +- render_3D/render_3D/renderable/Renderable_p.h | 38 +- render_3D/render_3D/visuals/Basic_Visual.h | 32 +- web_server/app/render_2D/Gallery_Scene2D.cpp | 5 +- 76 files changed, 3044 insertions(+), 3394 deletions(-) create mode 100644 Kernel/src/renderive/base/adapter/Enum.hpp create mode 100644 Kernel/src/renderive/base/adapter/Expected.hpp create mode 100644 Kernel/src/renderive/frame_control/Inheritance.hpp create mode 100644 Kernel/src/renderive/scene/base/Scene_Base.inl create mode 100644 render_2D/render_2D/frame_control/Plot_Frame_Control.cpp create mode 100644 render_2D/render_2D/frame_control/Plot_Frame_Control.h create mode 100644 render_2D/render_2D/scene/Plot_Frame_Mode.h delete mode 100644 render_2D/render_2D/scene/Plot_Frame_Status.h delete mode 100644 render_2D/render_2D/scene/Scene_Context.hpp delete mode 100644 render_2D/render_2D/scene/detail/Plot_Scene_Model.hpp delete mode 100644 render_3D/render_3D/Scene_Context.hpp create mode 100644 render_3D/render_3D/frame_control/Point_Frame_Control.cpp create mode 100644 render_3D/render_3D/frame_control/Point_Frame_Control.h diff --git a/AGENTS.md b/AGENTS.md index df63a22..16d2df6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,6 @@ ## 设计约束 +我使用 D:\ae\proj\Renderive\CMakeUserPresets.json 里的这个环境 "toolchain/vs2022.json" "C:\Program Files\JetBrains\CLion 2026.1\bin\cmake\win\x64\bin\cmake.exe" -DCMAKE_BUILD_TYPE=Debug --preset vs2022_debug -S D:\ae\proj\Renderive -B D:\ae\proj\Renderive\cmake-build-vs2022_debug diff --git a/Kernel/CMakeLists.txt b/Kernel/CMakeLists.txt index 507e824..2c639bf 100644 --- a/Kernel/CMakeLists.txt +++ b/Kernel/CMakeLists.txt @@ -1,5 +1,5 @@ include(${CMAKE_CURRENT_LIST_DIR}/cmake/rely.cmake) -set(Renderive_Kernel_dependencies render_kernel::oneTBB) +set(Renderive_Kernel_dependencies render_kernel::oneTBB global::magic_enum global::expected) if (RENDERIVE_BUILD_TESTS) set(Renderive_Kernel_test_targets) list(APPEND Renderive_Kernel_dependencies global::GTest) @@ -14,6 +14,8 @@ endif () rcl_load_dependency_environment(${Renderive_Kernel_dependencies}) find_package(Threads REQUIRED) find_package(TBB CONFIG REQUIRED) +find_package(magic_enum CONFIG REQUIRED) +find_package(tl-expected CONFIG REQUIRED) if (RENDERIVE_BUILD_TESTS) find_package(GTest CONFIG REQUIRED) endif () @@ -27,7 +29,10 @@ target_include_directories(Renderive_Kernel PUBLIC "$" ) target_compile_features(Renderive_Kernel PUBLIC cxx_std_20) -target_link_libraries(Renderive_Kernel PUBLIC TBB::tbb) +target_link_libraries(Renderive_Kernel PUBLIC + TBB::tbb + magic_enum::magic_enum + tl::expected) target_link_libraries(Renderive_Kernel PRIVATE Threads::Threads) function(renderive_stage_kernel_runtime target) if (WIN32) diff --git a/Kernel/src/renderive/base/adapter/Enum.hpp b/Kernel/src/renderive/base/adapter/Enum.hpp new file mode 100644 index 0000000..235a0e2 --- /dev/null +++ b/Kernel/src/renderive/base/adapter/Enum.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include + +#include +#include +#include +#include + +namespace renderive::adapter { + +template +requires std::is_enum_v +struct Enum_Adapter { + [[nodiscard]] static std::vector values() { + const auto reflected = magic_enum::enum_values(); + return {reflected.begin(), reflected.end()}; + } + + [[nodiscard]] static std::string_view name(Enum value) noexcept { + return magic_enum::enum_name(value); + } + + [[nodiscard]] static std::optional + cast(std::string_view value) noexcept { + return magic_enum::enum_cast(value); + } + + [[nodiscard]] static std::optional + cast(std::underlying_type_t value) noexcept { + return magic_enum::enum_cast(value); + } +}; + +template +requires std::is_enum_v && std::is_enum_v +[[nodiscard]] std::optional +enum_cast(Source value) noexcept { + return Enum_Adapter::cast(Enum_Adapter::name(value)); +} + +} // namespace renderive::adapter diff --git a/Kernel/src/renderive/base/adapter/Expected.hpp b/Kernel/src/renderive/base/adapter/Expected.hpp new file mode 100644 index 0000000..a58ad13 --- /dev/null +++ b/Kernel/src/renderive/base/adapter/Expected.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include + +#include +#include + +namespace renderive { + +template +using Expected = tl::expected; + +template +using Unexpected = tl::unexpected; + +template +[[nodiscard]] auto unexpected(Error&& error) + -> Unexpected> { + return Unexpected>( + std::forward(error)); +} + +} // namespace renderive diff --git a/Kernel/src/renderive/base/observer/Observer_State.hpp b/Kernel/src/renderive/base/observer/Observer_State.hpp index f6b99c4..0651282 100644 --- a/Kernel/src/renderive/base/observer/Observer_State.hpp +++ b/Kernel/src/renderive/base/observer/Observer_State.hpp @@ -40,6 +40,12 @@ public: observer_.observe(observation); } } + [[nodiscard]] Observer& observer() noexcept { + return observer_; + } + [[nodiscard]] const Observer& observer() const noexcept { + return observer_; + } private: struct Disabled_Mutex {}; [[no_unique_address]] Observer observer_; diff --git a/Kernel/src/renderive/event/Event.hpp b/Kernel/src/renderive/event/Event.hpp index abd2015..8d710e1 100644 --- a/Kernel/src/renderive/event/Event.hpp +++ b/Kernel/src/renderive/event/Event.hpp @@ -64,8 +64,24 @@ constexpr Keyboard_Modifier operator|(Keyboard_Modifier left, static_cast(right)); } +struct Pointer_Event_Capability { + virtual ~Pointer_Event_Capability() = default; + [[nodiscard]] virtual double position_x() const noexcept = 0; + [[nodiscard]] virtual double position_y() const noexcept = 0; + [[nodiscard]] virtual Mouse_Button pointer_button() const noexcept = 0; + [[nodiscard]] virtual Keyboard_Modifier keyboard_modifiers() const noexcept = 0; +}; + +struct Wheel_Event_Capability { + virtual ~Wheel_Event_Capability() = default; + [[nodiscard]] virtual double pixel_delta_x_value() const noexcept = 0; + [[nodiscard]] virtual double pixel_delta_y_value() const noexcept = 0; + [[nodiscard]] virtual double angle_delta_x_value() const noexcept = 0; + [[nodiscard]] virtual double angle_delta_y_value() const noexcept = 0; +}; + template -struct Basic_Pointer_Event : Event { +struct Basic_Pointer_Event : Event, Pointer_Event_Capability { explicit Basic_Pointer_Event(Event_Type value = Event_Type::Pointer_Move) : Event(value) {} @@ -74,16 +90,42 @@ struct Basic_Pointer_Event : Event { Mouse_Button button = Mouse_Button::None; Mouse_Button_Mask buttons{}; Keyboard_Modifier modifiers = Keyboard_Modifier::None; + + [[nodiscard]] double position_x() const noexcept override { + return static_cast(position.x); + } + [[nodiscard]] double position_y() const noexcept override { + return static_cast(position.y); + } + [[nodiscard]] Mouse_Button pointer_button() const noexcept override { + return button; + } + [[nodiscard]] Keyboard_Modifier keyboard_modifiers() const noexcept override { + return modifiers; + } }; template -struct Basic_Wheel_Event : Basic_Pointer_Event { +struct Basic_Wheel_Event : Basic_Pointer_Event, Wheel_Event_Capability { Basic_Wheel_Event() : Basic_Pointer_Event(Event_Type::Wheel) {} double angle_delta_x{}; double angle_delta_y{}; double pixel_delta_x{}; double pixel_delta_y{}; + + [[nodiscard]] double pixel_delta_x_value() const noexcept override { + return pixel_delta_x; + } + [[nodiscard]] double pixel_delta_y_value() const noexcept override { + return pixel_delta_y; + } + [[nodiscard]] double angle_delta_x_value() const noexcept override { + return angle_delta_x; + } + [[nodiscard]] double angle_delta_y_value() const noexcept override { + return angle_delta_y; + } }; template diff --git a/Kernel/src/renderive/frame_control/Inheritance.hpp b/Kernel/src/renderive/frame_control/Inheritance.hpp new file mode 100644 index 0000000..7d952d5 --- /dev/null +++ b/Kernel/src/renderive/frame_control/Inheritance.hpp @@ -0,0 +1,51 @@ +#pragma once + +#include "renderive/inheritance/Inheritance.hpp" + +namespace renderive::frame_control_inheritance { + +struct State_Chain {}; +struct Impl_Chain {}; + +template +struct State_Root + : inheritance::Type_Node {}; + +template +struct State_Node : inheritance::Type_Node { + using State_Base = inheritance::Type_Node; + using State_Base::State_Base; +}; + +template +struct Impl_Root + : inheritance::Type_Node {}; + +template +struct Impl_Node : inheritance::Type_Node { + using Impl_Base = inheritance::Type_Node; + using Impl_Base::Impl_Base; +}; + +} // namespace renderive::frame_control_inheritance + +namespace renderive::frame_control { + +template +struct strategy_base : Parent { +protected: + struct State_Layer + : frame_control_inheritance::State_Node< + State_Layer, typename Parent::State_Layer> {}; + + template + using next_State = frame_control_inheritance::State_Node; + + template + using next_Impl = frame_control_inheritance::Impl_Node< + Next, typename Parent::Impl>; + + using Parent::Parent; +}; + +} // namespace renderive::frame_control diff --git a/Kernel/src/renderive/frame_control/base/Frame_Control_Strategy_Base.hpp b/Kernel/src/renderive/frame_control/base/Frame_Control_Strategy_Base.hpp index a6e3550..387e353 100644 --- a/Kernel/src/renderive/frame_control/base/Frame_Control_Strategy_Base.hpp +++ b/Kernel/src/renderive/frame_control/base/Frame_Control_Strategy_Base.hpp @@ -1,34 +1,29 @@ #pragma once +#include "renderive/frame_control/Inheritance.hpp" + #include #include #include -#include +#include + struct Real_Time_Data_Observation; +class Stale_Latest_Data_Frame_Discard_Strategy { +public: + virtual ~Stale_Latest_Data_Frame_Discard_Strategy() = default; + virtual bool discard_stale_latest_data_frame() = 0; +}; + class Frame_Control_Strategy_Base { public: - struct State { - double frequency_hz{invalid_frequency_hz()}; - std::uint64_t next_refresh_interval_ns{}; - std::uint64_t publish_revision{}; - }; + Frame_Control_Strategy_Base(const Frame_Control_Strategy_Base&) = delete; + Frame_Control_Strategy_Base& operator=(const Frame_Control_Strategy_Base&) = delete; + Frame_Control_Strategy_Base(Frame_Control_Strategy_Base&&) = delete; + Frame_Control_Strategy_Base& operator=(Frame_Control_Strategy_Base&&) = delete; virtual ~Frame_Control_Strategy_Base() = default; virtual void swap() = 0; - double frequency_hz() const { - std::lock_guard lock(state_mutex_); - return published_state_.frequency_hz; - } - std::uint64_t next_refresh_interval_ns() const { - std::lock_guard lock(state_mutex_); - return published_state_.next_refresh_interval_ns; - } - State frame_control_state() const { - std::lock_guard lock(state_mutex_); - return published_state_; - } + [[nodiscard]] virtual double frequency_hz() const = 0; + [[nodiscard]] virtual std::uint64_t next_refresh_interval_ns() const = 0; virtual void on_real_time_data_update(const Real_Time_Data_Observation& observation) = 0; - virtual bool discard_stale_latest_data_frame() { - return false; - } static constexpr double invalid_frequency_hz() noexcept { return std::numeric_limits::quiet_NaN(); } @@ -41,15 +36,26 @@ public: return static_cast(interval_ns); } protected: - explicit Frame_Control_Strategy_Base(double frequency_hz = invalid_frequency_hz(), std::uint64_t next_refresh_interval_ns = 0) - : published_state_{frequency_hz, next_refresh_interval_ns, 0} {} - void publish_frame_control_state(double frequency_hz, std::uint64_t next_refresh_interval_ns) { - std::lock_guard lock(state_mutex_); - published_state_.frequency_hz = frequency_hz; - published_state_.next_refresh_interval_ns = next_refresh_interval_ns; - ++published_state_.publish_revision; + struct State_Layer + : renderive::frame_control_inheritance::State_Root {}; + + struct Impl + : renderive::frame_control_inheritance::Impl_Root {}; + + Frame_Control_Strategy_Base() : impl_(std::make_unique()) {} + explicit Frame_Control_Strategy_Base(std::unique_ptr impl) + : impl_(std::move(impl)) {} + + template + Implementation& d_func() noexcept { + return static_cast(*impl_); } + + template + const Implementation& d_func() const noexcept { + return static_cast(*impl_); + } + private: - State published_state_; - mutable std::mutex state_mutex_; + std::unique_ptr impl_; }; diff --git a/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy.hpp b/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy.hpp index d2948b3..c6c11a6 100644 --- a/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy.hpp +++ b/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy.hpp @@ -14,5 +14,8 @@ concept Frame_Lease_Strategy = Runtime_Frame_Control_Strategy && requires( }; template concept Frame_Control_Strategy = Frame_Lease_Strategy; -template -concept Frame_Control_Strategy_For = Frame_Control_Strategy && std::derived_from; +template +concept Frame_Control_Strategy_For = + Frame_Control_Strategy && + std::same_as && + std::derived_from; diff --git a/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy_Base.hpp b/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy_Base.hpp index 6da8d76..8cbf247 100644 --- a/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy_Base.hpp +++ b/Kernel/src/renderive/frame_control/concept/Frame_Control_Strategy_Base.hpp @@ -7,5 +7,4 @@ concept Runtime_Frame_Control_Strategy = std::derived_from std::same_as; { const_strategy.frequency_hz() } -> std::same_as; { const_strategy.next_refresh_interval_ns() } -> std::same_as; - { const_strategy.frame_control_state() } -> std::same_as; }; diff --git a/Kernel/src/renderive/frame_control/concept/Frame_Refresh_Strategy.hpp b/Kernel/src/renderive/frame_control/concept/Frame_Refresh_Strategy.hpp index e3382f7..64dc504 100644 --- a/Kernel/src/renderive/frame_control/concept/Frame_Refresh_Strategy.hpp +++ b/Kernel/src/renderive/frame_control/concept/Frame_Refresh_Strategy.hpp @@ -8,6 +8,5 @@ concept Frame_Refresh_Strategy = Frame_Control_Strategy && requires(That& typename That::Control_Error; { strategy.set_frequency_hz(frequency_hz) } -> std::same_as; { const_strategy.frequency_hz() } -> std::same_as; - { const_strategy.state() } -> std::same_as; { const_strategy.next_refresh_interval_ns() } -> std::same_as; }; diff --git a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp index 1bdb7e7..11c6e24 100644 --- a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp +++ b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -12,9 +13,15 @@ #include "renderive/base/observer/Observer.hpp" #include "renderive/frame_control/base/Frame_Control_Strategy_Base.hpp" #include "renderive/real_time_data/Observation.hpp" -template > -class Flow_Refresh_Strategy : public Frame_Control_Strategy_Base { +template > +class Flow_Refresh_Strategy + : public renderive::frame_control::strategy_base< + Flow_Refresh_Strategy, + Frame_Control_Strategy_Base> { public: + using Scene_Type = Scene; + using Control_Base = renderive::frame_control::strategy_base< + Flow_Refresh_Strategy, Frame_Control_Strategy_Base>; enum class Observation_Event { enqueued, dequeued, @@ -35,7 +42,7 @@ public: std::uint64_t render_end_time_ns{}; std::uint64_t render_duration_ns{}; }; - struct State { + struct State : Control_Base::template next_State { std::uint64_t enqueued_frame_count{}; std::uint64_t dequeued_frame_count{}; std::uint64_t rendered_frame_count{}; @@ -49,7 +56,7 @@ public: State state; Real_Time_Data_Update_State real_time_data; }; - struct Frame : Scene_Frame { + struct Frame : Scene::Frame { Statistics statistics; }; static_assert(Timed_Struct_Observer); @@ -101,22 +108,19 @@ public: Painter_Lease acquire_painter(); Render_Lease acquire_renderer(); void swap() override; + [[nodiscard]] double frequency_hz() const override; + [[nodiscard]] std::uint64_t next_refresh_interval_ns() const override; std::size_t pending_frame_count() const; - State state() const; + [[nodiscard]] Observer& observer() noexcept { return observer_; } + [[nodiscard]] const Observer& observer() const noexcept { return observer_; } void on_real_time_data_update(const Real_Time_Data_Observation& observation) override; private: - std::uint64_t now_ns() const; - void observe(const Observation& observation); - inline static thread_local Flow_Refresh_Strategy* rendering_strategy_{}; - std::pmr::memory_resource* const memory_resource_; + struct Impl; + + Impl& impl() noexcept { return this->template d_func(); } + const Impl& impl() const noexcept { + return this->template d_func(); + } Observer observer_; - using Frame_Ptr = Memory_Resource_Unique_Ptr; - oneapi::tbb::concurrent_bounded_queue frames_; - std::uint64_t next_sequence_{}; - std::uint64_t real_time_data_update_sequence_{}; - Real_Time_Data_Update_State last_real_time_data_update_; - State state_; - mutable Mutex state_mutex_; - Mutex render_mutex_; }; #include "Flow_Refresh_Strategy.inl" diff --git a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl index c8920d1..d59da0e 100644 --- a/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl +++ b/Kernel/src/renderive/frame_control/strategy/flow/Flow_Refresh_Strategy.inl @@ -1,196 +1,229 @@ #pragma once #include -template -Flow_Refresh_Strategy::Painter_Lease::Painter_Lease(Flow_Refresh_Strategy& strategy) +template +struct Flow_Refresh_Strategy::Impl + : Control_Base::template next_Impl { + using Impl_Base = Control_Base::template next_Impl; + using Frame_Ptr = Memory_Resource_Unique_Ptr; + + Impl(Flow_Refresh_Strategy& owner, std::pmr::memory_resource& resource, + std::size_t capacity) + : Impl_Base(), owner_(&owner), memory_resource_(&resource) { + if (capacity == 0) + ::renderive::error::unexpected( + "flow refresh queue capacity must be greater than zero"); + frames_.set_capacity( + static_cast(capacity)); + } + + [[nodiscard]] std::uint64_t now_ns() const; + void observe(const Observation& observation); + + inline static thread_local Flow_Refresh_Strategy* rendering_strategy_{}; + Flow_Refresh_Strategy* owner_; + std::pmr::memory_resource* const memory_resource_; + oneapi::tbb::concurrent_bounded_queue frames_; + std::uint64_t next_sequence_{}; + std::uint64_t real_time_data_update_sequence_{}; + Real_Time_Data_Update_State last_real_time_data_update_; + State state_; + mutable Mutex state_mutex_; + Mutex render_mutex_; +}; + +template +Flow_Refresh_Strategy::Painter_Lease::Painter_Lease(Flow_Refresh_Strategy& strategy) : strategy_(&strategy) { - std::lock_guard lock(strategy.state_mutex_); - frame_ = make_memory_resource_unique(*strategy.memory_resource_); - frame_->statistics.sequence = ++strategy.next_sequence_; - frame_->statistics.real_time_data_update_sequence = strategy.real_time_data_update_sequence_; - frame_->statistics.paint_begin_time_ns = strategy.now_ns(); + std::lock_guard lock(strategy.impl().state_mutex_); + frame_ = make_memory_resource_unique(*strategy.impl().memory_resource_); + frame_->statistics.sequence = ++strategy.impl().next_sequence_; + frame_->statistics.real_time_data_update_sequence = strategy.impl().real_time_data_update_sequence_; + frame_->statistics.paint_begin_time_ns = strategy.impl().now_ns(); } -template -Flow_Refresh_Strategy::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept +template +Flow_Refresh_Strategy::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::move(other.frame_)) {} -template -Flow_Refresh_Strategy::Painter_Lease::~Painter_Lease() { +template +Flow_Refresh_Strategy::Painter_Lease::~Painter_Lease() { if (!strategy_) { return; } - frame_->statistics.paint_end_time_ns = strategy_->now_ns(); + frame_->statistics.paint_end_time_ns = strategy_->impl().now_ns(); frame_->statistics.paint_duration_ns = frame_->statistics.paint_end_time_ns - frame_->statistics.paint_begin_time_ns; - frame_->statistics.queue_enter_time_ns = strategy_->now_ns(); + frame_->statistics.queue_enter_time_ns = strategy_->impl().now_ns(); const Statistics statistics = frame_->statistics; - strategy_->frames_.push(std::move(frame_)); + strategy_->impl().frames_.push(std::move(frame_)); Observation observation; { - std::lock_guard lock(strategy_->state_mutex_); - ++strategy_->state_.enqueued_frame_count; - strategy_->state_.pending_frame_count = static_cast(std::max(0, strategy_->frames_.size())); - observation = {Observation_Event::enqueued, statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + std::lock_guard lock(strategy_->impl().state_mutex_); + ++strategy_->impl().state_.enqueued_frame_count; + strategy_->impl().state_.pending_frame_count = static_cast(std::max(0, strategy_->impl().frames_.size())); + observation = {Observation_Event::enqueued, statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; } - strategy_->observe(observation); + strategy_->impl().observe(observation); } -template -Flow_Refresh_Strategy::Painter_Lease::operator bool() const { +template +Flow_Refresh_Strategy::Painter_Lease::operator bool() const { return frame_ != nullptr; } -template -auto Flow_Refresh_Strategy::Painter_Lease::operator->() -> Frame* { +template +auto Flow_Refresh_Strategy::Painter_Lease::operator->() -> Frame* { return frame_.get(); } -template -auto Flow_Refresh_Strategy::Painter_Lease::operator->() const -> const Frame* { +template +auto Flow_Refresh_Strategy::Painter_Lease::operator->() const -> const Frame* { return frame_.get(); } -template -auto Flow_Refresh_Strategy::Painter_Lease::operator*() -> Frame& { +template +auto Flow_Refresh_Strategy::Painter_Lease::operator*() -> Frame& { return *frame_; } -template -auto Flow_Refresh_Strategy::Painter_Lease::operator*() const -> const Frame& { +template +auto Flow_Refresh_Strategy::Painter_Lease::operator*() const -> const Frame& { return *frame_; } -template -auto Flow_Refresh_Strategy::Painter_Lease::get() -> Frame* { +template +auto Flow_Refresh_Strategy::Painter_Lease::get() -> Frame* { return frame_.get(); } -template -auto Flow_Refresh_Strategy::Painter_Lease::get() const -> const Frame* { +template +auto Flow_Refresh_Strategy::Painter_Lease::get() const -> const Frame* { return frame_.get(); } -template -Flow_Refresh_Strategy::Render_Lease::Render_Lease(Flow_Refresh_Strategy& strategy) +template +Flow_Refresh_Strategy::Render_Lease::Render_Lease(Flow_Refresh_Strategy& strategy) : strategy_(&strategy) { - if (rendering_strategy_ == &strategy) { + if (strategy.impl().rendering_strategy_ == &strategy) { strategy_ = nullptr; return; } - lease_lock_ = std::unique_lock(strategy.render_mutex_); - previous_render_strategy_ = std::exchange(rendering_strategy_, &strategy); + lease_lock_ = std::unique_lock(strategy.impl().render_mutex_); + previous_render_strategy_ = + std::exchange(strategy.impl().rendering_strategy_, &strategy); Observation observation; - const bool has_frame = strategy.frames_.try_pop(frame_); + const bool has_frame = strategy.impl().frames_.try_pop(frame_); { - std::lock_guard lock(strategy.state_mutex_); + std::lock_guard lock(strategy.impl().state_mutex_); if (has_frame) { - frame_->statistics.queue_leave_time_ns = strategy.now_ns(); + frame_->statistics.queue_leave_time_ns = strategy.impl().now_ns(); frame_->statistics.queue_wait_ns = frame_->statistics.queue_leave_time_ns - frame_->statistics.queue_enter_time_ns; - frame_->statistics.render_begin_time_ns = strategy.now_ns(); - ++strategy.state_.dequeued_frame_count; - strategy.state_.pending_frame_count = static_cast(std::max(0, strategy.frames_.size())); - observation = {Observation_Event::dequeued, frame_->statistics, strategy.state_, strategy.last_real_time_data_update_}; + frame_->statistics.render_begin_time_ns = strategy.impl().now_ns(); + ++strategy.impl().state_.dequeued_frame_count; + strategy.impl().state_.pending_frame_count = static_cast(std::max(0, strategy.impl().frames_.size())); + observation = {Observation_Event::dequeued, frame_->statistics, strategy.impl().state_, strategy.impl().last_real_time_data_update_}; } else { - ++strategy.state_.empty_acquire_count; - strategy.state_.pending_frame_count = 0; - observation = {Observation_Event::queue_empty, {}, strategy.state_, strategy.last_real_time_data_update_}; + ++strategy.impl().state_.empty_acquire_count; + strategy.impl().state_.pending_frame_count = 0; + observation = {Observation_Event::queue_empty, {}, strategy.impl().state_, strategy.impl().last_real_time_data_update_}; } } if (!has_frame) { lease_lock_.unlock(); - rendering_strategy_ = previous_render_strategy_; + strategy.impl().rendering_strategy_ = previous_render_strategy_; } - strategy.observe(observation); + strategy.impl().observe(observation); } -template -Flow_Refresh_Strategy::Render_Lease::Render_Lease(Render_Lease&& other) noexcept +template +Flow_Refresh_Strategy::Render_Lease::Render_Lease(Render_Lease&& other) noexcept : strategy_(std::exchange(other.strategy_, nullptr)), previous_render_strategy_(other.previous_render_strategy_), frame_(std::move(other.frame_)), lease_lock_(std::move(other.lease_lock_)) {} -template -Flow_Refresh_Strategy::Render_Lease::~Render_Lease() { +template +Flow_Refresh_Strategy::Render_Lease::~Render_Lease() { if (!strategy_ || !frame_) { return; } - frame_->statistics.render_end_time_ns = strategy_->now_ns(); + frame_->statistics.render_end_time_ns = strategy_->impl().now_ns(); frame_->statistics.render_duration_ns = frame_->statistics.render_end_time_ns - frame_->statistics.render_begin_time_ns; Observation observation; { - std::lock_guard lock(strategy_->state_mutex_); - ++strategy_->state_.rendered_frame_count; - strategy_->state_.pending_frame_count = static_cast(std::max(0, strategy_->frames_.size())); - observation = {Observation_Event::rendered, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + std::lock_guard lock(strategy_->impl().state_mutex_); + ++strategy_->impl().state_.rendered_frame_count; + strategy_->impl().state_.pending_frame_count = static_cast(std::max(0, strategy_->impl().frames_.size())); + observation = {Observation_Event::rendered, frame_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; frame_.reset(); } lease_lock_.unlock(); - rendering_strategy_ = previous_render_strategy_; - strategy_->observe(observation); + strategy_->impl().rendering_strategy_ = previous_render_strategy_; + strategy_->impl().observe(observation); } -template -Flow_Refresh_Strategy::Render_Lease::operator bool() const { +template +Flow_Refresh_Strategy::Render_Lease::operator bool() const { return frame_ != nullptr; } -template -auto Flow_Refresh_Strategy::Render_Lease::operator->() -> Frame* { +template +auto Flow_Refresh_Strategy::Render_Lease::operator->() -> Frame* { return frame_.get(); } -template -auto Flow_Refresh_Strategy::Render_Lease::operator->() const -> const Frame* { +template +auto Flow_Refresh_Strategy::Render_Lease::operator->() const -> const Frame* { return frame_.get(); } -template -auto Flow_Refresh_Strategy::Render_Lease::operator*() -> Frame& { +template +auto Flow_Refresh_Strategy::Render_Lease::operator*() -> Frame& { return *frame_; } -template -auto Flow_Refresh_Strategy::Render_Lease::operator*() const -> const Frame& { +template +auto Flow_Refresh_Strategy::Render_Lease::operator*() const -> const Frame& { return *frame_; } -template -auto Flow_Refresh_Strategy::Render_Lease::get() -> Frame* { +template +auto Flow_Refresh_Strategy::Render_Lease::get() -> Frame* { return frame_.get(); } -template -auto Flow_Refresh_Strategy::Render_Lease::get() const -> const Frame* { +template +auto Flow_Refresh_Strategy::Render_Lease::get() const -> const Frame* { return frame_.get(); } -template -Flow_Refresh_Strategy::Flow_Refresh_Strategy(Observer observer, std::size_t capacity) +template +Flow_Refresh_Strategy::Flow_Refresh_Strategy(Observer observer, std::size_t capacity) : Flow_Refresh_Strategy(*std::pmr::get_default_resource(), std::move(observer), capacity) {} -template -Flow_Refresh_Strategy::Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer, std::size_t capacity) - : Frame_Control_Strategy_Base(), memory_resource_(&memory_resource), observer_(std::move(observer)) { - if (capacity == 0) - ::renderive::error::unexpected("flow refresh queue capacity must be greater than zero"); - frames_.set_capacity(static_cast(capacity)); -} -template -auto Flow_Refresh_Strategy::acquire_painter() -> Painter_Lease { +template +Flow_Refresh_Strategy::Flow_Refresh_Strategy(std::pmr::memory_resource& memory_resource, Observer observer, std::size_t capacity) + : Control_Base(std::make_unique(*this, memory_resource, capacity)), + observer_(std::move(observer)) {} +template +auto Flow_Refresh_Strategy::acquire_painter() -> Painter_Lease { return Painter_Lease(*this); } -template -auto Flow_Refresh_Strategy::acquire_renderer() -> Render_Lease { +template +auto Flow_Refresh_Strategy::acquire_renderer() -> Render_Lease { return Render_Lease(*this); } -template -void Flow_Refresh_Strategy::swap() { - publish_frame_control_state(invalid_frequency_hz(), 0); +template +void Flow_Refresh_Strategy::swap() { } -template -std::size_t Flow_Refresh_Strategy::pending_frame_count() const { - return static_cast(std::max(0, frames_.size())); +template +double Flow_Refresh_Strategy::frequency_hz() const { + return this->invalid_frequency_hz(); } -template -auto Flow_Refresh_Strategy::state() const -> State { - std::lock_guard lock(state_mutex_); - State result = state_; - result.pending_frame_count = static_cast(std::max(0, frames_.size())); - return result; +template +std::uint64_t +Flow_Refresh_Strategy::next_refresh_interval_ns() + const { + return 0; } -template -void Flow_Refresh_Strategy::on_real_time_data_update(const Real_Time_Data_Observation& observation) { +template +std::size_t Flow_Refresh_Strategy::pending_frame_count() const { + return static_cast(std::max(0, impl().frames_.size())); +} +template +void Flow_Refresh_Strategy::on_real_time_data_update(const Real_Time_Data_Observation& observation) { Observation strategy_observation; { - std::lock_guard lock(state_mutex_); - ++real_time_data_update_sequence_; - state_.real_time_data_update_sequence = real_time_data_update_sequence_; - last_real_time_data_update_ = observation.state; - strategy_observation = {Observation_Event::real_time_data_updated, {}, state_, last_real_time_data_update_}; + std::lock_guard lock(impl().state_mutex_); + ++impl().real_time_data_update_sequence_; + impl().state_.real_time_data_update_sequence = impl().real_time_data_update_sequence_; + impl().last_real_time_data_update_ = observation.state; + strategy_observation = {Observation_Event::real_time_data_updated, {}, impl().state_, impl().last_real_time_data_update_}; } - observe(strategy_observation); + impl().observe(strategy_observation); } -template -std::uint64_t Flow_Refresh_Strategy::now_ns() const { - return observer_.now_ns(); +template +std::uint64_t +Flow_Refresh_Strategy::Impl::now_ns() const { + return owner_->observer_.now_ns(); } -template -void Flow_Refresh_Strategy::observe(const Observation& observation) { - observer_.observe(observation); +template +void Flow_Refresh_Strategy::Impl::observe( + const Observation& observation) { + owner_->observer_.observe(observation); } diff --git a/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.hpp b/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.hpp index 289a7c1..ed605f1 100644 --- a/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.hpp +++ b/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include "renderive/base/Concepts.hpp" @@ -10,9 +11,16 @@ #include "renderive/frame_control/base/Frame_Control_Strategy_Base.hpp" #include "renderive/frame_control/Frame_Consumer_Feedback.hpp" #include "renderive/real_time_data/Observation.hpp" -template > -class Low_Latency_Strategy : public Frame_Control_Strategy_Base { +template > +class Low_Latency_Strategy + : public renderive::frame_control::strategy_base< + Low_Latency_Strategy, + Frame_Control_Strategy_Base>, + public Stale_Latest_Data_Frame_Discard_Strategy { public: + using Scene_Type = Scene; + using Control_Base = renderive::frame_control::strategy_base< + Low_Latency_Strategy, Frame_Control_Strategy_Base>; enum class Control_Error : std::uint8_t { none, invalid_frequency, @@ -77,7 +85,7 @@ public: Timing_Statistics timing; Counter_Statistics counters; }; - struct State { + struct State : Control_Base::template next_State { double frequency_hz{60.0}; Limit_State limit_state{Limit_State::frequency_limited}; bool frequency_limit_enabled{true}; @@ -104,7 +112,7 @@ public: State state; Real_Time_Data_Update_State real_time_data; }; - struct Frame : Scene_Frame { + struct Frame : Scene::Frame { Statistics statistics; }; static_assert(Timed_Struct_Observer); @@ -167,37 +175,22 @@ public: [[nodiscard]] Control_Error set_consumer_feedback(Frame_Consumer_Feedback feedback); void clear_consumer_feedback(); void swap() override; - State state() const; - Counter_Statistics counter_statistics() const; + [[nodiscard]] double frequency_hz() const override; + [[nodiscard]] std::uint64_t next_refresh_interval_ns() const override; std::size_t pending_frame_count() const; + [[nodiscard]] Observer& observer() noexcept { return observer_; } + [[nodiscard]] const Observer& observer() const noexcept { return observer_; } void on_real_time_data_update(const Real_Time_Data_Observation& observation) override; bool discard_pending_frame(); bool discard_pending_frame_before(std::uint64_t real_time_data_update_sequence); bool discard_stale_latest_data_frame() override; private: - static double checked_configuration_frequency(double frequency_hz); - std::uint64_t now_ns() const; - void update_refresh_control(); - void update_consumer_estimator(std::uint64_t sample_interval_ns); - void update_state(const Frame& frame); - void observe(const Observation& observation); - bool discard_pending_frame_locked(Observation& observation); - Frame frames_[3]; + struct Impl; + + Impl& impl() noexcept { return this->template d_func(); } + const Impl& impl() const noexcept { + return this->template d_func(); + } Observer observer_; - State state_; - Frame* paint_; - Frame* cache_; - Frame* render_; - std::uint64_t next_sequence_{}; - std::uint64_t real_time_data_update_sequence_{}; - Real_Time_Data_Update_State last_real_time_data_update_; - Counter_Statistics counters_; - bool replace_pending_frame_{true}; - bool cache_ready_{}; - bool render_ready_{}; - mutable Mutex state_mutex_; - std::condition_variable_any pending_frame_condition_; - Mutex painter_mutex_; - Mutex render_mutex_; }; #include "Low_Latency_Strategy.inl" diff --git a/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.inl b/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.inl index eae9b53..810cee5 100644 --- a/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.inl +++ b/Kernel/src/renderive/frame_control/strategy/low_latency/Low_Latency_Strategy.inl @@ -3,149 +3,185 @@ #include #include #include -template -Low_Latency_Strategy::Painter_Lease::Painter_Lease(Low_Latency_Strategy& strategy) - : strategy_(&strategy), request_time_ns_(strategy.now_ns()), lease_lock_(strategy.painter_mutex_) { - const std::uint64_t lease_acquired_time_ns = strategy.now_ns(); - const std::uint64_t state_request_time_ns = strategy.now_ns(); +template +struct Low_Latency_Strategy::Impl + : Control_Base::template next_Impl { + using Impl_Base = Control_Base::template next_Impl; + + Impl(Low_Latency_Strategy& owner, Configuration configuration) + : Impl_Base(), owner_(&owner), paint_(&frames_[0]), + cache_(&frames_[1]), render_(&frames_[2]), + replace_pending_frame_(configuration.replace_pending_frame) {} + + [[nodiscard]] std::uint64_t now_ns() const; + void update_refresh_control(); + void update_consumer_estimator(std::uint64_t sample_interval_ns); + void update_state(const Frame& frame); + void observe(const Observation& observation); + bool discard_pending_frame_locked(Observation& observation); + + Frame frames_[3]; + Low_Latency_Strategy* owner_; + State state_; + Frame* paint_; + Frame* cache_; + Frame* render_; + std::uint64_t next_sequence_{}; + std::uint64_t real_time_data_update_sequence_{}; + Real_Time_Data_Update_State last_real_time_data_update_; + Counter_Statistics counters_; + bool replace_pending_frame_{true}; + bool cache_ready_{}; + bool render_ready_{}; + mutable Mutex state_mutex_; + std::condition_variable_any pending_frame_condition_; + Mutex painter_mutex_; + Mutex render_mutex_; +}; + +template +Low_Latency_Strategy::Painter_Lease::Painter_Lease(Low_Latency_Strategy& strategy) + : strategy_(&strategy), request_time_ns_(strategy.impl().now_ns()), lease_lock_(strategy.impl().painter_mutex_) { + const std::uint64_t lease_acquired_time_ns = strategy.impl().now_ns(); + const std::uint64_t state_request_time_ns = strategy.impl().now_ns(); { - std::unique_lock lock(strategy.state_mutex_); - if (!strategy.replace_pending_frame_) { - strategy.pending_frame_condition_.wait(lock, [&strategy] { - return !strategy.cache_ready_; + std::unique_lock lock(strategy.impl().state_mutex_); + if (!strategy.impl().replace_pending_frame_) { + strategy.impl().pending_frame_condition_.wait(lock, [&strategy] { + return !strategy.impl().cache_ready_; }); } - const std::uint64_t state_acquired_time_ns = strategy.now_ns(); - frame_ = strategy.paint_; + const std::uint64_t state_acquired_time_ns = strategy.impl().now_ns(); + frame_ = strategy.impl().paint_; frame_->statistics = {}; - frame_->statistics.sequence = ++strategy.next_sequence_; - frame_->statistics.real_time_data_update_sequence = strategy.real_time_data_update_sequence_; + frame_->statistics.sequence = ++strategy.impl().next_sequence_; + frame_->statistics.real_time_data_update_sequence = strategy.impl().real_time_data_update_sequence_; frame_->statistics.timing.paint_request_time_ns = request_time_ns_; frame_->statistics.timing.paint_lease_acquired_time_ns = lease_acquired_time_ns; frame_->statistics.timing.paint_lease_wait_ns = lease_acquired_time_ns - request_time_ns_; frame_->statistics.timing.paint_state_wait_ns = state_acquired_time_ns - state_request_time_ns; - frame_->statistics.counters = strategy.counters_; + frame_->statistics.counters = strategy.impl().counters_; } - frame_->statistics.timing.paint_begin_time_ns = strategy.now_ns(); + frame_->statistics.timing.paint_begin_time_ns = strategy.impl().now_ns(); } -template -Low_Latency_Strategy::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept +template +Low_Latency_Strategy::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::exchange(other.frame_, nullptr)), request_time_ns_(other.request_time_ns_), lease_lock_(std::move(other.lease_lock_)) {} -template -Low_Latency_Strategy::Painter_Lease::~Painter_Lease() { +template +Low_Latency_Strategy::Painter_Lease::~Painter_Lease() { if (!strategy_) { return; } - frame_->statistics.timing.paint_end_time_ns = strategy_->now_ns(); + frame_->statistics.timing.paint_end_time_ns = strategy_->impl().now_ns(); frame_->statistics.timing.paint_duration_ns = frame_->statistics.timing.paint_end_time_ns - frame_->statistics.timing.paint_begin_time_ns; Observation abandoned_observation; Observation published_observation; bool abandoned{}; - const std::uint64_t state_request_time_ns = strategy_->now_ns(); + const std::uint64_t state_request_time_ns = strategy_->impl().now_ns(); { - std::lock_guard lock(strategy_->state_mutex_); - const std::uint64_t state_acquired_time_ns = strategy_->now_ns(); + std::lock_guard lock(strategy_->impl().state_mutex_); + const std::uint64_t state_acquired_time_ns = strategy_->impl().now_ns(); frame_->statistics.timing.publish_state_wait_ns = state_acquired_time_ns - state_request_time_ns; - if (strategy_->cache_ready_) { - ++strategy_->counters_.abandoned_frame_count; - strategy_->cache_->statistics.counters = strategy_->counters_; - abandoned_observation = {Observation_Event::abandoned, strategy_->cache_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + if (strategy_->impl().cache_ready_) { + ++strategy_->impl().counters_.abandoned_frame_count; + strategy_->impl().cache_->statistics.counters = strategy_->impl().counters_; + abandoned_observation = {Observation_Event::abandoned, strategy_->impl().cache_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; abandoned = true; } - std::swap(strategy_->paint_, strategy_->cache_); - strategy_->cache_ready_ = true; - ++strategy_->counters_.published_frame_count; - frame_->statistics.timing.publish_time_ns = strategy_->now_ns(); - frame_->statistics.counters = strategy_->counters_; - published_observation = {Observation_Event::published, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + std::swap(strategy_->impl().paint_, strategy_->impl().cache_); + strategy_->impl().cache_ready_ = true; + ++strategy_->impl().counters_.published_frame_count; + frame_->statistics.timing.publish_time_ns = strategy_->impl().now_ns(); + frame_->statistics.counters = strategy_->impl().counters_; + published_observation = {Observation_Event::published, frame_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; } lease_lock_.unlock(); if (abandoned) { - strategy_->observe(abandoned_observation); + strategy_->impl().observe(abandoned_observation); } - strategy_->observe(published_observation); + strategy_->impl().observe(published_observation); } -template -Low_Latency_Strategy::Painter_Lease::operator bool() const { +template +Low_Latency_Strategy::Painter_Lease::operator bool() const { return frame_ != nullptr; } -template -auto Low_Latency_Strategy::Painter_Lease::operator->() -> Frame* { +template +auto Low_Latency_Strategy::Painter_Lease::operator->() -> Frame* { return frame_; } -template -auto Low_Latency_Strategy::Painter_Lease::operator->() const -> const Frame* { +template +auto Low_Latency_Strategy::Painter_Lease::operator->() const -> const Frame* { return frame_; } -template -auto Low_Latency_Strategy::Painter_Lease::operator*() -> Frame& { +template +auto Low_Latency_Strategy::Painter_Lease::operator*() -> Frame& { return *frame_; } -template -auto Low_Latency_Strategy::Painter_Lease::operator*() const -> const Frame& { +template +auto Low_Latency_Strategy::Painter_Lease::operator*() const -> const Frame& { return *frame_; } -template -auto Low_Latency_Strategy::Painter_Lease::get() -> Frame* { +template +auto Low_Latency_Strategy::Painter_Lease::get() -> Frame* { return frame_; } -template -auto Low_Latency_Strategy::Painter_Lease::get() const -> const Frame* { +template +auto Low_Latency_Strategy::Painter_Lease::get() const -> const Frame* { return frame_; } -template -Low_Latency_Strategy::Render_Lease::Render_Lease(Low_Latency_Strategy& strategy) - : strategy_(&strategy), request_time_ns_(strategy.now_ns()), lease_lock_(strategy.render_mutex_) { - const std::uint64_t lease_acquired_time_ns = strategy.now_ns(); - const std::uint64_t state_request_time_ns = strategy.now_ns(); +template +Low_Latency_Strategy::Render_Lease::Render_Lease(Low_Latency_Strategy& strategy) + : strategy_(&strategy), request_time_ns_(strategy.impl().now_ns()), lease_lock_(strategy.impl().render_mutex_) { + const std::uint64_t lease_acquired_time_ns = strategy.impl().now_ns(); + const std::uint64_t state_request_time_ns = strategy.impl().now_ns(); { - std::lock_guard lock(strategy.state_mutex_); - if (strategy.cache_ready_) { - std::swap(strategy.cache_, strategy.render_); - strategy.cache_ready_ = false; - strategy.render_ready_ = true; + std::lock_guard lock(strategy.impl().state_mutex_); + if (strategy.impl().cache_ready_) { + std::swap(strategy.impl().cache_, strategy.impl().render_); + strategy.impl().cache_ready_ = false; + strategy.impl().render_ready_ = true; acquired_new_frame_ = true; } else { - ++strategy.counters_.swap_failure_count; + ++strategy.impl().counters_.swap_failure_count; swap_failed_ = true; } - frame_ = strategy.render_ready_ ? strategy.render_ : nullptr; + frame_ = strategy.impl().render_ready_ ? strategy.impl().render_ : nullptr; } { - std::lock_guard lock(strategy.state_mutex_); - const std::uint64_t state_acquired_time_ns = strategy.now_ns(); + std::lock_guard lock(strategy.impl().state_mutex_); + const std::uint64_t state_acquired_time_ns = strategy.impl().now_ns(); if (frame_) { ++frame_->statistics.frame_render_count; frame_->statistics.timing.render_request_time_ns = request_time_ns_; frame_->statistics.timing.render_lease_acquired_time_ns = lease_acquired_time_ns; frame_->statistics.timing.render_lease_wait_ns = lease_acquired_time_ns - request_time_ns_; frame_->statistics.timing.render_state_wait_ns = state_acquired_time_ns - state_request_time_ns; - frame_->statistics.counters = strategy.counters_; + frame_->statistics.counters = strategy.impl().counters_; } if (swap_failed_) { swap_failed_observation_.event = Observation_Event::swap_failed; - swap_failed_observation_.state = strategy.state_; - swap_failed_observation_.real_time_data = strategy.last_real_time_data_update_; + swap_failed_observation_.state = strategy.impl().state_; + swap_failed_observation_.real_time_data = strategy.impl().last_real_time_data_update_; if (frame_) { swap_failed_observation_.statistics = frame_->statistics; } else { - swap_failed_observation_.statistics.counters = strategy.counters_; + swap_failed_observation_.statistics.counters = strategy.impl().counters_; } } } if (frame_) { - frame_->statistics.timing.render_begin_time_ns = strategy.now_ns(); + frame_->statistics.timing.render_begin_time_ns = strategy.impl().now_ns(); frame_->statistics.timing.frame_age_at_render_ns = frame_->statistics.timing.render_begin_time_ns - frame_->statistics.timing.publish_time_ns; if (acquired_new_frame_) { frame_->statistics.timing.ready_wait_ns = frame_->statistics.timing.frame_age_at_render_ns; } } } -template -Low_Latency_Strategy::Render_Lease::Render_Lease(Render_Lease&& other) noexcept +template +Low_Latency_Strategy::Render_Lease::Render_Lease(Render_Lease&& other) noexcept : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::exchange(other.frame_, nullptr)), request_time_ns_(other.request_time_ns_), acquired_new_frame_(other.acquired_new_frame_), swap_failed_(other.swap_failed_), swap_failed_observation_(other.swap_failed_observation_), lease_lock_(std::move(other.lease_lock_)) {} -template -Low_Latency_Strategy::Render_Lease::~Render_Lease() { +template +Low_Latency_Strategy::Render_Lease::~Render_Lease() { if (!strategy_) { return; } @@ -154,220 +190,210 @@ Low_Latency_Strategy::Render_Lease::~Render_Lease( bool rendered{}; bool lifecycle_completed{}; if (frame_) { - frame_->statistics.timing.render_end_time_ns = strategy_->now_ns(); + frame_->statistics.timing.render_end_time_ns = strategy_->impl().now_ns(); frame_->statistics.timing.render_duration_ns = frame_->statistics.timing.render_end_time_ns - frame_->statistics.timing.render_begin_time_ns; frame_->statistics.timing.end_to_end_ns = frame_->statistics.timing.render_end_time_ns - frame_->statistics.timing.paint_request_time_ns; - const std::uint64_t state_request_time_ns = strategy_->now_ns(); + const std::uint64_t state_request_time_ns = strategy_->impl().now_ns(); { - std::lock_guard lock(strategy_->state_mutex_); - const std::uint64_t state_acquired_time_ns = strategy_->now_ns(); + std::lock_guard lock(strategy_->impl().state_mutex_); + const std::uint64_t state_acquired_time_ns = strategy_->impl().now_ns(); frame_->statistics.timing.render_finish_state_wait_ns = state_acquired_time_ns - state_request_time_ns; - ++strategy_->counters_.render_pass_count; - frame_->statistics.counters = strategy_->counters_; + ++strategy_->impl().counters_.render_pass_count; + frame_->statistics.counters = strategy_->impl().counters_; if (acquired_new_frame_) { - strategy_->update_state(*frame_); - lifecycle_observation = {Observation_Event::lifecycle_completed, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + strategy_->impl().update_state(*frame_); + lifecycle_observation = {Observation_Event::lifecycle_completed, frame_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; lifecycle_completed = true; } - rendered_observation = {Observation_Event::rendered, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + rendered_observation = {Observation_Event::rendered, frame_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; rendered = true; } } lease_lock_.unlock(); if (acquired_new_frame_) { - strategy_->pending_frame_condition_.notify_one(); + strategy_->impl().pending_frame_condition_.notify_one(); } if (swap_failed_) { - strategy_->observe(swap_failed_observation_); + strategy_->impl().observe(swap_failed_observation_); } if (rendered) { - strategy_->observe(rendered_observation); + strategy_->impl().observe(rendered_observation); } if (lifecycle_completed) { - strategy_->observe(lifecycle_observation); + strategy_->impl().observe(lifecycle_observation); } } -template -Low_Latency_Strategy::Render_Lease::operator bool() const { +template +Low_Latency_Strategy::Render_Lease::operator bool() const { return frame_ != nullptr; } -template -auto Low_Latency_Strategy::Render_Lease::operator->() -> Frame* { +template +auto Low_Latency_Strategy::Render_Lease::operator->() -> Frame* { return frame_; } -template -auto Low_Latency_Strategy::Render_Lease::operator->() const -> const Frame* { +template +auto Low_Latency_Strategy::Render_Lease::operator->() const -> const Frame* { return frame_; } -template -auto Low_Latency_Strategy::Render_Lease::operator*() -> Frame& { +template +auto Low_Latency_Strategy::Render_Lease::operator*() -> Frame& { return *frame_; } -template -auto Low_Latency_Strategy::Render_Lease::operator*() const -> const Frame& { +template +auto Low_Latency_Strategy::Render_Lease::operator*() const -> const Frame& { return *frame_; } -template -auto Low_Latency_Strategy::Render_Lease::get() -> Frame* { +template +auto Low_Latency_Strategy::Render_Lease::get() -> Frame* { return frame_; } -template -auto Low_Latency_Strategy::Render_Lease::get() const -> const Frame* { +template +auto Low_Latency_Strategy::Render_Lease::get() const -> const Frame* { return frame_; } -template -Low_Latency_Strategy::Low_Latency_Strategy(Observer observer, Configuration configuration) - : Frame_Control_Strategy_Base(checked_configuration_frequency(configuration.frequency_hz), frequency_interval_ns(configuration.frequency_hz)), observer_(std::move(observer)), paint_(&frames_[0]), cache_(&frames_[1]), render_(&frames_[2]), replace_pending_frame_(configuration.replace_pending_frame) { - state_.frequency_hz = configuration.frequency_hz; - state_.target_interval_ns = frequency_interval_ns(configuration.frequency_hz); - update_refresh_control(); +template +Low_Latency_Strategy::Low_Latency_Strategy(Observer observer, Configuration configuration) + : Control_Base(std::make_unique(*this, configuration)), + observer_(std::move(observer)) { + impl().state_.frequency_hz = configuration.frequency_hz; + impl().state_.target_interval_ns = this->frequency_interval_ns(configuration.frequency_hz); + impl().update_refresh_control(); } -template -auto Low_Latency_Strategy::acquire_painter() -> Painter_Lease { +template +auto Low_Latency_Strategy::acquire_painter() -> Painter_Lease { return Painter_Lease(*this); } -template -auto Low_Latency_Strategy::acquire_renderer() -> Render_Lease { +template +auto Low_Latency_Strategy::acquire_renderer() -> Render_Lease { return Render_Lease(*this); } -template -auto Low_Latency_Strategy::set_frequency_hz(double frequency_hz) -> Control_Error { +template +auto Low_Latency_Strategy::set_frequency_hz(double frequency_hz) -> Control_Error { if (!std::isfinite(frequency_hz) || frequency_hz <= 0.0) return Control_Error::invalid_frequency; - std::lock_guard lock(state_mutex_); - state_.frequency_limit_enabled = true; - state_.frequency_hz = frequency_hz; - state_.target_interval_ns = frequency_interval_ns(frequency_hz); - update_refresh_control(); + std::lock_guard lock(impl().state_mutex_); + impl().state_.frequency_limit_enabled = true; + impl().state_.frequency_hz = frequency_hz; + impl().state_.target_interval_ns = this->frequency_interval_ns(frequency_hz); + impl().update_refresh_control(); return Control_Error::none; } -template -void Low_Latency_Strategy::clear_frequency_limit() { - std::lock_guard lock(state_mutex_); - state_.frequency_limit_enabled = false; - state_.frequency_hz = invalid_frequency_hz(); - state_.target_interval_ns = 0; - update_refresh_control(); +template +void Low_Latency_Strategy::clear_frequency_limit() { + std::lock_guard lock(impl().state_mutex_); + impl().state_.frequency_limit_enabled = false; + impl().state_.frequency_hz = this->invalid_frequency_hz(); + impl().state_.target_interval_ns = 0; + impl().update_refresh_control(); } -template -auto Low_Latency_Strategy::set_consumer_feedback(Frame_Consumer_Feedback feedback) -> Control_Error { +template +auto Low_Latency_Strategy::set_consumer_feedback(Frame_Consumer_Feedback feedback) -> Control_Error { if (feedback.observed_interval_ns == 0) return Control_Error::invalid_consumer_feedback; - std::lock_guard lock(state_mutex_); - update_consumer_estimator(feedback.observed_interval_ns); - update_refresh_control(); + std::lock_guard lock(impl().state_mutex_); + impl().update_consumer_estimator(feedback.observed_interval_ns); + impl().update_refresh_control(); return Control_Error::none; } -template -void Low_Latency_Strategy::clear_consumer_feedback() { - std::lock_guard lock(state_mutex_); - state_.consumer_feedback_enabled = false; - state_.consumer_sample_interval_ns = 0; - state_.consumer_smoothed_interval_ns = 0; - state_.consumer_variation_ns = 0; - state_.consumer_safety_interval_ns = 0; - state_.consumer_interval_ns = 0; - update_refresh_control(); +template +void Low_Latency_Strategy::clear_consumer_feedback() { + std::lock_guard lock(impl().state_mutex_); + impl().state_.consumer_feedback_enabled = false; + impl().state_.consumer_sample_interval_ns = 0; + impl().state_.consumer_smoothed_interval_ns = 0; + impl().state_.consumer_variation_ns = 0; + impl().state_.consumer_safety_interval_ns = 0; + impl().state_.consumer_interval_ns = 0; + impl().update_refresh_control(); } -template -void Low_Latency_Strategy::swap() { - double frequency_hz; - std::uint64_t next_refresh_interval_ns; - { - std::lock_guard lock(state_mutex_); - frequency_hz = state_.frequency_hz; - next_refresh_interval_ns = state_.next_refresh_interval_ns; - } - publish_frame_control_state(frequency_hz, next_refresh_interval_ns); +template +void Low_Latency_Strategy::swap() { } -template -auto Low_Latency_Strategy::state() const -> State { - std::lock_guard lock(state_mutex_); - return state_; +template +double Low_Latency_Strategy::frequency_hz() const { + std::lock_guard lock(impl().state_mutex_); + return impl().state_.frequency_hz; } -template -auto Low_Latency_Strategy::counter_statistics() const -> Counter_Statistics { - std::lock_guard lock(state_mutex_); - return counters_; +template +std::uint64_t +Low_Latency_Strategy::next_refresh_interval_ns() + const { + std::lock_guard lock(impl().state_mutex_); + return impl().state_.next_refresh_interval_ns; } -template -std::size_t Low_Latency_Strategy::pending_frame_count() const { - std::lock_guard lock(state_mutex_); - return cache_ready_ ? 1U : 0U; +template +std::size_t Low_Latency_Strategy::pending_frame_count() const { + std::lock_guard lock(impl().state_mutex_); + return impl().cache_ready_ ? 1U : 0U; } -template -void Low_Latency_Strategy::on_real_time_data_update(const Real_Time_Data_Observation& observation) { +template +void Low_Latency_Strategy::on_real_time_data_update(const Real_Time_Data_Observation& observation) { Observation strategy_observation; { - std::lock_guard lock(state_mutex_); - ++real_time_data_update_sequence_; - last_real_time_data_update_ = observation.state; - state_.real_time_data_update_sequence = real_time_data_update_sequence_; + std::lock_guard lock(impl().state_mutex_); + ++impl().real_time_data_update_sequence_; + impl().last_real_time_data_update_ = observation.state; + impl().state_.real_time_data_update_sequence = impl().real_time_data_update_sequence_; Statistics statistics; - statistics.real_time_data_update_sequence = real_time_data_update_sequence_; - statistics.counters = counters_; - strategy_observation = {Observation_Event::real_time_data_updated, statistics, state_, last_real_time_data_update_}; + statistics.real_time_data_update_sequence = impl().real_time_data_update_sequence_; + statistics.counters = impl().counters_; + strategy_observation = {Observation_Event::real_time_data_updated, statistics, impl().state_, impl().last_real_time_data_update_}; } - observe(strategy_observation); + impl().observe(strategy_observation); } -template -bool Low_Latency_Strategy::discard_pending_frame() { +template +bool Low_Latency_Strategy::discard_pending_frame() { Observation observation; bool discarded{}; { - std::lock_guard lock(state_mutex_); - discarded = discard_pending_frame_locked(observation); + std::lock_guard lock(impl().state_mutex_); + discarded = impl().discard_pending_frame_locked(observation); } if (discarded) { - pending_frame_condition_.notify_one(); - observe(observation); + impl().pending_frame_condition_.notify_one(); + impl().observe(observation); } return discarded; } -template -bool Low_Latency_Strategy::discard_pending_frame_before(std::uint64_t real_time_data_update_sequence) { +template +bool Low_Latency_Strategy::discard_pending_frame_before(std::uint64_t real_time_data_update_sequence) { Observation observation; bool discarded{}; { - std::lock_guard lock(state_mutex_); - if (cache_ready_ && cache_->statistics.real_time_data_update_sequence < real_time_data_update_sequence) { - discarded = discard_pending_frame_locked(observation); + std::lock_guard lock(impl().state_mutex_); + if (impl().cache_ready_ && impl().cache_->statistics.real_time_data_update_sequence < real_time_data_update_sequence) { + discarded = impl().discard_pending_frame_locked(observation); } } if (discarded) { - pending_frame_condition_.notify_one(); - observe(observation); + impl().pending_frame_condition_.notify_one(); + impl().observe(observation); } return discarded; } -template -bool Low_Latency_Strategy::discard_stale_latest_data_frame() { +template +bool Low_Latency_Strategy::discard_stale_latest_data_frame() { Observation observation; bool discarded{}; { - std::lock_guard lock(state_mutex_); - if (last_real_time_data_update_.retention == Real_Time_Data_Retention::latest && cache_ready_ && cache_->statistics.real_time_data_update_sequence < real_time_data_update_sequence_) { - discarded = discard_pending_frame_locked(observation); + std::lock_guard lock(impl().state_mutex_); + if (impl().last_real_time_data_update_.retention == Real_Time_Data_Retention::latest && impl().cache_ready_ && impl().cache_->statistics.real_time_data_update_sequence < impl().real_time_data_update_sequence_) { + discarded = impl().discard_pending_frame_locked(observation); } } if (discarded) { - pending_frame_condition_.notify_one(); - observe(observation); + impl().pending_frame_condition_.notify_one(); + impl().observe(observation); } return discarded; } -template -double Low_Latency_Strategy::checked_configuration_frequency(double frequency_hz) { - if (!std::isfinite(frequency_hz) || frequency_hz <= 0.0) - ::renderive::error::unexpected("low-latency frame-control configuration frequency must be positive and finite"); - return frequency_hz; +template +std::uint64_t +Low_Latency_Strategy::Impl::now_ns() const { + return owner_->observer_.now_ns(); } -template -std::uint64_t Low_Latency_Strategy::now_ns() const { - return observer_.now_ns(); -} -template -void Low_Latency_Strategy::update_refresh_control() { +template +void Low_Latency_Strategy::Impl::update_refresh_control() { state_.next_refresh_interval_ns = std::max({state_.target_interval_ns, state_.bottleneck_duration_ns, state_.consumer_interval_ns}); @@ -385,8 +411,9 @@ void Low_Latency_Strategy::update_refresh_control( state_.limit_state = Limit_State::unlimited; } } -template -void Low_Latency_Strategy::update_consumer_estimator(std::uint64_t sample_interval_ns) { +template +void Low_Latency_Strategy::Impl::update_consumer_estimator( + std::uint64_t sample_interval_ns) { const std::uint64_t previous_sample = state_.consumer_sample_interval_ns; state_.consumer_sample_interval_ns = sample_interval_ns; if (!state_.consumer_feedback_enabled) { @@ -413,8 +440,9 @@ void Low_Latency_Strategy::update_consumer_estimat state_.consumer_interval_ns = std::max(state_.consumer_smoothed_interval_ns, sample_interval_ns); state_.consumer_safety_interval_ns = std::max(state_.consumer_interval_ns, safety_interval); } -template -void Low_Latency_Strategy::update_state(const Frame& frame) { +template +void Low_Latency_Strategy::Impl::update_state( + const Frame& frame) { state_.paint_duration_ns = frame.statistics.timing.paint_duration_ns; state_.render_duration_ns = frame.statistics.timing.render_duration_ns; state_.bottleneck_duration_ns = std::max(state_.paint_duration_ns, state_.render_duration_ns); @@ -424,18 +452,21 @@ void Low_Latency_Strategy::update_state(const Fram state_.frame_sequence = frame.statistics.sequence; state_.completed_frame_real_time_data_update_sequence = frame.statistics.real_time_data_update_sequence; } -template -void Low_Latency_Strategy::observe(const Observation& observation) { - observer_.observe(observation); +template +void Low_Latency_Strategy::Impl::observe( + const Observation& observation) { + owner_->observer_.observe(observation); } -template -bool Low_Latency_Strategy::discard_pending_frame_locked(Observation& observation) { +template +bool Low_Latency_Strategy::Impl::discard_pending_frame_locked( + Observation& observation) { if (!cache_ready_) { return false; } cache_ready_ = false; ++counters_.manually_discarded_frame_count; cache_->statistics.counters = counters_; - observation = {Observation_Event::manually_discarded, cache_->statistics, state_, last_real_time_data_update_}; + observation = {Observation_Event::manually_discarded, cache_->statistics, + state_, last_real_time_data_update_}; return true; } diff --git a/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.hpp b/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.hpp index e3da70a..a6e793d 100644 --- a/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.hpp +++ b/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.hpp @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include "renderive/base/Concepts.hpp" @@ -10,9 +11,15 @@ enum class Manual_Refresh_Result : std::uint8_t { none, no_pending_frame }; -template > -class Manual_Refresh_Strategy : public Frame_Control_Strategy_Base { +template > +class Manual_Refresh_Strategy +: public renderive::frame_control::strategy_base< + Manual_Refresh_Strategy, + Frame_Control_Strategy_Base> { public: + using Scene_Type = Scene; + using Control_Base = renderive::frame_control::strategy_base< + Manual_Refresh_Strategy, Frame_Control_Strategy_Base>; using Refresh_Result = Manual_Refresh_Result; enum class Observation_Event { prepared, @@ -33,7 +40,7 @@ public: std::uint64_t render_end_time_ns{}; std::uint64_t render_duration_ns{}; }; - struct State { + struct State : Control_Base::template next_State { std::uint64_t prepared_frame_count{}; std::uint64_t replaced_prepared_frame_count{}; std::uint64_t discarded_prepared_frame_count{}; @@ -51,7 +58,7 @@ public: State state; Real_Time_Data_Update_State real_time_data; }; - struct Frame : Scene_Frame { + struct Frame : Scene::Frame { Statistics statistics; }; static_assert(Timed_Struct_Observer); @@ -101,24 +108,21 @@ public: Painter_Lease acquire_painter(); Render_Lease acquire_renderer(); void swap() override; + [[nodiscard]] double frequency_hz() const override; + [[nodiscard]] std::uint64_t next_refresh_interval_ns() const override; [[nodiscard]] Manual_Refresh_Result refresh(); bool discard_pending_frame(); - State state() const; + [[nodiscard]] Observer& observer() noexcept { return observer_; } + [[nodiscard]] const Observer& observer() const noexcept { return observer_; } void on_real_time_data_update(const Real_Time_Data_Observation& observation) override; private: - std::uint64_t now_ns() const; - void observe(const Observation& observation); - Frame frames_[3]; + struct Impl; + Impl& impl() noexcept { + return this->template d_func(); + } + const Impl& impl() const noexcept { + return this->template d_func(); + } Observer observer_; - Frame* paint_; - Frame* pending_; - Frame* render_; - std::uint64_t next_sequence_{}; - std::uint64_t real_time_data_update_sequence_{}; - Real_Time_Data_Update_State last_real_time_data_update_; - State state_; - mutable Mutex state_mutex_; - Mutex painter_mutex_; - Mutex render_mutex_; }; #include "Manual_Refresh_Strategy.inl" diff --git a/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.inl b/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.inl index 851225f..9ee2174 100644 --- a/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.inl +++ b/Kernel/src/renderive/frame_control/strategy/manual/Manual_Refresh_Strategy.inl @@ -1,206 +1,239 @@ #pragma once -template -Manual_Refresh_Strategy::Painter_Lease::Painter_Lease(Manual_Refresh_Strategy& strategy) - : strategy_(&strategy), lease_lock_(strategy.painter_mutex_) { - std::lock_guard lock(strategy.state_mutex_); - frame_ = strategy.paint_; +template +struct Manual_Refresh_Strategy::Impl + : Control_Base::template next_Impl { + using Impl_Base = Control_Base::template next_Impl; + + explicit Impl(Manual_Refresh_Strategy& owner) + : Impl_Base(), owner_(&owner), paint_(&frames_[0]), + pending_(&frames_[1]), render_(&frames_[2]) {} + + [[nodiscard]] std::uint64_t now_ns() const; + void observe(const Observation& observation); + + Frame frames_[3]; + Manual_Refresh_Strategy* owner_; + Frame* paint_; + Frame* pending_; + Frame* render_; + std::uint64_t next_sequence_{}; + std::uint64_t real_time_data_update_sequence_{}; + Real_Time_Data_Update_State last_real_time_data_update_; + State state_; + mutable Mutex state_mutex_; + Mutex painter_mutex_; + Mutex render_mutex_; +}; + +template +Manual_Refresh_Strategy::Painter_Lease::Painter_Lease(Manual_Refresh_Strategy& strategy) + : strategy_(&strategy), lease_lock_(strategy.impl().painter_mutex_) { + std::lock_guard lock(strategy.impl().state_mutex_); + frame_ = strategy.impl().paint_; frame_->statistics = {}; - frame_->statistics.sequence = ++strategy.next_sequence_; - frame_->statistics.real_time_data_update_sequence = strategy.real_time_data_update_sequence_; - frame_->statistics.paint_begin_time_ns = strategy.now_ns(); + frame_->statistics.sequence = ++strategy.impl().next_sequence_; + frame_->statistics.real_time_data_update_sequence = strategy.impl().real_time_data_update_sequence_; + frame_->statistics.paint_begin_time_ns = strategy.impl().now_ns(); } -template -Manual_Refresh_Strategy::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept +template +Manual_Refresh_Strategy::Painter_Lease::Painter_Lease(Painter_Lease&& other) noexcept : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::exchange(other.frame_, nullptr)), lease_lock_(std::move(other.lease_lock_)) {} -template -Manual_Refresh_Strategy::Painter_Lease::~Painter_Lease() { +template +Manual_Refresh_Strategy::Painter_Lease::~Painter_Lease() { if (!strategy_) { return; } - frame_->statistics.paint_end_time_ns = strategy_->now_ns(); + frame_->statistics.paint_end_time_ns = strategy_->impl().now_ns(); frame_->statistics.paint_duration_ns = frame_->statistics.paint_end_time_ns - frame_->statistics.paint_begin_time_ns; Observation replaced_observation; Observation prepared_observation; bool replaced{}; { - std::lock_guard lock(strategy_->state_mutex_); - if (strategy_->state_.pending_frame) { - ++strategy_->state_.replaced_prepared_frame_count; - replaced_observation = {Observation_Event::prepared_replaced, strategy_->pending_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + std::lock_guard lock(strategy_->impl().state_mutex_); + if (strategy_->impl().state_.pending_frame) { + ++strategy_->impl().state_.replaced_prepared_frame_count; + replaced_observation = {Observation_Event::prepared_replaced, strategy_->impl().pending_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; replaced = true; } - std::swap(strategy_->paint_, strategy_->pending_); - strategy_->state_.pending_frame = true; - ++strategy_->state_.prepared_frame_count; - prepared_observation = {Observation_Event::prepared, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + std::swap(strategy_->impl().paint_, strategy_->impl().pending_); + strategy_->impl().state_.pending_frame = true; + ++strategy_->impl().state_.prepared_frame_count; + prepared_observation = {Observation_Event::prepared, frame_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; } lease_lock_.unlock(); if (replaced) { - strategy_->observe(replaced_observation); + strategy_->impl().observe(replaced_observation); } - strategy_->observe(prepared_observation); + strategy_->impl().observe(prepared_observation); } -template -Manual_Refresh_Strategy::Painter_Lease::operator bool() const { +template +Manual_Refresh_Strategy::Painter_Lease::operator bool() const { return frame_ != nullptr; } -template -auto Manual_Refresh_Strategy::Painter_Lease::operator->() -> Frame* { +template +auto Manual_Refresh_Strategy::Painter_Lease::operator->() -> Frame* { return frame_; } -template -auto Manual_Refresh_Strategy::Painter_Lease::operator->() const -> const Frame* { +template +auto Manual_Refresh_Strategy::Painter_Lease::operator->() const -> const Frame* { return frame_; } -template -auto Manual_Refresh_Strategy::Painter_Lease::operator*() -> Frame& { +template +auto Manual_Refresh_Strategy::Painter_Lease::operator*() -> Frame& { return *frame_; } -template -auto Manual_Refresh_Strategy::Painter_Lease::operator*() const -> const Frame& { +template +auto Manual_Refresh_Strategy::Painter_Lease::operator*() const -> const Frame& { return *frame_; } -template -auto Manual_Refresh_Strategy::Painter_Lease::get() -> Frame* { +template +auto Manual_Refresh_Strategy::Painter_Lease::get() -> Frame* { return frame_; } -template -auto Manual_Refresh_Strategy::Painter_Lease::get() const -> const Frame* { +template +auto Manual_Refresh_Strategy::Painter_Lease::get() const -> const Frame* { return frame_; } -template -Manual_Refresh_Strategy::Render_Lease::Render_Lease(Manual_Refresh_Strategy& strategy) - : strategy_(&strategy), lease_lock_(strategy.render_mutex_) { - std::lock_guard lock(strategy.state_mutex_); - frame_ = strategy.state_.render_frame ? strategy.render_ : nullptr; +template +Manual_Refresh_Strategy::Render_Lease::Render_Lease(Manual_Refresh_Strategy& strategy) + : strategy_(&strategy), lease_lock_(strategy.impl().render_mutex_) { + std::lock_guard lock(strategy.impl().state_mutex_); + frame_ = strategy.impl().state_.render_frame ? strategy.impl().render_ : nullptr; if (frame_) { - frame_->statistics.render_begin_time_ns = strategy.now_ns(); + frame_->statistics.render_begin_time_ns = strategy.impl().now_ns(); } } -template -Manual_Refresh_Strategy::Render_Lease::Render_Lease(Render_Lease&& other) noexcept +template +Manual_Refresh_Strategy::Render_Lease::Render_Lease(Render_Lease&& other) noexcept : strategy_(std::exchange(other.strategy_, nullptr)), frame_(std::exchange(other.frame_, nullptr)), lease_lock_(std::move(other.lease_lock_)) {} -template -Manual_Refresh_Strategy::Render_Lease::~Render_Lease() { +template +Manual_Refresh_Strategy::Render_Lease::~Render_Lease() { if (!strategy_ || !frame_) { return; } - frame_->statistics.render_end_time_ns = strategy_->now_ns(); + frame_->statistics.render_end_time_ns = strategy_->impl().now_ns(); frame_->statistics.render_duration_ns = frame_->statistics.render_end_time_ns - frame_->statistics.render_begin_time_ns; Observation observation; { - std::lock_guard lock(strategy_->state_mutex_); - ++strategy_->state_.render_count; - observation = {Observation_Event::rendered, frame_->statistics, strategy_->state_, strategy_->last_real_time_data_update_}; + std::lock_guard lock(strategy_->impl().state_mutex_); + ++strategy_->impl().state_.render_count; + observation = {Observation_Event::rendered, frame_->statistics, strategy_->impl().state_, strategy_->impl().last_real_time_data_update_}; } lease_lock_.unlock(); - strategy_->observe(observation); + strategy_->impl().observe(observation); } -template -Manual_Refresh_Strategy::Render_Lease::operator bool() const { +template +Manual_Refresh_Strategy::Render_Lease::operator bool() const { return frame_ != nullptr; } -template -auto Manual_Refresh_Strategy::Render_Lease::operator->() -> Frame* { +template +auto Manual_Refresh_Strategy::Render_Lease::operator->() -> Frame* { return frame_; } -template -auto Manual_Refresh_Strategy::Render_Lease::operator->() const -> const Frame* { +template +auto Manual_Refresh_Strategy::Render_Lease::operator->() const -> const Frame* { return frame_; } -template -auto Manual_Refresh_Strategy::Render_Lease::operator*() -> Frame& { +template +auto Manual_Refresh_Strategy::Render_Lease::operator*() -> Frame& { return *frame_; } -template -auto Manual_Refresh_Strategy::Render_Lease::operator*() const -> const Frame& { +template +auto Manual_Refresh_Strategy::Render_Lease::operator*() const -> const Frame& { return *frame_; } -template -auto Manual_Refresh_Strategy::Render_Lease::get() -> Frame* { +template +auto Manual_Refresh_Strategy::Render_Lease::get() -> Frame* { return frame_; } -template -auto Manual_Refresh_Strategy::Render_Lease::get() const -> const Frame* { +template +auto Manual_Refresh_Strategy::Render_Lease::get() const -> const Frame* { return frame_; } -template -Manual_Refresh_Strategy::Manual_Refresh_Strategy(Observer observer) - : Frame_Control_Strategy_Base(), observer_(std::move(observer)), paint_(&frames_[0]), pending_(&frames_[1]), render_(&frames_[2]) {} -template -auto Manual_Refresh_Strategy::acquire_painter() -> Painter_Lease { +template +Manual_Refresh_Strategy::Manual_Refresh_Strategy(Observer observer) + : Control_Base(std::make_unique(*this)), + observer_(std::move(observer)) {} +template +auto Manual_Refresh_Strategy::acquire_painter() -> Painter_Lease { return Painter_Lease(*this); } -template -auto Manual_Refresh_Strategy::acquire_renderer() -> Render_Lease { +template +auto Manual_Refresh_Strategy::acquire_renderer() -> Render_Lease { return Render_Lease(*this); } -template -void Manual_Refresh_Strategy::swap() { - publish_frame_control_state(invalid_frequency_hz(), 0); +template +void Manual_Refresh_Strategy::swap() { } -template -Manual_Refresh_Result Manual_Refresh_Strategy::refresh() { - std::unique_lock render_lock(render_mutex_); +template +double Manual_Refresh_Strategy::frequency_hz() const { + return this->invalid_frequency_hz(); +} +template +std::uint64_t +Manual_Refresh_Strategy::next_refresh_interval_ns() + const { + return 0; +} +template +Manual_Refresh_Result Manual_Refresh_Strategy::refresh() { + std::unique_lock render_lock(impl().render_mutex_); Observation observation; bool refreshed{}; { - std::lock_guard lock(state_mutex_); - if (state_.pending_frame) { - std::swap(pending_, render_); - state_.pending_frame = false; - state_.render_frame = true; - state_.render_frame_sequence = render_->statistics.sequence; - ++state_.successful_refresh_count; - observation = {Observation_Event::refresh_succeeded, render_->statistics, state_, last_real_time_data_update_}; + std::lock_guard lock(impl().state_mutex_); + if (impl().state_.pending_frame) { + std::swap(impl().pending_, impl().render_); + impl().state_.pending_frame = false; + impl().state_.render_frame = true; + impl().state_.render_frame_sequence = impl().render_->statistics.sequence; + ++impl().state_.successful_refresh_count; + observation = {Observation_Event::refresh_succeeded, impl().render_->statistics, impl().state_, impl().last_real_time_data_update_}; refreshed = true; } else { - ++state_.failed_refresh_count; - observation = {Observation_Event::refresh_failed, {}, state_, last_real_time_data_update_}; + ++impl().state_.failed_refresh_count; + observation = {Observation_Event::refresh_failed, {}, impl().state_, impl().last_real_time_data_update_}; } } render_lock.unlock(); - observe(observation); + impl().observe(observation); return refreshed ? Manual_Refresh_Result::none : Manual_Refresh_Result::no_pending_frame; } -template -bool Manual_Refresh_Strategy::discard_pending_frame() { +template +bool Manual_Refresh_Strategy::discard_pending_frame() { Observation observation; { - std::lock_guard lock(state_mutex_); - if (!state_.pending_frame) { + std::lock_guard lock(impl().state_mutex_); + if (!impl().state_.pending_frame) { return false; } - state_.pending_frame = false; - ++state_.discarded_prepared_frame_count; - observation = {Observation_Event::manually_discarded, pending_->statistics, state_, last_real_time_data_update_}; + impl().state_.pending_frame = false; + ++impl().state_.discarded_prepared_frame_count; + observation = {Observation_Event::manually_discarded, impl().pending_->statistics, impl().state_, impl().last_real_time_data_update_}; } - observe(observation); + impl().observe(observation); return true; } -template -auto Manual_Refresh_Strategy::state() const -> State { - std::lock_guard lock(state_mutex_); - return state_; -} -template -void Manual_Refresh_Strategy::on_real_time_data_update(const Real_Time_Data_Observation& observation) { +template +void Manual_Refresh_Strategy::on_real_time_data_update(const Real_Time_Data_Observation& observation) { Observation strategy_observation; { - std::lock_guard lock(state_mutex_); - ++real_time_data_update_sequence_; - state_.real_time_data_update_sequence = real_time_data_update_sequence_; - last_real_time_data_update_ = observation.state; - strategy_observation = {Observation_Event::real_time_data_updated, {}, state_, last_real_time_data_update_}; + std::lock_guard lock(impl().state_mutex_); + ++impl().real_time_data_update_sequence_; + impl().state_.real_time_data_update_sequence = impl().real_time_data_update_sequence_; + impl().last_real_time_data_update_ = observation.state; + strategy_observation = {Observation_Event::real_time_data_updated, {}, impl().state_, impl().last_real_time_data_update_}; } - observe(strategy_observation); + impl().observe(strategy_observation); } -template -std::uint64_t Manual_Refresh_Strategy::now_ns() const { - return observer_.now_ns(); +template +std::uint64_t +Manual_Refresh_Strategy::Impl::now_ns() const { + return owner_->observer_.now_ns(); } -template -void Manual_Refresh_Strategy::observe(const Observation& observation) { - observer_.observe(observation); +template +void Manual_Refresh_Strategy::Impl::observe( + const Observation& observation) { + owner_->observer_.observe(observation); } diff --git a/Kernel/src/renderive/real_time_data/Attach_Real_Time_Data.hpp b/Kernel/src/renderive/real_time_data/Attach_Real_Time_Data.hpp index e108590..fbf8651 100644 --- a/Kernel/src/renderive/real_time_data/Attach_Real_Time_Data.hpp +++ b/Kernel/src/renderive/real_time_data/Attach_Real_Time_Data.hpp @@ -70,7 +70,11 @@ public: if (real_time_data_discard_mode.load(std::memory_order_acquire) != Real_Time_Data_Discard_Mode::retain_frame_interval) { return 0; } - const std::uint64_t interval_ns = Frame_Control_Strategy_Base::frequency_interval_ns(this->d_func().scene().frame_control_strategy().frequency_hz()); + const auto strategy = + this->d_func().scene().frame_control_strategy(); + const std::uint64_t interval_ns = + Frame_Control_Strategy_Base::frequency_interval_ns( + strategy->frequency_hz()); if (interval_ns == 0) { return 0; } diff --git a/Kernel/src/renderive/real_time_data/Frame_Strategy_Observer.cpp b/Kernel/src/renderive/real_time_data/Frame_Strategy_Observer.cpp index 272a551..3bbc026 100644 --- a/Kernel/src/renderive/real_time_data/Frame_Strategy_Observer.cpp +++ b/Kernel/src/renderive/real_time_data/Frame_Strategy_Observer.cpp @@ -138,11 +138,14 @@ void Frame_Strategy_Real_Time_Data_Observer::observe( auto lease = lifetime->acquire(); if (!lease) continue; - auto& strategy = lease.scene().frame_control_strategy(); - strategy.on_real_time_data_update(observation); - const double frequency_hz = strategy.frequency_hz(); + auto strategy = lease.scene().frame_control_strategy(); + strategy->on_real_time_data_update(observation); + const double frequency_hz = strategy->frequency_hz(); if (discard_stale_frame && std::isfinite(frequency_hz) && - frequency_hz > 0.0) - strategy.discard_stale_latest_data_frame(); + frequency_hz > 0.0) { + if (auto* discard_strategy = dynamic_cast< + Stale_Latest_Data_Frame_Discard_Strategy*>(strategy.get())) + discard_strategy->discard_stale_latest_data_frame(); + } } } diff --git a/Kernel/src/renderive/renderable/Inheritance.hpp b/Kernel/src/renderive/renderable/Inheritance.hpp index 94da11d..c3fdcc7 100644 --- a/Kernel/src/renderive/renderable/Inheritance.hpp +++ b/Kernel/src/renderive/renderable/Inheritance.hpp @@ -3,6 +3,7 @@ #include "renderive/inheritance/Inheritance.hpp" #include +#include #include namespace renderive::renderable_inheritance { @@ -11,6 +12,19 @@ struct Builder_Chain {}; struct State_Chain {}; struct Impl_Chain {}; +struct Builder_Access { + class Key { + Key() = default; + friend struct Builder_Access; + }; + + template + [[nodiscard]] static std::shared_ptr make(Args&&... args) { + return std::shared_ptr( + new Product(Key{}, std::forward(args)...)); + } +}; + template struct Builder_Root : inheritance::Type_Node {}; @@ -56,8 +70,6 @@ struct Observer_Handler { static void handle(Layer& layer, const Event& event) requires requires { typename Layer::Observer; - typename Layer::Observer::Owner; - requires std::same_as; Layer::Observer::handle(layer, event); } { @@ -120,6 +132,10 @@ protected: using next_Impl = renderable_inheritance::Impl_Node< Next, typename Parent::Impl>; + template + using next_Observer = renderable_inheritance::Observer_Node< + Next, typename Parent::Observer>; + using Parent::Parent; }; @@ -170,6 +186,15 @@ public: } } +private: + template + explicit attach(renderable_inheritance::Builder_Access::Key, + Args&&... args) + : Control(std::forward(args)...) {} + + friend struct renderable_inheritance::Builder_Access; + +protected: using Control::Control; }; diff --git a/Kernel/src/renderive/renderable/base/Renderable_Base.cpp b/Kernel/src/renderive/renderable/base/Renderable_Base.cpp index f5d1be6..ca663f6 100644 --- a/Kernel/src/renderive/renderable/base/Renderable_Base.cpp +++ b/Kernel/src/renderive/renderable/base/Renderable_Base.cpp @@ -1,62 +1,42 @@ #include #include "Renderable_Base_p.hpp" - #include #include #include #include - #include "renderive/real_time_data/base/Real_Time_Data_Base.hpp" #include "renderive/renderable/Renderive_Id_Allocator.hpp" #include "renderive/scene/base/Scene_Base.hpp" - +#include "renderive/scene/base/Scene_Base.inl" namespace { - void advance_revision(std::atomic& revision) noexcept { std::uint64_t value = revision.load(std::memory_order_relaxed); while (value != std::numeric_limits::max() && - !revision.compare_exchange_weak(value, value + 1, - std::memory_order_release, - std::memory_order_relaxed)) {} + !revision.compare_exchange_weak(value, value + 1, + std::memory_order_release, + std::memory_order_relaxed)) {} } - } // namespace - Renderable_Base::Impl::Impl() : renderable_id(allocate_renderive_renderable_id()), composite_node_id(allocate_renderive_node_id()) {} - Renderable_Base::Impl::~Impl() { real_time_data_state->attached.store(false, std::memory_order_release); real_time_data_state->renderable_alive.store(false, std::memory_order_release); } - -void Renderable_Base::Impl::initialize( - Renderable_Base& value, - Renderable_Configuration initial_configuration, - std::pmr::memory_resource& resource) noexcept { - owner_ = &value; - memory_resource_pointer = &resource; - configuration.store(initial_configuration, std::memory_order_relaxed); -} - Renderable_Base& Renderable_Base::Impl::owner() noexcept { return *owner_; } - const Renderable_Base& Renderable_Base::Impl::owner() const noexcept { return *owner_; } - void Renderable_Base::Impl::invalidate_prepare() noexcept { advance_revision(prepare_revision); } - void Renderable_Base::Impl::invalidate_paint() noexcept { advance_revision(paint_revision); } - void Renderable_Base::Impl::bind_scene( std::shared_ptr lifetime) { auto& current = real_time_data_state->scene_lifetime; @@ -65,27 +45,25 @@ void Renderable_Base::Impl::bind_scene( else if (current != lifetime) ::renderive::error::unexpected("renderable belongs to another scene"); } - void Renderable_Base::Impl::rebuild_render_graph() { if (!attached()) { reset_render_graph(); return; } - scene().request_render_graph_rebuild(owner()); + scene().d_func().request_render_graph_rebuild(owner()); } - void Renderable_Base::Impl::reset_render_graph() { std::lock_guard lock(render_graph_mutex); render_graph.reset(); invalidate_prepare(); } - std::shared_ptr Renderable_Base::Impl::render_graph_snapshot() { std::lock_guard lock(render_graph_mutex); if (!render_graph) { std::pmr::unordered_map next_identities{ - &memory_resource()}; + &memory_resource() + }; Renderable_Graph_Builder builder( renderable_id, [this, &next_identities](std::string_view key) { @@ -95,9 +73,9 @@ Renderable_Base::Impl::render_graph_snapshot() { "duplicate render node logical key"); const auto current = node_identities.find(logical_key); const Render_Node_Id node_id = - current == node_identities.end() - ? allocate_renderive_node_id() - : current->second; + current == node_identities.end() + ? allocate_renderive_node_id() + : current->second; next_identities.emplace(logical_key, node_id); return node_id; }); @@ -110,12 +88,10 @@ Renderable_Base::Impl::render_graph_snapshot() { } return render_graph; } - std::pmr::memory_resource& Renderable_Base::Impl::memory_resource() const noexcept { return *memory_resource_pointer; } - Scene_Base& Renderable_Base::Impl::scene() const { const auto lifetime = real_time_data_state->scene_lifetime; if (!lifetime) @@ -125,35 +101,30 @@ Scene_Base& Renderable_Base::Impl::scene() const { ::renderive::error::unexpected("renderable scene is no longer alive"); return lease.scene(); } - bool Renderable_Base::Impl::attached() const noexcept { return real_time_data_state->attached.load(std::memory_order_acquire); } - void Renderable_Base::Impl::set_configuration( Renderable_Configuration value) { const Renderable_Configuration previous = - configuration.load(std::memory_order_acquire); + configuration.load(std::memory_order_acquire); if (previous == value) return; configuration.store(value, std::memory_order_release); invalidate_prepare(); notify_scene_model_dirty(); } - void Renderable_Base::Impl::set_visible(bool value) { if (visible.exchange(value, std::memory_order_acq_rel) == value) return; invalidate_prepare(); notify_scene_model_dirty(); } - void Renderable_Base::Impl::discard_stale_frame_on_latest_data_update( bool enabled) noexcept { real_time_data_state->discard_stale_frame_on_latest_data_update.store( enabled, std::memory_order_release); } - void Renderable_Base::Impl::notify_scene_model_dirty() { const auto& state = real_time_data_state; if (!state->attached.load(std::memory_order_acquire) || @@ -163,7 +134,6 @@ void Renderable_Base::Impl::notify_scene_model_dirty() { if (lease) lease.scene().notify_model_dirty(); } - void Renderable_Base::Impl::build_prepare_graph( Renderable_Graph_Builder& builder) { builder.emplace("prepare", "Prepare", @@ -171,40 +141,31 @@ void Renderable_Base::Impl::build_prepare_graph( execute_prepare(context); }); } - void Renderable_Base::Impl::build_paint_graph( Renderable_Graph_Builder&) {} - void Renderable_Base::Impl::prepare(const Prepare_Render_Context&) {} - void Renderable_Base::Impl::capture_frame_data(Frame_Render_Snapshot&) const {} - void Renderable_Base::Impl::add_prepare_action(Prepare_Action action) { if (!action) ::renderive::error::unexpected("renderable prepare action is empty"); prepare_actions.push_back(std::move(action)); } - void Renderable_Base::Impl::execute_prepare( const Prepare_Render_Context& context) { for (const auto& action : prepare_actions) action(); prepare(context); } - Render_State_View Renderable_Base::Impl::render_state_view() noexcept { return {}; } - Real_Time_Data_Binding Renderable_Base::Impl::bind_real_time_data( Real_Time_Data_Base& data) { return data.bind_renderable(owner()); } - void Renderable_Base::Impl::mark_prepared(std::uint64_t revision) noexcept { prepared_revision.store(revision, std::memory_order_release); } - void Renderable_Base::Impl::mark_painted( std::uint64_t current_paint_revision, std::uint64_t current_prepare_revision) noexcept { @@ -212,7 +173,6 @@ void Renderable_Base::Impl::mark_painted( std::memory_order_release); painted_revision.store(current_paint_revision, std::memory_order_release); } - void Renderable_Base::Impl::register_real_time_data( Real_Time_Data_Base& data) { std::lock_guard lock(real_time_data_mutex); @@ -220,74 +180,62 @@ void Renderable_Base::Impl::register_real_time_data( real_time_data.end()) real_time_data.push_back(&data); } - void Renderable_Base::Impl::unregister_real_time_data( Real_Time_Data_Base& data) { std::lock_guard lock(real_time_data_mutex); std::erase(real_time_data, &data); } - void Renderable_Base::Impl::publish_real_time_data() { std::lock_guard lock(real_time_data_mutex); for (Real_Time_Data_Base* data : real_time_data) data->publish_render_state(); } - Renderable_Base::Renderable_Base(Renderable_Configuration configuration, std::pmr::memory_resource& memory_resource) : Renderable_Base(std::make_unique(), configuration, memory_resource) {} - Renderable_Base::Renderable_Base(std::unique_ptr implementation, Renderable_Configuration configuration, std::pmr::memory_resource& memory_resource) : d_ptr(std::move(implementation)) { if (!d_ptr) ::renderive::error::unexpected("renderable implementation is null"); - d_ptr->initialize(*this, configuration, memory_resource); + d_ptr->owner_ = this; + d_ptr->memory_resource_pointer = &memory_resource; + d_ptr->configuration.store(configuration, std::memory_order_release); } - Renderable_Base::~Renderable_Base() = default; - Renderable_Id Renderable_Base::renderable_id() const noexcept { return d_func().renderable_id; } - bool Renderable_Base::is_visible() const noexcept { return d_func().visible.load(std::memory_order_acquire); } - std::string Renderable_Base::object_name() const { const auto& implementation = d_func(); std::lock_guard lock(implementation.metadata_mutex); return implementation.object_name; } - void Renderable_Base::set_object_name(std::string name) { auto& implementation = d_func(); std::lock_guard lock(implementation.metadata_mutex); implementation.object_name = std::move(name); } - void Renderable_Base::set_visible(bool visible) { d_func().set_visible(visible); } - void Renderable_Base::discard_stale_frame_on_latest_data_update( bool enabled) noexcept { d_func().discard_stale_frame_on_latest_data_update(enabled); } - void Renderable_Base::Impl::changed() { invalidate_prepare(); notify_scene_model_dirty(); } - void Renderable_Base::Impl::paint_changed() { invalidate_paint(); notify_scene_model_dirty(); } - void Renderable_Base::Impl::render_graph_changed() { rebuild_render_graph(); } diff --git a/Kernel/src/renderive/renderable/base/Renderable_Base_p.hpp b/Kernel/src/renderive/renderable/base/Renderable_Base_p.hpp index 88ef0ec..6d91c63 100644 --- a/Kernel/src/renderive/renderable/base/Renderable_Base_p.hpp +++ b/Kernel/src/renderive/renderable/base/Renderable_Base_p.hpp @@ -1,8 +1,6 @@ #pragma once - #include "Renderable_Base.hpp" #include "renderive/inheritance/Inheritance.hpp" - #include #include #include @@ -10,11 +8,9 @@ #include #include #include - #include "renderive/renderable/Renderable_Graph_Builder.hpp" #include "renderive/scene/base/Scene_Lifetime.hpp" #include "renderive/state/Render_State_View.hpp" - class State_Strategy_Base; class Renderable_Base::Impl : public renderive::inheritance::Type_Root { public: @@ -24,15 +20,9 @@ public: std::atomic attached{}; std::atomic discard_stale_frame_on_latest_data_update{}; }; - using Prepare_Action = std::function; - Impl(); virtual ~Impl(); - - void initialize(Renderable_Base& owner, - Renderable_Configuration initial_configuration, - std::pmr::memory_resource& resource) noexcept; void invalidate_prepare() noexcept; void invalidate_paint() noexcept; void changed(); @@ -61,23 +51,22 @@ public: void add_prepare_action(Prepare_Action action); void execute_prepare(const Prepare_Render_Context& context); virtual void capture_frame_data(Frame_Render_Snapshot& snapshot) const; - protected: [[nodiscard]] Renderable_Base& owner() noexcept; [[nodiscard]] const Renderable_Base& owner() const noexcept; virtual void build_prepare_graph(Renderable_Graph_Builder& builder); virtual void build_paint_graph(Renderable_Graph_Builder& builder); virtual void prepare(const Prepare_Render_Context& context); - private: + friend class Renderable_Base; Renderable_Base* owner_{}; - public: - std::pmr::memory_resource* memory_resource_pointer{ - std::pmr::get_default_resource()}; + std::pmr::get_default_resource() + }; std::shared_ptr real_time_data_state{ - std::make_shared()}; + std::make_shared() + }; State_Strategy_Base* state_strategy{}; const Renderable_Id renderable_id; const Render_Node_Id composite_node_id; diff --git a/Kernel/src/renderive/scene/Inheritance.hpp b/Kernel/src/renderive/scene/Inheritance.hpp index 1171fbb..e18ff2b 100644 --- a/Kernel/src/renderive/scene/Inheritance.hpp +++ b/Kernel/src/renderive/scene/Inheritance.hpp @@ -1,95 +1,126 @@ #pragma once - #include "renderive/inheritance/Inheritance.hpp" - #include - +#include +#include namespace renderive::scene_inheritance { - struct Builder_Chain {}; struct State_Chain {}; struct Impl_Chain {}; struct Editor_Chain {}; - template struct Builder_Root - : inheritance::Type_Node {}; - +: inheritance::Type_Node {}; template struct Builder_Node : inheritance::Type_Node { using Builder_Base = inheritance::Type_Node; using Builder_Base::Builder_Base; }; - template struct State_Root - : inheritance::Type_Node {}; - +: inheritance::Type_Node {}; template struct State_Node : inheritance::Type_Node { using State_Base = inheritance::Type_Node; using State_Base::State_Base; }; - template struct Editor_Root - : inheritance::Type_Node {}; - +: inheritance::Type_Node {}; template struct Editor_Node : inheritance::Type_Node { using Editor_Base = inheritance::Type_Node; using Editor_Base::Editor_Base; }; - template struct Observer_Root { using Owner = Layer; }; - template struct Observer_Node : Parent { using Owner = Layer; }; - template struct Observer_Handler { using Owner = Layer; - - static void handle(Layer& layer, const Event& event) - requires requires { - typename Layer::Observer; - typename Layer::Observer::Owner; - requires std::same_as; - Layer::Observer::handle(layer, event); - } - { + static void handle(Layer& layer, const Event& event) requires requires { + typename Layer::Observer; + Layer::Observer::handle(layer, event); + } { Layer::Observer::handle(layer, event); } }; - template struct Impl_Root - : inheritance::Dispatch_Root { +: inheritance::Dispatch_Root { using Impl_Base = - inheritance::Dispatch_Root; + inheritance::Dispatch_Root; using Scene_Event = Event; using Handler = Observer_Handler; using Impl_Base::Impl_Base; }; - template struct Impl_Node - : inheritance::Dispatch_Node { +: inheritance::Dispatch_Node { using Impl_Base = - inheritance::Dispatch_Node; + inheritance::Dispatch_Node; using Scene_Event = typename Parent::Scene_Event; using Handler = Observer_Handler; using next_Observer = Observer_Node; using Impl_Base::Impl_Base; }; - } // namespace renderive::scene_inheritance +namespace renderive::scene { +template +struct scene_base : Parent { +public: + template + Layer& set(Value&& value) { + this->template d_func() + .scene_state.template set(std::forward(value)); + return static_cast(*this); + } + template + [[nodiscard]] auto get() const { + return this->template d_func() + .scene_state.template get(); + } + template requires std::invocable + Layer& update(Update&& operation) { + this->template d_func() + .scene_state.update(std::forward(operation)); + return static_cast(*this); + } + [[nodiscard]] auto state() const { + return this->template d_func() + .scene_state.read([](const typename Layer::State& value) { + return value; + }); + } + struct Observer + : scene_inheritance::Observer_Node {}; +protected: + struct Builder_Layer + : scene_inheritance::Builder_Node< + Builder_Layer, typename Parent::Builder_Layer> {}; + struct State_Layer + : scene_inheritance::State_Node< + State_Layer, typename Parent::State_Layer> {}; + template + using next_State = scene_inheritance::State_Node; + template + using next_Impl = scene_inheritance::Impl_Node; + template + using next_Editor = scene_inheritance::Editor_Node< + Next, typename Parent::Renderable_Editor>; + template + using next_Builder = scene_inheritance::Builder_Node< + Next, Builder_Layer>; + using Parent::Parent; +}; +} // namespace renderive::scene diff --git a/Kernel/src/renderive/scene/base/Scene_Base.cpp b/Kernel/src/renderive/scene/base/Scene_Base.cpp index 8f9075b..da7f35a 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.cpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.cpp @@ -1,6 +1,6 @@ #include #include "Scene_Base.hpp" - +#include "Scene_Base.inl" #include #include #include @@ -13,10 +13,8 @@ #include #include #include - #include #include - #include "renderive/renderable/Render_Frame_Completion.hpp" #include "renderive/renderable/Renderive_Id_Allocator.hpp" #include "renderive/renderable/base/Renderable_Base_p.hpp" @@ -24,15 +22,13 @@ #include "renderive/render_graph/detail/Render_Graph_Runtime.hpp" #include "renderive/scheduling/detail/OneTBB_Runtime.hpp" #include "renderive/state/base/State_Strategy_Base.hpp" - namespace { class Render_Failure_Notification_Error final : public std::runtime_error { public: Render_Failure_Notification_Error(std::exception_ptr render_exception, - std::exception_ptr notification_exception) - : std::runtime_error("render failure notification also failed"), - render_exception(std::move(render_exception)), - notification_exception(std::move(notification_exception)) {} + std::exception_ptr notification_exception) : std::runtime_error("render failure notification also failed"), + render_exception(std::move(render_exception)), + notification_exception(std::move(notification_exception)) {} std::exception_ptr render_exception; std::exception_ptr notification_exception; }; @@ -40,16 +36,14 @@ Scene_Edit_Error relationship_error( renderive::scene::dependency::Mutation_Error error, bool display) { using Error = renderive::scene::dependency::Mutation_Error; switch (error) { - case Error::none: - return Scene_Edit_Error::none; - case Error::endpoint_not_attached: - return Scene_Edit_Error::renderable_not_attached; - case Error::self_reference: - return display ? Scene_Edit_Error::display_self_reference - : Scene_Edit_Error::dependency_self_reference; - case Error::cycle: - return display ? Scene_Edit_Error::display_cycle - : Scene_Edit_Error::dependency_cycle; + case Error::none: return Scene_Edit_Error::none; + case Error::endpoint_not_attached: return Scene_Edit_Error::renderable_not_attached; + case Error::self_reference: return display + ? Scene_Edit_Error::display_self_reference + : Scene_Edit_Error::dependency_self_reference; + case Error::cycle: return display + ? Scene_Edit_Error::display_cycle + : Scene_Edit_Error::dependency_cycle; } ::renderive::error::unexpected("unknown scene relationship mutation error"); } @@ -57,36 +51,31 @@ Scene_Render_Result scene_render_error( renderive::render_graph::detail::Render_Graph_Execution_Result error) { using Error = renderive::render_graph::detail::Render_Graph_Execution_Result; switch (error) { - case Error::none: - return Scene_Render_Result::none; - case Error::cancelled: - return Scene_Render_Result::cancelled; - case Error::deadline_exceeded: - return Scene_Render_Result::deadline_exceeded; - case Error::external_failure: - return Scene_Render_Result::external_failure; + case Error::none: return Scene_Render_Result::none; + case Error::cancelled: return Scene_Render_Result::cancelled; + case Error::deadline_exceeded: return Scene_Render_Result::deadline_exceeded; + case Error::external_failure: return Scene_Render_Result::external_failure; } ::renderive::error::unexpected("unknown render graph execution error"); } } - -class Scene_Base::Execution_Context { +class Scene_Base::Impl::Execution_Context { public: using Message = oneapi::tbb::flow::continue_msg; using Operation = std::function; - explicit Execution_Context(Scene_Base& scene) - : scene_(scene), - node_(graph_, oneapi::tbb::flow::serial, [this](const Message&) { - try { - execute_available(); - } catch (...) { - scheduled_.store(false, std::memory_order_release); - scene_.record_pending_exception(::renderive::error::capture( - "executing queued scene operation", - std::current_exception())); - } - return Message{}; - }) { + explicit Execution_Context(Impl& scene) : scene_(scene), + node_(graph_, oneapi::tbb::flow::serial, [this](const Message&) { + try { + execute_available(); + } + catch (...) { + scheduled_.store(false, std::memory_order_release); + scene_.record_pending_exception(::renderive::error::capture( + "executing queued scene operation", + std::current_exception())); + } + return Message{}; + }) { operations_.set_capacity(default_capacity); } ~Execution_Context() { @@ -99,18 +88,18 @@ public: slots_.acquire(); const auto waited = std::chrono::duration_cast( std::chrono::steady_clock::now() - started).count(); - if (waited > 0) - backpressure_wait_ns_.fetch_add(static_cast(waited), std::memory_order_relaxed); + if (waited > 0) backpressure_wait_ns_.fetch_add(static_cast(waited), std::memory_order_relaxed); } const std::size_t queued = queued_.fetch_add(1, std::memory_order_relaxed) + 1; update_peak(peak_queued_, queued); - if (!operations_.try_push(std::move(operation))) - ::renderive::error::unexpected("scene execution queue rejected admitted operation"); + if (!operations_.try_push(std::move(operation))) ::renderive::error::unexpected("scene execution queue rejected admitted operation"); schedule(); } void wait() { auto& arena = renderive::scheduling::detail::OneTBB_Runtime::instance().arena(); - arena.execute([this] { graph_.wait_for_all(); }); + arena.execute([this] { + graph_.wait_for_all(); + }); } [[nodiscard]] Scene_Execution_Statistics statistics() const noexcept { return { @@ -128,10 +117,11 @@ private: current, value, std::memory_order_relaxed)) {} } void schedule() { - if (scheduled_.exchange(true, std::memory_order_acq_rel)) - return; + if (scheduled_.exchange(true, std::memory_order_acq_rel)) return; auto& arena = renderive::scheduling::detail::OneTBB_Runtime::instance().arena(); - const bool accepted = arena.execute([this] { return node_.try_put(Message{}); }); + const bool accepted = arena.execute([this] { + return node_.try_put(Message{}); + }); if (!accepted) { scheduled_.store(false, std::memory_order_release); ::renderive::error::unexpected("scene execution graph rejected work"); @@ -153,7 +143,7 @@ private: } } static constexpr std::ptrdiff_t default_capacity = 64; - Scene_Base& scene_; + Impl& scene_; std::counting_semaphore slots_{default_capacity}; oneapi::tbb::flow::graph graph_; oneapi::tbb::concurrent_bounded_queue operations_; @@ -164,20 +154,16 @@ private: std::atomic_uint64_t backpressure_wait_ns_{}; std::atomic_bool scheduled_{}; }; -class Scene_Base::Render_Execution_Scope { +class Scene_Base::Impl::Render_Execution_Scope { public: - explicit Render_Execution_Scope(Scene_Base& scene) noexcept - : previous_(std::exchange(active_execution_scene_, &scene)) {} - + explicit Render_Execution_Scope(Impl& scene) noexcept : previous_(std::exchange(active_execution_scene_, &scene.scene())) {} ~Render_Execution_Scope() { active_execution_scene_ = previous_; } - private: Scene_Base* previous_{}; }; - -struct Scene_Base::Compiled_Render_Plan { +struct Scene_Base::Impl::Compiled_Render_Plan { struct Renderable_Key { Renderable_Id id{}; std::shared_ptr graph; @@ -189,46 +175,40 @@ struct Scene_Base::Compiled_Render_Plan { std::vector execution_bindings; std::unique_ptr runtime; }; -class Scene_Base::Renderable_Edit_Transaction { +class Scene_Base::Impl::Renderable_Edit_Transaction { public: - explicit Renderable_Edit_Transaction(Scene_Base& scene) - : scene_(scene), dependency_(scene.dependency_resolver_.resolve()), - model_dirty_(scene.model_dirty_.load(std::memory_order_acquire)) { + explicit Renderable_Edit_Transaction(Impl& scene) : scene_(scene), dependency_(scene.dependency_resolver_.resolve()), + model_dirty_(scene.model_dirty_.load(std::memory_order_acquire)) { renderables_.reserve(scene.renderables_.size()); color_caches_.reserve(scene.color_caches_.size()); for (const auto& [id, renderable] : scene.renderables_) { renderables_.emplace_back(id, renderable); capture(renderable); } - for (const auto& [id, cache] : scene.color_caches_) - color_caches_.emplace_back(id, cache); - if (scene.raster_capabilities_) - display_ = scene.raster_capabilities_->resolve(); + for (const auto& [id, cache] : scene.color_caches_) color_caches_.emplace_back(id, cache); + if (const auto* relationships = scene.composition()) display_ = relationships->resolve(); } void capture(const Renderable& renderable) { - if (!renderable) - return; + if (!renderable) return; if (std::ranges::any_of(renderable_states_, [&](const auto& state) { - return state.renderable.get() == renderable.get(); - })) + return state.renderable.get() == renderable.get(); + })) return; auto& data = renderable->d_func(); renderable_states_.push_back({ renderable, data.real_time_data_state->scene_lifetime, data.real_time_data_state->attached.load(std::memory_order_acquire), - data.prepare_revision.load(std::memory_order_acquire)}); + data.prepare_revision.load(std::memory_order_acquire) + }); } void rollback() { scene_.renderables_.clear(); - for (const auto& [id, renderable] : renderables_) - scene_.renderables_.emplace(id, renderable); + for (const auto& [id, renderable] : renderables_) scene_.renderables_.emplace(id, renderable); scene_.dependency_resolver_.assign(dependency_); - if (scene_.raster_capabilities_ && display_) - scene_.raster_capabilities_->restore(*display_); + if (auto* relationships = scene_.composition(); relationships && display_) relationships->restore(*display_); scene_.color_caches_.clear(); - for (const auto& [id, cache] : color_caches_) - scene_.color_caches_.emplace(id, cache); + for (const auto& [id, cache] : color_caches_) scene_.color_caches_.emplace(id, cache); for (const auto& state : renderable_states_) { auto& data = state.renderable->d_func(); data.real_time_data_state->scene_lifetime = state.scene_lifetime; @@ -244,7 +224,7 @@ private: bool attached{}; std::uint64_t prepare_revision{}; }; - Scene_Base& scene_; + Impl& scene_; std::vector> renderables_; renderive::scene::dependency::Resolution dependency_; std::optional> display_; @@ -252,194 +232,223 @@ private: std::vector renderable_states_; bool model_dirty_{}; }; +Scene_Base::Impl::Impl(std::pmr::memory_resource& upstream_memory_resource) : memory_domain_(std::allocate_shared( + std::pmr::polymorphic_allocator( + &upstream_memory_resource), + upstream_memory_resource)), + renderables_(&memory_domain_->resource()), + dependency_resolver_(memory_domain_->resource()), + color_caches_(&memory_domain_->resource()), + composite_begin_node_id_(allocate_renderive_node_id()), + scene_render_node_id_(allocate_renderive_node_id()) {} +Scene_Base::Impl::~Impl() = default; +void Scene_Base::Impl::initialize(Scene_Base& scene) { + owner_ = &scene; + scene_lifetime_ = std::make_shared(scene); + execution_context_ = std::make_unique(*this); +} +Scene_Base& Scene_Base::Impl::scene() noexcept { + return *owner_; +} +const Scene_Base& Scene_Base::Impl::scene() const noexcept { + return *owner_; +} +std::pmr::memory_resource& Scene_Base::Impl::memory_resource() const noexcept { + return memory_domain_->resource(); +} +std::pmr::memory_resource& Scene_Base::Impl::upstream_memory_resource() const noexcept { + return memory_domain_->upstream(); +} +Scene_Base::Impl::Frame_Control_Capability& Scene_Base::Impl::frame_control() { + auto* capability = dynamic_cast(this); + if (!capability) + ::renderive::error::unexpected( + "scene implementation has no frame-control capability"); + return *capability; +} +const Scene_Base::Impl::Frame_Control_Capability& Scene_Base::Impl::frame_control() const { + const auto* capability = dynamic_cast(this); + if (!capability) + ::renderive::error::unexpected( + "scene implementation has no frame-control capability"); + return *capability; +} +Scene_Base::Impl::Scene_State_Capability& Scene_Base::Impl::scene_state() { + auto* capability = dynamic_cast(this); + if (!capability) + ::renderive::error::unexpected( + "scene implementation has no scene-state capability"); + return *capability; +} +const Scene_Base::Impl::Scene_State_Capability& Scene_Base::Impl::scene_state() const { + const auto* capability = dynamic_cast(this); + if (!capability) + ::renderive::error::unexpected( + "scene implementation has no scene-state capability"); + return *capability; +} +Scene_Base::Impl::Composition_Relationships* Scene_Base::Impl::composition() noexcept { + return dynamic_cast(this); +} +const Scene_Base::Impl::Composition_Relationships* Scene_Base::Impl::composition() const noexcept { + return dynamic_cast(this); +} +Scene_Base::Impl::Raster_Capabilities* Scene_Base::Impl::raster() noexcept { + return dynamic_cast(this); +} +const Scene_Base::Impl::Raster_Capabilities* Scene_Base::Impl::raster() const noexcept { + return dynamic_cast(this); +} Scene_Base::Scene_Base() : Scene_Base(*std::pmr::get_default_resource()) {} -Scene_Base::Scene_Base(std::pmr::memory_resource& upstream_memory_resource) - : Scene_Base(upstream_memory_resource, std::make_unique()) {} +Scene_Base::Scene_Base(std::pmr::memory_resource& upstream_memory_resource) : Scene_Base(upstream_memory_resource, + std::make_unique(upstream_memory_resource)) {} Scene_Base::Scene_Base(std::pmr::memory_resource& upstream_memory_resource, - std::unique_ptr impl) - : scene_lifetime_(std::make_shared(*this)), - memory_domain_(std::allocate_shared(std::pmr::polymorphic_allocator(&upstream_memory_resource), upstream_memory_resource)), - impl_(std::move(impl)), - execution_context_(std::make_unique(*this)), - renderables_(&memory_domain_->resource()), - dependency_resolver_(memory_domain_->resource()), - color_caches_(&memory_domain_->resource()), - composite_begin_node_id_(allocate_renderive_node_id()), - scene_render_node_id_(allocate_renderive_node_id()) { + std::unique_ptr impl) : impl_(std::move(impl)) { + static_cast(upstream_memory_resource); + impl_->initialize(*this); } Scene_Base::~Scene_Base() { - shutdown(); + d_func().shutdown(); } -Scene_Base::Attach_Builder::Attach_Builder(Scene_Base& scene) - : scene_(scene), task_lock_(scene.task_mutex_) { - if (scene.runtime_started_) - ::renderive::error::unexpected("scene attach builder is only available before runtime starts"); +Scene_Base::Attach_Builder::Attach_Builder(Scene_Base& scene) : scene_(scene), task_lock_(scene.d_func().task_mutex_) { + if (scene.d_func().runtime_started_) ::renderive::error::unexpected("scene attach builder is only available before runtime starts"); } Scene_Edit_Error Scene_Base::Attach_Builder::attach(Renderable renderable) { - std::lock_guard lock(scene_.model_mutex_); + std::lock_guard lock(scene_.d_func().model_mutex_); return Renderable_Editor(scene_).attach(std::move(renderable)); } Scene_Edit_Error Scene_Base::Attach_Builder::set_dependency_parent( const Renderable& child, const Renderable& parent) { - std::lock_guard lock(scene_.model_mutex_); + std::lock_guard lock(scene_.d_func().model_mutex_); return Renderable_Editor(scene_).set_dependency_parent(child, parent); } Scene_Edit_Error Scene_Base::Attach_Builder::add_dependency_parent( const Renderable& child, const Renderable& parent) { - std::lock_guard lock(scene_.model_mutex_); + std::lock_guard lock(scene_.d_func().model_mutex_); return Renderable_Editor(scene_).add_dependency_parent(child, parent); } Scene_Edit_Error Scene_Base::Attach_Builder::clear_dependency_parent( const Renderable& child) { - std::lock_guard lock(scene_.model_mutex_); + std::lock_guard lock(scene_.d_func().model_mutex_); return Renderable_Editor(scene_).clear_dependency_parent(child); } -Scene_Base::Attach_Builder Scene_Base::attach_builder() { - return Attach_Builder(*this); -} Scene_Edit_Error Scene_Base::Renderable_Editor::fail( Scene_Edit_Error error) noexcept { - if (error_ == Scene_Edit_Error::none) - error_ = error; + if (error_ == Scene_Edit_Error::none) error_ = error; return error_; } Scene_Edit_Error Scene_Base::Renderable_Editor::attach(Renderable renderable) { - if (error_ != Scene_Edit_Error::none) - return error_; - if (!renderable) - return fail(Scene_Edit_Error::null_renderable); - scene_.capture_edit_renderable(renderable); + if (error_ != Scene_Edit_Error::none) return error_; + if (!renderable) return fail(Scene_Edit_Error::null_renderable); + auto& scene_data = scene_.d_func(); + scene_data.capture_edit_renderable(renderable); const auto lifetime = renderable->d_func().real_time_data_state->scene_lifetime; - if (lifetime && lifetime.get() != scene_.scene_lifetime_.get()) - return fail(Scene_Edit_Error::foreign_renderable); + if (lifetime && lifetime.get() != scene_data.scene_lifetime_.get()) return fail(Scene_Edit_Error::foreign_renderable); auto& renderable_data = renderable->d_func(); const Renderable_Id id = renderable_data.renderable_id; - if (const auto existing = scene_.renderables_.find(id); - existing != scene_.renderables_.end()) { - if (existing->second != renderable) - ::renderive::error::unexpected("renderable id collision"); + if (const auto existing = scene_data.renderables_.find(id); existing != scene_data.renderables_.end()) { + if (existing->second != renderable) ::renderive::error::unexpected("renderable id collision"); return Scene_Edit_Error::none; } - auto cache = scene_.raster_capabilities_ - ? scene_.raster_capabilities_->make_renderable_color_cache() - : std::shared_ptr{}; - renderable_data.bind_scene(scene_.scene_lifetime_); - if (!scene_.dependency_resolver_.contains(id)) - scene_.dependency_resolver_.attach(id); - if (scene_.raster_capabilities_) - scene_.raster_capabilities_->attach(id); - if (!scene_.renderables_.try_emplace(id, renderable).second) - ::renderive::error::unexpected("renderable id collision"); + auto* raster = scene_data.raster(); + auto cache = raster + ? raster->make_renderable_color_cache() + : std::shared_ptr{}; + renderable_data.bind_scene(scene_data.scene_lifetime_); + if (!scene_data.dependency_resolver_.contains(id)) scene_data.dependency_resolver_.attach(id); + if (auto* composition = scene_data.composition()) composition->attach(id); + if (!scene_data.renderables_.try_emplace(id, renderable).second) ::renderive::error::unexpected("renderable id collision"); try { - if (cache && !scene_.color_caches_.try_emplace(id, std::move(cache)).second) - ::renderive::error::unexpected("renderable color cache collision"); - } catch (...) { - scene_.renderables_.erase(id); + if (cache && !scene_data.color_caches_.try_emplace(id, std::move(cache)).second) ::renderive::error::unexpected("renderable color cache collision"); + } + catch (...) { + scene_data.renderables_.erase(id); ::renderive::error::unexpected( "attaching renderable", std::current_exception()); } renderable_data.real_time_data_state->attached.store(true, std::memory_order_release); renderable_data.invalidate_prepare(); - scene_.notify_model_dirty(); + scene_data.model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } Scene_Edit_Error Scene_Base::Renderable_Editor::detach( const Renderable& renderable) { - if (error_ != Scene_Edit_Error::none) - return error_; - if (!renderable) - return fail(Scene_Edit_Error::null_renderable); - scene_.capture_edit_renderable(renderable); - const auto scene_error = scene_.renderable_scene_error(*renderable); - if (scene_error != Scene_Edit_Error::none) - return fail(scene_error); - if (!scene_.is_renderable_attached_locked(renderable)) - return fail(Scene_Edit_Error::renderable_not_attached); + if (error_ != Scene_Edit_Error::none) return error_; + if (!renderable) return fail(Scene_Edit_Error::null_renderable); + auto& scene_data = scene_.d_func(); + scene_data.capture_edit_renderable(renderable); + const auto scene_error = scene_data.renderable_scene_error(*renderable); + if (scene_error != Scene_Edit_Error::none) return fail(scene_error); + if (!scene_data.is_renderable_attached_locked(renderable)) return fail(Scene_Edit_Error::renderable_not_attached); auto& renderable_data = renderable->d_func(); const Renderable_Id id = renderable_data.renderable_id; renderable_data.real_time_data_state->attached.store(false, std::memory_order_release); - scene_.color_caches_.erase(id); - scene_.renderables_.erase(id); - scene_.notify_model_dirty(); + scene_data.color_caches_.erase(id); + scene_data.renderables_.erase(id); + scene_data.model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } Scene_Edit_Error Scene_Base::Renderable_Editor::set_dependency_parent( const Renderable& child, const Renderable& parent) { - if (error_ != Scene_Edit_Error::none) - return error_; - if (!child || !parent) - return fail(Scene_Edit_Error::null_renderable); - scene_.capture_edit_renderable(child); - auto scene_error = scene_.renderable_scene_error(*child); - if (scene_error != Scene_Edit_Error::none) - return fail(scene_error); - scene_error = scene_.renderable_scene_error(*parent); - if (scene_error != Scene_Edit_Error::none) - return fail(scene_error); - if (!scene_.is_renderable_attached_locked(child) || - !scene_.is_renderable_attached_locked(parent)) + if (error_ != Scene_Edit_Error::none) return error_; + if (!child || !parent) return fail(Scene_Edit_Error::null_renderable); + auto& scene_data = scene_.d_func(); + scene_data.capture_edit_renderable(child); + auto scene_error = scene_data.renderable_scene_error(*child); + if (scene_error != Scene_Edit_Error::none) return fail(scene_error); + scene_error = scene_data.renderable_scene_error(*parent); + if (scene_error != Scene_Edit_Error::none) return fail(scene_error); + if (!scene_data.is_renderable_attached_locked(child) || + !scene_data.is_renderable_attached_locked(parent)) return fail(Scene_Edit_Error::renderable_not_attached); - const auto result = scene_.dependency_resolver_.replace_parents( + const auto result = scene_data.dependency_resolver_.replace_parents( child->d_func().renderable_id, {parent->d_func().renderable_id}); - if (!result) - return fail(relationship_error(result.error, false)); - if (!result.changed) - return Scene_Edit_Error::none; + if (!result) return fail(relationship_error(result.error, false)); + if (!result.changed) return Scene_Edit_Error::none; child->d_func().invalidate_prepare(); - scene_.notify_model_dirty(); + scene_data.model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } Scene_Edit_Error Scene_Base::Renderable_Editor::add_dependency_parent( const Renderable& child, const Renderable& parent) { - if (error_ != Scene_Edit_Error::none) - return error_; - if (!child || !parent) - return fail(Scene_Edit_Error::null_renderable); - scene_.capture_edit_renderable(child); - auto scene_error = scene_.renderable_scene_error(*child); - if (scene_error != Scene_Edit_Error::none) - return fail(scene_error); - scene_error = scene_.renderable_scene_error(*parent); - if (scene_error != Scene_Edit_Error::none) - return fail(scene_error); - if (!scene_.is_renderable_attached_locked(child) || - !scene_.is_renderable_attached_locked(parent)) + if (error_ != Scene_Edit_Error::none) return error_; + if (!child || !parent) return fail(Scene_Edit_Error::null_renderable); + auto& scene_data = scene_.d_func(); + scene_data.capture_edit_renderable(child); + auto scene_error = scene_data.renderable_scene_error(*child); + if (scene_error != Scene_Edit_Error::none) return fail(scene_error); + scene_error = scene_data.renderable_scene_error(*parent); + if (scene_error != Scene_Edit_Error::none) return fail(scene_error); + if (!scene_data.is_renderable_attached_locked(child) || + !scene_data.is_renderable_attached_locked(parent)) return fail(Scene_Edit_Error::renderable_not_attached); - const auto result = scene_.dependency_resolver_.add_parent( + const auto result = scene_data.dependency_resolver_.add_parent( child->d_func().renderable_id, parent->d_func().renderable_id); - if (!result) - return fail(relationship_error(result.error, false)); - if (!result.changed) - return Scene_Edit_Error::none; + if (!result) return fail(relationship_error(result.error, false)); + if (!result.changed) return Scene_Edit_Error::none; child->d_func().invalidate_prepare(); - scene_.notify_model_dirty(); + scene_data.model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } Scene_Edit_Error Scene_Base::Renderable_Editor::clear_dependency_parent( const Renderable& child) { - if (error_ != Scene_Edit_Error::none) - return error_; - if (!child) - return fail(Scene_Edit_Error::null_renderable); - scene_.capture_edit_renderable(child); - const auto scene_error = scene_.renderable_scene_error(*child); - if (scene_error != Scene_Edit_Error::none) - return fail(scene_error); - if (!scene_.is_renderable_attached_locked(child)) - return fail(Scene_Edit_Error::renderable_not_attached); - const auto result = scene_.dependency_resolver_.clear_parents( + if (error_ != Scene_Edit_Error::none) return error_; + if (!child) return fail(Scene_Edit_Error::null_renderable); + auto& scene_data = scene_.d_func(); + scene_data.capture_edit_renderable(child); + const auto scene_error = scene_data.renderable_scene_error(*child); + if (scene_error != Scene_Edit_Error::none) return fail(scene_error); + if (!scene_data.is_renderable_attached_locked(child)) return fail(Scene_Edit_Error::renderable_not_attached); + const auto result = scene_data.dependency_resolver_.clear_parents( child->d_func().renderable_id); - if (!result) - return fail(relationship_error(result.error, false)); - if (!result.changed) - return Scene_Edit_Error::none; + if (!result) return fail(relationship_error(result.error, false)); + if (!result.changed) return Scene_Edit_Error::none; child->d_func().invalidate_prepare(); - scene_.notify_model_dirty(); + scene_data.model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } Scene_Edit_Error Scene_Base::Edit_Operation::wait() const { - if (!result_.valid()) - ::renderive::error::unexpected("scene edit operation is invalid"); + if (!result_.valid()) ::renderive::error::unexpected("scene edit operation is invalid"); return result_.get(); } Scene_Base::Edit_Operation Scene_Base::edit_renderables(Renderable_Edit edit) { @@ -448,24 +457,20 @@ Scene_Base::Edit_Operation Scene_Base::edit_renderables(Renderable_Edit edit) { promise.set_value(Scene_Edit_Error::empty_edit); return Edit_Operation(promise.get_future().share()); } - return enqueue_renderable_edit([this, edit = std::move(edit)]() mutable { - Renderable_Editor editor(*this); - edit(editor); - return editor.error(); - }); + return d_func().enqueue_renderable_edit( + [this, edit = std::move(edit)]() mutable { + Renderable_Editor editor(*this); + edit(editor); + return editor.error(); + }); } - -Scene_2D_Base::Scene_2D_Base() - : Scene_2D_Base(*std::pmr::get_default_resource()) {} -Scene_2D_Base::Scene_2D_Base(std::pmr::memory_resource& memory_resource) - : Scene_2D_Base(memory_resource, std::make_unique()) {} +Scene_2D_Base::Scene_2D_Base() : Scene_2D_Base(*std::pmr::get_default_resource()) {} +Scene_2D_Base::Scene_2D_Base(std::pmr::memory_resource& memory_resource) : Scene_2D_Base(memory_resource, std::make_unique(memory_resource)) {} Scene_2D_Base::Scene_2D_Base(std::pmr::memory_resource& memory_resource, - std::unique_ptr impl) - : Scene_Base(memory_resource, std::move(impl)), - display_resolver_(this->memory_resource()) { - bind_raster_capabilities(*this); -} - + std::unique_ptr impl) : Scene_Base(memory_resource, std::move(impl)) {} +Scene_2D_Base::Impl::Impl(std::pmr::memory_resource& memory_resource) : renderive::scene_inheritance::Impl_Node< + Impl, Scene_Base::Impl>(memory_resource), + display_resolver_(this->memory_resource()) {} Scene_2D_Base::Attach_Builder Scene_2D_Base::attach_builder() { return Attach_Builder(*this); } @@ -475,20 +480,21 @@ Scene_Base::Edit_Operation Scene_2D_Base::edit_renderables(Renderable_Edit edit) promise.set_value(Scene_Edit_Error::empty_edit); return Edit_Operation(promise.get_future().share()); } - return enqueue_renderable_edit([this, edit = std::move(edit)]() mutable { - Renderable_Editor editor(*this); - edit(editor); - return editor.error(); - }); + return d_func().enqueue_renderable_edit( + [this, edit = std::move(edit)]() mutable { + Renderable_Editor editor(*this); + edit(editor); + return editor.error(); + }); } -void Scene_2D_Base::with_initial_display_edit(std::function edit) { +void Scene_2D_Base::Impl::with_initial_display_edit(std::function edit) { std::lock_guard lock(model_mutex_); edit(); } Scene_Edit_Error Scene_2D_Base::Attach_Builder::set_display_parent( const Renderable& child, const Renderable& parent) { Scene_Edit_Error result{}; - scene_2d_.with_initial_display_edit([&] { + scene_2d_.d_func().with_initial_display_edit([&] { result = Renderable_Editor(scene_2d_).set_display_parent(child, parent); }); return result; @@ -496,7 +502,7 @@ Scene_Edit_Error Scene_2D_Base::Attach_Builder::set_display_parent( Scene_Edit_Error Scene_2D_Base::Attach_Builder::add_display_parent( const Renderable& child, const Renderable& parent) { Scene_Edit_Error result{}; - scene_2d_.with_initial_display_edit([&] { + scene_2d_.d_func().with_initial_display_edit([&] { result = Renderable_Editor(scene_2d_).add_display_parent(child, parent); }); return result; @@ -504,130 +510,109 @@ Scene_Edit_Error Scene_2D_Base::Attach_Builder::add_display_parent( Scene_Edit_Error Scene_2D_Base::Attach_Builder::clear_display_parent( const Renderable& child) { Scene_Edit_Error result{}; - scene_2d_.with_initial_display_edit([&] { + scene_2d_.d_func().with_initial_display_edit([&] { result = Renderable_Editor(scene_2d_).clear_display_parent(child); }); return result; } Scene_Edit_Error Scene_2D_Base::Renderable_Editor::set_display_parent( const Renderable& child, const Renderable& parent) { - if (error() != Scene_Edit_Error::none) - return error(); - const auto result = scene_2d_.set_display_parent_locked(child, parent); + if (error() != Scene_Edit_Error::none) return error(); + const auto result = scene_2d_.d_func().set_display_parent_locked( + child, parent); return result == Scene_Edit_Error::none ? result : fail(result); } Scene_Edit_Error Scene_2D_Base::Renderable_Editor::add_display_parent( const Renderable& child, const Renderable& parent) { - if (error() != Scene_Edit_Error::none) - return error(); - const auto result = scene_2d_.add_display_parent_locked(child, parent); + if (error() != Scene_Edit_Error::none) return error(); + const auto result = scene_2d_.d_func().add_display_parent_locked( + child, parent); return result == Scene_Edit_Error::none ? result : fail(result); } Scene_Edit_Error Scene_2D_Base::Renderable_Editor::clear_display_parent( const Renderable& child) { - if (error() != Scene_Edit_Error::none) - return error(); - const auto result = scene_2d_.clear_display_parent_locked(child); + if (error() != Scene_Edit_Error::none) return error(); + const auto result = scene_2d_.d_func().clear_display_parent_locked( + child); return result == Scene_Edit_Error::none ? result : fail(result); } -Scene_Edit_Error Scene_2D_Base::set_display_parent_locked( +Scene_Edit_Error Scene_2D_Base::Impl::set_display_parent_locked( const Renderable& child, const Renderable& parent) { - if (!child || !parent) - return Scene_Edit_Error::null_renderable; - auto scene_error = renderable_scene_error(*child); - if (scene_error != Scene_Edit_Error::none) - return scene_error; - scene_error = renderable_scene_error(*parent); - if (scene_error != Scene_Edit_Error::none) - return scene_error; - if (!is_renderable_attached_locked(child) || !is_renderable_attached_locked(parent)) + if (!child || !parent) return Scene_Edit_Error::null_renderable; + auto scene_error = Scene_Base::Impl::renderable_scene_error(*child); + if (scene_error != Scene_Edit_Error::none) return scene_error; + scene_error = Scene_Base::Impl::renderable_scene_error(*parent); + if (scene_error != Scene_Edit_Error::none) return scene_error; + if (!is_renderable_attached_locked(child) || + !is_renderable_attached_locked(parent)) return Scene_Edit_Error::renderable_not_attached; const auto result = display_resolver_.replace_parents( child->renderable_id(), {parent->renderable_id()}); - if (!result) - return relationship_error(result.error, true); - if (result.changed) - notify_model_dirty(); + if (!result) return relationship_error(result.error, true); + if (result.changed) model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } -Scene_Edit_Error Scene_2D_Base::add_display_parent_locked( +Scene_Edit_Error Scene_2D_Base::Impl::add_display_parent_locked( const Renderable& child, const Renderable& parent) { - if (!child || !parent) - return Scene_Edit_Error::null_renderable; - auto scene_error = renderable_scene_error(*child); - if (scene_error != Scene_Edit_Error::none) - return scene_error; - scene_error = renderable_scene_error(*parent); - if (scene_error != Scene_Edit_Error::none) - return scene_error; - if (!is_renderable_attached_locked(child) || !is_renderable_attached_locked(parent)) + if (!child || !parent) return Scene_Edit_Error::null_renderable; + auto scene_error = Scene_Base::Impl::renderable_scene_error(*child); + if (scene_error != Scene_Edit_Error::none) return scene_error; + scene_error = Scene_Base::Impl::renderable_scene_error(*parent); + if (scene_error != Scene_Edit_Error::none) return scene_error; + if (!is_renderable_attached_locked(child) || + !is_renderable_attached_locked(parent)) return Scene_Edit_Error::renderable_not_attached; const auto result = display_resolver_.add_parent( child->renderable_id(), parent->renderable_id()); - if (!result) - return relationship_error(result.error, true); - if (result.changed) - notify_model_dirty(); + if (!result) return relationship_error(result.error, true); + if (result.changed) model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } -Scene_Edit_Error Scene_2D_Base::clear_display_parent_locked( +Scene_Edit_Error Scene_2D_Base::Impl::clear_display_parent_locked( const Renderable& child) { - if (!child) - return Scene_Edit_Error::null_renderable; - const auto scene_error = renderable_scene_error(*child); - if (scene_error != Scene_Edit_Error::none) - return scene_error; - if (!is_renderable_attached_locked(child)) - return Scene_Edit_Error::renderable_not_attached; + if (!child) return Scene_Edit_Error::null_renderable; + const auto scene_error = Scene_Base::Impl::renderable_scene_error(*child); + if (scene_error != Scene_Edit_Error::none) return scene_error; + if (!is_renderable_attached_locked(child)) return Scene_Edit_Error::renderable_not_attached; const auto result = display_resolver_.clear_parents(child->renderable_id()); - if (!result) - return relationship_error(result.error, true); - if (result.changed) - notify_model_dirty(); + if (!result) return relationship_error(result.error, true); + if (result.changed) model_dirty_.store(true, std::memory_order_release); return Scene_Edit_Error::none; } -Scene_3D_Base::Scene_3D_Base() - : Scene_3D_Base(*std::pmr::get_default_resource()) {} -Scene_3D_Base::Scene_3D_Base(std::pmr::memory_resource& memory_resource) - : Scene_3D_Base(memory_resource, std::make_unique()) {} +Scene_3D_Base::Scene_3D_Base() : Scene_3D_Base(*std::pmr::get_default_resource()) {} +Scene_3D_Base::Scene_3D_Base(std::pmr::memory_resource& memory_resource) : Scene_3D_Base(memory_resource, std::make_unique(memory_resource)) {} Scene_3D_Base::Scene_3D_Base(std::pmr::memory_resource& memory_resource, - std::unique_ptr impl) - : Scene_Base(memory_resource, std::move(impl)) {} -Scene_3D_Base::Attach_Builder Scene_3D_Base::attach_builder() { - return Attach_Builder(*this); -} + std::unique_ptr impl) : Scene_Base(memory_resource, std::move(impl)) {} Scene_Base::Edit_Operation Scene_3D_Base::edit_renderables(Renderable_Edit edit) { if (!edit) { std::promise promise; promise.set_value(Scene_Edit_Error::empty_edit); return Edit_Operation(promise.get_future().share()); } - return enqueue_renderable_edit([this, edit = std::move(edit)]() mutable { - Renderable_Editor editor(*this); - edit(editor); - return editor.error(); - }); + return d_func().enqueue_renderable_edit( + [this, edit = std::move(edit)]() mutable { + Renderable_Editor editor(*this); + edit(editor); + return editor.error(); + }); } - Scene_Render_Result Scene_Base::render() { - return submit_render(nullptr); + return d_func().submit_render(nullptr); } Scene_Render_Result Scene_Base::render(Abstract_Frame& frame) { - return submit_render(&frame); + return d_func().submit_render(&frame); } -Scene_Render_Result Scene_Base::submit_render(Abstract_Frame* frame) { - if (active_submitted_observer_scene_ == this) { +Scene_Render_Result Scene_Base::Impl::submit_render(Abstract_Frame* frame) { + if (active_submitted_observer_scene_ == owner_) { std::lock_guard lock(task_mutex_); ++deferred_render_count_; return Scene_Render_Result::none; } auto task_lock = lock_render_idle(); - if (shutting_down_) - return Scene_Render_Result::shutting_down; + if (shutting_down_) return Scene_Render_Result::shutting_down; runtime_started_ = true; std::exception_ptr previous_exception; - if (pending_exception_) - previous_exception = std::exchange(pending_exception_, {}); + if (pending_exception_) previous_exception = std::exchange(pending_exception_, {}); else if (current_completion_ && current_completion_->completed && current_completion_->exception && !current_completion_->observed) { current_completion_->observed = true; previous_exception = current_completion_->exception; @@ -637,38 +622,40 @@ Scene_Render_Result Scene_Base::submit_render(Abstract_Frame* frame) { ::renderive::error::unexpected( "notifying render failure", previous_exception); } - auto& strategy = frame_control_strategy(); - strategy.swap(); + auto strategy = frame_control().frame_control_strategy(); + strategy->swap(); auto snapshot = frame - ? frame->state_->published_snapshot.exchange({}, std::memory_order_acq_rel) - : nullptr; - if (!snapshot) - snapshot = capture_live_frame(); - snapshot->next_refresh_interval_ns_ = strategy.frame_control_state().next_refresh_interval_ns; - if (render_sequence_ == std::numeric_limits::max()) - ::renderive::error::unexpected("render sequence exhausted"); + ? frame->state_->published_snapshot.exchange({}, std::memory_order_acq_rel) + : nullptr; + if (!snapshot) snapshot = capture_live_frame(); + snapshot->next_refresh_interval_ns_ = + strategy->next_refresh_interval_ns(); + if (render_sequence_ == std::numeric_limits::max()) ::renderive::error::unexpected("render sequence exhausted"); snapshot->render_sequence = ++render_sequence_; auto task = std::make_shared(); task->frame = std::make_shared(); - if (frame) - task->frame->shared_state = frame->state_; + if (frame) task->frame->shared_state = frame->state_; task->compiled_plan = compile_render_plan(*snapshot); - task->topology = std::make_shared(topology_snapshot()); + task->topology = + std::make_shared(scene().topology_snapshot()); task->completion = std::make_shared(); const Capture_Frame_Ticket capture_ticket = capture_controller_.begin_frame(); snapshot->capture_ticket_ = capture_ticket; task->snapshot = std::move(snapshot); current_completion_ = task->completion; const Observation submitted_observation{ - Observation_Event::render_submitted, d_func().now_ns(), task->snapshot->render_sequence, + Observation_Event::render_submitted, now_ns(), task->snapshot->render_sequence, task->snapshot->scene_state_revision_, task->snapshot->renderables.size(), task->topology, - task->snapshot, task->compiled_plan->plan}; + task->snapshot, task->compiled_plan->plan + }; ++pending_operations_; task_lock.unlock(); - Scene_Base* previous_observer_scene = std::exchange(active_submitted_observer_scene_, this); + Scene_Base* previous_observer_scene = + std::exchange(active_submitted_observer_scene_, owner_); try { - d_func().dispatch(submitted_observation); - } catch (...) { + dispatch(submitted_observation); + } + catch (...) { active_submitted_observer_scene_ = previous_observer_scene; const auto exception = ::renderive::error::capture( "notifying submitted scene render", std::current_exception()); @@ -690,7 +677,8 @@ Scene_Render_Result Scene_Base::submit_render(Abstract_Frame* frame) { execute_render_task(task); complete_pending_operation(); }); - } catch (...) { + } + catch (...) { capture_controller_.finish_frame(capture_ticket, false); const auto exception = ::renderive::error::capture( "scheduling scene render", std::current_exception()); @@ -709,21 +697,23 @@ Scene_Render_Result Scene_Base::submit_render(Abstract_Frame* frame) { std::lock_guard lock(task_mutex_); deferred_render_count = std::exchange(deferred_render_count_, 0); } - for (std::size_t index = 0; index < deferred_render_count; ++index) - static_cast(render()); + for (std::size_t index = 0; index < deferred_render_count; ++index) static_cast(submit_render(nullptr)); return Scene_Render_Result::none; } Scene_Render_Result Scene_Base::wait_for_render() { - if (is_render_execution_context() || active_submitted_observer_scene_ == this) + auto& implementation = d_func(); + if (implementation.is_render_execution_context() || + Impl::active_submitted_observer_scene_ == this) return Scene_Render_Result::none; - std::unique_lock lock(task_mutex_); - const auto completion = current_completion_; - if (!completion) - return Scene_Render_Result::none; - render_completed_.wait(lock, [&completion] { return completion->completed; }); + std::unique_lock lock(implementation.task_mutex_); + const auto completion = implementation.current_completion_; + if (!completion) return Scene_Render_Result::none; + implementation.render_completed_.wait( + lock, [&completion] { + return completion->completed; + }); std::exception_ptr exception; - if (pending_exception_) - exception = std::exchange(pending_exception_, {}); + if (implementation.pending_exception_) exception = std::exchange(implementation.pending_exception_, {}); else { exception = completion->exception; completion->observed = true; @@ -735,7 +725,7 @@ Scene_Render_Result Scene_Base::wait_for_render() { "waiting for scene idle", exception); return error; } -Scene_Base::Edit_Operation Scene_Base::enqueue_renderable_edit( +Scene_Base::Edit_Operation Scene_Base::Impl::enqueue_renderable_edit( std::function edit) { auto promise = std::make_shared>(); Edit_Operation operation(promise->get_future().share()); @@ -753,7 +743,8 @@ Scene_Base::Edit_Operation Scene_Base::enqueue_renderable_edit( [this, edit = std::move(edit), promise = std::move(promise)]() mutable { try { promise->set_value(execute_renderable_edit(std::move(edit))); - } catch (...) { + } + catch (...) { const auto exception = ::renderive::error::capture( "executing asynchronous renderable edit", std::current_exception()); @@ -761,119 +752,80 @@ Scene_Base::Edit_Operation Scene_Base::enqueue_renderable_edit( } complete_pending_operation(); }); - } catch (...) { + } + catch (...) { complete_pending_operation(); ::renderive::error::unexpected( "observing scene render submission", std::current_exception()); } return operation; } - -void Scene_Base::submit_operation(std::function operation) { - { - std::lock_guard lock(task_mutex_); - if (shutting_down_) - ::renderive::error::unexpected("scene is shutting down"); - runtime_started_ = true; - ++pending_operations_; - } - try { - execution_context_->submit([this, operation = std::move(operation)]() mutable { - try { - operation(); - } catch (...) { - record_pending_exception(::renderive::error::capture( - "executing asynchronous scene operation", - std::current_exception())); - } - complete_pending_operation(); - }); - } catch (...) { - complete_pending_operation(); - ::renderive::error::unexpected( - "submitting scene render", std::current_exception()); - } -} -void Scene_Base::complete_pending_operation() { +void Scene_Base::Impl::complete_pending_operation() { { std::lock_guard lock(task_mutex_); --pending_operations_; } render_completed_.notify_all(); } -void Scene_Base::record_pending_exception(std::exception_ptr exception) { +void Scene_Base::Impl::record_pending_exception(std::exception_ptr exception) { std::lock_guard lock(task_mutex_); - if (!pending_exception_) - pending_exception_ = std::move(exception); + if (!pending_exception_) pending_exception_ = std::move(exception); } -void Scene_Base::capture_edit_renderable(const Renderable& renderable) { - if (active_edit_transaction_) - active_edit_transaction_->capture(renderable); +void Scene_Base::Impl::capture_edit_renderable(const Renderable& renderable) { + if (active_edit_transaction_) active_edit_transaction_->capture(renderable); } - -Scene_Edit_Error Scene_Base::cleanup_detached_topology_locked() { +Scene_Edit_Error Scene_Base::Impl::cleanup_detached_topology_locked() { for (const Renderable_Id id : dependency_resolver_.ids()) { - if (renderables_.contains(id)) - continue; - if (!dependency_resolver_.isolated(id)) - return Scene_Edit_Error::dangling_dependency; + if (renderables_.contains(id)) continue; + if (!dependency_resolver_.isolated(id)) return Scene_Edit_Error::dangling_dependency; dependency_resolver_.erase(id); } - if (raster_capabilities_ && !raster_capabilities_->cleanup()) - return Scene_Edit_Error::dangling_display; + if (auto* relationships = composition(); relationships && !relationships->cleanup()) return Scene_Edit_Error::dangling_display; return Scene_Edit_Error::none; } -Scene_Edit_Error Scene_Base::validate_structure_locked() const { +Scene_Edit_Error Scene_Base::Impl::validate_structure_locked() const { for (const auto& [id, renderable] : renderables_) { - if (!renderable) - ::renderive::error::unexpected("scene contains null renderable owner"); - if (renderable_scene_error(*renderable) != Scene_Edit_Error::none) - ::renderive::error::unexpected("scene contains renderable bound to another scene"); + if (!renderable) ::renderive::error::unexpected("scene contains null renderable owner"); + if (renderable_scene_error(*renderable) != Scene_Edit_Error::none) ::renderive::error::unexpected("scene contains renderable bound to another scene"); if (!renderable->d_func().real_time_data_state->attached.load( - std::memory_order_acquire)) + std::memory_order_acquire)) ::renderive::error::unexpected("scene contains renderable whose attached state is false"); - if (!dependency_resolver_.contains(id)) - ::renderive::error::unexpected("dependency graph is missing attached renderable " + std::to_string(id)); + if (!dependency_resolver_.contains(id)) ::renderive::error::unexpected("dependency graph is missing attached renderable " + std::to_string(id)); } const auto dependency = dependency_resolver_.resolve(); - if (dependency.order.size() != renderables_.size()) - ::renderive::error::unexpected("dependency graph does not match attached renderables"); + if (dependency.order.size() != renderables_.size()) ::renderive::error::unexpected("dependency graph does not match attached renderables"); for (const Renderable_Id id : dependency.order) { - if (!renderables_.contains(id)) - ::renderive::error::unexpected("dependency graph references detached renderable " + std::to_string(id)); + if (!renderables_.contains(id)) ::renderive::error::unexpected("dependency graph references detached renderable " + std::to_string(id)); } - if (raster_capabilities_ && !raster_capabilities_->valid()) - ::renderive::error::unexpected("display graph does not match attached renderables"); + if (const auto* relationships = composition(); relationships && !relationships->valid()) ::renderive::error::unexpected("display graph does not match attached renderables"); return Scene_Edit_Error::none; } - -void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) { +void Scene_Base::Impl::request_render_graph_rebuild( + Renderable_Base& renderable) { auto& renderable_data = renderable.d_func(); if (!renderable_data.real_time_data_state->attached.load( - std::memory_order_acquire)) { + std::memory_order_acquire)) { renderable_data.reset_render_graph(); return; } - if (renderable_scene_error(renderable) != Scene_Edit_Error::none) - ::renderive::error::unexpected("attached renderable scene invariant violated"); - if (active_renderable_edit_scene_ == this) { + if (renderable_scene_error(renderable) != Scene_Edit_Error::none) ::renderive::error::unexpected("attached renderable scene invariant violated"); + if (active_renderable_edit_scene_ == owner_) { renderable_data.reset_render_graph(); - notify_model_dirty(); + model_dirty_.store(true, std::memory_order_release); return; } Renderable owner; { std::lock_guard lock(model_mutex_); const auto iterator = renderables_.find(renderable_data.renderable_id); - if (iterator == renderables_.end() || iterator->second.get() != &renderable) - return; + if (iterator == renderables_.end() || iterator->second.get() != &renderable) return; owner = iterator->second; } { std::lock_guard lock(task_mutex_); if (!runtime_started_) { owner->d_func().reset_render_graph(); - notify_model_dirty(); + model_dirty_.store(true, std::memory_order_release); return; } } @@ -884,19 +836,22 @@ void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) { })); } void Scene_Base::publish_frame_state() { - auto task_lock = lock_render_idle(); - publish_frame_state_locked(); + auto& implementation = d_func(); + auto task_lock = implementation.lock_render_idle(); + implementation.publish_frame_state_locked(); } - void Scene_Base::publish_frame_state(Abstract_Frame& frame) { - auto task_lock = lock_render_idle(); - publish_frame_state_locked(); - frame.state_->published_snapshot.store(capture_live_frame(), - std::memory_order_release); + auto& implementation = d_func(); + auto task_lock = implementation.lock_render_idle(); + implementation.publish_frame_state_locked(); + frame.state_->published_snapshot.store(implementation.capture_live_frame(), + std::memory_order_release); } - -void Scene_Base::publish_frame_state_locked() { - publish_scene_state(); +void Scene_Base::Impl::publish_frame_state_locked() { + auto& state = scene_state(); + state.before_scene_state_swap(); + state.swap_scene_state(); + state.after_scene_state_swap(); std::vector renderables; { std::lock_guard lock(model_mutex_); @@ -907,86 +862,83 @@ void Scene_Base::publish_frame_state_locked() { } } for (const Renderable& renderable : renderables) { - if (auto* state = renderable->d_func().state_strategy) - state->publish(); + if (auto* state = renderable->d_func().state_strategy) state->publish(); renderable->d_func().publish_real_time_data(); } } - void Scene_Base::notify_model_dirty() noexcept { - model_dirty_.store(true, std::memory_order_release); + d_func().model_dirty_.store(true, std::memory_order_release); } - -void Scene_Base::invalidate_renderables() { +void Scene_Base::Impl::invalidate_renderables() { std::lock_guard lock(model_mutex_); for (const auto& [id, renderable] : renderables_) { static_cast(id); renderable->d_func().invalidate_prepare(); } - notify_model_dirty(); + model_dirty_.store(true, std::memory_order_release); } - std::size_t Scene_Base::renderable_count() const { - std::lock_guard lock(model_mutex_); - return renderables_.size(); + const auto& implementation = d_func(); + std::lock_guard lock(implementation.model_mutex_); + return implementation.renderables_.size(); } - Scene_Base::Topology_Snapshot Scene_Base::topology_snapshot() const { - std::lock_guard lock(model_mutex_); + const auto& implementation = d_func(); + std::lock_guard lock(implementation.model_mutex_); Topology_Snapshot snapshot; - const auto dependency = dependency_resolver_.resolve(); - const auto display = raster_capabilities_ - ? raster_capabilities_->resolve() + const auto dependency = implementation.dependency_resolver_.resolve(); + const auto* relationships = implementation.composition(); + const auto display = relationships + ? relationships->resolve() : renderive::scene::dependency::Resolution< - Renderable_Id>{}; + Renderable_Id>{}; const auto& ids = dependency.order; snapshot.renderables.reserve(ids.size()); snapshot.display.reserve(display.relationships.size()); snapshot.dependency.reserve(dependency.relationships.size()); - for (const Renderable_Id id : ids) - snapshot.renderables.push_back(renderables_.at(id)); + for (const Renderable_Id id : ids) snapshot.renderables.push_back(implementation.renderables_.at(id)); const auto append_relationships = [&](const auto& resolution, auto& output) { std::unordered_set children; children.reserve(resolution.relationships.size()); - for (const auto& relationship : resolution.relationships) - children.insert(relationship.child); + for (const auto& relationship : resolution.relationships) children.insert(relationship.child); for (const Renderable_Id id : resolution.order) { - if (!children.contains(id)) - output.push_back({renderables_.at(id), {}}); + if (!children.contains(id)) output.push_back({implementation.renderables_.at(id), {}}); } for (const auto& relationship : resolution.relationships) - output.push_back({renderables_.at(relationship.child), - renderables_.at(relationship.parent)}); + output.push_back({ + implementation.renderables_.at( + relationship.child), + implementation.renderables_.at( + relationship.parent) + }); }; - if (!display.order.empty()) - append_relationships(display, snapshot.display); + if (!display.order.empty()) append_relationships(display, snapshot.display); append_relationships(dependency, snapshot.dependency); return snapshot; } - std::vector Scene_Base::paint_order_snapshot() const { - std::lock_guard lock(model_mutex_); + const auto& implementation = d_func(); + std::lock_guard lock(implementation.model_mutex_); std::vector result; - auto display = raster_capabilities_ - ? raster_capabilities_->resolve() + const auto* relationships = implementation.composition(); + auto display = relationships + ? relationships->resolve() : renderive::scene::dependency::Resolution< - Renderable_Id>{}; - if (display.order.empty()) - display = dependency_resolver_.resolve(); - for (const Renderable_Id id : display.order) - result.push_back(renderables_.at(id)); + Renderable_Id>{}; + if (display.order.empty()) display = implementation.dependency_resolver_.resolve(); + for (const Renderable_Id id : display.order) result.push_back(implementation.renderables_.at(id)); return result; } - -std::shared_ptr Scene_Base::snapshot_live_model() { +std::shared_ptr Scene_Base::Impl::snapshot_live_model() { auto snapshot = std::make_shared(); std::lock_guard lock(model_mutex_); const auto dependency = dependency_resolver_.resolve(); - const auto display = raster_capabilities_ - ? raster_capabilities_->resolve() + const auto* relationships = composition(); + const auto display = relationships + ? relationships->resolve() : renderive::scene::dependency::Resolution< - Renderable_Id>{}; + Renderable_Id>{}; const auto& dependency_order = dependency.order; snapshot->display_order = display.order; snapshot->renderables.reserve(dependency_order.size()); @@ -1007,28 +959,25 @@ std::shared_ptr Scene_Base::snapshot_live_model() { state.painted_revision = renderable_data.painted_revision.load( std::memory_order_acquire); state.painted_prepare_revision = - renderable_data.painted_prepare_revision.load( - std::memory_order_acquire); + renderable_data.painted_prepare_revision.load( + std::memory_order_acquire); const bool prepare_valid = - state.prepare_revision != std::numeric_limits::max() && - state.configuration.cache_enabled && - state.prepared_revision == state.prepare_revision; + state.prepare_revision != std::numeric_limits::max() && + state.configuration.cache_enabled && + state.prepared_revision == state.prepare_revision; const bool paint_valid = - state.paint_revision != std::numeric_limits::max() && - prepare_valid && state.painted_revision == state.paint_revision && - state.painted_prepare_revision == state.prepared_revision; + state.paint_revision != std::numeric_limits::max() && + prepare_valid && state.painted_revision == state.paint_revision && + state.painted_prepare_revision == state.prepared_revision; state.prepare_required = !prepare_valid; state.paint_required = !paint_valid; state.render_graph = renderable_data.render_graph_snapshot(); state.dependency_parent_ids = dependency_resolver_.parents(id); state.owner_ = owner; - if (const auto cache = color_caches_.find(id); - cache != color_caches_.end()) - state.paint_buffer_ = cache->second; + if (const auto cache = color_caches_.find(id); cache != color_caches_.end()) state.paint_buffer_ = cache->second; renderable_data.capture_frame_data(*snapshot); snapshot->renderables.push_back(std::move(state)); } - std::unordered_map indices; indices.reserve(snapshot->renderables.size()); std::unordered_map> children; @@ -1039,20 +988,17 @@ std::shared_ptr Scene_Base::snapshot_live_model() { children.try_emplace(state.renderable_id); } for (const auto& state : snapshot->renderables) { - for (const Renderable_Id parent : state.dependency_parent_ids) - children.at(parent).push_back(state.renderable_id); + for (const Renderable_Id parent : state.dependency_parent_ids) children.at(parent).push_back(state.renderable_id); } std::vector dirty; dirty.reserve(snapshot->renderables.size()); for (const auto& state : snapshot->renderables) { - if (state.prepare_required) - dirty.push_back(state.renderable_id); + if (state.prepare_required) dirty.push_back(state.renderable_id); } for (std::size_t cursor = 0; cursor < dirty.size(); ++cursor) { for (const Renderable_Id child : children.at(dirty[cursor])) { auto& state = snapshot->renderables.at(indices.at(child)); - if (state.prepare_required) - continue; + if (state.prepare_required) continue; state.prepare_required = true; state.paint_required = true; dirty.push_back(child); @@ -1060,172 +1006,140 @@ std::shared_ptr Scene_Base::snapshot_live_model() { } return snapshot; } - -std::shared_ptr Scene_Base::capture_live_frame() { +std::shared_ptr Scene_Base::Impl::capture_live_frame() { auto snapshot = snapshot_live_model(); - snapshot->scene_state_revision_ = acquire_scene_state(); - capture_scene_state(*snapshot); - if (raster_capabilities_) - snapshot->viewport = raster_capabilities_->frame_viewport(); + auto& state = scene_state(); + snapshot->scene_state_revision_ = state.acquire_scene_state(); + state.capture_scene_state(*snapshot); + if (const auto* raster_capabilities = raster()) snapshot->viewport = raster_capabilities->frame_viewport(); return snapshot; } - std::shared_ptr Scene_Base::render_plan_snapshot() const { - return render_plan_history_.current(); + return d_func().render_plan_history_.current(); } - std::shared_ptr Scene_Base::find_render_plan( Render_Plan_Version version) const { - return render_plan_history_.find(version); + return d_func().render_plan_history_.find(version); } - Capture_Request_Result Scene_Base::capture_next_frame() { return capture_frames(1); } Capture_Request_Result Scene_Base::capture_frames(std::size_t count) { - std::lock_guard lock(task_mutex_); - const auto request = capture_controller_.capture_frames(count); - if (!request) - return request; + auto& implementation = d_func(); + std::lock_guard lock(implementation.task_mutex_); + const auto request = implementation.capture_controller_.capture_frames(count); + if (!request) return request; try { - capture_repository_.begin_session(request.session_id, count); - } catch (...) { - capture_controller_.cancel(request.session_id); + implementation.capture_repository_.begin_session(request.session_id, + count); + } + catch (...) { + implementation.capture_controller_.cancel(request.session_id); ::renderive::error::unexpected( "executing renderable edit", std::current_exception()); } return request; } - Capture_Controller_State Scene_Base::capture_state() const noexcept { - return capture_controller_.state(); + return d_func().capture_controller_.state(); } - std::optional Scene_Base::capture_session( Capture_Session_Id session_id) const { - return capture_repository_.session(session_id); + return d_func().capture_repository_.session(session_id); } - std::vector Scene_Base::capture_sessions() const { - return capture_repository_.sessions(); + return d_func().capture_repository_.sessions(); } - std::vector Scene_Base::node_statistics( Capture_Session_Id session_id) const { - const auto captured = capture_repository_.session(session_id); - if (!captured) - return {}; + const auto captured = d_func().capture_repository_.session(session_id); + if (!captured) return {}; std::vector analyses; analyses.reserve(captured->frames.size()); - for (const auto& frame : captured->frames) - analyses.push_back(frame.analysis); + for (const auto& frame : captured->frames) analyses.push_back(frame.analysis); return analyze_node_statistics(analyses); } - std::vector Scene_Base::plan_version_statistics( Capture_Session_Id session_id) const { - const auto captured = capture_repository_.session(session_id); - if (!captured) - return {}; + const auto captured = d_func().capture_repository_.session(session_id); + if (!captured) return {}; std::vector analyses; analyses.reserve(captured->frames.size()); - for (const auto& frame : captured->frames) - analyses.push_back(frame.analysis); + for (const auto& frame : captured->frames) analyses.push_back(frame.analysis); return analyze_plan_versions(analyses); } - Scene_Execution_Statistics Scene_Base::execution_statistics() const noexcept { - return execution_context_->statistics(); + return d_func().execution_context_->statistics(); } - std::pmr::memory_resource& Scene_Base::memory_resource() const noexcept { - return memory_domain_->resource(); + return d_func().memory_resource(); } - std::pmr::memory_resource& Scene_Base::upstream_memory_resource() const noexcept { - return memory_domain_->upstream(); + return d_func().upstream_memory_resource(); } - -Frame_Control_Strategy_Base& Scene_Base::frame_control_strategy() { - return frame_control_strategy_impl(); +std::shared_ptr Scene_Base::frame_control_strategy() { + return d_func().frame_control().frame_control_strategy(); } - -const Frame_Control_Strategy_Base& Scene_Base::frame_control_strategy() const { - return frame_control_strategy_impl(); +std::shared_ptr Scene_Base::frame_control_strategy() const { + return d_func().frame_control().frame_control_strategy(); } - -void Scene_Base::bind_raster_capabilities( - renderive::scene::detail::Raster_Capabilities& capabilities) { - if (raster_capabilities_ != nullptr) - ::renderive::error::unexpected("scene raster capabilities are already bound"); - raster_capabilities_ = &capabilities; +void Scene_2D_Base::Impl::attach(Renderable_Id id) { + if (!display_resolver_.contains(id)) display_resolver_.attach(id); } - -void Scene_2D_Base::attach(Renderable_Id id) { - if (!display_resolver_.contains(id)) - display_resolver_.attach(id); -} -bool Scene_2D_Base::cleanup() { +bool Scene_2D_Base::Impl::cleanup() { for (const Renderable_Id id : display_resolver_.ids()) { - if (renderables_.contains(id)) - continue; - if (!display_resolver_.isolated(id)) - return false; + if (renderables_.contains(id)) continue; + if (!display_resolver_.isolated(id)) return false; display_resolver_.erase(id); } return true; } -bool Scene_2D_Base::valid() const { +bool Scene_2D_Base::Impl::valid() const { const auto display = display_resolver_.resolve(); - if (display.order.size() != renderables_.size()) - return false; + if (display.order.size() != renderables_.size()) return false; return std::ranges::all_of(display.order, [this](Renderable_Id id) { return renderables_.contains(id); }); } - -renderive::scene::dependency::Resolution -Scene_2D_Base::resolve() const { +renderive::scene::dependency::Resolution Scene_2D_Base::Impl::resolve() const { return display_resolver_.resolve(); } -void Scene_2D_Base::restore( +void Scene_2D_Base::Impl::restore( const renderive::scene::dependency::Resolution& resolution) { display_resolver_.assign(resolution); } - std::uint64_t Scene_Base::Impl::now_ns() const noexcept { return render_clock_now_ns(); } - -std::unique_lock Scene_Base::lock_render_idle() { - if (is_render_execution_context()) - ::renderive::error::unexpected("render-idle operation is not allowed during render execution"); - if (active_submitted_observer_scene_ == this) - ::renderive::error::unexpected("render-idle operation is not allowed during render submission observation"); +std::unique_lock Scene_Base::Impl::lock_render_idle() { + if (is_render_execution_context()) ::renderive::error::unexpected("render-idle operation is not allowed during render execution"); + if (active_submitted_observer_scene_ == owner_) ::renderive::error::unexpected("render-idle operation is not allowed during render submission observation"); std::unique_lock lock(task_mutex_); - render_completed_.wait(lock, [this] { return pending_operations_ == 0; }); + render_completed_.wait(lock, [this] { + return pending_operations_ == 0; + }); return lock; } -bool Scene_Base::is_render_execution_context() const noexcept { - return active_execution_scene_ == this; +bool Scene_Base::Impl::is_render_execution_context() const noexcept { + return active_execution_scene_ == owner_; } -bool Scene_Base::consume_model_dirty() noexcept { +bool Scene_Base::Impl::consume_model_dirty() noexcept { return model_dirty_.exchange(false, std::memory_order_acq_rel); } -void Scene_Base::shutdown() { +void Scene_Base::Impl::shutdown() { std::shared_ptr compiled_plan; { std::lock_guard lock(task_mutex_); - if (shutting_down_) - return; + if (shutting_down_) return; shutting_down_ = true; compiled_plan = compiled_render_plan_; } - if (compiled_plan && compiled_plan->runtime) - compiled_plan->runtime->cancel_pending(); + if (compiled_plan && compiled_plan->runtime) compiled_plan->runtime->cancel_pending(); { std::unique_lock lock(task_mutex_); - render_completed_.wait(lock, [this] { return pending_operations_ == 0; }); + render_completed_.wait(lock, [this] { + return pending_operations_ == 0; + }); } execution_context_->wait(); { @@ -1237,24 +1151,23 @@ void Scene_Base::shutdown() { } scene_lifetime_->invalidate(); } -Scene_Edit_Error Scene_Base::execute_renderable_edit( +Scene_Edit_Error Scene_Base::Impl::execute_renderable_edit( std::function edit) { std::lock_guard lock(model_mutex_); Renderable_Edit_Transaction transaction(*this); active_edit_transaction_ = &transaction; - Scene_Base* previous = std::exchange(active_renderable_edit_scene_, this); + Scene_Base* previous = + std::exchange(active_renderable_edit_scene_, owner_); try { Scene_Edit_Error error = edit(); - if (error == Scene_Edit_Error::none) - error = cleanup_detached_topology_locked(); - if (error == Scene_Edit_Error::none) - error = validate_structure_locked(); - if (error != Scene_Edit_Error::none) - transaction.rollback(); + if (error == Scene_Edit_Error::none) error = cleanup_detached_topology_locked(); + if (error == Scene_Edit_Error::none) error = validate_structure_locked(); + if (error != Scene_Edit_Error::none) transaction.rollback(); active_renderable_edit_scene_ = previous; active_edit_transaction_ = nullptr; return error; - } catch (...) { + } + catch (...) { const auto exception = std::current_exception(); transaction.rollback(); active_renderable_edit_scene_ = previous; @@ -1263,38 +1176,44 @@ Scene_Edit_Error Scene_Base::execute_renderable_edit( "executing scene render task", exception); } } - -void Scene_Base::execute_render_task(std::shared_ptr task) { +void Scene_Base::Impl::execute_render_task(std::shared_ptr task) { Render_Execution_Scope scope(*this); const auto& snapshot = *task->snapshot; Scene_Render_Result error{}; std::exception_ptr exception; bool render_graph_entered{}; try { - d_func().dispatch({Observation_Event::render_started, d_func().now_ns(), snapshot.render_sequence, - snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology, - task->snapshot, task->compiled_plan->plan}); + dispatch({ + Observation_Event::render_started, now_ns(), snapshot.render_sequence, + snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology, + task->snapshot, task->compiled_plan->plan + }); render_graph_entered = true; error = execute_render_graph(*task); const auto event = error == Scene_Render_Result::none - ? Observation_Event::render_completed - : Observation_Event::render_cancelled; - d_func().dispatch({event, d_func().now_ns(), snapshot.render_sequence, - snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology, - task->snapshot, task->compiled_plan->plan}); - } catch (...) { + ? Observation_Event::render_completed + : Observation_Event::render_cancelled; + dispatch({ + event, now_ns(), snapshot.render_sequence, + snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology, + task->snapshot, task->compiled_plan->plan + }); + } + catch (...) { exception = ::renderive::error::capture( "executing scene render task", std::current_exception()); - if (!render_graph_entered) - capture_controller_.finish_frame(snapshot.capture_ticket_, false); + if (!render_graph_entered) capture_controller_.finish_frame(snapshot.capture_ticket_, false); try { - d_func().dispatch({Observation_Event::render_failed, d_func().now_ns(), snapshot.render_sequence, - snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology, - task->snapshot, task->compiled_plan->plan}); - } catch (...) { + dispatch({ + Observation_Event::render_failed, now_ns(), snapshot.render_sequence, + snapshot.scene_state_revision_, snapshot.renderables.size(), task->topology, + task->snapshot, task->compiled_plan->plan + }); + } + catch (...) { exception = ::renderive::error::capture< Render_Failure_Notification_Error>( - std::move(exception), std::current_exception()); + std::move(exception), std::current_exception()); } } { @@ -1305,8 +1224,7 @@ void Scene_Base::execute_render_task(std::shared_ptr task) { } render_completed_.notify_all(); } - -std::shared_ptr Scene_Base::compile_render_plan( +std::shared_ptr Scene_Base::Impl::compile_render_plan( const Frame_Render_Snapshot& snapshot) { const auto matches = [this, &snapshot] { if (!compiled_render_plan_ || @@ -1323,14 +1241,14 @@ std::shared_ptr Scene_Base::compile_render_pla } return true; }; - if (matches()) - return compiled_render_plan_; + if (matches()) return compiled_render_plan_; const auto update_key = [&snapshot](Compiled_Render_Plan& compiled) { compiled.renderables.clear(); compiled.renderables.reserve(snapshot.renderables.size()); for (const auto& state : snapshot.renderables) { compiled.renderables.push_back({ - state.renderable_id, state.render_graph, state.dependency_parent_ids}); + state.renderable_id, state.render_graph, state.dependency_parent_ids + }); } compiled.display_order = snapshot.display_order; }; @@ -1340,7 +1258,6 @@ std::shared_ptr Scene_Base::compile_render_pla std::vector prepare_terminals; std::vector paint_terminals; }; - std::vector active(snapshot.renderables.size()); std::unordered_map indices; indices.reserve(snapshot.renderables.size()); @@ -1348,39 +1265,33 @@ std::shared_ptr Scene_Base::compile_render_pla active[index].snapshot_index = index; indices.emplace(snapshot.renderables[index].renderable_id, index); } - Render_Graph graph; std::unordered_map functions; std::unordered_map> - graph_successors; + graph_successors; const auto append_edge = [&graph, &graph_successors](Render_Node_Id from, - Render_Node_Id to) { - if (from == 0 || to == 0 || from == to) - return; - if (graph_successors[from].insert(to).second) - graph.edges.push_back({from, to}); + Render_Node_Id to) { + if (from == 0 || to == 0 || from == to) return; + if (graph_successors[from].insert(to).second) graph.edges.push_back({from, to}); }; - for (auto& item : active) { const auto& state = snapshot.renderables[item.snapshot_index]; - if (!state.render_graph) - ::renderive::error::unexpected("frame renderable has no graph snapshot"); + if (!state.render_graph) ::renderive::error::unexpected("frame renderable has no graph snapshot"); for (const auto& node : state.render_graph->graph.nodes) { if (node.kind == Render_Node_Kind::composite) ::renderive::error::unexpected( "renderable graph must not define composite nodes"); graph.nodes.push_back(node); const auto& render_function = - state.render_graph->functions.at(node.execution_index); + state.render_graph->functions.at(node.execution_index); std::visit([&](const auto& function) { functions.emplace(node.node_id, - Execution_Binding{item.snapshot_index, Execution_Function{function}}); + Execution_Binding{item.snapshot_index, Execution_Function{function}}); }, render_function); } std::unordered_map local_kinds; local_kinds.reserve(state.render_graph->graph.nodes.size()); - for (const auto& node : state.render_graph->graph.nodes) - local_kinds.emplace(node.node_id, node.kind); + for (const auto& node : state.render_graph->graph.nodes) local_kinds.emplace(node.node_id, node.kind); std::unordered_set prepare_has_predecessor; std::unordered_set prepare_has_successor; std::unordered_set paint_has_successor; @@ -1402,90 +1313,94 @@ std::shared_ptr Scene_Base::compile_render_pla } for (const auto& node : state.render_graph->graph.nodes) { if (node.kind == Render_Node_Kind::prepare) { - if (!prepare_has_predecessor.contains(node.node_id)) - item.prepare_roots.push_back(node.node_id); - if (!prepare_has_successor.contains(node.node_id)) - item.prepare_terminals.push_back(node.node_id); - } else if (node.kind == Render_Node_Kind::paint && - !paint_has_successor.contains(node.node_id)) { + if (!prepare_has_predecessor.contains(node.node_id)) item.prepare_roots.push_back(node.node_id); + if (!prepare_has_successor.contains(node.node_id)) item.prepare_terminals.push_back(node.node_id); + } + else if (node.kind == Render_Node_Kind::paint && + !paint_has_successor.contains(node.node_id)) { item.paint_terminals.push_back(node.node_id); } } } - for (const auto& child : active) { const auto& child_state = - snapshot.renderables[child.snapshot_index]; + snapshot.renderables[child.snapshot_index]; for (const Renderable_Id parent_id : child_state.dependency_parent_ids) { const auto parent = indices.find(parent_id); - if (parent == indices.end()) - continue; + if (parent == indices.end()) continue; for (const Render_Node_Id parent_terminal : active[parent->second].prepare_terminals) { - for (const Render_Node_Id child_root : child.prepare_roots) - append_edge(parent_terminal, child_root); + for (const Render_Node_Id child_root : child.prepare_roots) append_edge(parent_terminal, child_root); } } } - - if (auto* compositor = dynamic_cast(this)) { - graph.nodes.push_back({composite_begin_node_id_, 0, "Composite Frame", - Render_Node_Kind::composite, 0}); + if (auto* compositor = dynamic_cast(this)) { + graph.nodes.push_back({ + composite_begin_node_id_, 0, "Composite Frame", + Render_Node_Kind::composite, 0 + }); functions.emplace(composite_begin_node_id_, - Execution_Binding{std::nullopt, - Composite_Render_Node_Function{ - [compositor](const Composite_Render_Context& context) { - compositor->begin_composite(context); - }}}); + Execution_Binding{ + std::nullopt, + Composite_Render_Node_Function{ + [compositor](const Composite_Render_Context& context) { + compositor->begin_composite(context); + } + } + }); Render_Node_Id previous = composite_begin_node_id_; for (const Renderable_Id id : snapshot.display_order) { const std::size_t index = indices.at(id); const auto& state = snapshot.renderables[index]; const Render_Node_Id composite_node_id = - state.owner_->d_func().composite_node_id; - graph.nodes.push_back({composite_node_id, id, - "Composite", Render_Node_Kind::composite, 0}); + state.owner_->d_func().composite_node_id; + graph.nodes.push_back({ + composite_node_id, id, + "Composite", Render_Node_Kind::composite, 0 + }); functions.emplace(composite_node_id, - Execution_Binding{index, - Composite_Render_Node_Function{ - [compositor](const Composite_Render_Context& context) { - compositor->composite(context); - }}}); + Execution_Binding{ + index, + Composite_Render_Node_Function{ + [compositor](const Composite_Render_Context& context) { + compositor->composite(context); + } + } + }); append_edge(previous, composite_node_id); previous = composite_node_id; - for (const Render_Node_Id paint : active[index].paint_terminals) - append_edge(paint, composite_node_id); + for (const Render_Node_Id paint : active[index].paint_terminals) append_edge(paint, composite_node_id); } } - - if (auto* renderer = dynamic_cast(this)) { + if (auto* renderer = dynamic_cast(this)) { std::vector terminals; terminals.reserve(graph.nodes.size()); for (const auto& node : graph.nodes) { const auto successors = graph_successors.find(node.node_id); - if (successors == graph_successors.end() || successors->second.empty()) - terminals.push_back(node.node_id); + if (successors == graph_successors.end() || successors->second.empty()) terminals.push_back(node.node_id); } - graph.nodes.push_back({scene_render_node_id_, 0, "Render Scene", - Render_Node_Kind::render, 0}); + graph.nodes.push_back({ + scene_render_node_id_, 0, "Render Scene", + Render_Node_Kind::render, 0 + }); functions.emplace( scene_render_node_id_, - Execution_Binding{std::nullopt, + Execution_Binding{ + std::nullopt, Scene_Render_Node_Function{ [renderer](const Scene_Render_Context& context) { return renderer->render_scene(context); - }}}); - for (const Render_Node_Id terminal : terminals) - append_edge(terminal, scene_render_node_id_); + } + } + }); + for (const Render_Node_Id terminal : terminals) append_edge(terminal, scene_render_node_id_); } - auto plan = render_plan_history_.publish(std::move(graph)); std::vector execution_bindings(plan->graph.nodes.size()); for (const auto& node : plan->graph.nodes) { auto function = functions.find(node.node_id); - if (function == functions.end()) - ::renderive::error::unexpected("render plan node has no execution binding"); + if (function == functions.end()) ::renderive::error::unexpected("render plan node has no execution binding"); execution_bindings[node.execution_index] = std::move(function->second); } if (compiled_render_plan_ && compiled_render_plan_->plan == plan) { @@ -1502,20 +1417,16 @@ std::shared_ptr Scene_Base::compile_render_pla compiled_render_plan_ = compiled; return compiled; } - -Scene_Render_Result Scene_Base::execute_render_graph(Render_Task& task) { - if (!task.snapshot || !task.compiled_plan || !task.frame) - ::renderive::error::unexpected("render task is incomplete"); +Scene_Render_Result Scene_Base::Impl::execute_render_graph(Render_Task& task) { + if (!task.snapshot || !task.compiled_plan || !task.frame) ::renderive::error::unexpected("render task is incomplete"); auto& frame = *task.frame; const auto& snapshot = *task.snapshot; const Capture_Frame_Ticket capture_ticket = snapshot.capture_ticket_; bool frame_active{}; try { for (const auto& state : snapshot.renderables) { - if (state.paint_required && state.paint_buffer_) - state.paint_buffer_->clear(); + if (state.paint_required && state.paint_buffer_) state.paint_buffer_->clear(); } - const bool capture = capture_ticket.capture; const auto& compiled = *task.compiled_plan; Abstract_Frame::begin_render(frame, snapshot.render_sequence, @@ -1526,8 +1437,7 @@ Scene_Render_Result Scene_Base::execute_render_graph(Render_Task& task) { compiled.plan->graph.nodes.size()); for (const auto& node : compiled.plan->graph.nodes) execution_slots[node.execution_index] = - Abstract_Frame::execution_slot(frame, node.execution_index); - + Abstract_Frame::execution_slot(frame, node.execution_index); const auto graph_error = compiled.runtime->execute( execution_slots, [this, &compiled, &snapshot, &execution_slots]( @@ -1536,50 +1446,52 @@ Scene_Render_Result Scene_Base::execute_render_graph(Render_Task& task) { const auto& node = compiled.plan->graph.nodes.at(execution_index); const auto& binding = compiled.execution_bindings.at(execution_index); Node_Diagnostic_Sink diagnostics; - if (auto* execution = execution_slots.at(execution_index)) - diagnostics = Node_Diagnostic_Sink(execution->attachments); + if (auto* execution = execution_slots.at(execution_index)) diagnostics = Node_Diagnostic_Sink(execution->attachments); Render_Execution_Scope scope(*this); switch (node.kind) { case Render_Node_Kind::prepare: { const auto& state = snapshot.renderables.at( binding.renderable_index.value()); - if (!state.prepare_required) - return Node_Execution_Result::completed(); + if (!state.prepare_required) return Node_Execution_Result::completed(); std::get(binding.function)( - Prepare_Render_Context{snapshot, state, metrics, - diagnostics}); + Prepare_Render_Context{ + snapshot, state, metrics, + diagnostics + }); return Node_Execution_Result::completed(); } case Render_Node_Kind::paint: { const auto& state = snapshot.renderables.at( binding.renderable_index.value()); - if (!state.paint_required) - return Node_Execution_Result::completed(); + if (!state.paint_required) return Node_Execution_Result::completed(); if (!state.paint_buffer_) ::renderive::error::unexpected( "paint node has no frame color cache"); std::get(binding.function)( - Paint_Render_Context{snapshot, state, - *state.paint_buffer_, metrics, - diagnostics}); + Paint_Render_Context{ + snapshot, state, + *state.paint_buffer_, metrics, + diagnostics + }); return Node_Execution_Result::completed(); } case Render_Node_Kind::composite: { const Renderable_Frame_State* state = - binding.renderable_index - ? &snapshot.renderables.at( - *binding.renderable_index) - : nullptr; - const Color_Cache* cache = state && state->paint_buffer_ - ? state->paint_buffer_.get() + binding.renderable_index + ? &snapshot.renderables.at( + *binding.renderable_index) : nullptr; + const Color_Cache* cache = state && state->paint_buffer_ + ? state->paint_buffer_.get() + : nullptr; std::get(binding.function)( - Composite_Render_Context{snapshot, state, cache, - metrics, diagnostics}); + Composite_Render_Context{ + snapshot, state, cache, + metrics, diagnostics + }); return Node_Execution_Result::completed(); } - case Render_Node_Kind::render: - return std::get(binding.function)( + case Render_Node_Kind::render: return std::get(binding.function)( Scene_Render_Context{snapshot, metrics, diagnostics}); } ::renderive::error::unexpected("unknown render node kind"); @@ -1591,8 +1503,7 @@ Scene_Render_Result Scene_Base::execute_render_graph(Render_Task& task) { return scene_render_error(graph_error); } for (const auto& state : snapshot.renderables) { - if (state.prepare_required) - state.owner_->d_func().mark_prepared(state.prepare_revision); + if (state.prepare_required) state.owner_->d_func().mark_prepared(state.prepare_revision); if (state.paint_required) state.owner_->d_func().mark_painted(state.paint_revision, state.prepare_revision); @@ -1601,10 +1512,9 @@ Scene_Render_Result Scene_Base::execute_render_graph(Render_Task& task) { frame, capture ? render_clock_now_ns() : 0); frame_active = false; for (const auto& state : snapshot.renderables) { - if (!state.paint_required) - continue; + if (!state.paint_required) continue; if (auto* completion = dynamic_cast( - state.owner_->d_ptr.get())) { + state.owner_->d_ptr.get())) { completion->render_frame_completed( snapshot.next_refresh_interval_ns_); } @@ -1616,30 +1526,26 @@ Scene_Render_Result Scene_Base::execute_render_graph(Render_Task& task) { } capture_controller_.finish_frame(capture_ticket, true); return Scene_Render_Result::none; - } catch (...) { - if (frame_active) - Abstract_Frame::discard_render(frame); + } + catch (...) { + if (frame_active) Abstract_Frame::discard_render(frame); capture_controller_.finish_frame(capture_ticket, false); ::renderive::error::unexpected( "notifying scene render completion", std::current_exception()); } } - -Scene_Edit_Error Scene_Base::renderable_scene_error( +Scene_Edit_Error Scene_Base::Impl::renderable_scene_error( const Renderable_Base& renderable) const noexcept { const auto& lifetime = renderable.d_func().real_time_data_state->scene_lifetime; - if (!lifetime) - return Scene_Edit_Error::renderable_not_attached; + if (!lifetime) return Scene_Edit_Error::renderable_not_attached; return lifetime.get() == scene_lifetime_.get() - ? Scene_Edit_Error::none - : Scene_Edit_Error::foreign_renderable; + ? Scene_Edit_Error::none + : Scene_Edit_Error::foreign_renderable; } - -bool Scene_Base::is_renderable_attached_locked( +bool Scene_Base::Impl::is_renderable_attached_locked( const Renderable& renderable) const { - if (!renderable) - return false; + if (!renderable) return false; const auto iterator = renderables_.find(renderable->d_func().renderable_id); return iterator != renderables_.end() && - iterator->second.get() == renderable.get(); + iterator->second.get() == renderable.get(); } diff --git a/Kernel/src/renderive/scene/base/Scene_Base.hpp b/Kernel/src/renderive/scene/base/Scene_Base.hpp index ef654e2..3af1a90 100644 --- a/Kernel/src/renderive/scene/base/Scene_Base.hpp +++ b/Kernel/src/renderive/scene/base/Scene_Base.hpp @@ -1,5 +1,4 @@ #pragma once - #include #include #include @@ -16,7 +15,6 @@ #include #include #include - #include "renderive/base/memory/Memory_Resource.hpp" #include "renderive/capture/Capture.hpp" #include "renderive/frame_control/base/Frame_Control_Strategy_Base.hpp" @@ -32,9 +30,8 @@ #include "Frame_Viewport.hpp" #include "Render_Contexts.hpp" #include "Scene_Lifetime.hpp" - +#include "renderive/base/Concepts.hpp" class Color_Cache; - enum class Scene_Render_Result : std::uint8_t { none, cancelled, @@ -56,39 +53,6 @@ enum class Scene_Edit_Error : std::uint8_t { dangling_display, shutting_down }; - -namespace renderive::scene::detail { - -struct Composition_Relationships { - virtual ~Composition_Relationships() = default; - virtual void attach(Renderable_Id id) = 0; - [[nodiscard]] virtual bool cleanup() = 0; - [[nodiscard]] virtual bool valid() const = 0; - virtual dependency::Resolution resolve() const = 0; - virtual void restore(const dependency::Resolution& resolution) = 0; -}; - -struct Raster_Capabilities : Composition_Relationships { - virtual std::shared_ptr make_renderable_color_cache() = 0; - virtual Frame_Viewport frame_viewport() const = 0; -}; - -} // namespace renderive::scene::detail - -class Scene_Compositor { -public: - virtual ~Scene_Compositor() = default; - virtual void begin_composite(const Composite_Render_Context& context) = 0; - virtual void composite(const Composite_Render_Context& context) = 0; -}; - -class Scene_Renderer { -public: - virtual ~Scene_Renderer() = default; - virtual Node_Execution_Result render_scene( - const Scene_Render_Context& context) = 0; -}; - struct Scene_Execution_Statistics { std::size_t capacity{}; std::size_t queued{}; @@ -96,26 +60,22 @@ struct Scene_Execution_Statistics { std::uint64_t backpressure_count{}; std::uint64_t backpressure_wait_ns{}; }; - -class Scene_Base { -private: - struct Render_Completion; - +class Scene_Base : public Non_Copyable { public: using Renderable = renderive_Owner; using Const_Renderable = renderive_Owner; - class Attach_Builder; - class Renderable_Editor - : public renderive::scene_inheritance::Editor_Root { + : public renderive::scene_inheritance::Editor_Root { public: [[nodiscard]] Scene_Edit_Error attach(Renderable renderable); [[nodiscard]] Scene_Edit_Error detach(const Renderable& renderable); [[nodiscard]] Scene_Edit_Error set_dependency_parent(const Renderable& child, const Renderable& parent); [[nodiscard]] Scene_Edit_Error add_dependency_parent(const Renderable& child, const Renderable& parent); [[nodiscard]] Scene_Edit_Error clear_dependency_parent(const Renderable& child); - [[nodiscard]] Scene_Edit_Error error() const noexcept { return error_; } + [[nodiscard]] Scene_Edit_Error error() const noexcept { + return error_; + } protected: explicit Renderable_Editor(Scene_Base& scene) : scene_(scene) {} Scene_Base& scene_; @@ -125,9 +85,8 @@ public: friend class Scene_Base; friend class Attach_Builder; }; - class Attach_Builder - : public renderive::scene_inheritance::Builder_Root { + : public renderive::scene_inheritance::Builder_Root { public: Attach_Builder(const Attach_Builder&) = delete; Attach_Builder& operator=(const Attach_Builder&) = delete; @@ -144,33 +103,27 @@ public: private: friend class Scene_Base; }; - using Renderable_Edit = std::function; - class Edit_Operation { public: Edit_Operation() = default; [[nodiscard]] Scene_Edit_Error wait() const; private: - explicit Edit_Operation(std::shared_future result) - : result_(std::move(result)) {} + explicit Edit_Operation(std::shared_future result) : result_(std::move(result)) {} std::shared_future result_; friend class Scene_Base; friend class Scene_2D_Base; friend class Scene_3D_Base; }; - struct Topology_Relationship { Const_Renderable child; Const_Renderable parent; }; - struct Topology_Snapshot { std::vector renderables; std::vector display; std::vector dependency; }; - enum class Observation_Event { render_submitted, render_started, @@ -178,7 +131,6 @@ public: render_cancelled, render_failed }; - struct Observation { Observation_Event event{}; std::uint64_t time_ns{}; @@ -189,288 +141,137 @@ public: std::shared_ptr frame_snapshot; std::shared_ptr render_plan; }; - - Scene_Base(); - explicit Scene_Base(std::pmr::memory_resource& upstream_memory_resource); - Scene_Base(const Scene_Base&) = delete; - Scene_Base& operator=(const Scene_Base&) = delete; - Scene_Base(Scene_Base&&) = delete; - Scene_Base& operator=(Scene_Base&&) = delete; - virtual ~Scene_Base(); - Scene_Render_Result render(); Scene_Render_Result render(Abstract_Frame& frame); Scene_Render_Result wait_for_render(); void publish_frame_state(); void publish_frame_state(Abstract_Frame& frame); void notify_model_dirty() noexcept; - - Attach_Builder attach_builder(); [[nodiscard]] Edit_Operation edit_renderables(Renderable_Edit edit); - [[nodiscard]] std::size_t renderable_count() const; [[nodiscard]] Topology_Snapshot topology_snapshot() const; [[nodiscard]] std::vector paint_order_snapshot() const; [[nodiscard]] std::shared_ptr render_plan_snapshot() const; - [[nodiscard]] std::shared_ptr find_render_plan( - Render_Plan_Version version) const; - + [[nodiscard]] std::shared_ptr find_render_plan(Render_Plan_Version version) const; [[nodiscard]] Capture_Request_Result capture_next_frame(); [[nodiscard]] Capture_Request_Result capture_frames(std::size_t count); [[nodiscard]] Capture_Controller_State capture_state() const noexcept; - [[nodiscard]] std::optional capture_session( - Capture_Session_Id session_id) const; + [[nodiscard]] std::optional capture_session(Capture_Session_Id session_id) const; [[nodiscard]] std::vector capture_sessions() const; - [[nodiscard]] std::vector node_statistics( - Capture_Session_Id session_id) const; - [[nodiscard]] std::vector plan_version_statistics( - Capture_Session_Id session_id) const; + [[nodiscard]] std::vector node_statistics(Capture_Session_Id session_id) const; + [[nodiscard]] std::vector plan_version_statistics(Capture_Session_Id session_id) const; [[nodiscard]] Scene_Execution_Statistics execution_statistics() const noexcept; - [[nodiscard]] std::pmr::memory_resource& memory_resource() const noexcept; [[nodiscard]] std::pmr::memory_resource& upstream_memory_resource() const noexcept; - Frame_Control_Strategy_Base& frame_control_strategy(); - const Frame_Control_Strategy_Base& frame_control_strategy() const; - - + [[nodiscard]] auto frame_control_strategy() -> std::shared_ptr; + [[nodiscard]] auto frame_control_strategy() const -> std::shared_ptr; + struct Observer + : renderive::scene_inheritance::Observer_Root {}; protected: + Scene_Base(); + explicit Scene_Base(std::pmr::memory_resource& upstream_memory_resource); + virtual ~Scene_Base(); + struct Builder_Layer + : renderive::scene_inheritance::Builder_Root {}; struct State_Layer - : renderive::scene_inheritance::State_Root {}; - - struct Impl - : renderive::scene_inheritance::Impl_Root { - struct Observer : renderive::scene_inheritance::Observer_Root {}; - virtual std::uint64_t now_ns() const noexcept; - virtual ~Impl() = default; - }; - + : renderive::scene_inheritance::State_Root {}; + class Impl; Scene_Base(std::pmr::memory_resource& upstream_memory_resource, std::unique_ptr impl); - Impl& d_func() noexcept { return *impl_; } - const Impl& d_func() const noexcept { return *impl_; } - - virtual Frame_Control_Strategy_Base& frame_control_strategy_impl() = 0; - virtual const Frame_Control_Strategy_Base& frame_control_strategy_impl() const = 0; - virtual void publish_scene_state() = 0; - virtual std::uint64_t acquire_scene_state() = 0; - virtual void capture_scene_state(Frame_Render_Snapshot& snapshot) const = 0; - std::unique_lock lock_render_idle(); - [[nodiscard]] bool is_render_execution_context() const noexcept; - bool consume_model_dirty() noexcept; - void invalidate_renderables(); - void shutdown(); - [[nodiscard]] Edit_Operation enqueue_renderable_edit(std::function edit); - + template + Implementation& d_func() noexcept { + return static_cast(*impl_); + } + template + const Implementation& d_func() const noexcept { + return static_cast(*impl_); + } private: friend class Renderable_Base; friend class Scene_2D_Base; - - using Composite_Render_Node_Function = - std::function; - using Scene_Render_Node_Function = - std::function; - using Execution_Function = - std::variant; - - struct Execution_Binding { - std::optional renderable_index; - Execution_Function function; - }; - - struct Compiled_Render_Plan; - struct Render_Task { - std::shared_ptr snapshot; - std::shared_ptr compiled_plan; - std::shared_ptr topology; - std::shared_ptr frame; - std::shared_ptr completion; - }; - class Execution_Context; - class Render_Execution_Scope; - - class Renderable_Edit_Transaction; - - struct Render_Completion { - Scene_Render_Result error{}; - std::exception_ptr exception; - bool completed{}; - bool observed{}; - }; - - [[nodiscard]] Scene_Render_Result submit_render(Abstract_Frame* frame); - void submit_operation(std::function operation); - void complete_pending_operation(); - void record_pending_exception(std::exception_ptr exception); - void capture_edit_renderable(const Renderable& renderable); - void request_render_graph_rebuild(Renderable_Base& renderable); - [[nodiscard]] std::shared_ptr snapshot_live_model(); - [[nodiscard]] std::shared_ptr - capture_live_frame(); - void publish_frame_state_locked(); - std::shared_ptr compile_render_plan( - const Frame_Render_Snapshot& snapshot); - void execute_render_task(std::shared_ptr task); - [[nodiscard]] Scene_Edit_Error execute_renderable_edit(std::function edit); - [[nodiscard]] Scene_Render_Result execute_render_graph(Render_Task& task); - [[nodiscard]] Scene_Edit_Error renderable_scene_error(const Renderable_Base& renderable) const noexcept; - [[nodiscard]] bool is_renderable_attached_locked( - const Renderable& renderable) const; - [[nodiscard]] Scene_Edit_Error validate_structure_locked() const; - [[nodiscard]] Scene_Edit_Error cleanup_detached_topology_locked(); - void bind_raster_capabilities( - renderive::scene::detail::Raster_Capabilities& capabilities); - - inline static thread_local Scene_Base* active_execution_scene_{}; - inline static thread_local Scene_Base* active_renderable_edit_scene_{}; - inline static thread_local Scene_Base* active_submitted_observer_scene_{}; - - std::shared_ptr scene_lifetime_; - std::shared_ptr memory_domain_; std::unique_ptr impl_; - std::unique_ptr execution_context_; - std::atomic_bool model_dirty_{true}; - std::pmr::unordered_map renderables_; - renderive::scene::dependency::Dependency_Resolver - dependency_resolver_; - renderive::scene::detail::Raster_Capabilities* raster_capabilities_{}; - std::pmr::unordered_map> color_caches_; - mutable std::mutex model_mutex_; - - mutable std::recursive_mutex task_mutex_; - std::condition_variable_any render_completed_; - std::shared_ptr current_completion_; - std::exception_ptr pending_exception_; - std::size_t deferred_render_count_{}; - std::uint64_t render_sequence_{}; - std::size_t pending_operations_{}; - bool runtime_started_{}; - bool shutting_down_{}; - Renderable_Edit_Transaction* active_edit_transaction_{}; - - const Render_Node_Id composite_begin_node_id_; - const Render_Node_Id scene_render_node_id_; - Render_Plan_History render_plan_history_; - std::shared_ptr compiled_render_plan_; - Capture_Controller capture_controller_; - Capture_Repository capture_repository_; }; - -class Scene_2D_Base - : public Scene_Base, - private renderive::scene::detail::Raster_Capabilities { +class Scene_2D_Base : public Scene_Base { public: class Renderable_Editor - : public renderive::scene_inheritance::Editor_Node< - Renderable_Editor, Scene_Base::Renderable_Editor> { + : public renderive::scene_inheritance::Editor_Node< + Renderable_Editor, Scene_Base::Renderable_Editor> { public: [[nodiscard]] Scene_Edit_Error set_display_parent(const Renderable& child, const Renderable& parent); [[nodiscard]] Scene_Edit_Error add_display_parent(const Renderable& child, const Renderable& parent); [[nodiscard]] Scene_Edit_Error clear_display_parent(const Renderable& child); private: - explicit Renderable_Editor(Scene_2D_Base& scene) - : renderive::scene_inheritance::Editor_Node< - Renderable_Editor, Scene_Base::Renderable_Editor>(scene), - scene_2d_(scene) {} + explicit Renderable_Editor(Scene_2D_Base& scene) : renderive::scene_inheritance::Editor_Node< + Renderable_Editor, Scene_Base::Renderable_Editor>(scene), + scene_2d_(scene) {} Scene_2D_Base& scene_2d_; friend class Scene_2D_Base; friend class Attach_Builder; }; - class Attach_Builder - : public renderive::scene_inheritance::Builder_Node< - Attach_Builder, Scene_Base::Attach_Builder> { + : public renderive::scene_inheritance::Builder_Node< + Attach_Builder, Scene_Base::Attach_Builder> { public: [[nodiscard]] Scene_Edit_Error set_display_parent(const Renderable& child, const Renderable& parent); [[nodiscard]] Scene_Edit_Error add_display_parent(const Renderable& child, const Renderable& parent); [[nodiscard]] Scene_Edit_Error clear_display_parent(const Renderable& child); private: - explicit Attach_Builder(Scene_2D_Base& scene) - : renderive::scene_inheritance::Builder_Node< - Attach_Builder, Scene_Base::Attach_Builder>(scene), - scene_2d_(scene) {} + explicit Attach_Builder(Scene_2D_Base& scene) : renderive::scene_inheritance::Builder_Node< + Attach_Builder, Scene_Base::Attach_Builder>(scene), + scene_2d_(scene) {} Scene_2D_Base& scene_2d_; friend class Scene_2D_Base; }; - using Renderable_Edit = std::function; - Scene_2D_Base(); explicit Scene_2D_Base(std::pmr::memory_resource& memory_resource); Attach_Builder attach_builder(); [[nodiscard]] Edit_Operation edit_renderables(Renderable_Edit edit); - + struct Observer + : renderive::scene_inheritance::Observer_Node< + Scene_2D_Base, Scene_Base::Observer> {}; protected: + struct Builder_Layer + : renderive::scene_inheritance::Builder_Node< + Builder_Layer, Scene_Base::Builder_Layer> {}; struct State_Layer - : renderive::scene_inheritance::State_Node< - State_Layer, Scene_Base::State_Layer> {}; - struct Impl : renderive::scene_inheritance::Impl_Node { - struct Observer - : renderive::scene_inheritance::Observer_Node< - Impl, Scene_Base::Impl::Observer> {}; - }; + : renderive::scene_inheritance::State_Node< + State_Layer, Scene_Base::State_Layer> {}; + struct Impl; Scene_2D_Base(std::pmr::memory_resource& memory_resource, std::unique_ptr impl); - virtual std::shared_ptr make_renderable_color_cache() override = 0; - virtual Frame_Viewport frame_viewport() const override = 0; - private: friend class Scene_Base; - void with_initial_display_edit(std::function edit); - [[nodiscard]] Scene_Edit_Error set_display_parent_locked(const Renderable& child, - const Renderable& parent); - [[nodiscard]] Scene_Edit_Error add_display_parent_locked(const Renderable& child, - const Renderable& parent); - [[nodiscard]] Scene_Edit_Error clear_display_parent_locked(const Renderable& child); - void attach(Renderable_Id id) override; - [[nodiscard]] bool cleanup() override; - [[nodiscard]] bool valid() const override; - renderive::scene::dependency::Resolution resolve() - const override; - void restore(const renderive::scene::dependency::Resolution& resolution) override; - - renderive::scene::dependency::Dependency_Resolver - display_resolver_; }; - class Scene_3D_Base : public Scene_Base { public: - class Renderable_Editor - : public renderive::scene_inheritance::Editor_Node< - Renderable_Editor, Scene_Base::Renderable_Editor> { + class Renderable_Editor : public renderive::scene_inheritance::Editor_Node { private: - explicit Renderable_Editor(Scene_3D_Base& scene) - : renderive::scene_inheritance::Editor_Node< - Renderable_Editor, Scene_Base::Renderable_Editor>(scene) {} + explicit Renderable_Editor(Scene_3D_Base& scene) : renderive::scene_inheritance::Editor_Node< + Renderable_Editor, Scene_Base::Renderable_Editor>(scene) {} friend class Scene_3D_Base; }; - - class Attach_Builder - : public renderive::scene_inheritance::Builder_Node< - Attach_Builder, Scene_Base::Attach_Builder> { + class Attach_Builder : public renderive::scene_inheritance::Builder_Node { private: - explicit Attach_Builder(Scene_3D_Base& scene) - : renderive::scene_inheritance::Builder_Node< - Attach_Builder, Scene_Base::Attach_Builder>(scene) {} + explicit Attach_Builder(Scene_3D_Base& scene) : renderive::scene_inheritance::Builder_Node< + Attach_Builder, Scene_Base::Attach_Builder>(scene) {} friend class Scene_3D_Base; }; - using Renderable_Edit = std::function; + [[nodiscard]] Edit_Operation edit_renderables(Renderable_Edit edit); + struct Observer + : renderive::scene_inheritance::Observer_Node< + Scene_3D_Base, Scene_Base::Observer> {}; +protected: Scene_3D_Base(); explicit Scene_3D_Base(std::pmr::memory_resource& memory_resource); - Attach_Builder attach_builder(); - [[nodiscard]] Edit_Operation edit_renderables(Renderable_Edit edit); - -protected: + struct Builder_Layer + : renderive::scene_inheritance::Builder_Node< + Builder_Layer, Scene_Base::Builder_Layer> {}; struct State_Layer - : renderive::scene_inheritance::State_Node< - State_Layer, Scene_Base::State_Layer> {}; - struct Impl : renderive::scene_inheritance::Impl_Node { - struct Observer - : renderive::scene_inheritance::Observer_Node< - Impl, Scene_Base::Impl::Observer> {}; - }; + : renderive::scene_inheritance::State_Node< + State_Layer, Scene_Base::State_Layer> {}; + struct Impl; Scene_3D_Base(std::pmr::memory_resource& memory_resource, std::unique_ptr impl); }; diff --git a/Kernel/src/renderive/scene/base/Scene_Base.inl b/Kernel/src/renderive/scene/base/Scene_Base.inl new file mode 100644 index 0000000..e471d0b --- /dev/null +++ b/Kernel/src/renderive/scene/base/Scene_Base.inl @@ -0,0 +1,212 @@ +#pragma once + +#include "Scene_Base.hpp" + +#include + +class Scene_Base::Impl + : public renderive::scene_inheritance::Impl_Root { +public: + using Observer = Scene_Base::Observer; + + struct Frame_Control_Capability { + virtual ~Frame_Control_Capability() = default; + virtual std::shared_ptr + frame_control_strategy() = 0; + virtual std::shared_ptr + frame_control_strategy() const = 0; + }; + + struct Scene_State_Capability { + virtual ~Scene_State_Capability() = default; + virtual void before_scene_state_swap() = 0; + virtual void swap_scene_state() = 0; + virtual void after_scene_state_swap() {} + virtual std::uint64_t acquire_scene_state() = 0; + virtual void capture_scene_state( + Frame_Render_Snapshot& snapshot) const = 0; + }; + + struct Composition_Relationships { + virtual ~Composition_Relationships() = default; + virtual void attach(Renderable_Id id) = 0; + [[nodiscard]] virtual bool cleanup() = 0; + [[nodiscard]] virtual bool valid() const = 0; + virtual renderive::scene::dependency::Resolution + resolve() const = 0; + virtual void restore( + const renderive::scene::dependency::Resolution& + resolution) = 0; + }; + + struct Raster_Capabilities { + virtual ~Raster_Capabilities() = default; + virtual std::shared_ptr + make_renderable_color_cache() = 0; + virtual Frame_Viewport frame_viewport() const = 0; + }; + + struct Compositor { + virtual ~Compositor() = default; + virtual void begin_composite( + const Composite_Render_Context& context) = 0; + virtual void composite(const Composite_Render_Context& context) = 0; + }; + + struct Renderer { + virtual ~Renderer() = default; + virtual Node_Execution_Result render_scene( + const Scene_Render_Context& context) = 0; + }; + + using Composite_Render_Node_Function = + std::function; + using Scene_Render_Node_Function = + std::function; + using Execution_Function = + std::variant; + + struct Execution_Binding { + std::optional renderable_index; + Execution_Function function; + }; + + struct Compiled_Render_Plan; + struct Render_Completion { + Scene_Render_Result error{}; + std::exception_ptr exception; + bool completed{}; + bool observed{}; + }; + struct Render_Task { + std::shared_ptr snapshot; + std::shared_ptr compiled_plan; + std::shared_ptr topology; + std::shared_ptr frame; + std::shared_ptr completion; + }; + + class Execution_Context; + class Render_Execution_Scope; + class Renderable_Edit_Transaction; + + explicit Impl(std::pmr::memory_resource& upstream_memory_resource); + virtual ~Impl(); + void initialize(Scene_Base& scene); + + [[nodiscard]] Scene_Base& scene() noexcept; + [[nodiscard]] const Scene_Base& scene() const noexcept; + [[nodiscard]] virtual std::uint64_t now_ns() const noexcept; + [[nodiscard]] std::pmr::memory_resource& memory_resource() const noexcept; + [[nodiscard]] std::pmr::memory_resource& + upstream_memory_resource() const noexcept; + [[nodiscard]] Frame_Control_Capability& frame_control(); + [[nodiscard]] const Frame_Control_Capability& frame_control() const; + [[nodiscard]] Scene_State_Capability& scene_state(); + [[nodiscard]] const Scene_State_Capability& scene_state() const; + [[nodiscard]] Composition_Relationships* composition() noexcept; + [[nodiscard]] const Composition_Relationships* + composition() const noexcept; + [[nodiscard]] Raster_Capabilities* raster() noexcept; + [[nodiscard]] const Raster_Capabilities* raster() const noexcept; + + [[nodiscard]] Scene_Render_Result submit_render(Abstract_Frame* frame); + void complete_pending_operation(); + void record_pending_exception(std::exception_ptr exception); + void capture_edit_renderable(const Renderable& renderable); + void request_render_graph_rebuild(Renderable_Base& renderable); + [[nodiscard]] std::shared_ptr + snapshot_live_model(); + [[nodiscard]] std::shared_ptr capture_live_frame(); + void publish_frame_state_locked(); + std::shared_ptr compile_render_plan( + const Frame_Render_Snapshot& snapshot); + void execute_render_task(std::shared_ptr task); + [[nodiscard]] Scene_Edit_Error execute_renderable_edit( + std::function edit); + [[nodiscard]] Scene_Render_Result execute_render_graph(Render_Task& task); + [[nodiscard]] Scene_Edit_Error renderable_scene_error( + const Renderable_Base& renderable) const noexcept; + [[nodiscard]] bool is_renderable_attached_locked( + const Renderable& renderable) const; + [[nodiscard]] Scene_Edit_Error validate_structure_locked() const; + [[nodiscard]] Scene_Edit_Error cleanup_detached_topology_locked(); + [[nodiscard]] std::unique_lock lock_render_idle(); + [[nodiscard]] bool is_render_execution_context() const noexcept; + [[nodiscard]] bool consume_model_dirty() noexcept; + void invalidate_renderables(); + void shutdown(); + [[nodiscard]] Scene_Base::Edit_Operation enqueue_renderable_edit( + std::function edit); + + inline static thread_local Scene_Base* active_execution_scene_{}; + inline static thread_local Scene_Base* active_renderable_edit_scene_{}; + inline static thread_local Scene_Base* active_submitted_observer_scene_{}; + + Scene_Base* owner_{}; + std::shared_ptr scene_lifetime_; + std::shared_ptr memory_domain_; + std::unique_ptr execution_context_; + std::atomic_bool model_dirty_{true}; + std::pmr::unordered_map renderables_; + renderive::scene::dependency::Dependency_Resolver + dependency_resolver_; + std::pmr::unordered_map> + color_caches_; + mutable std::mutex model_mutex_; + + mutable std::recursive_mutex task_mutex_; + std::condition_variable_any render_completed_; + std::shared_ptr current_completion_; + std::exception_ptr pending_exception_; + std::size_t deferred_render_count_{}; + std::uint64_t render_sequence_{}; + std::size_t pending_operations_{}; + bool runtime_started_{}; + bool shutting_down_{}; + Renderable_Edit_Transaction* active_edit_transaction_{}; + + const Render_Node_Id composite_begin_node_id_; + const Render_Node_Id scene_render_node_id_; + Render_Plan_History render_plan_history_; + std::shared_ptr compiled_render_plan_; + Capture_Controller capture_controller_; + Capture_Repository capture_repository_; +}; + +struct Scene_2D_Base::Impl + : renderive::scene_inheritance::Impl_Node, + Scene_Base::Impl::Composition_Relationships { + using Observer = Scene_2D_Base::Observer; + + explicit Impl(std::pmr::memory_resource& memory_resource); + void with_initial_display_edit(std::function edit); + [[nodiscard]] Scene_Edit_Error set_display_parent_locked( + const Renderable& child, const Renderable& parent); + [[nodiscard]] Scene_Edit_Error add_display_parent_locked( + const Renderable& child, const Renderable& parent); + [[nodiscard]] Scene_Edit_Error clear_display_parent_locked( + const Renderable& child); + void attach(Renderable_Id id) override; + [[nodiscard]] bool cleanup() override; + [[nodiscard]] bool valid() const override; + [[nodiscard]] renderive::scene::dependency::Resolution + resolve() const override; + void restore( + const renderive::scene::dependency::Resolution& + resolution) override; + + renderive::scene::dependency::Dependency_Resolver + display_resolver_; +}; + +struct Scene_3D_Base::Impl + : renderive::scene_inheritance::Impl_Node { + using Observer = Scene_3D_Base::Observer; + + explicit Impl(std::pmr::memory_resource& memory_resource) + : renderive::scene_inheritance::Impl_Node< + Impl, Scene_Base::Impl>(memory_resource) {} +}; diff --git a/Kernel/src/renderive/scene/concept/Scene.hpp b/Kernel/src/renderive/scene/concept/Scene.hpp index 8379c2d..e4c3ab4 100644 --- a/Kernel/src/renderive/scene/concept/Scene.hpp +++ b/Kernel/src/renderive/scene/concept/Scene.hpp @@ -3,8 +3,8 @@ #include "renderive/scene/base/Scene_Base.hpp" template concept Scene = std::derived_from && requires(That& scene, const That& const_scene) { - { scene.frame_control_strategy() } -> std::same_as; - { const_scene.frame_control_strategy() } -> std::same_as; + { scene.frame_control_strategy() } -> std::same_as>; + { const_scene.frame_control_strategy() } -> std::same_as>; { scene.render() } -> std::same_as; { scene.wait_for_render() } -> std::same_as; }; diff --git a/Kernel/src/renderive/state/Render_Acquired_State_Storage.hpp b/Kernel/src/renderive/state/Render_Acquired_State_Storage.hpp index 06462ed..474de37 100644 --- a/Kernel/src/renderive/state/Render_Acquired_State_Storage.hpp +++ b/Kernel/src/renderive/state/Render_Acquired_State_Storage.hpp @@ -9,6 +9,7 @@ #include "renderive/base/property/Concepts.hpp" #include +#include #include #include #include @@ -84,6 +85,36 @@ struct Render_Acquired_State_Storage : State_Strategy_Base { return value; } + template + requires std::invocable + auto read(Read&& value) const { + std::lock_guard lock(mtx); + return std::invoke(std::forward(value), *cache_state); + } + + template + requires std::invocable + Self& update(Update&& value) { + Observation observation; + { + std::lock_guard lock(mtx); + std::invoke(std::forward(value), *cache_state); + ++cache_update_count; + observation = {Observation_Event::cache_updated, + observer.now_ns(), cache_update_count, + publish_count, render_revision, *cache_state}; + } + observer.observe(observation); + return *this; + } + + [[nodiscard]] State published_state_snapshot() const { + std::lock_guard lock(mtx); + return published_revision == render_revision + ? *render_state + : *published_state; + } + void publish() override { std::optional observation; { @@ -125,6 +156,11 @@ struct Render_Acquired_State_Storage : State_Strategy_Base { return publish_count; } + [[nodiscard]] State render_state_snapshot() const { + std::lock_guard lock(mtx); + return *render_state; + } + protected: friend class Render_State_View; diff --git a/Qt/plot/Latency_Eager_Plot.cpp b/Qt/plot/Latency_Eager_Plot.cpp index a28c422..2a8568f 100644 --- a/Qt/plot/Latency_Eager_Plot.cpp +++ b/Qt/plot/Latency_Eager_Plot.cpp @@ -23,8 +23,8 @@ double Latency_Eager_Plot::max_render_fps() const { return d->scene->max_render_fps(); } -tl::expected -Latency_Eager_Plot::set_max_render_fps(double fps) { +auto Latency_Eager_Plot::set_max_render_fps(double fps) + -> Expected { auto result = d->scene->set_max_render_fps(fps); if (result) update_render_interval(); diff --git a/Qt/plot/Latency_Eager_Plot.h b/Qt/plot/Latency_Eager_Plot.h index d33227b..9c599ea 100644 --- a/Qt/plot/Latency_Eager_Plot.h +++ b/Qt/plot/Latency_Eager_Plot.h @@ -12,8 +12,8 @@ public: void pause(); [[nodiscard]] bool running() const; [[nodiscard]] double max_render_fps() const; - [[nodiscard]] tl::expected - set_max_render_fps(double fps); + [[nodiscard]] auto set_max_render_fps(double fps) + -> Expected; [[nodiscard]] Low_Latency_Diagnostics diagnostics() const; [[nodiscard]] Refresh_Control_Snapshot refresh_feedback_snapshot() const; }; diff --git a/render_2D/CMakeLists.txt b/render_2D/CMakeLists.txt index 32fd74e..8bd6e67 100644 --- a/render_2D/CMakeLists.txt +++ b/render_2D/CMakeLists.txt @@ -1,4 +1,4 @@ -set(Renderive_render_2D_dependencies global::blend2d global::expected) +set(Renderive_render_2D_dependencies global::blend2d) if (RENDERIVE_BUILD_TESTS) set(Renderive_render_2D_test_targets) list(APPEND Renderive_render_2D_dependencies global::GTest) @@ -12,7 +12,6 @@ if (Renderive_render_2D_dependencies_missing) endif () rcl_load_dependency_environment(${Renderive_render_2D_dependencies}) find_package(blend2d CONFIG REQUIRED) -find_package(tl-expected CONFIG REQUIRED) if (RENDERIVE_BUILD_TESTS) find_package(GTest CONFIG REQUIRED) endif () @@ -32,8 +31,7 @@ target_include_directories(Renderive_render_2D PUBLIC target_compile_features(Renderive_render_2D PUBLIC cxx_std_20) target_link_libraries(Renderive_render_2D PUBLIC Renderive_Kernel - blend2d::blend2d - tl::expected) + blend2d::blend2d) if (MSVC) target_compile_options(Renderive_render_2D PRIVATE /utf-8) endif () diff --git a/render_2D/render_2D/axis/Frequency_Axis.cpp b/render_2D/render_2D/axis/Frequency_Axis.cpp index bc28aa9..6677b55 100644 --- a/render_2D/render_2D/axis/Frequency_Axis.cpp +++ b/render_2D/render_2D/axis/Frequency_Axis.cpp @@ -27,8 +27,8 @@ private: }; static_assert( - std::constructible_from<::renderive::Frequency_Axis, - const ::renderive::Frequency_Axis::Properties&>); + !std::constructible_from<::renderive::Frequency_Axis, + const ::renderive::Frequency_Axis::Properties&>); Frequency_Axis::Frequency_Axis(const State& state) : Renderable(With_Attached_Impl{}, state) {} diff --git a/render_2D/render_2D/axis/Frequency_Axis.h b/render_2D/render_2D/axis/Frequency_Axis.h index 22a60fc..cc9da1e 100644 --- a/render_2D/render_2D/axis/Frequency_Axis.h +++ b/render_2D/render_2D/axis/Frequency_Axis.h @@ -5,6 +5,8 @@ namespace detail { struct LIB_DECL Frequency_Axis : Renderable { using State = ::renderive::Axis::Properties; +private: + friend struct renderable::attach; explicit Frequency_Axis(const State& state); protected: struct Impl; diff --git a/render_2D/render_2D/axis/Numeric_Axis.cpp b/render_2D/render_2D/axis/Numeric_Axis.cpp index 6cebf36..d760669 100644 --- a/render_2D/render_2D/axis/Numeric_Axis.cpp +++ b/render_2D/render_2D/axis/Numeric_Axis.cpp @@ -5,8 +5,8 @@ namespace renderive::detail { -static_assert(std::constructible_from<::renderive::Axis, - const ::renderive::Axis::Properties&>); +static_assert(!std::constructible_from<::renderive::Axis, + const ::renderive::Axis::Properties&>); Axis::Axis(const State& state) : Axis_State_Strategy(With_Attached_Impl{}, state) {} diff --git a/render_2D/render_2D/axis/Numeric_Axis.h b/render_2D/render_2D/axis/Numeric_Axis.h index 45d974d..1949abb 100644 --- a/render_2D/render_2D/axis/Numeric_Axis.h +++ b/render_2D/render_2D/axis/Numeric_Axis.h @@ -24,6 +24,8 @@ struct LIB_DECL Axis State() = default; State(const Axis_Properties& properties) : Axis_Properties(properties) {} }; +private: + friend struct renderable::attach; explicit Axis(const State& state); protected: struct Impl; diff --git a/render_2D/render_2D/axis/Time_Axis.cpp b/render_2D/render_2D/axis/Time_Axis.cpp index 15a1de1..16a1829 100644 --- a/render_2D/render_2D/axis/Time_Axis.cpp +++ b/render_2D/render_2D/axis/Time_Axis.cpp @@ -48,8 +48,8 @@ private: }; static_assert( - std::constructible_from<::renderive::Time_Axis, - const ::renderive::Time_Axis::Properties&>); + !std::constructible_from<::renderive::Time_Axis, + const ::renderive::Time_Axis::Properties&>); Time_Axis::Time_Axis(const State& state) : Axis_State_Strategy(With_Attached_Impl{}, state) {} diff --git a/render_2D/render_2D/axis/Time_Axis.h b/render_2D/render_2D/axis/Time_Axis.h index 0deefef..27f7eb1 100644 --- a/render_2D/render_2D/axis/Time_Axis.h +++ b/render_2D/render_2D/axis/Time_Axis.h @@ -16,7 +16,10 @@ struct LIB_DECL Time_Axis int next_tick{}; std::deque> samples; }; +private: + friend struct renderable::attach; explicit Time_Axis(const State& state); +public: [[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; diff --git a/render_2D/render_2D/frame_control/Plot_Frame_Control.cpp b/render_2D/render_2D/frame_control/Plot_Frame_Control.cpp new file mode 100644 index 0000000..396df12 --- /dev/null +++ b/render_2D/render_2D/frame_control/Plot_Frame_Control.cpp @@ -0,0 +1,186 @@ +#include "Plot_Frame_Control.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace renderive::detail { +namespace { +using Manual_Strategy = Manual_Refresh_Strategy; +using Low_Latency_Strategy_Type = Low_Latency_Strategy; +using Playback_Strategy = Flow_Refresh_Strategy; + +template +decltype(auto) visit(Frame_Control_Strategy_Base& strategy, + Function&& function) { + if (auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, std::integral_constant{}); + if (auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, std::integral_constant{}); + if (auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, std::integral_constant{}); + ::renderive::error::unexpected( + "unknown plot frame strategy"); +} + +template +decltype(auto) visit(const Frame_Control_Strategy_Base& strategy, + Function&& function) { + if (const auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, std::integral_constant{}); + if (const auto* value = + dynamic_cast(&strategy)) + return std::forward(function)( + *value, std::integral_constant{}); + if (const auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, std::integral_constant{}); + ::renderive::error::unexpected( + "unknown plot frame strategy"); +} + +} // namespace + +struct Plot_Frame_Control::Impl { + std::shared_ptr strategy; +}; + +Plot_Frame_Control::Plot_Frame_Control( + Plot_Frame_Mode selected_mode, + std::pmr::memory_resource& memory_resource) + : impl_(std::make_unique()) { + switch (selected_mode) { + case Plot_Frame_Mode::Manual: + impl_->strategy = std::make_shared(); + break; + case Plot_Frame_Mode::Low_Latency: + impl_->strategy = std::make_shared(); + break; + case Plot_Frame_Mode::Playback: + impl_->strategy = std::make_shared( + memory_resource); + break; + } + if (!impl_->strategy) + ::renderive::error::unexpected( + "unknown plot frame mode"); +} + +Plot_Frame_Control::~Plot_Frame_Control() = default; + +std::shared_ptr +Plot_Frame_Control::runtime() { + return impl_->strategy; +} + +std::shared_ptr +Plot_Frame_Control::runtime() const { + return impl_->strategy; +} + +double Plot_Frame_Control::frequency_hz() const { + return impl_->strategy->frequency_hz(); +} + +std::uint64_t Plot_Frame_Control::next_refresh_interval_ns() const { + return impl_->strategy->next_refresh_interval_ns(); +} + +auto Plot_Frame_Control::prepare(Render_Scene_2D& scene) + -> Expected { + return visit(*impl_->strategy, [&scene](auto& strategy, auto) { + auto frame = strategy.acquire_painter(); + if (!frame) + return Expected( + unexpected( + Plot_Prepare_Frame_Error::painter_unavailable)); + scene.publish_frame_state(*frame); + return Expected{}; + }); +} + +auto Plot_Frame_Control::refresh(Render_Scene_2D&) + -> Expected { + auto* strategy = dynamic_cast(impl_->strategy.get()); + if (!strategy) + return unexpected( + Plot_Refresh_Manual_Frame_Error::unsupported_frame_mode); + if (strategy->refresh() != Manual_Refresh_Result::none) + return unexpected( + Plot_Refresh_Manual_Frame_Error::no_pending_frame); + return {}; +} + +auto Plot_Frame_Control::render(Render_Scene_2D& scene) + -> Expected { + return visit(*impl_->strategy, [&scene](auto& strategy, auto) { + auto frame = strategy.acquire_renderer(); + if (!frame) + return Expected( + unexpected( + Plot_Render_Prepared_Frame_Error::renderer_unavailable)); + const auto result = scene.Scene_Base::render(*frame); + if (result == Scene_Render_Result::none) + return Expected{}; + if (result == Scene_Render_Result::shutting_down) + return Expected( + unexpected( + Plot_Render_Prepared_Frame_Error::scene_shutting_down)); + const auto converted = + adapter::enum_cast(result); + if (!converted) + ::renderive::error::unexpected( + "unknown scene render error"); + return Expected( + unexpected(*converted)); + }); +} + +bool Plot_Frame_Control::discard(Render_Scene_2D&) { + if (auto* strategy = + dynamic_cast(impl_->strategy.get())) + return strategy->discard_pending_frame(); + if (auto* strategy = + dynamic_cast(impl_->strategy.get())) + return strategy->discard_pending_frame(); + return false; +} + +auto Plot_Frame_Control::request(Render_Scene_2D& scene) + -> Expected { + if (!prepare(scene)) + return unexpected(Plot_Render_Frame_Error::painter_unavailable); + if (auto* strategy = + dynamic_cast(impl_->strategy.get())) { + if (strategy->refresh() != Manual_Refresh_Result::none) + return unexpected(Plot_Render_Frame_Error::no_pending_frame); + } + if (const auto result = render(scene); !result) { + const auto converted = + adapter::enum_cast(result.error()); + if (!converted) + ::renderive::error::unexpected( + "unknown plot render error"); + return unexpected(*converted); + } + return {}; +} + +} // namespace renderive::detail diff --git a/render_2D/render_2D/frame_control/Plot_Frame_Control.h b/render_2D/render_2D/frame_control/Plot_Frame_Control.h new file mode 100644 index 0000000..c6c2930 --- /dev/null +++ b/render_2D/render_2D/frame_control/Plot_Frame_Control.h @@ -0,0 +1,40 @@ +#pragma once + +#include "../scene/Render_Scene_2D.h" + +#include +#include + +namespace renderive::detail { + +class Plot_Frame_Control final { +public: + Plot_Frame_Control(Plot_Frame_Mode mode, + std::pmr::memory_resource& memory_resource); + ~Plot_Frame_Control(); + + Plot_Frame_Control(const Plot_Frame_Control&) = delete; + Plot_Frame_Control& operator=(const Plot_Frame_Control&) = delete; + Plot_Frame_Control(Plot_Frame_Control&&) = delete; + Plot_Frame_Control& operator=(Plot_Frame_Control&&) = delete; + + [[nodiscard]] std::shared_ptr runtime(); + [[nodiscard]] std::shared_ptr + runtime() const; + [[nodiscard]] double frequency_hz() const; + [[nodiscard]] std::uint64_t next_refresh_interval_ns() const; + [[nodiscard]] auto prepare(Render_Scene_2D& scene) + -> Expected; + [[nodiscard]] auto refresh(Render_Scene_2D& scene) + -> Expected; + [[nodiscard]] auto render(Render_Scene_2D& scene) + -> Expected; + [[nodiscard]] bool discard(Render_Scene_2D& scene); + [[nodiscard]] auto request(Render_Scene_2D& scene) + -> Expected; +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace renderive::detail diff --git a/render_2D/render_2D/plottable/Afterglow.h b/render_2D/render_2D/plottable/Afterglow.h index 61f2486..190dfdd 100644 --- a/render_2D/render_2D/plottable/Afterglow.h +++ b/render_2D/render_2D/plottable/Afterglow.h @@ -8,8 +8,11 @@ namespace renderive { namespace detail { struct LIB_DECL Afterglow : Renderable { struct State; +private: + friend struct renderable::attach; Afterglow(const State& state, renderive_Owner frequency_axis, renderive_Owner power_axis); +public: ~Afterglow() override; [[nodiscard]] std::size_t history_count() const; [[nodiscard]] std::size_t latest_spectrum_point_count() const; diff --git a/render_2D/render_2D/plottable/Constellation_Diagram.h b/render_2D/render_2D/plottable/Constellation_Diagram.h index 3aec681..069b3ad 100644 --- a/render_2D/render_2D/plottable/Constellation_Diagram.h +++ b/render_2D/render_2D/plottable/Constellation_Diagram.h @@ -10,8 +10,11 @@ enum class Constellation_Diagram_Type : std::uint8_t { namespace detail { struct LIB_DECL Constellation_Diagram : Renderable { struct State; +private: + friend struct renderable::attach; Constellation_Diagram(const State& state, renderive_Owner i_axis, renderive_Owner q_axis); +public: ~Constellation_Diagram() override; void append_point(PointF point); [[nodiscard]] std::size_t point_count() const; diff --git a/render_2D/render_2D/plottable/Frequency_Trace.h b/render_2D/render_2D/plottable/Frequency_Trace.h index 65aa7e2..fdad038 100644 --- a/render_2D/render_2D/plottable/Frequency_Trace.h +++ b/render_2D/render_2D/plottable/Frequency_Trace.h @@ -5,8 +5,11 @@ namespace renderive { namespace detail { struct LIB_DECL Frequency_Trace : Renderable { struct State; +private: + friend struct renderable::attach; Frequency_Trace(const State& state, renderive_Owner time_axis, renderive_Owner value_axis); +public: ~Frequency_Trace() override; void append_sample(int tick, double value); void append_sample(Time_Of_Day time, double value); diff --git a/render_2D/render_2D/plottable/Performance_Overlay.cpp b/render_2D/render_2D/plottable/Performance_Overlay.cpp index 2d6fdba..9f6ae9e 100644 --- a/render_2D/render_2D/plottable/Performance_Overlay.cpp +++ b/render_2D/render_2D/plottable/Performance_Overlay.cpp @@ -41,6 +41,7 @@ struct Performance_Overlay::Impl { std::ofstream log; std::filesystem::path log_path; std::chrono::steady_clock::time_point previous_frame; + std::uint64_t frame_count{}; }; Performance_Overlay::Performance_Overlay() @@ -89,7 +90,8 @@ void Performance_Overlay::set_plot_name(std::string name) { } void Performance_Overlay::record_frame(double render_duration_ms, - const Low_Latency_Diagnostics& diagnostics, + double frequency_hz, + std::uint64_t next_refresh_interval_ns, std::size_t renderable_count, Size viewport) { if (!enabled()) @@ -112,15 +114,16 @@ void Performance_Overlay::record_frame(double render_duration_ms, snapshot->style = impl_->options.style; snapshot->viewport_size = viewport; snapshot->plot_name = impl_->plot_name; - snapshot->frame_count = diagnostics.refresh.frame_count; + snapshot->frame_count = ++impl_->frame_count; snapshot->lines = { "Renderive Core2 / Kernel", "Plot: " + (impl_->plot_name.empty() ? std::string{"unnamed"} : impl_->plot_name), "Frame: " + std::to_string(snapshot->frame_count), "Render: " + fixed(render_duration_ms, 3) + " ms", "Observed: " + fixed(rendered_fps) + " FPS", - "Limit: " + fixed(diagnostics.refresh.frequency_hz) + " FPS", - "Kernel interval: " + fixed(diagnostics.refresh.next_refresh_interval_ns / 1'000'000.0, 3) + " ms", + "Limit: " + fixed(frequency_hz) + " FPS", + "Kernel interval: " + fixed( + next_refresh_interval_ns / 1'000'000.0, 3) + " ms", "Renderables: " + std::to_string(renderable_count) }; @@ -141,8 +144,11 @@ void Performance_Overlay::record_frame(double render_duration_ms, impl_->log << "frame,render_ms,observed_fps,max_fps,renderables\n"; } if (impl_->log) { - impl_->log << diagnostics.refresh.frame_count << ',' << render_duration_ms << ',' - << rendered_fps << ',' << diagnostics.refresh.frequency_hz << ',' + impl_->log << impl_->frame_count + << ',' << render_duration_ms << ',' + << rendered_fps << ',' + << frequency_hz + << ',' << renderable_count << '\n'; impl_->log.flush(); } diff --git a/render_2D/render_2D/plottable/Performance_Overlay.h b/render_2D/render_2D/plottable/Performance_Overlay.h index df95a85..0bc67fa 100644 --- a/render_2D/render_2D/plottable/Performance_Overlay.h +++ b/render_2D/render_2D/plottable/Performance_Overlay.h @@ -8,8 +8,8 @@ namespace renderive { namespace detail { void record_performance_frame(Performance_Overlay&, double, - const Low_Latency_Diagnostics&, std::size_t, - Size); + double, std::uint64_t, + std::size_t, Size); } struct Performance_Log_Options { bool enabled{}; @@ -54,9 +54,12 @@ 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); + friend void detail::record_performance_frame( + Performance_Overlay&, double, double, std::uint64_t, + std::size_t, Size); void record_frame(double render_duration_ms, - const Low_Latency_Diagnostics& diagnostics, + double frequency_hz, + std::uint64_t next_refresh_interval_ns, std::size_t renderable_count, Size viewport); }; diff --git a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.cpp b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.cpp index d3d49ba..a12a611 100644 --- a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.cpp +++ b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.cpp @@ -23,14 +23,7 @@ RectF axis_content_rect(const Axis_Transform& first, const Axis_Transform& secon struct Selection_Rectangle_Overlay::Impl : next_Impl, Renderable_Event_Handler { - struct Observer : next_Observer { - - static void handle(Impl& impl, - const Renderable_Event_View& observation) { - if (observation.event == Renderable_Observer_Event::Published) - impl.interaction.publish(); - } - }; + using Observer = Selection_Rectangle_Overlay::Observer; struct Prepared_Region { RectF pixels; @@ -57,6 +50,11 @@ protected: private: void handle_event(const Event& event) override; }; +void Selection_Rectangle_Overlay::Observer::handle( + Impl& impl, const Renderable_Event_View& observation) { + if (observation.event == Renderable_Observer_Event::Published) + impl.interaction.publish(); +} Selection_Rectangle_Overlay::Selection_Rectangle_Overlay( const State& state, renderive_Owner horizontal_axis, renderive_Owner vertical_axis) diff --git a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.h b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.h index 21c7b03..2b6341d 100644 --- a/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.h +++ b/render_2D/render_2D/plottable/Selection_Rectangle_Overlay.h @@ -7,9 +7,12 @@ struct LIB_DECL Selection_Rectangle_Overlay : Renderable, Paint_Overlay { struct State; +private: + friend struct renderable::attach; Selection_Rectangle_Overlay( const State& state, renderive_Owner horizontal_axis, renderive_Owner vertical_axis); +public: ~Selection_Rectangle_Overlay() override; [[nodiscard]] std::vector selected_regions() const; void clear_selected_regions(); @@ -21,6 +24,11 @@ struct LIB_DECL Selection_Rectangle_Overlay }; private: struct Impl; +public: + struct Observer : next_Observer { + static void handle(Impl& impl, + const Renderable_Event_View& observation); + }; }; } using Selection_Rectangle_Overlay = diff --git a/render_2D/render_2D/plottable/Spectrum.cpp b/render_2D/render_2D/plottable/Spectrum.cpp index 805f1db..4300878 100644 --- a/render_2D/render_2D/plottable/Spectrum.cpp +++ b/render_2D/render_2D/plottable/Spectrum.cpp @@ -136,16 +136,7 @@ struct Spectrum::Impl Spectrum_Real_Time_Data_Strategy, Renderable_Event_Handler, ::Render_Frame_Completion { - struct Observer : next_Observer { - - static void handle(Impl& impl, - const Renderable_Event_View& observation) { - if (observation.event != Renderable_Observer_Event::Published) - return; - impl.Spectrum_Real_Time_Data_Strategy::publish(); - impl.interaction.publish(); - } - }; + using Observer = Spectrum::Observer; Impl(renderive_Owner frequency, renderive_Owner power) @@ -173,6 +164,13 @@ struct Spectrum::Impl return revision(); } }; +void Spectrum::Observer::handle( + Impl& impl, const Renderable_Event_View& observation) { + if (observation.event != Renderable_Observer_Event::Published) + return; + impl.Spectrum_Real_Time_Data_Strategy::publish(); + impl.interaction.publish(); +} Spectrum::Spectrum(const State& state, renderive_Owner frequency_axis, renderive_Owner power_axis) diff --git a/render_2D/render_2D/plottable/Spectrum.h b/render_2D/render_2D/plottable/Spectrum.h index 4b96a1f..0762355 100644 --- a/render_2D/render_2D/plottable/Spectrum.h +++ b/render_2D/render_2D/plottable/Spectrum.h @@ -61,9 +61,12 @@ struct LIB_DECL Spectrum : Renderable { Brush_Style::Solid }; }; +private: + friend struct renderable::attach; Spectrum(const State& state, renderive_Owner frequency_axis, renderive_Owner power_axis); +public: ~Spectrum() override; void update_samples(std::span values); void update_samples(std::pmr::vector&& values); @@ -90,6 +93,11 @@ struct LIB_DECL Spectrum : Renderable { void set_current_marker_frequency(double frequency); private: struct Impl; +public: + struct Observer : next_Observer { + static void handle(Impl& impl, + const Renderable_Event_View& observation); + }; }; } using Spectrum = renderable::attach; diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.cpp b/render_2D/render_2D/plottable/Sweep_Spectrum.cpp index 8ae5448..de2089e 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.cpp +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.cpp @@ -20,19 +20,8 @@ std::vector flatten(const std::deque>& blocks) { } struct Sweep_Spectrum::Impl : next_Impl { - struct Observer : next_Observer { - - static void handle(Impl& impl, - const Renderable_Event_View& observation) { - if (observation.event != Renderable_Observer_Event::Published) - return; - auto& control = - static_cast(impl.owner()); - const int limit = - control.get_state(); - impl.blocks->retain_latest(static_cast(limit)); - } - }; + using Observer = Sweep_Spectrum::Observer; + friend struct Sweep_Spectrum::Observer; struct Prepare_Buffer { std::vector points; @@ -49,6 +38,14 @@ struct Sweep_Spectrum::Impl void prepare_frame(const Prepare_Render_Context& context) override; void paint(Painter& painter, const Paint_Render_Context& context) override; }; +void Sweep_Spectrum::Observer::handle( + Impl& impl, const Renderable_Event_View& observation) { + if (observation.event != Renderable_Observer_Event::Published) + return; + auto& control = static_cast(impl.owner()); + const int limit = control.get_state(); + impl.blocks->retain_latest(static_cast(limit)); +} Sweep_Spectrum::Sweep_Spectrum(const State& state, renderive_Owner frequency_axis, renderive_Owner power_axis) diff --git a/render_2D/render_2D/plottable/Sweep_Spectrum.h b/render_2D/render_2D/plottable/Sweep_Spectrum.h index 0d338c5..7b4c021 100644 --- a/render_2D/render_2D/plottable/Sweep_Spectrum.h +++ b/render_2D/render_2D/plottable/Sweep_Spectrum.h @@ -7,8 +7,11 @@ namespace renderive { namespace detail { struct LIB_DECL Sweep_Spectrum : Renderable { struct State; +private: + friend struct renderable::attach; Sweep_Spectrum(const State& state, renderive_Owner frequency_axis, renderive_Owner power_axis); +public: ~Sweep_Spectrum() override; void append_block(std::span values); void append_block(std::pmr::vector&& values); @@ -31,6 +34,11 @@ struct LIB_DECL Sweep_Spectrum : Renderable { }; private: struct Impl; +public: + struct Observer : next_Observer { + static void handle(Impl& impl, + const Renderable_Event_View& observation); + }; }; } using Sweep_Spectrum = renderable::attach; diff --git a/render_2D/render_2D/plottable/Waterfall.cpp b/render_2D/render_2D/plottable/Waterfall.cpp index 82202cb..6c8f947 100644 --- a/render_2D/render_2D/plottable/Waterfall.cpp +++ b/render_2D/render_2D/plottable/Waterfall.cpp @@ -63,14 +63,7 @@ struct Waterfall::Impl : next_Impl, Renderable_Event_Handler, ::Render_Frame_Completion { - struct Observer : next_Observer { - - static void handle(Impl& impl, - const Renderable_Event_View& observation) { - if (observation.event == Renderable_Observer_Event::Published) - impl.interaction.publish(); - } - }; + using Observer = Waterfall::Observer; Impl(renderive_Owner frequency, renderive_Owner time) @@ -93,6 +86,11 @@ private: void handle_event(const Event& event) override; void render_frame_completed(std::uint64_t target_interval_ns) override; }; +void Waterfall::Observer::handle( + Impl& impl, const Renderable_Event_View& observation) { + if (observation.event == Renderable_Observer_Event::Published) + impl.interaction.publish(); +} Waterfall::Waterfall(const State& state, renderive_Owner frequency_axis, renderive_Owner time_axis) diff --git a/render_2D/render_2D/plottable/Waterfall.h b/render_2D/render_2D/plottable/Waterfall.h index 76596ad..6c0b98b 100644 --- a/render_2D/render_2D/plottable/Waterfall.h +++ b/render_2D/render_2D/plottable/Waterfall.h @@ -9,9 +9,12 @@ namespace renderive { namespace detail { struct LIB_DECL Waterfall : Renderable { struct State; +private: + friend struct renderable::attach; Waterfall(const State& state, renderive_Owner frequency_axis, renderive_Owner time_axis); +public: ~Waterfall() override; void append_row(int tick, std::span values); void append_row(int tick, std::pmr::vector&& values); @@ -41,6 +44,11 @@ struct LIB_DECL Waterfall : Renderable { }; private: struct Impl; +public: + struct Observer : next_Observer { + static void handle(Impl& impl, + const Renderable_Event_View& observation); + }; }; } using Waterfall = renderable::attach; diff --git a/render_2D/render_2D/renderable/Renderable.cpp b/render_2D/render_2D/renderable/Renderable.cpp index ec4c29b..93fe5b5 100644 --- a/render_2D/render_2D/renderable/Renderable.cpp +++ b/render_2D/render_2D/renderable/Renderable.cpp @@ -25,19 +25,11 @@ void Renderable::set_cache_mode(Renderable_Cache_Mode mode) { d_func().set_configuration( {.cache_enabled = mode == Renderable_Cache_Mode::Local_Pixel}); } -Renderable_Observation Renderable::observation() const { - const auto& d = d_func(); - std::lock_guard lock(d.observation_mutex); - return d.observation; -} void Renderable::dispatch_event(const Event& event) { if (auto* handler = dynamic_cast(&d_func())) handler->handle_event(event); } -Renderable& Renderable::Impl::renderable() noexcept { - return static_cast(owner()); -} void Renderable::Impl::prepare_frame(const Prepare_Render_Context&) {} void Renderable::Impl::paint(detail::Painter&, const Paint_Render_Context&) {} @@ -82,22 +74,4 @@ Renderable_Graph_Builder::Task Renderable::Impl::add_paint_task( function(painter, context); }); } -void Renderable::Impl::observe_state( - Renderable_Observer_Event event, std::uint64_t time_ns, - std::uint64_t cache_update_count, - std::uint64_t publish_count) { - std::lock_guard lock(observation_mutex); - observation.event = event; - observation.event_time_ns = time_ns; - observation.cache_update_count = cache_update_count; - observation.publish_count = publish_count; -} -void Renderable::Impl::observe_data( - std::uint64_t revision, std::size_t item_count, - std::size_t auxiliary_item_count) { - std::lock_guard lock(observation_mutex); - observation.data_revision = revision; - observation.item_count = item_count; - observation.auxiliary_item_count = auxiliary_item_count; -} } // namespace renderive diff --git a/render_2D/render_2D/renderable/Renderable.h b/render_2D/render_2D/renderable/Renderable.h index 4f88273..72f8b38 100644 --- a/render_2D/render_2D/renderable/Renderable.h +++ b/render_2D/render_2D/renderable/Renderable.h @@ -16,6 +16,7 @@ namespace renderive { struct Renderable; namespace detail { struct Painter; +struct Renderable_Event_View; struct Renderable_State_Observer; LIB_DECL ::renderive_Owner make_renderable_group(bool cache_enabled); } @@ -24,25 +25,19 @@ enum struct Renderable_Observer_Event { Cache_Updated, Published }; -struct Renderable_Observation { - Renderable_Observer_Event event{Renderable_Observer_Event::None}; - std::uint64_t event_time_ns{}; - std::uint64_t cache_update_count{}; - std::uint64_t publish_count{}; - std::uint64_t data_revision{}; - std::size_t item_count{}; - std::size_t auxiliary_item_count{}; -}; struct LIB_DECL Renderable : renderable::render_base { +protected: + struct Impl; +public: + struct Observer : renderable_inheritance::Observer_Root {}; + explicit Renderable(bool cache_enabled = true); ~Renderable() override; [[nodiscard]] Renderable_Cache_Mode get_cache_mode() const noexcept; void set_cache_mode(Renderable_Cache_Mode mode); - [[nodiscard]] Renderable_Observation observation() const; void dispatch_event(const Event& event); protected: - struct Impl; template explicit Renderable(With_Attached_Impl, bool cache_enabled = true) diff --git a/render_2D/render_2D/renderable/Renderable_Builder.h b/render_2D/render_2D/renderable/Renderable_Builder.h index e3bcb7a..bfac687 100644 --- a/render_2D/render_2D/renderable/Renderable_Builder.h +++ b/render_2D/render_2D/renderable/Renderable_Builder.h @@ -58,13 +58,15 @@ struct Renderable_Builder { return properties; } template - requires std::derived_from && std::constructible_from + requires std::derived_from renderive_Owner build(const renderive_Owner& parent, Args&&... args) const { if (!parent || !(valid_argument(args) && ...)) return {}; validator(properties); return inheritance::initialize( - renderive_Owner::make(properties, args...), + renderive_Owner( + renderable_inheritance::Builder_Access::make( + properties, args...)), [&](renderive_Owner& result) { if (attach_builder_) detail::attach_renderable(*attach_builder_, result, diff --git a/render_2D/render_2D/renderable/Renderable_p.h b/render_2D/render_2D/renderable/Renderable_p.h index b67278a..506afe5 100644 --- a/render_2D/render_2D/renderable/Renderable_p.h +++ b/render_2D/render_2D/renderable/Renderable_p.h @@ -5,7 +5,6 @@ #include #include #include -#include namespace renderive { namespace detail { struct Renderable_Event_Handler { @@ -14,17 +13,6 @@ struct Renderable_Event_Handler { }; struct Renderable_Event_View { Renderable_Observer_Event event{Renderable_Observer_Event::None}; - std::uint64_t time_ns{}; - std::uint64_t cache_update_count{}; - std::uint64_t publish_count{}; - const std::type_info* payload_type{&typeid(void)}; - const void* payload{}; - template - [[nodiscard]] const Payload* payload_if() const noexcept { - return *payload_type == typeid(Payload) - ? static_cast(payload) - : nullptr; - } }; template Renderable_Event_View renderable_event_view( @@ -33,19 +21,6 @@ Renderable_Event_View renderable_event_view( observation.event == decltype(observation.event)::cache_updated ? Renderable_Observer_Event::Cache_Updated : Renderable_Observer_Event::Published, - observation.time_ns, - observation.cache_update_count, - observation.publish_count, - &typeid(observation.state), - &observation.state - }; -} -template -Renderable_Event_View renderable_event_view( - const Payload& payload, std::uint64_t time_ns) noexcept { - return { - Renderable_Observer_Event::None, time_ns, 0, 0, - &typeid(payload), &payload }; } } @@ -53,24 +28,9 @@ struct Renderable::Impl : renderable_inheritance::Impl_Root { - struct Observer : renderable_inheritance::Observer_Root { - static void handle( - Impl& impl, - const detail::Renderable_Event_View& value) { - impl.observe_state(value.event, value.time_ns, - value.cache_update_count, - value.publish_count); - } - }; + using Observer = Renderable::Observer; virtual ~Impl() = default; protected: - template - void report_observation(const Payload& payload, - std::uint64_t time_ns = 0) { - dispatch(detail::renderable_event_view(payload, time_ns)); - } - void observe_data(std::uint64_t revision, std::size_t item_count, - std::size_t auxiliary_item_count = 0); using Prepare_Task_Function = std::function; using Paint_Task_Function = @@ -87,12 +47,6 @@ protected: Renderable_Graph_Builder& builder, std::string logical_key, std::string name, Paint_Task_Function function); private: - [[nodiscard]] Renderable& renderable() noexcept; - void observe_state(Renderable_Observer_Event event, std::uint64_t time_ns, - std::uint64_t cache_update_count, - std::uint64_t publish_count); - mutable std::mutex observation_mutex; - Renderable_Observation observation; friend class Renderable; friend class detail::Renderable_State_Observer; }; diff --git a/render_2D/render_2D/scene/Plot_Frame_Mode.h b/render_2D/render_2D/scene/Plot_Frame_Mode.h new file mode 100644 index 0000000..66c117f --- /dev/null +++ b/render_2D/render_2D/scene/Plot_Frame_Mode.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +namespace renderive { + +enum class Plot_Frame_Mode : std::uint8_t { + Manual, + Low_Latency, + Playback, +}; + +} // namespace renderive diff --git a/render_2D/render_2D/scene/Plot_Frame_Status.h b/render_2D/render_2D/scene/Plot_Frame_Status.h deleted file mode 100644 index 66c960e..0000000 --- a/render_2D/render_2D/scene/Plot_Frame_Status.h +++ /dev/null @@ -1,63 +0,0 @@ -#pragma once - -#include -#include - -namespace renderive { - -enum class Plot_Frame_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; -}; - -// A value result assembled at the read boundary. It does not own or mirror -// the scene observer state. -struct Plot_Frame_Status { - Plot_Frame_Mode mode{Plot_Frame_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{}; -}; - -} // namespace renderive diff --git a/render_2D/render_2D/scene/Render_Scene_2D.cpp b/render_2D/render_2D/scene/Render_Scene_2D.cpp index 7ec6d62..b2c9f8d 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.cpp +++ b/render_2D/render_2D/scene/Render_Scene_2D.cpp @@ -1,222 +1,267 @@ #include #include "Render_Scene_2D.h" -#include "detail/Plot_Scene_Model.hpp" +#include +#include "../frame_control/Plot_Frame_Control.h" #include "../plottable/Performance_Overlay.h" +#include "../render/Blend2D_Cache.h" #include "../renderable/Renderable.h" -#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include namespace renderive::detail { -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); +void record_performance_frame(Performance_Overlay& overlay, + double duration_ms, + double frequency_hz, + std::uint64_t next_refresh_interval_ns, + std::size_t renderable_count, Size viewport) { + overlay.record_frame(duration_ms, frequency_hz, + next_refresh_interval_ns, + renderable_count, viewport); } -} -namespace renderive { namespace { -using Manual_Plot_Scene = Plot_Scene_Model; -using Low_Latency_Plot_Scene = Plot_Scene_Model; -using Playback_Plot_Scene = Plot_Scene_Model; +Rect full_rect(Size size) { + return {0, 0, std::max(0, size.width), std::max(0, size.height)}; } -struct Render_Scene_2D::Impl { - using Model = std::variant, - std::unique_ptr, - std::unique_ptr>; - explicit Impl(Plot_Frame_Mode mode) : mode(mode), model(create(mode)) {} - template - decltype(auto) apply(Operation&& operation) { - return std::visit( - [&operation](auto& scene) -> decltype(auto) { - return operation(*scene); - }, - model); +} // namespace +} // namespace renderive::detail +namespace renderive { +struct Render_Scene_2D::Impl +: Render_Scene_2D::next_Impl, + Scene_Base::Impl::Frame_Control_Capability, + Scene_Base::Impl::Scene_State_Capability, + Scene_Base::Impl::Raster_Capabilities, + Scene_Base::Impl::Compositor { + using State_Storage = + Published_State_Storage>; + using Observer = Render_Scene_2D::Observer; + explicit Impl(std::pmr::memory_resource& resource) + : next_Impl(resource), scene_state(State{}), + final_color_cache(resource) {} + std::shared_ptr + frame_control_strategy() override { + return frame_control->runtime(); } - template - decltype(auto) apply(Operation&& operation) const { - return std::visit( - [&operation](const auto& scene) -> decltype(auto) { - return operation(*scene); - }, - model); + std::shared_ptr + frame_control_strategy() const override { + return frame_control->runtime(); } - static Model create(Plot_Frame_Mode mode) { - switch (mode) { - case Plot_Frame_Mode::Manual: - return std::make_unique(); - case Plot_Frame_Mode::Low_Latency: - return std::make_unique(); - case Plot_Frame_Mode::Playback: - return std::make_unique(); - } - ::renderive::error::unexpected("unknown Plot_Scene frame mode"); + std::shared_ptr + make_renderable_color_cache() override { + std::pmr::polymorphic_allocator + allocator(&memory_resource()); + return std::allocate_shared( + allocator, memory_resource()); } - Plot_Frame_Mode mode; - Model model; + Frame_Viewport frame_viewport() const override { + const auto viewport = scene_state.published_state().viewport; + return {viewport.width, viewport.height}; + } + void begin_composite(const Composite_Render_Context&) override { + final_color_cache.clear(); + } + void composite(const Composite_Render_Context& context) override { + if (context.renderable && context.renderable->visible && + context.color_cache) + final_color_cache.composite(*context.color_cache); + } + void before_scene_state_swap() override { + const auto pending = scene_state.read( + [](const Render_Scene_2D::State& value) { return value; }); + const auto published = scene_state.published_state(); + if (pending.viewport != published.viewport) + invalidate_renderables(); + else if (pending != published) + scene().notify_model_dirty(); + } + void swap_scene_state() override { + scene_state.publish(); + } + std::uint64_t acquire_scene_state() override { + return scene_state.state_revision(); + } + void capture_scene_state( + Frame_Render_Snapshot& snapshot) const override { + snapshot.set_scene_state(scene_state.published_state()); + } + State_Storage scene_state; + detail::Blend2D_Color_Cache final_color_cache; + std::unique_ptr frame_control; + mutable std::mutex control_mutex; + std::weak_ptr presentation_sink; + std::shared_ptr performance; + std::atomic_bool active{}; }; Render_Scene_2D::Render_Scene_2D(Plot_Scene_Options options) - : impl_(std::make_unique(options.frame_mode)) {} -Render_Scene_2D::~Render_Scene_2D() = default; -void Render_Scene_2D::init() { - impl_->apply([](auto& scene) { - scene.init(); - }); + : scene::scene_base( + *::renderive::memory_resource(Memory_Domain::Plot_Frame), + std::make_unique( + *::renderive::memory_resource(Memory_Domain::Plot_Frame))) { + auto& impl = d_func(); + impl.frame_control = std::make_unique( + options.frame_mode, impl.memory_resource()); } -renderive_Owner Render_Scene_2D::root_renderable() const { - return impl_->apply([](const auto& scene) { - return scene.root_renderable(); - }); +Render_Scene_2D::~Render_Scene_2D() { + d_func().shutdown(); } -Scene_2D_Base::Attach_Builder Render_Scene_2D::attach_builder() { - return static_cast(render_scene()).attach_builder(); +std::unique_ptr +Render_Scene_2D::Builder::build() const { + auto result = std::unique_ptr( + new Render_Scene_2D(options_)); + auto root = detail::make_renderable_group(true); + root->set_object_name("root"); + auto builder = result->attach_builder(); + if (builder.attach(root) != Scene_Edit_Error::none) + ::renderive::error::unexpected( + "validated plot root attachment failed"); + return result; } -Scene_Base::Edit_Operation Render_Scene_2D::edit_renderables(Scene_2D_Base::Renderable_Edit edit) { - return static_cast(render_scene()).edit_renderables(std::move(edit)); +renderive_Owner<::renderive::Renderable> +Render_Scene_2D::root_renderable() const { + const auto topology = topology_snapshot(); + for (const auto& relationship : topology.display) { + if (relationship.parent) + continue; + auto base = renderive_const_owner_cast<::Renderable_Base>( + relationship.child); + if (auto renderable = + renderive_dynamic_owner_cast<::renderive::Renderable>(base)) + return renderable; + } + return {}; } -Scene_Base& Render_Scene_2D::render_scene() noexcept { - return impl_->apply([](auto& scene) -> Scene_Base& { - return scene; - }); -} -const Scene_Base& Render_Scene_2D::render_scene() const noexcept { - return impl_->apply([](const auto& scene) -> const Scene_Base& { - return scene; - }); -} -void Render_Scene_2D::set_background_color(Color color) { - impl_->apply([color](auto& scene) { - scene.set_background_color(color); - }); -} -Color Render_Scene_2D::background_color() const { - return impl_->apply([](const auto& scene) { - return scene.background_color(); - }); -} -void Render_Scene_2D::set_viewport_size(Size size) { - impl_->apply([size](auto& scene) { - scene.set_viewport_size(size); - }); -} -Size Render_Scene_2D::viewport_size() const { - return impl_->apply([](const auto& scene) { - return scene.viewport_size(); - }); +Render_Scene_2D::Observer Render_Scene_2D::observer() const { + const auto& frame_control = *d_func().frame_control; + return { + {}, + frame_control.frequency_hz(), + frame_control.next_refresh_interval_ns(), + }; } void Render_Scene_2D::dispatch_event(const Event& event) { - impl_->apply([&event](auto& scene) { - scene.dispatch_event(event); - }); + const auto paint_order = paint_order_snapshot(); + for (auto iterator = paint_order.rbegin(); iterator != paint_order.rend(); + ++iterator) { + auto base = renderive_const_owner_cast<::Renderable_Base>(*iterator); + if (auto renderable = + renderive_dynamic_owner_cast<::renderive::Renderable>(base)) { + if (renderable->is_visible()) + renderable->dispatch_event(event); + if (event.is_accepted()) + break; + } + } } -tl::expected Render_Scene_2D::prepare_frame() { - return impl_->apply([](auto& scene) { - return scene.prepare_frame(); - }); +auto Render_Scene_2D::prepare_frame() + -> Expected { + return d_func().frame_control->prepare(*this); } -tl::expected Render_Scene_2D::refresh_manual_frame() { - return impl_->apply([](auto& scene) { - return scene.refresh_manual_frame(); - }); +auto Render_Scene_2D::refresh_manual_frame() + -> Expected { + return d_func().frame_control->refresh(*this); } -tl::expected Render_Scene_2D::render_prepared_frame() { - return impl_->apply([](auto& scene) { - return scene.render_prepared_frame(); - }); +auto Render_Scene_2D::render_prepared_frame() + -> Expected { + return d_func().frame_control->render(*this); } bool Render_Scene_2D::discard_pending_frame() { - return impl_->apply([](auto& scene) { - return scene.discard_pending_frame(); - }); + return d_func().frame_control->discard(*this); } -tl::expected -Render_Scene_2D::render_frame(bool force) { - return impl_->apply([force](auto& scene) { - return scene.render_frame(force); - }); +auto Render_Scene_2D::render_frame(bool force) + -> Expected { + if (!view_active() && !force) + return Plot_Render_Status::view_inactive; + const Size viewport = get<&State::viewport>(); + if (viewport.empty()) + return Plot_Render_Status::empty_viewport; + if (!d_func().consume_model_dirty() && !force) + return Plot_Render_Status::model_unchanged; + const auto started = std::chrono::steady_clock::now(); + try { + if (const auto result = d_func().frame_control->request(*this); + !result) + return unexpected(result.error()); + } + catch (...) { + notify_model_dirty(); + ::renderive::error::unexpected( + "rolling back plot frame preparation", + std::current_exception()); + } + const auto finished = std::chrono::steady_clock::now(); + const double duration_ms = + std::chrono::duration(finished - started).count(); + std::shared_ptr sink; + std::shared_ptr overlay; + { + auto& impl = d_func(); + std::lock_guard lock(impl.control_mutex); + sink = impl.presentation_sink.lock(); + overlay = impl.performance; + } + if (overlay) { + const auto& frame_control = *d_func().frame_control; + detail::record_performance_frame( + *overlay, duration_ms, frame_control.frequency_hz(), + frame_control.next_refresh_interval_ns(), + renderable_count(), viewport); + } + if (sink) + sink->request_present(detail::full_rect(viewport)); + return Plot_Render_Status::rendered; } -void Render_Scene_2D::with_frame(const std::function& consumer) { - impl_->apply([&consumer](auto& scene) { - scene.with_frame(consumer); - }); +void Render_Scene_2D::with_frame( + const std::function& consumer) { + if (!consumer) + return; + const auto consume = [this, &consumer] { + consumer(d_func().final_color_cache.view()); + }; + if (d_func().is_render_execution_context()) { + consume(); + return; + } + auto lock = d_func().lock_render_idle(); + consume(); } void Render_Scene_2D::activate_view() { - impl_->apply([](auto& scene) { - scene.activate_view(); - }); + d_func().active.store(true, std::memory_order_release); + notify_model_dirty(); } void Render_Scene_2D::deactivate_view() noexcept { - impl_->apply([](auto& scene) { - scene.deactivate_view(); - }); + d_func().active.store(false, std::memory_order_release); } bool Render_Scene_2D::view_active() const noexcept { - return impl_->apply([](const auto& scene) { - return scene.view_active(); - }); + return d_func().active.load(std::memory_order_acquire); } void Render_Scene_2D::request_redraw() noexcept { - render_scene().notify_model_dirty(); + notify_model_dirty(); } -tl::expected -Render_Scene_2D::set_max_render_fps(double fps) { - return impl_->apply([fps](auto& scene) { - return scene.set_max_render_fps(fps); - }); +void Render_Scene_2D::set_presentation_sink( + std::weak_ptr sink) { + auto& impl = d_func(); + std::lock_guard lock(impl.control_mutex); + impl.presentation_sink = std::move(sink); } -tl::expected -Render_Scene_2D::clear_max_render_fps() { - return impl_->apply([](auto& scene) { - return scene.clear_max_render_fps(); - }); -} -double Render_Scene_2D::max_render_fps() const { - return impl_->apply([](const auto& scene) { - return scene.max_render_fps(); - }); -} -tl::expected -Render_Scene_2D::set_consumer_feedback(Frame_Consumer_Feedback feedback) { - return impl_->apply([feedback](auto& scene) { - return scene.set_consumer_feedback(feedback); - }); -} -tl::expected -Render_Scene_2D::clear_consumer_feedback() { - return impl_->apply([](auto& scene) { - return scene.clear_consumer_feedback(); - }); -} -Plot_Frame_Mode Render_Scene_2D::frame_mode() const noexcept { - return impl_->mode; -} -Plot_Frame_Status Render_Scene_2D::frame_status() const { - return impl_->apply([](const auto& scene) { - return scene.frame_status(); - }); -} -Low_Latency_Diagnostics Render_Scene_2D::diagnostics() const { - return impl_->apply([](const auto& scene) { - return scene.diagnostics(); - }); -} -Refresh_Control_Snapshot Render_Scene_2D::refresh_feedback_snapshot() const { - return impl_->apply( - [](const auto& scene) { - return scene.refresh_feedback_snapshot(); - }); -} -void Render_Scene_2D::set_presentation_sink(std::weak_ptr sink) { - impl_->apply([&sink](auto& scene) { - scene.set_presentation_sink(std::move(sink)); - }); -} -std::shared_ptr Render_Scene_2D::performance_overlay() const { - return impl_->apply([](const auto& scene) { - return scene.performance_overlay(); - }); +std::shared_ptr +Render_Scene_2D::performance_overlay() const { + const auto& impl = d_func(); + std::lock_guard lock(impl.control_mutex); + return impl.performance; } void Render_Scene_2D::set_performance_overlay( std::shared_ptr overlay) { - impl_->apply([&overlay](auto& scene) { - scene.set_performance_overlay(std::move(overlay)); - }); + { + auto& impl = d_func(); + std::lock_guard lock(impl.control_mutex); + impl.performance = std::move(overlay); + } + notify_model_dirty(); } } // namespace renderive diff --git a/render_2D/render_2D/scene/Render_Scene_2D.h b/render_2D/render_2D/scene/Render_Scene_2D.h index d597b45..ef089d5 100644 --- a/render_2D/render_2D/scene/Render_Scene_2D.h +++ b/render_2D/render_2D/scene/Render_Scene_2D.h @@ -2,16 +2,17 @@ #include "../base/Types.h" #include "../event/Event.h" #include "../renderable/Renderable.h" -#include "Plot_Frame_Status.h" +#include "Plot_Frame_Mode.h" +#include #include #include #include #include -#include #include class Scene_Base; namespace renderive { struct Performance_Overlay; +struct Scene2D_Frame_Data : Abstract_Frame {}; struct Plot_Scene_Options { Plot_Frame_Mode frame_mode{Plot_Frame_Mode::Low_Latency}; }; @@ -63,53 +64,55 @@ struct Presentation_Sink { virtual ~Presentation_Sink() = default; virtual void request_present(Rect dirty_rect) = 0; }; -class LIB_DECL Render_Scene_2D final { +class LIB_DECL Render_Scene_2D final +: public scene::scene_base { public: - explicit Render_Scene_2D(Plot_Scene_Options options = {}); - ~Render_Scene_2D(); - Render_Scene_2D(const Render_Scene_2D&) = delete; - Render_Scene_2D& operator=(const Render_Scene_2D&) = delete; - Render_Scene_2D(Render_Scene_2D&&) = delete; - Render_Scene_2D& operator=(Render_Scene_2D&&) = delete; - void init(); - [[nodiscard]] ::renderive_Owner root_renderable() const; - [[nodiscard]] Scene_2D_Base::Attach_Builder attach_builder(); - [[nodiscard]] Scene_Base::Edit_Operation edit_renderables(Scene_2D_Base::Renderable_Edit edit); - [[nodiscard]] Scene_Base& render_scene() noexcept; - [[nodiscard]] const Scene_Base& render_scene() const noexcept; - void set_background_color(Color color); - [[nodiscard]] Color background_color() const; - void set_viewport_size(Size size); - [[nodiscard]] Size viewport_size() const; + using Frame = Scene2D_Frame_Data; + struct State : next_State { + Size viewport; + Color background = Color::black(); + [[nodiscard]] bool operator==(const State& other) const { + return viewport == other.viewport && + background == other.background; + } + }; + struct Observer + : scene::scene_base::Observer { + double frequency_hz{}; + std::uint64_t next_refresh_interval_ns{}; + }; + class Builder : public next_Builder { + public: + Builder() = default; + explicit Builder(Plot_Scene_Options options) : options_(options) {} + Builder& options(Plot_Scene_Options value) noexcept { + options_ = value; + return *this; + } + [[nodiscard]] std::unique_ptr build() const; + private: + Plot_Scene_Options options_; + }; + [[nodiscard]] ::renderive_Owner<::renderive::Renderable> root_renderable() const; + [[nodiscard]] Observer observer() const; void dispatch_event(const Event& event); - [[nodiscard]] tl::expected prepare_frame(); - [[nodiscard]] tl::expected refresh_manual_frame(); - [[nodiscard]] tl::expected render_prepared_frame(); - [[nodiscard]] bool discard_pending_frame(); - [[nodiscard]] tl::expected - render_frame(bool force = false); + [[nodiscard]] auto prepare_frame() -> Expected; + [[nodiscard]] auto refresh_manual_frame() -> Expected; + [[nodiscard]] auto render_prepared_frame() -> Expected; + [[nodiscard]] auto discard_pending_frame() -> bool; + [[nodiscard]] auto render_frame(bool force = false) -> Expected; void with_frame(const std::function& consumer); void activate_view(); void deactivate_view() noexcept; [[nodiscard]] bool view_active() const noexcept; void request_redraw() noexcept; - [[nodiscard]] tl::expected - set_max_render_fps(double fps); - [[nodiscard]] tl::expected clear_max_render_fps(); - [[nodiscard]] double max_render_fps() const; - [[nodiscard]] tl::expected - set_consumer_feedback(Frame_Consumer_Feedback feedback); - [[nodiscard]] tl::expected - clear_consumer_feedback(); - [[nodiscard]] Plot_Frame_Mode frame_mode() const noexcept; - [[nodiscard]] Plot_Frame_Status frame_status() const; - [[nodiscard]] Low_Latency_Diagnostics diagnostics() const; - [[nodiscard]] Refresh_Control_Snapshot refresh_feedback_snapshot() const; void set_presentation_sink(std::weak_ptr sink); [[nodiscard]] std::shared_ptr performance_overlay() const; void set_performance_overlay(std::shared_ptr overlay); + ~Render_Scene_2D() override; private: struct Impl; - std::unique_ptr impl_; + explicit Render_Scene_2D(Plot_Scene_Options options); + friend struct scene::scene_base; }; } // namespace renderive diff --git a/render_2D/render_2D/scene/Scene_Context.hpp b/render_2D/render_2D/scene/Scene_Context.hpp deleted file mode 100644 index eaccea4..0000000 --- a/render_2D/render_2D/scene/Scene_Context.hpp +++ /dev/null @@ -1,148 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include -#include -#include "renderive/base/observer/Observer.hpp" -#include "renderive/frame_control/Frame_Control.hpp" -#include "renderive/renderable/color/Concepts.hpp" -#include "renderive/state/Published_State_Storage.hpp" -#include "renderive/scene/base/Scene_Base.hpp" -#include "renderive/scene/base/Frame_Viewport.hpp" -struct Scene2D_Frame_Data : Abstract_Frame {}; -struct Scene2D_State { - Frame_Viewport viewport; -}; -template -concept Scene2D_State_Value = State_Value && requires(State& state) { - requires std::same_as, Frame_Viewport>; -}; -template -concept Scene2D_Frame_Control_Constructible = std::constructible_from || std::constructible_from; -template , Color_Cache_Type Cache = Recording_Color_Cache, class State = Scene2D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>> -requires Frame_Control_Strategy_For && Scene2D_State_Value -class Scene2D_Context : public Scene_2D_Base, - public Published_State_Storage, - public Scene_Compositor { -public: - using Scene_State_Strategy = - Published_State_Storage; - using Frame_Control = Strategy; - using Frame = typename Frame_Control::Frame; - using Painter_Lease = typename Frame_Control::Painter_Lease; - using Render_Lease = typename Frame_Control::Render_Lease; - template - requires Scene2D_Frame_Control_Constructible - explicit Scene2D_Context(Args&&... args) : Scene2D_Context(*std::pmr::get_default_resource(), std::forward(args)...) {} - template - requires Scene2D_Frame_Control_Constructible - explicit Scene2D_Context(std::pmr::memory_resource& memory_resource, Args&&... args) - : Scene_2D_Base(memory_resource, std::make_unique()), - Scene_State_Strategy(State{}), frame_control(make_frame_control(this->memory_resource(), std::forward(args)...)), final_color_cache_(make_cache(this->memory_resource())) {} - template - requires Scene2D_Frame_Control_Constructible - Scene2D_Context(With_Observer state_observer, With_Observer scene_observer, Args&&... args) - : Scene2D_Context(*std::pmr::get_default_resource(), std::move(state_observer), std::move(scene_observer), std::forward(args)...) {} - template - requires Scene2D_Frame_Control_Constructible - Scene2D_Context(std::pmr::memory_resource& memory_resource, With_Observer state_observer, With_Observer scene_observer, Args&&... args) - : Scene_2D_Base( - memory_resource, - std::make_unique(std::move(scene_observer.observer))), - Scene_State_Strategy(State{}, std::move(state_observer)), frame_control(make_frame_control(this->memory_resource(), std::forward(args)...)), final_color_cache_(make_cache(this->memory_resource())) {} - ~Scene2D_Context() override { - this->shutdown(); - } - Frame_Control frame_control; - template - void with_final_color_cache(Function&& function) { - if (this->is_render_execution_context()) { - function(static_cast(final_color_cache_)); - return; - } - auto lock = this->lock_render_idle(); - function(static_cast(final_color_cache_)); - } -protected: - Frame_Control_Strategy_Base& frame_control_strategy_impl() noexcept override { - return frame_control; - } - const Frame_Control_Strategy_Base& frame_control_strategy_impl() const noexcept override { - return frame_control; - } - std::shared_ptr make_renderable_color_cache() override { - return make_cache_pointer(this->memory_resource()); - } - Frame_Viewport frame_viewport() const override { - return this->Scene_State_Strategy::render_state_value().viewport; - } - void begin_composite(const Composite_Render_Context&) override { - final_color_cache_.clear(); - } - void composite(const Composite_Render_Context& context) override { - if (context.renderable && context.renderable->visible && context.color_cache) - final_color_cache_.composite(*context.color_cache); - } - void publish_scene_state() override { - this->Scene_State_Strategy::publish(); - } - std::uint64_t acquire_scene_state() override { - return this->Scene_State_Strategy::state_revision(); - } - void capture_scene_state(Frame_Render_Snapshot& snapshot) const override { - snapshot.set_scene_state( - this->Scene_State_Strategy::render_state_value()); - } -private: - struct Context_State_Layer - : renderive::scene_inheritance::State_Node< - Context_State_Layer, Scene_2D_Base::State_Layer> {}; - - struct Impl - : renderive::scene_inheritance::Impl_Node { - struct Observer - : renderive::scene_inheritance::Observer_Node< - Impl, Scene_2D_Base::Impl::Observer> { - static void handle(Impl& layer, - const Scene_Base::Observation& observation) { - layer.scene_observer.observe(observation); - } - }; - - Impl() = default; - explicit Impl(Scene_Observer observer) - : scene_observer(std::move(observer)) {} - std::uint64_t now_ns() const noexcept override { - return scene_observer.now_ns(); - } - Scene_Observer scene_observer; - }; - - template - static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) { - if constexpr (std::constructible_from) { - return Frame_Control(memory_resource, std::forward(args)...); - } else { - return Frame_Control(std::forward(args)...); - } - } - static Cache make_cache(std::pmr::memory_resource& memory_resource) { - if constexpr (std::constructible_from) { - return Cache(memory_resource); - } else { - return Cache(); - } - } - static std::shared_ptr make_cache_pointer(std::pmr::memory_resource& memory_resource) { - std::pmr::polymorphic_allocator allocator(&memory_resource); - if constexpr (std::constructible_from) { - return std::allocate_shared(allocator, memory_resource); - } else { - return std::allocate_shared(allocator); - } - } - Cache final_color_cache_; -}; diff --git a/render_2D/render_2D/scene/detail/Plot_Scene_Model.hpp b/render_2D/render_2D/scene/detail/Plot_Scene_Model.hpp deleted file mode 100644 index 2b16a83..0000000 --- a/render_2D/render_2D/scene/detail/Plot_Scene_Model.hpp +++ /dev/null @@ -1,475 +0,0 @@ -#pragma once -#include -#include "../Render_Scene_2D.h" -#include "../../render/Blend2D_Cache.h" -#include -#include -#include -#include -#include -#include "../Scene_Context.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -namespace renderive { -struct Performance_Overlay; -struct Renderable; -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 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 data; - template - void observe(const Observation& observation) { - std::lock_guard lock(data->mutex); - data->last_event = static_cast(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; - } - } -}; -using Frame_Observer_State = ::Observer_State; -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 -concept Scene_Frame_Control = std::same_as || std::same_as || std::same_as; -template -constexpr Plot_Frame_Mode frame_control_mode() noexcept { - if constexpr (std::same_as) - return Plot_Frame_Mode::Manual; - if constexpr (std::same_as) - return Plot_Frame_Mode::Low_Latency; - return Plot_Frame_Mode::Playback; -} -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(Plot_Frame_Mode mode, std::uint32_t event) { - if (mode == Plot_Frame_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 == Plot_Frame_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 -struct Plot_Scene_Model final : ::Scene2D_Context { - using Kernel_Scene = ::Scene2D_Context; - using Scene_State_Strategy = typename Kernel_Scene::Scene_State_Strategy; - static constexpr Plot_Frame_Mode Mode = detail::frame_control_mode(); - Plot_Scene_Model() : Plot_Scene_Model(std::make_shared()) {} - ~Plot_Scene_Model() override = default; - Plot_Scene_Model(const Plot_Scene_Model&) = delete; - Plot_Scene_Model& operator=(const Plot_Scene_Model&) = delete; - void init() { - std::lock_guard lock(initialization_mutex_); - if (root_renderable()) - return; - auto root = detail::make_renderable_group(true); - root->set_object_name("root"); - auto builder = this->attach_builder(); - if (builder.attach(root) != Scene_Edit_Error::none) - ::renderive::error::unexpected("validated plot root attachment failed"); - } - [[nodiscard]] renderive_Owner root_renderable() const { - const auto topology = this->topology_snapshot(); - for (const auto& relationship : topology.display) { - if (relationship.parent) - continue; - auto base = renderive_const_owner_cast<::Renderable_Base>(relationship.child); - if (auto renderable = renderive_dynamic_owner_cast(base)) - return renderable; - } - return {}; - } - void set_background_color(Color color) { - if (background_color() == color) - return; - static_cast(*this).template set<&Scene_State::background>(color); - this->notify_model_dirty(); - } - [[nodiscard]] Color background_color() const { - return static_cast(*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(*this).template get<&Scene_State::viewport>() == viewport) - return; - static_cast(*this).template set<&Scene_State::viewport>(viewport); - this->invalidate_renderables(); - } - [[nodiscard]] Size viewport_size() const { - const auto viewport = static_cast(*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 = renderive_const_owner_cast<::Renderable_Base>(*iterator); - if (auto renderable = renderive_dynamic_owner_cast(base)) { - if (renderable->is_visible()) { - renderable->dispatch_event(event); - } - if (event.is_accepted()) - break; - } - } - } - [[nodiscard]] tl::expected prepare_frame() { - auto paint_frame = this->frame_control.acquire_painter(); - if (!paint_frame) - return tl::unexpected(Plot_Prepare_Frame_Error::painter_unavailable); - this->publish_frame_state(*paint_frame); - return {}; - } - [[nodiscard]] tl::expected - refresh_manual_frame() { - if constexpr (Mode == Plot_Frame_Mode::Manual) { - if (this->frame_control.refresh() != Manual_Refresh_Result::none) - return tl::unexpected(Plot_Refresh_Manual_Frame_Error::no_pending_frame); - return {}; - } - return tl::unexpected( - Plot_Refresh_Manual_Frame_Error::unsupported_frame_mode); - } - [[nodiscard]] tl::expected - render_prepared_frame() { - auto render_frame = this->frame_control.acquire_renderer(); - if (!render_frame) - return tl::unexpected( - Plot_Render_Prepared_Frame_Error::renderer_unavailable); - switch (Scene_Base::render(*render_frame)) { - case Scene_Render_Result::none: - return {}; - case Scene_Render_Result::cancelled: - return tl::unexpected(Plot_Render_Prepared_Frame_Error::cancelled); - case Scene_Render_Result::deadline_exceeded: - return tl::unexpected( - Plot_Render_Prepared_Frame_Error::deadline_exceeded); - case Scene_Render_Result::external_failure: - return tl::unexpected( - Plot_Render_Prepared_Frame_Error::external_failure); - case Scene_Render_Result::shutting_down: - return tl::unexpected( - Plot_Render_Prepared_Frame_Error::scene_shutting_down); - } - ::renderive::error::unexpected("unknown scene render error"); - } - [[nodiscard]] bool discard_pending_frame() { - if constexpr (Mode == Plot_Frame_Mode::Playback) - return false; - else - return this->frame_control.discard_pending_frame(); - } - [[nodiscard]] tl::expected - render_frame(bool force = false) { - if (!view_active() && !force) - return Plot_Render_Status::view_inactive; - if (viewport_size().empty()) - return Plot_Render_Status::empty_viewport; - if (!this->consume_model_dirty() && !force) - return Plot_Render_Status::model_unchanged; - const auto started = std::chrono::steady_clock::now(); - Size viewport; - try { - if (const auto result = prepare_frame(); !result) - return tl::unexpected(Plot_Render_Frame_Error::painter_unavailable); - const auto frame_viewport = this->Scene_State_Strategy::render_state_value().viewport; - viewport = {frame_viewport.width, frame_viewport.height}; - if constexpr (Mode == Plot_Frame_Mode::Manual) { - if (const auto result = refresh_manual_frame(); !result) { - switch (result.error()) { - case Plot_Refresh_Manual_Frame_Error::unsupported_frame_mode: - return tl::unexpected( - Plot_Render_Frame_Error::manual_refresh_not_supported); - case Plot_Refresh_Manual_Frame_Error::no_pending_frame: - return tl::unexpected( - Plot_Render_Frame_Error::no_pending_frame); - } - ::renderive::error::unexpected( - "unknown manual refresh error"); - } - } - if (const auto result = render_prepared_frame(); !result) { - switch (result.error()) { - case Plot_Render_Prepared_Frame_Error::renderer_unavailable: - return tl::unexpected( - Plot_Render_Frame_Error::renderer_unavailable); - case Plot_Render_Prepared_Frame_Error::cancelled: - return tl::unexpected(Plot_Render_Frame_Error::cancelled); - case Plot_Render_Prepared_Frame_Error::deadline_exceeded: - return tl::unexpected( - Plot_Render_Frame_Error::deadline_exceeded); - case Plot_Render_Prepared_Frame_Error::external_failure: - return tl::unexpected( - Plot_Render_Frame_Error::external_failure); - case Plot_Render_Prepared_Frame_Error::scene_shutting_down: - return tl::unexpected( - Plot_Render_Frame_Error::scene_shutting_down); - } - ::renderive::error::unexpected( - "unknown prepared frame render error"); - } - } - catch (...) { - this->notify_model_dirty(); - ::renderive::error::unexpected( - "rolling back plot frame preparation", std::current_exception()); - } - const auto finished = std::chrono::steady_clock::now(); - const double duration_ms = std::chrono::duration(finished - started).count(); - std::shared_ptr sink; - std::shared_ptr 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 Plot_Render_Status::rendered; - } - void with_frame(const std::function& consumer) { - if (!consumer) - return; - this->with_final_color_cache([&consumer](const detail::Blend2D_Color_Cache& cache) { - consumer(cache.view()); - }); - } - void activate_view() { - 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); - } - [[nodiscard]] tl::expected - set_max_render_fps(double fps) { - if constexpr (Mode != Plot_Frame_Mode::Low_Latency) { - return tl::unexpected( - Plot_Set_Max_Render_Fps_Error::unsupported_frame_mode); - } - else { - using Error = typename Frame_Control::Control_Error; - const auto error = this->frame_control.set_frequency_hz(fps); - if (error != Error::none) - return tl::unexpected(Plot_Set_Max_Render_Fps_Error::invalid_fps); - this->notify_model_dirty(); - return {}; - } - } - [[nodiscard]] tl::expected - clear_max_render_fps() { - if constexpr (Mode != Plot_Frame_Mode::Low_Latency) { - return tl::unexpected( - Plot_Clear_Max_Render_Fps_Error::unsupported_frame_mode); - } - else { - this->frame_control.clear_frequency_limit(); - this->notify_model_dirty(); - return {}; - } - } - [[nodiscard]] tl::expected - set_consumer_feedback(Frame_Consumer_Feedback feedback) { - if constexpr (Mode != Plot_Frame_Mode::Low_Latency) { - return tl::unexpected( - Plot_Set_Consumer_Feedback_Error::unsupported_frame_mode); - } - else { - using Error = typename Frame_Control::Control_Error; - const auto error = this->frame_control.set_consumer_feedback(feedback); - if (error != Error::none) - return tl::unexpected( - Plot_Set_Consumer_Feedback_Error::invalid_consumer_feedback); - return {}; - } - } - [[nodiscard]] tl::expected - clear_consumer_feedback() { - if constexpr (Mode != Plot_Frame_Mode::Low_Latency) { - return tl::unexpected( - Plot_Clear_Consumer_Feedback_Error::unsupported_frame_mode); - } - else { - this->frame_control.clear_consumer_feedback(); - return {}; - } - } - [[nodiscard]] double max_render_fps() const { - if constexpr (Mode == Plot_Frame_Mode::Low_Latency) - return this->frame_control.state().frequency_hz; - return std::numeric_limits::quiet_NaN(); - } - [[nodiscard]] Low_Latency_Diagnostics diagnostics() const { - if constexpr (Mode == Plot_Frame_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_status(); - return {view_active(), {std::numeric_limits::quiet_NaN(), observed.consumed_frame_count, 0}}; - } - [[nodiscard]] Refresh_Control_Snapshot refresh_feedback_snapshot() const { - return diagnostics().refresh; - } - [[nodiscard]] static constexpr Plot_Frame_Mode frame_control_mode() noexcept { - return Mode; - } - [[nodiscard]] Plot_Frame_Status frame_status() const { - Plot_Frame_Status 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.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 == Plot_Frame_Mode::Manual) { - const auto state = this->frame_control.state(); - snapshot.produced_frame_count = state.prepared_frame_count; - snapshot.consumed_frame_count = state.render_count; - snapshot.dropped_frame_count = state.replaced_prepared_frame_count + - state.discarded_prepared_frame_count; - snapshot.failed_operation_count = state.failed_refresh_count; - snapshot.pending_frame_count = state.pending_frame ? 1U : 0U; - } - else if constexpr (Mode == Plot_Frame_Mode::Playback) { - const auto state = this->frame_control.state(); - snapshot.produced_frame_count = state.enqueued_frame_count; - snapshot.consumed_frame_count = state.rendered_frame_count; - snapshot.failed_operation_count = state.empty_acquire_count; - snapshot.pending_frame_count = state.pending_frame_count; - } - else { - const auto state = this->frame_control.state(); - const auto counters = this->frame_control.counter_statistics(); - snapshot.produced_frame_count = counters.published_frame_count; - snapshot.consumed_frame_count = state.completed_lifecycle_count; - snapshot.dropped_frame_count = counters.abandoned_frame_count + - counters.manually_discarded_frame_count; - snapshot.failed_operation_count = counters.swap_failure_count; - snapshot.pending_frame_count = this->frame_control.pending_frame_count(); - snapshot.limit_state = detail::limit_state_name(static_cast(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 sink) { - std::lock_guard lock(control_mutex_); - presentation_sink_ = std::move(sink); - } - [[nodiscard]] std::shared_ptr performance_overlay() const { - std::lock_guard lock(control_mutex_); - return performance_; - } - void set_performance_overlay(std::shared_ptr overlay) { - { - std::lock_guard lock(control_mutex_); - performance_ = std::move(overlay); - } - this->notify_model_dirty(); - } -private: - explicit Plot_Scene_Model(std::shared_ptr observer) - : Kernel_Scene(*memory_resource(Memory_Domain::Plot_Frame), detail::Frame_Observer_State(detail::Frame_Observer{observer})), observer_(std::move(observer)) {} - std::shared_ptr observer_; - mutable std::mutex control_mutex_; - std::mutex initialization_mutex_; - std::weak_ptr presentation_sink_; - std::shared_ptr performance_; - std::atomic_bool active_{}; -}; -} diff --git a/render_3D/render_3D/Point_Visual.cpp b/render_3D/render_3D/Point_Visual.cpp index fa23efb..541b880 100644 --- a/render_3D/render_3D/Point_Visual.cpp +++ b/render_3D/render_3D/Point_Visual.cpp @@ -40,11 +40,6 @@ using Point_Buffer_Strategy = Point_Buffer_Layout, std::mutex, Observer_State<::Double_Buffer_Frame_Observer>>; -struct Point_Data_Observation { - std::uint64_t revision{}; - std::size_t point_count{}; -}; - } // namespace void Point_Visual::State_Validator::operator()(const State& state) const { @@ -60,17 +55,8 @@ void Point_Visual::State_Validator::operator()(const State& state) const { } struct Point_Visual::Impl : next_Impl, Point_Buffer_Strategy { - struct Observer : next_Observer { - static void handle(Impl& impl, - const Renderable_Event_View& observation) noexcept { - if (observation.event == Renderable_Observer_Event::Published) - impl.Point_Buffer_Strategy::publish(); - if (const auto* data = - observation.payload_if()) { - impl.observe_data(data->revision, data->point_count); - } - } - }; + using Observer = Point_Visual::Observer; + friend struct Point_Visual::Observer; explicit Impl(std::vector points) { if (update_points(std::move(points)) != Visual_Data_Error::none) @@ -82,11 +68,8 @@ struct Point_Visual::Impl : next_Impl, Point_Buffer_Strategy { [[nodiscard]] Visual_Data_Error update_points(std::vector points) { if (!valid_points(points)) return Visual_Data_Error::invalid_data; - const std::size_t count = points.size(); write( std::make_shared>(std::move(points))); - report_observation(Point_Data_Observation{ - cache_revision(), count}); return Visual_Data_Error::none; } [[nodiscard]] Visual_Data_Error edit_points(const std::function&)>& edit) { @@ -213,6 +196,11 @@ struct Point_Visual::Impl : next_Impl, Point_Buffer_Strategy { } }; +void Point_Visual::Observer::handle( + Impl& impl, const Renderable_Event_View& observation) noexcept { + if (observation.event == Renderable_Observer_Event::Published) + impl.Point_Buffer_Strategy::publish(); +} Point_Visual::Point_Visual(const State& state, std::vector points) : Renderable(With_Attached_Impl{}, state, std::move(points)) { diff --git a/render_3D/render_3D/Point_Visual.h b/render_3D/render_3D/Point_Visual.h index 66f40fc..e1ff3aa 100644 --- a/render_3D/render_3D/Point_Visual.h +++ b/render_3D/render_3D/Point_Visual.h @@ -83,8 +83,11 @@ struct Point_Visual : Renderable { ::renderive::render_3d::Renderable_Builder< Product, Properties, State_Validator>; +private: + friend struct ::renderive::renderable::attach; explicit Point_Visual(const State& state, std::vector points = {}); +public: ~Point_Visual() override; [[nodiscard]] Visual_Data_Error update_points(std::vector points); @@ -107,6 +110,11 @@ protected: private: struct Impl; +public: + struct Observer : next_Observer { + static void handle(Impl& impl, + const Renderable_Event_View& observation) noexcept; + }; }; } // namespace detail diff --git a/render_3D/render_3D/Render_Scene_3D.cpp b/render_3D/render_3D/Render_Scene_3D.cpp index ef26d59..2133585 100644 --- a/render_3D/render_3D/Render_Scene_3D.cpp +++ b/render_3D/render_3D/Render_Scene_3D.cpp @@ -1,14 +1,15 @@ #include + #include "Render_Scene_3D.h" -#include "Scene_Context.hpp" +#include #include "detail/Datoviz_Visual_Backend.h" #include "detail/Gpu_Completion_Service.h" #include "detail/Point_Core.h" #include "detail/Render_Domain.h" +#include "frame_control/Point_Frame_Control.h" -#include -#include +#include #include #include @@ -22,12 +23,12 @@ #include #include + namespace renderive::render_3d { namespace { -bool valid(Extent extent) noexcept { - return !extent.empty(); -} +bool valid(Extent extent) noexcept { return !extent.empty(); } + bool valid(Clear_Color color) noexcept { const auto component = [](float value) { return std::isfinite(value) && value >= 0.0F && value <= 1.0F; @@ -84,48 +85,20 @@ void publish_trace(Node_Execution_Metrics* metrics, std::move(trace.artifact_json)); } -struct Scene_Model { - virtual ~Scene_Model() = default; - virtual void resize(Extent extent) = 0; - virtual void set_clear_color(Clear_Color color) = 0; - [[nodiscard]] virtual Scene_Control_Error dispatch_pointer( - ::renderive::Event_Type type, float x, float y, - ::renderive::Mouse_Button button, - ::renderive::Keyboard_Modifier modifiers) = 0; - [[nodiscard]] virtual Scene_Control_Error dispatch_wheel( - float x, float y, float delta_x, float delta_y, - ::renderive::Keyboard_Modifier modifiers) = 0; - [[nodiscard]] virtual Scene_Control_Error dispatch_key( - const ::renderive::Key_Event& event) = 0; - [[nodiscard]] virtual Frame_Request_Result prepare_frame() = 0; - [[nodiscard]] virtual Frame_Request_Result refresh_manual_frame() = 0; - [[nodiscard]] virtual bool discard_pending_frame() = 0; - [[nodiscard]] virtual Frame_Request_Result render_prepared_frame() = 0; - [[nodiscard]] virtual Frame_Request_Result request_frame() = 0; - [[nodiscard]] virtual std::shared_ptr latest_frame() - const = 0; - [[nodiscard]] virtual Frame_Status frame_status() const = 0; - [[nodiscard]] virtual Runtime_Statistics runtime_statistics() const noexcept = 0; - [[nodiscard]] virtual Scene_Base& scene() noexcept = 0; - [[nodiscard]] virtual const Scene_Base& scene() const noexcept = 0; -}; - -using Manual_Frame = ::Manual_Refresh_Strategy<::Scene3D_Frame_Data>; -using Low_Latency_Frame = ::Low_Latency_Strategy<::Scene3D_Frame_Data>; -using Playback_Frame = ::Flow_Refresh_Strategy<::Scene3D_Frame_Data>; - struct Point_Backend_State final { explicit Point_Backend_State(std::uint32_t gpu_index) : render_domain(detail::Render_Domain::acquire(gpu_index)) {} ~Point_Backend_State() { if (backend) - static_cast(render_domain->invoke([this] { backend.reset(); })); + static_cast( + render_domain->invoke([this] { backend.reset(); })); } detail::Datoviz_Visual_Backend& require_backend() { if (!backend) - ::renderive::error::unexpected("Datoviz backend invariant is unavailable"); + ::renderive::error::unexpected( + "Datoviz backend invariant is unavailable"); return *backend; } [[nodiscard]] bool backend_available() const noexcept { @@ -137,6 +110,7 @@ struct Point_Backend_State final { std::lock_guard lock(frame_mutex); latest.reset(); } + std::shared_ptr render_domain; std::unique_ptr backend; std::atomic_bool quarantined{}; @@ -144,91 +118,50 @@ struct Point_Backend_State final { std::shared_ptr latest; }; -template -struct Basic_Point_Scene final - : ::Scene3D_Context, - ::Scene_Renderer, - Scene_Model { - using Kernel_Scene = ::Scene3D_Context; - using Scene_State_Strategy = typename Kernel_Scene::Scene_State_Strategy; +} // namespace - Basic_Point_Scene(const Scene_Options& options, - std::shared_ptr visual) - requires (!std::same_as) - : Kernel_Scene(), backend_state(std::make_shared(options.gpu_index)) { - initialize(options, std::move(visual)); - } +struct Render_Scene_3D::Impl + : Render_Scene_3D::next_Impl, + Scene_Base::Impl::Frame_Control_Capability, + Scene_Base::Impl::Scene_State_Capability, + Scene_Base::Impl::Renderer { + using State_Storage = Render_Acquired_State_Storage< + State, Atomic_Spin_Mutex, Observer_State<>>; - Basic_Point_Scene(const Scene_Options& options, - std::shared_ptr visual) - requires std::same_as - : Kernel_Scene(Observer_State<>{}, typename Strategy::Configuration{ - .frequency_hz = options.maximum_frames_per_second, - .replace_pending_frame = true}), - backend_state(std::make_shared(options.gpu_index)) { - initialize(options, std::move(visual)); - } + using Observer = Render_Scene_3D::Observer; - ~Basic_Point_Scene() override { - this->shutdown(); - backend_state.reset(); - } - - void initialize(const Scene_Options& options, - std::shared_ptr point_visual) { - if (!point_visual) - ::renderive::error::unexpected("Point_Scene requires a Point_Visual"); - visual = std::move(point_visual); - point_id = visual->renderable_id(); - this->Scene_State_Strategy::template set< - &detail::Scene_State::viewport>(options.viewport); - this->Scene_State_Strategy::template set< - &detail::Scene_State::clear_color>(options.clear_color); - this->Scene_State_Strategy::template set< - &detail::Scene_State::visual_family>(options.visual_family); - { - auto builder = this->attach_builder(); - if (builder.attach(renderive_Owner(visual)) != - Scene_Edit_Error::none) - ::renderive::error::unexpected("validated point visual attachment failed"); - } - const detail::Scene_State initial{ - options.viewport, options.clear_color, options.visual_family}; - const auto state = backend_state; - const auto invocation = state->render_domain->invoke([state, &options, &initial] { - state->backend = std::make_unique( - options.gpu_index, options.validation_enabled, initial); - }); - if (!invocation) - ::renderive::error::unexpected("render domain stopped during Point_Scene initialization"); - } - - void resize(Extent extent) override { - this->Scene_State_Strategy::template set< - &detail::Scene_State::viewport>(extent); - this->invalidate_renderables(); - } - - void set_clear_color(Clear_Color color) override { - this->Scene_State_Strategy::template set< - &detail::Scene_State::clear_color>(color); - this->notify_model_dirty(); + Impl(std::pmr::memory_resource& resource, const Scene_Options& options) + : next_Impl(resource), scene_state([&options] { + State state; + state.viewport = options.viewport; + state.clear_color = options.clear_color; + state.visual_family = options.visual_family; + return state; + }()), + backend_state( + std::make_shared(options.gpu_index)) { + if (!valid(options.viewport)) + ::renderive::error::unexpected( + "Point_Scene viewport must be nonempty"); + if (!valid(options.clear_color)) + ::renderive::error::unexpected( + "Point_Scene clear color must be in [0, 1]"); } Scene_Control_Error dispatch_pointer( - ::renderive::Event_Type type, float x, float y, - ::renderive::Mouse_Button button, - ::renderive::Keyboard_Modifier modifiers) override { - const Extent viewport = this->Scene_State_Strategy::template get< - &detail::Scene_State::viewport>(); - const auto state = backend_state; - if (!state->backend_available()) + Render_Scene_3D& scene, ::renderive::Event_Type type, float x, + float y, ::renderive::Mouse_Button button, + ::renderive::Keyboard_Modifier modifiers) { + const Extent viewport = + scene_state.template get<&State::viewport>(); + const auto backend = backend_state; + if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - const auto invocation = state->render_domain->invoke( - [state, type, x, y, button, modifiers, viewport] { - if (!state->backend_available()) + const auto invocation = backend->render_domain->invoke( + [backend, type, x, y, button, modifiers, viewport] { + if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - state->require_backend().dispatch_pointer( + backend->require_backend().dispatch_pointer( type, x, y, button, modifiers, viewport); return Scene_Control_Error::none; }); @@ -236,22 +169,23 @@ struct Basic_Point_Scene final return Scene_Control_Error::backend_unavailable; const auto error = *invocation.value; if (error == Scene_Control_Error::none) - this->notify_model_dirty(); + scene.notify_model_dirty(); return error; } + Scene_Control_Error dispatch_wheel( - float x, float y, float delta_x, float delta_y, - ::renderive::Keyboard_Modifier modifiers) override { - const Extent viewport = this->Scene_State_Strategy::template get< - &detail::Scene_State::viewport>(); - const auto state = backend_state; - if (!state->backend_available()) + Render_Scene_3D& scene, float x, float y, float delta_x, + float delta_y, ::renderive::Keyboard_Modifier modifiers) { + const Extent viewport = + scene_state.template get<&State::viewport>(); + const auto backend = backend_state; + if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - const auto invocation = state->render_domain->invoke( - [state, x, y, delta_x, delta_y, modifiers, viewport] { - if (!state->backend_available()) + const auto invocation = backend->render_domain->invoke( + [backend, x, y, delta_x, delta_y, modifiers, viewport] { + if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - state->require_backend().dispatch_wheel( + backend->require_backend().dispatch_wheel( x, y, delta_x, delta_y, modifiers, viewport); return Scene_Control_Error::none; }); @@ -259,157 +193,51 @@ struct Basic_Point_Scene final return Scene_Control_Error::backend_unavailable; const auto error = *invocation.value; if (error == Scene_Control_Error::none) - this->notify_model_dirty(); + scene.notify_model_dirty(); return error; } - Scene_Control_Error dispatch_key( - const ::renderive::Key_Event& event) override { - const auto state = backend_state; - if (!state->backend_available()) + + Scene_Control_Error dispatch_key(Render_Scene_3D& scene, + const ::renderive::Key_Event& event) { + const auto backend = backend_state; + if (!backend->backend_available()) return Scene_Control_Error::backend_unavailable; - const auto invocation = state->render_domain->invoke([state, event] { - if (!state->backend_available()) - return Scene_Control_Error::backend_unavailable; - state->require_backend().dispatch_key(event); - return Scene_Control_Error::none; - }); + const auto invocation = backend->render_domain->invoke( + [backend, event] { + if (!backend->backend_available()) + return Scene_Control_Error::backend_unavailable; + backend->require_backend().dispatch_key(event); + return Scene_Control_Error::none; + }); if (!invocation) return Scene_Control_Error::backend_unavailable; const auto error = *invocation.value; if (error == Scene_Control_Error::none) - this->notify_model_dirty(); + scene.notify_model_dirty(); return error; } - [[nodiscard]] Frame_Request_Result prepare_frame() override { - auto painter = this->frame_control.acquire_painter(); - if (!painter) - return Frame_Request_Result::painter_unavailable; - this->publish_frame_state(*painter); - return Frame_Request_Result::none; - } - - [[nodiscard]] Frame_Request_Result refresh_manual_frame() override { - if constexpr (Mode == Frame_Mode::Manual) { - return this->frame_control.refresh() == Manual_Refresh_Result::none - ? Frame_Request_Result::none - : Frame_Request_Result::no_pending_frame; - } - return Frame_Request_Result::manual_refresh_not_supported; - } - - [[nodiscard]] bool discard_pending_frame() override { - if constexpr (Mode == Frame_Mode::Playback) - return false; - else - return this->frame_control.discard_pending_frame(); - } - - [[nodiscard]] Frame_Request_Result render_prepared_frame() override { - auto renderer = this->frame_control.acquire_renderer(); - if (!renderer) - return Frame_Request_Result::renderer_unavailable; - switch (Scene_Base::render(*renderer)) { - case Scene_Render_Result::none: - return Frame_Request_Result::none; - case Scene_Render_Result::cancelled: - return Frame_Request_Result::cancelled; - case Scene_Render_Result::deadline_exceeded: - return Frame_Request_Result::deadline_exceeded; - case Scene_Render_Result::external_failure: - return Frame_Request_Result::external_failure; - case Scene_Render_Result::shutting_down: - return Frame_Request_Result::scene_shutting_down; - } - ::renderive::error::unexpected("unknown scene render error"); - } - - [[nodiscard]] Frame_Request_Result request_frame() override { - if (const auto error = prepare_frame(); error != Frame_Request_Result::none) - return error; - if constexpr (Mode == Frame_Mode::Manual) { - if (const auto error = refresh_manual_frame(); error != Frame_Request_Result::none) - return error; - } - return render_prepared_frame(); - } - - [[nodiscard]] std::shared_ptr latest_frame() - const override { - const auto state = backend_state; - std::lock_guard lock(state->frame_mutex); - return state->latest; - } - - [[nodiscard]] Frame_Status frame_status() const override { - Frame_Status result; - result.mode = Mode; - if constexpr (Mode == Frame_Mode::Manual) { - const auto state = this->frame_control.state(); - result.produced_frame_count = state.prepared_frame_count; - result.consumed_frame_count = state.render_count; - result.dropped_frame_count = state.replaced_prepared_frame_count + - state.discarded_prepared_frame_count; - result.failed_operation_count = state.failed_refresh_count; - result.pending_frame_count = state.pending_frame ? 1U : 0U; - result.latest_sequence = state.render_frame_sequence; - } else if constexpr (Mode == Frame_Mode::Low_Latency) { - const auto state = this->frame_control.state(); - const auto counters = this->frame_control.counter_statistics(); - result.frequency_hz = state.frequency_hz; - result.produced_frame_count = counters.published_frame_count; - result.consumed_frame_count = counters.render_pass_count; - result.dropped_frame_count = counters.abandoned_frame_count + - counters.manually_discarded_frame_count; - result.failed_operation_count = counters.swap_failure_count; - result.pending_frame_count = - this->frame_control.pending_frame_count(); - result.latest_sequence = state.frame_sequence; - result.next_refresh_interval_ns = state.next_refresh_interval_ns; - } else { - const auto state = this->frame_control.state(); - result.produced_frame_count = state.enqueued_frame_count; - result.consumed_frame_count = state.rendered_frame_count; - result.failed_operation_count = state.empty_acquire_count; - result.pending_frame_count = state.pending_frame_count; - } + [[nodiscard]] std::shared_ptr latest_frame() const { const auto backend = backend_state; std::lock_guard lock(backend->frame_mutex); - if (backend->latest) - result.latest_sequence = - std::max(result.latest_sequence, backend->latest->sequence); - return result; + return backend->latest; } - [[nodiscard]] Runtime_Statistics runtime_statistics() const noexcept override { - const auto render = backend_state->render_domain->statistics(); - const auto completion = detail::Gpu_Completion_Service::instance().statistics(); - return { - {render.capacity, render.admitted, render.peak_admitted, render.queued, - render.peak_queued, render.backpressure_count, render.backpressure_wait_ns}, - {completion.capacity, completion.in_flight, completion.peak_in_flight, - completion.watched, completion.peak_watched, completion.backpressure_count, - completion.backpressure_wait_ns}, - completion.fault_count, - completion.abandoned_count - }; - } - - [[nodiscard]] Scene_Base& scene() noexcept override { return *this; } - [[nodiscard]] const Scene_Base& scene() const noexcept override { - return *this; + [[nodiscard]] State render_state_snapshot() const { + return scene_state.render_state_snapshot(); } Node_Execution_Result render_scene( - const Scene_Render_Context& context) override { + const Scene_Render_Context& context) { if (!backend_state->backend_available()) return Node_Execution_Result::failed( External_Operation_Result::external_failure); - const auto prepared = context.prepared(point_id); + const auto prepared = + context.prepared(point_id); if (!prepared) - ::renderive::error::unexpected("Point_Visual did not publish prepared data"); - const detail::Scene_State scene_state = - context.frame.scene_state(); + ::renderive::error::unexpected( + "Point_Visual did not publish prepared data"); + const State scene_state_value = context.frame.scene_state(); const std::uint64_t scene_revision = context.frame.scene_state_revision(); const std::uint64_t frame_sequence = @@ -417,20 +245,22 @@ struct Basic_Point_Scene final Node_Execution_Metrics* const metrics = context.metrics; const Node_Diagnostic_Sink diagnostics = context.diagnostics; const bool observe = metrics != nullptr; - const auto state = backend_state; + const auto backend = backend_state; auto source = std::make_shared(); const auto operation = source->operation(); - using Pending_Frame = detail::Datoviz_Visual_Backend::Pending_Frame; + using Pending_Frame = + detail::Datoviz_Visual_Backend::Pending_Frame; static_assert(std::is_nothrow_move_constructible_v); using Completion_Result = detail::Gpu_Completion_Service::Result; - static_assert(std::is_nothrow_move_assignable_v); + static_assert( + std::is_nothrow_move_assignable_v); struct Async_Frame_State { std::optional pending; Completion_Result completion; }; auto async_frame = std::make_shared(); - auto prepared_completion = state->render_domain->prepare( - [state, async_frame, metrics, diagnostics, source]() mutable { + auto prepared_completion = backend->render_domain->prepare( + [backend, async_frame, metrics, diagnostics, source]() mutable { if (!async_frame->pending) { static_cast(source->fail( ::renderive::error::capture( @@ -444,7 +274,7 @@ struct Basic_Point_Scene final if (async_frame->completion.error != detail::Gpu_Completion_Service::Completion_Error::none) { try { - state->quarantine_backend(); + backend->quarantine_backend(); static_cast(source->fail( External_Operation_Result::external_failure)); } catch (...) { @@ -456,13 +286,15 @@ struct Basic_Point_Scene final return; } try { - auto completed = state->require_backend().collect( + auto completed = backend->require_backend().collect( std::move(pending)); auto frame = std::move(completed.frame); - auto trace = std::make_shared( - std::move(completed.trace)); + auto trace = + std::make_shared( + std::move(completed.trace)); static_cast(source->complete( - [state, metrics, diagnostics, frame = std::move(frame), + [backend, metrics, diagnostics, + frame = std::move(frame), trace = std::move(trace)]() mutable { if (metrics && frame) { metrics->set( @@ -473,8 +305,8 @@ struct Basic_Point_Scene final } publish_trace(metrics, diagnostics, std::move(*trace)); - std::lock_guard lock(state->frame_mutex); - state->latest = std::move(frame); + std::lock_guard lock(backend->frame_mutex); + backend->latest = std::move(frame); })); } catch (...) { static_cast(source->fail( @@ -482,23 +314,24 @@ struct Basic_Point_Scene final "collecting Point_Scene frame", std::current_exception()))); } - }, [source](std::exception_ptr exception) { + }, + [source](std::exception_ptr exception) { static_cast(source->fail(std::move(exception))); }); if (!prepared_completion) { static_cast(source->cancel()); return Node_Execution_Result::external(operation); } - auto render_completion = std::make_shared< - detail::Render_Domain::Prepared_Task>( + auto render_completion = + std::make_shared( std::move(prepared_completion.task)); auto prepared_gpu_completion = detail::Gpu_Completion_Service::instance().prepare( - [state, async_frame, render_completion, source]( + [backend, async_frame, render_completion, source]( Completion_Result result) { async_frame->completion = std::move(result); try { - if (state->render_domain->post( + if (backend->render_domain->post( std::move(*render_completion)) != detail::Render_Domain::Admission_Result::none) static_cast(source->cancel()); @@ -517,22 +350,23 @@ struct Basic_Point_Scene final static_cast(source->cancel()); return Node_Execution_Result::external(operation); } - auto completion = std::make_shared< - detail::Gpu_Completion_Service::Reservation>( + auto completion = + std::make_shared( std::move(prepared_gpu_completion.reservation)); const auto queued_at = observe ? std::chrono::steady_clock::now() : std::chrono::steady_clock::time_point{}; try { - const auto post_error = state->render_domain->post( - [state, scene_state, scene_revision, frame_sequence, prepared, - source, async_frame, completion, observe, queued_at] { + const auto post_error = backend->render_domain->post( + [backend, scene_state_value, scene_revision, + frame_sequence, prepared, source, async_frame, + completion, observe, queued_at] { try { if (source->operation().status() == External_Operation_Status::cancelled) return; - if (!state->backend_available()) { + if (!backend->backend_available()) { static_cast(source->fail( External_Operation_Result::external_failure)); return; @@ -543,18 +377,20 @@ struct Basic_Point_Scene final std::chrono::duration_cast< std::chrono::nanoseconds>( std::chrono::steady_clock::now() - - queued_at).count(); + queued_at) + .count(); queue_wait_ns = queue_wait > 0 ? static_cast(queue_wait) : 0; } - auto pending = state->require_backend().submit( - scene_state, scene_revision, *prepared, + auto pending = backend->require_backend().submit( + scene_state_value, scene_revision, *prepared, frame_sequence, observe); if (!pending) { - static_cast(source->complete([state] { - std::lock_guard lock(state->frame_mutex); - state->latest.reset(); + static_cast(source->complete([backend] { + std::lock_guard lock( + backend->frame_mutex); + backend->latest.reset(); })); return; } @@ -569,10 +405,12 @@ struct Basic_Point_Scene final "submitting Point_Scene frame", std::current_exception()))); } - }, [source](std::exception_ptr exception) { + }, + [source](std::exception_ptr exception) { static_cast(source->fail(std::move(exception))); }); - if (post_error != detail::Render_Domain::Admission_Result::none) + if (post_error != + detail::Render_Domain::Admission_Result::none) static_cast(source->cancel()); } catch (...) { static_cast(source->fail( @@ -583,132 +421,145 @@ struct Basic_Point_Scene final return Node_Execution_Result::external(operation); } + void before_scene_state_swap() override { + const auto pending = scene_state.read( + [](const State& value) { return value; }); + const auto published = scene_state.published_state_snapshot(); + if (pending.viewport != published.viewport) + invalidate_renderables(); + else if (pending != published) + scene().notify_model_dirty(); + } + void swap_scene_state() override { scene_state.publish(); } + std::shared_ptr + frame_control_strategy() override { + return frame_control->runtime(); + } + std::shared_ptr + frame_control_strategy() const override { + return frame_control->runtime(); + } + std::uint64_t acquire_scene_state() override { + return scene_state.acquire_render_state(); + } + void capture_scene_state( + Frame_Render_Snapshot& snapshot) const override { + snapshot.set_scene_state(render_state_snapshot()); + } + + State_Storage scene_state; + std::unique_ptr frame_control; std::shared_ptr visual; Renderable_Id point_id{}; std::shared_ptr backend_state; }; -std::unique_ptr make_scene_model( - const Scene_Options& options, std::shared_ptr visual) { - switch (options.frame_mode) { - case Frame_Mode::Manual: - return std::make_unique< - Basic_Point_Scene>( - options, std::move(visual)); - case Frame_Mode::Low_Latency: - return std::make_unique< - Basic_Point_Scene>( - options, std::move(visual)); - case Frame_Mode::Playback: - return std::make_unique< - Basic_Point_Scene>( - options, std::move(visual)); - } - ::renderive::error::unexpected("unknown Point_Scene frame mode"); +Render_Scene_3D::Render_Scene_3D(Scene_Options options) + : ::renderive::scene::scene_base( + *std::pmr::get_default_resource(), + std::make_unique(*std::pmr::get_default_resource(), + options)) { + auto& impl = d_func(); + impl.frame_control = std::make_unique( + options.frame_mode, options.maximum_frames_per_second, + impl.memory_resource()); } -} // namespace - -struct Render_Scene_3D::Impl { - explicit Impl(const Scene_Options& options, - std::shared_ptr visual) - : model(make_scene_model(options, std::move(visual))) {} - - std::unique_ptr model; -}; - -Render_Scene_3D::Render_Scene_3D(Scene_Options options, - std::shared_ptr visual) { - if (!valid(options.viewport)) - ::renderive::error::unexpected("Point_Scene viewport must be nonempty"); - if (!valid(options.clear_color)) - ::renderive::error::unexpected("Point_Scene clear color must be in [0, 1]"); - if (!std::isfinite(options.maximum_frames_per_second) || - options.maximum_frames_per_second <= 0.0) +std::unique_ptr +Render_Scene_3D::Builder::build() const { + if (!visual_) ::renderive::error::unexpected( - "Point_Scene frame frequency must be positive"); - impl_ = std::make_unique(options, std::move(visual)); + "Point_Scene requires a Point_Visual"); + auto result = std::unique_ptr( + new Render_Scene_3D(options_)); + auto& impl = result->d_func(); + impl.visual = visual_; + impl.point_id = impl.visual->renderable_id(); + const auto attachment = result->edit_renderables( + [&impl](Scene_3D_Base::Renderable_Editor& editor) { + static_cast(editor.attach( + renderive_Owner(impl.visual))); + }); + if (attachment.wait() != Scene_Edit_Error::none) + ::renderive::error::unexpected( + "validated point visual attachment failed"); + const State initial = impl.scene_state.read( + [](const State& value) { return value; }); + const auto backend = impl.backend_state; + const auto invocation = backend->render_domain->invoke( + [backend, options = options_, initial] { + backend->backend = + std::make_unique( + options.gpu_index, options.validation_enabled, + initial); + }); + if (!invocation) + ::renderive::error::unexpected( + "render domain stopped during Point_Scene initialization"); + return result; } -Render_Scene_3D::~Render_Scene_3D() = default; +Render_Scene_3D::~Render_Scene_3D() { d_func().shutdown(); } -Scene_Control_Error Render_Scene_3D::resize(Extent extent) { - if (!valid(extent)) - return Scene_Control_Error::empty_viewport; - impl_->model->resize(extent); +Render_Scene_3D::Observer Render_Scene_3D::observer() const { + const auto& frame_control = *d_func().frame_control; + const auto runtime = frame_control.runtime(); + return { + {}, + frame_control.mode(), + runtime->frequency_hz(), + runtime->next_refresh_interval_ns(), + }; +} + +Scene_Control_Error Render_Scene_3D::dispatch( + const ::renderive::Event& event) { + if (const auto* wheel = + dynamic_cast(&event)) { + const auto* pointer = + dynamic_cast(&event); + if (!pointer) + ::renderive::error::unexpected( + "wheel event has no pointer capability"); + const float x = static_cast(pointer->position_x()); + const float y = static_cast(pointer->position_y()); + const float pixel_delta_x = + static_cast(wheel->pixel_delta_x_value()); + const float pixel_delta_y = + static_cast(wheel->pixel_delta_y_value()); + const float angle_delta_x = + static_cast(wheel->angle_delta_x_value()); + const float angle_delta_y = + static_cast(wheel->angle_delta_y_value()); + if (!std::isfinite(x) || !std::isfinite(y) || + !std::isfinite(pixel_delta_x) || + !std::isfinite(pixel_delta_y) || + !std::isfinite(angle_delta_x) || + !std::isfinite(angle_delta_y)) + return Scene_Control_Error::invalid_event; + return d_func().dispatch_wheel( + *this, x, y, + datoviz_wheel_step(pixel_delta_x, angle_delta_x), + datoviz_wheel_step(pixel_delta_y, angle_delta_y), + pointer->keyboard_modifiers()); + } + if (const auto* pointer = + dynamic_cast(&event)) { + const float x = static_cast(pointer->position_x()); + const float y = static_cast(pointer->position_y()); + if (!std::isfinite(x) || !std::isfinite(y)) + return Scene_Control_Error::invalid_event; + if (event.type != ::renderive::Event_Type::Pointer_Move && + event.type != ::renderive::Event_Type::Pointer_Press && + event.type != ::renderive::Event_Type::Pointer_Release) + return Scene_Control_Error::invalid_event; + return d_func().dispatch_pointer( + *this, event.type, x, y, pointer->pointer_button(), + pointer->keyboard_modifiers()); + } + if (const auto* key = dynamic_cast(&event)) + return d_func().dispatch_key(*this, *key); return Scene_Control_Error::none; } -Scene_Control_Error Render_Scene_3D::set_clear_color(Clear_Color color) { - if (!valid(color)) - return Scene_Control_Error::invalid_clear_color; - impl_->model->set_clear_color(color); - return Scene_Control_Error::none; -} - -Scene_Control_Error Render_Scene_3D::dispatch(const ::renderive::Event&) { - return Scene_Control_Error::none; -} - -Scene_Control_Error Render_Scene_3D::dispatch_pointer( - ::renderive::Event_Type type, float x, float y, - ::renderive::Mouse_Button button, ::renderive::Mouse_Button_Mask, - ::renderive::Keyboard_Modifier modifiers) { - if (!std::isfinite(x) || !std::isfinite(y)) - return Scene_Control_Error::invalid_event; - if (type != ::renderive::Event_Type::Pointer_Move && - type != ::renderive::Event_Type::Pointer_Press && - type != ::renderive::Event_Type::Pointer_Release) - return Scene_Control_Error::invalid_event; - return impl_->model->dispatch_pointer(type, x, y, button, modifiers); -} - -Scene_Control_Error Render_Scene_3D::dispatch_wheel( - float x, float y, float pixel_delta_x, float pixel_delta_y, - float angle_delta_x, float angle_delta_y, - ::renderive::Keyboard_Modifier modifiers) { - if (!std::isfinite(x) || !std::isfinite(y) || - !std::isfinite(pixel_delta_x) || !std::isfinite(pixel_delta_y) || - !std::isfinite(angle_delta_x) || !std::isfinite(angle_delta_y)) - return Scene_Control_Error::invalid_event; - return impl_->model->dispatch_wheel( - x, y, datoviz_wheel_step(pixel_delta_x, angle_delta_x), - datoviz_wheel_step(pixel_delta_y, angle_delta_y), modifiers); -} - -Scene_Control_Error Render_Scene_3D::dispatch(const ::renderive::Key_Event& event) { - return impl_->model->dispatch_key(event); -} - -Frame_Request_Result Render_Scene_3D::prepare_frame() { return impl_->model->prepare_frame(); } -Frame_Request_Result Render_Scene_3D::refresh_manual_frame() { - return impl_->model->refresh_manual_frame(); -} -bool Render_Scene_3D::discard_pending_frame() { - return impl_->model->discard_pending_frame(); -} -Frame_Request_Result Render_Scene_3D::render_prepared_frame() { - return impl_->model->render_prepared_frame(); -} -Frame_Request_Result Render_Scene_3D::request_frame() { return impl_->model->request_frame(); } - -std::shared_ptr Render_Scene_3D::latest_frame() const { - return impl_->model->latest_frame(); -} - -Frame_Status Render_Scene_3D::frame_status() const { - return impl_->model->frame_status(); -} - -Runtime_Statistics Render_Scene_3D::runtime_statistics() const noexcept { - return impl_->model->runtime_statistics(); -} - -Scene_Base& Render_Scene_3D::render_scene() noexcept { - return impl_->model->scene(); -} - -const Scene_Base& Render_Scene_3D::render_scene() const noexcept { - return impl_->model->scene(); -} } // namespace renderive::render_3d diff --git a/render_3D/render_3D/Render_Scene_3D.h b/render_3D/render_3D/Render_Scene_3D.h index ed07e27..d5abf2b 100644 --- a/render_3D/render_3D/Render_Scene_3D.h +++ b/render_3D/render_3D/Render_Scene_3D.h @@ -1,12 +1,15 @@ #pragma once #include "Point_Visual.h" #include +#include #include #include #include +#include #include class Scene_Base; namespace renderive::render_3d { +struct Scene3D_Frame_Data : Abstract_Frame {}; struct Extent { std::uint32_t width{}; std::uint32_t height{}; @@ -24,8 +27,6 @@ struct Clear_Color { }; enum class Scene_Control_Error : std::uint8_t { none, - empty_viewport, - invalid_clear_color, invalid_event, backend_unavailable }; @@ -76,83 +77,49 @@ struct Pixel_Frame { std::uint64_t sequence{}; std::vector rgba8; }; -struct Runtime_Admission_Statistics { - std::size_t capacity{}; - std::size_t active{}; - std::size_t peak_active{}; - std::size_t queued{}; - std::size_t peak_queued{}; - std::uint64_t backpressure_count{}; - std::uint64_t backpressure_wait_ns{}; -}; -struct Runtime_Statistics { - Runtime_Admission_Statistics render_domain; - Runtime_Admission_Statistics gpu_completion; - std::uint64_t gpu_completion_fault_count{}; - std::uint64_t gpu_completion_abandoned_count{}; -}; -struct Frame_Status { - Frame_Mode mode{Frame_Mode::Low_Latency}; - double frequency_hz{}; - 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 next_refresh_interval_ns{}; -}; // Owns frame scheduling and the single Datoviz mutation/render domain. -class Render_Scene_3D final { +class Render_Scene_3D final +: public ::renderive::scene::scene_base { public: - Render_Scene_3D(Scene_Options options, std::shared_ptr visual); - ~Render_Scene_3D(); - Render_Scene_3D(const Render_Scene_3D&) = delete; - Render_Scene_3D& operator=(const Render_Scene_3D&) = delete; - Render_Scene_3D(Render_Scene_3D&&) = delete; - Render_Scene_3D& operator=(Render_Scene_3D&&) = delete; - [[nodiscard]] Scene_Control_Error resize(Extent extent); - [[nodiscard]] Scene_Control_Error set_clear_color(Clear_Color color); + using Frame = Scene3D_Frame_Data; + struct State : next_State { + Extent viewport{560, 320}; + Clear_Color clear_color; + Visual_Family visual_family{Visual_Family::Point}; + [[nodiscard]] bool operator==(const State& other) const { + return viewport == other.viewport && + clear_color == other.clear_color && + visual_family == other.visual_family; + } + }; + struct Observer + : ::renderive::scene::scene_base::Observer { + Frame_Mode mode{Frame_Mode::Low_Latency}; + double frequency_hz{}; + std::uint64_t next_refresh_interval_ns{}; + }; + class Builder : public next_Builder { + public: + explicit Builder(std::shared_ptr visual) + : visual_(std::move(visual)) {} + Builder& options(Scene_Options value) noexcept { + options_ = value; + return *this; + } + [[nodiscard]] std::unique_ptr build() const; + private: + std::shared_ptr visual_; + Scene_Options options_; + }; + [[nodiscard]] Observer observer() const; [[nodiscard]] Scene_Control_Error dispatch(const ::renderive::Event& event); - template <::renderive::Event_Point Point_Type> - [[nodiscard]] Scene_Control_Error dispatch( - const ::renderive::Basic_Pointer_Event& event) { - return dispatch_pointer(event.type, static_cast(event.position.x), - static_cast(event.position.y), event.button, - event.buttons, event.modifiers); - } - template <::renderive::Event_Point Point_Type> - [[nodiscard]] Scene_Control_Error dispatch( - const ::renderive::Basic_Wheel_Event& event) { - return dispatch_wheel(static_cast(event.position.x), - static_cast(event.position.y), - static_cast(event.pixel_delta_x), - static_cast(event.pixel_delta_y), - static_cast(event.angle_delta_x), - static_cast(event.angle_delta_y), event.modifiers); - } - [[nodiscard]] Scene_Control_Error dispatch(const ::renderive::Key_Event& event); - // Frame strategy operations. request_frame() is the complete low-latency path. - [[nodiscard]] Frame_Request_Result prepare_frame(); - [[nodiscard]] Frame_Request_Result refresh_manual_frame(); - [[nodiscard]] bool discard_pending_frame(); - [[nodiscard]] Frame_Request_Result render_prepared_frame(); - [[nodiscard]] Frame_Request_Result request_frame(); - [[nodiscard]] std::shared_ptr latest_frame() const; - [[nodiscard]] Frame_Status frame_status() const; - [[nodiscard]] Runtime_Statistics runtime_statistics() const noexcept; - [[nodiscard]] Scene_Base& render_scene() noexcept; - [[nodiscard]] const Scene_Base& render_scene() const noexcept; + ~Render_Scene_3D() override; private: - [[nodiscard]] Scene_Control_Error dispatch_pointer(::renderive::Event_Type type, float x, float y, - ::renderive::Mouse_Button button, - ::renderive::Mouse_Button_Mask buttons, - ::renderive::Keyboard_Modifier modifiers); - [[nodiscard]] Scene_Control_Error dispatch_wheel(float x, float y, float pixel_delta_x, - float pixel_delta_y, float angle_delta_x, - float angle_delta_y, - ::renderive::Keyboard_Modifier modifiers); + explicit Render_Scene_3D(Scene_Options options); struct Impl; - std::unique_ptr impl_; + friend struct ::renderive::scene::scene_base; }; } // namespace renderive::render_3d diff --git a/render_3D/render_3D/Scene_Context.hpp b/render_3D/render_3D/Scene_Context.hpp deleted file mode 100644 index 8ccacab..0000000 --- a/render_3D/render_3D/Scene_Context.hpp +++ /dev/null @@ -1,105 +0,0 @@ -#pragma once -#include -#include -#include -#include -#include "renderive/base/observer/Observer.hpp" -#include "renderive/frame_control/Frame_Control.hpp" -#include "renderive/state/Render_Acquired_State_Storage.hpp" -#include "renderive/scene/base/Scene_Base.hpp" -struct Scene3D_Frame_Data : Abstract_Frame {}; -struct Scene3D_State { - std::uint64_t revision{}; -}; -template -concept Scene3D_Frame_Control_Constructible = std::constructible_from || std::constructible_from; -template , class State = Scene3D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>> -requires Frame_Control_Strategy_For && State_Value -class Scene3D_Context : public Scene_3D_Base, - public Render_Acquired_State_Storage< - State, Atomic_Spin_Mutex, State_Observer> { -public: - using Scene_State_Strategy = - Render_Acquired_State_Storage; - using Frame_Control = Strategy; - using Frame = typename Frame_Control::Frame; - using Painter_Lease = typename Frame_Control::Painter_Lease; - using Render_Lease = typename Frame_Control::Render_Lease; - template - requires Scene3D_Frame_Control_Constructible - explicit Scene3D_Context(Args&&... args) : Scene3D_Context(*std::pmr::get_default_resource(), std::forward(args)...) {} - template - requires Scene3D_Frame_Control_Constructible - explicit Scene3D_Context(std::pmr::memory_resource& memory_resource, Args&&... args) - : Scene_3D_Base(memory_resource, std::make_unique()), - Scene_State_Strategy(State{}), - frame_control(make_frame_control(this->memory_resource(), - std::forward(args)...)) {} - template - requires Scene3D_Frame_Control_Constructible - Scene3D_Context(With_Observer state_observer, With_Observer scene_observer, Args&&... args) - : Scene3D_Context(*std::pmr::get_default_resource(), std::move(state_observer), std::move(scene_observer), std::forward(args)...) {} - template - requires Scene3D_Frame_Control_Constructible - Scene3D_Context(std::pmr::memory_resource& memory_resource, With_Observer state_observer, With_Observer scene_observer, Args&&... args) - : Scene_3D_Base( - memory_resource, - std::make_unique(std::move(scene_observer.observer))), - Scene_State_Strategy(State{}, std::move(state_observer)), - frame_control(make_frame_control(this->memory_resource(), - std::forward(args)...)) {} - ~Scene3D_Context() override { - this->shutdown(); - } - Frame_Control frame_control; -protected: - Frame_Control_Strategy_Base& frame_control_strategy_impl() noexcept override { - return frame_control; - } - const Frame_Control_Strategy_Base& frame_control_strategy_impl() const noexcept override { - return frame_control; - } - void publish_scene_state() override { - this->Scene_State_Strategy::publish(); - } - std::uint64_t acquire_scene_state() override { - return this->Scene_State_Strategy::acquire_render_state(); - } - void capture_scene_state(Frame_Render_Snapshot& snapshot) const override { - snapshot.set_scene_state( - this->Scene_State_Strategy::render_state_value()); - } -private: - struct Context_State_Layer - : renderive::scene_inheritance::State_Node< - Context_State_Layer, Scene_3D_Base::State_Layer> {}; - - struct Impl - : renderive::scene_inheritance::Impl_Node { - struct Observer - : renderive::scene_inheritance::Observer_Node< - Impl, Scene_3D_Base::Impl::Observer> { - static void handle(Impl& layer, - const Scene_Base::Observation& observation) { - layer.scene_observer.observe(observation); - } - }; - - Impl() = default; - explicit Impl(Scene_Observer observer) - : scene_observer(std::move(observer)) {} - std::uint64_t now_ns() const noexcept override { - return scene_observer.now_ns(); - } - Scene_Observer scene_observer; - }; - - template - static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) { - if constexpr (std::constructible_from) { - return Frame_Control(memory_resource, std::forward(args)...); - } else { - return Frame_Control(std::forward(args)...); - } - } -}; diff --git a/render_3D/render_3D/detail/Point_Core.h b/render_3D/render_3D/detail/Point_Core.h index 7f909ae..f6fe803 100644 --- a/render_3D/render_3D/detail/Point_Core.h +++ b/render_3D/render_3D/detail/Point_Core.h @@ -9,12 +9,7 @@ namespace renderive::render_3d::detail { -struct Scene_State { - Extent viewport{560, 320}; - Clear_Color clear_color; - Visual_Family visual_family{Visual_Family::Point}; - bool operator==(const Scene_State&) const = default; -}; +using Scene_State = Render_Scene_3D::State; // Immutable input captured at the frame publication boundary. Playback frames // retain this shared payload instead of consulting a later buffer role. diff --git a/render_3D/render_3D/frame_control/Point_Frame_Control.cpp b/render_3D/render_3D/frame_control/Point_Frame_Control.cpp new file mode 100644 index 0000000..51e4ebb --- /dev/null +++ b/render_3D/render_3D/frame_control/Point_Frame_Control.cpp @@ -0,0 +1,190 @@ +#include "Point_Frame_Control.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +namespace renderive::render_3d::detail { +namespace { + +using Manual_Strategy = Manual_Refresh_Strategy; +using Low_Latency_Strategy_Type = Low_Latency_Strategy; +using Playback_Strategy = Flow_Refresh_Strategy; + +template +decltype(auto) visit(Frame_Control_Strategy_Base& strategy, + Function&& function) { + if (auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, + std::integral_constant{}); + if (auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, + std::integral_constant{}); + if (auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, + std::integral_constant{}); + ::renderive::error::unexpected( + "unknown Point_Scene frame strategy"); +} + +template +decltype(auto) visit(const Frame_Control_Strategy_Base& strategy, + Function&& function) { + if (const auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, + std::integral_constant{}); + if (const auto* value = + dynamic_cast(&strategy)) + return std::forward(function)( + *value, + std::integral_constant{}); + if (const auto* value = dynamic_cast(&strategy)) + return std::forward(function)( + *value, + std::integral_constant{}); + ::renderive::error::unexpected( + "unknown Point_Scene frame strategy"); +} + +std::shared_ptr +make_strategy(Frame_Mode mode, double maximum_frames_per_second, + std::pmr::memory_resource& memory_resource) { + switch (mode) { + case Frame_Mode::Manual: + return std::make_shared(); + case Frame_Mode::Low_Latency: + return std::make_shared( + Observer_State<>{}, + Low_Latency_Strategy_Type::Configuration{ + .frequency_hz = maximum_frames_per_second, + .replace_pending_frame = true}); + case Frame_Mode::Playback: + return std::make_shared( + memory_resource); + } + ::renderive::error::unexpected( + "unknown Point_Scene frame mode"); +} + +Frame_Mode strategy_mode(const Frame_Control_Strategy_Base& strategy) { + return visit(strategy, []( + const Strategy&, + std::integral_constant) { + return Mode; + }); +} + +} // namespace + +struct Point_Frame_Control::Impl { + std::atomic> strategy; +}; + +Point_Frame_Control::Point_Frame_Control( + Frame_Mode mode, double maximum_frames_per_second, + std::pmr::memory_resource& memory_resource) + : impl_(std::make_unique()) { + replace(mode, maximum_frames_per_second, memory_resource); +} + +Point_Frame_Control::~Point_Frame_Control() = default; + +std::shared_ptr +Point_Frame_Control::runtime() { + return impl_->strategy.load(std::memory_order_acquire); +} + +std::shared_ptr +Point_Frame_Control::runtime() const { + return impl_->strategy.load(std::memory_order_acquire); +} + +Frame_Request_Result Point_Frame_Control::prepare(Render_Scene_3D& scene) { + const auto current = runtime(); + return visit(*current, [&scene](auto& strategy, auto) { + auto frame = strategy.acquire_painter(); + if (!frame) + return Frame_Request_Result::painter_unavailable; + scene.publish_frame_state(*frame); + return Frame_Request_Result::none; + }); +} + +Frame_Request_Result Point_Frame_Control::refresh(Render_Scene_3D&) { + const auto current = runtime(); + auto* strategy = dynamic_cast(current.get()); + if (!strategy) + return Frame_Request_Result::manual_refresh_not_supported; + return strategy->refresh() == Manual_Refresh_Result::none + ? Frame_Request_Result::none + : Frame_Request_Result::no_pending_frame; +} + +bool Point_Frame_Control::discard(Render_Scene_3D&) { + const auto current = runtime(); + if (auto* strategy = dynamic_cast(current.get())) + return strategy->discard_pending_frame(); + if (auto* strategy = + dynamic_cast(current.get())) + return strategy->discard_pending_frame(); + return false; +} + +Frame_Request_Result Point_Frame_Control::render(Render_Scene_3D& scene) { + const auto current = runtime(); + return visit(*current, [&scene](auto& strategy, auto) { + auto frame = strategy.acquire_renderer(); + if (!frame) + return Frame_Request_Result::renderer_unavailable; + const auto result = scene.Scene_Base::render(*frame); + if (result == Scene_Render_Result::shutting_down) + return Frame_Request_Result::scene_shutting_down; + const auto converted = + adapter::enum_cast(result); + if (!converted) + ::renderive::error::unexpected( + "unknown scene render result"); + return *converted; + }); +} + +Frame_Request_Result Point_Frame_Control::request(Render_Scene_3D& scene) { + if (const auto result = prepare(scene); + result != Frame_Request_Result::none) + return result; + if (mode() == Frame_Mode::Manual) { + if (const auto result = refresh(scene); + result != Frame_Request_Result::none) + return result; + } + return render(scene); +} + +Frame_Mode Point_Frame_Control::mode() const { + return strategy_mode(*runtime()); +} + +void Point_Frame_Control::replace( + Frame_Mode mode, double maximum_frames_per_second, + std::pmr::memory_resource& memory_resource) { + if (!std::isfinite(maximum_frames_per_second) || + maximum_frames_per_second <= 0.0) + ::renderive::error::unexpected( + "Point_Scene frame frequency must be positive"); + impl_->strategy.store( + make_strategy(mode, maximum_frames_per_second, memory_resource), + std::memory_order_release); +} + +} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/frame_control/Point_Frame_Control.h b/render_3D/render_3D/frame_control/Point_Frame_Control.h new file mode 100644 index 0000000..f331401 --- /dev/null +++ b/render_3D/render_3D/frame_control/Point_Frame_Control.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../Render_Scene_3D.h" + +#include +#include + +namespace renderive::render_3d::detail { + +class Point_Frame_Control final { +public: + Point_Frame_Control(Frame_Mode mode, double maximum_frames_per_second, + std::pmr::memory_resource& memory_resource); + ~Point_Frame_Control(); + + Point_Frame_Control(const Point_Frame_Control&) = delete; + Point_Frame_Control& operator=(const Point_Frame_Control&) = delete; + Point_Frame_Control(Point_Frame_Control&&) = delete; + Point_Frame_Control& operator=(Point_Frame_Control&&) = delete; + + [[nodiscard]] std::shared_ptr runtime(); + [[nodiscard]] std::shared_ptr + runtime() const; + [[nodiscard]] Frame_Request_Result prepare(Render_Scene_3D& scene); + [[nodiscard]] Frame_Request_Result refresh(Render_Scene_3D& scene); + [[nodiscard]] bool discard(Render_Scene_3D& scene); + [[nodiscard]] Frame_Request_Result render(Render_Scene_3D& scene); + [[nodiscard]] Frame_Request_Result request(Render_Scene_3D& scene); + [[nodiscard]] Frame_Mode mode() const; + void replace(Frame_Mode mode, double maximum_frames_per_second, + std::pmr::memory_resource& memory_resource); +private: + struct Impl; + std::unique_ptr impl_; +}; + +} // namespace renderive::render_3d::detail diff --git a/render_3D/render_3D/renderable/Renderable.cpp b/render_3D/render_3D/renderable/Renderable.cpp index 52ff2eb..8e9a01a 100644 --- a/render_3D/render_3D/renderable/Renderable.cpp +++ b/render_3D/render_3D/renderable/Renderable.cpp @@ -3,24 +3,4 @@ namespace renderive::render_3d { Renderable::Renderable() : render_base(std::make_unique()) {} Renderable::~Renderable() = default; -Renderable_Observation Renderable::observation() const { - const auto& impl = d_func(); - std::lock_guard lock(impl.observation_mutex); - return impl.observation; -} -void Renderable::Impl::observe( - const detail::Renderable_Event_View& value) { - std::lock_guard lock(observation_mutex); - observation.event = value.event; - observation.event_time_ns = value.time_ns; - observation.cache_update_count = value.cache_update_count; - observation.publish_count = value.publish_count; -} -void Renderable::Impl::observe_data(std::uint64_t revision, - std::size_t item_count) { - std::lock_guard lock(observation_mutex); - observation.event = Renderable_Observer_Event::Data_Updated; - observation.data_revision = revision; - observation.item_count = item_count; -} } // namespace renderive::render_3d diff --git a/render_3D/render_3D/renderable/Renderable.h b/render_3D/render_3D/renderable/Renderable.h index 1ab2bbd..ea0df24 100644 --- a/render_3D/render_3D/renderable/Renderable.h +++ b/render_3D/render_3D/renderable/Renderable.h @@ -7,7 +7,6 @@ #include #include #include -#include #include namespace renderive::render_3d { @@ -18,16 +17,6 @@ enum struct Renderable_Observer_Event { None, Cache_Updated, Published, - Data_Updated, -}; - -struct Renderable_Observation { - Renderable_Observer_Event event{Renderable_Observer_Event::None}; - std::uint64_t event_time_ns{}; - std::uint64_t cache_update_count{}; - std::uint64_t publish_count{}; - std::uint64_t data_revision{}; - std::size_t item_count{}; }; namespace detail { @@ -35,32 +24,22 @@ struct Renderable_State_Observer; struct Renderable_Event_View { Renderable_Observer_Event event{Renderable_Observer_Event::None}; - std::uint64_t time_ns{}; - std::uint64_t cache_update_count{}; - std::uint64_t publish_count{}; - const std::type_info* payload_type{&typeid(void)}; - const void* payload{}; - - template - [[nodiscard]] const Payload* payload_if() const noexcept { - return *payload_type == typeid(Payload) - ? static_cast(payload) - : nullptr; - } }; } // namespace detail struct Renderable : ::renderive::renderable::render_base { +protected: + struct Impl; +public: + struct Observer + : ::renderive::renderable_inheritance::Observer_Root {}; + Renderable(); ~Renderable() override; - [[nodiscard]] Renderable_Observation observation() const; - protected: - struct Impl; - template explicit Renderable(With_Attached_Impl) : render_base(std::make_unique()) {} diff --git a/render_3D/render_3D/renderable/Renderable_Builder.h b/render_3D/render_3D/renderable/Renderable_Builder.h index 79fff87..45c334b 100644 --- a/render_3D/render_3D/renderable/Renderable_Builder.h +++ b/render_3D/render_3D/renderable/Renderable_Builder.h @@ -46,12 +46,11 @@ struct Renderable_Builder { } template - requires std::constructible_from std::shared_ptr build(Args&&... args) const { validator_(properties_); - auto result = std::make_shared(properties_, - std::forward(args)...); + auto result = + ::renderive::renderable_inheritance::Builder_Access::make( + properties_, std::forward(args)...); return ::renderive::inheritance::initialize( std::move(result), [](auto&) {}); } diff --git a/render_3D/render_3D/renderable/Renderable_p.h b/render_3D/render_3D/renderable/Renderable_p.h index 88b8b03..b325fb8 100644 --- a/render_3D/render_3D/renderable/Renderable_p.h +++ b/render_3D/render_3D/renderable/Renderable_p.h @@ -15,23 +15,6 @@ Renderable_Event_View renderable_event_view( observation.event == decltype(observation.event)::cache_updated ? Renderable_Observer_Event::Cache_Updated : Renderable_Observer_Event::Published, - observation.time_ns, - observation.cache_update_count, - observation.publish_count, - &typeid(observation.state), - &observation.state, - }; -} - -template -Renderable_Event_View renderable_payload_view(const Payload& payload) noexcept { - return { - Renderable_Observer_Event::Data_Updated, - 0, - 0, - 0, - &typeid(payload), - &payload, }; } @@ -43,30 +26,11 @@ struct Renderable::Impl : ::renderive::renderable_inheritance::Impl_Root< Renderable::Impl, ::Renderable_Base::Impl, detail::Renderable_Event_View> { - struct Observer - : ::renderive::renderable_inheritance::Observer_Root { - static void handle( - Impl& impl, const detail::Renderable_Event_View& value) { - impl.observe(value); - } - }; + using Observer = Renderable::Observer; virtual ~Impl() = default; -protected: - template - void report_observation(const Payload& payload) { - dispatch(detail::renderable_payload_view(payload)); - } - void observe_data(std::uint64_t revision, - std::size_t item_count); - private: - void observe(const detail::Renderable_Event_View& value); - - mutable std::mutex observation_mutex; - Renderable_Observation observation; - friend struct Renderable; }; diff --git a/render_3D/render_3D/visuals/Basic_Visual.h b/render_3D/render_3D/visuals/Basic_Visual.h index 4d9927f..5288f83 100644 --- a/render_3D/render_3D/visuals/Basic_Visual.h +++ b/render_3D/render_3D/visuals/Basic_Visual.h @@ -22,12 +22,6 @@ namespace renderive::render_3d::detail { template struct Visual_Data_Tag {}; -template -struct Visual_Data_Observation { - std::uint64_t revision{}; - std::size_t item_count{}; -}; - template struct Basic_Visual : Renderable> { using Base = Renderable>; @@ -79,17 +73,10 @@ struct Basic_Visual : Renderable> { std::uint64_t data_revision{}; }; + struct Observer; struct Impl : Base::template next_Impl, Buffer { - struct Observer : Base::template next_Impl::next_Observer { - static void handle(Impl& impl, - const Renderable_Event_View& observation) noexcept { - if (observation.event == Renderable_Observer_Event::Published) - impl.Buffer::publish(); - if (const auto* data = observation.template payload_if< - Visual_Data_Observation>()) - impl.observe_data(data->revision, data->item_count); - } - }; + using Observer = Basic_Visual::Observer; + friend struct Basic_Visual::Observer; explicit Impl(std::vector items) { if (update(std::move(items)) != Visual_Data_Error::none) @@ -104,11 +91,8 @@ struct Basic_Visual : Renderable> { if (!Spec::valid(item)) return Visual_Data_Error::invalid_data; } - const auto count = items.size(); this->template write>( std::make_shared>(std::move(items))); - this->report_observation(Visual_Data_Observation{ - this->template cache_revision>(), count}); return Visual_Data_Error::none; } @@ -143,13 +127,23 @@ struct Basic_Visual : Renderable> { context.template publish(input); } }; + struct Observer : Impl::next_Observer { + static void handle(Impl& impl, + const Renderable_Event_View& observation) noexcept { + if (observation.event == Renderable_Observer_Event::Published) + impl.Buffer::publish(); + } + }; +private: + friend struct ::renderive::renderable::attach; explicit Basic_Visual(const State& state, std::vector items = {}) : Base(With_Attached_Impl{}, state, std::move(items)) { State_Validator{}(state); this->template d_func().real_time_data_binding = this->template d_func().Buffer::bind(*this); } +public: ~Basic_Visual() override = default; [[nodiscard]] Visual_Data_Error update_items(std::vector items) { diff --git a/web_server/app/render_2D/Gallery_Scene2D.cpp b/web_server/app/render_2D/Gallery_Scene2D.cpp index 7f54efd..74b4d04 100644 --- a/web_server/app/render_2D/Gallery_Scene2D.cpp +++ b/web_server/app/render_2D/Gallery_Scene2D.cpp @@ -29,8 +29,9 @@ #include namespace renderive::web { namespace { -Gallery_Render_Result gallery_render_result( - const tl::expected& result) { +auto gallery_render_result( + const Expected& result) + -> Gallery_Render_Result { if (result) { switch (*result) { case Plot_Render_Status::rendered: