MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
MeshNetworkProcessor.cpp
Go to the documentation of this file.
2
4
7
9
10namespace MayaFlux::Buffers {
11
13 std::shared_ptr<Nodes::Network::MeshNetwork> network)
14 : m_network(std::move(network))
15{
18}
19
20// =============================================================================
21// on_attach
22// =============================================================================
23
24void MeshNetworkProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
25{
26 if (!m_network || m_network->slot_count() == 0) {
28 "MeshNetworkProcessor attached to empty network");
29 return;
30 }
31
32 auto vk_buf = std::dynamic_pointer_cast<VKBuffer>(buffer);
33 if (!vk_buf) {
35 "MeshNetworkProcessor: attached buffer is not a VKBuffer");
36 return;
37 }
38
39 m_network->ensure_sorted();
40
41 ensure_initialized(vk_buf);
42
44 upload_combined(vk_buf);
47
49 "MeshNetworkProcessor: initialised — {} slots, {} bytes vertex, {} indices",
50 m_network->slot_count(),
51 m_vertex_aggregate.size(),
52 m_index_aggregate.size());
53}
54
55void MeshNetworkProcessor::on_detach(const std::shared_ptr<Buffer>& /*buffer*/)
56{
57 m_gpu_index_buffer.reset();
58 m_vertex_staging.reset();
59 m_index_staging.reset();
60 m_model_ssbo.reset();
61 m_slot_index_ssbo.reset();
62}
63
64// =============================================================================
65// processing_function
66// =============================================================================
67
68void MeshNetworkProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
69{
70 if (!m_network || !any_slot_dirty())
71 return;
72
73 auto vk_buf = std::dynamic_pointer_cast<VKBuffer>(buffer);
74 if (!vk_buf)
75 return;
76
77 upload_combined(vk_buf);
80
83
85 push_ssbo_bindings(vk_buf);
86
88 "MeshNetworkProcessor: re-uploaded {} bytes vertex, {} indices",
90}
91
92// =============================================================================
93// Private helpers
94// =============================================================================
95
97 const std::shared_ptr<VKBuffer>& vertex_buf)
98{
99 const size_t v_bytes = total_vertex_bytes();
100 const size_t i_bytes = total_index_count() * sizeof(uint32_t);
101
102 if (v_bytes == 0 || i_bytes == 0) {
104 "MeshNetworkProcessor::allocate_gpu_buffers: network has no geometry");
105 return;
106 }
107
108 if (!vertex_buf->is_host_visible())
110
111 m_gpu_index_buffer = std::make_shared<VKBuffer>(
115
117 "MeshNetworkProcessor: allocated vertex ({} bytes) + index ({} bytes)",
118 v_bytes, i_bytes);
119}
120
122 const std::shared_ptr<VKBuffer>& vertex_buf)
123{
124 const auto& slots = m_network->slots();
125 const auto& order = m_network->sorted_indices();
126
127 m_vertex_aggregate.clear();
128 m_index_aggregate.clear();
129 m_model_aggregate.clear();
131
132 uint32_t running_vertex_count = 0;
133
134 for (uint32_t idx : order) {
135 const auto& slot = slots[idx];
136 if (!slot.node)
137 continue;
138
139 const auto& verts = slot.node->get_mesh_vertices();
140 const auto& indices = slot.node->get_mesh_indices();
141
142 const size_t v_bytes = verts.size() * sizeof(Nodes::MeshVertex);
143 const auto* v_src = reinterpret_cast<const uint8_t*>(verts.data());
144 m_vertex_aggregate.insert(m_vertex_aggregate.end(), v_src, v_src + v_bytes);
145
146 for (uint32_t i : indices)
147 m_index_aggregate.push_back(i + running_vertex_count);
148
149 m_model_aggregate.push_back(slot.world_transform);
150
151 const auto sorted_slot_pos = static_cast<uint32_t>(m_model_aggregate.size() - 1);
153 m_slot_index_aggregate.end(), verts.size(), sorted_slot_pos);
154
155 running_vertex_count += static_cast<uint32_t>(verts.size());
156 }
157
158 if (m_vertex_aggregate.empty())
159 return;
160
161 const size_t v_required = m_vertex_aggregate.size();
162 if (v_required > vertex_buf->get_size_bytes()) {
163 const auto new_size = static_cast<size_t>(
164 static_cast<float>(v_required) * 1.5F);
165 vertex_buf->resize(new_size, false);
167 m_vertex_staging->resize(new_size, false);
168 }
169
171 m_vertex_aggregate.data(),
172 v_required,
173 vertex_buf,
174 vertex_buf->is_host_visible() ? nullptr : m_vertex_staging);
175
176 const size_t i_required = m_index_aggregate.size() * sizeof(uint32_t);
177 if (m_gpu_index_buffer && i_required > m_gpu_index_buffer->get_size_bytes()) {
178 const auto new_size = static_cast<size_t>(
179 static_cast<float>(i_required) * 1.5F);
180 m_gpu_index_buffer->resize(new_size, false);
181 if (m_index_staging)
182 m_index_staging->resize(new_size, false);
183 }
184
185 if (m_gpu_index_buffer && !m_index_aggregate.empty()) {
187 m_index_aggregate.data(),
188 i_required,
191 }
192
193 for (uint32_t idx : order) {
194 const auto& slot = slots[idx];
195 if (slot.node && slot.node->get_mesh_vertex_count() > 0) {
196 if (auto layout = slot.node->get_vertex_layout()) {
197 layout->vertex_count = running_vertex_count;
198 vertex_buf->set_vertex_layout(*layout);
199 }
200 break;
201 }
202 }
203}
204
206 const std::shared_ptr<VKBuffer>& vertex_buf)
207{
209 return;
210
211 vertex_buf->set_index_resources(
212 m_gpu_index_buffer->get_buffer(),
213 m_gpu_index_buffer->get_buffer_resources().memory,
214 m_gpu_index_buffer->get_size_bytes());
215}
216
218{
219 return std::ranges::any_of(m_network->slots(), [](const auto& slot) {
220 return slot.dirty || (slot.node && slot.node->needs_gpu_update());
221 });
222}
223
225{
226 for (auto& slot : m_network->slots()) {
227 slot.dirty = false;
228 if (slot.node)
229 slot.node->clear_gpu_update_flag();
230 }
231}
232
234{
235 size_t total = 0;
236 for (const auto& slot : m_network->slots()) {
237 if (slot.node)
238 total += slot.node->get_mesh_vertex_count() * sizeof(Nodes::MeshVertex);
239 }
240 return total;
241}
242
244{
245 size_t total = 0;
246 for (const auto& slot : m_network->slots()) {
247 if (slot.node)
248 total += slot.node->get_mesh_indices().size();
249 }
250 return total;
251}
252
254{
255 const size_t model_bytes = m_model_aggregate.size() * sizeof(glm::mat4);
256 const size_t slot_index_bytes = m_slot_index_aggregate.size() * sizeof(uint32_t);
257
258 if (model_bytes == 0 || slot_index_bytes == 0) {
259 MF_WARN(Journal::Component::Buffers, Journal::Context::BufferProcessing, "MeshNetworkProcessor::allocate_ssbo_buffers: aggregates empty at alloc time");
260 return;
261 }
262
263 m_model_ssbo = std::make_shared<VKBuffer>(
266
267 m_slot_index_ssbo = std::make_shared<VKBuffer>(
270}
271
273{
275 return;
276
278 m_model_aggregate.data(),
279 m_model_aggregate.size() * sizeof(glm::mat4),
281 nullptr);
282
285 m_slot_index_aggregate.size() * sizeof(uint32_t),
287 nullptr);
288}
289
290void MeshNetworkProcessor::push_ssbo_bindings(const std::shared_ptr<VKBuffer>& buffer)
291{
293 return;
294
295 auto& bindings = buffer->get_engine_context().ssbo_bindings;
296
297 auto push = [&](uint32_t binding_idx, const std::shared_ptr<VKBuffer>& ssbo) {
299 info.set = 0;
300 info.binding = binding_idx;
301 info.type = vk::DescriptorType::eStorageBuffer;
302 info.buffer_info.buffer = ssbo->get_buffer();
303 info.buffer_info.offset = 0;
304 info.buffer_info.range = ssbo->get_size_bytes();
305
306 auto it = std::ranges::find_if(bindings, [&](const auto& b) {
307 return b.set == info.set && b.binding == info.binding;
308 });
309 if (it != bindings.end()) {
310 *it = info;
311 } else {
312 bindings.push_back(info);
313 }
314 };
315
316 push(1, m_model_ssbo);
317 push(2, m_slot_index_ssbo);
318}
319
320} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
size_t b
std::shared_ptr< VKBuffer > m_slot_index_ssbo
Per-vertex slot index. One uint32_t per concatenated vertex.
std::vector< uint32_t > m_slot_index_aggregate
CPU-side scratch for per-vertex slot indices, reused each cycle.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
The core processing function that must be implemented by derived classes.
std::vector< uint8_t > m_vertex_aggregate
Aggregate scratch buffers reused each cycle to avoid allocation.
void link_index_resources(const std::shared_ptr< VKBuffer > &vertex_buf)
void upload_combined(const std::shared_ptr< VKBuffer > &vertex_buf)
MeshNetworkProcessor(std::shared_ptr< Nodes::Network::MeshNetwork > network)
void allocate_gpu_buffers(const std::shared_ptr< VKBuffer > &vertex_buf)
void push_ssbo_bindings(const std::shared_ptr< VKBuffer > &buffer)
std::shared_ptr< Nodes::Network::MeshNetwork > m_network
std::shared_ptr< VKBuffer > m_model_ssbo
Per-slot world matrices in sorted_indices order. One mat4 per slot.
void on_detach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is detached from a buffer.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when this processor is attached to a buffer.
std::vector< glm::mat4 > m_model_aggregate
CPU-side scratch for model matrices, reused each cycle.
Registry::Service::BufferService * m_buffer_service
Definition VKBuffer.hpp:651
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:462
@ COMPUTE
Storage buffer for compute shaders.
void upload_resizing(const void *data, size_t size, const std::shared_ptr< VKBuffer > &target, const std::shared_ptr< VKBuffer > &staging, float growth_factor)
Upload size bytes to target, growing both buffers first if needed.
@ 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)
Upload raw data to GPU buffer (auto-detects host-visible vs device-local)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
@ UNKNOWN
Unknown or undefined modality.
Vertex type for indexed triangle mesh primitives (TRIANGLE_LIST topology)
std::function< void(const std::shared_ptr< void > &)> initialize_buffer
Initialize a buffer object.