Files
Renderive/Kernel/src/renderive/renderable/base/Renderable_Base.cpp
T
2026-08-18 11:09:48 +08:00

302 lines
13 KiB
C++

#include <renderive/error/Error_Policy.hpp>
#include "Renderable_Base_p.hpp"
#include "renderive/renderable/Renderable_Build.hpp"
#include <algorithm>
#include <limits>
#include <stdexcept>
#include <utility>
#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<std::uint64_t>& revision) noexcept {
std::uint64_t value = revision.load(std::memory_order_relaxed);
while (value != std::numeric_limits<std::uint64_t>::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<renderive::inheritance::Observation_Source> 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<renderive::inheritance::Observation_Source> 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<renderive::inheritance::Observation_Source> source) {
std::lock_guard lock(observation_mutex);
observation_source = std::move(source);
}
void Renderable_Base::Impl::unbind_observation_source() noexcept {
std::shared_ptr<renderive::inheritance::Observation_Source> 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<renderive::inheritance::Observation_Source> 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<Scene_Lifetime> lifetime) {
auto& current = real_time_data_state->scene_lifetime;
if (!current) current = std::move(lifetime);
else if (current != lifetime) ::renderive::error::unexpected<std::invalid_argument>("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<const Renderable_Graph> Renderable_Base::Impl::render_graph_snapshot() {
std::lock_guard lock(render_graph_mutex);
if (!render_graph) {
std::pmr::unordered_map<std::string, Render_Node_Id> 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<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<Renderable_Graph>(
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<std::logic_error>("renderable is not bound to a scene");
auto lease = lifetime->acquire();
if (!lease) ::renderive::error::unexpected<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();
}
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<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) {
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<Impl>(), configuration,
memory_resource) {}
Renderable_Base::Renderable_Base(std::unique_ptr<Impl> implementation,
Renderable_Configuration configuration,
std::pmr::memory_resource& memory_resource) : d_ptr(std::move(implementation)) {
if (!d_ptr) ::renderive::error::unexpected<std::invalid_argument>("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<std::logic_error>(
"object_name is immutable after scene construction");
implementation.object_name = std::move(name);
}
void Renderable_Base::add_build_dependency_parent(
const renderive_Owner<Renderable_Base>& parent) {
auto& implementation = d_func();
if (implementation.real_time_data_state->scene_lifetime)
::renderive::error::unexpected<std::logic_error>(
"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<Renderable_Base>& parent) {
auto& implementation = d_func();
if (implementation.real_time_data_state->scene_lifetime)
::renderive::error::unexpected<std::logic_error>(
"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<Renderable_Base>& child) {
auto& implementation = d_func();
if (implementation.real_time_data_state->scene_lifetime)
::renderive::error::unexpected<std::logic_error>(
"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<Renderable_Base>& parent) {
renderable.add_build_dependency_parent(parent);
}
void renderive::detail::Renderable_Build_Access::add_display_parent(
Renderable_Base& renderable,
const renderive_Owner<Renderable_Base>& parent) {
renderable.add_build_display_parent(parent);
}
void renderive::detail::Renderable_Build_Access::add_display_child(
Renderable_Base& renderable,
const renderive_Owner<Renderable_Base>& 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();
}