改结构
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user