secene 接入 多继承链
This commit is contained in:
@@ -0,0 +1,95 @@
|
||||
#pragma once
|
||||
|
||||
#include "renderive/inheritance/Inheritance.hpp"
|
||||
|
||||
#include <concepts>
|
||||
|
||||
namespace renderive::scene_inheritance {
|
||||
|
||||
struct Builder_Chain {};
|
||||
struct State_Chain {};
|
||||
struct Impl_Chain {};
|
||||
struct Editor_Chain {};
|
||||
|
||||
template <class Layer>
|
||||
struct Builder_Root
|
||||
: inheritance::Type_Node<Layer, inheritance::Type_Root, Builder_Chain> {};
|
||||
|
||||
template <class Layer, class Parent>
|
||||
struct Builder_Node : inheritance::Type_Node<Layer, Parent, Builder_Chain> {
|
||||
using Builder_Base = inheritance::Type_Node<Layer, Parent, Builder_Chain>;
|
||||
using Builder_Base::Builder_Base;
|
||||
};
|
||||
|
||||
template <class Layer>
|
||||
struct State_Root
|
||||
: inheritance::Type_Node<Layer, inheritance::Type_Root, State_Chain> {};
|
||||
|
||||
template <class Layer, class Parent>
|
||||
struct State_Node : inheritance::Type_Node<Layer, Parent, State_Chain> {
|
||||
using State_Base = inheritance::Type_Node<Layer, Parent, State_Chain>;
|
||||
using State_Base::State_Base;
|
||||
};
|
||||
|
||||
template <class Layer>
|
||||
struct Editor_Root
|
||||
: inheritance::Type_Node<Layer, inheritance::Type_Root, Editor_Chain> {};
|
||||
|
||||
template <class Layer, class Parent>
|
||||
struct Editor_Node : inheritance::Type_Node<Layer, Parent, Editor_Chain> {
|
||||
using Editor_Base = inheritance::Type_Node<Layer, Parent, Editor_Chain>;
|
||||
using Editor_Base::Editor_Base;
|
||||
};
|
||||
|
||||
template <class Layer>
|
||||
struct Observer_Root {
|
||||
using Owner = Layer;
|
||||
};
|
||||
|
||||
template <class Layer, class Parent>
|
||||
struct Observer_Node : Parent {
|
||||
using Owner = Layer;
|
||||
};
|
||||
|
||||
template <class Layer, class Event>
|
||||
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<typename Layer::Observer::Owner, Layer>;
|
||||
Layer::Observer::handle(layer, event);
|
||||
}
|
||||
{
|
||||
Layer::Observer::handle(layer, event);
|
||||
}
|
||||
};
|
||||
|
||||
template <class Layer, class Event>
|
||||
struct Impl_Root
|
||||
: inheritance::Dispatch_Root<Layer, inheritance::Type_Root, Impl_Chain,
|
||||
Event> {
|
||||
using Impl_Base =
|
||||
inheritance::Dispatch_Root<Layer, inheritance::Type_Root, Impl_Chain,
|
||||
Event>;
|
||||
using Scene_Event = Event;
|
||||
using Handler = Observer_Handler<Layer, Event>;
|
||||
using Impl_Base::Impl_Base;
|
||||
};
|
||||
|
||||
template <class Layer, class Parent>
|
||||
struct Impl_Node
|
||||
: inheritance::Dispatch_Node<Layer, Parent, Impl_Chain,
|
||||
typename Parent::Scene_Event> {
|
||||
using Impl_Base =
|
||||
inheritance::Dispatch_Node<Layer, Parent, Impl_Chain,
|
||||
typename Parent::Scene_Event>;
|
||||
using Scene_Event = typename Parent::Scene_Event;
|
||||
using Handler = Observer_Handler<Layer, Scene_Event>;
|
||||
using next_Observer = Observer_Node<Layer, typename Parent::Observer>;
|
||||
using Impl_Base::Impl_Base;
|
||||
};
|
||||
|
||||
} // namespace renderive::scene_inheritance
|
||||
@@ -40,7 +40,8 @@ public:
|
||||
template <class... Args>
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
explicit Scene2D_Context(std::pmr::memory_resource& memory_resource, Args&&... args)
|
||||
: Scene_2D_Base(memory_resource), Scene_State_Strategy(State{}), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())) {}
|
||||
: Scene_2D_Base(memory_resource, std::make_unique<Impl>()),
|
||||
Scene_State_Strategy(State{}), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())) {}
|
||||
template <class... Args>
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene2D_Context(With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
|
||||
@@ -48,7 +49,10 @@ public:
|
||||
template <class... Args>
|
||||
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene2D_Context(std::pmr::memory_resource& memory_resource, With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
|
||||
: Scene_2D_Base(memory_resource), Scene_State_Strategy(State{}, std::move(state_observer)), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())), scene_observer_(std::move(scene_observer.observer)) {}
|
||||
: Scene_2D_Base(
|
||||
memory_resource,
|
||||
std::make_unique<Impl>(std::move(scene_observer.observer))),
|
||||
Scene_State_Strategy(State{}, std::move(state_observer)), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())) {}
|
||||
~Scene2D_Context() override {
|
||||
this->shutdown();
|
||||
}
|
||||
@@ -89,13 +93,31 @@ protected:
|
||||
snapshot.set_scene_state(
|
||||
this->Scene_State_Strategy::render_state_value());
|
||||
}
|
||||
void observe_scene(const Scene_Base::Observation& observation) noexcept override {
|
||||
scene_observer_.observe(observation);
|
||||
}
|
||||
std::uint64_t observer_now_ns() const noexcept override {
|
||||
return scene_observer_.now_ns();
|
||||
}
|
||||
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<Impl, Scene_2D_Base::Impl> {
|
||||
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 <class... Args>
|
||||
static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
|
||||
if constexpr (std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args&&...>) {
|
||||
@@ -120,5 +142,4 @@ private:
|
||||
}
|
||||
}
|
||||
Cache final_color_cache_;
|
||||
Scene_Observer scene_observer_;
|
||||
};
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <concepts>
|
||||
#include <memory>
|
||||
#include <memory_resource>
|
||||
#include <utility>
|
||||
#include "renderive/base/observer/Observer.hpp"
|
||||
@@ -30,7 +31,8 @@ public:
|
||||
template <class... Args>
|
||||
requires Scene3D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
explicit Scene3D_Context(std::pmr::memory_resource& memory_resource, Args&&... args)
|
||||
: Scene_3D_Base(memory_resource), Scene_State_Strategy(State{}),
|
||||
: Scene_3D_Base(memory_resource, std::make_unique<Impl>()),
|
||||
Scene_State_Strategy(State{}),
|
||||
frame_control(make_frame_control(this->memory_resource(),
|
||||
std::forward<Args>(args)...)) {}
|
||||
template <class... Args>
|
||||
@@ -40,11 +42,12 @@ public:
|
||||
template <class... Args>
|
||||
requires Scene3D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
||||
Scene3D_Context(std::pmr::memory_resource& memory_resource, With_Observer<State_Observer> state_observer, With_Observer<Scene_Observer> scene_observer, Args&&... args)
|
||||
: Scene_3D_Base(memory_resource),
|
||||
: Scene_3D_Base(
|
||||
memory_resource,
|
||||
std::make_unique<Impl>(std::move(scene_observer.observer))),
|
||||
Scene_State_Strategy(State{}, std::move(state_observer)),
|
||||
frame_control(make_frame_control(this->memory_resource(),
|
||||
std::forward<Args>(args)...)),
|
||||
scene_observer_(std::move(scene_observer.observer)) {}
|
||||
std::forward<Args>(args)...)) {}
|
||||
~Scene3D_Context() override {
|
||||
this->shutdown();
|
||||
}
|
||||
@@ -63,13 +66,31 @@ protected:
|
||||
snapshot.set_scene_state(
|
||||
this->Scene_State_Strategy::render_state_value());
|
||||
}
|
||||
void observe_scene(const Scene_Base::Observation& observation) noexcept override {
|
||||
scene_observer_.observe(observation);
|
||||
}
|
||||
std::uint64_t observer_now_ns() const noexcept override {
|
||||
return scene_observer_.now_ns();
|
||||
}
|
||||
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<Impl, Scene_3D_Base::Impl> {
|
||||
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 <class... Args>
|
||||
static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
|
||||
if constexpr (std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args&&...>) {
|
||||
@@ -78,5 +99,4 @@ private:
|
||||
return Frame_Control(std::forward<Args>(args)...);
|
||||
}
|
||||
}
|
||||
Scene_Observer scene_observer_;
|
||||
};
|
||||
|
||||
@@ -23,131 +23,6 @@
|
||||
|
||||
static_assert(TF_VERSION == 400100, "Renderive requires Taskflow 4.1.0");
|
||||
|
||||
namespace {
|
||||
|
||||
bool contains_id(const std::vector<Renderable_Id>& values, Renderable_Id id) {
|
||||
return std::find(values.begin(), values.end(), id) != values.end();
|
||||
}
|
||||
|
||||
bool contains_id(const std::pmr::vector<Renderable_Id>& values,
|
||||
Renderable_Id id) {
|
||||
return std::find(values.begin(), values.end(), id) != values.end();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Scene_Base::Id_Topology::erase_id(std::pmr::vector<Renderable_Id>& values, Renderable_Id id) noexcept {
|
||||
std::erase(values, id);
|
||||
}
|
||||
void Scene_Base::Id_Topology::validate_endpoint(Renderable_Id id) const {
|
||||
if (!nodes_.contains(id))
|
||||
throw std::invalid_argument("topology endpoint is not attached");
|
||||
}
|
||||
void Scene_Base::Id_Topology::attach(Renderable_Id id) {
|
||||
if (id == 0 || nodes_.contains(id))
|
||||
throw std::invalid_argument("topology node is already attached");
|
||||
nodes_.try_emplace(id, *nodes_.get_allocator().resource());
|
||||
}
|
||||
void Scene_Base::Id_Topology::erase(Renderable_Id id) {
|
||||
const auto iterator = nodes_.find(id);
|
||||
if (iterator == nodes_.end())
|
||||
return;
|
||||
if (!iterator->second.parents.empty() || !iterator->second.children.empty())
|
||||
throw std::logic_error("topology node still has relationships");
|
||||
nodes_.erase(iterator);
|
||||
}
|
||||
bool Scene_Base::Id_Topology::replace_parents(Renderable_Id child, const std::vector<Renderable_Id>& parents) {
|
||||
validate_endpoint(child);
|
||||
std::vector<Renderable_Id> unique;
|
||||
unique.reserve(parents.size());
|
||||
for (const Renderable_Id parent : parents) {
|
||||
validate_endpoint(parent);
|
||||
if (!contains_id(unique, parent))
|
||||
unique.push_back(parent);
|
||||
}
|
||||
auto& child_node = nodes_.at(child);
|
||||
if (child_node.parents.size() == unique.size() && std::all_of(unique.begin(), unique.end(), [&](Renderable_Id parent) {
|
||||
return contains_id(child_node.parents, parent);
|
||||
}))
|
||||
return false;
|
||||
const std::vector<Renderable_Id> previous(child_node.parents.begin(), child_node.parents.end());
|
||||
for (const Renderable_Id parent : previous)
|
||||
erase_id(nodes_.at(parent).children, child);
|
||||
child_node.parents.clear();
|
||||
child_node.parents.insert(child_node.parents.end(), unique.begin(), unique.end());
|
||||
for (const Renderable_Id parent : unique) {
|
||||
auto& children = nodes_.at(parent).children;
|
||||
if (!contains_id(children, child))
|
||||
children.push_back(child);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool Scene_Base::Id_Topology::add_parent(Renderable_Id child, Renderable_Id parent) {
|
||||
validate_endpoint(child);
|
||||
validate_endpoint(parent);
|
||||
auto& child_node = nodes_.at(child);
|
||||
if (contains_id(child_node.parents, parent))
|
||||
return false;
|
||||
child_node.parents.push_back(parent);
|
||||
nodes_.at(parent).children.push_back(child);
|
||||
return true;
|
||||
}
|
||||
bool Scene_Base::Id_Topology::clear_parents(Renderable_Id child) {
|
||||
validate_endpoint(child);
|
||||
auto& child_node = nodes_.at(child);
|
||||
if (child_node.parents.empty())
|
||||
return false;
|
||||
const std::vector<Renderable_Id> parents(child_node.parents.begin(), child_node.parents.end());
|
||||
for (const Renderable_Id parent : parents)
|
||||
erase_id(nodes_.at(parent).children, child);
|
||||
child_node.parents.clear();
|
||||
return true;
|
||||
}
|
||||
bool Scene_Base::Id_Topology::contains(Renderable_Id id) const noexcept {
|
||||
return nodes_.contains(id);
|
||||
}
|
||||
bool Scene_Base::Id_Topology::isolated(Renderable_Id id) const {
|
||||
const auto& value = node(id);
|
||||
return value.parents.empty() && value.children.empty();
|
||||
}
|
||||
const Scene_Base::Topology_Node& Scene_Base::Id_Topology::node(Renderable_Id id) const {
|
||||
return nodes_.at(id);
|
||||
}
|
||||
std::vector<Renderable_Id> Scene_Base::Id_Topology::ids() const {
|
||||
std::vector<Renderable_Id> result;
|
||||
result.reserve(nodes_.size());
|
||||
for (const auto& [id, node] : nodes_) {
|
||||
static_cast<void>(node);
|
||||
result.push_back(id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::vector<Renderable_Id> Scene_Base::Id_Topology::order() const {
|
||||
std::unordered_map<Renderable_Id, std::size_t> indegree;
|
||||
indegree.reserve(nodes_.size());
|
||||
std::priority_queue<Renderable_Id, std::vector<Renderable_Id>, std::greater<>> ready;
|
||||
for (const auto& [id, node] : nodes_) {
|
||||
indegree.emplace(id, node.parents.size());
|
||||
if (node.parents.empty())
|
||||
ready.push(id);
|
||||
}
|
||||
std::vector<Renderable_Id> result;
|
||||
result.reserve(nodes_.size());
|
||||
while (!ready.empty()) {
|
||||
const Renderable_Id id = ready.top();
|
||||
ready.pop();
|
||||
result.push_back(id);
|
||||
for (const Renderable_Id child : nodes_.at(id).children) {
|
||||
auto& value = indegree.at(child);
|
||||
if (--value == 0)
|
||||
ready.push(child);
|
||||
}
|
||||
}
|
||||
if (result.size() != nodes_.size())
|
||||
throw std::logic_error("topology contains a cycle");
|
||||
return result;
|
||||
}
|
||||
|
||||
class Scene_Base::Execution_Context {
|
||||
public:
|
||||
static tf::Executor& executor() {
|
||||
@@ -171,11 +46,14 @@ private:
|
||||
|
||||
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<Impl>()) {}
|
||||
Scene_Base::Scene_Base(std::pmr::memory_resource& upstream_memory_resource,
|
||||
std::unique_ptr<Impl> impl)
|
||||
: scene_lifetime_(std::make_shared<Scene_Lifetime>(*this)),
|
||||
memory_domain_(std::allocate_shared<Scene_Memory_Domain>(std::pmr::polymorphic_allocator<Scene_Memory_Domain>(&upstream_memory_resource), upstream_memory_resource)),
|
||||
impl_(std::move(impl)),
|
||||
renderables_(&memory_domain_->resource()),
|
||||
display_topology_(memory_domain_->resource()),
|
||||
dependency_topology_(memory_domain_->resource()),
|
||||
dependency_resolver_(memory_domain_->resource()),
|
||||
color_caches_(&memory_domain_->resource()),
|
||||
task_(memory_domain_->resource()),
|
||||
renderable_edit_queue_(&memory_domain_->resource()),
|
||||
@@ -187,7 +65,7 @@ Scene_Base::~Scene_Base() {
|
||||
shutdown();
|
||||
}
|
||||
Scene_Base::Attach_Builder::Attach_Builder(Scene_Base& scene)
|
||||
: scene_(scene), task_lock_(scene.task_mutex_), editor_(scene) {
|
||||
: scene_(scene), task_lock_(scene.task_mutex_) {
|
||||
if (scene.runtime_started_)
|
||||
throw std::logic_error("scene attach builder is only available before runtime starts");
|
||||
}
|
||||
@@ -202,31 +80,19 @@ Scene_Base::Attach_Builder::~Attach_Builder() {
|
||||
}
|
||||
void Scene_Base::Attach_Builder::attach(Renderable renderable) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.attach(std::move(renderable));
|
||||
}
|
||||
void Scene_Base::Attach_Builder::set_display_parent(const Renderable& child, const Renderable& parent) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.set_display_parent(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::add_display_parent(const Renderable& child, const Renderable& parent) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.add_display_parent(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::clear_display_parent(const Renderable& child) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.clear_display_parent(child);
|
||||
Renderable_Editor(scene_).attach(std::move(renderable));
|
||||
}
|
||||
void Scene_Base::Attach_Builder::set_dependency_parent(const Renderable& child, const Renderable& parent) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.set_dependency_parent(child, parent);
|
||||
Renderable_Editor(scene_).set_dependency_parent(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::add_dependency_parent(const Renderable& child, const Renderable& parent) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.add_dependency_parent(child, parent);
|
||||
Renderable_Editor(scene_).add_dependency_parent(child, parent);
|
||||
}
|
||||
void Scene_Base::Attach_Builder::clear_dependency_parent(const Renderable& child) {
|
||||
std::lock_guard lock(scene_.model_mutex_);
|
||||
editor_.clear_dependency_parent(child);
|
||||
Renderable_Editor(scene_).clear_dependency_parent(child);
|
||||
}
|
||||
Scene_Base::Attach_Builder Scene_Base::attach_builder() {
|
||||
return Attach_Builder(*this);
|
||||
@@ -243,10 +109,10 @@ void Scene_Base::Renderable_Editor::attach(Renderable renderable) {
|
||||
throw std::logic_error("renderable id is already attached");
|
||||
return;
|
||||
}
|
||||
if (!scene_.display_topology_.contains(id))
|
||||
scene_.display_topology_.attach(id);
|
||||
if (!scene_.dependency_topology_.contains(id))
|
||||
scene_.dependency_topology_.attach(id);
|
||||
if (!scene_.dependency_resolver_.contains(id))
|
||||
scene_.dependency_resolver_.attach(id);
|
||||
if (auto* composition = scene_.composition_relationships())
|
||||
composition->attach(id);
|
||||
auto cache = scene_.make_renderable_color_cache();
|
||||
if (!scene_.renderables_.try_emplace(id, renderable).second)
|
||||
throw std::logic_error("renderable id is already attached");
|
||||
@@ -276,38 +142,12 @@ void Scene_Base::Renderable_Editor::detach(const Renderable& renderable) {
|
||||
scene_.renderables_.erase(id);
|
||||
scene_.notify_model_dirty();
|
||||
}
|
||||
void Scene_Base::Renderable_Editor::set_display_parent(const Renderable& child, const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("display relationship endpoint is null");
|
||||
scene_.validate_renderable_scene(*child);
|
||||
scene_.validate_renderable_scene(*parent);
|
||||
if (scene_.display_topology_.replace_parents(
|
||||
child->d_func().renderable_id,
|
||||
{parent->d_func().renderable_id}))
|
||||
scene_.notify_model_dirty();
|
||||
}
|
||||
void Scene_Base::Renderable_Editor::add_display_parent(const Renderable& child, const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("display relationship endpoint is null");
|
||||
scene_.validate_renderable_scene(*child);
|
||||
scene_.validate_renderable_scene(*parent);
|
||||
if (scene_.display_topology_.add_parent(child->d_func().renderable_id,
|
||||
parent->d_func().renderable_id))
|
||||
scene_.notify_model_dirty();
|
||||
}
|
||||
void Scene_Base::Renderable_Editor::clear_display_parent(const Renderable& child) {
|
||||
if (!child)
|
||||
throw std::invalid_argument("display child is null");
|
||||
scene_.validate_renderable_scene(*child);
|
||||
if (scene_.display_topology_.clear_parents(child->d_func().renderable_id))
|
||||
scene_.notify_model_dirty();
|
||||
}
|
||||
void Scene_Base::Renderable_Editor::set_dependency_parent(const Renderable& child, const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("dependency relationship endpoint is null");
|
||||
scene_.validate_renderable_scene(*child);
|
||||
scene_.validate_renderable_scene(*parent);
|
||||
if (!scene_.dependency_topology_.replace_parents(
|
||||
if (!scene_.dependency_resolver_.replace_parents(
|
||||
child->d_func().renderable_id,
|
||||
{parent->d_func().renderable_id}))
|
||||
return;
|
||||
@@ -319,7 +159,7 @@ void Scene_Base::Renderable_Editor::add_dependency_parent(const Renderable& chil
|
||||
throw std::invalid_argument("dependency relationship endpoint is null");
|
||||
scene_.validate_renderable_scene(*child);
|
||||
scene_.validate_renderable_scene(*parent);
|
||||
if (!scene_.dependency_topology_.add_parent(child->d_func().renderable_id,
|
||||
if (!scene_.dependency_resolver_.add_parent(child->d_func().renderable_id,
|
||||
parent->d_func().renderable_id))
|
||||
return;
|
||||
child->d_func().invalidate_prepare();
|
||||
@@ -329,7 +169,7 @@ void Scene_Base::Renderable_Editor::clear_dependency_parent(const Renderable& ch
|
||||
if (!child)
|
||||
throw std::invalid_argument("dependency child is null");
|
||||
scene_.validate_renderable_scene(*child);
|
||||
if (!scene_.dependency_topology_.clear_parents(child->d_func().renderable_id))
|
||||
if (!scene_.dependency_resolver_.clear_parents(child->d_func().renderable_id))
|
||||
return;
|
||||
child->d_func().invalidate_prepare();
|
||||
scene_.notify_model_dirty();
|
||||
@@ -337,8 +177,118 @@ void Scene_Base::Renderable_Editor::clear_dependency_parent(const Renderable& ch
|
||||
void Scene_Base::edit_renderables(Renderable_Edit edit) {
|
||||
if (!edit)
|
||||
throw std::invalid_argument("renderable edit callback is empty");
|
||||
enqueue_renderable_edit(std::move(edit));
|
||||
enqueue_renderable_edit([this, edit = std::move(edit)]() mutable {
|
||||
Renderable_Editor editor(*this);
|
||||
edit(editor);
|
||||
});
|
||||
}
|
||||
|
||||
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<Impl>()) {}
|
||||
Scene_2D_Base::Scene_2D_Base(std::pmr::memory_resource& memory_resource,
|
||||
std::unique_ptr<Scene_Base::Impl> impl)
|
||||
: Scene_Base(memory_resource, std::move(impl)),
|
||||
display_resolver_(this->memory_resource()) {}
|
||||
|
||||
Scene_2D_Base::Attach_Builder Scene_2D_Base::attach_builder() {
|
||||
return Attach_Builder(*this);
|
||||
}
|
||||
|
||||
void Scene_2D_Base::edit_renderables(Renderable_Edit edit) {
|
||||
if (!edit)
|
||||
throw std::invalid_argument("renderable edit callback is empty");
|
||||
enqueue_renderable_edit([this, edit = std::move(edit)]() mutable {
|
||||
Renderable_Editor editor(*this);
|
||||
edit(editor);
|
||||
});
|
||||
}
|
||||
|
||||
void Scene_2D_Base::with_initial_display_edit(std::function<void()> edit) {
|
||||
std::lock_guard lock(model_mutex_);
|
||||
edit();
|
||||
}
|
||||
|
||||
void Scene_2D_Base::Attach_Builder::set_display_parent(
|
||||
const Renderable& child, const Renderable& parent) {
|
||||
scene_2d_.with_initial_display_edit([&] {
|
||||
Renderable_Editor(scene_2d_).set_display_parent(child, parent);
|
||||
});
|
||||
}
|
||||
void Scene_2D_Base::Attach_Builder::add_display_parent(
|
||||
const Renderable& child, const Renderable& parent) {
|
||||
scene_2d_.with_initial_display_edit([&] {
|
||||
Renderable_Editor(scene_2d_).add_display_parent(child, parent);
|
||||
});
|
||||
}
|
||||
void Scene_2D_Base::Attach_Builder::clear_display_parent(
|
||||
const Renderable& child) {
|
||||
scene_2d_.with_initial_display_edit([&] {
|
||||
Renderable_Editor(scene_2d_).clear_display_parent(child);
|
||||
});
|
||||
}
|
||||
|
||||
void Scene_2D_Base::Renderable_Editor::set_display_parent(
|
||||
const Renderable& child, const Renderable& parent) {
|
||||
scene_2d_.set_display_parent_locked(child, parent);
|
||||
}
|
||||
void Scene_2D_Base::Renderable_Editor::add_display_parent(
|
||||
const Renderable& child, const Renderable& parent) {
|
||||
scene_2d_.add_display_parent_locked(child, parent);
|
||||
}
|
||||
void Scene_2D_Base::Renderable_Editor::clear_display_parent(
|
||||
const Renderable& child) {
|
||||
scene_2d_.clear_display_parent_locked(child);
|
||||
}
|
||||
|
||||
void Scene_2D_Base::set_display_parent_locked(const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("display relationship endpoint is null");
|
||||
validate_renderable_scene(*child);
|
||||
validate_renderable_scene(*parent);
|
||||
if (display_resolver_.replace_parents(
|
||||
child->renderable_id(), {parent->renderable_id()}))
|
||||
notify_model_dirty();
|
||||
}
|
||||
void Scene_2D_Base::add_display_parent_locked(const Renderable& child,
|
||||
const Renderable& parent) {
|
||||
if (!child || !parent)
|
||||
throw std::invalid_argument("display relationship endpoint is null");
|
||||
validate_renderable_scene(*child);
|
||||
validate_renderable_scene(*parent);
|
||||
if (display_resolver_.add_parent(child->renderable_id(),
|
||||
parent->renderable_id()))
|
||||
notify_model_dirty();
|
||||
}
|
||||
void Scene_2D_Base::clear_display_parent_locked(const Renderable& child) {
|
||||
if (!child)
|
||||
throw std::invalid_argument("display child is null");
|
||||
validate_renderable_scene(*child);
|
||||
if (display_resolver_.clear_parents(child->renderable_id()))
|
||||
notify_model_dirty();
|
||||
}
|
||||
|
||||
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<Impl>()) {}
|
||||
Scene_3D_Base::Scene_3D_Base(std::pmr::memory_resource& memory_resource,
|
||||
std::unique_ptr<Scene_Base::Impl> impl)
|
||||
: Scene_Base(memory_resource, std::move(impl)) {}
|
||||
Scene_3D_Base::Attach_Builder Scene_3D_Base::attach_builder() {
|
||||
return Attach_Builder(*this);
|
||||
}
|
||||
void Scene_3D_Base::edit_renderables(Renderable_Edit edit) {
|
||||
if (!edit)
|
||||
throw std::invalid_argument("renderable edit callback is empty");
|
||||
enqueue_renderable_edit([this, edit = std::move(edit)]() mutable {
|
||||
Renderable_Editor editor(*this);
|
||||
edit(editor);
|
||||
});
|
||||
}
|
||||
|
||||
void Scene_Base::render() {
|
||||
submit_render(nullptr);
|
||||
}
|
||||
@@ -382,21 +332,24 @@ void Scene_Base::submit_render(Abstract_Frame* frame) {
|
||||
? Render_Task::Frame{std::ref(*frame)}
|
||||
: Render_Task::Frame{std::make_shared<Abstract_Frame>()};
|
||||
task.plan = compile_render_plan(*snapshot, task);
|
||||
task.topology =
|
||||
std::make_shared<Topology_Snapshot>(topology_snapshot());
|
||||
task.completion = std::make_shared<Render_Completion>();
|
||||
snapshot->capture_ticket_ = capture_controller_.begin_frame();
|
||||
task.snapshot = std::move(snapshot);
|
||||
current_completion_ = task.completion;
|
||||
const Observation submitted_observation{
|
||||
Observation_Event::render_submitted, observer_now_ns(),
|
||||
Observation_Event::render_submitted, d_func().now_ns(),
|
||||
task.snapshot->render_sequence, task.snapshot->scene_state_revision_,
|
||||
task.snapshot->renderables.size()};
|
||||
task.snapshot->renderables.size(), task.topology, task.snapshot,
|
||||
task.plan};
|
||||
task_ = std::move(task);
|
||||
task_pending_ = true;
|
||||
task_lock.unlock();
|
||||
|
||||
Scene_Base* previous_observer_scene =
|
||||
std::exchange(active_submitted_observer_scene_, this);
|
||||
observe_scene(submitted_observation);
|
||||
d_func().dispatch(submitted_observation);
|
||||
active_submitted_observer_scene_ = previous_observer_scene;
|
||||
task_ready_.notify_one();
|
||||
|
||||
@@ -431,11 +384,11 @@ void Scene_Base::wait_for_render() {
|
||||
if (exception)
|
||||
std::rethrow_exception(exception);
|
||||
}
|
||||
void Scene_Base::enqueue_renderable_edit(Renderable_Edit edit) {
|
||||
void Scene_Base::enqueue_renderable_edit(std::function<void()> edit) {
|
||||
std::lock_guard<std::recursive_mutex> lock(task_mutex_);
|
||||
enqueue_renderable_edit_locked(std::move(edit));
|
||||
}
|
||||
void Scene_Base::enqueue_renderable_edit_locked(Renderable_Edit edit) {
|
||||
void Scene_Base::enqueue_renderable_edit_locked(std::function<void()> edit) {
|
||||
if (stop_)
|
||||
throw std::logic_error("scene is shutting down");
|
||||
runtime_started_ = true;
|
||||
@@ -443,17 +396,17 @@ void Scene_Base::enqueue_renderable_edit_locked(Renderable_Edit edit) {
|
||||
task_ready_.notify_one();
|
||||
}
|
||||
void Scene_Base::cleanup_detached_topology_locked() {
|
||||
const auto cleanup = [this](Id_Topology& topology, const char* name) {
|
||||
for (const Renderable_Id id : topology.ids()) {
|
||||
if (renderables_.contains(id))
|
||||
continue;
|
||||
if (!topology.isolated(id))
|
||||
throw std::logic_error(std::string(name) + " topology still references detached renderable " + std::to_string(id));
|
||||
topology.erase(id);
|
||||
}
|
||||
};
|
||||
cleanup(display_topology_, "display");
|
||||
cleanup(dependency_topology_, "dependency");
|
||||
for (const Renderable_Id id : dependency_resolver_.ids()) {
|
||||
if (renderables_.contains(id))
|
||||
continue;
|
||||
if (!dependency_resolver_.isolated(id))
|
||||
throw std::logic_error(
|
||||
"dependency graph still references detached renderable " +
|
||||
std::to_string(id));
|
||||
dependency_resolver_.erase(id);
|
||||
}
|
||||
if (auto* composition = composition_relationships())
|
||||
composition->cleanup();
|
||||
}
|
||||
void Scene_Base::validate_structure_locked() {
|
||||
for (const auto& [id, renderable] : renderables_) {
|
||||
@@ -463,22 +416,21 @@ void Scene_Base::validate_structure_locked() {
|
||||
if (!renderable->d_func().real_time_data_state->attached.load(
|
||||
std::memory_order_acquire))
|
||||
throw std::logic_error("scene contains renderable whose attached state is false");
|
||||
if (!display_topology_.contains(id))
|
||||
throw std::logic_error("display topology is missing attached renderable " + std::to_string(id));
|
||||
if (!dependency_topology_.contains(id))
|
||||
throw std::logic_error("dependency topology is missing attached renderable " + std::to_string(id));
|
||||
if (!dependency_resolver_.contains(id))
|
||||
throw std::logic_error("dependency graph is missing attached renderable " + std::to_string(id));
|
||||
}
|
||||
const auto validate_topology = [this](const Id_Topology& topology, const char* name) {
|
||||
const auto order = topology.order();
|
||||
if (order.size() != renderables_.size())
|
||||
throw std::logic_error(std::string(name) + " topology does not match attached renderables");
|
||||
for (const Renderable_Id id : order) {
|
||||
if (!renderables_.contains(id))
|
||||
throw std::logic_error(std::string(name) + " topology references detached renderable " + std::to_string(id));
|
||||
}
|
||||
};
|
||||
validate_topology(display_topology_, "display");
|
||||
validate_topology(dependency_topology_, "dependency");
|
||||
const auto dependency = dependency_resolver_.resolve();
|
||||
if (dependency.order.size() != renderables_.size())
|
||||
throw std::logic_error(
|
||||
"dependency graph does not match attached renderables");
|
||||
for (const Renderable_Id id : dependency.order) {
|
||||
if (!renderables_.contains(id))
|
||||
throw std::logic_error(
|
||||
"dependency graph references detached renderable " +
|
||||
std::to_string(id));
|
||||
}
|
||||
if (const auto* composition = composition_relationships())
|
||||
composition->validate();
|
||||
}
|
||||
[[noreturn]] void Scene_Base::structure_fail_fast(const char* stage, std::exception_ptr exception) noexcept {
|
||||
std::fprintf(stderr, "Renderive scene structure failure during %s", stage ? stage : "unknown stage");
|
||||
@@ -522,7 +474,7 @@ void Scene_Base::request_render_graph_rebuild(Renderable_Base& renderable) {
|
||||
notify_model_dirty();
|
||||
return;
|
||||
}
|
||||
enqueue_renderable_edit_locked([owner = std::move(owner)](Renderable_Editor&) {
|
||||
enqueue_renderable_edit_locked([owner = std::move(owner)] {
|
||||
owner->d_func().reset_render_graph();
|
||||
owner->d_func().notify_scene_model_dirty();
|
||||
});
|
||||
@@ -579,33 +531,49 @@ std::size_t Scene_Base::renderable_count() const {
|
||||
Scene_Base::Topology_Snapshot Scene_Base::topology_snapshot() const {
|
||||
std::lock_guard lock(model_mutex_);
|
||||
Topology_Snapshot snapshot;
|
||||
const auto ids = display_topology_.order();
|
||||
const auto dependency = dependency_resolver_.resolve();
|
||||
const auto* composition = composition_relationships();
|
||||
const auto display = composition
|
||||
? composition->resolve()
|
||||
: renderive::scene::dependency::Resolution<
|
||||
Renderable_Id>{};
|
||||
const auto& ids = dependency.order;
|
||||
snapshot.renderables.reserve(ids.size());
|
||||
snapshot.display.reserve(ids.size());
|
||||
snapshot.dependency.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));
|
||||
const auto append_relationships = [&](const Id_Topology& topology,
|
||||
const auto append_relationships = [&](const auto& resolution,
|
||||
auto& output) {
|
||||
for (const Renderable_Id id : ids) {
|
||||
const auto& parents = topology.node(id).parents;
|
||||
if (parents.empty()) {
|
||||
for (const Renderable_Id id : resolution.order) {
|
||||
if (std::ranges::none_of(
|
||||
resolution.relationships,
|
||||
[id](const auto& relationship) {
|
||||
return relationship.child == id;
|
||||
}))
|
||||
output.push_back({renderables_.at(id), {}});
|
||||
continue;
|
||||
}
|
||||
for (const Renderable_Id parent : parents)
|
||||
output.push_back({renderables_.at(id), renderables_.at(parent)});
|
||||
}
|
||||
for (const auto& relationship : resolution.relationships)
|
||||
output.push_back({renderables_.at(relationship.child),
|
||||
renderables_.at(relationship.parent)});
|
||||
};
|
||||
append_relationships(display_topology_, snapshot.display);
|
||||
append_relationships(dependency_topology_, snapshot.dependency);
|
||||
if (!display.order.empty())
|
||||
append_relationships(display, snapshot.display);
|
||||
append_relationships(dependency, snapshot.dependency);
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
std::vector<Scene_Base::Const_Renderable> Scene_Base::paint_order_snapshot() const {
|
||||
std::lock_guard lock(model_mutex_);
|
||||
std::vector<Const_Renderable> result;
|
||||
for (const Renderable_Id id : display_topology_.order())
|
||||
const auto* composition = composition_relationships();
|
||||
auto display = composition
|
||||
? composition->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));
|
||||
return result;
|
||||
}
|
||||
@@ -613,8 +581,14 @@ std::vector<Scene_Base::Const_Renderable> Scene_Base::paint_order_snapshot() con
|
||||
std::shared_ptr<Frame_Render_Snapshot> Scene_Base::snapshot_live_model() {
|
||||
auto snapshot = std::make_shared<Frame_Render_Snapshot>();
|
||||
std::lock_guard lock(model_mutex_);
|
||||
const auto dependency_order = dependency_topology_.order();
|
||||
snapshot->display_order = display_topology_.order();
|
||||
const auto dependency = dependency_resolver_.resolve();
|
||||
const auto* composition = composition_relationships();
|
||||
const auto display = composition
|
||||
? composition->resolve()
|
||||
: renderive::scene::dependency::Resolution<
|
||||
Renderable_Id>{};
|
||||
const auto& dependency_order = dependency.order;
|
||||
snapshot->display_order = display.order;
|
||||
snapshot->renderables.reserve(dependency_order.size());
|
||||
for (const Renderable_Id id : dependency_order) {
|
||||
const Renderable& owner = renderables_.at(id);
|
||||
@@ -646,8 +620,7 @@ std::shared_ptr<Frame_Render_Snapshot> Scene_Base::snapshot_live_model() {
|
||||
state.prepare_required = !prepare_valid;
|
||||
state.paint_required = !paint_valid;
|
||||
state.render_graph = renderable_data.render_graph_snapshot();
|
||||
const auto& parents = dependency_topology_.node(id).parents;
|
||||
state.dependency_parent_ids.assign(parents.begin(), parents.end());
|
||||
state.dependency_parent_ids = dependency_resolver_.parents(id);
|
||||
state.owner_ = owner;
|
||||
if (const auto cache = color_caches_.find(id);
|
||||
cache != color_caches_.end())
|
||||
@@ -795,9 +768,57 @@ std::uint64_t Scene_Base::acquire_scene_state() {
|
||||
|
||||
void Scene_Base::capture_scene_state(Frame_Render_Snapshot&) const {}
|
||||
|
||||
void Scene_Base::observe_scene(const Observation&) noexcept {}
|
||||
renderive::scene::detail::Composition_Relationships*
|
||||
Scene_Base::composition_relationships() noexcept {
|
||||
auto* scene_2d = dynamic_cast<Scene_2D_Base*>(this);
|
||||
return scene_2d
|
||||
? static_cast<
|
||||
renderive::scene::detail::Composition_Relationships*>(
|
||||
scene_2d)
|
||||
: nullptr;
|
||||
}
|
||||
const renderive::scene::detail::Composition_Relationships*
|
||||
Scene_Base::composition_relationships() const noexcept {
|
||||
const auto* scene_2d = dynamic_cast<const Scene_2D_Base*>(this);
|
||||
return scene_2d
|
||||
? static_cast<const renderive::scene::detail::
|
||||
Composition_Relationships*>(scene_2d)
|
||||
: nullptr;
|
||||
}
|
||||
|
||||
std::uint64_t Scene_Base::observer_now_ns() const noexcept {
|
||||
void Scene_2D_Base::attach(Renderable_Id id) {
|
||||
if (!display_resolver_.contains(id))
|
||||
display_resolver_.attach(id);
|
||||
}
|
||||
void Scene_2D_Base::cleanup() {
|
||||
for (const Renderable_Id id : display_resolver_.ids()) {
|
||||
if (renderables_.contains(id))
|
||||
continue;
|
||||
if (!display_resolver_.isolated(id))
|
||||
throw std::logic_error(
|
||||
"display graph still references detached renderable " +
|
||||
std::to_string(id));
|
||||
display_resolver_.erase(id);
|
||||
}
|
||||
}
|
||||
void Scene_2D_Base::validate() const {
|
||||
const auto display = display_resolver_.resolve();
|
||||
if (display.order.size() != renderables_.size())
|
||||
throw std::logic_error(
|
||||
"display graph does not match attached renderables");
|
||||
for (const Renderable_Id id : display.order) {
|
||||
if (!renderables_.contains(id))
|
||||
throw std::logic_error(
|
||||
"display graph references detached renderable " +
|
||||
std::to_string(id));
|
||||
}
|
||||
}
|
||||
renderive::scene::dependency::Resolution<Renderable_Id>
|
||||
Scene_2D_Base::resolve() const {
|
||||
return display_resolver_.resolve();
|
||||
}
|
||||
|
||||
std::uint64_t Scene_Base::Impl::now_ns() const noexcept {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -875,10 +896,9 @@ void Scene_Base::render_loop() {
|
||||
{
|
||||
std::lock_guard lock(model_mutex_);
|
||||
Scene_Base* previous = std::exchange(active_renderable_edit_scene_, this);
|
||||
Renderable_Editor editor(*this);
|
||||
for (auto& command : edits) {
|
||||
try {
|
||||
command.edit(editor);
|
||||
command.execute();
|
||||
cleanup_detached_topology_locked();
|
||||
validate_structure_locked();
|
||||
} catch (...) {
|
||||
@@ -896,23 +916,26 @@ void Scene_Base::render_loop() {
|
||||
continue;
|
||||
}
|
||||
const auto& snapshot = *task.snapshot;
|
||||
observe_scene({Observation_Event::render_started, observer_now_ns(),
|
||||
d_func().dispatch({Observation_Event::render_started, d_func().now_ns(),
|
||||
snapshot.render_sequence,
|
||||
snapshot.scene_state_revision_,
|
||||
snapshot.renderables.size()});
|
||||
snapshot.renderables.size(), task.topology,
|
||||
task.snapshot, task.plan});
|
||||
std::exception_ptr exception;
|
||||
try {
|
||||
execute_taskflow(task);
|
||||
observe_scene({Observation_Event::render_completed, observer_now_ns(),
|
||||
d_func().dispatch({Observation_Event::render_completed, d_func().now_ns(),
|
||||
snapshot.render_sequence,
|
||||
snapshot.scene_state_revision_,
|
||||
snapshot.renderables.size()});
|
||||
snapshot.renderables.size(), task.topology,
|
||||
task.snapshot, task.plan});
|
||||
} catch (...) {
|
||||
exception = std::current_exception();
|
||||
observe_scene({Observation_Event::render_failed, observer_now_ns(),
|
||||
d_func().dispatch({Observation_Event::render_failed, d_func().now_ns(),
|
||||
snapshot.render_sequence,
|
||||
snapshot.scene_state_revision_,
|
||||
snapshot.renderables.size()});
|
||||
snapshot.renderables.size(), task.topology,
|
||||
task.snapshot, task.plan});
|
||||
}
|
||||
{
|
||||
std::lock_guard lock(task_mutex_);
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
#include "renderive/renderable/base/Renderable_Base.hpp"
|
||||
#include "renderive/renderable/base/Renderive_Owner.hpp"
|
||||
#include "renderive/render_graph/Render_Plan.hpp"
|
||||
#include "renderive/scene/Inheritance.hpp"
|
||||
#include "renderive/scene/dependency/Dependency_Resolver.hpp"
|
||||
#include "Abstract_Frame.hpp"
|
||||
#include "Frame_Render_Snapshot.hpp"
|
||||
#include "Frame_Viewport.hpp"
|
||||
@@ -32,6 +34,18 @@
|
||||
|
||||
class Color_Cache;
|
||||
|
||||
namespace renderive::scene::detail {
|
||||
|
||||
struct Composition_Relationships {
|
||||
virtual ~Composition_Relationships() = default;
|
||||
virtual void attach(Renderable_Id id) = 0;
|
||||
virtual void cleanup() = 0;
|
||||
virtual void validate() const = 0;
|
||||
virtual dependency::Resolution<Renderable_Id> resolve() const = 0;
|
||||
};
|
||||
|
||||
} // namespace renderive::scene::detail
|
||||
|
||||
class Scene_Compositor {
|
||||
public:
|
||||
virtual ~Scene_Compositor() = default;
|
||||
@@ -55,24 +69,24 @@ public:
|
||||
|
||||
class Attach_Builder;
|
||||
|
||||
class Renderable_Editor {
|
||||
class Renderable_Editor
|
||||
: public renderive::scene_inheritance::Editor_Root<Renderable_Editor> {
|
||||
public:
|
||||
void attach(Renderable renderable);
|
||||
void detach(const Renderable& renderable);
|
||||
void set_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void clear_display_parent(const Renderable& child);
|
||||
void set_dependency_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_dependency_parent(const Renderable& child, const Renderable& parent);
|
||||
void clear_dependency_parent(const Renderable& child);
|
||||
private:
|
||||
protected:
|
||||
explicit Renderable_Editor(Scene_Base& scene) : scene_(scene) {}
|
||||
Scene_Base& scene_;
|
||||
private:
|
||||
friend class Scene_Base;
|
||||
friend class Attach_Builder;
|
||||
};
|
||||
|
||||
class Attach_Builder {
|
||||
class Attach_Builder
|
||||
: public renderive::scene_inheritance::Builder_Root<Attach_Builder> {
|
||||
public:
|
||||
Attach_Builder(const Attach_Builder&) = delete;
|
||||
Attach_Builder& operator=(const Attach_Builder&) = delete;
|
||||
@@ -80,22 +94,30 @@ public:
|
||||
Attach_Builder& operator=(Attach_Builder&&) = delete;
|
||||
~Attach_Builder();
|
||||
void attach(Renderable renderable);
|
||||
void set_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void clear_display_parent(const Renderable& child);
|
||||
void set_dependency_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_dependency_parent(const Renderable& child, const Renderable& parent);
|
||||
void clear_dependency_parent(const Renderable& child);
|
||||
private:
|
||||
protected:
|
||||
explicit Attach_Builder(Scene_Base& scene);
|
||||
Scene_Base& scene_;
|
||||
std::unique_lock<std::recursive_mutex> task_lock_;
|
||||
Renderable_Editor editor_;
|
||||
private:
|
||||
friend class Scene_Base;
|
||||
};
|
||||
|
||||
using Renderable_Edit = std::function<void(Renderable_Editor&)>;
|
||||
|
||||
struct Topology_Relationship {
|
||||
Const_Renderable child;
|
||||
Const_Renderable parent;
|
||||
};
|
||||
|
||||
struct Topology_Snapshot {
|
||||
std::vector<Const_Renderable> renderables;
|
||||
std::vector<Topology_Relationship> display;
|
||||
std::vector<Topology_Relationship> dependency;
|
||||
};
|
||||
|
||||
enum class Observation_Event {
|
||||
render_submitted,
|
||||
render_started,
|
||||
@@ -109,17 +131,9 @@ public:
|
||||
std::uint64_t render_sequence{};
|
||||
std::uint64_t scene_state_revision{};
|
||||
std::size_t renderable_count{};
|
||||
};
|
||||
|
||||
struct Topology_Relationship {
|
||||
Const_Renderable child;
|
||||
Const_Renderable parent;
|
||||
};
|
||||
|
||||
struct Topology_Snapshot {
|
||||
std::vector<Const_Renderable> renderables;
|
||||
std::vector<Topology_Relationship> display;
|
||||
std::vector<Topology_Relationship> dependency;
|
||||
std::shared_ptr<const Topology_Snapshot> topology;
|
||||
std::shared_ptr<const Frame_Render_Snapshot> frame_snapshot;
|
||||
std::shared_ptr<const Render_Plan> render_plan;
|
||||
};
|
||||
|
||||
Scene_Base();
|
||||
@@ -166,54 +180,37 @@ public:
|
||||
|
||||
|
||||
protected:
|
||||
struct State_Layer
|
||||
: renderive::scene_inheritance::State_Root<State_Layer> {};
|
||||
|
||||
struct Impl
|
||||
: renderive::scene_inheritance::Impl_Root<Impl, Observation> {
|
||||
struct Observer : renderive::scene_inheritance::Observer_Root<Impl> {};
|
||||
virtual std::uint64_t now_ns() const noexcept;
|
||||
virtual ~Impl() = default;
|
||||
};
|
||||
|
||||
Scene_Base(std::pmr::memory_resource& upstream_memory_resource,
|
||||
std::unique_ptr<Impl> impl);
|
||||
Impl& d_func() noexcept { return *impl_; }
|
||||
const Impl& d_func() const noexcept { return *impl_; }
|
||||
|
||||
virtual Frame_Control_Strategy_Base& frame_control_strategy_impl();
|
||||
virtual const Frame_Control_Strategy_Base& frame_control_strategy_impl() const;
|
||||
virtual std::shared_ptr<Color_Cache> make_renderable_color_cache();
|
||||
virtual Frame_Viewport frame_viewport() const;
|
||||
virtual std::uint64_t acquire_scene_state();
|
||||
virtual void capture_scene_state(Frame_Render_Snapshot& snapshot) const;
|
||||
virtual void observe_scene(const Observation& observation) noexcept;
|
||||
virtual std::uint64_t observer_now_ns() const noexcept;
|
||||
|
||||
std::unique_lock<std::recursive_mutex> lock_render_idle();
|
||||
[[nodiscard]] bool is_render_worker_thread() const noexcept;
|
||||
bool consume_model_dirty() noexcept;
|
||||
void invalidate_renderables();
|
||||
void shutdown() noexcept;
|
||||
void enqueue_renderable_edit(std::function<void()> edit);
|
||||
|
||||
private:
|
||||
friend class Renderable_Base;
|
||||
|
||||
struct Topology_Node {
|
||||
explicit Topology_Node(std::pmr::memory_resource& memory_resource)
|
||||
: parents(&memory_resource), children(&memory_resource) {}
|
||||
|
||||
std::pmr::vector<Renderable_Id> parents;
|
||||
std::pmr::vector<Renderable_Id> children;
|
||||
};
|
||||
|
||||
class Id_Topology {
|
||||
public:
|
||||
explicit Id_Topology(std::pmr::memory_resource& memory_resource)
|
||||
: nodes_(&memory_resource) {}
|
||||
|
||||
void attach(Renderable_Id id);
|
||||
void erase(Renderable_Id id);
|
||||
bool replace_parents(Renderable_Id child, const std::vector<Renderable_Id>& parents);
|
||||
bool add_parent(Renderable_Id child, Renderable_Id parent);
|
||||
bool clear_parents(Renderable_Id child);
|
||||
[[nodiscard]] bool contains(Renderable_Id id) const noexcept;
|
||||
[[nodiscard]] bool isolated(Renderable_Id id) const;
|
||||
[[nodiscard]] const Topology_Node& node(Renderable_Id id) const;
|
||||
[[nodiscard]] std::vector<Renderable_Id> ids() const;
|
||||
[[nodiscard]] std::vector<Renderable_Id> order() const;
|
||||
|
||||
private:
|
||||
void validate_endpoint(Renderable_Id id) const;
|
||||
static void erase_id(std::pmr::vector<Renderable_Id>& values, Renderable_Id id) noexcept;
|
||||
|
||||
std::pmr::unordered_map<Renderable_Id, Topology_Node> nodes_;
|
||||
};
|
||||
friend class Scene_2D_Base;
|
||||
|
||||
using Composite_Render_Node_Function =
|
||||
std::function<void(const Composite_Render_Context&)>;
|
||||
@@ -239,13 +236,14 @@ private:
|
||||
|
||||
std::shared_ptr<const Frame_Render_Snapshot> snapshot;
|
||||
std::shared_ptr<const Render_Plan> plan;
|
||||
std::shared_ptr<const Topology_Snapshot> topology;
|
||||
std::pmr::vector<Execution_Binding> execution_bindings;
|
||||
Frame frame;
|
||||
std::shared_ptr<Render_Completion> completion;
|
||||
};
|
||||
|
||||
struct Renderable_Edit_Command {
|
||||
Renderable_Edit edit;
|
||||
std::function<void()> execute;
|
||||
};
|
||||
|
||||
class Execution_Context;
|
||||
@@ -258,8 +256,7 @@ private:
|
||||
};
|
||||
|
||||
void submit_render(Abstract_Frame* frame);
|
||||
void enqueue_renderable_edit(Renderable_Edit edit);
|
||||
void enqueue_renderable_edit_locked(Renderable_Edit edit);
|
||||
void enqueue_renderable_edit_locked(std::function<void()> edit);
|
||||
void request_render_graph_rebuild(Renderable_Base& renderable);
|
||||
[[nodiscard]] std::shared_ptr<Frame_Render_Snapshot> snapshot_live_model();
|
||||
[[nodiscard]] std::shared_ptr<Frame_Render_Snapshot>
|
||||
@@ -274,6 +271,10 @@ private:
|
||||
const Renderable& renderable) const;
|
||||
void validate_structure_locked();
|
||||
void cleanup_detached_topology_locked();
|
||||
renderive::scene::detail::Composition_Relationships*
|
||||
composition_relationships() noexcept;
|
||||
const renderive::scene::detail::Composition_Relationships*
|
||||
composition_relationships() const noexcept;
|
||||
[[noreturn]] static void structure_fail_fast(const char* stage, std::exception_ptr exception = {}) noexcept;
|
||||
|
||||
inline static thread_local Scene_Base* active_execution_scene_{};
|
||||
@@ -282,10 +283,11 @@ private:
|
||||
|
||||
std::shared_ptr<Scene_Lifetime> scene_lifetime_;
|
||||
std::shared_ptr<Scene_Memory_Domain> memory_domain_;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
std::atomic_bool model_dirty_{true};
|
||||
std::pmr::unordered_map<Renderable_Id, Renderable> renderables_;
|
||||
Id_Topology display_topology_;
|
||||
Id_Topology dependency_topology_;
|
||||
renderive::scene::dependency::Dependency_Resolver<Renderable_Id>
|
||||
dependency_resolver_;
|
||||
std::pmr::unordered_map<Renderable_Id, std::shared_ptr<Color_Cache>> color_caches_;
|
||||
mutable std::mutex model_mutex_;
|
||||
|
||||
@@ -313,16 +315,117 @@ private:
|
||||
Capture_Repository capture_repository_;
|
||||
};
|
||||
|
||||
class Scene_2D_Base : public Scene_Base {
|
||||
class Scene_2D_Base
|
||||
: public Scene_Base,
|
||||
private renderive::scene::detail::Composition_Relationships {
|
||||
public:
|
||||
Scene_2D_Base() = default;
|
||||
explicit Scene_2D_Base(std::pmr::memory_resource& memory_resource)
|
||||
: Scene_Base(memory_resource) {}
|
||||
class Renderable_Editor
|
||||
: public renderive::scene_inheritance::Editor_Node<
|
||||
Renderable_Editor, Scene_Base::Renderable_Editor> {
|
||||
public:
|
||||
void set_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void 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) {}
|
||||
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:
|
||||
void set_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void add_display_parent(const Renderable& child, const Renderable& parent);
|
||||
void 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) {}
|
||||
Scene_2D_Base& scene_2d_;
|
||||
friend class Scene_2D_Base;
|
||||
};
|
||||
|
||||
using Renderable_Edit = std::function<void(Renderable_Editor&)>;
|
||||
|
||||
Scene_2D_Base();
|
||||
explicit Scene_2D_Base(std::pmr::memory_resource& memory_resource);
|
||||
Attach_Builder attach_builder();
|
||||
void edit_renderables(Renderable_Edit edit);
|
||||
|
||||
protected:
|
||||
struct State_Layer
|
||||
: renderive::scene_inheritance::State_Node<
|
||||
State_Layer, Scene_Base::State_Layer> {};
|
||||
struct Impl : renderive::scene_inheritance::Impl_Node<Impl, Scene_Base::Impl> {
|
||||
struct Observer
|
||||
: renderive::scene_inheritance::Observer_Node<
|
||||
Impl, Scene_Base::Impl::Observer> {};
|
||||
};
|
||||
Scene_2D_Base(std::pmr::memory_resource& memory_resource,
|
||||
std::unique_ptr<Scene_Base::Impl> impl);
|
||||
|
||||
private:
|
||||
friend class Scene_Base;
|
||||
void with_initial_display_edit(std::function<void()> edit);
|
||||
void set_display_parent_locked(const Renderable& child,
|
||||
const Renderable& parent);
|
||||
void add_display_parent_locked(const Renderable& child,
|
||||
const Renderable& parent);
|
||||
void clear_display_parent_locked(const Renderable& child);
|
||||
void attach(Renderable_Id id) override;
|
||||
void cleanup() override;
|
||||
void validate() const override;
|
||||
renderive::scene::dependency::Resolution<Renderable_Id> resolve()
|
||||
const override;
|
||||
|
||||
renderive::scene::dependency::Dependency_Resolver<Renderable_Id>
|
||||
display_resolver_;
|
||||
};
|
||||
|
||||
class Scene_3D_Base : public Scene_Base {
|
||||
public:
|
||||
Scene_3D_Base() = default;
|
||||
explicit Scene_3D_Base(std::pmr::memory_resource& memory_resource)
|
||||
: Scene_Base(memory_resource) {}
|
||||
class Renderable_Editor
|
||||
: public renderive::scene_inheritance::Editor_Node<
|
||||
Renderable_Editor, Scene_Base::Renderable_Editor> {
|
||||
private:
|
||||
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> {
|
||||
private:
|
||||
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<void(Renderable_Editor&)>;
|
||||
Scene_3D_Base();
|
||||
explicit Scene_3D_Base(std::pmr::memory_resource& memory_resource);
|
||||
Attach_Builder attach_builder();
|
||||
void edit_renderables(Renderable_Edit edit);
|
||||
|
||||
protected:
|
||||
struct State_Layer
|
||||
: renderive::scene_inheritance::State_Node<
|
||||
State_Layer, Scene_Base::State_Layer> {};
|
||||
struct Impl : renderive::scene_inheritance::Impl_Node<Impl, Scene_Base::Impl> {
|
||||
struct Observer
|
||||
: renderive::scene_inheritance::Observer_Node<
|
||||
Impl, Scene_Base::Impl::Observer> {};
|
||||
};
|
||||
Scene_3D_Base(std::pmr::memory_resource& memory_resource,
|
||||
std::unique_ptr<Scene_Base::Impl> impl);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <memory_resource>
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace renderive::scene::dependency {
|
||||
|
||||
template <class Id>
|
||||
struct Relationship {
|
||||
Id child{};
|
||||
Id parent{};
|
||||
bool operator==(const Relationship&) const = default;
|
||||
};
|
||||
|
||||
template <class Id>
|
||||
struct Resolution {
|
||||
std::vector<Id> order;
|
||||
std::vector<Relationship<Id>> relationships;
|
||||
};
|
||||
|
||||
// Stores only declared relationships. Order and relationship snapshots are
|
||||
// derived on demand, so mutations have one authoritative source.
|
||||
template <class Id, class Hash = std::hash<Id>>
|
||||
class Dependency_Resolver {
|
||||
public:
|
||||
explicit Dependency_Resolver(std::pmr::memory_resource& resource)
|
||||
: nodes_(&resource) {}
|
||||
|
||||
void attach(Id id) {
|
||||
if (!nodes_.try_emplace(id, resource()).second)
|
||||
throw std::logic_error("dependency id is already attached");
|
||||
}
|
||||
|
||||
void erase(Id id) {
|
||||
validate_endpoint(id);
|
||||
for (auto& [node_id, node] : nodes_) {
|
||||
static_cast<void>(node_id);
|
||||
erase_id(node.parents, id);
|
||||
erase_id(node.children, id);
|
||||
}
|
||||
nodes_.erase(id);
|
||||
}
|
||||
|
||||
bool replace_parents(Id child, const std::vector<Id>& parents) {
|
||||
validate_endpoint(child);
|
||||
std::vector<Id> unique;
|
||||
unique.reserve(parents.size());
|
||||
for (const Id parent : parents) {
|
||||
validate_relationship(child, parent);
|
||||
if (std::find(unique.begin(), unique.end(), parent) == unique.end())
|
||||
unique.push_back(parent);
|
||||
}
|
||||
std::sort(unique.begin(), unique.end());
|
||||
auto& current = nodes_.at(child).parents;
|
||||
if (std::equal(current.begin(), current.end(), unique.begin(),
|
||||
unique.end()))
|
||||
return false;
|
||||
const std::vector<Id> previous(current.begin(), current.end());
|
||||
assign_parents(child, unique);
|
||||
try {
|
||||
validate_acyclic();
|
||||
} catch (...) {
|
||||
assign_parents(child, previous);
|
||||
throw;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool add_parent(Id child, Id parent) {
|
||||
validate_relationship(child, parent);
|
||||
auto parents = std::vector<Id>(nodes_.at(child).parents.begin(),
|
||||
nodes_.at(child).parents.end());
|
||||
if (std::find(parents.begin(), parents.end(), parent) != parents.end())
|
||||
return false;
|
||||
parents.push_back(parent);
|
||||
return replace_parents(child, parents);
|
||||
}
|
||||
|
||||
bool clear_parents(Id child) {
|
||||
validate_endpoint(child);
|
||||
if (nodes_.at(child).parents.empty())
|
||||
return false;
|
||||
return replace_parents(child, {});
|
||||
}
|
||||
|
||||
[[nodiscard]] bool contains(Id id) const noexcept {
|
||||
return nodes_.contains(id);
|
||||
}
|
||||
|
||||
[[nodiscard]] bool isolated(Id id) const {
|
||||
validate_endpoint(id);
|
||||
const auto& node = nodes_.at(id);
|
||||
return node.parents.empty() && node.children.empty();
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Id> ids() const {
|
||||
std::vector<Id> result;
|
||||
result.reserve(nodes_.size());
|
||||
for (const auto& [id, node] : nodes_) {
|
||||
static_cast<void>(node);
|
||||
result.push_back(id);
|
||||
}
|
||||
std::sort(result.begin(), result.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::vector<Id> parents(Id id) const {
|
||||
validate_endpoint(id);
|
||||
const auto& values = nodes_.at(id).parents;
|
||||
return {values.begin(), values.end()};
|
||||
}
|
||||
|
||||
[[nodiscard]] Resolution<Id> resolve() const {
|
||||
Resolution<Id> result;
|
||||
result.relationships.reserve(edge_count());
|
||||
for (const Id child : ids()) {
|
||||
for (const Id parent : nodes_.at(child).parents)
|
||||
result.relationships.push_back({child, parent});
|
||||
}
|
||||
|
||||
std::unordered_map<Id, std::size_t, Hash> indegree;
|
||||
indegree.reserve(nodes_.size());
|
||||
std::priority_queue<Id, std::vector<Id>, std::greater<Id>> ready;
|
||||
for (const auto& [id, node] : nodes_) {
|
||||
indegree.emplace(id, node.parents.size());
|
||||
if (node.parents.empty())
|
||||
ready.push(id);
|
||||
}
|
||||
result.order.reserve(nodes_.size());
|
||||
while (!ready.empty()) {
|
||||
const Id id = ready.top();
|
||||
ready.pop();
|
||||
result.order.push_back(id);
|
||||
for (const Id child : nodes_.at(id).children) {
|
||||
auto& degree = indegree.at(child);
|
||||
if (--degree == 0)
|
||||
ready.push(child);
|
||||
}
|
||||
}
|
||||
if (result.order.size() != nodes_.size())
|
||||
throw std::logic_error("dependency graph contains a cycle");
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
struct Node {
|
||||
explicit Node(std::pmr::memory_resource* resource)
|
||||
: parents(resource), children(resource) {}
|
||||
std::pmr::vector<Id> parents;
|
||||
std::pmr::vector<Id> children;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::pmr::memory_resource* resource() const noexcept {
|
||||
return nodes_.get_allocator().resource();
|
||||
}
|
||||
|
||||
void validate_endpoint(Id id) const {
|
||||
if (!nodes_.contains(id))
|
||||
throw std::invalid_argument("dependency endpoint is not attached");
|
||||
}
|
||||
|
||||
void validate_relationship(Id child, Id parent) const {
|
||||
validate_endpoint(child);
|
||||
validate_endpoint(parent);
|
||||
if (child == parent)
|
||||
throw std::invalid_argument("dependency cannot reference itself");
|
||||
}
|
||||
|
||||
void validate_acyclic() const { static_cast<void>(resolve()); }
|
||||
|
||||
void assign_parents(Id child, const std::vector<Id>& parents) {
|
||||
auto& current = nodes_.at(child).parents;
|
||||
for (const Id parent : current)
|
||||
erase_id(nodes_.at(parent).children, child);
|
||||
current.assign(parents.begin(), parents.end());
|
||||
for (const Id parent : current) {
|
||||
auto& children = nodes_.at(parent).children;
|
||||
if (std::find(children.begin(), children.end(), child) ==
|
||||
children.end())
|
||||
children.push_back(child);
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::size_t edge_count() const noexcept {
|
||||
std::size_t result{};
|
||||
for (const auto& [id, node] : nodes_) {
|
||||
static_cast<void>(id);
|
||||
result += node.parents.size();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static void erase_id(std::pmr::vector<Id>& values, Id id) noexcept {
|
||||
values.erase(std::remove(values.begin(), values.end(), id),
|
||||
values.end());
|
||||
}
|
||||
|
||||
std::pmr::unordered_map<Id, Node, Hash> nodes_;
|
||||
};
|
||||
|
||||
} // namespace renderive::scene::dependency
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory_resource>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "renderive/scene/dependency/Dependency_Resolver.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
enum class Preparation {
|
||||
acquire_source,
|
||||
transform_vertices,
|
||||
upload_visual
|
||||
};
|
||||
|
||||
using Resolver =
|
||||
renderive::scene::dependency::Dependency_Resolver<Preparation>;
|
||||
|
||||
TEST(dependency_resolver_test,
|
||||
resolves_declared_dependencies_in_deterministic_order) {
|
||||
std::pmr::unsynchronized_pool_resource memory;
|
||||
Resolver resolver(memory);
|
||||
resolver.attach(Preparation::upload_visual);
|
||||
resolver.attach(Preparation::acquire_source);
|
||||
resolver.attach(Preparation::transform_vertices);
|
||||
resolver.add_parent(Preparation::transform_vertices,
|
||||
Preparation::acquire_source);
|
||||
resolver.add_parent(Preparation::upload_visual,
|
||||
Preparation::transform_vertices);
|
||||
|
||||
const auto resolution = resolver.resolve();
|
||||
EXPECT_EQ(resolution.order,
|
||||
(std::vector{Preparation::acquire_source,
|
||||
Preparation::transform_vertices,
|
||||
Preparation::upload_visual}));
|
||||
}
|
||||
|
||||
TEST(dependency_resolver_test,
|
||||
rejected_cycle_preserves_the_previous_authoritative_relationships) {
|
||||
std::pmr::unsynchronized_pool_resource memory;
|
||||
Resolver resolver(memory);
|
||||
resolver.attach(Preparation::acquire_source);
|
||||
resolver.attach(Preparation::transform_vertices);
|
||||
resolver.attach(Preparation::upload_visual);
|
||||
resolver.add_parent(Preparation::transform_vertices,
|
||||
Preparation::acquire_source);
|
||||
resolver.add_parent(Preparation::upload_visual,
|
||||
Preparation::transform_vertices);
|
||||
|
||||
EXPECT_THROW(
|
||||
resolver.add_parent(Preparation::acquire_source,
|
||||
Preparation::upload_visual),
|
||||
std::logic_error);
|
||||
|
||||
EXPECT_TRUE(resolver.parents(Preparation::acquire_source).empty());
|
||||
EXPECT_EQ(resolver.resolve().order,
|
||||
(std::vector{Preparation::acquire_source,
|
||||
Preparation::transform_vertices,
|
||||
Preparation::upload_visual}));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -155,8 +155,6 @@ TEST(scene3d_context_test, runtime_edit_explicitly_reparents_children_before_det
|
||||
attach.attach(grandparent);
|
||||
attach.attach(parent);
|
||||
attach.attach(child);
|
||||
attach.set_display_parent(parent, grandparent);
|
||||
attach.set_display_parent(child, parent);
|
||||
attach.set_dependency_parent(parent, grandparent);
|
||||
attach.set_dependency_parent(child, parent);
|
||||
});
|
||||
@@ -164,28 +162,19 @@ TEST(scene3d_context_test, runtime_edit_explicitly_reparents_children_before_det
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(child->render_count, 1);
|
||||
wait_renderable_edit(scene, [grandparent, parent, child](auto& editor) {
|
||||
editor.set_display_parent(child, grandparent);
|
||||
editor.set_dependency_parent(child, grandparent);
|
||||
editor.clear_display_parent(parent);
|
||||
editor.clear_dependency_parent(parent);
|
||||
editor.detach(parent);
|
||||
});
|
||||
const auto topology = scene.topology_snapshot();
|
||||
bool display_checked{};
|
||||
bool dependency_checked{};
|
||||
for (const auto& relation : topology.display) {
|
||||
if (relation.child.get() == child.get()) {
|
||||
EXPECT_EQ(relation.parent.get(), grandparent.get());
|
||||
display_checked = true;
|
||||
}
|
||||
}
|
||||
for (const auto& relation : topology.dependency) {
|
||||
if (relation.child.get() == child.get()) {
|
||||
EXPECT_EQ(relation.parent.get(), grandparent.get());
|
||||
dependency_checked = true;
|
||||
}
|
||||
}
|
||||
EXPECT_TRUE(display_checked);
|
||||
EXPECT_TRUE(topology.display.empty());
|
||||
EXPECT_TRUE(dependency_checked);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "renderive/inheritance/Registry.hpp"
|
||||
#include "renderive/scene/Inheritance.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
struct Lifecycle_Event {
|
||||
std::vector<std::string>& handled_by;
|
||||
};
|
||||
|
||||
struct Scene_Impl
|
||||
: renderive::scene_inheritance::Impl_Root<Scene_Impl,
|
||||
Lifecycle_Event> {
|
||||
struct Observer :
|
||||
renderive::scene_inheritance::Observer_Root<Scene_Impl> {
|
||||
static void handle(Scene_Impl&, const Lifecycle_Event& event) {
|
||||
event.handled_by.emplace_back("scene");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct Scene2D_Impl
|
||||
: renderive::scene_inheritance::Impl_Node<Scene2D_Impl, Scene_Impl> {
|
||||
struct Observer
|
||||
: renderive::scene_inheritance::Observer_Node<
|
||||
Scene2D_Impl, Scene_Impl::Observer> {
|
||||
static void handle(Scene2D_Impl&, const Lifecycle_Event& event) {
|
||||
event.handled_by.emplace_back("scene_2d");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct Plot_Scene_Impl
|
||||
: renderive::scene_inheritance::Impl_Node<Plot_Scene_Impl,
|
||||
Scene2D_Impl> {
|
||||
struct Observer
|
||||
: renderive::scene_inheritance::Observer_Node<
|
||||
Plot_Scene_Impl, Scene2D_Impl::Observer> {
|
||||
static void handle(Plot_Scene_Impl&, const Lifecycle_Event& event) {
|
||||
event.handled_by.emplace_back("plot_scene");
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
struct Scene_Editor
|
||||
: renderive::scene_inheritance::Editor_Root<Scene_Editor> {};
|
||||
struct Scene2D_Editor
|
||||
: renderive::scene_inheritance::Editor_Node<Scene2D_Editor,
|
||||
Scene_Editor> {};
|
||||
struct Plot_Scene_Editor
|
||||
: renderive::scene_inheritance::Editor_Node<Plot_Scene_Editor,
|
||||
Scene2D_Editor> {};
|
||||
|
||||
struct Scene_Builder
|
||||
: renderive::scene_inheritance::Builder_Root<Scene_Builder> {};
|
||||
struct Scene2D_Builder
|
||||
: renderive::scene_inheritance::Builder_Node<Scene2D_Builder,
|
||||
Scene_Builder> {};
|
||||
struct Plot_Scene_Builder
|
||||
: renderive::scene_inheritance::Builder_Node<Plot_Scene_Builder,
|
||||
Scene2D_Builder> {};
|
||||
|
||||
struct Scene_State
|
||||
: renderive::scene_inheritance::State_Root<Scene_State> {};
|
||||
struct Scene2D_State
|
||||
: renderive::scene_inheritance::State_Node<Scene2D_State,
|
||||
Scene_State> {};
|
||||
struct Plot_Scene_State
|
||||
: renderive::scene_inheritance::State_Node<Plot_Scene_State,
|
||||
Scene2D_State> {};
|
||||
|
||||
TEST(scene_inheritance_test,
|
||||
observer_dispatch_follows_the_scene_chain_parent_before_child) {
|
||||
Plot_Scene_Impl impl;
|
||||
std::vector<std::string> handled_by;
|
||||
impl.dispatch({handled_by});
|
||||
EXPECT_EQ(handled_by,
|
||||
(std::vector<std::string>{"scene", "scene_2d", "plot_scene"}));
|
||||
}
|
||||
|
||||
TEST(scene_inheritance_test,
|
||||
editor_chain_registers_each_semantic_layer_at_its_own_position) {
|
||||
using Registry = Plot_Scene_Editor::Inheritance_Registry;
|
||||
using Chain = renderive::scene_inheritance::Editor_Chain;
|
||||
static_assert(renderive::inheritance::Chain_Registration_Count_V<
|
||||
Registry, Chain> == 3);
|
||||
static_assert(std::same_as<
|
||||
renderive::inheritance::Type_At_Position_T<Registry, Chain,
|
||||
0>,
|
||||
Scene_Editor>);
|
||||
static_assert(std::same_as<
|
||||
renderive::inheritance::Type_At_Position_T<Registry, Chain,
|
||||
2>,
|
||||
Plot_Scene_Editor>);
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST(scene_inheritance_test,
|
||||
builder_and_state_chains_extend_without_sharing_registrations) {
|
||||
using Builder_Registry = Plot_Scene_Builder::Inheritance_Registry;
|
||||
using State_Registry = Plot_Scene_State::Inheritance_Registry;
|
||||
using Builder_Chain = renderive::scene_inheritance::Builder_Chain;
|
||||
using State_Chain = renderive::scene_inheritance::State_Chain;
|
||||
static_assert(renderive::inheritance::Chain_Registration_Count_V<
|
||||
Builder_Registry, Builder_Chain> == 3);
|
||||
static_assert(renderive::inheritance::Chain_Registration_Count_V<
|
||||
Builder_Registry, State_Chain> == 0);
|
||||
static_assert(renderive::inheritance::Chain_Registration_Count_V<
|
||||
State_Registry, State_Chain> == 3);
|
||||
static_assert(renderive::inheritance::Chain_Registration_Count_V<
|
||||
State_Registry, Builder_Chain> == 0);
|
||||
static_assert(std::same_as<
|
||||
renderive::inheritance::Type_At_Position_T<
|
||||
Builder_Registry, Builder_Chain, 2>,
|
||||
Plot_Scene_Builder>);
|
||||
static_assert(std::same_as<
|
||||
renderive::inheritance::Type_At_Position_T<
|
||||
State_Registry, State_Chain, 2>,
|
||||
Plot_Scene_State>);
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -16,12 +16,17 @@ struct Scene_State_Observer_Test_Time_Source {
|
||||
};
|
||||
struct Scene_State_Observer_Test_Data {
|
||||
std::vector<Scene_Base::Observation_Event> events;
|
||||
std::atomic<int> complete_snapshot_count{};
|
||||
};
|
||||
struct Scene_State_Observer_Test_Recorder {
|
||||
static constexpr bool enabled = true;
|
||||
std::shared_ptr<Scene_State_Observer_Test_Data> data{std::make_shared<Scene_State_Observer_Test_Data>()};
|
||||
void observe(const Scene_Base::Observation& observation) noexcept {
|
||||
data->events.push_back(observation.event);
|
||||
if (observation.topology && observation.frame_snapshot &&
|
||||
observation.render_plan)
|
||||
data->complete_snapshot_count.fetch_add(1,
|
||||
std::memory_order_relaxed);
|
||||
}
|
||||
};
|
||||
using Scene_State_Observer_Test_Scene_Observer = Observer_State<Scene_State_Observer_Test_Recorder, Scene_State_Observer_Test_Time_Source>;
|
||||
@@ -37,6 +42,7 @@ TEST(scene_state_observer_test, scene_uses_double_state_and_observes_lifecycle)
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(render.read(static_cast<const Scene::Scene_State_Strategy&>(scene)).value, 9);
|
||||
EXPECT_EQ(recorder.data->events, (std::vector<Scene_Base::Observation_Event>{Scene_Base::Observation_Event::render_submitted, Scene_Base::Observation_Event::render_started, Scene_Base::Observation_Event::render_completed}));
|
||||
EXPECT_EQ(recorder.data->complete_snapshot_count.load(), 3);
|
||||
}
|
||||
struct Scene_Reentrant_Observer_Data {
|
||||
std::function<void(Scene_Base::Observation_Event)> callback;
|
||||
|
||||
@@ -2,21 +2,21 @@
|
||||
#include <future>
|
||||
#include <utility>
|
||||
#include "renderive/scene/base/Scene_Base.hpp"
|
||||
template <class... Owners>
|
||||
void attach_initial(Scene_Base& scene, const Owners&... owners) {
|
||||
template <class Scene, class... Owners>
|
||||
void attach_initial(Scene& scene, const Owners&... owners) {
|
||||
auto builder = scene.attach_builder();
|
||||
(builder.attach(owners), ...);
|
||||
}
|
||||
template <class Edit>
|
||||
void with_attach_builder(Scene_Base& scene, Edit&& edit) {
|
||||
template <class Scene, class Edit>
|
||||
void with_attach_builder(Scene& scene, Edit&& edit) {
|
||||
auto builder = scene.attach_builder();
|
||||
edit(builder);
|
||||
}
|
||||
template <class Edit>
|
||||
void wait_renderable_edit(Scene_Base& scene, Edit&& edit) {
|
||||
template <class Scene, class Edit>
|
||||
void wait_renderable_edit(Scene& scene, Edit&& edit) {
|
||||
std::promise<void> completed;
|
||||
auto future = completed.get_future();
|
||||
scene.edit_renderables([edit = std::forward<Edit>(edit), &completed](Scene_Base::Renderable_Editor& editor) mutable {
|
||||
scene.edit_renderables([edit = std::forward<Edit>(edit), &completed](auto& editor) mutable {
|
||||
edit(editor);
|
||||
completed.set_value();
|
||||
});
|
||||
|
||||
@@ -473,7 +473,7 @@ TEST(scene_base_test, detached_topology_reference_fails_fast_after_callback) {
|
||||
});
|
||||
scene.render();
|
||||
},
|
||||
"dependency topology still references detached renderable");
|
||||
"dependency graph still references detached renderable");
|
||||
}
|
||||
TEST(scene_base_test, wait_for_render_propagates_background_render_failure) {
|
||||
Scene2D_Context<> scene;
|
||||
|
||||
Reference in New Issue
Block a user