Files
Aethera/render_3D/render_3D/scene/Render_Scene_3D_Model.ipp
T
2026-09-05 19:49:11 +08:00

394 lines
29 KiB
C++

#pragma once
#include "../detail/Exception.hpp"
#include "../detail/Gpu_Completion_Service.hpp"
#include "Error_handling_specification/Failure_Policy.hpp"
#include <algorithm>
#include <array>
#include <chrono>
#include <cmath>
#include <cstdio>
#include <limits>
#include <optional>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
namespace aethera::render_3d {
namespace detail {
struct Scene_Paint_Context final {
Scene_3D_Parameters parameters{}; /* 当前 CPU Prepare 唯一读取的 Scene 参数。 */
Prepared_Visual_Batch visuals{}; /* 各 Visual 节点只写自己的预分配位置。 */
};
inline float wheel_step(double pixel, double angle) {
return angle != 0.0 ? static_cast<float>(angle / 120.0) : static_cast<float>(pixel / 100.0);
}
inline Mouse_Button held_button(Mouse_Button_Mask buttons) {
if ((buttons & 1U) != 0) return Mouse_Button::left;
if ((buttons & 2U) != 0) return Mouse_Button::right;
if ((buttons & 4U) != 0) return Mouse_Button::middle;
return Mouse_Button::none;
}
inline void publish_visual(void* raw_context, std::size_t slot, const Prepared_Visual& prepared) {
if (!raw_context) throw std::logic_error("3D Visual lost its Scene paint context");
auto& context = *static_cast<Scene_Paint_Context*>(raw_context);
if (slot >= context.visuals.size()) throw std::logic_error("3D Visual published outside its Scene slot");
context.visuals[slot].visual = prepared;
}
inline void publish_datoviz_observation(Frame_3D* frame, Datoviz_Frame_Observation observation) {
if (!frame) return;
if (observation.apply_ns) frame->record(Frame_Trace_Measurement::backend_apply_ns, observation.apply_ns);
const auto plan_ns = observation.runtime_plan_ns + observation.frame_plan_ns;
const auto execute_ns = observation.runtime_execute_ns + observation.frame_attach_ns + observation.frame_execute_ns;
if (plan_ns) frame->record(Frame_Trace_Measurement::backend_plan_ns, plan_ns);
if (execute_ns) frame->record(Frame_Trace_Measurement::backend_execute_ns, execute_ns);
const std::array detailed_timings{std::pair{Frame_Trace_Measurement::backend_target_acquire_ns, observation.target_acquire_ns}, std::pair{Frame_Trace_Measurement::backend_structure_check_ns, observation.structure_check_ns}, std::pair{Frame_Trace_Measurement::backend_query_ns, observation.query_ns}, std::pair{Frame_Trace_Measurement::backend_runtime_plan_ns, observation.runtime_plan_ns}, std::pair{Frame_Trace_Measurement::backend_runtime_plan_cpu_ns, observation.runtime_plan_cpu_ns}, std::pair{Frame_Trace_Measurement::backend_runtime_execute_ns, observation.runtime_execute_ns}, std::pair{Frame_Trace_Measurement::backend_runtime_execute_cpu_ns, observation.runtime_execute_cpu_ns}, std::pair{Frame_Trace_Measurement::backend_mvp_update_ns, observation.mvp_update_ns}, std::pair{Frame_Trace_Measurement::backend_frame_begin_ns, observation.frame_begin_ns}, std::pair{Frame_Trace_Measurement::backend_frame_plan_ns, observation.frame_plan_ns}, std::pair{Frame_Trace_Measurement::backend_frame_plan_cpu_ns, observation.frame_plan_cpu_ns}, std::pair{Frame_Trace_Measurement::backend_emit_replay_dirty_ns, observation.emit_replay_dirty_ns}, std::pair{Frame_Trace_Measurement::backend_emit_layout_ns, observation.emit_layout_ns}, std::pair{Frame_Trace_Measurement::backend_emit_prepare_ns, observation.emit_prepare_ns}, std::pair{Frame_Trace_Measurement::backend_emit_plan_build_ns, observation.emit_plan_build_ns}, std::pair{Frame_Trace_Measurement::backend_emit_contract_ns, observation.emit_contract_ns}, std::pair{Frame_Trace_Measurement::backend_emit_stream_ns, observation.emit_stream_ns}, std::pair{Frame_Trace_Measurement::backend_emit_stream_freeze_ns, observation.emit_stream_freeze_ns}, std::pair{Frame_Trace_Measurement::backend_emit_commit_ns, observation.emit_commit_ns}, std::pair{Frame_Trace_Measurement::backend_emit_plan_reset_ns, observation.emit_plan_reset_ns}, std::pair{Frame_Trace_Measurement::backend_emit_artifact_create_ns, observation.emit_artifact_create_ns}, std::pair{Frame_Trace_Measurement::backend_emit_artifact_freeze_ns, observation.emit_artifact_freeze_ns}, std::pair{Frame_Trace_Measurement::backend_emit_packet_encode_ns, observation.emit_packet_encode_ns}, std::pair{Frame_Trace_Measurement::backend_external_register_ns, observation.external_register_ns}, std::pair{Frame_Trace_Measurement::backend_frame_attach_ns, observation.frame_attach_ns}, std::pair{Frame_Trace_Measurement::backend_frame_execute_ns, observation.frame_execute_ns}, std::pair{Frame_Trace_Measurement::backend_frame_execute_cpu_ns, observation.frame_execute_cpu_ns}, std::pair{Frame_Trace_Measurement::backend_frame_finish_ns, observation.frame_finish_ns}, std::pair{Frame_Trace_Measurement::backend_drp_validation_ns, observation.drp_validation_ns}, std::pair{Frame_Trace_Measurement::backend_drp_state_ns, observation.drp_state_ns}, std::pair{Frame_Trace_Measurement::backend_drp_buffer_create_ns, observation.drp_buffer_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_texture_create_ns, observation.drp_texture_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_shader_create_ns, observation.drp_shader_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_shader_compile_ns, observation.drp_shader_compile_ns}, std::pair{Frame_Trace_Measurement::backend_drp_shader_module_create_ns, observation.drp_shader_module_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_pipeline_create_ns, observation.drp_pipeline_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_binding_create_ns, observation.drp_binding_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_ns, observation.drp_upload_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_decode_ns, observation.drp_upload_decode_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_vulkan_allocate_ns, observation.drp_upload_vulkan_allocate_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_host_copy_ns, observation.drp_upload_host_copy_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_command_allocate_ns, observation.drp_upload_command_allocate_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_command_record_ns, observation.drp_upload_command_record_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_fence_create_ns, observation.drp_upload_fence_create_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_submit_enqueue_ns, observation.drp_upload_submit_enqueue_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_submit_queue_wait_ns, observation.drp_upload_submit_queue_wait_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_queue_submit_ns, observation.drp_upload_queue_submit_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_fence_wait_ns, observation.drp_upload_fence_wait_ns}, std::pair{Frame_Trace_Measurement::backend_drp_upload_retire_ns, observation.drp_upload_retire_ns}, std::pair{Frame_Trace_Measurement::backend_drp_transfer_ns, observation.drp_transfer_ns}, std::pair{Frame_Trace_Measurement::backend_drp_record_ns, observation.drp_record_ns}};
for (const auto& [measurement, value] : detailed_timings) if (value) frame->record(measurement, value);
if (observation.queue_submit_wait_ns) frame->record(Frame_Trace_Measurement::backend_submit_queue_ns, observation.queue_submit_wait_ns);
if (observation.submit_ns) frame->record(Frame_Trace_Measurement::backend_submit_ns, observation.submit_ns);
if (observation.readback_ns) frame->record(Frame_Trace_Measurement::readback_ns, observation.readback_ns);
if (observation.gpu_completion_observation_ns) frame->record(Frame_Trace_Measurement::gpu_completion_observation_ns, observation.gpu_completion_observation_ns);
if (observation.completion_task_queue_ns) frame->record(Frame_Trace_Measurement::completion_task_queue_ns, observation.completion_task_queue_ns);
if (observation.gpu) {
frame->record(Frame_Trace_Measurement::gpu_render_ns, observation.gpu->render_ns);
frame->record(Frame_Trace_Measurement::gpu_transition_ns, observation.gpu->transition_ns);
frame->record(Frame_Trace_Measurement::gpu_copy_ns, observation.gpu->copy_ns);
frame->record(Frame_Trace_Measurement::gpu_total_ns, observation.gpu->total_ns);
}
Frame_3D_Access::assign_datoviz_observation(frame, std::move(observation));
}
} // namespace detail
struct Render_Scene_3D::Private : Prev_Private, private detail::Scene_Datoviz_State {
/* 一次 render() 的异步执行租约;不保存、选择或复用 Frame Policy 槽位。 */
struct Render_Execution final {
Frame_3D* frame{}; /* 必填、非拥有借用;由 Frame Policy 保持至 completion 返回。 */
Completion completion{};
std::atomic_bool cpu_finished{};
std::atomic_bool backend_started{};
std::atomic_bool backend_finished{};
std::atomic_bool completion_started{};
std::chrono::steady_clock::time_point completion_observed_at{};
std::optional<detail::Gpu_Completion_Service::Result> completion_result{};
std::exception_ptr cpu_failure{};
std::exception_ptr backend_failure{};
std::exception_ptr collection_failure{};
std::optional<detail::Datoviz_Pending_Frame> datoviz_frame{};
std::shared_ptr<detail::Scene_Paint_Context> paint_context{};
};
using Shared_Render_Execution = std::shared_ptr<Render_Execution>;
Private(Camera_3D& camera_value, Axes_3D& axes_value, std::uint32_t gpu_index_value = 0, bool validation_enabled_value = false);
~Private() override = default;
Render_Result render(Frame_3D* frame, Completion completion);
private:
std::atomic_bool backend_available{true};
bool backend_initialized{};
std::uint32_t gpu_index{};
bool validation_enabled{};
proxy<scene::Camera> camera{}; /* 非拥有观察;Camera_3D 必须覆盖 Scene 生命周期。 */
proxy<scene::Axes> axes{}; /* 非拥有观察;Axes_3D 必须覆盖 Scene 生命周期。 */
std::vector<detail::Visual_Registration> registrations{};
[[nodiscard]] const Prop& advance_scene();
[[nodiscard]] detail::Scene_3D_Parameters current_parameters(const Prop& properties);
void initialize_or_validate_backend(const detail::Scene_3D_Parameters& parameters);
[[nodiscard]] proxy<scene::Task_Graph> build_frame_graph(const Shared_Render_Execution& execution, const detail::Scene_3D_Parameters& parameters);
void dispatch_events(Event_Batch& events) override;
void dispatch_datoviz(Event& event, Extent viewport);
void render_datoviz(const Shared_Render_Execution& execution, const detail::Prepared_Visual_Batch& visuals, detail::Scene_3D_Parameters parameters) noexcept;
void observe_datoviz_completion(const Shared_Render_Execution& execution, std::optional<detail::Gpu_Completion_Service::Result> result, std::exception_ptr failure) noexcept;
void try_complete(const Shared_Render_Execution& execution) noexcept;
[[nodiscard]] std::exception_ptr collect_datoviz(Render_Execution& execution) noexcept;
static void complete_execution(Shared_Render_Execution execution, std::exception_ptr runtime_failure);
void fail_datoviz(std::exception_ptr failure) noexcept;
};
inline Render_Scene_3D::Private::Private(Camera_3D& camera_value, Axes_3D& axes_value, std::uint32_t gpu_index_value, bool validation_enabled_value) : gpu_index(gpu_index_value), validation_enabled(validation_enabled_value), camera(model_proxy_view<scene::Camera>(camera_value)), axes(model_proxy_view<scene::Axes>(axes_value)) {}
inline const Render_Scene_3D::Prop& Render_Scene_3D::Private::advance_scene() {
auto& properties = this->template attachment<Prop_Tag>();
properties.internal.advance();
camera->advance();
axes->advance();
commit<aethera::scene::Renderable>();
auto& renderables = relation<aethera::scene::Renderable>();
renderables.for_each_topological([](Dag_Node_Id, proxy<scene::Renderable>& renderable) {
renderable->advance();
});
return static_cast<const Prop&>(*properties.internal.use());
}
inline detail::Scene_3D_Parameters Render_Scene_3D::Private::current_parameters(const Prop& properties) {
const auto axis = axes->use();
return {properties.viewport, properties.clear_color, camera->use(), axis[0], axis[1], axis[2]};
}
inline void Render_Scene_3D::Private::initialize_or_validate_backend(const detail::Scene_3D_Parameters& parameters) {
std::vector<detail::Visual_Registration> current;
auto& renderables = relation<aethera::scene::Renderable>();
renderables.for_each_topological([&](Dag_Node_Id id, proxy<scene::Renderable>& renderable) {
current.push_back({static_cast<detail::Visual_Identity>(id.value), std::addressof(renderable->use())});
});
if (current.empty()) throw std::invalid_argument("Render_Scene_3D requires at least one Visual");
if (backend_initialized) {
const bool same = current.size() == registrations.size() && std::ranges::equal(current, registrations, [](const auto& left, const auto& right) {
return left.identity == right.identity && left.operations == right.operations;
});
if (!same) throw std::logic_error("Render_Scene_3D Visual DAG cannot change after Datoviz initialization");
return;
}
detail::Scene_Datoviz_State::initialize(gpu_index, validation_enabled, current, parameters);
registrations = std::move(current);
backend_initialized = true;
}
inline proxy<scene::Task_Graph> Render_Scene_3D::Private::build_frame_graph(const Shared_Render_Execution& execution, const detail::Scene_3D_Parameters& parameters) {
execution->paint_context = std::make_shared<detail::Scene_Paint_Context>();
execution->paint_context->parameters = parameters;
execution->paint_context->visuals.reserve(registrations.size());
for (const auto& registration : registrations) execution->paint_context->visuals.push_back({registration.identity, {}});
auto graph = scene::make_task_graph("render_3d.frame");
auto begin = graph->add("scene.begin", [execution] {
execution->frame->mark(Frame_Trace_Marker::scene_render_started);
execution->frame->mark(Frame_Trace_Marker::prepare_started);
execution->frame->mark(Frame_Trace_Marker::prepare_finished);
execution->frame->mark(Frame_Trace_Marker::paint_started);
});
if (!begin) throw std::logic_error("failed to create 3D frame setup task");
auto& renderables = relation<aethera::scene::Renderable>();
std::unordered_map<std::uint64_t, proxy<task_flow::Task_Node>> nodes;
std::size_t slot{};
renderables.for_each_topological([&](Dag_Node_Id id, proxy<scene::Renderable>& renderable) {
renderable->render(execution->paint_context, slot++, &detail::publish_visual);
auto component = renderable->taskflow();
auto composed = graph->compose("renderable." + std::to_string(id.value), component);
if (!composed) throw std::logic_error("failed to compose a 3D Renderable task graph");
if (graph->precede(*begin, *composed) != Precede_Task_Graph_Result::preceded) throw std::logic_error("failed to order 3D frame setup before a Renderable");
nodes.emplace(id.value, std::move(*composed));
});
renderables.for_each_dependency([&](Dag_Node_Id target, Dag_Node_Id source) {
if (graph->precede(nodes.at(source.value), nodes.at(target.value)) != Precede_Task_Graph_Result::preceded) throw std::logic_error("failed to preserve a 3D Renderable DAG dependency");
});
auto backend = graph->add("scene.backend.prepare", [this, execution, paint = execution->paint_context] {
if (!detail::prepared_visual_batch_complete(paint->visuals)) throw std::logic_error("3D Scene published an incomplete Visual batch");
render_datoviz(execution, paint->visuals, paint->parameters);
});
if (!backend) throw std::logic_error("failed to create 3D backend task");
for (auto& [id, node] : nodes) if (graph->precede(node, *backend) != Precede_Task_Graph_Result::preceded) throw std::logic_error("failed to order a 3D Renderable before backend submission");
dispatch_collected_events();
return graph;
}
inline void Render_Scene_3D::Private::dispatch_events(Event_Batch& events) {
auto& renderables = relation<aethera::scene::Renderable>();
aethera::detail::dispatch_scene_events<scene::Renderable>(events, renderables);
const auto& properties = static_cast<const Prop&>(*this->template attachment<Prop_Tag>().internal.use());
for (auto& event : events) if (event) dispatch_datoviz(*event, properties.viewport);
}
inline void Render_Scene_3D::Private::dispatch_datoviz(Event& event, Extent viewport) {
if (event.is_accepted()) return;
const auto* pointer = dynamic_cast<const Basic_Pointer_Event<Vec2>*>(std::addressof(event));
const auto* wheel = dynamic_cast<const Basic_Wheel_Event<Vec2>*>(std::addressof(event));
const auto* key = dynamic_cast<const Key_Event*>(std::addressof(event));
const bool pointer_valid = pointer && std::isfinite(pointer->position.x) && std::isfinite(pointer->position.y) && pointer->position.x >= 0.0F && pointer->position.y >= 0.0F && pointer->position.x <= static_cast<float>(viewport.width) && pointer->position.y <= static_cast<float>(viewport.height);
if (wheel && pointer_valid) {
dispatch_wheel(pointer->position.x, pointer->position.y, detail::wheel_step(wheel->pixel_delta_x, wheel->angle_delta_x), detail::wheel_step(wheel->pixel_delta_y, wheel->angle_delta_y), pointer->modifiers, viewport, event.occurred_at.nanoseconds);
}
else if (pointer_valid && (event.type == Event_Type::pointer_move || event.type == Event_Type::pointer_press || event.type == Event_Type::pointer_release)) {
const auto button = event.type == Event_Type::pointer_move ? detail::held_button(pointer->buttons) : pointer->button;
dispatch_pointer(event.type, pointer->position.x, pointer->position.y, button, pointer->modifiers, viewport, event.occurred_at.nanoseconds);
}
else if (key) {
dispatch_key(*key);
}
if ((wheel && pointer_valid) || pointer_valid || key) event.accept();
}
inline void Render_Scene_3D::Private::render_datoviz(const Shared_Render_Execution& execution, const detail::Prepared_Visual_Batch& visuals, detail::Scene_3D_Parameters parameters) noexcept {
execution->backend_started.store(true, std::memory_order_release);
try {
if (visuals.empty()) throw std::invalid_argument("3D Scene requires prepared Visual data");
if (!backend_available.load(std::memory_order_acquire)) throw std::runtime_error("3D Scene resources are unavailable");
auto* frame = execution->frame;
auto reservation = detail::Gpu_Completion_Service::instance().prepare([this, execution](detail::Gpu_Completion_Service::Result result) {
observe_datoviz_completion(execution, std::move(result), {});
}, [this, execution](std::exception_ptr failure) {
observe_datoviz_completion(execution, {}, std::move(failure));
}, frame->taskflow_trace_requested());
if (!reservation) throw std::runtime_error("GPU completion capacity is exhausted");
const auto sequence = frame->identity().sequence;
const bool observe = frame->taskflow_trace_requested();
const bool readback = frame->output() == Frame_3D_Output::pixels;
frame->mark(Frame_Trace_Marker::backend_prepare_started);
auto prepared = reuse_recorded_target(parameters, visuals, sequence, observe, readback);
if (!prepared) prepared = prepare(parameters, visuals, sequence, observe, readback);
if (!prepared) throw std::logic_error("Datoviz did not provide a frame target after Scene admission");
frame->mark(Frame_Trace_Marker::backend_prepare_finished);
execution->datoviz_frame.emplace(std::move(*prepared));
execution->datoviz_frame->observation.prepare_released_after_submission = false;
frame->mark(Frame_Trace_Marker::backend_queue_entered);
frame->mark(Frame_Trace_Marker::backend_submit_queued);
auto shared_reservation = std::make_shared<detail::Gpu_Completion_Service::Reservation>(std::move(reservation.reservation));
submit(*execution->datoviz_frame, [this, execution, frame, shared_reservation = std::move(shared_reservation)](std::exception_ptr submission_failure) mutable {
try {
frame->mark(Frame_Trace_Marker::backend_queue_left);
if (submission_failure) {
fail_datoviz(submission_failure);
if (execution->datoviz_frame) {
discard(std::move(*execution->datoviz_frame));
execution->datoviz_frame.reset();
}
execution->backend_failure = std::move(submission_failure);
execution->backend_finished.store(true, std::memory_order_release);
try_complete(execution);
return;
}
frame->mark(Frame_Trace_Marker::gpu_submitted);
shared_reservation->watch(execution->datoviz_frame->device, execution->datoviz_frame->fence);
}
catch (...) {
observe_datoviz_completion(execution, {}, std::current_exception());
}
});
}
catch (...) {
auto failure = std::current_exception();
fail_datoviz(failure);
if (execution->datoviz_frame) {
discard(std::move(*execution->datoviz_frame));
execution->datoviz_frame.reset();
}
execution->backend_failure = std::move(failure);
execution->backend_finished.store(true, std::memory_order_release);
try_complete(execution);
}
}
inline void Render_Scene_3D::Private::observe_datoviz_completion(const Shared_Render_Execution& execution, std::optional<detail::Gpu_Completion_Service::Result> result, std::exception_ptr failure) noexcept {
execution->frame->mark(Frame_Trace_Marker::gpu_completed);
execution->completion_result = std::move(result);
execution->backend_failure = std::move(failure);
execution->completion_observed_at = std::chrono::steady_clock::now();
execution->backend_finished.store(true, std::memory_order_release);
try_complete(execution);
}
inline void Render_Scene_3D::Private::try_complete(const Shared_Render_Execution& execution) noexcept {
if (!execution->cpu_finished.load(std::memory_order_acquire) || !execution->backend_finished.load(std::memory_order_acquire)) return;
if (execution->completion_started.exchange(true, std::memory_order_acq_rel)) return;
try {
auto graph = scene::make_task_graph("render_3d.completion");
auto task = graph->add("scene.collect", [this, execution] {
execution->collection_failure = collect_datoviz(*execution);
});
if (!task) throw std::logic_error("failed to create 3D completion task");
const auto submitted = detail::Frame_3D_Access::run_taskflow(execution->frame, graph, [execution](std::exception_ptr runtime_failure) mutable {
complete_execution(std::move(execution), std::move(runtime_failure));
});
if (submitted != Run_Taskflow_Result::submitted) throw std::runtime_error("3D completion task runtime is unavailable");
}
catch (...) {
fail_datoviz(std::current_exception());
}
}
inline std::exception_ptr Render_Scene_3D::Private::collect_datoviz(Render_Execution& execution) noexcept {
if (execution.backend_failure) return execution.backend_failure;
try {
if (!execution.datoviz_frame) throw std::logic_error("3D completion lost its Datoviz target");
const auto completion_started = std::chrono::steady_clock::now();
execution.frame->mark(Frame_Trace_Marker::readback_started);
auto& observation = execution.datoviz_frame->observation;
if (execution.completion_result) observation.gpu_completion_observation_ns = execution.completion_result->wait_duration_ns;
observation.completion_task_queue_ns = static_cast<std::uint64_t>(std::max<std::int64_t>(0, std::chrono::duration_cast<std::chrono::nanoseconds>(completion_started - execution.completion_observed_at).count()));
if (!execution.completion_result || execution.completion_result->error != detail::Gpu_Completion_Service::Completion_Error::none) throw std::runtime_error("Datoviz GPU completion failed");
auto completed = collect(std::move(*execution.datoviz_frame));
execution.datoviz_frame.reset();
execution.frame->mark(Frame_Trace_Marker::readback_finished);
detail::publish_datoviz_observation(execution.frame, std::move(completed.observation));
std::array outputs{execution.frame};
detail::Frame_3D_Access::assign_pixels(outputs, completed.extent, std::move(completed.pixels), execution.frame->identity());
return {};
}
catch (...) {
auto failure = std::current_exception();
fail_datoviz(failure);
if (execution.datoviz_frame) {
quarantine(std::move(*execution.datoviz_frame));
execution.datoviz_frame.reset();
}
return failure;
}
}
inline void Render_Scene_3D::Private::complete_execution(Shared_Render_Execution execution, std::exception_ptr runtime_failure) {
auto* frame = execution->frame;
auto callback = std::move(execution->completion);
auto failure = execution->cpu_failure ? std::move(execution->cpu_failure) : std::move(execution->backend_failure);
if (!failure) failure = std::move(execution->collection_failure);
if (!failure) failure = std::move(runtime_failure);
frame->mark(Frame_Trace_Marker::paint_finished);
frame->mark(Frame_Trace_Marker::scene_render_finished);
frame->mark(Frame_Trace_Marker::frame_ready);
execution->frame = nullptr;
execution->datoviz_frame.reset();
execution->paint_context.reset();
execution->completion_result.reset();
execution->backend_failure = {};
execution->collection_failure = {};
callback(frame, std::move(failure));
}
inline void Render_Scene_3D::Private::fail_datoviz(std::exception_ptr failure) noexcept {
backend_available.store(false, std::memory_order_release);
try {
if (failure) std::rethrow_exception(std::move(failure));
}
catch (const std::exception& exception) {
std::fprintf(stderr, "Render_Scene_3D backend failure: %s\n", exception.what());
}
catch (...) {}
}
inline Render_Scene_3D::Render_Result Render_Scene_3D::Private::render(Frame_3D* frame, Completion completion) {
if (!frame) return Render_Result::frame_missing;
if (!completion) return Render_Result::completion_missing;
frame->mark(Frame_Trace_Marker::scene_render_entered);
try {
frame->mark(Frame_Trace_Marker::scene_advance_started);
const Prop& properties = advance_scene();
frame->mark(Frame_Trace_Marker::scene_advance_finished);
if (!properties.view_active) return Render_Result::view_inactive;
if (properties.viewport.empty()) return Render_Result::empty_viewport;
if (!backend_available.load(std::memory_order_acquire)) return Render_Result::backend_unavailable;
const auto parameters = current_parameters(properties);
initialize_or_validate_backend(parameters);
auto execution = std::make_shared<Render_Execution>();
execution->frame = frame;
execution->completion = std::move(completion);
auto graph = build_frame_graph(execution, parameters);
frame->mark(Frame_Trace_Marker::scene_render_requested);
auto completed = [this, execution](std::exception_ptr failure) {
execution->cpu_failure = std::move(failure);
if (execution->cpu_failure && !execution->backend_started.load(std::memory_order_acquire)) execution->backend_finished.store(true, std::memory_order_release);
execution->cpu_finished.store(true, std::memory_order_release);
try_complete(execution);
};
const auto submitted = detail::Frame_3D_Access::run_taskflow(frame, graph, std::move(completed));
if (submitted != Run_Taskflow_Result::submitted) return Render_Result::backend_unavailable;
return Render_Result::submitted;
}
catch (...) {
fail_datoviz(std::current_exception());
return Render_Result::backend_unavailable;
}
}
} // namespace aethera::render_3d