MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
SDFMeshProcessor.hpp
Go to the documentation of this file.
1#pragma once
2
4
6
7namespace MayaFlux::Buffers {
8
9/**
10 * @class SDFMeshProcessor
11 * @brief ComputeProcessor that dispatches mc_emit.comp directly into the
12 * owning VKBuffer (Usage::VERTEX), bypassing all CPU readback.
13 *
14 * Binding layout (matches mc_emit.comp):
15 * set=0, binding=0 SDF corner grid STORAGE (host-visible, written each dirty frame)
16 * set=0, binding=1 Edge table STORAGE (uploaded once at init)
17 * set=0, binding=2 Tri table STORAGE (uploaded once at init)
18 * set=0, binding=3 Vertex output STORAGE (the VKBuffer this processor is attached to)
19 * set=0, binding=4 Atomic vertex counter STORAGE (host-visible, read back after dispatch)
20 *
21 * The SDF grid is evaluated on CPU via a parallel for_each into the grid
22 * buffer's mapped pointer each dirty frame. No staging or upload call is
23 * needed: the buffer is eHostVisible | eHostCoherent.
24 *
25 * After dispatch, the counter buffer's mapped pointer is read to obtain the
26 * actual vertex count, which is forwarded to the owner via the callback
27 * supplied at construction.
28 *
29 * Dirtiness is managed by the owner. on_before_execute returns false when
30 * not dirty, suppressing dispatch for that frame.
31 */
32class MAYAFLUX_API SDFMeshProcessor : public ComputeProcessor {
33public:
34 /**
35 * @param field SpatialField evaluated on CPU each dirty frame.
36 * @param bounds_min World-space minimum corner.
37 * @param bounds_max World-space maximum corner.
38 * @param res_x Cell count along X (minimum 1).
39 * @param res_y Cell count along Y (minimum 1).
40 * @param res_z Cell count along Z (minimum 1).
41 * @param iso_level Isosurface threshold.
42 */
45 const glm::vec3& bounds_min,
46 const glm::vec3& bounds_max,
47 uint32_t res_x,
48 uint32_t res_y,
49 uint32_t res_z,
50 float iso_level);
51
52 /**
53 * @brief Construct in GPU-field mode with externally owned buffers.
54 *
55 * @p grid_buf and @p counter_buf are owned by SdfPrepProcessor and
56 * pre-zeroed before this processor runs. evaluate_grid() is never
57 * called in this mode.
58 *
59 * @param grid_buf Corner grid buffer written by SdfFieldProcessor.
60 * @param counter_buf Atomic vertex counter buffer, zeroed by SdfPrepProcessor.
61 * @param bounds_min World-space minimum corner.
62 * @param bounds_max World-space maximum corner.
63 * @param res_x Cell count along X.
64 * @param res_y Cell count along Y.
65 * @param res_z Cell count along Z.
66 * @param iso_level Isosurface threshold.
67 */
69 std::shared_ptr<VKBuffer> grid_buf,
70 std::shared_ptr<VKBuffer> counter_buf,
71 const glm::vec3& bounds_min,
72 const glm::vec3& bounds_max,
73 uint32_t res_x,
74 uint32_t res_y,
75 uint32_t res_z,
76 float iso_level);
77
78 ~SDFMeshProcessor() override = default;
79
80 /** @brief Replace the scalar field and mark dirty. */
81 void set_field(Kinesis::SpatialField field);
82
83 /** @brief Replace the evaluation volume and mark dirty. */
84 void set_bounds(const glm::vec3& bounds_min, const glm::vec3& bounds_max);
85
86 /**
87 * @brief Replace the grid resolution and mark dirty.
88 *
89 * Triggers recreation of the grid and counter buffers on the next attach
90 * or dirty frame. Descriptor rebuild is forced.
91 */
92 void set_resolution(uint32_t res_x, uint32_t res_y, uint32_t res_z);
93
94 /** @brief Replace the iso_level threshold and mark dirty. */
95 void set_iso_level(float iso_level);
96
97 /** @brief Force a re-dispatch on the next frame. */
98 void set_dirty() { m_dirty = true; }
99
100 [[nodiscard]] bool is_dirty() const { return m_dirty; }
101
102 /**
103 * @brief Color each triangle by the dominant axis of its surface normal.
104 * @param x Color for surfaces facing along X.
105 * @param y Color for surfaces facing along Y.
106 * @param z Color for surfaces facing along Z.
107 *
108 * Selection is a hard choice rather than a blend, so the surface
109 * resolves into flat regions that switch as the geometry turns.
110 * Three identical colors give a flat surface, which is what the
111 * default reproduces. Takes effect next cycle.
112 */
113 void set_axis_palette(const glm::vec3& x, const glm::vec3& y, const glm::vec3& z);
114
115 /** @brief The three axis colors currently written into vertex color. */
116 [[nodiscard]] const std::array<glm::vec3, 3>& get_axis_palette() const { return m_palette; }
117
118 /**
119 * @brief Atomic vertex counter buffer, host-visible in both construction
120 * modes: owned and zeroed here when this processor evaluates its
121 * own field, or owned by SDFPrepProcessor and passed in for the
122 * GPU-field construction mode. Read via get_mapped_ptr() for the
123 * true live vertex count after a dispatch, which is the count a
124 * readback of the owning VKBuffer must be sized to rather than
125 * the buffer's own worst-case allocated capacity.
126 */
127 [[nodiscard]] std::shared_ptr<VKBuffer> counter_buf() const { return m_counter_buf; }
128
129 /**
130 * @brief The live vertex count the most recent dispatch produced.
131 *
132 * Read from counter_buf()'s host mapping with no transfer. The owning
133 * buffer's allocation is worst-case capacity, not this count.
134 *
135 * @return The count, or nullopt when the counter buffer is not allocated and mapped.
136 */
137 [[nodiscard]] std::optional<uint32_t> live_vertex_count() const;
138
139protected:
140 void on_attach(const std::shared_ptr<Buffer>& buffer) override;
141 void on_descriptors_created() override;
142 bool on_before_execute(
144 const std::shared_ptr<VKBuffer>& buffer) override;
145 void on_after_execute(
147 const std::shared_ptr<VKBuffer>& buffer) override;
148
149private:
151 glm::vec3 m_bounds_min;
152 glm::vec3 m_bounds_max;
153 uint32_t m_res_x;
154 uint32_t m_res_y;
155 uint32_t m_res_z;
157 bool m_dirty { true };
158 bool m_owns_buffers { true }; ///< false in GPU-field mode; buffers owned by SdfPrepProcessor.
159 std::array<glm::vec3, 3> m_palette { glm::vec3(1.0F), glm::vec3(1.0F), glm::vec3(1.0F) };
160
161 std::shared_ptr<VKBuffer> m_grid_buf;
162 std::shared_ptr<VKBuffer> m_edge_buf;
163 std::shared_ptr<VKBuffer> m_tri_buf;
164 std::shared_ptr<VKBuffer> m_counter_buf;
165
166 struct McPC {
167 glm::vec3 bounds_min;
169 glm::vec3 step;
170 uint32_t res_x;
171 uint32_t res_y;
172 uint32_t res_z;
173 uint32_t _pad {};
174 alignas(16) glm::vec4 palette[3] {}; ///< Axis-selected vertex colors, X then Y then Z.
175 };
176 static_assert(sizeof(McPC) % 16 == 0);
177
178 void rebuild_owned_buffers();
179 void evaluate_grid();
180 void rebuild_lookup_buffers();
181
182 [[nodiscard]] uint32_t corner_count() const noexcept
183 {
184 return (m_res_x + 1) * (m_res_y + 1) * (m_res_z + 1);
185 }
186
187 [[nodiscard]] uint32_t voxel_count() const noexcept
188 {
189 return m_res_x * m_res_y * m_res_z;
190 }
191
192 [[nodiscard]] uint32_t worst_case_vertices() const noexcept
193 {
194 return voxel_count() * 15U;
195 }
196};
197
198} // namespace MayaFlux::Buffers
std::shared_ptr< NetworkGeometryBuffer > buffer
Specialized ShaderProcessor for Compute Pipelines.
const std::array< glm::vec3, 3 > & get_axis_palette() const
The three axis colors currently written into vertex color.
std::shared_ptr< VKBuffer > counter_buf() const
Atomic vertex counter buffer, host-visible in both construction modes: owned and zeroed here when thi...
~SDFMeshProcessor() override=default
std::shared_ptr< VKBuffer > m_grid_buf
void set_dirty()
Force a re-dispatch on the next frame.
uint32_t corner_count() const noexcept
std::shared_ptr< VKBuffer > m_tri_buf
std::shared_ptr< VKBuffer > m_counter_buf
uint32_t worst_case_vertices() const noexcept
uint32_t voxel_count() const noexcept
std::shared_ptr< VKBuffer > m_edge_buf
ComputeProcessor that dispatches mc_emit.comp directly into the owning VKBuffer (Usage::VERTEX),...
Typed, composable, stateless callable from domain D to range R.
Definition Tendency.hpp:22