216 lines
10 KiB
C++
216 lines
10 KiB
C++
#pragma once
|
|
#include <algorithm>
|
|
#include <concepts>
|
|
#include <cstddef>
|
|
#include <memory_resource>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <utility>
|
|
#include <vector>
|
|
#include "renderive/base/memory/Memory_Resource.hpp"
|
|
#include "renderive/base/observer/Observer.hpp"
|
|
#include "renderive/frame_control/Frame_Control.hpp"
|
|
#include "renderive/renderable/color/Concepts.hpp"
|
|
#include "renderive/state/Triple_State_Strategy.hpp"
|
|
#include "base/Scene_Base.hpp"
|
|
struct Scene2D_Frame_Data {};
|
|
struct Scene2D_State {
|
|
std::uint64_t revision{};
|
|
};
|
|
template <class Frame_Control, class... Args>
|
|
concept Scene2D_Frame_Control_Constructible = std::constructible_from<Frame_Control, Args...> || std::constructible_from<Frame_Control, std::pmr::memory_resource&, Args...>;
|
|
template <class Strategy = Low_Latency_Strategy<Scene2D_Frame_Data>, Color_Cache_Type Cache = Recording_Color_Cache, class State = Scene2D_State, class State_Observer = Observer_State<>, class Scene_Observer = Observer_State<>>
|
|
requires Frame_Control_Strategy_For<Strategy, Scene2D_Frame_Data> && State_Value<State>
|
|
class Scene2D_Context final : public Triple_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer> {
|
|
public:
|
|
using Scene_State_Strategy = Triple_State_Strategy<Scene_2D_Base, State, Atomic_Spin_Mutex, State_Observer>;
|
|
using Render_Task = Scene_Base::Render_Task;
|
|
using Renderable = Scene_Base::Renderable;
|
|
using Renderable_List = Scene_Base::Renderable_List;
|
|
using Frame_Control = Strategy;
|
|
using Frame = typename Frame_Control::Frame;
|
|
using Painter_Lease = typename Frame_Control::Painter_Lease;
|
|
using Render_Lease = typename Frame_Control::Render_Lease;
|
|
template <class... Args>
|
|
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
|
explicit Scene2D_Context(Args&&... args) : Scene2D_Context(*std::pmr::get_default_resource(), std::forward<Args>(args)...) {}
|
|
template <class... Args>
|
|
requires Scene2D_Frame_Control_Constructible<Frame_Control, Args&&...>
|
|
explicit Scene2D_Context(std::pmr::memory_resource& memory_resource, Args&&... args)
|
|
: Scene_State_Strategy(State{}, memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())), display_root_(nullptr, this->memory_resource()), dependency_root_(nullptr, this->memory_resource()), color_caches_(&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)
|
|
: Scene2D_Context(*std::pmr::get_default_resource(), std::move(state_observer), std::move(scene_observer), std::forward<Args>(args)...) {}
|
|
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_State_Strategy(State{}, std::move(state_observer), memory_resource), frame_control(make_frame_control(this->memory_resource(), std::forward<Args>(args)...)), final_color_cache_(make_cache(this->memory_resource())), display_root_(nullptr, this->memory_resource()), dependency_root_(nullptr, this->memory_resource()), scene_observer_(std::move(scene_observer.observer)), color_caches_(&this->memory_resource()) {}
|
|
~Scene2D_Context() override {
|
|
this->shutdown();
|
|
}
|
|
Frame_Control frame_control;
|
|
template <class Function>
|
|
void with_final_color_cache(Function&& function) {
|
|
if (this->is_render_worker_thread()) {
|
|
function(static_cast<const Cache&>(final_color_cache_));
|
|
return;
|
|
}
|
|
auto lock = this->lock_render_idle();
|
|
function(static_cast<const Cache&>(final_color_cache_));
|
|
}
|
|
protected:
|
|
Frame_Control_Strategy_Base& frame_control_strategy_impl() noexcept override {
|
|
return frame_control;
|
|
}
|
|
const Frame_Control_Strategy_Base& frame_control_strategy_impl() const noexcept override {
|
|
return frame_control;
|
|
}
|
|
Renderable_Base::Layer_Node* display_root_node() noexcept override {
|
|
return &display_root_;
|
|
}
|
|
Renderable_Base::Dependency_Node* dependency_root_node() noexcept override {
|
|
return &dependency_root_;
|
|
}
|
|
void on_renderable_attached(Renderable_Base& renderable) override {
|
|
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();
|
|
try {
|
|
if (attach_display) {
|
|
display_root_.append_child(display_node);
|
|
}
|
|
if (attach_dependency) {
|
|
dependency_root_.append_child(dependency_node);
|
|
}
|
|
color_caches_.try_emplace(&renderable, std::move(cache));
|
|
} catch (...) {
|
|
if (attach_dependency) {
|
|
dependency_node.detach();
|
|
}
|
|
if (attach_display) {
|
|
display_node.detach();
|
|
}
|
|
throw;
|
|
}
|
|
renderable.invalidate_cache();
|
|
}
|
|
void on_renderable_detached(Renderable_Base& renderable) override {
|
|
promote_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);
|
|
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);
|
|
}
|
|
Color_Cache* prepare_renderable_cache(Renderable_Base& renderable, bool clear) override {
|
|
Cache* cache = color_caches_.at(&renderable).get();
|
|
if (clear) {
|
|
cache->clear();
|
|
}
|
|
return cache;
|
|
}
|
|
void generate_final_color_cache(const Scene_Render_Context&, const Renderable_List& renderables) override {
|
|
final_color_cache_.clear();
|
|
for (const Renderable& renderable : renderables) {
|
|
final_color_cache_.composite(*color_caches_.at(renderable.get()));
|
|
}
|
|
}
|
|
std::uint64_t acquire_scene_state() override {
|
|
return this->Scene_State_Strategy::acquire_render_state();
|
|
}
|
|
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:
|
|
using Cache_Pointer = Memory_Resource_Unique_Ptr<Cache>;
|
|
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&&...>) {
|
|
return Frame_Control(memory_resource, std::forward<Args>(args)...);
|
|
} else {
|
|
return Frame_Control(std::forward<Args>(args)...);
|
|
}
|
|
}
|
|
static Cache make_cache(std::pmr::memory_resource& memory_resource) {
|
|
if constexpr (std::constructible_from<Cache, std::pmr::memory_resource&>) {
|
|
return Cache(memory_resource);
|
|
} else {
|
|
return Cache();
|
|
}
|
|
}
|
|
static Cache_Pointer make_cache_pointer(std::pmr::memory_resource& memory_resource) {
|
|
if constexpr (std::constructible_from<Cache, std::pmr::memory_resource&>) {
|
|
return make_memory_resource_unique<Cache>(memory_resource, memory_resource);
|
|
} else {
|
|
return make_memory_resource_unique<Cache>(memory_resource);
|
|
}
|
|
}
|
|
template <class Node>
|
|
void promote_children(Node& node) {
|
|
if (!node.parent()) {
|
|
return;
|
|
}
|
|
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));
|
|
}
|
|
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_;
|
|
Renderable_Base::Dependency_Node dependency_root_;
|
|
Scene_Observer scene_observer_;
|
|
std::pmr::unordered_map<Renderable_Base*, Cache_Pointer> color_caches_;
|
|
};
|