MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NetworkGeometryProcessor.cpp
Go to the documentation of this file.
2
4
8
10
11namespace MayaFlux::Buffers {
12
18
20 const std::string& name,
21 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
22 const std::shared_ptr<VKBuffer>& vertex_buffer)
23{
24 if (!network) {
25 error<std::invalid_argument>(
28 std::source_location::current(),
29 "Cannot bind null network '{}'", name);
30 }
31
32 if (!vertex_buffer) {
33 error<std::invalid_argument>(
36 std::source_location::current(),
37 "Cannot bind network '{}' to null vertex buffer", name);
38 }
39
40 std::shared_ptr<VKBuffer> staging = nullptr;
41 if (!vertex_buffer->is_host_visible()) {
42 staging = create_staging_buffer(vertex_buffer->get_size_bytes());
43
45 "Created staging buffer for device-local network geometry '{}' ({} bytes)",
46 name, vertex_buffer->get_size_bytes());
47 } else {
49 "No staging needed for host-visible network geometry '{}'", name);
50 }
51
54 .gpu_vertex_buffer = vertex_buffer,
55 .staging_buffer = staging
56 };
57
59 "Bound network '{}' ({} nodes, {} bytes buffer)",
60 name, network->get_node_count(), vertex_buffer->get_size_bytes());
61}
62
64{
65 if (m_bindings.erase(name) == 0) {
67 "Attempted to unbind non-existent network '{}'", name);
68 } else {
70 "Unbound network '{}'", name);
71 }
72}
73
74bool NetworkGeometryProcessor::has_binding(const std::string& name) const
75{
76 return m_bindings.contains(name);
77}
78
79std::vector<std::string> NetworkGeometryProcessor::get_binding_names() const
80{
81 std::vector<std::string> names;
82 names.reserve(m_bindings.size());
83 for (const auto& [name, _] : m_bindings) {
84 names.push_back(name);
85 }
86 return names;
87}
88
90{
91 return m_bindings.size();
92}
93
94std::optional<NetworkGeometryProcessor::NetworkBinding>
96{
97 auto it = m_bindings.find(name);
98 if (it != m_bindings.end()) {
99 return it->second;
100 }
101 return std::nullopt;
102}
103
104void NetworkGeometryProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
105{
106 if (m_bindings.empty())
107 return;
108
109 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
110 if (!vk_buffer) {
112 "NetworkGeometryProcessor requires VKBuffer");
113 return;
114 }
115
116 for (auto& [name, binding] : m_bindings) {
117 if (!binding.network || !binding.network->is_enabled()) {
119 "Network '{}' disabled, skipping upload", name);
120 continue;
121 }
122
123 const auto* chain = binding.network->get_operator_chain().get();
124 const bool has_chain = chain && !chain->empty();
125
126 if (!has_chain) {
127 auto* primary_op = dynamic_cast<Nodes::Network::GraphicsOperator*>(
128 binding.network->get_operator());
129
130 if (primary_op && primary_op->supports_incremental_upload()) {
131 const auto layout = primary_op->get_vertex_layout();
132 const size_t total = static_cast<size_t>(primary_op->get_vertex_count())
133 * layout.stride_bytes;
134 if (total > 0
135 && try_incremental_upload(primary_op, layout.stride_bytes,
136 binding, vk_buffer, total, name)) {
137 continue;
138 }
139 }
140
141 auto gpu_data = extract_network_gpu_data(binding.network, name);
142 if (gpu_data.vertex_data.empty() || gpu_data.vertex_count == 0) {
143 if (vk_buffer->is_host_visible())
144 vk_buffer->clear();
145 if (gpu_data.layout)
146 vk_buffer->set_vertex_layout(*gpu_data.layout);
147 continue;
148 }
149
150 size_t total = gpu_data.vertex_data.size();
151
152 if (total > vk_buffer->get_size_bytes()) {
153 vk_buffer->resize(static_cast<size_t>(static_cast<float>(total) * 1.5F), false);
154 }
155
156 ensure_staging(binding, total);
157 upload_to_gpu(gpu_data.vertex_data.data(), total, vk_buffer, binding.staging_buffer);
158
159 if (gpu_data.layout)
160 vk_buffer->set_vertex_layout(*gpu_data.layout);
161
162 if (auto ngb = std::dynamic_pointer_cast<NetworkGeometryBuffer>(buffer);
163 ngb && primary_op) {
164 ngb->update_render_runs(primary_op->topology_runs());
165 }
166
167 binding.last_total_bytes = total;
168
170 "Uploaded {} vertices from network '{}' ({} bytes)",
171 gpu_data.vertex_count, name, total);
172 continue;
173 }
174
175 struct SliceData {
176 std::span<const uint8_t> bytes;
177 size_t vertex_count {};
178 std::optional<Kakshya::VertexLayout> layout;
180 };
181
182 std::vector<SliceData> slices;
183 slices.reserve(1 + chain->size());
184
185 auto primary = extract_network_gpu_data(binding.network, name);
186 slices.push_back({ .bytes = primary.vertex_data,
187 .vertex_count = primary.vertex_count,
188 .layout = primary.layout,
189 .op = dynamic_cast<Nodes::Network::GraphicsOperator*>(
190 binding.network->get_operator()) });
191
192 for (const auto& op : chain->operators()) {
193 auto* gfx = dynamic_cast<Nodes::Network::GraphicsOperator*>(op.get());
194 if (!gfx) {
195 slices.push_back({ .bytes = {}, .vertex_count = 0, .layout = std::nullopt, .op = nullptr });
196 continue;
197 }
198
199 if (!gfx->participates_in_rendering())
200 continue;
201
202 slices.push_back({ .bytes = gfx->get_vertex_data(),
203 .vertex_count = gfx->get_vertex_count(),
204 .layout = gfx->get_vertex_layout(),
205 .op = gfx });
206 }
207
208 size_t total_bytes = 0;
209 for (const auto& s : slices)
210 total_bytes += s.bytes.size();
211
212 if (total_bytes == 0) {
213 if (vk_buffer->is_host_visible())
214 vk_buffer->clear();
215 if (primary.layout)
216 vk_buffer->set_vertex_layout(*primary.layout);
217 continue;
218 }
219
220 bool chain_gfx_dirty = false;
221 bool needs_full_restore = false;
222 for (const auto& op : chain->operators()) {
223 if (op && op->demands_full_vertex_restore())
224 needs_full_restore = true;
225
226 auto* gfx = dynamic_cast<Nodes::Network::GraphicsOperator*>(op.get());
227 if (gfx && gfx->participates_in_rendering() && gfx->is_vertex_data_dirty())
228 chain_gfx_dirty = true;
229 }
230
231 if (!needs_full_restore && !chain_gfx_dirty && primary.layout.has_value()
233 dynamic_cast<Nodes::Network::GraphicsOperator*>(binding.network->get_operator()),
234 primary.layout->stride_bytes, binding, vk_buffer, total_bytes, name)) {
235 continue;
236 }
237
238 if (total_bytes > vk_buffer->get_size_bytes()) {
239 vk_buffer->resize(static_cast<size_t>(static_cast<float>(total_bytes) * 1.5F), false);
240 }
241
242 m_staging_aggregate.resize(total_bytes);
243 size_t byte_offset = 0;
244 for (const auto& s : slices) {
245 if (!s.bytes.empty())
246 std::memcpy(m_staging_aggregate.data() + byte_offset, s.bytes.data(), s.bytes.size());
247 byte_offset += s.bytes.size();
248 }
249
250 ensure_staging(binding, total_bytes);
251 upload_to_gpu(m_staging_aggregate.data(), total_bytes, vk_buffer, binding.staging_buffer);
252
253 size_t aggregate_vertices = 0;
254 for (const auto& s : slices)
255 aggregate_vertices += s.vertex_count;
256
257 for (auto& slice : std::views::reverse(slices)) {
258 if (slice.layout) {
259 auto aggregate = *slice.layout;
260 aggregate.vertex_count = static_cast<uint32_t>(aggregate_vertices);
261 vk_buffer->set_vertex_layout(aggregate);
262 break;
263 }
264 }
265
266 auto ngb = std::dynamic_pointer_cast<NetworkGeometryBuffer>(buffer);
267 if (ngb) {
268 byte_offset = 0;
269 for (size_t i = 0; i < slices.size(); ++i) {
270 const auto& s = slices[i];
271 const uint32_t stride = s.layout ? s.layout->stride_bytes
272 : (slices[0].layout ? slices[0].layout->stride_bytes : 0);
273 const uint32_t vert_offset = stride > 0
274 ? static_cast<uint32_t>(byte_offset / stride)
275 : 0;
276
277 std::vector<Portal::Graphics::DrawRun> runs;
278 if (s.op) {
279 for (auto run : s.op->topology_runs()) {
280 if (run.vertex_count == 0)
281 continue;
282 run.vertex_offset += vert_offset;
283 runs.push_back(run);
284 }
285 }
286
287 ngb->update_chain_render_range(i, vert_offset,
288 static_cast<uint32_t>(s.vertex_count), s.layout, std::move(runs));
289
290 byte_offset += s.bytes.size();
291 }
292 }
293
294 binding.last_total_bytes = total_bytes;
295
297 "Uploaded {} bytes ({} slices) from network '{}'",
298 total_bytes, slices.size(), name);
299 }
300}
301
303{
304 if (!binding.gpu_vertex_buffer || binding.gpu_vertex_buffer->is_host_visible())
305 return;
306
307 if (binding.staging_buffer && binding.staging_buffer->get_size_bytes() >= bytes)
308 return;
309
310 const auto grown = static_cast<size_t>(static_cast<float>(bytes) * 1.5F);
311 binding.staging_buffer = create_staging_buffer(grown);
312
314 "NetworkGeometryProcessor: staging buffer grown to {} bytes", grown);
315}
316
319 uint32_t primary_stride_bytes,
320 NetworkBinding& binding,
321 const std::shared_ptr<VKBuffer>& vk_buffer,
322 size_t expected_total_bytes,
323 const std::string& name)
324{
325 if (!primary_op || !primary_op->supports_incremental_upload()
326 || primary_stride_bytes == 0
327 || expected_total_bytes != binding.last_total_bytes) {
328 return false;
329 }
330
331 const auto ranges = primary_op->dirty_vertex_ranges();
332
333 for (const auto& r : ranges) {
334 const auto pack = primary_op->get_vertex_data_for_collection(r.group_index);
335 const size_t offset = static_cast<size_t>(r.vertex_offset) * primary_stride_bytes;
336 const size_t length = static_cast<size_t>(r.vertex_count) * primary_stride_bytes;
337
338 if (length == 0)
339 continue;
340
341 if (pack.size() < length || offset + length > vk_buffer->get_size_bytes())
342 return false;
343
344 ensure_staging(binding, length);
345 upload_to_gpu(pack.data(), length, vk_buffer, binding.staging_buffer, offset);
346 }
347
348 primary_op->mark_vertex_data_clean();
349
351 "Network '{}': incremental upload, {} dirty range(s)", name, ranges.size());
352
353 return true;
354}
355
356} // namespace MayaFlux::Buffers
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
uint64_t length
Definition Composite.cpp:11
uint64_t offset
Definition Composite.cpp:10
Core::GlobalNetworkConfig network
Definition Config.cpp:43
std::shared_ptr< NetworkGeometryBuffer > buffer
BufferProcessingChain * chain
uint32_t total
std::string name
Definition VKDevice.cpp:123
std::unordered_map< std::string, NetworkBinding > m_bindings
bool try_incremental_upload(Nodes::Network::GraphicsOperator *primary_op, uint32_t primary_stride_bytes, NetworkBinding &binding, const std::shared_ptr< VKBuffer > &vk_buffer, size_t expected_total_bytes, const std::string &name)
Upload only the vertex sub-ranges a multi-group primary operator reports as dirty,...
void unbind_network(const std::string &name)
Remove a network binding.
bool has_binding(const std::string &name) const
Check if a binding exists.
std::optional< NetworkBinding > get_binding(const std::string &name) const
Get a specific binding.
size_t get_binding_count() const
Get number of active bindings.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
BufferProcessor interface - aggregates and uploads network geometry.
std::vector< std::string > get_binding_names() const
Get all binding names.
void ensure_staging(NetworkBinding &binding, size_t bytes)
Grow a binding's staging buffer to hold at least bytes.
void bind_network(const std::string &name, const std::shared_ptr< Nodes::Network::NodeNetwork > &network, const std::shared_ptr< VKBuffer > &vertex_buffer)
Bind a network to a GPU vertex buffer.
virtual std::span< const uint8_t > get_vertex_data_for_collection(uint32_t idx=0) const =0
Get vertex data for specific collection (if multiple)
virtual Kakshya::VertexLayout get_vertex_layout() const =0
Get vertex layout describing vertex structure.
virtual bool supports_incremental_upload() const
Whether every vertex mutation on this operator sets a group's dirty flag, so dirty_vertex_ranges() is...
virtual std::vector< DirtyVertexRange > dirty_vertex_ranges() const
Vertex sub-ranges that changed since the last mark_vertex_data_clean().
virtual void mark_vertex_data_clean()=0
Clear dirty flag after GPU upload.
Operator that produces GPU-renderable geometry.
void run()
Definition main.cpp:21
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
std::shared_ptr< VKBuffer > create_staging_buffer(size_t size)
Create staging buffer for transfers.
void upload_to_gpu(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, size_t dst_offset)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
NetworkGpuData extract_network_gpu_data(const std::shared_ptr< Nodes::Network::NodeNetwork > &network, std::string_view name)
Extract GPU geometry data from a NodeNetwork via its GraphicsOperator.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
size_t last_total_bytes
Aggregate byte size of the most recent full upload.
Structure representing a network geometry binding.
uint32_t stride_bytes
Total bytes per vertex (stride in Vulkan terms) e.g., 3 floats (position) + 3 floats (normal) = 24 by...