22 auto root_buf = std::dynamic_pointer_cast<RootGraphicsBuffer>(
buffer);
25 "GraphicsBatchProcessor can only process its associated RootGraphicsBuffer");
29 root_buf->clear_renderable_buffers();
30 root_buf->cleanup_marked_buffers();
32 for (
const auto& child : root_buf->get_child_buffers()) {
35 child->clear_pipeline_commands();
38 if (!child->needs_removal() && child->has_data_for_cycle()) {
39 if (child->needs_default_processing() && child->get_default_processor())
40 child->process_default();
42 if (
auto chain = child->get_processing_chain()) {
43 if (child->has_data_for_cycle())
44 chain->process_complete(child);
47 if (!child->needs_removal()) {
48 for (
const auto& [
id,
window] : child->get_render_pipelines()) {
49 const auto command = child->get_pipeline_command(
id);
59 root_buf->add_renderable_buffer(info);
63 }
catch (
const std::exception& e) {
65 "Error processing graphics buffer: {}", e.what());
68 if (child->needs_removal()) {
69 for (
const auto& [
id,
window] : child->get_render_pipelines())
70 root_buf->request_presentation_refresh(
window);
72 for (
const auto&
window : child->take_presentation_refreshes())
73 root_buf->request_presentation_refresh(
window);
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);
82 for (
const auto& child : root_buf->get_child_buffers()) {
83 if (!child || child->needs_removal())
86 for (
const auto& [
id,
window] : child->get_render_pipelines()) {
90 auto render = child->get_render_processor(
id);
91 if (!render || !render->is_visible(child))
95 auto command = child->get_pipeline_command(
id);
97 render->record_draw(child);
98 command = child->get_pipeline_command(
id);
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());
118 root_buf->sort_renderable_buffers();
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");
133 error<std::runtime_error>(
136 std::source_location::current(),
137 "GraphicsBatchProcessor token incompatible with RootGraphicsBuffer requirements");
143 return std::dynamic_pointer_cast<RootGraphicsBuffer>(
buffer) !=
nullptr;
153 : m_callback(nullptr)
160 auto root_graphics_buffer = std::dynamic_pointer_cast<RootGraphicsBuffer>(
buffer);
161 if (!root_graphics_buffer) {
163 "RenderProcessor received non-RootGraphicsBuffer");
169 "RenderProcessor processing buffer that doesn't match attached root");
176 }
catch (
const std::exception& e) {
180 std::source_location::current(),
181 "RenderProcessor callback threw exception: {}",
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");
201 error<std::runtime_error>(
204 std::source_location::current(),
205 "RenderProcessor token incompatible with RootGraphicsBuffer requirements");
212 "RenderProcessor attached to RootGraphicsBuffer (has_callback: {})",
218 if (
auto root = std::dynamic_pointer_cast<RootGraphicsBuffer>(
buffer)) {
226 "RenderProcessor detached from RootGraphicsBuffer");
231 return std::dynamic_pointer_cast<RootGraphicsBuffer>(
buffer) !=
nullptr;
239 "RenderProcessor callback {} (attached: {})",
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()) {
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);
259 buffers_by_window.try_emplace(
window);
262 for (
const auto& renderable : renderable_buffers) {
263 if (renderable.target_window && renderable.target_window->is_graphics_registered()
265 buffers_by_window[renderable.target_window].push_back(&renderable);
269 if (buffers_by_window.empty()) {
270 root->clear_renderable_buffers();
275 "PresentProcessor submitting to {} windows", buffers_by_window.size());
282 if (!display_service) {
284 "DisplayService not available for dynamic rendering");
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);
296 vk::Image swapchain_image {
reinterpret_cast<VkImage
>(image_bits) };
299 auto primary_cmd = foundry.get_command_buffer(primary_cmd_id);
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();
312 bool window_needs_depth =
false;
313 for (
const auto* info : buffer_infos) {
314 if (info->needs_depth) {
315 window_needs_depth =
true;
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);
325 depth_view = *
static_cast<vk::ImageView*
>(view_ptr);
329 flow.begin_rendering(primary_cmd_id,
window, swapchain_image,
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);
336 secondary_buffers.push_back(secondary);
340 if (!secondary_buffers.empty()) {
341 primary_cmd.executeCommands(secondary_buffers);
344 flow.end_rendering(primary_cmd_id,
window);
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);
352 "Presented {} buffers to window '{}'",
353 secondary_buffers.size(),
window->get_create_info().title);
355 }
catch (
const std::exception& e) {
357 "Failed to submit/present for window '{}': {}",
358 window->get_create_info().title,
362 for (
const auto* info : buffer_infos) {
363 info->buffer->clear_pipeline_commands();
367 root->clear_renderable_buffers();
371 : m_final_processor(nullptr)
386 if (batch_processor) {
402 auto it = std::remove_if(
405 [
this](
const std::shared_ptr<VKBuffer>& buf) {
406 if (!buf || !buf->needs_removal())
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);
417 if (removed_count > 0) {
421 "Cleaned up {} graphics buffers (remaining: {})",
443 std::vector<std::shared_ptr<VKBuffer>> filtered_buffers;
447 filtered_buffers.push_back(
buffer);
451 return filtered_buffers;
468 auto remove_render = [&
buffer, &
chain, &
window](
const std::shared_ptr<RenderProcessor>& render) {
469 if (!render || render->get_target_window() !=
window)
472 if (
buffer->get_default_processor() == render) {
473 buffer->set_default_processor(
nullptr);
483 if (
auto render =
buffer->get_render_processor())
484 remove_render(render);
486 for (
const auto& render :
buffer->get_additional_render_processors())
487 remove_render(render);
495 return std::make_shared<GraphicsBatchProcessor>(shared_from_this());
#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
std::weak_ptr< Core::Window > window
ProcessingToken m_processing_token
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.
bool has_pending_operations() const
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.
void set_default_processor(const std::shared_ptr< BufferProcessor > &processor) override
Set the buffer's default processor.
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
CommandBufferID command_buffer_id
std::shared_ptr< VKBuffer > buffer
std::shared_ptr< Core::Window > target_window
RenderPipelineID pipeline_id
Information about a buffer that's ready to render.
Backend display and presentation service interface.