18 Portal::Graphics::RenderConfig resolve_config(
const Portal::Graphics::RenderConfig& config)
22 switch (config.topology) {
24 if (config.vertex_shader.empty())
26 if (config.fragment_shader.empty())
27 resolved_config.fragment_shader =
"point.frag.spv";
32 if (config.vertex_shader.empty())
33 resolved_config.vertex_shader =
"line.vert.spv";
35 if (config.fragment_shader.empty())
36 resolved_config.fragment_shader =
"line.frag.spv";
38#ifndef MAYAFLUX_PLATFORM_MACOS
39 if (config.geometry_shader.empty())
40 resolved_config.geometry_shader =
"line.geom.spv";
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";
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";
59 return resolved_config;
65 std::shared_ptr<Nodes::Network::NodeNetwork>
network,
66 const std::string& binding_name,
67 float over_allocate_factor)
69 calculate_buffer_size(
network, over_allocate_factor),
71 Kakshya::DataModality::VERTEX_POSITIONS_3D)
73 , m_binding_name(binding_name)
76 error<std::invalid_argument>(
79 std::source_location::current(),
80 "Cannot create NetworkGeometryBuffer with null NodeNetwork");
84 "Created NetworkGeometryBuffer '{}' for {} nodes ({} bytes estimated)",
92 auto self = std::dynamic_pointer_cast<NetworkGeometryBuffer>(shared_from_this());
94 m_processor = std::make_shared<NetworkGeometryProcessor>();
105 chain = std::make_shared<BufferProcessingChain>();
108 chain->set_preferred_token(token);
111 "Setup NetworkGeometryProcessor for '{}' with token {}",
119 auto resolved_config = resolve_config(config);
134 auto* operator_ptr =
m_network->get_operator();
138 return static_cast<uint32_t
>(graphics_op->get_vertex_count());
142 return static_cast<uint32_t
>(
m_network->get_node_count());
146 const std::shared_ptr<Nodes::Network::NodeNetwork>&
network,
147 float over_allocate_factor)
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.");
160 size_t base_size = 0;
162 if (
auto* operator_ptr =
network->get_operator()) {
164 size_t vertex_count = graphics_op->get_vertex_count();
165 auto layout = graphics_op->get_vertex_layout();
167 if (vertex_count > 0 && layout.stride_bytes > 0) {
168 base_size = vertex_count * layout.stride_bytes;
171 "Network geometry buffer sizing: {} vertices × {} bytes = {} bytes (operator: {})",
172 vertex_count, layout.stride_bytes, base_size, operator_ptr->get_type_name());
177 if (base_size == 0) {
179 base_size = node_count * vertex_size;
182 "Network geometry buffer fallback sizing: {} nodes × {} bytes = {} bytes",
183 node_count, vertex_size, base_size);
186 auto allocated_size =
static_cast<size_t>(
187 static_cast<float>(base_size) * over_allocate_factor);
189 if (over_allocate_factor > 1.0F) {
191 "Over-allocated by {}x: {} → {} bytes",
192 over_allocate_factor, base_size, allocated_size);
195 return allocated_size;
200 const auto resolved_config = resolve_config(config);
201 auto self = std::dynamic_pointer_cast<VKBuffer>(shared_from_this());
203 std::shared_ptr<RenderProcessor> render;
208 render->set_vertex_range(0, 0);
213 "Added chain render processor #{} to NetworkGeometryBuffer",
226 uint32_t vertex_offset,
227 uint32_t vertex_count,
228 const std::optional<Kakshya::VertexLayout>& layout)
236 const size_t ci =
index - 1;
241 entry.vertex_offset = vertex_offset;
242 entry.vertex_count = vertex_count;
243 entry.render_processor->set_vertex_range(vertex_offset, vertex_count);
246 auto self = std::dynamic_pointer_cast<VKBuffer>(shared_from_this());
247 entry.render_processor->set_buffer_vertex_layout(self, *layout);
256 const std::string&
name,
257 size_t element_count,
259 bool double_buffered)
263 "NetworkGeometryBuffer: state field declared with empty name, skipped");
267 if (element_count == 0 || stride_bytes == 0) {
269 "NetworkGeometryBuffer: state field '{}' declares zero element count "
270 "or stride, skipped",
277 "NetworkGeometryBuffer: duplicate state field '{}', later declaration "
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");
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));
300 const auto memory_flags =
static_cast<uint32_t
>(
301 static_cast<VkMemoryPropertyFlags
>(vk::MemoryPropertyFlagBits::eDeviceLocal));
304 const size_t field_bytes = element_count * stride_bytes;
305 const uint32_t slot_count = double_buffered ? 2 : 1;
309 .stride_bytes = stride_bytes,
310 .slot_a =
static_cast<uint32_t
>(resources.back_buffers.size()),
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;
320 buffer_service->allocate_raw_buffer(
321 field_bytes, usage_flags, memory_flags,
false,
322 out_buffer, out_memory, out_mapped);
325 slot.
buffer =
static_cast<vk::Buffer
>(
static_cast<VkBuffer
>(out_buffer));
326 slot.
memory =
static_cast<vk::DeviceMemory
>(
static_cast<VkDeviceMemory
>(out_memory));
329 resources.back_buffers.push_back(slot);
332 field.slot_b = double_buffered ? field.slot_a + 1 : field.slot_a;
337 "NetworkGeometryBuffer: state field '{}', {} elements, stride {}, {} slot(s), {} bytes",
338 name, element_count, stride_bytes, slot_count, field_bytes * slot_count);
344 const std::string&
name,
const char* context)
const
349 "NetworkGeometryBuffer::{}: no state field named '{}'", context,
name);
366 return it->second.element_count * it->second.stride_bytes;
376 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
387 const uint32_t slot = field->read_is_a ? field->slot_a : field->slot_b;
398 const uint32_t slot = field->read_is_a ? field->slot_b : field->slot_a;
409 const uint32_t slot = field->read_is_a ? field->slot_b : field->slot_a;
418 "NetworkGeometryBuffer::swap_state: no state field named '{}'",
name);
422 if (it->second.slot_a == it->second.slot_b) {
426 it->second.read_is_a = !it->second.read_is_a;
442 const size_t vertex_count = graphics_op->get_vertex_count();
443 if (vertex_count == 0) {
447 if (!
declare_state(
"hash_cluster_id", vertex_count,
sizeof(uint32_t),
false)) {
451 auto cluster_ids = graphics_op->build_cluster_ids();
452 if (cluster_ids.size() != vertex_count) {
453 cluster_ids.assign(vertex_count, 0U);
456 std::shared_ptr<VKBuffer> staging;
460 cluster_ids.size() *
sizeof(uint32_t),
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
Core::GlobalNetworkConfig network
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.
std::string m_binding_name
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.
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Portal::Graphics::RenderConfig RenderConfig
vk::DeviceSize get_size_bytes() const
void set_processing_chain(const std::shared_ptr< BufferProcessingChain > &chain, bool force=false) override
Replace the buffer's processing chain.
void apply_render_config(const RenderConfig &config, const ShaderConfig &shader_config)
Configure the internal m_render_processor from a RenderConfig.
std::shared_ptr< RenderProcessor > m_render_processor
void set_default_render_config(const RenderConfig &config)
Called by derived classes to set their context-specific defaults.
const VKBufferResources & get_buffer_resources() const
Get all buffer resources at once (read-only)
Vulkan-backed buffer wrapper used in processing chains.
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
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)
std::string vertex_shader
Unified rendering configuration for graphics buffers.
Backend buffer management service interface.