前后端全部提交

This commit is contained in:
2026-08-21 01:30:17 +08:00
parent 70dedbe0ec
commit 26a89335b4
70 changed files with 752 additions and 284 deletions
@@ -11,28 +11,34 @@ float wheel_step(double pixel, double angle) { return static_cast<float>(pixel !
}
struct Async_Render_Backend::Implementation : std::enable_shared_from_this<Implementation> {
struct Pending { std::optional<Datoviz_Visual_Backend::Pending_Frame> frame{}; };
struct Submission { Prepared_Visual visual{}; Scene_3D_Parameters parameters{}; };
std::shared_ptr<Render_Domain> render_domain; /* GPU 索引对应的单线程 Datoviz 域。 */
std::unique_ptr<Datoviz_Visual_Backend> backend; /* 只允许 render_domain 线程访问。 */
mutable std::mutex frame_mutex; /* 保护异步发布的最新像素帧。 */
std::shared_ptr<const Pixel_Frame> latest_frame{}; /* 最近完成读回的像素帧。 */
mutable std::mutex render_mutex; /* 保护合并帧意图、在途标记和完成回调。 */
std::optional<Submission> pending_submission{}; /* 在途期间覆盖保存的最新绘制意图。 */
Frame_Callback frame_callback{}; /* 完成帧的唯一发布出口。 */
bool frame_in_flight{}; /* 是否已有帧占用唯一 Frame Target。 */
mutable std::mutex failure_mutex; /* 保护最后一次 Unknown Failure。 */
std::exception_ptr failure{}; /* 后端隔离边界捕获的最后一次 Unknown Failure。 */
std::atomic_uint64_t next_sequence{1}; /* 下一次接受请求的帧序号。 */
std::atomic_uint64_t submitted{}; /* 已提交帧计数。 */
std::atomic_uint64_t completed{}; /* 已完成帧计数。 */
std::atomic_uint64_t dropped{}; /* 已丢弃帧请求计数。 */
std::atomic_bool frame_in_flight{}; /* 限制后端唯一 Frame_Target 同时只有一帧。 */
std::atomic_bool available{}; /* 后端是否可接受事件和帧请求。 */
std::atomic_uint64_t next_sequence{1}; /* 下一完成帧使用的单调序号。 */
std::atomic_bool available{}; /* 后端是否可接受事件和绘制。 */
Implementation(std::uint32_t gpu_index, bool validation_enabled, Visual_Family visual_family, Scene_3D_Parameters initial) : render_domain(Render_Domain::acquire(gpu_index)) { const auto initialized = render_domain->invoke([this, gpu_index, validation_enabled, visual_family, initial] { backend = std::make_unique<Datoviz_Visual_Backend>(gpu_index, validation_enabled, visual_family, initial); }); if (!initialized) throw std::runtime_error("render domain stopped during 3D backend initialization"); available.store(true, std::memory_order_release); }
~Implementation() { available.store(false, std::memory_order_release); if (!backend) return; const auto destroyed = render_domain->invoke([this] { backend.reset(); }); if (!destroyed) backend.release(); }
void fail(std::exception_ptr value) noexcept { { std::lock_guard lock(failure_mutex); failure = std::move(value); } available.store(false, std::memory_order_release); frame_in_flight.store(false, std::memory_order_release); }
void finish(std::shared_ptr<Pending> pending, Gpu_Completion_Service::Result completion) noexcept { auto self = shared_from_this(); try { const auto result = render_domain->post([self, pending = std::move(pending), completion] { if (!pending->frame) { self->frame_in_flight.store(false, std::memory_order_release); return; } if (completion.error == Gpu_Completion_Service::Completion_Error::none) { auto finished = self->backend->collect(std::move(*pending->frame)); { std::lock_guard lock(self->frame_mutex); self->latest_frame = std::move(finished.frame); } self->completed.fetch_add(1, std::memory_order_relaxed); } else { self->backend->discard(std::move(*pending->frame)); self->dropped.fetch_add(1, std::memory_order_relaxed); } self->frame_in_flight.store(false, std::memory_order_release); }, [self](std::exception_ptr value) { self->fail(std::move(value)); }); if (result != Render_Domain::Admission_Result::none) fail(std::make_exception_ptr(std::runtime_error("render domain stopped before GPU completion collection"))); } catch (...) { fail(std::current_exception()); } }
void enqueue(const Prepared_Visual& visual, Scene_3D_Parameters parameters) { if (!available.load(std::memory_order_acquire)) { dropped.fetch_add(1, std::memory_order_relaxed); return; } bool expected = false; if (!frame_in_flight.compare_exchange_strong(expected, true, std::memory_order_acq_rel)) { dropped.fetch_add(1, std::memory_order_relaxed); return; } const auto sequence = next_sequence.fetch_add(1, std::memory_order_relaxed); auto self = shared_from_this(); auto prepared = std::make_shared<Prepared_Visual>(visual); auto pending = std::make_shared<Pending>(); const auto queued = render_domain->try_post([self, parameters, sequence, prepared, pending] { auto reservation = Gpu_Completion_Service::instance().prepare([self, pending](Gpu_Completion_Service::Result result) { self->finish(pending, std::move(result)); }, [self](std::exception_ptr value) { self->fail(std::move(value)); }, false); if (!reservation) { self->frame_in_flight.store(false, std::memory_order_release); self->dropped.fetch_add(1, std::memory_order_relaxed); return; } pending->frame = self->backend->submit(parameters, *prepared, sequence, false); if (!pending->frame) { self->frame_in_flight.store(false, std::memory_order_release); return; } self->submitted.fetch_add(1, std::memory_order_relaxed); reservation.reservation.watch(pending->frame->device, pending->frame->fence); }, [self](std::exception_ptr value) { self->fail(std::move(value)); }); if (queued != Render_Domain::Try_Post_Result::queued) { frame_in_flight.store(false, std::memory_order_release); dropped.fetch_add(1, std::memory_order_relaxed); } }
void fail(std::exception_ptr value) noexcept;
void render(const Prepared_Visual& visual, Scene_3D_Parameters parameters);
void submit(Submission submission);
void finish(std::shared_ptr<Pending> pending, Gpu_Completion_Service::Result completion) noexcept;
void complete(std::shared_ptr<const Pixel_Frame> frame);
};
Async_Render_Backend::Async_Render_Backend(std::uint32_t gpu_index, bool validation_enabled, Visual_Family visual_family, Scene_3D_Parameters parameters) : implementation_(std::make_shared<Implementation>(gpu_index, validation_enabled, visual_family, parameters)) {}
Async_Render_Backend::~Async_Render_Backend() = default;
void Async_Render_Backend::enqueue(const Prepared_Visual& visual, Scene_3D_Parameters parameters) { implementation_->enqueue(visual, parameters); }
std::shared_ptr<const Pixel_Frame> Async_Render_Backend::latest_frame() const { std::lock_guard lock(implementation_->frame_mutex); return implementation_->latest_frame; }
Async_Render_Backend::Statistics Async_Render_Backend::statistics() const noexcept { return {implementation_->submitted.load(std::memory_order_relaxed), implementation_->completed.load(std::memory_order_relaxed), implementation_->dropped.load(std::memory_order_relaxed), implementation_->frame_in_flight.load(std::memory_order_acquire), implementation_->available.load(std::memory_order_acquire)}; }
void Async_Render_Backend::Implementation::fail(std::exception_ptr value) noexcept { { std::lock_guard lock(failure_mutex); failure = std::move(value); } available.store(false, std::memory_order_release); std::lock_guard lock(render_mutex); frame_in_flight = false; pending_submission.reset(); }
void Async_Render_Backend::Implementation::render(const Prepared_Visual& visual, Scene_3D_Parameters parameters) { if (!available.load(std::memory_order_acquire)) return; std::optional<Submission> submission; { std::lock_guard lock(render_mutex); pending_submission = Submission{visual, parameters}; if (frame_in_flight) return; frame_in_flight = true; submission = std::move(pending_submission); pending_submission.reset(); } submit(std::move(*submission)); }
void Async_Render_Backend::Implementation::submit(Submission submission) { const auto sequence = next_sequence.fetch_add(1, std::memory_order_relaxed); auto self = shared_from_this(); auto prepared = std::make_shared<Prepared_Visual>(std::move(submission.visual)); auto pending = std::make_shared<Pending>(); const auto parameters = submission.parameters; const auto queued = render_domain->try_post([self, parameters, sequence, prepared, pending] { auto reservation = Gpu_Completion_Service::instance().prepare([self, pending](Gpu_Completion_Service::Result result) { self->finish(pending, std::move(result)); }, [self](std::exception_ptr value) { self->fail(std::move(value)); }, false); if (!reservation) { self->complete({}); return; } pending->frame = self->backend->submit(parameters, *prepared, sequence, false); if (!pending->frame) { self->complete({}); return; } reservation.reservation.watch(pending->frame->device, pending->frame->fence); }, [self](std::exception_ptr value) { self->fail(std::move(value)); }); if (queued != Render_Domain::Try_Post_Result::queued) { std::lock_guard lock(render_mutex); pending_submission = Submission{*prepared, parameters}; frame_in_flight = false; } }
void Async_Render_Backend::Implementation::finish(std::shared_ptr<Pending> pending, Gpu_Completion_Service::Result completion) noexcept { auto self = shared_from_this(); try { const auto result = render_domain->post([self, pending = std::move(pending), completion] { std::shared_ptr<const Pixel_Frame> frame; if (pending->frame) { if (completion.error == Gpu_Completion_Service::Completion_Error::none) frame = self->backend->collect(std::move(*pending->frame)).frame; else self->backend->discard(std::move(*pending->frame)); } self->complete(std::move(frame)); }, [self](std::exception_ptr value) { self->fail(std::move(value)); }); if (result != Render_Domain::Admission_Result::none) fail(std::make_exception_ptr(std::runtime_error("render domain stopped before GPU completion collection"))); } catch (...) { fail(std::current_exception()); } }
void Async_Render_Backend::Implementation::complete(std::shared_ptr<const Pixel_Frame> frame) { Frame_Callback callback; std::optional<Submission> next; { std::lock_guard lock(render_mutex); callback = frame_callback; frame_in_flight = false; if (pending_submission && available.load(std::memory_order_acquire)) { frame_in_flight = true; next = std::move(pending_submission); pending_submission.reset(); } } if (frame && callback) callback(std::move(frame)); if (next) submit(std::move(*next)); }
void Async_Render_Backend::render(const Prepared_Visual& visual, Scene_3D_Parameters parameters) { implementation_->render(visual, parameters); }
void Async_Render_Backend::set_frame_callback(Frame_Callback callback) { std::lock_guard lock(implementation_->render_mutex); implementation_->frame_callback = std::move(callback); }
bool Async_Render_Backend::available() const noexcept { return implementation_->available.load(std::memory_order_acquire); }
Async_Render_Backend::Dispatch_Event_Result Async_Render_Backend::dispatch_event(const Event& event, Extent viewport) { auto& data = *implementation_; if (!data.available.load(std::memory_order_acquire)) return Dispatch_Event_Result::backend_unavailable; if (const auto* wheel = dynamic_cast<const Wheel_Event_Capability*>(&event)) { const auto* pointer = dynamic_cast<const Pointer_Event_Capability*>(&event); if (!pointer || !std::isfinite(pointer->position_x()) || !std::isfinite(pointer->position_y())) return Dispatch_Event_Result::invalid_event; const auto result = data.render_domain->invoke([&data, pointer, wheel, viewport] { data.backend->dispatch_wheel(static_cast<float>(pointer->position_x()), static_cast<float>(pointer->position_y()), wheel_step(wheel->pixel_delta_x_value(), wheel->angle_delta_x_value()), wheel_step(wheel->pixel_delta_y_value(), wheel->angle_delta_y_value()), pointer->keyboard_modifiers(), viewport); }); return result ? Dispatch_Event_Result::dispatched : Dispatch_Event_Result::backend_unavailable; } if (const auto* pointer = dynamic_cast<const Pointer_Event_Capability*>(&event)) { if (!std::isfinite(pointer->position_x()) || !std::isfinite(pointer->position_y()) || (event.type != Event_Type::pointer_move && event.type != Event_Type::pointer_press && event.type != Event_Type::pointer_release)) return Dispatch_Event_Result::invalid_event; const auto result = data.render_domain->invoke([&data, pointer, &event, viewport] { data.backend->dispatch_pointer(event.type, static_cast<float>(pointer->position_x()), static_cast<float>(pointer->position_y()), pointer->pointer_button(), pointer->keyboard_modifiers(), viewport); }); return result ? Dispatch_Event_Result::dispatched : Dispatch_Event_Result::backend_unavailable; } if (const auto* key = dynamic_cast<const Key_Event*>(&event)) { const auto result = data.render_domain->invoke([&data, key] { data.backend->dispatch_key(*key); }); return result ? Dispatch_Event_Result::dispatched : Dispatch_Event_Result::backend_unavailable; } return Dispatch_Event_Result::ignored; }
}
@@ -1,24 +1,20 @@
#pragma once
#include "Backend_Types.hpp"
#include <functional>
#include <memory>
namespace aethera::render_3d::detail {
class Async_Render_Backend final {
public:
struct Statistics {
std::uint64_t submitted{}; /* 已提交给 Vulkan 的帧数。 */
std::uint64_t completed{}; /* 已完成读回并发布的帧数。 */
std::uint64_t dropped{}; /* 因已有在途帧或队列已满而丢弃的请求数。 */
bool frame_in_flight{}; /* 是否存在尚未完成的 GPU 帧。 */
bool available{}; /* Datoviz 后端是否完成初始化且未隔离。 */
};
using Frame_Callback = std::function<void(std::shared_ptr<const Pixel_Frame>)>;
Async_Render_Backend(std::uint32_t gpu_index, bool validation_enabled, Visual_Family visual_family, Scene_3D_Parameters parameters);
~Async_Render_Backend();
Async_Render_Backend(const Async_Render_Backend&) = delete;
Async_Render_Backend& operator=(const Async_Render_Backend&) = delete;
/* 无等待地把 Prepared_Visual 放入渲染域;队列忙时直接记录丢帧。 */
void enqueue(const Prepared_Visual& visual, Scene_3D_Parameters parameters);
[[nodiscard]] std::shared_ptr<const Pixel_Frame> latest_frame() const;
[[nodiscard]] Statistics statistics() const noexcept;
/* 记录最新绘制意图并无等待提交;在途期间的多次调用自动合并为一次后继帧。 */
void render(const Prepared_Visual& visual, Scene_3D_Parameters parameters);
/* 设置完成帧出口;回调在 Datoviz Render Domain 线程执行。 */
void set_frame_callback(Frame_Callback callback);
[[nodiscard]] bool available() const noexcept;
enum class Dispatch_Event_Result : std::uint8_t { dispatched, ignored, invalid_event, backend_unavailable };
[[nodiscard]] Dispatch_Event_Result dispatch_event(const Event& event, Extent viewport);
private:
@@ -2,9 +2,9 @@
namespace aethera::render_3d {
bool Render_Scene_3D::Prop::operator==(const Prop&) const = default;
bool Render_Scene_3D::State::operator==(const State&) const = default;
Render_Scene_3D::Request_Frame_Result Render_Scene_3D::request_frame() { return static_cast<Private&>(*d).dispatch->request_frame(this); }
void Render_Scene_3D::render() { static_cast<Private&>(*d).dispatch->render(this); }
void Render_Scene_3D::set_frame_callback(Frame_Callback callback) { static_cast<Private&>(*d).dispatch->set_frame_callback(this, std::move(callback)); }
Render_Scene_3D::Dispatch_Event_Result Render_Scene_3D::dispatch_event(const Event& event) { return static_cast<Private&>(*d).dispatch->dispatch_event(this, event); }
std::shared_ptr<const Pixel_Frame> Render_Scene_3D::latest_frame() const { return static_cast<const Private&>(*d).dispatch->latest_frame(this); }
void Render_Scene_3D::activate_view() { static_cast<Private&>(*d).dispatch->set_active(this, true); }
void Render_Scene_3D::deactivate_view() { static_cast<Private&>(*d).dispatch->set_active(this, false); }
}
+7 -11
View File
@@ -3,6 +3,7 @@
#include "../visual/Visuals.hpp"
#include <scene.hpp>
#include <expected>
#include <functional>
#include <memory>
namespace aethera::render_3d {
/* 执行 3D Renderable 图,并把 Paint 阶段无等待地提交给 Datoviz 渲染域。 */
@@ -10,15 +11,10 @@ struct Render_Scene_3D : Def<Render_Scene_3D, Scene> {
struct Prop : Prev_Prop {
Extent viewport{560, 320}; /* 离屏渲染目标尺寸,单位为像素。 */
Linear_Color clear_color{}; /* 每帧开始时写入的线性背景色。 */
bool view_active{true}; /* 是否接受新帧请求;停用不清除最近完成帧。 */
bool view_active{true}; /* 是否接受 render() 产生新的完成帧。 */
bool operator==(const Prop&) const;
};
struct State : Prev_State {
std::uint64_t submitted_frame_count{}; /* 已提交给 Vulkan 的累计帧数。 */
std::uint64_t completed_frame_count{}; /* 已完成 GPU 读回并发布的累计帧数。 */
std::uint64_t dropped_frame_count{}; /* 因背压或已有在途帧而丢弃的累计请求数。 */
bool frame_in_flight{}; /* 是否存在尚未完成的 GPU 帧。 */
bool backend_available{}; /* Datoviz 后端是否可接受新工作。 */
bool operator==(const State&) const;
};
struct Private;
@@ -37,14 +33,14 @@ struct Render_Scene_3D : Def<Render_Scene_3D, Scene> {
std::uint32_t gpu_index{}; /* Datoviz 使用的物理 GPU 下标。 */
bool validation_enabled{}; /* 是否启用 Vulkan 验证。 */
};
enum class Request_Frame_Result : std::uint8_t { queued, view_inactive, empty_viewport, backend_unavailable };
/* 执行 CPU TaskflowPaint 节点只入队,不等待 Vulkan 或 GPU fence。 */
[[nodiscard]] Request_Frame_Result request_frame();
using Frame_Callback = std::function<void(std::shared_ptr<const Pixel_Frame>)>;
/* 触发绘制;在途期间的多次调用合并为一个使用最新数据的后继帧。 */
void render();
/* 安装完成帧回调;回调由 Datoviz Render Domain 线程调用。 */
void set_frame_callback(Frame_Callback callback);
enum class Dispatch_Event_Result : std::uint8_t { dispatched, ignored, invalid_event, backend_unavailable };
/* 在 Datoviz 单线程渲染域中同步派发输入事件。 */
[[nodiscard]] Dispatch_Event_Result dispatch_event(const Event& event);
/* 返回最近一次异步完成的 RGBA8 帧;尚无完成帧时为空。 */
[[nodiscard]] std::shared_ptr<const Pixel_Frame> latest_frame() const;
void activate_view();
void deactivate_view();
};
+8 -11
View File
@@ -10,14 +10,14 @@ struct Scene_Paint_Context {
};
}
struct Render_Scene_3D::Private : Prev_Private {
using Request_Run = Request_Frame_Result (*)(Root*);
using Render_Run = void (*)(Root*);
using Callback_Run = void (*)(Root*, Frame_Callback);
using Event_Run = Dispatch_Event_Result (*)(Root*, const Event&);
using Frame_Run = std::shared_ptr<const Pixel_Frame> (*)(const Root*);
using Active_Run = void (*)(Root*, bool);
struct Dispatch {
Request_Run request_frame; /* 执行 CPU 图并异步提交 Paint。 */
Render_Run render; /* 执行 CPU 图并异步提交 Paint。 */
Callback_Run set_frame_callback; /* 安装最终完成帧回调。 */
Event_Run dispatch_event; /* 向 Datoviz 输入路由器派发事件。 */
Frame_Run latest_frame; /* 获取最近异步完成帧。 */
Active_Run set_active; /* 修改最终 Scene 的活动属性。 */
};
std::shared_ptr<detail::Async_Render_Backend> backend{}; /* Scene 拥有的异步后端;已入队命令自行延长实现寿命。 */
@@ -28,21 +28,18 @@ struct Render_Scene_3D::Private : Prev_Private {
/* CRTP 覆盖:绑定 Scene 机制和 Render_Scene_3D 公开薄壳。 */
template <Attached Object> void bind_private_crtp(Object* object);
/* CRTP 覆盖:执行 Kernel Scene Taskflow;其 Paint 子图只负责异步入队。 */
template <Attached Object, typename Callback> void process(Object* object, Callback&& callback) requires std::invocable<Callback, Request_Frame_Result>;
/* CRTP 推进 hook:发布异步后端的统计快照。 */
template <typename Object, typename Prop_Type, typename State_Type> void before_advance(Object* object, Prop_Type* pending_prop, State_Access<State_Type> pending_states, const Prop_Type* current_prop, State_Access<const State_Type> current_states);
template <Attached Object, typename Callback> void process(Object* object, Callback&& callback) requires std::invocable<Callback>;
template <Attached Object> [[nodiscard]] static const Dispatch& dispatch_for();
};
template <typename Object> template <Attached Visual_Object>
Render_Scene_3D::Builder<Object>::Builder(Visual_Object* visual_value, std::uint32_t gpu_index_value, bool validation_enabled_value) : Base(), visual(visual_value), visual_family(Visual_Object::Attached_Object::Specification::family), gpu_index(gpu_index_value), validation_enabled(validation_enabled_value) {
bind_visual = [](Root* root, std::shared_ptr<void> context) { auto* object = static_cast<Visual_Object*>(root); Base::private_access(object).template get<typename Visual_Object::Attached_Object::Base_Tag>().bind_paint_target(std::move(context), [](void* raw_context, const detail::Prepared_Visual& prepared) { auto& submission = *static_cast<detail::Scene_Paint_Context<Object>*>(raw_context); const auto& prop = submission.scene->template read_prop<Render_Scene_3D::Base_Tag>(); submission.backend->enqueue(prepared, detail::Scene_3D_Parameters{prop.viewport, prop.clear_color}); }); };
bind_visual = [](Root* root, std::shared_ptr<void> context) { auto* object = static_cast<Visual_Object*>(root); Base::private_access(object).template get<typename Visual_Object::Attached_Object::Base_Tag>().bind_paint_target(std::move(context), [](void* raw_context, const detail::Prepared_Visual& prepared) { auto& submission = *static_cast<detail::Scene_Paint_Context<Object>*>(raw_context); const auto& prop = submission.scene->template read_prop<Render_Scene_3D::Base_Tag>(); submission.backend->render(prepared, detail::Scene_3D_Parameters{prop.viewport, prop.clear_color}); }); };
attach_visual = [](Object* scene, Root* root) { auto* object = static_cast<Visual_Object*>(root); return scene->template edit_dependency_graph<Prepare_Data_Tag, Paint_Tag>([&](auto& prepare, auto& paint) { prepare.add(object); paint.add(object); prepare.template add_prop_dependency<Render_Scene_3D::Base_Tag>(object, scene); }); };
}
template <typename Object>
std::expected<std::unique_ptr<Object>, Dependency_Graph_Error> Render_Scene_3D::Builder<Object>::build() { if (!visual || !bind_visual || !attach_visual) throw std::invalid_argument("Render_Scene_3D requires one Visual"); auto result = Base::build(); if (!result) return std::unexpected(result.error()); auto scene = std::move(result).value(); auto& private_data = Base::private_access(scene.get()).template get<Render_Scene_3D::Base_Tag>(); private_data.initialize_backend(scene.get(), gpu_index, validation_enabled, visual_family); bind_visual(visual, private_data.paint_context); auto graph_result = attach_visual(scene.get(), visual); if (!graph_result) return std::unexpected(graph_result.error()); return std::move(scene); }
template <Attached Object> void Render_Scene_3D::Private::initialize_backend(Object* object, std::uint32_t gpu_index, bool validation_enabled, Visual_Family visual_family) { const auto& prop = object->template read_prop<Render_Scene_3D::Base_Tag>(); backend = std::make_shared<detail::Async_Render_Backend>(gpu_index, validation_enabled, visual_family, detail::Scene_3D_Parameters{prop.viewport, prop.clear_color}); paint_context = std::make_shared<detail::Scene_Paint_Context<Object>>(detail::Scene_Paint_Context<Object>{backend, object}); }
template <Attached Object, typename Callback> void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback, Request_Frame_Result> { const auto& prop = object->template read_prop<Render_Scene_3D::Base_Tag>(); if (!prop.view_active) { std::invoke(std::forward<Callback>(callback), Request_Frame_Result::view_inactive); return; } if (prop.viewport.empty()) { std::invoke(std::forward<Callback>(callback), Request_Frame_Result::empty_viewport); return; } if (!backend || !backend->statistics().available) { std::invoke(std::forward<Callback>(callback), Request_Frame_Result::backend_unavailable); return; } Prev_Private::process(object, [](const Scene::Private::Result&) {}); std::invoke(std::forward<Callback>(callback), Request_Frame_Result::queued); }
template <typename Object, typename Prop_Type, typename State_Type> void Render_Scene_3D::Private::before_advance(Object*, Prop_Type*, State_Access<State_Type> pending_states, const Prop_Type*, State_Access<const State_Type>) { if (!backend) return; const auto statistics = backend->statistics(); auto& state = pending_states.template get<Render_Scene_3D::Base_Tag>(); state.submitted_frame_count = statistics.submitted; state.completed_frame_count = statistics.completed; state.dropped_frame_count = statistics.dropped; state.frame_in_flight = statistics.frame_in_flight; state.backend_available = statistics.available; }
template <Attached Object> const Render_Scene_3D::Private::Dispatch& Render_Scene_3D::Private::dispatch_for() { static const Dispatch value{[](Root* root) { auto* object = static_cast<Object*>(root); Request_Frame_Result result{}; object->process([&](Request_Frame_Result value) { result = value; }); return result; }, [](Root* root, const Event& event) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (!data.backend) return Dispatch_Event_Result::backend_unavailable; const auto viewport = object->template read_prop<Render_Scene_3D::Base_Tag>().viewport; switch (data.backend->dispatch_event(event, viewport)) { case detail::Async_Render_Backend::Dispatch_Event_Result::dispatched: return Dispatch_Event_Result::dispatched; case detail::Async_Render_Backend::Dispatch_Event_Result::ignored: return Dispatch_Event_Result::ignored; case detail::Async_Render_Backend::Dispatch_Event_Result::invalid_event: return Dispatch_Event_Result::invalid_event; case detail::Async_Render_Backend::Dispatch_Event_Result::backend_unavailable: return Dispatch_Event_Result::backend_unavailable; } return Dispatch_Event_Result::backend_unavailable; }, [](const Root* root) { const auto* object = static_cast<const Object*>(root); const auto& data = static_cast<const typename Object::Private&>(*object->d); return data.backend ? data.backend->latest_frame() : std::shared_ptr<const Pixel_Frame>{}; }, [](Root* root, bool active) { static_cast<Object*>(root)->template set<&Prop::view_active>(active); }}; return value; }
template <Attached Object, typename Callback> void Render_Scene_3D::Private::process(Object* object, Callback&& callback) requires std::invocable<Callback> { const auto& prop = object->template read_prop<Render_Scene_3D::Base_Tag>(); if (!prop.view_active || prop.viewport.empty() || !backend || !backend->available()) return; Prev_Private::process(object, [](const Scene::Private::Result&) {}); std::invoke(std::forward<Callback>(callback)); }
template <Attached Object> const Render_Scene_3D::Private::Dispatch& Render_Scene_3D::Private::dispatch_for() { static const Dispatch value{[](Root* root) { auto* object = static_cast<Object*>(root); object->process([] {}); }, [](Root* root, Frame_Callback callback) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (data.backend) data.backend->set_frame_callback(std::move(callback)); }, [](Root* root, const Event& event) { auto* object = static_cast<Object*>(root); auto& data = static_cast<typename Object::Private&>(*object->d); if (!data.backend) return Dispatch_Event_Result::backend_unavailable; const auto viewport = object->template read_prop<Render_Scene_3D::Base_Tag>().viewport; switch (data.backend->dispatch_event(event, viewport)) { case detail::Async_Render_Backend::Dispatch_Event_Result::dispatched: return Dispatch_Event_Result::dispatched; case detail::Async_Render_Backend::Dispatch_Event_Result::ignored: return Dispatch_Event_Result::ignored; case detail::Async_Render_Backend::Dispatch_Event_Result::invalid_event: return Dispatch_Event_Result::invalid_event; case detail::Async_Render_Backend::Dispatch_Event_Result::backend_unavailable: return Dispatch_Event_Result::backend_unavailable; } return Dispatch_Event_Result::backend_unavailable; }, [](Root* root, bool active) { static_cast<Object*>(root)->template set<&Prop::view_active>(active); }}; return value; }
template <Attached Object> void Render_Scene_3D::Private::bind_private_crtp(Object* object) { Prev_Private::bind_private_crtp(object); dispatch = &dispatch_for<Object>(); }
}
+7 -3
View File
@@ -1,5 +1,6 @@
#include <render_3D/Render_3D.hpp>
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <thread>
namespace aethera::render_3d {
@@ -55,12 +56,15 @@ TEST(Render_3D_Scene, Paint_Submits_Without_Waiting_For_Gpu) {
auto scene_result = typename Scene_Object::Builder(visual.get()).build();
ASSERT_TRUE(scene_result.has_value());
auto scene = std::move(scene_result).value();
std::atomic_bool frame_ready{};
std::shared_ptr<const Pixel_Frame> frame;
scene->set_frame_callback([&](std::shared_ptr<const Pixel_Frame> value) { frame = std::move(value); frame_ready.store(true, std::memory_order_release); });
const auto started = std::chrono::steady_clock::now();
EXPECT_EQ(scene->request_frame(), Render_Scene_3D::Request_Frame_Result::queued);
scene->render();
const auto submit_duration = std::chrono::steady_clock::now() - started;
EXPECT_LT(submit_duration, std::chrono::seconds(1));
for (std::size_t attempt = 0; attempt != 500 && !scene->latest_frame(); ++attempt) std::this_thread::sleep_for(std::chrono::milliseconds(10));
EXPECT_NE(scene->latest_frame(), nullptr);
for (std::size_t attempt = 0; attempt != 500 && !frame_ready.load(std::memory_order_acquire); ++attempt) std::this_thread::sleep_for(std::chrono::milliseconds(10));
EXPECT_NE(frame, nullptr);
}
catch (const std::exception& error) {
GTEST_SKIP() << "Vulkan/Datoviz unavailable: " << error.what();
@@ -127,7 +127,7 @@ DvzAnnotation* dvz_annotation(DvzPanel* panel, const DvzAnnotationDesc* desc)
annotation->version = 1;
if (desc->text != NULL)
dvz_strlcpy(annotation->text, desc->text, sizeof(annotation->text));
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return annotation;
}
@@ -174,7 +174,7 @@ DvzResult dvz_annotation_set_style(DvzAnnotation* annotation, const DvzTextStyle
annotation->style = resolved;
annotation->dirty_flags |= DVZ_TEXT_DIRTY_STYLE | DVZ_TEXT_DIRTY_LAYOUT | DVZ_TEXT_DIRTY_RENDER;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -192,7 +192,7 @@ DvzResult dvz_annotation_set_placement(
annotation->dirty_flags |=
DVZ_TEXT_DIRTY_PLACEMENT | DVZ_TEXT_DIRTY_LAYOUT | DVZ_TEXT_DIRTY_RENDER;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -211,7 +211,7 @@ void dvz_annotation_destroy(DvzAnnotation* annotation)
dvz_visual_set_visible(annotation->visual, false);
if (annotation->scalebar_visual != NULL)
dvz_visual_set_visible(annotation->scalebar_visual, false);
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
annotation->scene = NULL;
annotation->panel = NULL;
annotation->has_format = false;
@@ -235,6 +235,6 @@ DvzResult dvz_annotation_set_format(DvzAnnotation* annotation, const DvzFormatDe
annotation->dirty_flags |=
DVZ_TEXT_DIRTY_STRING | DVZ_TEXT_DIRTY_LAYOUT | DVZ_TEXT_DIRTY_RENDER;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return DVZ_OK;
}
+1 -1
View File
@@ -183,7 +183,7 @@ void _axis_mark_dirty(DvzAxis* axis)
axis->dirty = true;
axis->version++;
_scene_panel_refresh_axis_reserve(axis->panel);
_scene_notify_request_frame(axis->panel != NULL ? axis->panel->figure : NULL);
_scene_notify_invalidated(axis->panel != NULL ? axis->panel->figure : NULL);
}
@@ -87,7 +87,7 @@ void _scene_mark_colorbar_dirty(DvzColorbar* colorbar)
colorbar->dirty = true;
colorbar->version = colorbar->version == UINT64_MAX ? 1 : colorbar->version + 1;
_colorbar_apply_auto_reserve(colorbar);
_scene_notify_request_frame(colorbar->panel != NULL ? colorbar->panel->figure : NULL);
_scene_notify_invalidated(colorbar->panel != NULL ? colorbar->panel->figure : NULL);
}
+4 -4
View File
@@ -444,7 +444,7 @@ DvzGuideLine* dvz_guide_line(DvzPanel* panel, const DvzGuideLineDesc* desc)
guide->label = _guide_label_create(panel, resolved.label);
_guide_attach_visual(panel, visual, DVZ_GENERATED_VISUAL_GUIDE_LINE, resolved.z_layer);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return guide;
}
@@ -471,7 +471,7 @@ DvzResult dvz_guide_line_set_value(DvzGuideLine* guide, double value)
guide->desc.value = value;
guide->dirty = true;
guide->version++;
_scene_notify_request_frame(guide->panel != NULL ? guide->panel->figure : NULL);
_scene_notify_invalidated(guide->panel != NULL ? guide->panel->figure : NULL);
return 0;
}
@@ -523,7 +523,7 @@ DvzGuideSpan* dvz_guide_span(DvzPanel* panel, const DvzGuideSpanDesc* desc)
_guide_attach_visual(panel, fill, DVZ_GENERATED_VISUAL_GUIDE_FILL, resolved.z_layer);
if (outline != NULL)
_guide_attach_visual(panel, outline, DVZ_GENERATED_VISUAL_GUIDE_OUTLINE, resolved.z_layer);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return span;
}
@@ -564,7 +564,7 @@ DvzResult dvz_guide_span_set_range(DvzGuideSpan* span, double min_value, double
span->desc.max_value = max_value;
span->dirty = true;
span->version++;
_scene_notify_request_frame(span->panel != NULL ? span->panel->figure : NULL);
_scene_notify_invalidated(span->panel != NULL ? span->panel->figure : NULL);
return 0;
}
@@ -144,7 +144,7 @@ void _scene_mark_legend_dirty(DvzLegend* legend)
return;
legend->dirty = true;
legend->version = legend->version == UINT64_MAX ? 1 : legend->version + 1;
_scene_notify_request_frame(legend->panel != NULL ? legend->panel->figure : NULL);
_scene_notify_invalidated(legend->panel != NULL ? legend->panel->figure : NULL);
}
@@ -1035,7 +1035,7 @@ DvzScaleBar* dvz_scale_bar(DvzPanel* panel, const DvzScaleBarDesc* desc)
annotation->scalebar_units_format = _scalebar_descriptor_units(annotation);
annotation->dirty_flags = DVZ_TEXT_DIRTY_ALL;
annotation->version = 1;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return (DvzScaleBar*)annotation;
}
@@ -1049,7 +1049,7 @@ DvzResult dvz_scale_bar_set_dimension(DvzScaleBar* scalebar, DvzDim dim)
annotation->scalebar.dimension = dim;
annotation->dirty_flags = DVZ_TEXT_DIRTY_ALL;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -1064,7 +1064,7 @@ DvzResult dvz_scale_bar_set_anchor(DvzScaleBar* scalebar, DvzSceneAnchor anchor)
anchor;
annotation->dirty_flags = DVZ_TEXT_DIRTY_ALL;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -1078,7 +1078,7 @@ DvzResult dvz_scale_bar_set_units(DvzScaleBar* scalebar, DvzUnits* units)
annotation->scalebar_units_format = units;
annotation->dirty_flags = DVZ_TEXT_DIRTY_ALL;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -1108,7 +1108,7 @@ DvzResult dvz_scale_bar_set_label_style(DvzScaleBar* scalebar, const DvzTextStyl
annotation->style = resolved;
annotation->dirty_flags |= DVZ_TEXT_DIRTY_STYLE | DVZ_TEXT_DIRTY_LAYOUT | DVZ_TEXT_DIRTY_RENDER;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -1127,7 +1127,7 @@ DvzResult dvz_scale_bar_set_placement(
annotation->dirty_flags |=
DVZ_TEXT_DIRTY_PLACEMENT | DVZ_TEXT_DIRTY_LAYOUT | DVZ_TEXT_DIRTY_RENDER;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
@@ -1144,6 +1144,6 @@ DvzResult dvz_scale_bar_set_format(DvzScaleBar* scalebar, const DvzFormatDesc* f
annotation->dirty_flags |=
DVZ_TEXT_DIRTY_STRING | DVZ_TEXT_DIRTY_LAYOUT | DVZ_TEXT_DIRTY_RENDER;
annotation->version++;
_scene_notify_request_frame(annotation->panel != NULL ? annotation->panel->figure : NULL);
_scene_notify_invalidated(annotation->panel != NULL ? annotation->panel->figure : NULL);
return 0;
}
+3 -3
View File
@@ -394,7 +394,7 @@ static void _text_mark_dirty(DvzText* text, uint32_t flags)
ANN(text);
text->dirty_flags |= flags;
text->version++;
_scene_notify_request_frame(text->panel != NULL ? text->panel->figure : NULL);
_scene_notify_invalidated(text->panel != NULL ? text->panel->figure : NULL);
}
@@ -428,7 +428,7 @@ DvzText* dvz_text(DvzPanel* panel, uint32_t flags)
text->flags = flags;
text->dirty_flags = DVZ_TEXT_DIRTY_ALL;
text->version = 1;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return text;
}
@@ -460,7 +460,7 @@ void dvz_text_destroy(DvzText* text)
dvz_visual_set_visible(text->visual, false);
}
_text_free_collection(text);
_scene_notify_request_frame(text->panel != NULL ? text->panel->figure : NULL);
_scene_notify_invalidated(text->panel != NULL ? text->panel->figure : NULL);
text->scene = NULL;
text->panel = NULL;
text->legacy_string = NULL;
@@ -198,7 +198,7 @@ static bool _text_sync_glyph_visual_attach(
attach->has_generated_role = source_has_role;
attach->generated_role = source_role;
if (changed)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return true;
}
if (dvz_panel_add_visual(panel, glyph_visual, desc) != 0)
+7 -7
View File
@@ -95,7 +95,7 @@
#define DVZ_SCENE_MAX_AXIS_TICKS 64
#define DVZ_SCENE_MAX_AXIS_TEXTS (DVZ_SCENE_MAX_AXIS_TICKS + 2)
#define DVZ_SCENE_MAX_AXIS_MINOR_TICKS 8
#define DVZ_SCENE_MAX_REQUEST_FRAME_SUBSCRIPTIONS 16
#define DVZ_SCENE_MAX_INVALIDATION_SUBSCRIPTIONS 16
#define DVZ_SCENE_MAX_CONTROLLERS 128
#define DVZ_SCENE_MAX_CONTROLLER_LINKS 128
#define DVZ_SCENE_MAX_AXIS_LINES \
@@ -375,9 +375,9 @@ typedef enum
} DvzGeneratedVisualRole;
typedef struct DvzTextBlock DvzTextBlock;
typedef void (*DvzSceneRequestFrameCallback)(DvzFigure* figure, void* user_data);
typedef void (*DvzSceneInvalidationCallback)(DvzFigure* figure, void* user_data);
typedef struct DvzSceneRequestFrameSubscription DvzSceneRequestFrameSubscription;
typedef struct DvzSceneInvalidationSubscription DvzSceneInvalidationSubscription;
typedef struct DvzPanelView2DResolved DvzPanelView2DResolved;
DvzId _scene_next_id(DvzScene* scene);
@@ -428,9 +428,9 @@ void _scene_panel_view3d_dirty(DvzPanel* panel);
/* Request frame subscriptions */
/*************************************************************************************************/
struct DvzSceneRequestFrameSubscription
struct DvzSceneInvalidationSubscription
{
DvzSceneRequestFrameCallback callback;
DvzSceneInvalidationCallback callback;
void* user_data;
bool active;
};
@@ -2220,8 +2220,8 @@ struct DvzScene
uint32_t query_scope_count;
DvzRequestFreshnessScope query_scopes[DVZ_SCENE_MAX_REQUEST_SCOPES];
DvzSampledField* text_bitmap_atlas;
DvzSceneRequestFrameSubscription
request_frame_subscriptions[DVZ_SCENE_MAX_REQUEST_FRAME_SUBSCRIPTIONS];
DvzSceneInvalidationSubscription
invalidation_subscriptions[DVZ_SCENE_MAX_INVALIDATION_SUBSCRIPTIONS];
struct
{
+3 -3
View File
@@ -269,7 +269,7 @@ static void _scene_notify_controller_figures(const DvzController* controller)
for (uint32_t pi = 0; pi < figure->panel_count; pi++)
bound = bound || _scene_panel_has_controller(&figure->panels[pi], controller);
if (bound)
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
}
@@ -327,7 +327,7 @@ static void _scene_controller_detach_from_panels(DvzController* controller)
}
for (uint32_t i = 0; i < affected_count; i++)
_scene_notify_request_frame(affected[i]);
_scene_notify_invalidated(affected[i]);
}
@@ -1664,7 +1664,7 @@ DvzResult dvz_panel_bind_controller(DvzPanel* panel, DvzController* controller,
default:
return -1;
}
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
@@ -324,7 +324,7 @@ static inline int _scene_panel_add_generated_visual(
slot->has_generated_role = true;
slot->generated_role = role;
if (changed)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
if (dvz_panel_add_visual(panel, visual, &attach) != 0)
+2 -2
View File
@@ -117,7 +117,7 @@ static void _scene_grid_mark_dirty(DvzGrid* grid)
return;
grid->dirty = true;
if (grid->figure != NULL)
_scene_notify_request_frame(grid->figure);
_scene_notify_invalidated(grid->figure);
}
@@ -254,7 +254,7 @@ void dvz_grid_destroy(DvzGrid* grid)
}
dvz_memset(grid, sizeof(DvzGrid), 0, sizeof(DvzGrid));
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
@@ -1001,7 +1001,7 @@ DvzOrientationGizmo* dvz_orientation_gizmo(
if (!_orientation_gizmo_sync_transform(gizmo))
goto fail;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return gizmo;
fail:
@@ -1028,7 +1028,7 @@ void dvz_orientation_gizmo_destroy(DvzOrientationGizmo* gizmo)
dvz_panel_destroy(gizmo->panel);
_orientation_gizmo_free_geometry(gizmo);
dvz_memset(gizmo, sizeof(DvzOrientationGizmo), 0, sizeof(DvzOrientationGizmo));
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
@@ -1050,7 +1050,7 @@ DvzResult dvz_orientation_gizmo_set_visible(DvzOrientationGizmo* gizmo, bool vis
if (gizmo->rings_visual != NULL)
(void)dvz_visual_set_visible(gizmo->rings_visual, visible && gizmo->desc.show_axes);
gizmo->version = gizmo->version == UINT64_MAX ? 1 : gizmo->version + 1;
_scene_notify_request_frame(gizmo->source_panel != NULL ? gizmo->source_panel->figure : NULL);
_scene_notify_invalidated(gizmo->source_panel != NULL ? gizmo->source_panel->figure : NULL);
return DVZ_OK;
}
@@ -369,7 +369,7 @@ void _panel_mark_layout_changed(DvzPanel* panel)
legend->dirty = true;
legend->version = legend->version == UINT64_MAX ? 1 : legend->version + 1;
}
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
}
+2 -2
View File
@@ -98,7 +98,7 @@ void _scene_panel_view_dirty(DvzPanel* panel)
_axis_mark_dirty(x_axis);
if (y_axis != NULL && y_axis->panel != NULL)
_axis_mark_dirty(y_axis);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
}
@@ -107,7 +107,7 @@ void _scene_panel_view3d_dirty(DvzPanel* panel)
if (panel == NULL)
return;
panel->view3d_revision = _panel_view_next_revision(panel->view3d_revision);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
}
@@ -476,7 +476,7 @@ DvzReferenceGrid* dvz_reference_grid(DvzPanel* panel, const DvzReferenceGridDesc
if (dvz_panel_add_visual(panel, grid->visual, &attach) != 0)
goto fail;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return grid;
fail:
@@ -498,7 +498,7 @@ void dvz_reference_grid_destroy(DvzReferenceGrid* grid)
if (grid->visual != NULL)
dvz_visual_set_visible(grid->visual, false);
dvz_memset(grid, sizeof(DvzReferenceGrid), 0, sizeof(DvzReferenceGrid));
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
@@ -518,6 +518,6 @@ DvzResult dvz_reference_grid_set_visible(DvzReferenceGrid* grid, bool visible)
if (grid->visual != NULL)
(void)dvz_visual_set_visible(grid->visual, visible);
grid->version = grid->version == UINT64_MAX ? 1 : grid->version + 1;
_scene_notify_request_frame(grid->panel != NULL ? grid->panel->figure : NULL);
_scene_notify_invalidated(grid->panel != NULL ? grid->panel->figure : NULL);
return DVZ_OK;
}
+10 -10
View File
@@ -556,7 +556,7 @@ DvzResult dvz_figure_resize(DvzFigure* figure, uint32_t width, uint32_t height)
dvz_camera_resize(panel->camera, panel_width, panel_height);
}
}
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
return DVZ_OK;
}
@@ -596,7 +596,7 @@ DvzResult dvz_figure_set_reserve(DvzFigure* figure, const DvzPanelReserve* reser
figure->reserve = next;
(void)_scene_figure_resolve_layouts(figure);
(void)_scene_figure_resolve_panel_descs(figure);
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
return DVZ_OK;
}
@@ -666,7 +666,7 @@ DvzResult dvz_figure_set_color_pipeline(DvzFigure* figure, DvzColorPipeline pipe
if (figure->color_pipeline == pipeline)
return DVZ_OK;
figure->color_pipeline = pipeline;
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
return DVZ_OK;
}
@@ -883,7 +883,7 @@ DvzResult dvz_panel_set_edl(DvzPanel* panel, const DvzEdlDesc* desc)
ANN(panel);
bool ok = _scene_technique_state_set_edl(&panel->techniques, desc);
if (ok)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return ok ? DVZ_OK : DVZ_ERROR;
}
@@ -900,7 +900,7 @@ DvzResult dvz_panel_set_msaa(DvzPanel* panel, const DvzMsaaDesc* desc)
ANN(panel);
bool ok = _scene_technique_state_set_msaa(&panel->techniques, desc);
if (ok)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return ok ? DVZ_OK : DVZ_ERROR;
}
@@ -917,7 +917,7 @@ DvzResult dvz_panel_set_ssao(DvzPanel* panel, const DvzSsaoDesc* desc)
ANN(panel);
bool ok = _scene_technique_state_set_ssao(&panel->techniques, desc);
if (ok)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return ok ? DVZ_OK : DVZ_ERROR;
}
@@ -940,7 +940,7 @@ DvzResult dvz_panel_set_scene_occlusion(DvzPanel* panel, const DvzSceneOcclusion
dvz_memset(
&panel->scene_occlusion, sizeof(DvzSceneOcclusionDesc), 0,
sizeof(DvzSceneOcclusionDesc));
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
@@ -953,7 +953,7 @@ DvzResult dvz_panel_set_scene_occlusion(DvzPanel* panel, const DvzSceneOcclusion
if (panel->scene_occlusion.hidden_alpha > 1.0f)
panel->scene_occlusion.hidden_alpha = 1.0f;
panel->scene_occlusion_enabled = true;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
@@ -979,7 +979,7 @@ DvzResult dvz_panel_set_volume_occluder(
dvz_memset(
&panel->volume_occlusion, sizeof(DvzVolumeOcclusionDesc), 0,
sizeof(DvzVolumeOcclusionDesc));
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
if (volume->type != DVZ_VISUAL_TYPE_VOLUME)
@@ -1007,7 +1007,7 @@ DvzResult dvz_panel_set_volume_occluder(
if (panel->volume_occlusion.occluded_alpha <= 0.0f)
panel->volume_occlusion.occluded_alpha = 0.20f;
panel->volume_occlusion_enabled = true;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
+17 -17
View File
@@ -35,16 +35,16 @@
* @param user_data opaque pointer forwarded to the callback
* @return true on success, false when the subscription table is full or input is invalid
*/
bool _scene_add_request_frame_callback(
DvzScene* scene, DvzSceneRequestFrameCallback callback, void* user_data)
bool _scene_add_invalidation_callback(
DvzScene* scene, DvzSceneInvalidationCallback callback, void* user_data)
{
if (scene == NULL || callback == NULL)
return false;
DvzSceneRequestFrameSubscription* free_slot = NULL;
for (uint32_t i = 0; i < DVZ_SCENE_MAX_REQUEST_FRAME_SUBSCRIPTIONS; i++)
DvzSceneInvalidationSubscription* free_slot = NULL;
for (uint32_t i = 0; i < DVZ_SCENE_MAX_INVALIDATION_SUBSCRIPTIONS; i++)
{
DvzSceneRequestFrameSubscription* sub = &scene->request_frame_subscriptions[i];
DvzSceneInvalidationSubscription* sub = &scene->invalidation_subscriptions[i];
if (sub->active && sub->callback == callback && sub->user_data == user_data)
return true;
if (!sub->active && free_slot == NULL)
@@ -71,19 +71,19 @@ bool _scene_add_request_frame_callback(
* @param callback callback pointer
* @param user_data opaque pointer previously registered with the callback
*/
void _scene_remove_request_frame_callback(
DvzScene* scene, DvzSceneRequestFrameCallback callback, void* user_data)
void _scene_remove_invalidation_callback(
DvzScene* scene, DvzSceneInvalidationCallback callback, void* user_data)
{
if (scene == NULL || callback == NULL)
return;
for (uint32_t i = 0; i < DVZ_SCENE_MAX_REQUEST_FRAME_SUBSCRIPTIONS; i++)
for (uint32_t i = 0; i < DVZ_SCENE_MAX_INVALIDATION_SUBSCRIPTIONS; i++)
{
DvzSceneRequestFrameSubscription* sub = &scene->request_frame_subscriptions[i];
DvzSceneInvalidationSubscription* sub = &scene->invalidation_subscriptions[i];
if (sub->active && sub->callback == callback && sub->user_data == user_data)
{
dvz_memset(sub, sizeof(DvzSceneRequestFrameSubscription), 0,
sizeof(DvzSceneRequestFrameSubscription));
dvz_memset(sub, sizeof(DvzSceneInvalidationSubscription), 0,
sizeof(DvzSceneInvalidationSubscription));
return;
}
}
@@ -95,16 +95,16 @@ void _scene_remove_request_frame_callback(
*
* @param figure figure requesting a frame
*/
void _scene_notify_request_frame(DvzFigure* figure)
void _scene_notify_invalidated(DvzFigure* figure)
{
if (figure == NULL || figure->scene == NULL)
return;
figure->frame_revision = figure->frame_revision == UINT64_MAX ? 1 : figure->frame_revision + 1;
DvzScene* scene = figure->scene;
for (uint32_t i = 0; i < DVZ_SCENE_MAX_REQUEST_FRAME_SUBSCRIPTIONS; i++)
for (uint32_t i = 0; i < DVZ_SCENE_MAX_INVALIDATION_SUBSCRIPTIONS; i++)
{
const DvzSceneRequestFrameSubscription* sub = &scene->request_frame_subscriptions[i];
DvzSceneRequestFrameCallback callback = sub->callback;
const DvzSceneInvalidationSubscription* sub = &scene->invalidation_subscriptions[i];
DvzSceneInvalidationCallback callback = sub->callback;
void* user_data = sub->user_data;
if (sub->active && callback != NULL)
callback(figure, user_data);
@@ -139,7 +139,7 @@ void _scene_notify_visual_changed(DvzVisual* visual)
}
}
if (contains_visual)
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
}
@@ -207,6 +207,6 @@ void _scene_notify_buffer_changed(DvzSceneBuffer* buffer)
}
}
if (uses_buffer)
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
}
@@ -12,13 +12,13 @@
#include "_scene.h"
bool _scene_add_request_frame_callback(
DvzScene* scene, DvzSceneRequestFrameCallback callback, void* user_data);
bool _scene_add_invalidation_callback(
DvzScene* scene, DvzSceneInvalidationCallback callback, void* user_data);
void _scene_remove_request_frame_callback(
DvzScene* scene, DvzSceneRequestFrameCallback callback, void* user_data);
void _scene_remove_invalidation_callback(
DvzScene* scene, DvzSceneInvalidationCallback callback, void* user_data);
void _scene_notify_request_frame(DvzFigure* figure);
void _scene_notify_invalidated(DvzFigure* figure);
void _scene_notify_visual_changed(DvzVisual* visual);
+3 -3
View File
@@ -45,7 +45,7 @@ static void _scene_compute_notify(DvzSceneCompute* compute)
{
if (figure->computes[j] == compute)
{
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
break;
}
}
@@ -307,7 +307,7 @@ DvzResult dvz_figure_add_compute(DvzFigure* figure, DvzSceneCompute* compute)
return DVZ_ERROR;
}
figure->computes[figure->compute_count++] = compute;
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
return DVZ_OK;
}
@@ -323,7 +323,7 @@ DvzResult dvz_figure_remove_compute(DvzFigure* figure, DvzSceneCompute* compute)
for (uint32_t j = i + 1; j < figure->compute_count; j++)
figure->computes[j - 1] = figure->computes[j];
figure->compute_count--;
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
return DVZ_OK;
}
return DVZ_OK;
@@ -911,7 +911,7 @@ DvzResult dvz_panel_add_composite(
slot->insertion_index = panel->visual_count;
panel->visual_count++;
}
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
+13 -13
View File
@@ -891,7 +891,7 @@ static void _selection_card_hide(DvzSelection* selection)
selection->card_query = (DvzQueryResult){0};
selection->card.text[0] = '\0';
selection->card.dirty = true;
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
@@ -923,7 +923,7 @@ static void _selection_card_update_from_query(DvzSelection* selection, const Dvz
selection->card.anchor_px[1] = (float)query->panel_position[1];
_selection_card_refresh_text(selection, query);
selection->card.dirty = true;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
}
@@ -2711,7 +2711,7 @@ DvzOverlayCard* dvz_overlay_card(DvzOverlay* overlay, const DvzOverlayCardDesc*
card->card.offset_px[1] = resolved.offset_px[1];
card->card.visible = (card->flags & DVZ_OVERLAY_CARD_HIDDEN) == 0;
card->card.dirty = true;
_scene_notify_request_frame(overlay->panel->figure);
_scene_notify_invalidated(overlay->panel->figure);
return card;
}
@@ -2739,7 +2739,7 @@ void dvz_overlay_card_destroy(DvzOverlayCard* card)
card->card.dirty = false;
card->rich_enabled = false;
card->rich_dirty = false;
_scene_notify_request_frame(figure);
_scene_notify_invalidated(figure);
}
@@ -2760,7 +2760,7 @@ DvzResult dvz_overlay_card_set_style(DvzOverlayCard* card, const DvzOverlayCardS
DvzOverlayCardStyle resolved = style != NULL ? *style : dvz_overlay_card_style();
if (_scene_card_apply_style(&card->card, &resolved) != 0)
return -1;
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return 0;
}
@@ -2788,7 +2788,7 @@ DvzResult dvz_overlay_card_set_text(DvzOverlayCard* card, const char* text)
else
card->card.text[0] = '\0';
card->card.dirty = true;
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return DVZ_OK;
}
@@ -2837,7 +2837,7 @@ DvzResult dvz_overlay_card_set_rich_text(DvzOverlayCard* card, const DvzOverlayR
card->rich_dirty = true;
card->card.content = DVZ_SCENE_CARD_CONTENT_IMAGE;
card->card.dirty = true;
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return 0;
}
@@ -2859,7 +2859,7 @@ DvzResult dvz_overlay_card_clear_rich_text(DvzOverlayCard* card)
card->card.content_size_px[0] = 0.0f;
card->card.content_size_px[1] = 0.0f;
card->card.dirty = true;
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return DVZ_OK;
}
@@ -2889,7 +2889,7 @@ DvzResult dvz_overlay_card_set_layout(
card->card.offset_px[1] = offset_px[1];
}
card->card.dirty = true;
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return DVZ_OK;
}
@@ -2914,7 +2914,7 @@ DvzResult dvz_overlay_card_set_placement(
card->card.offset_px[1] = offset_px[1];
}
card->card.dirty = true;
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return DVZ_OK;
}
@@ -2937,7 +2937,7 @@ DvzResult dvz_overlay_card_set_visible(DvzOverlayCard* card, bool visible)
_scene_card_hide(&card->card);
_overlay_card_hide_rich(card);
}
_scene_notify_request_frame(card->panel != NULL ? card->panel->figure : NULL);
_scene_notify_invalidated(card->panel != NULL ? card->panel->figure : NULL);
return DVZ_OK;
}
@@ -2982,7 +2982,7 @@ DvzPinnedReadout* dvz_pinned_readout_query(DvzPanel* panel, const DvzQueryResult
readout->card.anchor_px[0] = (float)query->panel_position[0];
readout->card.anchor_px[1] = (float)query->panel_position[1];
panel->pinned_readouts[panel->pinned_readout_count++] = readout;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return readout;
}
@@ -3044,6 +3044,6 @@ DvzResult dvz_pinned_readout_set_format(DvzPinnedReadout* readout, const DvzForm
_readout_refresh_text(readout);
dvz_strlcpy(readout->card.text, readout->text, sizeof(readout->card.text));
readout->card.dirty = true;
_scene_notify_request_frame(readout->panel != NULL ? readout->panel->figure : NULL);
_scene_notify_invalidated(readout->panel != NULL ? readout->panel->figure : NULL);
return DVZ_OK;
}
+4 -4
View File
@@ -535,7 +535,7 @@ DvzBand* dvz_band(DvzPanel* panel, const DvzBandDesc* desc)
_band_attach_visual(panel, line, resolved.z_layer + 1);
if (bounds != NULL)
_band_attach_visual(panel, bounds, resolved.z_layer + 2);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return band;
}
@@ -574,7 +574,7 @@ DvzResult dvz_band_set_bounds(
band->count = count;
band->dirty = true;
band->version++;
_scene_notify_request_frame(band->panel != NULL ? band->panel->figure : NULL);
_scene_notify_invalidated(band->panel != NULL ? band->panel->figure : NULL);
return 0;
}
@@ -608,7 +608,7 @@ DvzResult dvz_band_set_center(DvzBand* band, const double* x, const double* y, u
band->has_center = count > 0;
band->dirty = true;
band->version++;
_scene_notify_request_frame(band->panel != NULL ? band->panel->figure : NULL);
_scene_notify_invalidated(band->panel != NULL ? band->panel->figure : NULL);
return 0;
}
@@ -661,7 +661,7 @@ DvzResult dvz_band_set_style(DvzBand* band, const DvzBandDesc* desc)
band->desc = resolved;
band->dirty = true;
band->version++;
_scene_notify_request_frame(band->panel != NULL ? band->panel->figure : NULL);
_scene_notify_invalidated(band->panel != NULL ? band->panel->figure : NULL);
return 0;
}
+3 -3
View File
@@ -416,7 +416,7 @@ DvzBars* dvz_bars(DvzPanel* panel, const DvzBarsDesc* desc)
_bars_attach_visual(panel, fill, resolved.z_layer);
if (outline != NULL)
_bars_attach_visual(panel, outline, resolved.z_layer + 1);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return bars;
}
@@ -460,7 +460,7 @@ DvzResult dvz_bars_set_intervals(
bars->count = count;
bars->dirty = true;
bars->version++;
_scene_notify_request_frame(bars->panel != NULL ? bars->panel->figure : NULL);
_scene_notify_invalidated(bars->panel != NULL ? bars->panel->figure : NULL);
return 0;
}
@@ -495,7 +495,7 @@ DvzResult dvz_bars_set_style(DvzBars* bars, const DvzBarsDesc* desc)
bars->desc = resolved;
bars->dirty = true;
bars->version++;
_scene_notify_request_frame(bars->panel != NULL ? bars->panel->figure : NULL);
_scene_notify_invalidated(bars->panel != NULL ? bars->panel->figure : NULL);
return 0;
}
+1 -1
View File
@@ -290,7 +290,7 @@ DvzResult dvz_panel_query_px(DvzPanel* panel, double x, double y, const DvzQuery
pending->freshness_serial = _scene_next_request_serial(scene);
pending->request = local;
_query_track_request_serial(scene, panel, local.request_id, pending->freshness_serial);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
@@ -1718,7 +1718,7 @@ int test_controller_destroy_detaches_panels_links_and_reuses_slot(
AT(link->active);
ControllerDestroyRequestProbe probe = {0};
AT(_scene_add_request_frame_callback(
AT(_scene_add_invalidation_callback(
scene, _controller_destroy_request_frame_callback, &probe));
dvz_controller_destroy(source);
@@ -1745,7 +1745,7 @@ int test_controller_destroy_detaches_panels_links_and_reuses_slot(
scene, reused, target, DVZ_CONTROLLER_LINK_ROTATION, DVZ_CONTROLLER_LINK_ONE_WAY);
AT(reused_link == link);
_scene_remove_request_frame_callback(scene, _controller_destroy_request_frame_callback, &probe);
_scene_remove_invalidation_callback(scene, _controller_destroy_request_frame_callback, &probe);
dvz_scene_destroy(scene);
return 0;
}
@@ -579,7 +579,7 @@ int test_scene_grid_destroy_detaches_panels_and_reuses_slot(TstContext* suite, c
AT(grid->panel_count == 2);
GridDestroyRequestProbe probe = {0};
AT(_scene_add_request_frame_callback(scene, _grid_destroy_request_frame_callback, &probe));
AT(_scene_add_invalidation_callback(scene, _grid_destroy_request_frame_callback, &probe));
dvz_grid_destroy(grid);
AT(probe.calls == 1);
@@ -598,7 +598,7 @@ int test_scene_grid_destroy_detaches_panels_and_reuses_slot(TstContext* suite, c
AT(reused->cols == 1);
AT(reused->panel_count == 0);
_scene_remove_request_frame_callback(scene, _grid_destroy_request_frame_callback, &probe);
_scene_remove_invalidation_callback(scene, _grid_destroy_request_frame_callback, &probe);
dvz_scene_destroy(scene);
return 0;
}
@@ -901,7 +901,7 @@ int test_scene_figure_destroy_cascades_and_reuses_slot(TstContext* suite, const
AT(figure->compute_count == 1);
GridDestroyRequestProbe probe = {0};
AT(_scene_add_request_frame_callback(scene, _grid_destroy_request_frame_callback, &probe));
AT(_scene_add_invalidation_callback(scene, _grid_destroy_request_frame_callback, &probe));
dvz_figure_destroy(figure);
AT(probe.calls == 0);
@@ -931,7 +931,7 @@ int test_scene_figure_destroy_cascades_and_reuses_slot(TstContext* suite, const
AT(dvz_panel_bind_controller(reused_panel, controller, DVZ_DIM_MASK_XY) == 0);
AT(dvz_figure_add_compute(reused, compute) == DVZ_OK);
_scene_remove_request_frame_callback(scene, _grid_destroy_request_frame_callback, &probe);
_scene_remove_invalidation_callback(scene, _grid_destroy_request_frame_callback, &probe);
dvz_scene_destroy(scene);
return 0;
}
+1 -1
View File
@@ -991,7 +991,7 @@ DvzResult dvz_panel_set_bounds_visible(DvzPanel* panel, bool visible)
dvz_visual_set_visible(panel->bounds_visual, visible);
if (panel->bounds_occluded_visual != NULL)
dvz_visual_set_visible(panel->bounds_occluded_visual, visible);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
+4 -4
View File
@@ -717,7 +717,7 @@ static void _panel_background_detach(DvzPanel* panel)
panel->background_type = DVZ_PANEL_BACKGROUND_NONE;
panel->background = dvz_panel_background_desc();
if (panel->figure != NULL)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
}
@@ -751,7 +751,7 @@ static void _panel_border_detach(DvzPanel* panel)
panel->border_visual = NULL;
panel->border.visible = false;
if (panel->figure != NULL)
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
}
@@ -960,7 +960,7 @@ DvzResult dvz_panel_add_visual(DvzPanel* panel, DvzVisual* visual, const DvzVisu
slot->viewport_rect = resolved.viewport_rect;
slot->insertion_index = panel->visual_count;
panel->visual_count++;
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return 0;
}
@@ -1279,7 +1279,7 @@ DvzResult dvz_panel_set_border(DvzPanel* panel, const DvzPanelBorderDesc* border
panel->border = *border;
dvz_visual_set_visible(panel->border_visual, true);
_scene_notify_request_frame(panel->figure);
_scene_notify_invalidated(panel->figure);
return DVZ_OK;
}