MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RootGraphicsBuffer.cpp
Go to the documentation of this file.
2
5
8
11
12namespace MayaFlux::Buffers {
13
14GraphicsBatchProcessor::GraphicsBatchProcessor(std::shared_ptr<Buffer> root_buffer)
15 : m_root_buffer(std::dynamic_pointer_cast<RootGraphicsBuffer>(std::move(root_buffer)))
16{
18}
19
20void GraphicsBatchProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
21{
22 auto root_buf = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
23 if (!root_buf || root_buf != m_root_buffer.lock()) {
25 "GraphicsBatchProcessor can only process its associated RootGraphicsBuffer");
26 return;
27 }
28
29 root_buf->clear_renderable_buffers();
30 root_buf->cleanup_marked_buffers();
31
32 for (const auto& child : root_buf->get_child_buffers()) {
33 if (!child)
34 continue;
35 child->clear_pipeline_commands();
36
37 try {
38 if (!child->needs_removal() && child->has_data_for_cycle()) {
39 if (child->needs_default_processing() && child->get_default_processor())
40 child->process_default();
41
42 if (auto chain = child->get_processing_chain()) {
43 if (child->has_data_for_cycle())
44 chain->process_complete(child);
45 }
46
47 if (!child->needs_removal()) {
48 for (const auto& [id, window] : child->get_render_pipelines()) {
49 const auto command = child->get_pipeline_command(id);
51 continue;
52
54 info.buffer = child;
55 info.target_window = window;
56 info.pipeline_id = id;
57 info.command_buffer_id = command;
58 info.needs_depth = child->needs_depth_attachment();
59 root_buf->add_renderable_buffer(info);
60 }
61 }
62 }
63 } catch (const std::exception& e) {
65 "Error processing graphics buffer: {}", e.what());
66 }
67
68 if (child->needs_removal()) {
69 for (const auto& [id, window] : child->get_render_pipelines())
70 root_buf->request_presentation_refresh(window);
71 }
72 for (const auto& window : child->take_presentation_refreshes())
73 root_buf->request_presentation_refresh(window);
74 }
75
76 const auto& refreshes = root_buf->get_pending_presentation_refreshes();
77 if (!refreshes.empty()) {
78 std::erase_if(root_buf->m_renderable_buffers, [&refreshes](const auto& info) {
79 return refreshes.contains(info.target_window);
80 });
81
82 for (const auto& child : root_buf->get_child_buffers()) {
83 if (!child || child->needs_removal())
84 continue;
85
86 for (const auto& [id, window] : child->get_render_pipelines()) {
87 if (!refreshes.contains(window) || !window || !window->is_graphics_registered())
88 continue;
89
90 auto render = child->get_render_processor(id);
91 if (!render || !render->is_visible(child))
92 continue;
93
94 try {
95 auto command = child->get_pipeline_command(id);
97 render->record_draw(child);
98 command = child->get_pipeline_command(id);
99 }
101 continue;
102
104 info.buffer = child;
105 info.target_window = window;
106 info.pipeline_id = id;
107 info.command_buffer_id = command;
108 info.needs_depth = child->needs_depth_attachment();
109 root_buf->add_renderable_buffer(info);
110 } catch (const std::exception& e) {
112 "Error recording retained graphics buffer: {}", e.what());
113 }
114 }
115 }
116 }
117
118 root_buf->sort_renderable_buffers();
119}
120
121void GraphicsBatchProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
122{
123 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
124 if (!root_graphics_buffer) {
125 error<std::invalid_argument>(
128 std::source_location::current(),
129 "GraphicsBatchProcessor can only be attached to RootGraphicsBuffer");
130 }
131
133 error<std::runtime_error>(
136 std::source_location::current(),
137 "GraphicsBatchProcessor token incompatible with RootGraphicsBuffer requirements");
138 }
139}
140
141bool GraphicsBatchProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
142{
143 return std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer) != nullptr;
144}
145
151
157
158void PresentProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
159{
160 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
161 if (!root_graphics_buffer) {
163 "RenderProcessor received non-RootGraphicsBuffer");
164 return;
165 }
166
167 if (m_attached && root_graphics_buffer != m_root_buffer.lock()) {
169 "RenderProcessor processing buffer that doesn't match attached root");
170 return;
171 }
172
173 if (m_callback) {
174 try {
175 m_callback(root_graphics_buffer);
176 } catch (const std::exception& e) {
177 error_rethrow(
180 std::source_location::current(),
181 "RenderProcessor callback threw exception: {}",
182 e.what());
183 }
184 } else {
185 fallback_renderer(root_graphics_buffer);
186 }
187}
188
189void PresentProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
190{
191 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
192 if (!root_graphics_buffer) {
193 error<std::invalid_argument>(
196 std::source_location::current(),
197 "RenderProcessor can only be attached to RootGraphicsBuffer");
198 }
199
201 error<std::runtime_error>(
204 std::source_location::current(),
205 "RenderProcessor token incompatible with RootGraphicsBuffer requirements");
206 }
207
208 m_root_buffer = root_graphics_buffer;
209 m_attached = true;
210
212 "RenderProcessor attached to RootGraphicsBuffer (has_callback: {})",
213 has_callback());
214}
215
216void PresentProcessor::on_detach(const std::shared_ptr<Buffer>& buffer)
217{
218 if (auto root = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer)) {
219 if (root == m_root_buffer.lock()) {
220 m_root_buffer.reset();
221 m_attached = false;
222 }
223 }
224
226 "RenderProcessor detached from RootGraphicsBuffer");
227}
228
229bool PresentProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
230{
231 return std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer) != nullptr;
232}
233
235{
236 m_callback = std::move(callback);
237
239 "RenderProcessor callback {} (attached: {})",
240 m_callback ? "configured" : "cleared",
241 m_attached);
242}
243
244void PresentProcessor::fallback_renderer(const std::shared_ptr<RootGraphicsBuffer>& root)
245{
246 const auto& renderable_buffers = root->get_renderable_buffers();
247 const auto& pending_refreshes = root->get_pending_presentation_refreshes();
248 if (renderable_buffers.empty() && pending_refreshes.empty()) {
249 return;
250 }
251
252 std::unordered_map<std::shared_ptr<Core::Window>, std::vector<const RootGraphicsBuffer::RenderableBufferInfo*>> buffers_by_window;
253 for (auto it = pending_refreshes.begin(); it != pending_refreshes.end();) {
254 const auto& window = *it++;
255 if (window->should_close() || !window->is_graphics_registered()) {
256 root->clear_presentation_refresh(window);
257 continue;
258 }
259 buffers_by_window.try_emplace(window);
260 }
261
262 for (const auto& renderable : renderable_buffers) {
263 if (renderable.target_window && renderable.target_window->is_graphics_registered()
264 && renderable.command_buffer_id != Portal::Graphics::INVALID_COMMAND_BUFFER) {
265 buffers_by_window[renderable.target_window].push_back(&renderable);
266 }
267 }
268
269 if (buffers_by_window.empty()) {
270 root->clear_renderable_buffers();
271 return;
272 }
273
275 "PresentProcessor submitting to {} windows", buffers_by_window.size());
276
277 auto& foundry = Portal::Graphics::get_shader_foundry();
279 auto display_service = Registry::BackendRegistry::instance()
281
282 if (!display_service) {
284 "DisplayService not available for dynamic rendering");
285 return;
286 }
287
288 for (const auto& [window, buffer_infos] : buffers_by_window) {
289 uint64_t image_bits = display_service->acquire_next_swapchain_image(window);
290 if (image_bits == 0) {
292 "Failed to acquire swapchain image for window '{}'",
293 window->get_create_info().title);
294 continue;
295 }
296 vk::Image swapchain_image { reinterpret_cast<VkImage>(image_bits) };
297
298 auto primary_cmd_id = foundry.begin_commands(Portal::Graphics::ShaderFoundry::CommandBufferType::GRAPHICS);
299 auto primary_cmd = foundry.get_command_buffer(primary_cmd_id);
300
301 if (!primary_cmd) {
303 "Failed to create primary command buffer for window '{}'",
304 window->get_create_info().title);
305 for (const auto* info : buffer_infos) {
306 info->buffer->clear_pipeline_commands();
307 }
308 continue;
309 }
310
311 try {
312 bool window_needs_depth = false;
313 for (const auto* info : buffer_infos) {
314 if (info->needs_depth) {
315 window_needs_depth = true;
316 break;
317 }
318 }
319
320 vk::ImageView depth_view = nullptr;
321 if (window_needs_depth) {
322 display_service->ensure_depth_attachment(window);
323 auto* view_ptr = display_service->get_depth_image_view(window);
324 if (view_ptr) {
325 depth_view = *static_cast<vk::ImageView*>(view_ptr);
326 }
327 }
328
329 flow.begin_rendering(primary_cmd_id, window, swapchain_image,
331
332 std::vector<vk::CommandBuffer> secondary_buffers;
333 for (const auto* info : buffer_infos) {
334 auto secondary = foundry.get_command_buffer(info->command_buffer_id);
335 if (secondary) {
336 secondary_buffers.push_back(secondary);
337 }
338 }
339
340 if (!secondary_buffers.empty()) {
341 primary_cmd.executeCommands(secondary_buffers);
342 }
343
344 flow.end_rendering(primary_cmd_id, window);
345
346 foundry.end_commands(primary_cmd_id);
347 uint64_t primary_bits = *reinterpret_cast<uint64_t*>(&primary_cmd);
348 display_service->submit_and_present(window, primary_bits);
349 root->clear_presentation_refresh(window);
350
352 "Presented {} buffers to window '{}'",
353 secondary_buffers.size(), window->get_create_info().title);
354
355 } catch (const std::exception& e) {
357 "Failed to submit/present for window '{}': {}",
358 window->get_create_info().title,
359 e.what());
360 }
361
362 for (const auto* info : buffer_infos) {
363 info->buffer->clear_pipeline_commands();
364 }
365 }
366
367 root->clear_renderable_buffers();
368}
369
376
382
384{
385 auto batch_processor = create_default_processor();
386 if (batch_processor) {
387 set_default_processor(batch_processor);
388 }
389}
390
392{
393 if (this->has_pending_operations()) {
395 }
396
397 get_default_processor()->process(shared_from_this());
398}
399
401{
402 auto it = std::remove_if(
403 m_child_buffers.begin(),
404 m_child_buffers.end(),
405 [this](const std::shared_ptr<VKBuffer>& buf) {
406 if (!buf || !buf->needs_removal())
407 return false;
408 for (const auto& [id, window] : buf->get_render_pipelines())
409 request_presentation_refresh(window);
410 for (const auto& window : buf->take_presentation_refreshes())
411 request_presentation_refresh(window);
412 return true;
413 });
414
415 size_t removed_count = std::distance(it, m_child_buffers.end());
416
417 if (removed_count > 0) {
418 m_child_buffers.erase(it, m_child_buffers.end());
419
421 "Cleaned up {} graphics buffers (remaining: {})",
422 removed_count, m_child_buffers.size());
423 }
424}
425
426void RootGraphicsBuffer::set_final_processor(std::shared_ptr<BufferProcessor> processor)
427{
428 m_final_processor = std::move(processor);
429}
430
431std::shared_ptr<BufferProcessor> RootGraphicsBuffer::get_final_processor() const
432{
433 return m_final_processor;
434}
435
436bool RootGraphicsBuffer::has_buffer(const std::shared_ptr<VKBuffer>& buffer) const
437{
438 return std::ranges::find(m_child_buffers, buffer) != m_child_buffers.end();
439}
440
441std::vector<std::shared_ptr<VKBuffer>> RootGraphicsBuffer::get_buffers_by_usage(VKBuffer::Usage usage) const
442{
443 std::vector<std::shared_ptr<VKBuffer>> filtered_buffers;
444
445 for (const auto& buffer : m_child_buffers) {
446 if (buffer && buffer->get_usage() == usage) {
447 filtered_buffers.push_back(buffer);
448 }
449 }
450
451 return filtered_buffers;
452}
453
454void RootGraphicsBuffer::detach_window(const std::shared_ptr<Core::Window>& window)
455{
456 if (!window)
457 return;
458
459 std::erase_if(m_renderable_buffers, [&window](const RenderableBufferInfo& info) {
460 return info.target_window == window;
461 });
462
463 for (const auto& buffer : m_child_buffers) {
464 if (!buffer)
465 continue;
466
467 auto chain = buffer->get_processing_chain();
468 auto remove_render = [&buffer, &chain, &window](const std::shared_ptr<RenderProcessor>& render) {
469 if (!render || render->get_target_window() != window)
470 return;
471
472 if (buffer->get_default_processor() == render) {
473 buffer->set_default_processor(nullptr);
474 return;
475 }
476
477 if (!chain)
478 return;
479
480 chain->remove_processor_anywhere(render, buffer);
481 };
482
483 if (auto render = buffer->get_render_processor())
484 remove_render(render);
485
486 for (const auto& render : buffer->get_additional_render_processors())
487 remove_render(render);
488 }
489
491}
492
493std::shared_ptr<BufferProcessor> RootGraphicsBuffer::create_default_processor()
494{
495 return std::make_shared<GraphicsBatchProcessor>(shared_from_this());
496}
497
498} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_DEBUG(comp, ctx,...)
std::shared_ptr< NetworkGeometryBuffer > buffer
BufferProcessingChain * chain
PickCallback callback
Definition Picker.cpp:22
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
uint16_t usage
std::weak_ptr< RootGraphicsBuffer > m_root_buffer
Root buffer this processor manages.
bool is_compatible_with(const std::shared_ptr< Buffer > &buffer) const override
Checks compatibility with a specific buffer type.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
Processes a buffer by coordinating child buffer operations.
GraphicsBatchProcessor(std::shared_ptr< Buffer > root_buffer)
Creates a new graphics batch processor.
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when processor is attached to a buffer.
std::function< void(const std::shared_ptr< RootGraphicsBuffer > &root)> RenderCallback
Callback signature for render operations.
void processing_function(const std::shared_ptr< Buffer > &buffer) override
Executes the render callback.
RenderCallback m_callback
User-provided render callback.
bool has_callback() const
Checks if a callback is configured.
std::weak_ptr< RootGraphicsBuffer > m_root_buffer
Root buffer this presenter is attached to.
bool is_compatible_with(const std::shared_ptr< Buffer > &buffer) const override
Checks compatibility with a specific buffer type.
void set_callback(RenderCallback callback)
Sets or updates the render callback.
void fallback_renderer(const std::shared_ptr< RootGraphicsBuffer > &root)
void on_attach(const std::shared_ptr< Buffer > &buffer) override
Called when processor is attached to a buffer.
void on_detach(const std::shared_ptr< Buffer > &buffer) override
Called when processor is detached from a buffer.
PresentProcessor()
Default constructor (no callback set)
bool m_attached
Distinguishes an expired attachment from an unattached presenter.
void process_pending_buffer_operations()
Process pending operations - call this at start of processing cycles.
ProcessingToken m_preferred_processing_token
Preferred processing token for this root buffer.
TokenEnforcementStrategy m_token_enforcement_strategy
Current token enforcement strategy for this root buffer.
std::vector< std::shared_ptr< VKBuffer > > m_child_buffers
Vector of tributary buffers that contribute to this root buffer.
void process_default() override
Processes this root buffer using default processing.
std::vector< RenderableBufferInfo > m_renderable_buffers
std::shared_ptr< BufferProcessor > get_final_processor() const
Gets the current final processor.
std::unordered_set< std::shared_ptr< Core::Window > > m_pending_presentation_refreshes
void cleanup_marked_buffers()
Removes buffers marked for deletion.
void set_final_processor(std::shared_ptr< BufferProcessor > processor)
Sets an optional final processor.
std::vector< std::shared_ptr< VKBuffer > > get_buffers_by_usage(VKBuffer::Usage usage) const
Gets buffers filtered by usage type.
void detach_window(const std::shared_ptr< Core::Window > &window)
Stop all render processors from presenting to a window.
std::shared_ptr< BufferProcessor > m_final_processor
Optional final processor (rarely used in graphics)
void initialize()
Initializes the root buffer with default processor.
bool has_buffer(const std::shared_ptr< VKBuffer > &buffer) const
Checks if a specific buffer is registered.
~RootGraphicsBuffer() override
Virtual destructor ensuring proper cleanup.
RootGraphicsBuffer()
Creates a new root graphics buffer.
std::shared_ptr< BufferProcessor > create_default_processor()
Creates the default graphics batch processor.
Root container for GPU buffer lifecycle management and batch processing.
std::shared_ptr< Buffers::BufferProcessor > get_default_processor() const override
Get the currently attached default processor.
Definition VKBuffer.cpp:266
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:255
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
bool are_tokens_compatible(ProcessingToken preferred, ProcessingToken current)
Determines if two processing tokens are compatible for joint execution.
@ STRICT
Strictly enforces token assignment with no cross-token sharing.
@ BufferManagement
Buffer Management (Buffers::BufferManager, creating buffers)
@ BufferProcessing
Buffer processing (Buffers::BufferManager, processing chains)
@ Buffers
Buffers, Managers, processors and processing chains.
@ Core
Core engine, backend, subsystems.
const std::array< float, 4 > default_color
MAYAFLUX_API RenderFlow & get_render_flow()
Get the global render flow instance.
MAYAFLUX_API ShaderFoundry & get_shader_foundry()
Get the global shader compiler instance.
BufferUsageHint
Semantic usage hint for buffer allocation and memory properties.
constexpr CommandBufferID INVALID_COMMAND_BUFFER
Information about a buffer that's ready to render.
Backend display and presentation service interface.