#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" 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)) {} } } // 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; if (!current) current = std::move(lifetime); else if (current != lifetime) throw std::invalid_argument("renderable belongs to another scene"); } void Renderable_Base::Impl::rebuild_render_graph() { if (!attached()) { reset_render_graph(); return; } scene().request_render_graph_rebuild(owner()); } void Renderable_Base::Impl::reset_render_graph() noexcept { render_graph.reset(); invalidate_prepare(); } std::shared_ptr Renderable_Base::Impl::render_graph_snapshot() { if (!render_graph) { std::unordered_map next_identities; Renderable_Graph_Builder builder( renderable_id, [this, &next_identities](std::string_view key) { const std::string logical_key(key); if (next_identities.contains(logical_key)) throw std::invalid_argument( "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; next_identities.emplace(logical_key, node_id); return node_id; }); build_prepare_graph(builder); build_paint_graph(builder); render_graph = std::make_shared( std::move(builder).finish()); node_identities = std::move(next_identities); } 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) throw std::logic_error("renderable is not bound to a scene"); auto lease = lifetime->acquire(); if (!lease) throw std::logic_error("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); 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() noexcept { const auto& state = real_time_data_state; if (!state->attached.load(std::memory_order_acquire) || !state->scene_lifetime) return; auto lease = state->scene_lifetime->acquire(); if (lease) lease.scene().notify_model_dirty(); } void Renderable_Base::Impl::build_prepare_graph( Renderable_Graph_Builder& builder) { builder.emplace("prepare", "Prepare", [this](const Prepare_Render_Context& context) { 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::add_prepare_action(Prepare_Action action) { if (!action) throw std::invalid_argument("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 { painted_prepare_revision.store(current_prepare_revision, 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); if (std::find(real_time_data.begin(), real_time_data.end(), &data) == real_time_data.end()) real_time_data.push_back(&data); } void Renderable_Base::Impl::unregister_real_time_data( Real_Time_Data_Base& data) noexcept { 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) throw std::invalid_argument("renderable implementation is null"); d_ptr->initialize(*this, configuration, memory_resource); } 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); } 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); }