MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RootGraphicsBuffer.cpp
Go to the documentation of this file.
2
4
7
10
12
13namespace MayaFlux::Buffers {
14
15GraphicsBatchProcessor::GraphicsBatchProcessor(std::shared_ptr<Buffer> root_buffer)
16 : m_root_buffer(std::dynamic_pointer_cast<RootGraphicsBuffer>(std::move(root_buffer)))
17{
19}
20
21void GraphicsBatchProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
22{
23 auto root_buf = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
24 if (!root_buf || root_buf != m_root_buffer) {
26 "GraphicsBatchProcessor can only process its associated RootGraphicsBuffer");
27 return;
28 }
29
30 root_buf->cleanup_marked_buffers();
31
32 for (auto& ch_buffer : root_buf->get_child_buffers()) {
33 if (!ch_buffer)
34 continue;
35
36 if (ch_buffer->needs_removal()) {
37 continue;
38 }
39
40 if (!ch_buffer->has_data_for_cycle()) {
41 continue;
42 }
43
44 try {
45 if (ch_buffer->needs_default_processing() && ch_buffer->get_default_processor()) {
46 ch_buffer->process_default();
47 }
48
49 if (auto chain = ch_buffer->get_processing_chain()) {
50 if (ch_buffer->has_data_for_cycle()) {
51 chain->process_complete(ch_buffer);
52 }
53 }
54
55 auto vk_buffer = std::dynamic_pointer_cast<Buffers::VKBuffer>(ch_buffer);
56 if (vk_buffer && vk_buffer->has_render_pipeline()) {
57 for (const auto& [id, window] : vk_buffer->get_render_pipelines()) {
59 info.buffer = vk_buffer;
60 info.target_window = window;
61 info.pipeline_id = id;
62 info.command_buffer_id = vk_buffer->get_pipeline_command(id);
63 info.needs_depth = vk_buffer->needs_depth_attachment();
64
65 root_buf->add_renderable_buffer(info);
66
68 "Registered buffer for rendering to window '{}'",
69 window->get_create_info().title);
70 }
71 }
72
73 } catch (const std::exception& e) {
75 "Error processing graphics buffer: {}", e.what());
76 }
77 }
78}
79
80void GraphicsBatchProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
81{
82 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
83 if (!root_graphics_buffer) {
84 error<std::invalid_argument>(
87 std::source_location::current(),
88 "GraphicsBatchProcessor can only be attached to RootGraphicsBuffer");
89 }
90
92 error<std::runtime_error>(
95 std::source_location::current(),
96 "GraphicsBatchProcessor token incompatible with RootGraphicsBuffer requirements");
97 }
98}
99
100bool GraphicsBatchProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
101{
102 return std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer) != nullptr;
103}
104
106 : m_callback(std::move(callback))
107 , m_root_buffer(nullptr)
108{
110}
111
113 : m_callback(nullptr)
114 , m_root_buffer(nullptr)
115{
117}
118
119void PresentProcessor::processing_function(const std::shared_ptr<Buffer>& buffer)
120{
121 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
122 if (!root_graphics_buffer) {
124 "RenderProcessor received non-RootGraphicsBuffer");
125 return;
126 }
127
128 if (m_root_buffer && root_graphics_buffer != m_root_buffer) {
130 "RenderProcessor processing buffer that doesn't match attached root");
131 return;
132 }
133
134 if (m_callback) {
135 try {
136 m_callback(root_graphics_buffer);
137 } catch (const std::exception& e) {
138 error_rethrow(
141 std::source_location::current(),
142 "RenderProcessor callback threw exception: {}",
143 e.what());
144 }
145 } else {
146 fallback_renderer(root_graphics_buffer);
147 }
148}
149
150void PresentProcessor::on_attach(const std::shared_ptr<Buffer>& buffer)
151{
152 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer);
153 if (!root_graphics_buffer) {
154 error<std::invalid_argument>(
157 std::source_location::current(),
158 "RenderProcessor can only be attached to RootGraphicsBuffer");
159 }
160
162 error<std::runtime_error>(
165 std::source_location::current(),
166 "RenderProcessor token incompatible with RootGraphicsBuffer requirements");
167 }
168
169 m_root_buffer = root_graphics_buffer;
170
172 "RenderProcessor attached to RootGraphicsBuffer (has_callback: {})",
173 has_callback());
174}
175
176void PresentProcessor::on_detach(const std::shared_ptr<Buffer>& buffer)
177{
178 if (auto root = std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer)) {
179 if (root == m_root_buffer) {
180 m_root_buffer = nullptr;
181 }
182 }
183
185 "RenderProcessor detached from RootGraphicsBuffer");
186}
187
188bool PresentProcessor::is_compatible_with(const std::shared_ptr<Buffer>& buffer) const
189{
190 return std::dynamic_pointer_cast<RootGraphicsBuffer>(buffer) != nullptr;
191}
192
194{
195 m_callback = std::move(callback);
196
198 "RenderProcessor callback {} (attached: {})",
199 m_callback ? "configured" : "cleared",
200 m_root_buffer != nullptr);
201}
202
203void PresentProcessor::fallback_renderer(const std::shared_ptr<RootGraphicsBuffer>& root)
204{
205 const auto& renderable_buffers = root->get_renderable_buffers();
206 if (renderable_buffers.empty()) {
207 return;
208 }
209
210 std::unordered_map<Core::Window*, std::vector<const RootGraphicsBuffer::RenderableBufferInfo*>> buffers_by_window;
211 for (const auto& renderable : renderable_buffers) {
212 if (renderable.target_window && renderable.target_window->is_graphics_registered()
213 && renderable.command_buffer_id != Portal::Graphics::INVALID_COMMAND_BUFFER) {
214 buffers_by_window[renderable.target_window.get()].push_back(&renderable);
215 }
216 }
217
218 if (buffers_by_window.empty()) {
219 root->clear_renderable_buffers();
220 return;
221 }
222
224 "PresentProcessor submitting to {} windows", buffers_by_window.size());
225
226 auto& foundry = Portal::Graphics::get_shader_foundry();
228 auto display_service = Registry::BackendRegistry::instance()
230
231 if (!display_service) {
233 "DisplayService not available for dynamic rendering");
234 return;
235 }
236
237 for (const auto& [window_ptr, buffer_infos] : buffers_by_window) {
238 auto window = buffer_infos[0]->target_window;
239
240 uint64_t image_bits = display_service->acquire_next_swapchain_image(window);
241 if (image_bits == 0) {
243 "Failed to acquire swapchain image for window '{}'",
244 window->get_create_info().title);
245 continue;
246 }
247 vk::Image swapchain_image { reinterpret_cast<VkImage>(image_bits) };
248
249 auto primary_cmd_id = foundry.begin_commands(Portal::Graphics::ShaderFoundry::CommandBufferType::GRAPHICS);
250 auto primary_cmd = foundry.get_command_buffer(primary_cmd_id);
251
252 if (!primary_cmd) {
254 "Failed to create primary command buffer for window '{}'",
255 window->get_create_info().title);
256 for (const auto* info : buffer_infos) {
257 info->buffer->clear_pipeline_commands();
258 }
259 continue;
260 }
261
262 try {
263 bool window_needs_depth = false;
264 for (const auto* info : buffer_infos) {
265 if (info->needs_depth) {
266 window_needs_depth = true;
267 break;
268 }
269 }
270
271 vk::ImageView depth_view = nullptr;
272 if (window_needs_depth) {
273 display_service->ensure_depth_attachment(window);
274 auto* view_ptr = display_service->get_depth_image_view(window);
275 if (view_ptr) {
276 depth_view = *static_cast<vk::ImageView*>(view_ptr);
277 }
278 }
279
280 flow.begin_rendering(primary_cmd_id, window, swapchain_image,
282
283 std::vector<vk::CommandBuffer> secondary_buffers;
284 for (const auto* info : buffer_infos) {
285 auto secondary = foundry.get_command_buffer(info->command_buffer_id);
286 if (secondary) {
287 secondary_buffers.push_back(secondary);
288 }
289 }
290
291 if (!secondary_buffers.empty()) {
292 primary_cmd.executeCommands(secondary_buffers);
293 }
294
295 flow.end_rendering(primary_cmd_id, window);
296
297 foundry.end_commands(primary_cmd_id);
298 uint64_t primary_bits = *reinterpret_cast<uint64_t*>(&primary_cmd);
299 display_service->submit_and_present(window, primary_bits);
300
302 "Presented {} buffers to window '{}'",
303 secondary_buffers.size(), window->get_create_info().title);
304
305 } catch (const std::exception& e) {
307 "Failed to submit/present for window '{}': {}",
308 window->get_create_info().title,
309 e.what());
310 }
311
312 for (const auto* info : buffer_infos) {
313 info->buffer->clear_pipeline_commands();
314 }
315 }
316
317 root->clear_renderable_buffers();
318}
319
326
333
335{
336 auto batch_processor = create_default_processor();
337 if (batch_processor) {
338 set_default_processor(batch_processor);
339 }
340}
341
343{
344 if (this->has_pending_operations()) {
346 }
347
348 get_default_processor()->process(shared_from_this());
349}
350
352{
353 if (m_pending_removal.empty()) {
354 return;
355 }
356
357 auto it = std::remove_if(
358 m_child_buffers.begin(),
359 m_child_buffers.end(),
360 [](const std::shared_ptr<Buffer>& buf) {
361 return buf && buf->needs_removal();
362 });
363
364 size_t removed_count = std::distance(it, m_child_buffers.end());
365
366 if (removed_count > 0) {
367 m_child_buffers.erase(it, m_child_buffers.end());
368
370 "Cleaned up {} graphics buffers (remaining: {})",
371 removed_count, m_child_buffers.size());
372 }
373
374 m_pending_removal.clear();
375}
376
377void RootGraphicsBuffer::set_final_processor(std::shared_ptr<BufferProcessor> processor)
378{
379 m_final_processor = std::move(processor);
380}
381
382std::shared_ptr<BufferProcessor> RootGraphicsBuffer::get_final_processor() const
383{
384 return m_final_processor;
385}
386
387bool RootGraphicsBuffer::has_buffer(const std::shared_ptr<VKBuffer>& buffer) const
388{
389 return std::ranges::find(m_child_buffers, buffer) != m_child_buffers.end();
390}
391
392std::vector<std::shared_ptr<VKBuffer>> RootGraphicsBuffer::get_buffers_by_usage(VKBuffer::Usage usage) const
393{
394 std::vector<std::shared_ptr<VKBuffer>> filtered_buffers;
395
396 for (const auto& buffer : m_child_buffers) {
397 if (buffer && buffer->get_usage() == usage) {
398 filtered_buffers.push_back(buffer);
399 }
400 }
401
402 return filtered_buffers;
403}
404
405std::shared_ptr<BufferProcessor> RootGraphicsBuffer::create_default_processor()
406{
407 return std::make_shared<GraphicsBatchProcessor>(shared_from_this());
408}
409
410} // namespace MayaFlux::Buffers
#define MF_INFO(comp, ctx,...)
#define MF_RT_WARN(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_RT_TRACE(comp, ctx,...)
#define MF_RT_DEBUG(comp, ctx,...)
std::shared_ptr< RootGraphicsBuffer > m_root_buffer
Shared pointer to the 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.
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)
std::shared_ptr< RootGraphicsBuffer > m_root_buffer
Reference to root buffer (for validation and callbacks)
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::shared_ptr< BufferProcessor > get_final_processor() const
Gets the current final processor.
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.
std::shared_ptr< BufferProcessor > m_final_processor
Optional final processor (rarely used in graphics)
std::vector< std::shared_ptr< VKBuffer > > m_pending_removal
Buffers pending removal (cleaned up in next process cycle)
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:269
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
Definition VKBuffer.cpp:258
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
bool are_tokens_compatible(ProcessingToken preferred, ProcessingToken current)
Determines if two processing tokens are compatible for joint execution.
@ GRAPHICS_BACKEND
Standard graphics processing backend configuration.
@ 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.
constexpr CommandBufferID INVALID_COMMAND_BUFFER
Information about a buffer that's ready to render.
Backend display and presentation service interface.