#include #include "Renderable_Base_p.hpp" #include "renderive/renderable/Renderable_Build.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)) {} } } // 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::bind_observation_source( std::unique_ptr source) { d_func().bind_observation_source(std::move(source)); } void Renderable_Base::unbind_observation_source() noexcept { d_func().unbind_observation_source(); } bool Renderable_Base::observation_snapshot( std::type_index type, void* destination) const { std::shared_ptr source; { std::lock_guard lock(d_func().observation_mutex); source = d_func().observation_source; } return source && source->snapshot(type, destination); } void Renderable_Base::Impl::bind_observation_source( std::unique_ptr source) { std::lock_guard lock(observation_mutex); observation_source = std::move(source); } void Renderable_Base::Impl::unbind_observation_source() noexcept { std::shared_ptr source; { std::lock_guard lock(observation_mutex); source = std::exchange(observation_source, {}); } if (source) source->stop(); } void Renderable_Base::Impl::publish_observation() { std::shared_ptr source; { std::lock_guard lock(observation_mutex); source = observation_source; } if (source) source->publish(); } 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) ::renderive::error::unexpected("renderable belongs to another scene"); } void Renderable_Base::Impl::rebuild_render_graph() { if (!attached()) { reset_render_graph(); return; } 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() }; 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)) ::renderive::error::unexpected( "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.clear(); node_identities.insert(next_identities.begin(), next_identities.end()); } 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) ::renderive::error::unexpected("renderable is not bound to a scene"); auto lease = lifetime->acquire(); if (!lease) ::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); if (previous == value) return; configuration.store(value, std::memory_order_release); invalidate_prepare(); } void Renderable_Base::Impl::set_visible(bool value) { if (visible.exchange(value, std::memory_order_acq_rel) == value) return; invalidate_prepare(); } 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::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::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 { 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) { 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->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 { return d_func().object_name; } void renderive::detail::Renderable_Build_Access::set_object_name( Renderable_Base& renderable, std::string name) { auto& implementation = renderable.d_func(); if (implementation.real_time_data_state->scene_lifetime) ::renderive::error::unexpected( "object_name is immutable after scene construction"); implementation.object_name = std::move(name); } void Renderable_Base::add_build_dependency_parent( const renderive_Owner& parent) { auto& implementation = d_func(); if (implementation.real_time_data_state->scene_lifetime) ::renderive::error::unexpected( "renderable build topology is immutable after scene construction"); implementation.build_topology.dependency_parents.emplace_back( parent.share()); } void Renderable_Base::add_build_display_parent( const renderive_Owner& parent) { auto& implementation = d_func(); if (implementation.real_time_data_state->scene_lifetime) ::renderive::error::unexpected( "renderable build topology is immutable after scene construction"); implementation.build_topology.display_parents.emplace_back(parent.share()); } void Renderable_Base::add_build_display_child( const renderive_Owner& child) { auto& implementation = d_func(); if (implementation.real_time_data_state->scene_lifetime) ::renderive::error::unexpected( "renderable build topology is immutable after scene construction"); implementation.build_topology.display_children.emplace_back(child.share()); } void renderive::detail::Renderable_Build_Access::add_dependency_parent( Renderable_Base& renderable, const renderive_Owner& parent) { renderable.add_build_dependency_parent(parent); } void renderive::detail::Renderable_Build_Access::add_display_parent( Renderable_Base& renderable, const renderive_Owner& parent) { renderable.add_build_display_parent(parent); } void renderive::detail::Renderable_Build_Access::add_display_child( Renderable_Base& renderable, const renderive_Owner& child) { renderable.add_build_display_child(child); } const renderive::Renderable_Build_Topology& renderive::detail::Renderable_Build_Access::topology( const Renderable_Base& renderable) noexcept { return renderable.d_func().build_topology; } void renderive::detail::Renderable_Build_Access::complete( Renderable_Base& renderable) noexcept { renderable.d_func().build_topology = {}; } 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(); } void Renderable_Base::Impl::paint_changed() { invalidate_paint(); } void Renderable_Base::Impl::render_graph_changed() { rebuild_render_graph(); }