MayaFlux 0.5.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
NetworkGeometryBuffer.cpp
Go to the documentation of this file.
2
4
9
12
14
15namespace MayaFlux::Buffers {
16
17namespace {
18 Portal::Graphics::RenderConfig resolve_config(const Portal::Graphics::RenderConfig& config)
19 {
20 VKBuffer::RenderConfig resolved_config = config;
21
22 switch (config.topology) {
24 if (config.vertex_shader.empty())
25 resolved_config.vertex_shader = "point.vert.spv";
26 if (config.fragment_shader.empty())
27 resolved_config.fragment_shader = "point.frag.spv";
28 break;
31
32 if (config.vertex_shader.empty())
33 resolved_config.vertex_shader = "line.vert.spv";
34
35 if (config.fragment_shader.empty())
36 resolved_config.fragment_shader = "line.frag.spv";
37
38#ifndef MAYAFLUX_PLATFORM_MACOS
39 if (config.geometry_shader.empty())
40 resolved_config.geometry_shader = "line.geom.spv";
41#else
43#endif // !MAYAFLUX_PLATFORM_MACOS
44 break;
47 if (config.vertex_shader.empty())
48 resolved_config.vertex_shader = "triangle.vert.spv";
49 if (config.fragment_shader.empty())
50 resolved_config.fragment_shader = "triangle.frag.spv";
51 break;
52 default:
53 if (config.vertex_shader.empty())
54 resolved_config.vertex_shader = "point.vert.spv";
55 if (config.fragment_shader.empty())
56 resolved_config.fragment_shader = "point.frag.spv";
57 }
58
59 return resolved_config;
60 }
61
62}
63
65 std::shared_ptr<Nodes::Network::NodeNetwork> network,
66 const std::string& binding_name,
67 float over_allocate_factor)
68 : VKBuffer(
69 calculate_buffer_size(network, over_allocate_factor),
70 Usage::VERTEX,
71 Kakshya::DataModality::VERTEX_POSITIONS_3D)
72 , m_network(std::move(network))
73 , m_binding_name(binding_name)
74{
75 if (!m_network) {
76 error<std::invalid_argument>(
79 std::source_location::current(),
80 "Cannot create NetworkGeometryBuffer with null NodeNetwork");
81 }
82
84 "Created NetworkGeometryBuffer '{}' for {} nodes ({} bytes estimated)",
86 m_network->get_node_count(),
88}
89
91{
92 auto self = std::dynamic_pointer_cast<NetworkGeometryBuffer>(shared_from_this());
93
94 m_processor = std::make_shared<NetworkGeometryProcessor>();
95 m_processor->set_processing_token(token);
96 m_processor->bind_network(
99 self);
100
102
103 auto chain = get_processing_chain();
104 if (!chain) {
105 chain = std::make_shared<BufferProcessingChain>();
107 }
108 chain->set_preferred_token(token);
109
111 "Setup NetworkGeometryProcessor for '{}' with token {}",
112 m_binding_name, static_cast<int>(token));
113
115}
116
118{
119 auto resolved_config = resolve_config(config);
120
121 apply_render_config(resolved_config, ShaderConfig { resolved_config.vertex_shader });
122
123 get_processing_chain()->add_processor(m_render_processor, shared_from_this());
124
125 set_default_render_config(resolved_config);
126}
127
129{
130 if (!m_network) {
131 return 0;
132 }
133
134 auto* operator_ptr = m_network->get_operator();
135 if (operator_ptr) {
136 auto* graphics_op = dynamic_cast<Nodes::Network::GraphicsOperator*>(operator_ptr);
137 if (graphics_op) {
138 return static_cast<uint32_t>(graphics_op->get_vertex_count());
139 }
140 }
141
142 return static_cast<uint32_t>(m_network->get_node_count());
143}
144
146 const std::shared_ptr<Nodes::Network::NodeNetwork>& network,
147 float over_allocate_factor)
148{
149 if (!network) {
150 return 0;
151 }
152
153 size_t node_count = network->get_node_count();
154 if (node_count == 0) {
156 "NodeNetwork has zero nodes. Buffer will be created with minimum size.");
157 return 4096;
158 }
159
160 size_t base_size = 0;
161
162 if (auto* operator_ptr = network->get_operator()) {
163 if (auto graphics_op = dynamic_cast<Nodes::Network::GraphicsOperator*>(operator_ptr)) {
164 size_t vertex_count = graphics_op->get_vertex_count();
165 auto layout = graphics_op->get_vertex_layout();
166
167 if (vertex_count > 0 && layout.stride_bytes > 0) {
168 base_size = vertex_count * layout.stride_bytes;
169
171 "Network geometry buffer sizing: {} vertices × {} bytes = {} bytes (operator: {})",
172 vertex_count, layout.stride_bytes, base_size, operator_ptr->get_type_name());
173 }
174 }
175 }
176
177 if (base_size == 0) {
178 size_t vertex_size = sizeof(Kakshya::PointVertex);
179 base_size = node_count * vertex_size;
180
182 "Network geometry buffer fallback sizing: {} nodes × {} bytes = {} bytes",
183 node_count, vertex_size, base_size);
184 }
185
186 auto allocated_size = static_cast<size_t>(
187 static_cast<float>(base_size) * over_allocate_factor);
188
189 if (over_allocate_factor > 1.0F) {
191 "Over-allocated by {}x: {} → {} bytes",
192 over_allocate_factor, base_size, allocated_size);
193 }
194
195 return allocated_size;
196}
197
199{
200 const auto resolved_config = resolve_config(config);
201 auto self = std::dynamic_pointer_cast<VKBuffer>(shared_from_this());
202
203 std::shared_ptr<RenderProcessor> render;
204 apply_render_config(render, resolved_config, ShaderConfig { resolved_config.vertex_shader });
205
206 get_processing_chain()->add_processor(render, shared_from_this());
207 render->set_buffer_vertex_layout(self, Kakshya::VertexLayout::for_lines());
208 render->set_vertex_range(0, 0);
209
210 m_chain_render_processors.push_back({ .render_processor = render });
211
213 "Added chain render processor #{} to NetworkGeometryBuffer",
215}
216
217std::shared_ptr<RenderProcessor> NetworkGeometryBuffer::get_chain_render_processor(size_t index) const
218{
219 if (index >= m_chain_render_processors.size())
220 return nullptr;
221 return m_chain_render_processors[index].render_processor;
222}
223
225 size_t index,
226 uint32_t vertex_offset,
227 uint32_t vertex_count,
228 const std::optional<Kakshya::VertexLayout>& layout)
229{
230 if (index == 0) {
232 m_render_processor->set_vertex_range(vertex_offset, vertex_count);
233 return;
234 }
235
236 const size_t ci = index - 1;
237 if (ci >= m_chain_render_processors.size())
238 return;
239
240 auto& entry = m_chain_render_processors[ci];
241 entry.vertex_offset = vertex_offset;
242 entry.vertex_count = vertex_count;
243 entry.render_processor->set_vertex_range(vertex_offset, vertex_count);
244
245 if (layout) {
246 auto self = std::dynamic_pointer_cast<VKBuffer>(shared_from_this());
247 entry.render_processor->set_buffer_vertex_layout(self, *layout);
248 }
249}
250
251//-----------------------------------------------------------------------------
252// Auxiliary state
253//-----------------------------------------------------------------------------
254
256 const std::string& name,
257 size_t element_count,
258 size_t stride_bytes,
259 bool double_buffered)
260{
261 if (name.empty()) {
263 "NetworkGeometryBuffer: state field declared with empty name, skipped");
264 return false;
265 }
266
267 if (element_count == 0 || stride_bytes == 0) {
269 "NetworkGeometryBuffer: state field '{}' declares zero element count "
270 "or stride, skipped",
271 name);
272 return false;
273 }
274
275 if (m_state_fields.contains(name)) {
277 "NetworkGeometryBuffer: duplicate state field '{}', later declaration "
278 "discarded",
279 name);
280 return false;
281 }
282
283 auto buffer_service = Registry::BackendRegistry::instance()
285
286 if (!buffer_service || !buffer_service->allocate_raw_buffer) {
287 error<std::runtime_error>(
290 std::source_location::current(),
291 "NetworkGeometryBuffer requires a valid buffer service");
292 }
293
294 const auto usage_flags = static_cast<uint32_t>(
295 static_cast<VkBufferUsageFlags>(
296 vk::BufferUsageFlagBits::eStorageBuffer
297 | vk::BufferUsageFlagBits::eTransferSrc
298 | vk::BufferUsageFlagBits::eTransferDst));
299
300 const auto memory_flags = static_cast<uint32_t>(
301 static_cast<VkMemoryPropertyFlags>(vk::MemoryPropertyFlagBits::eDeviceLocal));
302
303 auto& resources = get_buffer_resources();
304 const size_t field_bytes = element_count * stride_bytes;
305 const uint32_t slot_count = double_buffered ? 2 : 1;
306
307 StateField field {
308 .element_count = element_count,
309 .stride_bytes = stride_bytes,
310 .slot_a = static_cast<uint32_t>(resources.back_buffers.size()),
311 .slot_b = 0,
312 .read_is_a = true,
313 };
314
315 for (uint32_t i = 0; i < slot_count; ++i) {
316 void* out_buffer = nullptr;
317 void* out_memory = nullptr;
318 void* out_mapped = nullptr;
319
320 buffer_service->allocate_raw_buffer(
321 field_bytes, usage_flags, memory_flags, false,
322 out_buffer, out_memory, out_mapped);
323
325 slot.buffer = static_cast<vk::Buffer>(static_cast<VkBuffer>(out_buffer));
326 slot.memory = static_cast<vk::DeviceMemory>(static_cast<VkDeviceMemory>(out_memory));
327 slot.mapped_ptr = out_mapped;
328
329 resources.back_buffers.push_back(slot);
330 }
331
332 field.slot_b = double_buffered ? field.slot_a + 1 : field.slot_a;
333
334 m_state_fields.emplace(name, field);
335
337 "NetworkGeometryBuffer: state field '{}', {} elements, stride {}, {} slot(s), {} bytes",
338 name, element_count, stride_bytes, slot_count, field_bytes * slot_count);
339
340 return true;
341}
342
344 const std::string& name, const char* context) const
345{
346 auto it = m_state_fields.find(name);
347 if (it == m_state_fields.end()) {
349 "NetworkGeometryBuffer::{}: no state field named '{}'", context, name);
350 return nullptr;
351 }
352 return &it->second;
353}
354
355bool NetworkGeometryBuffer::has_state(const std::string& name) const
356{
357 return m_state_fields.contains(name);
358}
359
360size_t NetworkGeometryBuffer::get_state_bytes(const std::string& name) const
361{
362 auto it = m_state_fields.find(name);
363 if (it == m_state_fields.end()) {
364 return 0;
365 }
366 return it->second.element_count * it->second.stride_bytes;
367}
368
369vk::Buffer NetworkGeometryBuffer::read_state_handle(const std::string& name) const
370{
371 const auto* field = find_state_field(name, "read_state_handle");
372 if (!field) {
373 return nullptr;
374 }
375
376 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
377 return get_buffer_resources().back_buffers[slot].buffer;
378}
379
381{
382 const auto* field = find_state_field(name, "read_state_slot");
383 if (!field) {
384 return {};
385 }
386
387 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
388 return get_buffer_resources().back_buffers[slot];
389}
390
391vk::Buffer NetworkGeometryBuffer::write_state_handle(const std::string& name) const
392{
393 const auto* field = find_state_field(name, "write_state_handle");
394 if (!field) {
395 return nullptr;
396 }
397
398 const uint32_t slot = field->read_is_a ? field->slot_b : field->slot_a;
399 return get_buffer_resources().back_buffers[slot].buffer;
400}
401
403{
404 const auto* field = find_state_field(name, "write_state_slot");
405 if (!field) {
406 return {};
407 }
408
409 const uint32_t slot = field->read_is_a ? field->slot_b : field->slot_a;
410 return get_buffer_resources().back_buffers[slot];
411}
412
414{
415 auto it = m_state_fields.find(name);
416 if (it == m_state_fields.end()) {
418 "NetworkGeometryBuffer::swap_state: no state field named '{}'", name);
419 return;
420 }
421
422 if (it->second.slot_a == it->second.slot_b) {
423 return;
424 }
425
426 it->second.read_is_a = !it->second.read_is_a;
427}
428
430{
431 if (has_state("hash_cluster_id")) {
432 return true;
433 }
434
435 auto* graphics_op = m_network
436 ? dynamic_cast<Nodes::Network::GraphicsOperator*>(m_network->get_operator())
437 : nullptr;
438 if (!graphics_op) {
439 return false;
440 }
441
442 const size_t vertex_count = graphics_op->get_vertex_count();
443 if (vertex_count == 0) {
444 return false;
445 }
446
447 if (!declare_state("hash_cluster_id", vertex_count, sizeof(uint32_t), false)) {
448 return false;
449 }
450
451 auto cluster_ids = graphics_op->build_cluster_ids();
452 if (cluster_ids.size() != vertex_count) {
453 cluster_ids.assign(vertex_count, 0U);
454 }
455
456 std::shared_ptr<VKBuffer> staging;
458 write_state_slot("hash_cluster_id"),
459 cluster_ids.data(),
460 cluster_ids.size() * sizeof(uint32_t),
461 staging);
462
463 return true;
464}
465
466} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
Core::GlobalNetworkConfig network
Definition Config.cpp:39
std::string name
Definition VKDevice.cpp:143
uint32_t index
Definition VKDevice.cpp:142
std::vector< ChainRenderEntry > m_chain_render_processors
NetworkGeometryBuffer(std::shared_ptr< Nodes::Network::NodeNetwork > network, const std::string &binding_name="network_geometry", float over_allocate_factor=2.0F)
Create geometry buffer from network.
std::shared_ptr< NetworkGeometryProcessor > m_processor
void add_chain_operator_rendering(const RenderConfig &config)
Add a RenderProcessor for a specific operator chain index.
vk::Buffer write_state_handle(const std::string &name) const
Handle a stage should write the named state field to.
size_t get_state_bytes(const std::string &name) const
Byte size of one slot of the named state field, or 0 if undeclared.
uint32_t get_vertex_count() const
Get current vertex count (aggregated from all network nodes)
bool declare_state(const std::string &name, size_t element_count, size_t stride_bytes, bool double_buffered=true)
Declare a named auxiliary state field alongside the vertex data.
std::shared_ptr< Nodes::Network::NodeNetwork > m_network
VKBufferResources::GenerationSlot read_state_slot(const std::string &name) const
Full back_buffers slot a stage should read the named state field from.
const StateField * find_state_field(const std::string &name, const char *context) const
Resolve a declared state field by name.
bool has_state(const std::string &name) const
Whether a state field of this name was declared.
vk::Buffer read_state_handle(const std::string &name) const
Handle a stage should read the named state field from.
void setup_rendering(const RenderConfig &config)
Setup rendering with RenderProcessor.
VKBufferResources::GenerationSlot write_state_slot(const std::string &name) const
Full back_buffers slot a stage should write the named state field to.
void swap_state(const std::string &name)
Exchange read and write slots for the named state field.
void update_chain_render_range(size_t index, uint32_t vertex_offset, uint32_t vertex_count, const std::optional< Kakshya::VertexLayout > &layout)
Update vertex range for a specific operator chain index.
std::unordered_map< std::string, StateField > m_state_fields
void wire_field_operators()
Find the first GpuFieldOperator in the network's operator chain and wire its compute stages: a Vertex...
std::shared_ptr< RenderProcessor > get_chain_render_processor(size_t index) const
Get RenderProcessor for a specific operator chain index.
static size_t calculate_buffer_size(const std::shared_ptr< Nodes::Network::NodeNetwork > &network, float over_allocate_factor)
Calculate initial buffer size based on network node count.
void setup_processors(ProcessingToken token) override
Initialize the buffer and its processors.
bool ensure_cluster_ids()
Ensure the hash_cluster_id state field exists, declaring and populating it from the network's own pri...
std::shared_ptr< Buffers::BufferProcessingChain > get_processing_chain() override
Access the buffer's processing chain.
Definition VKBuffer.cpp:263
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:247
Portal::Graphics::RenderConfig RenderConfig
Definition VKBuffer.hpp:78
vk::DeviceSize get_size_bytes() const
Definition VKBuffer.hpp:278
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
Definition VKBuffer.cpp:268
void apply_render_config(const RenderConfig &config, const ShaderConfig &shader_config)
Configure the internal m_render_processor from a RenderConfig.
Definition VKBuffer.cpp:336
std::shared_ptr< RenderProcessor > m_render_processor
Definition VKBuffer.hpp:620
void set_default_render_config(const RenderConfig &config)
Called by derived classes to set their context-specific defaults.
Definition VKBuffer.hpp:597
const VKBufferResources & get_buffer_resources() const
Get all buffer resources at once (read-only)
Definition VKBuffer.hpp:341
Vulkan-backed buffer wrapper used in processing chains.
Definition VKBuffer.hpp:76
Operator that produces GPU-renderable geometry.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
ProcessingToken
Bitfield enum defining processing characteristics and backend requirements for buffer operations.
void upload_back_buffer(const VKBufferResources::GenerationSlot &slot, const void *data, size_t size, std::shared_ptr< VKBuffer > &staging)
Upload host memory into a raw back_buffers slot.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ Init
Engine/subsystem initialization.
@ Buffers
Buffers, Managers, processors and processing chains.
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
std::vector< GenerationSlot > back_buffers
Definition VKBuffer.hpp:48
Vertex type for point primitives (POINT_LIST topology)
static VertexLayout for_lines(uint32_t stride=60)
Factory: layout for LineVertex (position, color, thickness, uv, normal, tangent)
Unified rendering configuration for graphics buffers.
Backend buffer management service interface.