MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
GeometryBindingsProcessor.cpp
Go to the documentation of this file.
2
4
6
9
11
12namespace MayaFlux::Buffers {
13
19
21 const std::string& name,
22 const std::shared_ptr<Nodes::GpuSync::GeometryWriterNode>& node,
23 const std::shared_ptr<VKBuffer>& vertex_buffer)
24{
25 if (!node) {
26 error<std::invalid_argument>(
29 std::source_location::current(),
30 "Cannot bind null geometry node '{}'", name);
31 }
32
33 if (!vertex_buffer) {
34 error<std::invalid_argument>(
37 std::source_location::current(),
38 "Cannot bind geometry node '{}' to null vertex buffer", name);
39 }
40
41 size_t vertex_data_size = node->get_vertex_buffer_size_bytes();
42
43 if (vertex_buffer->get_size_bytes() < vertex_data_size) {
45 "Vertex buffer for '{}' may be too small: {} bytes required, {} available. "
46 "Will upload partial data.",
47 name, vertex_data_size, vertex_buffer->get_size_bytes());
48 }
49
50 std::shared_ptr<VKBuffer> staging = nullptr;
51 if (!vertex_buffer->is_host_visible()) {
52 size_t staging_size = std::max<size_t>(vertex_buffer->get_size_bytes(), vertex_data_size);
53 staging = create_staging_buffer(staging_size);
54
56 "Created staging buffer for device-local geometry '{}' ({} bytes)",
57 name, staging_size);
58 } else {
60 "No staging needed for host-visible geometry '{}'", name);
61 }
62
63 std::shared_ptr<VKBuffer> index_buf = nullptr;
64 std::shared_ptr<VKBuffer> index_staging_buf = nullptr;
65
66 if (node->has_indices()) {
67 const size_t index_data_size = node->get_index_count() * sizeof(uint32_t);
68 index_buf = std::make_shared<VKBuffer>(
69 index_data_size,
72
73 ensure_initialized(index_buf);
74
75 index_staging_buf = create_staging_buffer(index_data_size);
77 "Created index staging buffer for device-local geometry '{}' ({} bytes)",
78 name, index_data_size);
79 }
80
82 .node = node,
83 .gpu_vertex_buffer = vertex_buffer,
84 .staging_buffer = staging,
85 .gpu_index_buffer = index_buf,
86 .index_staging_buffer = index_staging_buf
87 };
88
90 "Bound geometry node '{}' ({} vertices, {} bytes, stride: {}, indices: {})",
91 name, node->get_vertex_count(), vertex_data_size, node->get_vertex_stride(),
92 node->get_index_count());
93}
94
96{
97 if (m_bindings.erase(name) == 0) {
99 "Attempted to unbind non-existent geometry node '{}'", name);
100 } else {
102 "Unbound geometry node '{}'", name);
103 }
104}
105
106bool GeometryBindingsProcessor::has_binding(const std::string& name) const
107{
108 return m_bindings.contains(name);
109}
110
111std::vector<std::string> GeometryBindingsProcessor::get_binding_names() const
112{
113 std::vector<std::string> names;
114 names.reserve(m_bindings.size());
115 for (const auto& [name, _] : m_bindings) {
116 names.push_back(name);
117 }
118 return names;
119}
120
122{
123 return m_bindings.size();
124}
125
126std::optional<GeometryBindingsProcessor::GeometryBinding>
127GeometryBindingsProcessor::get_binding(const std::string& name) const
128{
129 auto it = m_bindings.find(name);
130 if (it != m_bindings.end()) {
131 return it->second;
132 }
133 return std::nullopt;
134}
135
137 std::shared_ptr<Core::VKImage> image, std::string binding)
138{
140 .image = std::move(image),
141 .binding = std::move(binding),
142 };
143 m_texture_dirty.test_and_set(std::memory_order_release);
144}
145
146void GeometryBindingsProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
147{
148 if (m_texture_dirty.test(std::memory_order_acquire)) {
149 m_texture_dirty.clear(std::memory_order_release);
150 if (auto vk = std::dynamic_pointer_cast<VKBuffer>(buffer)) {
151 if (auto rp = vk->get_render_processor()) {
152 if (m_pending_texture) {
153 rp->bind_texture(m_pending_texture->binding, m_pending_texture->image);
154 }
155 }
156 }
157 m_pending_texture.reset();
158 }
159
160 if (m_bindings.empty()) {
161 return;
162 }
163
164 auto vk_buffer = std::dynamic_pointer_cast<VKBuffer>(buffer);
165 if (!vk_buffer) {
167 "GeometryBindingsProcessor requires VKBuffer, got different buffer type");
168 return;
169 }
170
171 for (auto& [name, binding] : m_bindings) {
172 if (!binding.node->needs_gpu_update()) {
174 "Geometry '{}' unchanged, skipping upload", name);
175 continue;
176 }
177
178 auto vertices = binding.node->get_vertex_data();
179
180 if (vertices.empty()) {
181 if (binding.gpu_vertex_buffer->is_host_visible()) {
182 binding.gpu_vertex_buffer->clear();
183 }
184
185 if (binding.node->get_vertex_layout()) {
186 binding.gpu_vertex_buffer->set_vertex_layout(
187 binding.node->get_vertex_layout().value());
188 }
189
191 "Geometry '{}' cleared", name);
192 continue;
193 }
194
196 vertices.data(),
197 vertices.size_bytes(),
198 binding.gpu_vertex_buffer,
199 binding.staging_buffer);
200
201 if (binding.node->get_vertex_layout()) {
202 binding.gpu_vertex_buffer->set_vertex_layout(
203 binding.node->get_vertex_layout().value());
204
206 "Set vertex layout for '{}' ({} vertices, {} attributes)",
207 name,
208 binding.node->get_vertex_count(),
209 binding.node->get_vertex_layout()->attributes.size());
210 } else {
212 "Geometry node '{}' has no vertex layout. "
213 "RenderProcessor may fail without layout info.",
214 name);
215 }
216
217 upload_index_data(name, binding);
218 binding.node->clear_gpu_update_flag();
219 }
220
221 bool attached_is_target = false;
222 for (const auto& [name, binding] : m_bindings) {
223 if (binding.gpu_vertex_buffer == vk_buffer) {
224 attached_is_target = true;
225 break;
226 }
227 }
228
229 if (!attached_is_target && !m_bindings.empty()) {
230 auto& first_binding = m_bindings.begin()->second;
231 if (first_binding.node->needs_gpu_update()) {
232 auto vertices = first_binding.node->get_vertex_data();
233
234 if (!vertices.empty()) {
236 vertices.data(),
237 vertices.size_bytes(),
238 vk_buffer,
239 first_binding.staging_buffer);
240
241 if (first_binding.node->get_vertex_layout()) {
242 vk_buffer->set_vertex_layout(
243 first_binding.node->get_vertex_layout().value());
244 }
245 }
246 upload_index_data(m_bindings.begin()->first, first_binding);
247 first_binding.node->clear_gpu_update_flag();
248 }
249 }
250}
251
253 const std::string& name,
254 GeometryBinding& binding)
255{
256 if (!binding.node->has_indices()) {
257 return;
258 }
259
260 const auto indices = binding.node->get_index_data();
261 const size_t required = indices.size_bytes();
262
263 if (required == 0) {
264 return;
265 }
266
267 if (!binding.gpu_index_buffer) {
268 binding.gpu_index_buffer = std::make_shared<VKBuffer>(
269 required,
272
274
276
278 "Lazily created index buffer for '{}' ({} bytes)", name, required);
279 }
280
282 indices.data(),
283 required,
284 binding.gpu_index_buffer,
285 binding.index_staging_buffer);
286
287 binding.gpu_vertex_buffer->set_index_resources(
288 binding.gpu_index_buffer->get_buffer(),
289 binding.gpu_index_buffer->get_buffer_resources().memory,
290 binding.gpu_index_buffer->get_size_bytes());
291
293 "Uploaded {} indices ({} bytes) for '{}'",
294 binding.node->get_index_count(), required, name);
295}
296
297} // namespace MayaFlux::Buffers
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_TRACE(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
IO::ImageData image
Definition Decoder.cpp:57
std::optional< GeometryBinding > get_binding(const std::string &name) const
Get a specific binding.
void set_texture(std::shared_ptr< Core::VKImage > image, std::string binding)
Supply a texture to bind on the next graphics tick.
std::unordered_map< std::string, GeometryBinding > m_bindings
void upload_index_data(const std::string &name, GeometryBinding &binding)
Upload index data for one binding, creating or growing the GPU index buffer as needed.
std::vector< std::string > get_binding_names() const
Get all binding names.
size_t get_binding_count() const
Get number of active bindings.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
BufferProcessor interface - uploads all bound geometries.
void bind_geometry_node(const std::string &name, const std::shared_ptr< Nodes::GpuSync::GeometryWriterNode > &node, const std::shared_ptr< VKBuffer > &vertex_buffer)
Bind a geometry node to a GPU vertex buffer.
bool has_binding(const std::string &name) const
Check if a binding exists.
void unbind_geometry_node(const std::string &name)
Remove a geometry binding.
void ensure_initialized(const std::shared_ptr< VKBuffer > &buffer)
Definition VKBuffer.cpp:471
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.
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
@ UNKNOWN
Unknown or undefined modality.
std::shared_ptr< Nodes::GpuSync::GeometryWriterNode > node
Holds GPU resources for one GeometryWriterNode binding.