改结构

This commit is contained in:
2026-08-29 00:38:34 +08:00
parent 314a14176c
commit e61542092e
63 changed files with 953 additions and 470 deletions
-3
View File
@@ -45,9 +45,6 @@ struct Linear_Color {
Coordinate_3D alpha{1.0F}; /* 线性不透明度通道,范围为 0 到 1。 */
bool operator==(const Linear_Color&) const = default;
};
struct Visual_Settings {
bool operator==(const Visual_Settings&) const = default;
};
namespace detail {
[[nodiscard]] bool finite(Coordinate_3D value) noexcept;
[[nodiscard]] bool finite(Vec2 value) noexcept;
@@ -10,22 +10,6 @@
#include <ranges>
#include <vector>
namespace aethera::render_3d::detail {
/* Scene 到 Backend 的唯一异构边界;Visual 对象内部不保存此信封。 */
struct Prepared_Visual {
Matrix4 transform{};
bool visible{true};
bool depth_test{true};
std::uint64_t revision{};
std::uint64_t data_revision{};
const Datoviz_Visual_Operations* operations{}; /* 可空借用;未发布 payload 时为空。 */
std::shared_ptr<const void> data{}; /* 具体 Visual Prepared_Data 的只读所有权。 */
};
template <typename Spec>
Prepared_Visual erase_prepared_visual(
const Prepared_Visual_For<typename Spec::Prepared_Data>& source) {
return {source.transform, source.visible, source.depth_test, source.revision,
source.data_revision, &Spec::datoviz_operations(), source.data};
}
using Visual_Identity = std::uintptr_t;
struct Visual_Registration {
Visual_Identity identity{}; /* Scene 内稳定对象身份,仅供后端匹配原生 Visual。 */
@@ -205,15 +205,12 @@ typename Render_Scene_3D::Builder<Object>::Final_Builder& Render_Scene_3D::Build
&Visual_Object::Attached_Object::Specification::datoviz_operations(),
[](not_null<Root*> root, std::shared_ptr<void> context) {
using Definition = typename Visual_Object::Attached_Object;
using Prepared = typename Definition::Prepared_Visual;
const not_null object{static_cast<Visual_Object*>(root.get())};
Base::private_access(object.get()).template get<typename Definition::Base_Tag>()
.bind_paint_target(std::move(context), [](void* raw_context, const Root* identity,
const Prepared& prepared) {
const detail::Prepared_Visual& prepared) {
auto& submission = *static_cast<detail::Scene_Paint_Context<Object>*>(raw_context);
const auto visual_identity = reinterpret_cast<detail::Visual_Identity>(identity);
auto erased = detail::erase_prepared_visual<
typename Definition::Specification>(prepared);
auto& visuals = *submission.visuals;
const auto found = std::ranges::find(
visuals, visual_identity,
@@ -223,7 +220,7 @@ typename Render_Scene_3D::Builder<Object>::Final_Builder& Render_Scene_3D::Build
"3D Visual published outside its Scene registration");
/* Builder 已经为每个 Visual 建立稳定槽位。并行 Submit 节点只写各自
* 的 visual 成员,不扩容容器,也不互相读写同一对象。 */
found->visual = std::move(erased);
found->visual = prepared;
});
}
};
@@ -392,7 +389,7 @@ void Render_Scene_3D::Private::render_datoviz(
std::optional<detail::Datoviz_Pending_Frame> prepared;
if (context->events.empty()) {
frame->mark(Frame_Trace_Marker::backend_prepare_started);
prepared = try_prepare_reused(
prepared = reuse_recorded_target(
scene, *visuals, sequence, observe, readback);
}
if (!prepared) {
@@ -3,8 +3,8 @@
#include "../base/Datoviz_Frame_Observation.hpp"
#include "../detail/Backend_Types.hpp"
#include "detail/Datoviz_Frame_State.hpp"
#include "detail/Datoviz_Scene_Components.hpp"
#include "detail/Datoviz_Visual_State.hpp"
#include <plot/Axis.hpp>
#include <render_common.hpp>
#include <ownership.hpp>
#include <array>
@@ -42,10 +42,7 @@ namespace aethera { namespace render_3d { namespace detail {
struct Datoviz_Render_Context;
struct Scene_Datoviz_State : private Datoviz_Native_Scene,
private Datoviz_Visual_Set,
private Datoviz_Interaction_State,
private Datoviz_Frame_Pipeline {
struct Scene_Datoviz_State {
Scene_Datoviz_State();
~Scene_Datoviz_State();
Scene_Datoviz_State(const Scene_Datoviz_State&) = delete;
@@ -93,10 +90,6 @@ struct Scene_Datoviz_State : private Datoviz_Native_Scene,
const Scene_3D_Parameters& scene,
const Prepared_Visual_Batch& visuals,
std::uint64_t frame_sequence, bool observe, bool readback);
[[nodiscard]] std::optional<Datoviz_Pending_Frame> try_prepare_reused(
const Scene_3D_Parameters& scene,
const Prepared_Visual_Batch& visuals,
std::uint64_t frame_sequence, bool observe, bool readback);
[[nodiscard]] DvzSceneFrameArtifact* emit(
const Scene_3D_Parameters& scene, std::uint8_t target_index);
[[nodiscard]] std::optional<std::uint8_t> acquire_target(Extent extent);
@@ -120,6 +113,41 @@ struct Scene_Datoviz_State : private Datoviz_Native_Scene,
void abandon_resources() noexcept;
void destroy();
private:
struct Runtime_Slot {
owner<DvzDrp2Runtime*> runtime{}; /* 本目标槽独占的 DRP2 Runtime。 */
owner<DvzFramePlanEmitter*> emitter{}; /* 本目标槽独占的帧计划 emitter。 */
};
std::shared_ptr<Datoviz_Render_Context> render_context_{}; /* 本 Scene 独占 GPU 上下文的共享销毁闸门。 */
std::uintptr_t command_pool_{}; /* 本 Scene 独占 Vulkan Command Pool。 */
std::uintptr_t descriptor_pool_{}; /* 本 Scene 独占 Vulkan Descriptor Pool。 */
std::array<Runtime_Slot, 3> runtime_slots_{}; /* 三个固定目标槽的 Runtime。 */
owner<DvzScene*> scene_{}; /* Datoviz Scene 所有权。 */
DvzFigure* figure_{}; /* 可空、非拥有;由 scene_ 拥有。 */
DvzPanel* panel_{}; /* 可空、非拥有;由 scene_ 拥有。 */
DvzVisual* axes_visual_{}; /* 可空、非拥有;由 scene_ 拥有。 */
DvzText* axes_text_{}; /* 可空、非拥有;由 scene_ 拥有。 */
Extent figure_extent_{}; /* 当前原生 Figure 尺寸。 */
std::uint64_t command_revision_{1}; /* 原生命令结构版本。 */
std::optional<Camera_Descriptor> applied_camera_{}; /* 已应用相机,仅用于结构复用判定。 */
std::optional<std::array<plot::Axis_Descriptor, 3>> applied_axes_{}; /* 已应用坐标轴。 */
std::atomic_bool quarantined_{}; /* Unknown Failure 后是否禁止销毁在途资源。 */
std::vector<Datoviz_Visual_Instance> visuals_{}; /* 注册顺序稳定的具体 Visual 集合。 */
std::vector<owner<DvzBuffer*>> external_buffers_{}; /* Visual 三槽属性 Buffer 唯一所有权。 */
owner<DvzItemInteraction*> item_interaction_{}; /* item query 能力所有权。 */
owner<DvzPinnedReadout*> hover_readout_{}; /* 当前 hover readout 所有权。 */
owner<DvzController*> camera_controller_{}; /* 相机控制器所有权。 */
owner<DvzInputRouter*> input_router_{}; /* 输入路由所有权。 */
owner<DvzPointerGestureHandler*> gesture_handler_{}; /* 指针手势处理器所有权。 */
std::uint64_t controller_revision_{1}; /* 已应用控制器输入版本。 */
bool input_changed_{}; /* 当前录制周期是否消费了输入。 */
mutable std::mutex target_mutex_{}; /* 仅保护三槽在提交与回读边界的生命周期。 */
std::unique_ptr<Datoviz_Frame_Target_Set> targets_{}; /* 三槽唯一所有权。 */
std::uint64_t target_generation_{}; /* 目标槽重建代数。 */
};
}}} // namespace aethera::render_3d::detail
@@ -1,6 +1,3 @@
Datoviz_Frame_Pipeline::Datoviz_Frame_Pipeline() :
targets_(std::make_unique<Datoviz_Frame_Target_Set>()) {}
DvzSceneFrameArtifact* Scene_Datoviz_State::emit(
const Scene_3D_Parameters& scene, std::uint8_t target_index) {
if (target_index >= runtime_slots_.size() ||
@@ -118,19 +115,12 @@ std::optional<Datoviz_Pending_Frame> Scene_Datoviz_State::reuse_recorded_target(
return Datoviz_Pending_Frame{
reinterpret_cast<std::uintptr_t>(candidate->device()),
reinterpret_cast<std::uintptr_t>(candidate->fence()), scene.viewport,
frame_sequence, target_index, candidate->generation(),
target_index, candidate->generation(),
std::move(observation)
};
}
return std::nullopt;
}
std::optional<Datoviz_Pending_Frame> Scene_Datoviz_State::try_prepare_reused(
const Scene_3D_Parameters& scene, const Prepared_Visual_Batch& visuals,
std::uint64_t frame_sequence, bool observe, bool readback) {
if (scene.viewport.empty()) return std::nullopt;
return reuse_recorded_target(
scene, visuals, frame_sequence, observe, readback);
}
std::optional<Datoviz_Pending_Frame> Scene_Datoviz_State::prepare(
const Scene_3D_Parameters& scene, const Prepared_Visual_Batch& visuals,
std::uint64_t frame_sequence, bool observe, bool readback) {
@@ -279,7 +269,7 @@ std::optional<Datoviz_Pending_Frame> Scene_Datoviz_State::prepare(
return Datoviz_Pending_Frame{
reinterpret_cast<std::uintptr_t>(frame_target.device()),
reinterpret_cast<std::uintptr_t>(frame_target.fence()), scene.viewport,
frame_sequence, *target_index, frame_target.generation(),
*target_index, frame_target.generation(),
std::move(observation)
};
}
@@ -372,7 +362,7 @@ std::optional<Datoviz_Pending_Frame> Scene_Datoviz_State::prepare(
return Datoviz_Pending_Frame{
reinterpret_cast<std::uintptr_t>(frame_target.device()),
reinterpret_cast<std::uintptr_t>(frame_target.fence()), scene.viewport,
frame_sequence, *target_index, frame_target.generation(),
*target_index, frame_target.generation(),
std::move(observation)
};
}
@@ -12,7 +12,6 @@ struct Datoviz_Pending_Frame {
std::uintptr_t device{}; /* Datoviz Device 的非拥有句柄值。 */
std::uintptr_t fence{}; /* 本帧完成 Fence 的非拥有句柄值。 */
Extent extent{}; /* 本帧离屏目标尺寸。 */
std::uint64_t sequence{}; /* 调用方 Frame 的物理序号。 */
std::uint8_t target_index{}; /* Scene 三槽中的目标下标。 */
std::uint64_t target_generation{}; /* 防止槽重建后的陈旧引用命中。 */
Datoviz_Frame_Observation observation{}; /* 直接附加到本帧的后端事实。 */
@@ -332,9 +332,6 @@ public:
[[nodiscard]] bool available() const noexcept {
return !quarantined_ && !recording_ && !prepared_ && !in_flight_;
}
[[nodiscard]] bool quiescent() const noexcept {
return !recording_ && !prepared_ && !in_flight_;
}
void abort() noexcept {
if (recording_ && commands_ != nullptr) dvz_cmd_reset(commands_);
recording_ = false;
@@ -354,7 +351,6 @@ private:
}
void initialize_timestamps(not_null<DvzDevice*> device,
not_null<DvzQueue*> queue) noexcept {
timestamps_initialized_ = true;
if (vkGetPhysicalDeviceQueueFamilyProperties == nullptr ||
vkGetPhysicalDeviceProperties == nullptr ||
vkCreateQueryPool == nullptr ||
@@ -490,7 +486,6 @@ private:
bool in_flight_{};
bool observing_{};
bool readback_requested_{};
bool timestamps_initialized_{};
bool timestamps_supported_{};
std::uint64_t recording_command_revision_{};
std::uint64_t recorded_command_revision_{};
@@ -1,77 +0,0 @@
#pragma once
#include "Datoviz_Frame_State.hpp"
#include "Datoviz_Visual_State.hpp"
#include <plot/Axis.hpp>
#include <ownership.hpp>
#include <array>
#include <atomic>
#include <cstdint>
#include <memory>
#include <mutex>
#include <optional>
#include <vector>
struct DvzBuffer;
struct DvzController;
struct DvzDrp2Runtime;
struct DvzFigure;
struct DvzFramePlanEmitter;
struct DvzInputRouter;
struct DvzItemInteraction;
struct DvzPanel;
struct DvzPinnedReadout;
struct DvzPointerGestureHandler;
struct DvzScene;
struct DvzText;
struct DvzVisual;
namespace aethera::render_3d::detail {
struct Datoviz_Render_Context;
struct Datoviz_Runtime_Slot {
owner<DvzDrp2Runtime*> runtime{}; /* 本目标槽独占的 DRP2 Runtime。 */
owner<DvzFramePlanEmitter*> emitter{}; /* 本目标槽独占的帧计划 emitter。 */
};
struct Datoviz_Native_Scene {
std::shared_ptr<Datoviz_Render_Context> render_context_{}; /* 本 Scene 独占 GPU 上下文的共享销毁门闩。 */
std::uintptr_t command_pool_{}; /* 本 Scene 独占 Vulkan Command Pool。 */
std::uintptr_t descriptor_pool_{}; /* 本 Scene 独占 Vulkan Descriptor Pool。 */
std::array<Datoviz_Runtime_Slot, 3> runtime_slots_{}; /* 三个固定目标槽的 Runtime。 */
owner<DvzScene*> scene_{}; /* Datoviz Scene 所有权。 */
DvzFigure* figure_{}; /* 可空、非拥有;由 scene_ 拥有。 */
DvzPanel* panel_{}; /* 可空、非拥有;由 scene_ 拥有。 */
DvzVisual* axes_visual_{}; /* 可空、非拥有;由 scene_ 拥有。 */
DvzText* axes_text_{}; /* 可空、非拥有;由 scene_ 拥有。 */
Extent figure_extent_{}; /* 当前原生 Figure 尺寸。 */
std::uint64_t command_revision_{1}; /* 原生命令结构版本。 */
std::optional<Camera_Descriptor> applied_camera_{}; /* 已应用相机,仅用于结构复用判定。 */
std::optional<std::array<plot::Axis_Descriptor, 3>> applied_axes_{}; /* 已应用坐标轴。 */
std::atomic_bool quarantined_{}; /* Unknown Failure 后是否禁止销毁在途资源。 */
};
struct Datoviz_Visual_Set {
std::vector<Datoviz_Visual_Instance> visuals_{}; /* 注册顺序稳定的具体 Visual 集合。 */
std::vector<owner<DvzBuffer*>> external_buffers_{}; /* Visual 三槽属性 Buffer 所有权。 */
};
struct Datoviz_Interaction_State {
owner<DvzItemInteraction*> item_interaction_{}; /* item query 能力所有权。 */
owner<DvzPinnedReadout*> hover_readout_{}; /* 当前 hover readout 所有权。 */
owner<DvzController*> camera_controller_{}; /* 相机控制器所有权。 */
owner<DvzInputRouter*> input_router_{}; /* 输入路由所有权。 */
owner<DvzPointerGestureHandler*> gesture_handler_{}; /* 指针手势处理器所有权。 */
std::uint64_t controller_revision_{1}; /* 已应用控制器输入版本。 */
bool input_changed_{}; /* 当前录制周期是否消费了输入。 */
};
struct Datoviz_Frame_Pipeline {
Datoviz_Frame_Pipeline();
mutable std::mutex target_mutex_{}; /* 仅保护三槽在提交与回读边界的生命周期。 */
std::unique_ptr<Datoviz_Frame_Target_Set> targets_{}; /* 三槽唯一所有权。 */
std::uint64_t target_generation_{}; /* 目标槽重建代数。 */
};
}
@@ -1,4 +1,5 @@
Scene_Datoviz_State::Scene_Datoviz_State() = default;
Scene_Datoviz_State::Scene_Datoviz_State() :
targets_(std::make_unique<Datoviz_Frame_Target_Set>()) {}
Scene_Datoviz_State::~Scene_Datoviz_State() {
try {
destroy();
@@ -72,7 +72,7 @@ void Scene_Datoviz_State::ensure_external_attributes(
external_buffers_.push_back(gpu_buffer);
attributes.push_back({
name, stride, static_cast<std::uint32_t>(capacity),
not_null{scene_buffer}, gpu_buffer, false
not_null{scene_buffer}, not_null{gpu_buffer}, false
});
};
for (const auto& attribute : layout)
@@ -114,7 +114,7 @@ std::uint64_t Scene_Datoviz_State::upload_external_attributes(
const auto byte_offset = static_cast<DvzSize>(context.target_index) *
attribute.capacity * attribute.stride;
dvz_buffer_upload(
attribute.gpu_buffer, byte_offset, byte_count, source.get());
attribute.gpu_buffer.get(), byte_offset, byte_count, source.get());
*context.uploaded_bytes += byte_count;
};
target.operations->visit_external_attributes(
@@ -156,7 +156,7 @@ void Scene_Datoviz_State::register_external_attributes(
&scene_descriptor))
throw std::runtime_error("failed to inspect Datoviz external buffer");
auto descriptor = dvz_drp2_external_buffer_desc();
descriptor.buffer = attribute.gpu_buffer;
descriptor.buffer = attribute.gpu_buffer.get();
descriptor.size = scene_descriptor.byte_size;
descriptor.usage = DVZ_DRP2_BUFFER_USAGE_VERTEX;
if (!dvz_drp2_runtime_register_external_buffer(
@@ -237,21 +237,18 @@ std::uint64_t Scene_Datoviz_State::apply_visual(
visual, point.visible && item_count != 0) != DVZ_OK)
throw std::runtime_error("failed to apply Datoviz visual state");
}
std::uint64_t uploaded_bytes{};
if (item_count == 0) {
if (dvz_visual_set_visible(visual, false) != DVZ_OK) throw std::runtime_error("failed to hide empty Datoviz point visual");
target.applied_revision = point.revision;
target.applied = point;
return 0;
}
const auto count = static_cast<std::uint32_t>(item_count);
if (!target.operations->external_attributes.empty()) {
else if (!target.operations->external_attributes.empty()) {
const auto count = static_cast<std::uint32_t>(item_count);
/*
* 普通顶点族在第一次录制前即绑定三槽外部属性。Segment/Vector 的
* typed-stroke 元数据、Glyph/Text 的 atlas 几何以及 Image/Labels/Volume
* 的采样场资源继续使用 Datoviz 结构路径;
* 把 dense 数据先写入再改绑会违反 Datoviz 的单一属性所有权契约。
*/
std::uint64_t uploaded_bytes{};
ensure_external_attributes(target, point);
if (bind_target) bind_external_attributes(target, target_index, count);
if (target.uploaded_data_revisions[target_index] !=
@@ -265,20 +262,16 @@ std::uint64_t Scene_Datoviz_State::apply_visual(
dvz_visual_set_visible(visual, point.visible) != DVZ_OK)
throw std::runtime_error(
"failed to apply Datoviz external visual visibility");
target.applied_revision = point.revision;
target.applied = point;
return uploaded_bytes;
}
if (target.applied && target.applied->data_revision == point.data_revision) {
target.applied_revision = point.revision;
target.applied = point;
return 0;
else if (!target.applied ||
target.applied->data_revision != point.data_revision) {
target.operations->upload_visual(
target, not_null{point.data.get()},
static_cast<std::uint32_t>(item_count));
if (dvz_visual_set_visible(visual, point.visible) != DVZ_OK)
throw std::runtime_error("failed to upload Datoviz visual payload");
}
target.operations->upload_visual(
target, not_null{point.data.get()}, count);
if (dvz_visual_set_visible(visual, point.visible) != DVZ_OK)
throw std::runtime_error("failed to upload Datoviz visual payload");
target.applied_revision = point.revision;
target.applied = point;
return 0;
return uploaded_bytes;
}
@@ -23,7 +23,7 @@ struct Datoviz_External_Attribute {
std::uint32_t stride{}; /* 单项字节跨度。 */
std::uint32_t capacity{}; /* 每个帧槽可容纳的项数。 */
not_null<DvzSceneBuffer*> scene_buffer; /* Scene 图借用的资源描述。 */
owner<DvzBuffer*> gpu_buffer{}; /* 本 Scene 独占的映射 GPU Buffer。 */
not_null<DvzBuffer*> gpu_buffer; /* 必填、非拥有;由 Scene 的 Buffer 集合拥有。 */
std::uint8_t registered_targets{}; /* 已绑定到哪些固定帧槽。 */
};
@@ -0,0 +1,6 @@
#include "Basic_Visual.hpp"
namespace aethera::render_3d {
bool Basic_Visual::Prop::operator==(const Prop&) const = default;
bool Basic_Visual::State::operator==(const State&) const = default;
}
+12 -16
View File
@@ -1,29 +1,25 @@
#pragma once
#include "Prepared_Visual.hpp"
#include <renderable.hpp>
#include <memory>
#include <vector>
namespace aethera::render_3d {
template <typename Spec>
struct Basic_Visual : Def<Basic_Visual<Spec>, Renderable> {
using Specification = Spec;
using Item = typename Spec::Item;
using Prepared_Data = typename Spec::Prepared_Data;
using Prepared_Visual = detail::Prepared_Visual_For<Prepared_Data>;
struct Prop : Basic_Visual::Prev_Prop, Spec::Settings {
Matrix4 transform{}; /* 本地坐标到 Scene 坐标的变换。 */
bool visible{true}; /* 是否向后端提交可见图元。 */
bool depth_test{true}; /* 是否启用深度测试。 */
std::vector<Item> items{}; /* 外部读写的图元数据唯一权威来源。 */
/* 所有 3D Visual 共享的变换、可见性和 Scene 提交能力;业务数据由具体 Visual 的 Buffer 持有。 */
struct Basic_Visual : Def<Basic_Visual, Renderable> {
struct Prop : Prev_Prop {
Matrix4 transform{}; /* 本地坐标到 Scene 坐标的变换。 */
bool visible{true}; /* 是否向后端提交可见图元。 */
bool depth_test{true}; /* 是否启用深度测试。 */
bool operator==(const Prop&) const;
};
struct State : Basic_Visual::Prev_State {
std::size_t item_count{}; /* 最近一次推进后发布的输入图元数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 输出的后端图元数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 输出版本。 */
struct State : Prev_State {
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Basic_Visual.ipp"
+116 -88
View File
@@ -1,70 +1,82 @@
#pragma once
#include <algorithm>
#include <stdexcept>
#include <utility>
namespace aethera::render_3d {
template <typename Spec>
struct Basic_Visual<Spec>::Private : Basic_Visual::Prev_Private {
using Enqueue_Paint = void (*)(void*, const Root*, const Prepared_Visual&);
struct Basic_Visual::Private : Prev_Private {
using Enqueue_Paint = void (*)(
void*, const Root*, const detail::Prepared_Visual&);
struct Paint_Target {
std::weak_ptr<void> context{}; /* Scene 拥有的异步提交上下文;Scene 销毁后自动失效。 */
Enqueue_Paint enqueue{}; /* 只入队不等待 GPU 完成的 Paint 入口。 */
Enqueue_Paint enqueue{}; /* 只入队不等待 GPU 完成的 Paint 入口。 */
};
Paint_Target paint_target{}; /* Builder 绑定的所属 Scene 异步 Paint 目标。 */
Prepared_Visual prepared{}; /* 最近一次 Prepare 生成的唯一提交产物。 */
std::uint64_t next_revision{1}; /* 下一份 Prepared_Visual 的单调版本。 */
Paint_Target paint_target{}; /* Builder 绑定的所属 Scene 异步 Paint 目标。 */
detail::Prepared_Visual prepared{}; /* 最近一次 Prepare 生成的唯一提交产物。 */
std::uint64_t next_revision{1}; /* 下一份提交产物的单调版本。 */
std::uint64_t next_data_revision{1}; /* 下一份实际数据转换结果的版本。 */
static constexpr std::uint8_t state_change{1U};
static constexpr std::uint8_t data_change{2U};
std::uint8_t pending_changes{data_change}; /* 单写者推进域内尚未由 Prepare 消费的变更域。 */
/* Scene Builder 调用:绑定只入队的异步 Paint 目标,目标生命周期必须覆盖 Visual。 */
void bind_paint_target(std::shared_ptr<void> context, Enqueue_Paint enqueue);
/* CRTP 数据能力:从当前 Prop 构建后端字段,不访问其他缓冲角色。 */
bool data_changed{true}; /* 当前 Buffer 或具体 Visual 配置是否尚未 Prepare。 */
void bind_paint_target(std::shared_ptr<void> context,
Enqueue_Paint enqueue);
template <Attached Object>
void prepare_data(Object* object);
template <Attached Object>
[[nodiscard]] Task_Graph build_graph(Object* object, const Prop& prop);
template <Attached Object>
[[nodiscard]] bool should_render(Object* object, const State& state, bool dirty);
/* CRTP Prop hook:本层字段改变时标记 Prepare。 */
template <typename Object, typename Owner, typename Member, typename Prop_Type>
void after_prop_set(Object* object, Member Owner::* member, Prop_Access<Prop_Type> props);
[[nodiscard]] static bool valid_items(const std::vector<Item>& items);
[[nodiscard]] bool should_render(
Object* object, const typename Object::State& state, bool dirty);
template <typename Object, typename Owner, typename Member,
typename Prop_Type>
void after_prop_set(Object* object, Member Owner::* member,
Prop_Access<Prop_Type> props);
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 <typename Spec>
bool Basic_Visual<Spec>::Prop::operator==(const Prop&) const = default;
template <typename Spec>
bool Basic_Visual<Spec>::State::operator==(const State&) const = default;
template <typename Spec>
bool Basic_Visual<Spec>::Private::valid_items(const std::vector<Item>& items) {
return std::ranges::all_of(items, [](const Item& item) {
return Spec::valid(item);
});
}
template <typename Spec>
void Basic_Visual<Spec>::Private::bind_paint_target(std::shared_ptr<void> context, Enqueue_Paint enqueue) {
inline void Basic_Visual::Private::bind_paint_target(
std::shared_ptr<void> context, Enqueue_Paint enqueue) {
paint_target = {std::move(context), enqueue};
}
template <typename Spec>
template <Attached Object>
void Basic_Visual<Spec>::Private::prepare_data(Object* object) {
using Visual_Tag = typename Basic_Visual::Base_Tag;
const auto& prop = this->template read_current_prop<Visual_Tag>(object);
if (!valid_items(prop.items) || !detail::finite(prop.transform)) throw std::invalid_argument("3D visual property contains invalid data");
const auto changes = std::exchange(pending_changes, 0);
void Basic_Visual::Private::prepare_data(Object* object) {
using Definition = typename Object::Attached_Object;
using Visual = typename Definition::Visual_Base;
using Visual_State = typename Visual::State;
using Specification = typename Visual::Specification;
using Frame_Tag = typename Visual::Frame_Tag;
const auto& common = this->template read_current_prop<Basic_Visual::Base_Tag>(
object);
const auto& prop = this->template read_current_prop<
typename Visual::Base_Tag>(object);
const auto& frame = double_buffer::detail::Internal_Access::
current_buffer<Frame_Tag>(object);
if (!std::ranges::all_of(frame.items, [](const auto& item) {
return Specification::valid(item);
}) || !detail::finite(common.transform))
throw std::invalid_argument("3D visual property contains invalid data");
auto data = prepared.data;
auto data_revision = prepared.data_revision;
if ((changes & data_change) != 0 || !data) {
auto updated = std::make_shared<Prepared_Data>();
Spec::prepare(prop.items, *updated);
if constexpr (requires { updated->style = prop.style; }) updated->style = prop.style;
if constexpr (requires { updated->field_width = prop.field_width; }) {
updated->field_width = prop.field_width;
updated->field_height = prop.field_height;
if (std::exchange(data_changed, false) || !data) {
auto updated = std::make_shared<typename Specification::Prepared_Data>();
Specification::prepare(frame.items, *updated);
if constexpr (requires { updated->style = prop.style; })
updated->style = prop.style;
if constexpr (requires { updated->field_width = frame.field_width; }) {
updated->field_width = frame.field_width;
updated->field_height = frame.field_height;
}
if constexpr (requires { updated->field_pixels; prop.field_pixels; }) {
updated->field_pixels.reserve(prop.field_pixels.size());
for (const auto pixel : prop.field_pixels)
if constexpr (requires { updated->field_pixels; frame.field_pixels; }) {
updated->field_pixels.reserve(frame.field_pixels.size());
for (const auto pixel : frame.field_pixels)
updated->field_pixels.push_back(
{pixel.red, pixel.green, pixel.blue, pixel.alpha});
if (updated->field_width == 0 || updated->field_height == 0 ||
@@ -73,18 +85,19 @@ void Basic_Visual<Spec>::Private::prepare_data(Object* object) {
throw std::invalid_argument(
"3D image pixel count does not match field dimensions");
}
if constexpr (requires { updated->field_labels = prop.field_labels; }) {
updated->field_labels = prop.field_labels;
if constexpr (requires { updated->field_labels = frame.field_labels; }) {
updated->field_labels = frame.field_labels;
if (updated->field_width == 0 || updated->field_height == 0 ||
static_cast<std::uint64_t>(updated->field_width) *
updated->field_height != updated->field_labels.size())
throw std::invalid_argument(
"3D label count does not match field dimensions");
}
if constexpr (requires { updated->field_depth = prop.field_depth; })
updated->field_depth = prop.field_depth;
if constexpr (requires { updated->field_depth = frame.field_depth; })
updated->field_depth = frame.field_depth;
if constexpr (requires { updated->values; updated->field_depth; }) {
const auto expected = static_cast<std::uint64_t>(updated->field_width) *
const auto expected =
static_cast<std::uint64_t>(updated->field_width) *
updated->field_height * updated->field_depth;
if (updated->field_width == 0 || updated->field_height == 0 ||
updated->field_depth == 0 || expected != updated->values.size())
@@ -94,51 +107,66 @@ void Basic_Visual<Spec>::Private::prepare_data(Object* object) {
data = std::move(updated);
data_revision = next_data_revision++;
}
auto& output = prepared;
output = {};
output.transform = prop.transform;
output.visible = prop.visible;
output.depth_test = prop.depth_test;
output.revision = next_revision++;
output.data_revision = data_revision;
output.data = std::move(data);
double_buffer::detail::Internal_Access::update_state<&State::item_count, &State::prepared_item_count, &State::prepared_revision>(object, [&](State_Access<typename Object::State> states) {
auto& state = states.template get<Visual_Tag>();
state.item_count = prop.items.size();
state.prepared_item_count = prop.items.size();
state.prepared_revision = output.revision;
});
prepared = {
common.transform, common.visible, common.depth_test, next_revision++,
data_revision, &Specification::datoviz_operations(), std::move(data)};
double_buffer::detail::Internal_Access::template update_state<
&Visual_State::item_count, &Visual_State::prepared_item_count,
&Visual_State::prepared_revision>(
object, [&](State_Access<typename Object::State> states) {
auto& state = states.template get<typename Visual::Base_Tag>();
state.item_count = frame.items.size();
state.prepared_item_count = frame.items.size();
state.prepared_revision = prepared.revision;
});
}
template <typename Spec>
template <Attached Object>
Task_Graph Basic_Visual<Spec>::Private::build_graph(Object* object, const Prop&) {
Task_Graph Basic_Visual::Private::build_graph(Object* object, const Prop&) {
Task_Graph graph{"render_3d.visual"};
graph.add("prepare_and_publish", [this, object] {
if (double_buffer::detail::Internal_Access::
dirty<Render_Graph_Tag>(object))
prepare_data(object);
if (auto context = paint_target.context.lock(); context && paint_target.enqueue) {
paint_target.enqueue(context.get(), object, prepared);
}
graph.add("prepare_and_publish", [object] {
auto& runtime = double_buffer::detail::Internal_Access::get(object);
if (double_buffer::detail::Internal_Access::dirty<Render_Graph_Tag>(
object))
runtime.prepare_data(object);
if (auto context = runtime.paint_target.context.lock();
context && runtime.paint_target.enqueue)
runtime.paint_target.enqueue(
context.get(), object, runtime.prepared);
});
return graph;
}
template <typename Spec>
template <Attached Object>
bool Basic_Visual<Spec>::Private::should_render(Object*, const State&, bool) {
bool Basic_Visual::Private::should_render(
Object*, const typename Object::State&, bool) {
return paint_target.enqueue != nullptr && !paint_target.context.expired();
}
template <typename Spec>
template <typename Object, typename Owner, typename Member, typename Prop_Type>
void Basic_Visual<Spec>::Private::after_prop_set(Object* object, Member Owner::* member, Prop_Access<Prop_Type>) {
if constexpr (std::same_as<Owner, Prop>) {
bool state_only{};
if constexpr (std::same_as<Member Owner::*, decltype(&Prop::transform)>)
state_only = member == &Prop::transform;
else if constexpr (std::same_as<Member Owner::*, decltype(&Prop::visible)>)
state_only = member == &Prop::visible || member == &Prop::depth_test;
pending_changes |= state_only ? state_change : data_change;
double_buffer::detail::Internal_Access::mark_dirty<Render_Graph_Tag>(object);
}
template <typename Object, typename Owner, typename Member,
typename Prop_Type>
void Basic_Visual::Private::after_prop_set(
Object* object, Member Owner::*, Prop_Access<Prop_Type>) {
using Visual = typename Object::Attached_Object::Visual_Base;
if constexpr (std::same_as<Owner, typename Visual::Prop>)
data_changed = true;
if constexpr (std::same_as<Owner, Basic_Visual::Prop> ||
std::same_as<Owner, typename Visual::Prop>)
double_buffer::detail::Internal_Access::mark_dirty<Render_Graph_Tag>(
object);
}
template <typename Object, typename Prop_Type, typename State_Type>
void Basic_Visual::Private::before_advance(
Object* object, Prop_Type*, State_Access<State_Type>, const Prop_Type*,
State_Access<const State_Type>) {
using Frame_Tag = typename Object::Attached_Object::Visual_Base::Frame_Tag;
if (double_buffer::detail::Internal_Access::template pending_buffer<Frame_Tag>(
object) !=
double_buffer::detail::Internal_Access::template current_buffer<Frame_Tag>(
object))
data_changed = true;
}
}
@@ -0,0 +1,7 @@
#include "Glyph_Visual.hpp"
namespace aethera::render_3d {
bool Glyph_Frame::operator==(const Glyph_Frame&) const = default;
bool Glyph_Visual::Prop::operator==(const Prop&) const = default;
bool Glyph_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Glyph {
@@ -11,7 +12,11 @@ struct Glyph {
Coordinate_3D angle{};
bool operator==(const Glyph&) const = default;
};
struct Glyph_Frame_Tag {};
struct Glyph_Frame {
std::vector<Glyph> items{}; /* 本次提交的完整 Glyph 集合。 */
bool operator==(const Glyph_Frame&) const;
};
namespace detail {
struct Glyph_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -20,15 +25,29 @@ struct Glyph_Prepared_Data {
std::vector<std::array<float, 4>> bounds{};
std::vector<std::array<float, 4>> texture_coordinates{};
};
struct Glyph_Spec : Visual_Spec<Glyph, Visual_Settings, Glyph_Prepared_Data> {
struct Glyph_Spec {
using Item = Glyph;
using Prepared_Data = Glyph_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Glyph_Visual = Basic_Visual<detail::Glyph_Spec>;
struct Glyph_Visual : Def<Glyph_Visual, Basic_Visual,
Tagged_Buffer<Glyph_Frame_Tag, Glyph_Frame>> {
using Visual_Base = Glyph_Visual;
using Frame_Tag = Glyph_Frame_Tag;
using Frame = Glyph_Frame;
using Specification = detail::Glyph_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Glyph_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Glyph_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.bounds) && finite(item.texture_coordinates) && finite(item.angle);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Glyph_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Glyph_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.angles.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Image_Visual.hpp"
namespace aethera::render_3d {
bool Image_Frame::operator==(const Image_Frame&) const = default;
bool Image_Visual::Prop::operator==(const Prop&) const = default;
bool Image_Visual::State::operator==(const State&) const = default;
}
+29 -14
View File
@@ -1,22 +1,23 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Image_Field_Settings {
std::uint32_t field_width{};
std::uint32_t field_height{};
std::vector<Color> field_pixels{};
bool operator==(const Image_Field_Settings&) const = default;
};
struct Image {
Vec3 position{};
Vec2 extent{1.0F, 1.0F};
Vec4 texture_rectangle{0.0F, 0.0F, 1.0F, 1.0F};
bool operator==(const Image&) const = default;
};
struct Image_Frame_Tag {};
struct Image_Frame {
std::vector<Image> items{}; /* 本次提交的完整 Image 集合。 */
std::uint32_t field_width{}; /* 本帧采样场宽度,单位为像素。 */
std::uint32_t field_height{}; /* 本帧采样场高度,单位为像素。 */
std::vector<Color> field_pixels{}; /* 本帧采样场的 RGBA 像素。 */
bool operator==(const Image_Frame&) const;
};
namespace detail {
struct Image_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -26,15 +27,29 @@ struct Image_Prepared_Data {
std::uint32_t field_width{};
std::uint32_t field_height{};
};
struct Image_Spec : Visual_Spec<Image, Image_Field_Settings, Image_Prepared_Data> {
struct Image_Spec {
using Item = Image;
using Prepared_Data = Image_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Image_Visual = Basic_Visual<detail::Image_Spec>;
struct Image_Visual : Def<Image_Visual, Basic_Visual,
Tagged_Buffer<Image_Frame_Tag, Image_Frame>> {
using Visual_Base = Image_Visual;
using Frame_Tag = Image_Frame_Tag;
using Frame = Image_Frame;
using Specification = detail::Image_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Image_Visual.ipp"
@@ -6,6 +6,15 @@ inline bool Image_Spec::valid(const Item& item) noexcept {
finite(item.texture_rectangle);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Image_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Image_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
output.positions.reserve(items.size());
output.extents.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Labels_Visual.hpp"
namespace aethera::render_3d {
bool Labels_Frame::operator==(const Labels_Frame&) const = default;
bool Labels_Visual::Prop::operator==(const Prop&) const = default;
bool Labels_Visual::State::operator==(const State&) const = default;
}
+29 -14
View File
@@ -1,22 +1,23 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Labels_Field_Settings {
std::uint32_t field_width{};
std::uint32_t field_height{};
std::vector<std::int32_t> field_labels{};
bool operator==(const Labels_Field_Settings&) const = default;
};
struct Label {
Vec3 position{};
Vec2 extent{32.0F, 16.0F};
Vec4 texture_rectangle{0.0F, 0.0F, 1.0F, 1.0F};
bool operator==(const Label&) const = default;
};
struct Labels_Frame_Tag {};
struct Labels_Frame {
std::vector<Label> items{}; /* 本次提交的完整 Label 集合。 */
std::uint32_t field_width{}; /* 本帧标签场宽度。 */
std::uint32_t field_height{}; /* 本帧标签场高度。 */
std::vector<std::int32_t> field_labels{}; /* 本帧标签场数据。 */
bool operator==(const Labels_Frame&) const;
};
namespace detail {
struct Labels_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -26,15 +27,29 @@ struct Labels_Prepared_Data {
std::uint32_t field_width{};
std::uint32_t field_height{};
};
struct Labels_Spec : Visual_Spec<Label, Labels_Field_Settings, Labels_Prepared_Data> {
struct Labels_Spec {
using Item = Label;
using Prepared_Data = Labels_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Labels_Visual = Basic_Visual<detail::Labels_Spec>;
struct Labels_Visual : Def<Labels_Visual, Basic_Visual,
Tagged_Buffer<Labels_Frame_Tag, Labels_Frame>> {
using Visual_Base = Labels_Visual;
using Frame_Tag = Labels_Frame_Tag;
using Frame = Labels_Frame;
using Specification = detail::Labels_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Labels_Visual.ipp"
@@ -6,6 +6,15 @@ inline bool Labels_Spec::valid(const Item& item) noexcept {
finite(item.texture_rectangle);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Labels_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Labels_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
output.positions.reserve(items.size());
output.extents.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Marker_Visual.hpp"
namespace aethera::render_3d {
bool Marker_Frame::operator==(const Marker_Frame&) const = default;
bool Marker_Visual::Prop::operator==(const Prop&) const = default;
bool Marker_Visual::State::operator==(const State&) const = default;
}
+26 -8
View File
@@ -1,10 +1,10 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
enum struct Marker_Shape : std::uint8_t { disc, square, triangle, diamond, cross };
struct Marker {
Vec3 position{};
Color color{Color::white()};
@@ -14,7 +14,11 @@ struct Marker {
bool coordinate_label_visible{false};
bool operator==(const Marker&) const = default;
};
struct Marker_Frame_Tag {};
struct Marker_Frame {
std::vector<Marker> items{}; /* 本次提交的完整 Marker 集合。 */
bool operator==(const Marker_Frame&) const;
};
namespace detail {
struct Marker_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -24,15 +28,29 @@ struct Marker_Prepared_Data {
std::vector<std::uint32_t> shapes{};
std::vector<std::uint8_t> coordinate_label_visibility{};
};
struct Marker_Spec : Visual_Spec<Marker, Visual_Settings, Marker_Prepared_Data> {
struct Marker_Spec {
using Item = Marker;
using Prepared_Data = Marker_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Marker_Visual = Basic_Visual<detail::Marker_Spec>;
struct Marker_Visual : Def<Marker_Visual, Basic_Visual,
Tagged_Buffer<Marker_Frame_Tag, Marker_Frame>> {
using Visual_Base = Marker_Visual;
using Frame_Tag = Marker_Frame_Tag;
using Frame = Marker_Frame;
using Specification = detail::Marker_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Marker_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Marker_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.diameter_px) && item.diameter_px > 0.0F && finite(item.angle);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Marker_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Marker_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.diameters.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Mesh_Visual.hpp"
namespace aethera::render_3d {
bool Mesh_Frame::operator==(const Mesh_Frame&) const = default;
bool Mesh_Visual::Prop::operator==(const Prop&) const = default;
bool Mesh_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Mesh_Vertex {
@@ -10,7 +11,11 @@ struct Mesh_Vertex {
Vec2 texture_coordinate{};
bool operator==(const Mesh_Vertex&) const = default;
};
struct Mesh_Frame_Tag {};
struct Mesh_Frame {
std::vector<Mesh_Vertex> items{}; /* 本次提交的完整 Mesh 顶点集合。 */
bool operator==(const Mesh_Frame&) const;
};
namespace detail {
struct Mesh_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -18,15 +23,29 @@ struct Mesh_Prepared_Data {
std::vector<Prepared_Position> normals{};
std::vector<std::array<float, 2>> texture_coordinates{};
};
struct Mesh_Spec : Visual_Spec<Mesh_Vertex, Visual_Settings, Mesh_Prepared_Data> {
struct Mesh_Spec {
using Item = Mesh_Vertex;
using Prepared_Data = Mesh_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Mesh_Visual = Basic_Visual<detail::Mesh_Spec>;
struct Mesh_Visual : Def<Mesh_Visual, Basic_Visual,
Tagged_Buffer<Mesh_Frame_Tag, Mesh_Frame>> {
using Visual_Base = Mesh_Visual;
using Frame_Tag = Mesh_Frame_Tag;
using Frame = Mesh_Frame;
using Specification = detail::Mesh_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Mesh_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Mesh_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.normal) && finite(item.texture_coordinate);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Mesh_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Mesh_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.normals.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Path_Visual.hpp"
namespace aethera::render_3d {
bool Path_Frame::operator==(const Path_Frame&) const = default;
bool Path_Visual::Prop::operator==(const Prop&) const = default;
bool Path_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Path_Vertex {
@@ -9,22 +10,40 @@ struct Path_Vertex {
Pixel_Distance width_px{1.0F};
bool operator==(const Path_Vertex&) const = default;
};
struct Path_Frame_Tag {};
struct Path_Frame {
std::vector<Path_Vertex> items{}; /* 本次提交的完整路径顶点集合。 */
bool operator==(const Path_Frame&) const;
};
namespace detail {
struct Path_Prepared_Data {
std::vector<Prepared_Position> positions{};
std::vector<Prepared_Color> colors{};
std::vector<float> widths{};
};
struct Path_Spec : Visual_Spec<Path_Vertex, Visual_Settings, Path_Prepared_Data> {
struct Path_Spec {
using Item = Path_Vertex;
using Prepared_Data = Path_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Path_Visual = Basic_Visual<detail::Path_Spec>;
struct Path_Visual : Def<Path_Visual, Basic_Visual,
Tagged_Buffer<Path_Frame_Tag, Path_Frame>> {
using Visual_Base = Path_Visual;
using Frame_Tag = Path_Frame_Tag;
using Frame = Path_Frame;
using Specification = detail::Path_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Path_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Path_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.width_px) && item.width_px > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Path_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Path_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.widths.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Pixel_Visual.hpp"
namespace aethera::render_3d {
bool Pixel_Frame::operator==(const Pixel_Frame&) const = default;
bool Pixel_Visual::Prop::operator==(const Prop&) const = default;
bool Pixel_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Pixel {
@@ -9,22 +10,40 @@ struct Pixel {
Pixel_Distance size_px{1.0F};
bool operator==(const Pixel&) const = default;
};
struct Pixel_Frame_Tag {};
struct Pixel_Frame {
std::vector<Pixel> items{}; /* 本次提交的完整 Pixel 集合。 */
bool operator==(const Pixel_Frame&) const;
};
namespace detail {
struct Pixel_Prepared_Data {
std::vector<Prepared_Position> positions{};
std::vector<Prepared_Color> colors{};
std::vector<float> sizes{};
};
struct Pixel_Spec : Visual_Spec<Pixel, Visual_Settings, Pixel_Prepared_Data> {
struct Pixel_Spec {
using Item = Pixel;
using Prepared_Data = Pixel_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Pixel_Visual = Basic_Visual<detail::Pixel_Spec>;
struct Pixel_Visual : Def<Pixel_Visual, Basic_Visual,
Tagged_Buffer<Pixel_Frame_Tag, Pixel_Frame>> {
using Visual_Base = Pixel_Visual;
using Frame_Tag = Pixel_Frame_Tag;
using Frame = Pixel_Frame;
using Specification = detail::Pixel_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Pixel_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Pixel_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.size_px) && item.size_px > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Pixel_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Pixel_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.sizes.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Point_Visual.hpp"
namespace aethera::render_3d {
bool Point_Frame::operator==(const Point_Frame&) const = default;
bool Point_Visual::Prop::operator==(const Prop&) const = default;
bool Point_Visual::State::operator==(const State&) const = default;
}
+29 -14
View File
@@ -1,45 +1,60 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
enum struct Point_Aspect : std::uint8_t { filled, stroke, outline };
struct Point_Style {
Color edge_color{Color::black()};
Pixel_Distance stroke_width_px{};
Point_Aspect aspect{Point_Aspect::filled};
bool operator==(const Point_Style&) const = default;
};
struct Point {
Vec3 position{};
Color color{Color::white()};
Pixel_Distance diameter_px{8.0F};
bool operator==(const Point&) const = default;
};
namespace detail {
struct Point_Settings : Visual_Settings {
Point_Style style{};
bool operator==(const Point_Settings&) const = default;
struct Point_Frame_Tag {};
struct Point_Frame {
std::vector<Point> items{}; /* 本次提交的完整 Point 集合。 */
bool operator==(const Point_Frame&) const;
};
namespace detail {
struct Point_Prepared_Data {
std::vector<Prepared_Position> positions{};
std::vector<Prepared_Color> colors{};
std::vector<float> diameters{};
Point_Style style{};
};
struct Point_Spec : Visual_Spec<Point, Point_Settings, Point_Prepared_Data> {
struct Point_Spec {
using Item = Point;
using Prepared_Data = Point_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Point_Visual = Basic_Visual<detail::Point_Spec>;
struct Point_Visual : Def<Point_Visual, Basic_Visual,
Tagged_Buffer<Point_Frame_Tag, Point_Frame>> {
using Visual_Base = Point_Visual;
using Frame_Tag = Point_Frame_Tag;
using Frame = Point_Frame;
using Specification = detail::Point_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop {
Point_Style style{}; /* 当前 Point Visual 的统一描边样式。 */
bool operator==(const Prop&) const;
};
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Point_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Point_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.diameter_px) && item.diameter_px > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Point_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Point_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.diameters.reserve(items.size());
+10 -4
View File
@@ -1,18 +1,24 @@
#pragma once
#include "../base/Types.hpp"
#include <memory>
namespace aethera::render_3d::detail {
struct Datoviz_Visual_Operations;
using Prepared_Position = std::array<float, 3>;
using Prepared_Color = std::array<std::uint8_t, 4>;
/* 每个 Visual 的 Private 只保存自己 Spec::Prepared_Data 的强类型结果。 */
template <typename Data>
struct Prepared_Visual_For {
/* Scene 与具体 Visual 之间唯一的不可变提交信封。 */
struct Prepared_Visual {
Matrix4 transform{};
bool visible{true};
bool depth_test{true};
std::uint64_t revision{};
std::uint64_t data_revision{};
std::shared_ptr<const Data> data{};
const Datoviz_Visual_Operations* operations{}; /* 可空借用;尚未发布时为空。 */
std::shared_ptr<const void> data{}; /* 具体 Prepared_Data 的只读所有权。 */
};
}
@@ -0,0 +1,7 @@
#include "Primitive_Visual.hpp"
namespace aethera::render_3d {
bool Primitive_Frame::operator==(const Primitive_Frame&) const = default;
bool Primitive_Visual::Prop::operator==(const Prop&) const = default;
bool Primitive_Visual::State::operator==(const State&) const = default;
}
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Primitive_Vertex {
@@ -9,22 +10,40 @@ struct Primitive_Vertex {
Vec3 normal{};
bool operator==(const Primitive_Vertex&) const = default;
};
struct Primitive_Frame_Tag {};
struct Primitive_Frame {
std::vector<Primitive_Vertex> items{}; /* 本次提交的完整 Primitive 顶点集合。 */
bool operator==(const Primitive_Frame&) const;
};
namespace detail {
struct Primitive_Prepared_Data {
std::vector<Prepared_Position> positions{};
std::vector<Prepared_Color> colors{};
std::vector<Prepared_Position> normals{};
};
struct Primitive_Spec : Visual_Spec<Primitive_Vertex, Visual_Settings, Primitive_Prepared_Data> {
struct Primitive_Spec {
using Item = Primitive_Vertex;
using Prepared_Data = Primitive_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Primitive_Visual = Basic_Visual<detail::Primitive_Spec>;
struct Primitive_Visual : Def<Primitive_Visual, Basic_Visual,
Tagged_Buffer<Primitive_Frame_Tag, Primitive_Frame>> {
using Visual_Base = Primitive_Visual;
using Frame_Tag = Primitive_Frame_Tag;
using Frame = Primitive_Frame;
using Specification = detail::Primitive_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Primitive_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Primitive_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.normal);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Primitive_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Primitive_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.normals.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Segment_Visual.hpp"
namespace aethera::render_3d {
bool Segment_Frame::operator==(const Segment_Frame&) const = default;
bool Segment_Visual::Prop::operator==(const Prop&) const = default;
bool Segment_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Segment {
@@ -10,7 +11,11 @@ struct Segment {
Pixel_Distance width_px{1.0F};
bool operator==(const Segment&) const = default;
};
struct Segment_Frame_Tag {};
struct Segment_Frame {
std::vector<Segment> items{}; /* 本次提交的完整 Segment 集合。 */
bool operator==(const Segment_Frame&) const;
};
namespace detail {
struct Segment_Prepared_Data {
std::vector<Prepared_Position> starts{};
@@ -18,15 +23,29 @@ struct Segment_Prepared_Data {
std::vector<Prepared_Color> colors{};
std::vector<float> widths{};
};
struct Segment_Spec : Visual_Spec<Segment, Visual_Settings, Segment_Prepared_Data> {
struct Segment_Spec {
using Item = Segment;
using Prepared_Data = Segment_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Segment_Visual = Basic_Visual<detail::Segment_Spec>;
struct Segment_Visual : Def<Segment_Visual, Basic_Visual,
Tagged_Buffer<Segment_Frame_Tag, Segment_Frame>> {
using Visual_Base = Segment_Visual;
using Frame_Tag = Segment_Frame_Tag;
using Frame = Segment_Frame;
using Specification = detail::Segment_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Segment_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Segment_Spec::valid(const Item& item) noexcept {
return finite(item.start) && finite(item.end) && finite(item.width_px) && item.width_px > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Segment_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Segment_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
output.starts.reserve(items.size());
output.ends.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Sphere_Visual.hpp"
namespace aethera::render_3d {
bool Sphere_Frame::operator==(const Sphere_Frame&) const = default;
bool Sphere_Visual::Prop::operator==(const Prop&) const = default;
bool Sphere_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Sphere {
@@ -9,22 +10,40 @@ struct Sphere {
Coordinate_3D radius{0.1F};
bool operator==(const Sphere&) const = default;
};
struct Sphere_Frame_Tag {};
struct Sphere_Frame {
std::vector<Sphere> items{}; /* 本次提交的完整 Sphere 集合。 */
bool operator==(const Sphere_Frame&) const;
};
namespace detail {
struct Sphere_Prepared_Data {
std::vector<Prepared_Position> centers{};
std::vector<Prepared_Color> colors{};
std::vector<float> radii{};
};
struct Sphere_Spec : Visual_Spec<Sphere, Visual_Settings, Sphere_Prepared_Data> {
struct Sphere_Spec {
using Item = Sphere;
using Prepared_Data = Sphere_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Sphere_Visual = Basic_Visual<detail::Sphere_Spec>;
struct Sphere_Visual : Def<Sphere_Visual, Basic_Visual,
Tagged_Buffer<Sphere_Frame_Tag, Sphere_Frame>> {
using Visual_Base = Sphere_Visual;
using Frame_Tag = Sphere_Frame_Tag;
using Frame = Sphere_Frame;
using Specification = detail::Sphere_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Sphere_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Sphere_Spec::valid(const Item& item) noexcept {
return finite(item.center) && finite(item.radius) && item.radius > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Sphere_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Sphere_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
output.centers.reserve(items.size());
output.colors.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Splat_Visual.hpp"
namespace aethera::render_3d {
bool Splat_Frame::operator==(const Splat_Frame&) const = default;
bool Splat_Visual::Prop::operator==(const Prop&) const = default;
bool Splat_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Splat {
@@ -10,7 +11,11 @@ struct Splat {
Coordinate_3D angle{};
bool operator==(const Splat&) const = default;
};
struct Splat_Frame_Tag {};
struct Splat_Frame {
std::vector<Splat> items{}; /* 本次提交的完整 Splat 集合。 */
bool operator==(const Splat_Frame&) const;
};
namespace detail {
struct Splat_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -18,15 +23,29 @@ struct Splat_Prepared_Data {
std::vector<std::array<float, 2>> sigmas{};
std::vector<float> angles{};
};
struct Splat_Spec : Visual_Spec<Splat, Visual_Settings, Splat_Prepared_Data> {
struct Splat_Spec {
using Item = Splat;
using Prepared_Data = Splat_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Splat_Visual = Basic_Visual<detail::Splat_Spec>;
struct Splat_Visual : Def<Splat_Visual, Basic_Visual,
Tagged_Buffer<Splat_Frame_Tag, Splat_Frame>> {
using Visual_Base = Splat_Visual;
using Frame_Tag = Splat_Frame_Tag;
using Frame = Splat_Frame;
using Specification = detail::Splat_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Splat_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Splat_Spec::valid(const Item& item) noexcept {
return finite(item.position) && finite(item.sigma) && item.sigma.x > 0.0F && item.sigma.y > 0.0F && finite(item.angle);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Splat_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Splat_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.sigmas.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Text_Visual.hpp"
namespace aethera::render_3d {
bool Text_Frame::operator==(const Text_Frame&) const = default;
bool Text_Visual::Prop::operator==(const Prop&) const = default;
bool Text_Visual::State::operator==(const State&) const = default;
}
+27 -7
View File
@@ -1,6 +1,8 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <string>
#include <vector>
namespace aethera::render_3d {
struct Text_Label {
@@ -10,7 +12,11 @@ struct Text_Label {
Pixel_Distance size_px{18.0F};
bool operator==(const Text_Label&) const = default;
};
struct Text_Frame_Tag {};
struct Text_Frame {
std::vector<Text_Label> items{}; /* 本次提交的完整 Text 集合。 */
bool operator==(const Text_Frame&) const;
};
namespace detail {
struct Text_Prepared_Data {
std::vector<Prepared_Position> positions{};
@@ -18,15 +24,29 @@ struct Text_Prepared_Data {
std::vector<float> sizes{};
std::vector<std::string> strings{};
};
struct Text_Spec : Visual_Spec<Text_Label, Visual_Settings, Text_Prepared_Data> {
struct Text_Spec {
using Item = Text_Label;
using Prepared_Data = Text_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Text_Visual = Basic_Visual<detail::Text_Spec>;
struct Text_Visual : Def<Text_Visual, Basic_Visual,
Tagged_Buffer<Text_Frame_Tag, Text_Frame>> {
using Visual_Base = Text_Visual;
using Frame_Tag = Text_Frame_Tag;
using Frame = Text_Frame;
using Specification = detail::Text_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Text_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Text_Spec::valid(const Item& item) noexcept {
return finite(item.position) && !item.text.empty() && finite(item.size_px) && item.size_px > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Text_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Text_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
reserve_position_color(output, items.size());
output.sizes.reserve(items.size());
@@ -0,0 +1,7 @@
#include "Vector_Visual.hpp"
namespace aethera::render_3d {
bool Vector_Frame::operator==(const Vector_Frame&) const = default;
bool Vector_Visual::Prop::operator==(const Prop&) const = default;
bool Vector_Visual::State::operator==(const State&) const = default;
}
+26 -7
View File
@@ -1,6 +1,7 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Vector_Glyph {
@@ -10,7 +11,11 @@ struct Vector_Glyph {
Pixel_Distance width_px{1.0F};
bool operator==(const Vector_Glyph&) const = default;
};
struct Vector_Frame_Tag {};
struct Vector_Frame {
std::vector<Vector_Glyph> items{}; /* 本次提交的完整 Vector 集合。 */
bool operator==(const Vector_Frame&) const;
};
namespace detail {
struct Vector_Prepared_Data {
std::vector<Prepared_Position> origins{};
@@ -18,15 +23,29 @@ struct Vector_Prepared_Data {
std::vector<Prepared_Color> colors{};
std::vector<float> widths{};
};
struct Vector_Spec : Visual_Spec<Vector_Glyph, Visual_Settings, Vector_Prepared_Data> {
struct Vector_Spec {
using Item = Vector_Glyph;
using Prepared_Data = Vector_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Vector_Visual = Basic_Visual<detail::Vector_Spec>;
struct Vector_Visual : Def<Vector_Visual, Basic_Visual,
Tagged_Buffer<Vector_Frame_Tag, Vector_Frame>> {
using Visual_Base = Vector_Visual;
using Frame_Tag = Vector_Frame_Tag;
using Frame = Vector_Frame;
using Specification = detail::Vector_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Vector_Visual.ipp"
@@ -7,6 +7,15 @@ inline bool Vector_Spec::valid(const Item& item) noexcept {
return finite(item.origin) && finite(item.direction) && finite(item.width_px) && item.width_px > 0.0F;
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Vector_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Vector_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
output.origins.reserve(items.size());
output.directions.reserve(items.size());
@@ -1,14 +0,0 @@
#pragma once
#include "Basic_Visual.hpp"
namespace aethera::render_3d::detail {
struct Datoviz_Visual_Operations;
template <typename Item_Type, typename Settings_Type, typename Prepared_Type>
struct Visual_Spec {
using Item = Item_Type;
using Settings = Settings_Type;
using Prepared_Data = Prepared_Type;
};
}
@@ -0,0 +1,7 @@
#include "Volume_Visual.hpp"
namespace aethera::render_3d {
bool Volume_Frame::operator==(const Volume_Frame&) const = default;
bool Volume_Visual::Prop::operator==(const Prop&) const = default;
bool Volume_Visual::State::operator==(const State&) const = default;
}
+29 -14
View File
@@ -1,20 +1,21 @@
#pragma once
#include "Visual_Spec.hpp"
#include "Basic_Visual.hpp"
#include <vector>
namespace aethera::render_3d {
struct Volume_Field_Settings {
std::uint32_t field_width{};
std::uint32_t field_height{};
std::uint32_t field_depth{};
bool operator==(const Volume_Field_Settings&) const = default;
};
struct Voxel {
Coordinate_3D value{};
bool operator==(const Voxel&) const = default;
};
struct Volume_Frame_Tag {};
struct Volume_Frame {
std::vector<Voxel> items{}; /* 本次提交的完整体素集合。 */
std::uint32_t field_width{}; /* 本帧体素场宽度。 */
std::uint32_t field_height{}; /* 本帧体素场高度。 */
std::uint32_t field_depth{}; /* 本帧体素场深度。 */
bool operator==(const Volume_Frame&) const;
};
namespace detail {
struct Volume_Prepared_Data {
std::vector<float> values{};
@@ -22,15 +23,29 @@ struct Volume_Prepared_Data {
std::uint32_t field_height{};
std::uint32_t field_depth{};
};
struct Volume_Spec : Visual_Spec<Voxel, Volume_Field_Settings, Volume_Prepared_Data> {
struct Volume_Spec {
using Item = Voxel;
using Prepared_Data = Volume_Prepared_Data;
[[nodiscard]] static const Datoviz_Visual_Operations& datoviz_operations();
[[nodiscard]] static bool valid(const Item&) noexcept;
static void prepare(const std::vector<Item>&, Prepared_Data&);
};
}
using Volume_Visual = Basic_Visual<detail::Volume_Spec>;
struct Volume_Visual : Def<Volume_Visual, Basic_Visual,
Tagged_Buffer<Volume_Frame_Tag, Volume_Frame>> {
using Visual_Base = Volume_Visual;
using Frame_Tag = Volume_Frame_Tag;
using Frame = Volume_Frame;
using Specification = detail::Volume_Spec;
using Item = typename Specification::Item;
struct Prop : Prev_Prop { bool operator==(const Prop&) const; };
struct State : Prev_State {
std::size_t item_count{}; /* 最近一次发布的输入项数。 */
std::size_t prepared_item_count{}; /* 最近一次 Prepare 的后端项数。 */
std::uint64_t prepared_revision{}; /* 最近一次 Prepare 的提交版本。 */
bool operator==(const State&) const;
};
struct Private;
};
}
#include "Volume_Visual.ipp"
@@ -5,6 +5,15 @@ inline bool Volume_Spec::valid(const Item& item) noexcept {
return finite(item.value);
}
} // namespace aethera::render_3d::detail
namespace aethera::render_3d {
/* 具体 Visual 复用 Basic_Visual 的 Prepare、发布及缓冲推进钩子。 */
struct Volume_Visual::Private : Prev_Private {};
}
namespace aethera::render_3d::detail {
inline void Volume_Spec::prepare(const std::vector<Item>& items, Prepared_Data& output) {
output.values.reserve(items.size());
for (const auto& item : items) {
+4 -2
View File
@@ -9,9 +9,11 @@ void verify_visual(Item valid, Item) {
auto built = typename Object::template Builder<Object>{}.build();
ASSERT_TRUE(built.has_value());
auto object = std::move(built).value();
object->template set<&Visual::Prop::items>(std::vector<Item>{valid});
object->template pending_buffer<typename Visual::Frame_Tag>().items =
std::vector<Item>{valid};
object->advance();
EXPECT_EQ(object->template read_prop<typename Visual::Base_Tag>().items.size(), 1U);
EXPECT_EQ(object->template current_buffer<typename Visual::Frame_Tag>()
.items.size(), 1U);
}
}
TEST(Render_3D_Visual, Migrated_Families_Build_And_Validate) {