改造一些
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
#pragma once
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <memory_resource>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
template <class Owner, class Tag = void>
|
||||
class Directed_Acyclic_Node {
|
||||
public:
|
||||
explicit Directed_Acyclic_Node(
|
||||
Owner* owner = nullptr,
|
||||
std::pmr::memory_resource& memory_resource = *std::pmr::get_default_resource()) noexcept
|
||||
: owner_(owner), parents_(&memory_resource), children_(&memory_resource) {}
|
||||
Directed_Acyclic_Node(const Directed_Acyclic_Node&) = delete;
|
||||
Directed_Acyclic_Node& operator=(const Directed_Acyclic_Node&) = delete;
|
||||
Directed_Acyclic_Node(Directed_Acyclic_Node&&) = delete;
|
||||
Directed_Acyclic_Node& operator=(Directed_Acyclic_Node&&) = delete;
|
||||
~Directed_Acyclic_Node() {
|
||||
detach();
|
||||
while (!children_.empty()) {
|
||||
children_.front()->remove_parent(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void append_child(Directed_Acyclic_Node& child) {
|
||||
insert_child(children_.size(), child);
|
||||
}
|
||||
|
||||
void insert_child(std::size_t index, Directed_Acyclic_Node& child) {
|
||||
if (index > children_.size()) {
|
||||
throw std::out_of_range("directed acyclic node child index");
|
||||
}
|
||||
if (&child == this || child.is_ancestor_of(*this)) {
|
||||
throw std::invalid_argument("directed acyclic node cycle");
|
||||
}
|
||||
const auto current = std::find(children_.begin(), children_.end(), &child);
|
||||
if (current != children_.end()) {
|
||||
const auto current_index = static_cast<std::size_t>(current - children_.begin());
|
||||
if (current_index < index) {
|
||||
--index;
|
||||
}
|
||||
if (current_index == index) {
|
||||
return;
|
||||
}
|
||||
children_.erase(current);
|
||||
children_.insert(children_.begin() + static_cast<std::ptrdiff_t>(index), &child);
|
||||
return;
|
||||
}
|
||||
children_.insert(children_.begin() + static_cast<std::ptrdiff_t>(index), &child);
|
||||
child.parents_.push_back(this);
|
||||
}
|
||||
|
||||
void replace_parent(Directed_Acyclic_Node* parent) {
|
||||
detach();
|
||||
if (parent) {
|
||||
parent->append_child(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void remove_parent(Directed_Acyclic_Node& parent) noexcept {
|
||||
std::erase(parents_, &parent);
|
||||
std::erase(parent.children_, this);
|
||||
}
|
||||
|
||||
void detach() noexcept {
|
||||
while (!parents_.empty()) {
|
||||
remove_parent(*parents_.front());
|
||||
}
|
||||
}
|
||||
|
||||
bool has_parent(const Directed_Acyclic_Node& parent) const noexcept {
|
||||
return std::find(parents_.begin(), parents_.end(), &parent) != parents_.end();
|
||||
}
|
||||
|
||||
bool is_ancestor_of(const Directed_Acyclic_Node& node) const noexcept {
|
||||
for (Directed_Acyclic_Node* child : children_) {
|
||||
if (child == &node || child->is_ancestor_of(node)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Owner* owner() noexcept { return owner_; }
|
||||
const Owner* owner() const noexcept { return owner_; }
|
||||
Directed_Acyclic_Node* parent() noexcept { return parents_.empty() ? nullptr : parents_.front(); }
|
||||
const Directed_Acyclic_Node* parent() const noexcept { return parents_.empty() ? nullptr : parents_.front(); }
|
||||
std::size_t parent_count() const noexcept { return parents_.size(); }
|
||||
Directed_Acyclic_Node& parent(std::size_t index) { return *parents_.at(index); }
|
||||
const Directed_Acyclic_Node& parent(std::size_t index) const { return *parents_.at(index); }
|
||||
std::size_t child_count() const noexcept { return children_.size(); }
|
||||
bool children_empty() const noexcept { return children_.empty(); }
|
||||
Directed_Acyclic_Node& child(std::size_t index) { return *children_.at(index); }
|
||||
const Directed_Acyclic_Node& child(std::size_t index) const { return *children_.at(index); }
|
||||
|
||||
private:
|
||||
Owner* owner_{};
|
||||
std::pmr::vector<Directed_Acyclic_Node*> parents_;
|
||||
std::pmr::vector<Directed_Acyclic_Node*> children_;
|
||||
};
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include "renderive/base/memory/Memory_Resource.hpp"
|
||||
#include "renderive/base/node/Multiway_Node.hpp"
|
||||
#include "renderive/base/node/Directed_Acyclic_Node.hpp"
|
||||
#include "renderive/renderable/Renderable_Task_Graph.hpp"
|
||||
#include "renderive/scene/base/Scene_Lifetime.hpp"
|
||||
class Frame_Strategy_Real_Time_Data_Observer;
|
||||
@@ -27,8 +27,8 @@ private:
|
||||
std::shared_ptr<Scene_Memory_Domain> memory_domain_;
|
||||
std::shared_ptr<Real_Time_Data_State> real_time_data_state_;
|
||||
public:
|
||||
using Layer_Node = Multiway_Node<Renderable_Base, Renderable_Layer_Node_Tag>;
|
||||
using Dependency_Node = Multiway_Node<Renderable_Base, Renderable_Dependency_Node_Tag>;
|
||||
using Layer_Node = Directed_Acyclic_Node<Renderable_Base, Renderable_Layer_Node_Tag>;
|
||||
using Dependency_Node = Directed_Acyclic_Node<Renderable_Base, Renderable_Dependency_Node_Tag>;
|
||||
explicit Renderable_Base(Scene_Base& scene, Renderable_Configuration configuration = {});
|
||||
virtual ~Renderable_Base();
|
||||
virtual void render(const Scene_Render_Context& context);
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
#include <cstddef>
|
||||
#include <memory_resource>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "renderive/base/memory/Memory_Resource.hpp"
|
||||
@@ -76,8 +75,8 @@ protected:
|
||||
Cache_Pointer cache = make_cache_pointer(this->memory_resource());
|
||||
auto& display_node = Scene_Base::layer_node(renderable);
|
||||
auto& dependency_node = Scene_Base::dependency_node(renderable);
|
||||
const bool attach_display = !display_node.parent();
|
||||
const bool attach_dependency = !dependency_node.parent();
|
||||
const bool attach_display = display_node.parent_count() == 0;
|
||||
const bool attach_dependency = dependency_node.parent_count() == 0;
|
||||
try {
|
||||
if (attach_display) {
|
||||
display_root_.append_child(display_node);
|
||||
@@ -98,17 +97,17 @@ protected:
|
||||
renderable.invalidate_cache();
|
||||
}
|
||||
void on_renderable_detached(Renderable_Base& renderable) override {
|
||||
promote_children(Scene_Base::layer_node(renderable));
|
||||
promote_graph_children(Scene_Base::layer_node(renderable), display_root_);
|
||||
auto& dependency = Scene_Base::dependency_node(renderable);
|
||||
for (std::size_t index = 0; index < dependency.child_count(); ++index) {
|
||||
dependency.child(index).owner()->invalidate_cache();
|
||||
}
|
||||
promote_children(dependency);
|
||||
promote_graph_children(dependency, dependency_root_);
|
||||
color_caches_.erase(&renderable);
|
||||
}
|
||||
void prepare_render_task(Render_Task& task, const Renderable_List& renderables) override {
|
||||
task.render_order = tree_order(dependency_root_, renderables);
|
||||
task.display_order = tree_order(display_root_, renderables);
|
||||
task.render_order = Scene_Base::dependency_order(renderables);
|
||||
task.display_order = Scene_Base::display_order(renderables);
|
||||
}
|
||||
Color_Cache* prepare_renderable_cache(Renderable_Base& renderable, bool clear) override {
|
||||
Cache* cache = color_caches_.at(&renderable).get();
|
||||
@@ -157,55 +156,23 @@ private:
|
||||
}
|
||||
}
|
||||
template <class Node>
|
||||
void promote_children(Node& node) {
|
||||
if (!node.parent()) {
|
||||
return;
|
||||
void promote_graph_children(Node& node, Node& root) {
|
||||
std::pmr::vector<Node*> parents(&this->memory_resource());
|
||||
parents.reserve(node.parent_count());
|
||||
for (std::size_t index = 0; index < node.parent_count(); ++index) {
|
||||
parents.push_back(&node.parent(index));
|
||||
}
|
||||
Node* parent = node.parent();
|
||||
std::size_t index{};
|
||||
while (&parent->child(index) != &node) {
|
||||
++index;
|
||||
}
|
||||
std::pmr::vector<Node*> children(&this->memory_resource());
|
||||
children.reserve(node.child_count());
|
||||
for (std::size_t child_index = 0; child_index < node.child_count(); ++child_index) {
|
||||
children.push_back(&node.child(child_index));
|
||||
while (!node.children_empty()) {
|
||||
auto& child = node.child(0);
|
||||
child.remove_parent(node);
|
||||
for (auto* parent : parents) {
|
||||
parent->append_child(child);
|
||||
}
|
||||
if (child.parent_count() == 0) {
|
||||
root.append_child(child);
|
||||
}
|
||||
}
|
||||
node.detach();
|
||||
for (Node* child : children) {
|
||||
parent->insert_child(index++, *child);
|
||||
}
|
||||
}
|
||||
template <class Node, class Attached, class Visited>
|
||||
static void append_tree_order(Node& node, const Attached& attached, Visited& visited, Renderable_List& order) {
|
||||
if (node.owner()) {
|
||||
auto iterator = attached.find(node.owner());
|
||||
if (iterator != attached.end() && visited.insert(node.owner()).second) {
|
||||
order.push_back(iterator->second);
|
||||
}
|
||||
}
|
||||
for (std::size_t index = 0; index < node.child_count(); ++index) {
|
||||
append_tree_order(node.child(index), attached, visited, order);
|
||||
}
|
||||
}
|
||||
template <class Node>
|
||||
Renderable_List tree_order(Node& root, const Renderable_List& renderables) {
|
||||
std::pmr::monotonic_buffer_resource scratch_resource(&this->memory_resource());
|
||||
std::pmr::unordered_map<Renderable_Base*, Renderable> attached(&scratch_resource);
|
||||
attached.reserve(renderables.size());
|
||||
for (const Renderable& renderable : renderables) {
|
||||
attached.emplace(renderable.get(), renderable);
|
||||
}
|
||||
std::pmr::unordered_set<Renderable_Base*> visited(&scratch_resource);
|
||||
Renderable_List order(&this->memory_resource());
|
||||
order.reserve(renderables.size());
|
||||
append_tree_order(root, attached, visited, order);
|
||||
for (const Renderable& renderable : renderables) {
|
||||
if (visited.insert(renderable.get()).second) {
|
||||
order.push_back(renderable);
|
||||
}
|
||||
}
|
||||
return order;
|
||||
}
|
||||
Cache final_color_cache_;
|
||||
Renderable_Base::Layer_Node display_root_;
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include <concepts>
|
||||
#include <memory_resource>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "renderive/base/observer/Observer.hpp"
|
||||
#include "renderive/frame_control/Frame_Control.hpp"
|
||||
#include "renderive/state/Triple_State_Strategy.hpp"
|
||||
@@ -48,12 +49,12 @@ protected:
|
||||
return frame_control;
|
||||
}
|
||||
void on_renderable_detached(Renderable_Base& renderable) override {
|
||||
promote_children(Scene_Base::layer_node(renderable));
|
||||
promote_graph_children(Scene_Base::layer_node(renderable));
|
||||
auto& dependency = Scene_Base::dependency_node(renderable);
|
||||
for (std::size_t index = 0; index < dependency.child_count(); ++index) {
|
||||
dependency.child(index).owner()->invalidate_cache();
|
||||
}
|
||||
promote_children(dependency);
|
||||
promote_graph_children(dependency);
|
||||
}
|
||||
std::uint64_t acquire_scene_state() override {
|
||||
return this->Scene_State_Strategy::acquire_render_state();
|
||||
@@ -66,22 +67,20 @@ protected:
|
||||
}
|
||||
private:
|
||||
template <class Node>
|
||||
static void promote_children(Node& node) {
|
||||
Node* parent = node.parent();
|
||||
if (!parent) {
|
||||
while (!node.children_empty()) {
|
||||
node.child(0).detach();
|
||||
}
|
||||
return;
|
||||
static void promote_graph_children(Node& node) {
|
||||
std::vector<Node*> parents;
|
||||
parents.reserve(node.parent_count());
|
||||
for (std::size_t index = 0; index < node.parent_count(); ++index) {
|
||||
parents.push_back(&node.parent(index));
|
||||
}
|
||||
std::size_t index{};
|
||||
while (&parent->child(index) != &node) {
|
||||
++index;
|
||||
while (!node.children_empty()) {
|
||||
auto& child = node.child(0);
|
||||
child.remove_parent(node);
|
||||
for (auto* parent : parents) {
|
||||
parent->append_child(child);
|
||||
}
|
||||
}
|
||||
node.detach();
|
||||
while (!node.children_empty()) {
|
||||
parent->insert_child(index++, node.child(0));
|
||||
}
|
||||
}
|
||||
template <class... Args>
|
||||
static Frame_Control make_frame_control(std::pmr::memory_resource& memory_resource, Args&&... args) {
|
||||
|
||||
@@ -5,9 +5,62 @@
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <taskflow/taskflow.hpp>
|
||||
#include "renderive/renderable/color/Color_Cache.hpp"
|
||||
static_assert(TF_VERSION == 400100, "Renderive requires Taskflow 4.1.0");
|
||||
namespace {
|
||||
template <class Node_Accessor>
|
||||
Scene_Base::Renderable_List topological_order(
|
||||
const Scene_Base::Renderable_List& renderables,
|
||||
std::pmr::memory_resource& memory_resource,
|
||||
Node_Accessor node_of,
|
||||
const char* graph_name) {
|
||||
std::pmr::monotonic_buffer_resource scratch_resource(&memory_resource);
|
||||
std::pmr::unordered_map<Renderable_Base*, Scene_Base::Renderable> attached(&scratch_resource);
|
||||
std::pmr::unordered_map<Renderable_Base*, std::size_t> indegree(&scratch_resource);
|
||||
attached.reserve(renderables.size());
|
||||
indegree.reserve(renderables.size());
|
||||
for (const auto& renderable : renderables) {
|
||||
attached.emplace(renderable.get(), renderable);
|
||||
indegree.emplace(renderable.get(), 0U);
|
||||
}
|
||||
for (const auto& renderable : renderables) {
|
||||
auto& node = node_of(*renderable);
|
||||
for (std::size_t index = 0; index < node.parent_count(); ++index) {
|
||||
auto* parent = node.parent(index).owner();
|
||||
if (parent && attached.contains(parent)) {
|
||||
++indegree.at(renderable.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
std::pmr::vector<Renderable_Base*> ready(&scratch_resource);
|
||||
ready.reserve(renderables.size());
|
||||
for (const auto& renderable : renderables) {
|
||||
if (indegree.at(renderable.get()) == 0) {
|
||||
ready.push_back(renderable.get());
|
||||
}
|
||||
}
|
||||
Scene_Base::Renderable_List order(&memory_resource);
|
||||
order.reserve(renderables.size());
|
||||
for (std::size_t ready_index = 0; ready_index < ready.size(); ++ready_index) {
|
||||
Renderable_Base* renderable = ready[ready_index];
|
||||
order.push_back(attached.at(renderable));
|
||||
auto& node = node_of(*renderable);
|
||||
for (std::size_t child_index = 0; child_index < node.child_count(); ++child_index) {
|
||||
Renderable_Base* child = node.child(child_index).owner();
|
||||
auto iterator = indegree.find(child);
|
||||
if (iterator != indegree.end() && --iterator->second == 0) {
|
||||
ready.push_back(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (order.size() != renderables.size()) {
|
||||
throw std::logic_error(std::string(graph_name) + " graph contains a cycle");
|
||||
}
|
||||
return order;
|
||||
}
|
||||
}
|
||||
class Scene_Base::Execution_Context {
|
||||
public:
|
||||
static tf::Executor& executor() {
|
||||
@@ -148,15 +201,32 @@ void Scene_Base::set_display_parent(Renderable_Base& renderable, Renderable_Base
|
||||
}
|
||||
auto& node = layer_node(renderable);
|
||||
auto* target = parent ? &layer_node(*parent) : display_root_node();
|
||||
if (node.parent() == target) {
|
||||
if (node.parent_count() == 1 && node.parent() == target) {
|
||||
return;
|
||||
}
|
||||
if (target) {
|
||||
target->append_child(node);
|
||||
node.replace_parent(target);
|
||||
} else {
|
||||
node.detach();
|
||||
}
|
||||
}
|
||||
void Scene_Base::add_display_parent(Renderable_Base& renderable, Renderable_Base& parent) {
|
||||
auto task_lock = lock_render_idle();
|
||||
validate_renderable_scene(renderable);
|
||||
validate_renderable_scene(parent);
|
||||
std::lock_guard<std::mutex> lock(renderable_mutex_);
|
||||
validate_renderable_attached_locked(renderable);
|
||||
validate_renderable_attached_locked(parent);
|
||||
auto& node = layer_node(renderable);
|
||||
auto& parent_node = layer_node(parent);
|
||||
if (node.has_parent(parent_node)) {
|
||||
return;
|
||||
}
|
||||
if (auto* root = display_root_node(); root && node.has_parent(*root)) {
|
||||
node.remove_parent(*root);
|
||||
}
|
||||
parent_node.append_child(node);
|
||||
}
|
||||
void Scene_Base::set_dependency_parent(Renderable_Base& renderable, Renderable_Base* parent) {
|
||||
auto task_lock = lock_render_idle();
|
||||
validate_renderable_scene(renderable);
|
||||
@@ -170,16 +240,34 @@ void Scene_Base::set_dependency_parent(Renderable_Base& renderable, Renderable_B
|
||||
}
|
||||
auto& node = dependency_node(renderable);
|
||||
auto* target = parent ? &dependency_node(*parent) : dependency_root_node();
|
||||
if (node.parent() == target) {
|
||||
if (node.parent_count() == 1 && node.parent() == target) {
|
||||
return;
|
||||
}
|
||||
if (target) {
|
||||
target->append_child(node);
|
||||
node.replace_parent(target);
|
||||
} else {
|
||||
node.detach();
|
||||
}
|
||||
renderable.invalidate_cache();
|
||||
}
|
||||
void Scene_Base::add_dependency_parent(Renderable_Base& renderable, Renderable_Base& parent) {
|
||||
auto task_lock = lock_render_idle();
|
||||
validate_renderable_scene(renderable);
|
||||
validate_renderable_scene(parent);
|
||||
std::lock_guard<std::mutex> lock(renderable_mutex_);
|
||||
validate_renderable_attached_locked(renderable);
|
||||
validate_renderable_attached_locked(parent);
|
||||
auto& node = dependency_node(renderable);
|
||||
auto& parent_node = dependency_node(parent);
|
||||
if (node.has_parent(parent_node)) {
|
||||
return;
|
||||
}
|
||||
if (auto* root = dependency_root_node(); root && node.has_parent(*root)) {
|
||||
node.remove_parent(*root);
|
||||
}
|
||||
parent_node.append_child(node);
|
||||
renderable.invalidate_cache();
|
||||
}
|
||||
void Scene_Base::set_renderable_configuration(Renderable_Base& renderable, Renderable_Configuration configuration) {
|
||||
auto task_lock = lock_render_idle();
|
||||
validate_renderable_scene(renderable);
|
||||
@@ -204,15 +292,30 @@ Scene_Base::Topology_Snapshot Scene_Base::topology_snapshot() const {
|
||||
snapshot.renderables.push_back(std::move(value));
|
||||
}
|
||||
for (const Renderable& renderable : *cache_renderables_) {
|
||||
const auto* display_parent_node = renderable->layer_node_.parent();
|
||||
const auto* dependency_parent_node = renderable->dependency_node_.parent();
|
||||
const auto* display_parent = display_parent_node ? display_parent_node->owner() : nullptr;
|
||||
const auto* dependency_parent = dependency_parent_node ? dependency_parent_node->owner() : nullptr;
|
||||
snapshot.display.push_back({renderables.at(renderable.get()), display_parent ? renderables.at(display_parent) : Const_Renderable{}});
|
||||
snapshot.dependency.push_back({renderables.at(renderable.get()), dependency_parent ? renderables.at(dependency_parent) : Const_Renderable{}});
|
||||
if (renderable->layer_node_.parent_count() == 0) {
|
||||
snapshot.display.push_back({renderables.at(renderable.get()), Const_Renderable{}});
|
||||
} else {
|
||||
for (std::size_t parent_index = 0; parent_index < renderable->layer_node_.parent_count(); ++parent_index) {
|
||||
const auto* display_parent = renderable->layer_node_.parent(parent_index).owner();
|
||||
snapshot.display.push_back({renderables.at(renderable.get()), display_parent ? renderables.at(display_parent) : Const_Renderable{}});
|
||||
}
|
||||
}
|
||||
if (renderable->dependency_node_.parent_count() == 0) {
|
||||
snapshot.dependency.push_back({renderables.at(renderable.get()), Const_Renderable{}});
|
||||
continue;
|
||||
}
|
||||
for (std::size_t parent_index = 0; parent_index < renderable->dependency_node_.parent_count(); ++parent_index) {
|
||||
const auto* dependency_parent = renderable->dependency_node_.parent(parent_index).owner();
|
||||
snapshot.dependency.push_back({renderables.at(renderable.get()), dependency_parent ? renderables.at(dependency_parent) : Const_Renderable{}});
|
||||
}
|
||||
}
|
||||
return snapshot;
|
||||
}
|
||||
std::vector<Scene_Base::Const_Renderable> Scene_Base::paint_order_snapshot() const {
|
||||
std::lock_guard<std::mutex> lock(renderable_mutex_);
|
||||
const auto ordered = display_order(*cache_renderables_);
|
||||
return {ordered.begin(), ordered.end()};
|
||||
}
|
||||
std::pmr::memory_resource& Scene_Base::memory_resource() const noexcept {
|
||||
return memory_domain_->resource();
|
||||
}
|
||||
@@ -237,6 +340,16 @@ Renderable_Base::Layer_Node& Scene_Base::layer_node(Renderable_Base& renderable)
|
||||
Renderable_Base::Dependency_Node& Scene_Base::dependency_node(Renderable_Base& renderable) noexcept {
|
||||
return renderable.dependency_node_;
|
||||
}
|
||||
Scene_Base::Renderable_List Scene_Base::display_order(const Renderable_List& renderables) const {
|
||||
return topological_order(renderables, memory_resource(), [](Renderable_Base& renderable) -> auto& {
|
||||
return renderable.layer_node_;
|
||||
}, "display");
|
||||
}
|
||||
Scene_Base::Renderable_List Scene_Base::dependency_order(const Renderable_List& renderables) const {
|
||||
return topological_order(renderables, memory_resource(), [](Renderable_Base& renderable) -> auto& {
|
||||
return renderable.dependency_node_;
|
||||
}, "dependency");
|
||||
}
|
||||
Renderable_Base::Layer_Node* Scene_Base::display_root_node() noexcept {
|
||||
return nullptr;
|
||||
}
|
||||
@@ -401,10 +514,12 @@ void Scene_Base::execute_taskflow(Render_Task& task) {
|
||||
}
|
||||
for (std::size_t index = 0; index < task.render_order.size(); ++index) {
|
||||
auto& node = dependency_node(*task.render_order[index]);
|
||||
Renderable_Base* parent = node.parent() ? node.parent()->owner() : nullptr;
|
||||
auto parent_iterator = indices.find(parent);
|
||||
if (parent_iterator != indices.end()) {
|
||||
modules[parent_iterator->second].module_task.precede(modules[index].module_task);
|
||||
for (std::size_t parent_index = 0; parent_index < node.parent_count(); ++parent_index) {
|
||||
Renderable_Base* parent = node.parent(parent_index).owner();
|
||||
auto parent_iterator = indices.find(parent);
|
||||
if (parent_iterator != indices.end()) {
|
||||
modules[parent_iterator->second].module_task.precede(modules[index].module_task);
|
||||
}
|
||||
}
|
||||
}
|
||||
const Scene_Render_Context final_context{this, task.frame_control_state, task.scene_state_revision, task.render_sequence, nullptr, nullptr};
|
||||
|
||||
@@ -57,10 +57,13 @@ public:
|
||||
void attach_renderable(Renderable renderable);
|
||||
void detach_renderable(Renderable_Base& renderable);
|
||||
void set_display_parent(Renderable_Base& renderable, Renderable_Base* parent);
|
||||
void add_display_parent(Renderable_Base& renderable, Renderable_Base& parent);
|
||||
void set_dependency_parent(Renderable_Base& renderable, Renderable_Base* parent);
|
||||
void add_dependency_parent(Renderable_Base& renderable, Renderable_Base& parent);
|
||||
void set_renderable_configuration(Renderable_Base& renderable, Renderable_Configuration configuration);
|
||||
std::size_t renderable_count() const;
|
||||
Topology_Snapshot topology_snapshot() const;
|
||||
std::vector<Const_Renderable> paint_order_snapshot() const;
|
||||
std::pmr::memory_resource& memory_resource() const noexcept;
|
||||
std::pmr::memory_resource& upstream_memory_resource() const noexcept;
|
||||
Frame_Control_Strategy_Base& frame_control_strategy();
|
||||
@@ -82,6 +85,8 @@ protected:
|
||||
};
|
||||
static Renderable_Base::Layer_Node& layer_node(Renderable_Base& renderable) noexcept;
|
||||
static Renderable_Base::Dependency_Node& dependency_node(Renderable_Base& renderable) noexcept;
|
||||
Renderable_List display_order(const Renderable_List& renderables) const;
|
||||
Renderable_List dependency_order(const Renderable_List& renderables) const;
|
||||
virtual Frame_Control_Strategy_Base& frame_control_strategy_impl();
|
||||
virtual const Frame_Control_Strategy_Base& frame_control_strategy_impl() const;
|
||||
virtual Renderable_Base::Layer_Node* display_root_node() noexcept;
|
||||
|
||||
@@ -118,6 +118,35 @@ TEST(scene2d_context_test, dependency_reparent_invalidates_cached_child) {
|
||||
EXPECT_EQ(second_parent->render_count, 1);
|
||||
EXPECT_EQ(child->render_count, 2);
|
||||
}
|
||||
TEST(scene2d_context_test, multiple_dependency_parents_invalidate_cached_child) {
|
||||
Scene2D_Context<> scene;
|
||||
auto first_parent = std::make_shared<Scene2D_Cached_Renderable>(scene, 1);
|
||||
auto second_parent = std::make_shared<Scene2D_Cached_Renderable>(scene, 2);
|
||||
auto child = std::make_shared<Scene2D_Cached_Renderable>(scene, 3);
|
||||
scene.attach_renderable(first_parent);
|
||||
scene.attach_renderable(second_parent);
|
||||
scene.attach_renderable(child);
|
||||
scene.set_dependency_parent(*child, first_parent.get());
|
||||
scene.add_dependency_parent(*child, *second_parent);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(child->render_count, 1);
|
||||
first_parent->invalidate_cache();
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(child->render_count, 2);
|
||||
second_parent->invalidate_cache();
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(child->render_count, 3);
|
||||
int dependency_edges{};
|
||||
for (const auto& relation : scene.topology_snapshot().dependency) {
|
||||
if (relation.child.get() == child.get() && relation.parent) {
|
||||
++dependency_edges;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(dependency_edges, 2);
|
||||
}
|
||||
TEST(scene2d_context_test, final_color_cache_callback_can_reenter_scene_control_api) {
|
||||
Scene2D_Context<> scene;
|
||||
auto renderable = std::make_shared<Scene2D_Cached_Renderable>(scene, 9);
|
||||
|
||||
@@ -37,3 +37,26 @@ TEST(scene2d_render_order_test, separates_dependency_order_from_display_order) {
|
||||
EXPECT_EQ(final_values.at(0), 2);
|
||||
EXPECT_EQ(final_values.at(1), 1);
|
||||
}
|
||||
TEST(scene2d_render_order_test, shared_overlay_waits_for_every_display_parent) {
|
||||
Scene2D_Context<> scene;
|
||||
Scene2D_Render_Order_State state;
|
||||
auto first_plot = std::make_shared<Scene2D_Render_Order_Renderable>(scene, state, 2);
|
||||
auto axis = std::make_shared<Scene2D_Render_Order_Renderable>(scene, state, 1);
|
||||
auto second_plot = std::make_shared<Scene2D_Render_Order_Renderable>(scene, state, 3);
|
||||
scene.attach_renderable(first_plot);
|
||||
scene.attach_renderable(axis);
|
||||
scene.attach_renderable(second_plot);
|
||||
scene.add_dependency_parent(*first_plot, *axis);
|
||||
scene.add_dependency_parent(*second_plot, *axis);
|
||||
scene.add_display_parent(*axis, *first_plot);
|
||||
scene.add_display_parent(*axis, *second_plot);
|
||||
scene.render();
|
||||
scene.wait_for_render();
|
||||
EXPECT_EQ(state.render_order.front(), 1);
|
||||
std::vector<std::uint64_t> final_values;
|
||||
scene.with_final_color_cache([&final_values](const Recording_Color_Cache& cache) {
|
||||
final_values.assign(cache.values.begin(), cache.values.end());
|
||||
});
|
||||
ASSERT_EQ(final_values.size(), 3);
|
||||
EXPECT_EQ(final_values.back(), 1);
|
||||
}
|
||||
|
||||
@@ -164,6 +164,18 @@ TEST(scene_base_test, setting_same_topology_parent_is_a_noop) {
|
||||
}
|
||||
EXPECT_TRUE(found);
|
||||
}
|
||||
TEST(scene_base_test, display_graph_rejects_cycles) {
|
||||
Scene2D_Context<> scene;
|
||||
auto first = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
auto second = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
auto third = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
scene.attach_renderable(first);
|
||||
scene.attach_renderable(second);
|
||||
scene.attach_renderable(third);
|
||||
scene.set_display_parent(*second, first.get());
|
||||
scene.add_display_parent(*third, *second);
|
||||
EXPECT_THROW(scene.add_display_parent(*first, *third), std::invalid_argument);
|
||||
}
|
||||
TEST(scene_base_test, topology_snapshot_retains_renderable_lifetime) {
|
||||
Scene2D_Context<> scene;
|
||||
auto renderable = std::make_shared<Scene_Base_Test_Renderable>(scene);
|
||||
|
||||
Reference in New Issue
Block a user