102 lines
5.1 KiB
C++
102 lines
5.1 KiB
C++
#include "Render_Scene_2D.hpp"
|
|
#include "observation/Tracy.hpp"
|
|
#include <optional>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <utility>
|
|
namespace aethera::render_2d {
|
|
proxy<scene::Task_Graph> scene::make_task_graph(std::string name) {
|
|
return task_flow::make_task_graph(std::move(name));
|
|
}
|
|
|
|
Render_Scene_2D::Render_Scene_2D() = default;
|
|
Render_Scene_2D::~Render_Scene_2D() noexcept = default;
|
|
|
|
Render_Scene_2D::Private::Build_Result Render_Scene_2D::Private::build_frame(Blend2D_Cache& target, Size viewport, Color background) {
|
|
auto& renderables = relation<aethera::scene::Renderable>();
|
|
renderables.for_each_topological([&](Dag_Node_Id, proxy<scene::Renderable>& renderable) {
|
|
renderable->render(target);
|
|
});
|
|
|
|
auto next_graph = scene::make_task_graph("render_2d.scene");
|
|
auto setup = next_graph->add("scene.background", [&target, viewport, background] {
|
|
target.ensure_size(viewport);
|
|
detail::Painter painter(target, viewport);
|
|
painter.clear();
|
|
painter.rect({0.0, 0.0, static_cast<double>(viewport.width), static_cast<double>(viewport.height)}, Pen{Color::transparent(), 0.0, Line_Style::none}, Brush{background, Brush_Style::solid});
|
|
});
|
|
if (!setup) return std::unexpected(Render_Scene_2D_Error{setup.error()});
|
|
|
|
std::unordered_map<std::uint64_t, proxy<task_flow::Task_Node>> nodes;
|
|
std::optional<Render_Scene_2D_Error> error;
|
|
renderables.for_each_topological([&](Dag_Node_Id id, proxy<scene::Renderable>& renderable) {
|
|
if (error) return;
|
|
auto component = renderable->taskflow();
|
|
auto composed = next_graph->compose("renderable." + std::to_string(id.value), component);
|
|
if (!composed) {
|
|
error = composed.error();
|
|
return;
|
|
}
|
|
const auto after_setup = next_graph->precede(*setup, *composed);
|
|
if (after_setup != Precede_Task_Graph_Result::preceded) {
|
|
error = after_setup;
|
|
return;
|
|
}
|
|
nodes.emplace(id.value, std::move(*composed));
|
|
});
|
|
if (error) return std::unexpected(*error);
|
|
renderables.for_each_dependency([&](Dag_Node_Id target_id, Dag_Node_Id source_id) {
|
|
if (error) return;
|
|
const auto dependency = next_graph->precede(nodes.at(source_id.value), nodes.at(target_id.value));
|
|
if (dependency != Precede_Task_Graph_Result::preceded) error = dependency;
|
|
});
|
|
if (error) return std::unexpected(*error);
|
|
taskflow() = std::move(next_graph);
|
|
dispatch_collected_events();
|
|
return {};
|
|
}
|
|
|
|
void Render_Scene_2D::Private::dispatch_events(Event_Batch& events) {
|
|
auto& renderables = relation<aethera::scene::Renderable>();
|
|
aethera::detail::dispatch_scene_events<scene::Renderable>(events, renderables);
|
|
}
|
|
|
|
Render_Scene_2D_Result Render_Scene_2D::Private::render(Frame_2D* frame, Completion completion) {
|
|
if (!frame) return std::unexpected(Render_Scene_2D_Error{Render_Scene_2D_State_Result::frame_missing});
|
|
if (!completion) return std::unexpected(Render_Scene_2D_Error{Render_Scene_2D_State_Result::completion_missing});
|
|
AETHERA_TRACE_FRAME("Render2D");
|
|
auto properties = concurrent<Prop_Tag>();
|
|
properties.internal.advance();
|
|
commit<aethera::scene::Renderable>();
|
|
auto& renderables = relation<aethera::scene::Renderable>();
|
|
renderables.for_each_topological([](Dag_Node_Id, proxy<scene::Renderable>& renderable) {
|
|
renderable->advance();
|
|
});
|
|
const Prop& current = *properties.internal.use();
|
|
if (!current.view_active) return std::unexpected(Render_Scene_2D_Error{Render_Scene_2D_State_Result::view_inactive});
|
|
if (current.viewport.empty()) return std::unexpected(Render_Scene_2D_Error{Render_Scene_2D_State_Result::empty_viewport});
|
|
auto& target = detail::Frame_2D_Access::render_target(frame);
|
|
const auto built = build_frame(target, current.viewport, current.background);
|
|
if (!built) return built;
|
|
auto completed = [frame, completion = std::move(completion)](std::exception_ptr failure) mutable {
|
|
AETHERA_TRACE_ZONE_TAGS("Render2D.Completion", Taskflow_Trace_Context{frame->identity().sequence, frame->identity().generation});
|
|
completion(frame, std::move(failure));
|
|
};
|
|
const auto submitted = frame->taskflow_trace_requested() ? taskflow()->run_observed(std::move(completed), detail::Frame_2D_Access::taskflow_observation(frame) AETHERA_TRACE_TASKFLOW_CONTEXT_ARG(frame->identity().sequence, frame->identity().generation)) : taskflow()->run(std::move(completed) AETHERA_TRACE_TASKFLOW_CONTEXT_ARG(frame->identity().sequence, frame->identity().generation));
|
|
if (submitted != Run_Taskflow_Result::submitted) return std::unexpected(Render_Scene_2D_Error{submitted});
|
|
return {};
|
|
}
|
|
|
|
Render_Scene_2D_Result Render_Scene_2D::render(Frame_2D* frame, Completion completion) {
|
|
return aethera::detail::model_private(*this).render(frame, std::move(completion));
|
|
}
|
|
|
|
void Render_Scene_2D::activate_view() {
|
|
set<Prop_Tag>(&Prop::view_active, true);
|
|
}
|
|
|
|
void Render_Scene_2D::deactivate_view() {
|
|
set<Prop_Tag>(&Prop::view_active, false);
|
|
}
|
|
} // namespace aethera::render_2d
|