105{
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
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
136 binding, vk_buffer, total,
name)) {
137 continue;
138 }
139 }
140
142 if (gpu_data.vertex_data.empty() || gpu_data.vertex_count == 0) {
143 if (vk_buffer->is_host_visible())
144 vk_buffer->clear();
145 continue;
146 }
147
148 size_t total = gpu_data.vertex_data.size();
149
150 if (total > vk_buffer->get_size_bytes()) {
151 vk_buffer->resize(static_cast<size_t>(static_cast<float>(total) * 1.5F), false);
152 }
153
155 upload_to_gpu(gpu_data.vertex_data.data(), total, vk_buffer, binding.staging_buffer);
156
157 if (gpu_data.layout)
158 vk_buffer->set_vertex_layout(*gpu_data.layout);
159
160 binding.last_total_bytes = total;
161
163 "Uploaded {} vertices from network '{}' ({} bytes)",
164 gpu_data.vertex_count,
name, total);
165 continue;
166 }
167
168 struct SliceData {
169 std::span<const uint8_t> bytes;
170 size_t vertex_count {};
171 std::optional<Kakshya::VertexLayout> layout;
172 };
173
174 std::vector<SliceData> slices;
175 slices.reserve(1 +
chain->size());
176
178 slices.push_back({ .bytes = primary.vertex_data, .vertex_count = primary.vertex_count, .layout = primary.layout });
179
180 for (
const auto& op :
chain->operators()) {
181 auto* gfx = dynamic_cast<Nodes::Network::GraphicsOperator*>(op.get());
182 if (!gfx) {
183 slices.push_back({ .bytes = {}, .vertex_count = 0, .layout = std::nullopt });
184 continue;
185 }
186
187 if (!gfx->participates_in_rendering())
188 continue;
189
190 slices.push_back({ .bytes = gfx->get_vertex_data(), .vertex_count = gfx->get_vertex_count(), .layout = gfx->get_vertex_layout() });
191 }
192
193 size_t total_bytes = 0;
194 for (const auto& s : slices)
195 total_bytes += s.bytes.size();
196
197 if (total_bytes == 0) {
198 if (vk_buffer->is_host_visible())
199 vk_buffer->clear();
200 continue;
201 }
202
203 bool chain_gfx_dirty = false;
204 bool needs_full_restore = false;
205 for (
const auto& op :
chain->operators()) {
206 if (op && op->demands_full_vertex_restore())
207 needs_full_restore = true;
208
209 auto* gfx = dynamic_cast<Nodes::Network::GraphicsOperator*>(op.get());
210 if (gfx && gfx->participates_in_rendering() && gfx->is_vertex_data_dirty())
211 chain_gfx_dirty = true;
212 }
213
214 if (!needs_full_restore && !chain_gfx_dirty && primary.layout.has_value()
216 dynamic_cast<Nodes::Network::GraphicsOperator*>(binding.network->get_operator()),
217 primary.layout->stride_bytes, binding, vk_buffer, total_bytes,
name)) {
218 continue;
219 }
220
221 if (total_bytes > vk_buffer->get_size_bytes()) {
222 vk_buffer->resize(static_cast<size_t>(static_cast<float>(total_bytes) * 1.5F), false);
223 }
224
226 size_t byte_offset = 0;
227 for (const auto& s : slices) {
228 if (!s.bytes.empty())
230 byte_offset += s.bytes.size();
231 }
232
235
236 for (
auto& slice :
std::views::
reverse(slices)) {
238 vk_buffer->set_vertex_layout(*
slice.layout);
239 break;
240 }
241 }
242
243 auto ngb = std::dynamic_pointer_cast<NetworkGeometryBuffer>(buffer);
244 if (ngb) {
245 byte_offset = 0;
246 for (size_t i = 0; i < slices.size(); ++i) {
247 const auto& s = slices[i];
248 const uint32_t stride = s.layout ? s.layout->stride_bytes
249 : (slices[0].layout ? slices[0].layout->stride_bytes : 0);
250 const uint32_t vert_offset = stride > 0
251 ? static_cast<uint32_t>(byte_offset / stride)
252 : 0;
253
254 ngb->update_chain_render_range(i, vert_offset,
255 static_cast<uint32_t>(s.vertex_count), s.layout);
256
257 byte_offset += s.bytes.size();
258 }
259 }
260
261 binding.last_total_bytes = total_bytes;
262
264 "Uploaded {} bytes ({} slices) from network '{}'",
265 total_bytes, slices.size(),
name);
266 }
267}
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
std::unordered_map< std::string, NetworkBinding > m_bindings
std::vector< uint8_t > m_staging_aggregate
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 ensure_staging(NetworkBinding &binding, size_t bytes)
Grow a binding's staging buffer to hold at least bytes.
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.
std::vector< double > slice(std::span< const double > data, double start_ratio, double end_ratio)
Extract a contiguous slice by ratio, returning a new vector.
Tendency< A, C > chain(const Tendency< A, B > &first, const Tendency< B, C > &second)
Sequential composition: evaluate first, feed result into second.
void reverse(std::vector< double > &data)
Reverse time order of single-channel data (in-place)