MayaFlux 1.0.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
RenderFlow.cpp
Go to the documentation of this file.
1#include "RenderFlow.hpp"
2
4
7
11
13
15
16bool RenderFlow::s_initialized = false;
17
18namespace {
19
20 /**
21 * @brief Translate semantic VertexLayout to Vulkan bindings/attributes
22 * @param layout Semantic vertex layout
23 * @return Tuple of (bindings, attributes)
24 */
25 std::pair<
26 std::vector<Core::VertexBinding>,
27 std::vector<Core::VertexAttribute>>
28 translate_semantic_layout(const Kakshya::VertexLayout& layout)
29 {
30 using namespace MayaFlux::Portal::Graphics;
31
32 auto [vk_bindings, vk_attributes] = VertexLayoutTranslator::translate_layout(layout);
33
35 "Translated semantic vertex layout: {} bindings, {} attributes",
36 vk_bindings.size(), vk_attributes.size());
37
38 return { vk_bindings, vk_attributes };
39 }
40
41} // anonymous namespace
42
43//==============================================================================
44// Initialization
45//==============================================================================
46
48{
49 if (s_initialized) {
51 "RenderFlow already initialized (static flag)");
52 return true;
53 }
54
55 if (m_shader_foundry) {
57 "RenderFlow already initialized");
58 return true;
59 }
60
61 auto* foundry = &ShaderFoundry::instance();
62 if (!foundry->is_initialized()) {
64 "ShaderFoundry must be initialized before RenderFlow");
65 return false;
66 }
67
68 auto* display_service = Registry::BackendRegistry::instance()
70
71 if (!display_service) {
73 "DisplayService not found in BackendRegistry");
74 return false;
75 }
76
77 m_shader_foundry = foundry;
78 m_display_service = display_service;
79 s_initialized = true;
80
82 "RenderFlow initialized");
83 return true;
84}
85
87{
88 if (!s_initialized) {
89 return;
90 }
91
93 "RenderFlow stopped");
94}
95
97{
98 if (!s_initialized) {
99 return;
100 }
101
103 "Shutting down RenderFlow...");
104
107 "Cannot shutdown RenderFlow: ShaderFoundry not initialized");
108 return;
109 }
110
112
113 if (!device) {
115 "Cannot shutdown RenderFlow: Vulkan device is null");
116 return;
117 }
118
120
121 m_window_associations.clear();
122
123 m_shader_foundry = nullptr;
124 m_display_service = nullptr;
125
126 s_initialized = false;
127
129 "RenderFlow shutdown complete");
130}
131
132//==============================================================================
133// Utility Conversions (Portal enums → Vulkan enums)
134//==============================================================================
135
136namespace {
137
138 vk::PrimitiveTopology to_vk_topology(PrimitiveTopology topology)
139 {
140 switch (topology) {
142 return vk::PrimitiveTopology::ePointList;
144 return vk::PrimitiveTopology::eLineList;
146 return vk::PrimitiveTopology::eLineStrip;
148 return vk::PrimitiveTopology::eTriangleList;
150 return vk::PrimitiveTopology::eTriangleStrip;
152 return vk::PrimitiveTopology::eTriangleFan;
153 default:
154 return vk::PrimitiveTopology::eTriangleList;
155 }
156 }
157
158 vk::PolygonMode to_vk_polygon_mode(PolygonMode mode)
159 {
160 switch (mode) {
162 return vk::PolygonMode::eFill;
164 return vk::PolygonMode::eLine;
166 return vk::PolygonMode::ePoint;
167 default:
168 return vk::PolygonMode::eFill;
169 }
170 }
171
172 vk::CullModeFlags to_vk_cull_mode(CullMode mode)
173 {
174 switch (mode) {
175 case CullMode::NONE:
176 return vk::CullModeFlagBits::eNone;
177 case CullMode::FRONT:
178 return vk::CullModeFlagBits::eFront;
179 case CullMode::BACK:
180 return vk::CullModeFlagBits::eBack;
182 return vk::CullModeFlagBits::eFrontAndBack;
183 default:
184 return vk::CullModeFlagBits::eBack;
185 }
186 }
187
188 vk::CompareOp to_vk_compare_op(CompareOp op)
189 {
190 switch (op) {
191 case CompareOp::NEVER:
192 return vk::CompareOp::eNever;
193 case CompareOp::LESS:
194 return vk::CompareOp::eLess;
195 case CompareOp::EQUAL:
196 return vk::CompareOp::eEqual;
198 return vk::CompareOp::eLessOrEqual;
200 return vk::CompareOp::eGreater;
202 return vk::CompareOp::eNotEqual;
204 return vk::CompareOp::eGreaterOrEqual;
206 return vk::CompareOp::eAlways;
207 default:
208 return vk::CompareOp::eLess;
209 }
210 }
211
212 vk::BlendFactor to_vk_blend_factor(BlendFactor factor)
213 {
214 switch (factor) {
216 return vk::BlendFactor::eZero;
217 case BlendFactor::ONE:
218 return vk::BlendFactor::eOne;
220 return vk::BlendFactor::eSrcColor;
222 return vk::BlendFactor::eOneMinusSrcColor;
224 return vk::BlendFactor::eDstColor;
226 return vk::BlendFactor::eOneMinusDstColor;
228 return vk::BlendFactor::eSrcAlpha;
230 return vk::BlendFactor::eOneMinusSrcAlpha;
232 return vk::BlendFactor::eDstAlpha;
234 return vk::BlendFactor::eOneMinusDstAlpha;
235 default:
236 return vk::BlendFactor::eOne;
237 }
238 }
239
240 vk::BlendOp to_vk_blend_op(BlendOp op)
241 {
242 switch (op) {
243 case BlendOp::ADD:
244 return vk::BlendOp::eAdd;
246 return vk::BlendOp::eSubtract;
248 return vk::BlendOp::eReverseSubtract;
249 case BlendOp::MIN:
250 return vk::BlendOp::eMin;
251 case BlendOp::MAX:
252 return vk::BlendOp::eMax;
253 default:
254 return vk::BlendOp::eAdd;
255 }
256 }
257} // anonymous namespace
258
259//==============================================================================
260// Pipeline Creation
261//==============================================================================
262
264 const RenderPipelineConfig& config,
265 const std::vector<vk::Format>& color_formats,
266 vk::Format depth_format)
267{
268 if (!is_initialized()) {
270 "RenderFlow not initialized");
272 }
273
274 if (config.mesh_shader == INVALID_SHADER && config.vertex_shader == INVALID_SHADER) {
276 "Pipeline requires either mesh shader or vertex shader");
278 }
279
280 if (color_formats.empty()) {
282 "At least one color format required for dynamic rendering pipeline");
284 }
285
287
289 if (config.fragment_shader != INVALID_SHADER) {
291 }
292 if (config.geometry_shader != INVALID_SHADER) {
294 }
295 if (config.tess_control_shader != INVALID_SHADER) {
297 }
298 if (config.tess_eval_shader != INVALID_SHADER) {
300 }
301
303 if (config.task_shader != INVALID_SHADER) {
305 }
306
307 if (config.fragment_shader != INVALID_SHADER) {
309 }
310
311 if (config.semantic_vertex_layout.has_value()) {
313 "Pipeline using semantic VertexLayout ({} vertices, {} attributes)",
314 config.semantic_vertex_layout->vertex_count,
315 config.semantic_vertex_layout->attributes.size());
316
317 auto [vk_bindings, vk_attributes] = translate_semantic_layout(
318 config.semantic_vertex_layout.value());
319
320 vk_config.vertex_bindings = vk_bindings;
321 vk_config.vertex_attributes = vk_attributes;
322 vk_config.use_vertex_shader_reflection = false;
323
324 } else if (!config.vertex_bindings.empty() || !config.vertex_attributes.empty()) {
326 "Pipeline using explicit vertex config ({} bindings, {} attributes)",
327 config.vertex_bindings.size(), config.vertex_attributes.size());
328
329 for (const auto& binding : config.vertex_bindings) {
330 Core::VertexBinding vk_binding {};
331 vk_binding.binding = binding.binding;
332 vk_binding.stride = binding.stride;
333 vk_binding.input_rate = binding.per_instance ? vk::VertexInputRate::eInstance : vk::VertexInputRate::eVertex;
334 vk_config.vertex_bindings.push_back(vk_binding);
335 }
336
337 for (const auto& attr : config.vertex_attributes) {
338 Core::VertexAttribute vk_attr {};
339 vk_attr.location = attr.location;
340 vk_attr.binding = attr.binding;
341 vk_attr.format = attr.format;
342 vk_attr.offset = attr.offset;
343 vk_config.vertex_attributes.push_back(vk_attr);
344 }
345
346 vk_config.use_vertex_shader_reflection = false;
347 } else {
349 "Pipeline will use shader reflection for vertex input");
351 }
352
353 vk_config.topology = to_vk_topology(config.topology);
354 vk_config.primitive_restart_enable = false;
355
356 vk_config.polygon_mode = to_vk_polygon_mode(config.rasterization.polygon_mode);
357 if (vk_config.polygon_mode != vk::PolygonMode::eFill
358 && !m_shader_foundry->get_physical_device().getFeatures().fillModeNonSolid) {
360 "Polygon mode {} needs fillModeNonSolid, which this device lacks; drawing filled",
361 vk::to_string(vk_config.polygon_mode));
362 vk_config.polygon_mode = vk::PolygonMode::eFill;
363 }
364 vk_config.cull_mode = to_vk_cull_mode(config.rasterization.cull_mode);
365 vk_config.front_face = config.rasterization.front_face_ccw ? vk::FrontFace::eCounterClockwise : vk::FrontFace::eClockwise;
366 vk_config.line_width = config.rasterization.line_width;
368 vk_config.depth_bias_enable = config.rasterization.depth_bias;
369
372 vk_config.depth_compare_op = to_vk_compare_op(config.depth_stencil.depth_compare_op);
374
375 for (const auto& blend : config.blend_attachments) {
377 vk_blend.blend_enable = blend.blend_enable;
378 vk_blend.src_color_blend_factor = to_vk_blend_factor(blend.src_color_factor);
379 vk_blend.dst_color_blend_factor = to_vk_blend_factor(blend.dst_color_factor);
380 vk_blend.color_blend_op = to_vk_blend_op(blend.color_blend_op);
381 vk_blend.src_alpha_blend_factor = to_vk_blend_factor(blend.src_alpha_factor);
382 vk_blend.dst_alpha_blend_factor = to_vk_blend_factor(blend.dst_alpha_factor);
383 vk_blend.alpha_blend_op = to_vk_blend_op(blend.alpha_blend_op);
384 vk_config.color_blend_attachments.push_back(vk_blend);
385 }
386
387 std::vector<vk::DescriptorSetLayout> layouts;
388 vk::DescriptorSetLayout state_vt_layout {};
389
390 {
391 std::vector<vk::DescriptorSetLayoutBinding> set0_bindings;
392
393 vk::DescriptorSetLayoutBinding vt_b;
394 vt_b.binding = 0;
395 vt_b.descriptorType = vk::DescriptorType::eUniformBuffer;
396 vt_b.descriptorCount = 1;
397 vt_b.stageFlags = vk::ShaderStageFlagBits::eVertex;
398 set0_bindings.push_back(vt_b);
399
400 if (!config.descriptor_sets.empty()) {
401 for (const auto& binding : config.descriptor_sets[0]) {
402 if (binding.set == 0 && binding.binding != 0) {
403 vk::DescriptorSetLayoutBinding vk_b;
404 vk_b.binding = binding.binding;
405 vk_b.descriptorType = binding.type;
406 vk_b.descriptorCount = binding.count > 0 ? binding.count : 1;
407 vk_b.stageFlags = vk::ShaderStageFlagBits::eVertex
408 | vk::ShaderStageFlagBits::eFragment;
409 set0_bindings.push_back(vk_b);
410 }
411 }
412 }
413
414 vk::DescriptorSetLayoutCreateInfo layout_info;
415 layout_info.bindingCount = static_cast<uint32_t>(set0_bindings.size());
416 layout_info.pBindings = set0_bindings.data();
417 vk::DescriptorSetLayout vt_layout = m_shader_foundry->get_device().createDescriptorSetLayout(layout_info);
418 layouts.push_back(vt_layout);
419 state_vt_layout = vt_layout;
420 }
421
422 for (const auto& desc_set : config.descriptor_sets) {
423 bool all_set_zero = std::ranges::all_of(desc_set,
424 [](const auto& b) { return b.set == 0; });
425 if (all_set_zero)
426 continue;
427
428 std::vector<vk::DescriptorSetLayoutBinding> bindings;
429 for (const auto& binding : desc_set) {
430 if (binding.set == 0)
431 continue;
432 vk::DescriptorSetLayoutBinding vk_b;
433 vk_b.binding = binding.binding;
434 vk_b.descriptorType = binding.type;
435 vk_b.descriptorCount = binding.count > 0 ? binding.count : 1;
436 vk_b.stageFlags = vk::ShaderStageFlagBits::eVertex
437 | vk::ShaderStageFlagBits::eFragment;
438 bindings.push_back(vk_b);
439 }
440
441 if (bindings.empty())
442 continue;
443
444 vk::DescriptorSetLayoutCreateInfo layout_info;
445 layout_info.bindingCount = static_cast<uint32_t>(bindings.size());
446 layout_info.pBindings = bindings.data();
447 layouts.push_back(
448 m_shader_foundry->get_device().createDescriptorSetLayout(layout_info));
449 }
450
451 vk_config.descriptor_set_layouts = layouts;
452
453 vk::ShaderStageFlags push_constant_stages;
454
455 if (config.mesh_shader != INVALID_SHADER) {
456 push_constant_stages = vk::ShaderStageFlagBits::eMeshEXT;
457 if (config.task_shader != INVALID_SHADER) {
458 push_constant_stages |= vk::ShaderStageFlagBits::eTaskEXT;
459 }
460 if (config.fragment_shader != INVALID_SHADER) {
461 push_constant_stages |= vk::ShaderStageFlagBits::eFragment;
462 }
463 } else {
464 push_constant_stages = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment;
465 if (config.geometry_shader != INVALID_SHADER) {
466 push_constant_stages |= vk::ShaderStageFlagBits::eGeometry;
467 }
468 if (config.tess_control_shader != INVALID_SHADER) {
469 push_constant_stages |= vk::ShaderStageFlagBits::eTessellationControl;
470 }
471 if (config.tess_eval_shader != INVALID_SHADER) {
472 push_constant_stages |= vk::ShaderStageFlagBits::eTessellationEvaluation;
473 }
474 }
475
476 if (config.push_constant_size > 0) {
477 vk::PushConstantRange range;
478 range.stageFlags = push_constant_stages;
479 range.offset = 0;
480 range.size = static_cast<uint32_t>(config.push_constant_size);
481 vk_config.push_constant_ranges.push_back(range);
482 }
483
484 vk_config.color_attachment_formats = color_formats;
485 vk_config.depth_attachment_format = depth_format;
486
487 vk_config.dynamic_states = {
488 vk::DynamicState::eViewport,
489 vk::DynamicState::eScissor
490 };
491
492 auto pipeline = std::make_shared<Core::VKGraphicsPipeline>();
493 if (!pipeline->create(m_shader_foundry->get_device(), vk_config)) {
495 "Failed to create VKGraphicsPipeline for dynamic rendering");
496
497 for (auto layout : layouts) {
498 m_shader_foundry->get_device().destroyDescriptorSetLayout(layout);
499 }
501 }
502
503 auto pipeline_id = m_next_pipeline_id.fetch_add(1);
504 PipelineState state;
505 state.shader_ids = { config.vertex_shader, config.fragment_shader };
506 state.pipeline = pipeline;
507 state.layouts = layouts;
508 state.view_transform_layout = state_vt_layout;
509 state.layout = pipeline->get_layout();
510 state.push_constant_stages = push_constant_stages;
511 m_pipelines[pipeline_id] = std::move(state);
512
514 "Dynamic rendering pipeline created (ID: {}, {} color attachments)",
515 pipeline_id, color_formats.size());
516
517 return pipeline_id;
518}
519
521{
522 auto it = m_pipelines.find(pipeline_id);
523 if (it == m_pipelines.end()) {
524 return;
525 }
526
528
529 if (it->second.pipeline) {
530 it->second.pipeline->cleanup(device);
531 }
532
533 for (auto layout : it->second.layouts) {
534 if (layout) {
535 device.destroyDescriptorSetLayout(layout);
536 }
537 }
538
539 m_pipelines.erase(it);
540
542 "Destroyed graphics pipeline (ID: {})", pipeline_id);
543}
544
546{
548
549 for (auto& [id, state] : m_pipelines) {
550 if (state.pipeline) {
551 state.pipeline->cleanup(device);
552 }
553
554 for (auto layout : state.layouts) {
555 if (layout) {
556 device.destroyDescriptorSetLayout(layout);
557 }
558 }
559 }
560 m_pipelines.clear();
561
563 "Cleaned up all graphics pipelines");
564}
565
567 RenderPipelineID pipeline_id) const
568{
569 auto it = m_pipelines.find(pipeline_id);
570 if (it == m_pipelines.end()) {
571 return {};
572 }
573 return it->second.view_transform_layout;
574}
575
576//==============================================================================
577// Dynamic Rendering
578//==============================================================================
579
581 CommandBufferID cmd_id,
582 const std::shared_ptr<Core::Window>& window,
583 vk::Image swapchain_image,
584 const std::array<float, 4>& clear_color,
585 vk::ImageView depth_image_view)
586{
588 if (!cmd) {
590 "Invalid command buffer ID: {}", cmd_id);
591 return;
592 }
593
594 if (!window) {
596 "Cannot begin rendering for null window");
597 return;
598 }
599
600 if (!swapchain_image) {
602 "Cannot begin rendering with null swapchain image");
603 return;
604 }
605
606 auto it = m_window_associations.find(window);
607 if (it == m_window_associations.end()) {
609 "Window '{}' not registered for rendering. "
610 "Call register_window_for_rendering() first.",
611 window->get_create_info().title);
612 m_window_associations.emplace(window, WindowRenderAssociation { .window = window, .swapchain_image = swapchain_image });
613 } else {
614 it->second.swapchain_image = swapchain_image;
615 }
616
617 uint32_t width = 0, height = 0;
619
620 if (width == 0 || height == 0) {
622 "Invalid swapchain extent for window '{}': {}x{}",
623 window->get_create_info().title, width, height);
624 return;
625 }
626
627 vk::ImageView image_view = get_current_image_view(window);
628 if (!image_view) {
630 "Failed to get image view for window '{}'",
631 window->get_create_info().title);
632 return;
633 }
634
635 vk::ImageMemoryBarrier pre_barrier {};
636 pre_barrier.oldLayout = vk::ImageLayout::eUndefined;
637 pre_barrier.newLayout = vk::ImageLayout::eColorAttachmentOptimal;
638 pre_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
639 pre_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
640 pre_barrier.image = swapchain_image;
641 pre_barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
642 pre_barrier.subresourceRange.baseMipLevel = 0;
643 pre_barrier.subresourceRange.levelCount = 1;
644 pre_barrier.subresourceRange.baseArrayLayer = 0;
645 pre_barrier.subresourceRange.layerCount = 1;
646 pre_barrier.srcAccessMask = vk::AccessFlagBits::eNone;
647 pre_barrier.dstAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
648
649 cmd.pipelineBarrier(
650 vk::PipelineStageFlagBits::eTopOfPipe,
651 vk::PipelineStageFlagBits::eColorAttachmentOutput,
652 vk::DependencyFlags {},
653 0, nullptr,
654 0, nullptr,
655 1, &pre_barrier);
656
657 vk::RenderingAttachmentInfo color_attachment {};
658 color_attachment.sType = vk::StructureType::eRenderingAttachmentInfo;
659 color_attachment.pNext = nullptr;
660 color_attachment.imageView = image_view;
661 color_attachment.imageLayout = vk::ImageLayout::eColorAttachmentOptimal;
662 color_attachment.resolveMode = vk::ResolveModeFlagBits::eNone;
663 color_attachment.resolveImageView = nullptr;
664 color_attachment.resolveImageLayout = vk::ImageLayout::eUndefined;
665 color_attachment.loadOp = vk::AttachmentLoadOp::eClear;
666 color_attachment.storeOp = vk::AttachmentStoreOp::eStore;
667
668 if (clear_color != default_color) {
669 color_attachment.clearValue.color = vk::ClearColorValue(clear_color);
670 } else {
671 color_attachment.clearValue.color = vk::ClearColorValue(window->get_create_info().clear_color);
672 }
673
674 vk::RenderingAttachmentInfo depth_attachment {};
675 if (depth_image_view) {
676 depth_attachment.sType = vk::StructureType::eRenderingAttachmentInfo;
677 depth_attachment.pNext = nullptr;
678 depth_attachment.imageView = depth_image_view;
679 depth_attachment.imageLayout = vk::ImageLayout::eDepthStencilAttachmentOptimal;
680 depth_attachment.resolveMode = vk::ResolveModeFlagBits::eNone;
681 depth_attachment.resolveImageView = nullptr;
682 depth_attachment.resolveImageLayout = vk::ImageLayout::eUndefined;
683 depth_attachment.loadOp = vk::AttachmentLoadOp::eClear;
684 depth_attachment.storeOp = vk::AttachmentStoreOp::eDontCare;
685 depth_attachment.clearValue.depthStencil = vk::ClearDepthStencilValue { 1.0F, 0 };
686 }
687
688 vk::RenderingInfo rendering_info {};
689 rendering_info.sType = vk::StructureType::eRenderingInfo;
690 rendering_info.pNext = nullptr;
691 rendering_info.flags = vk::RenderingFlagBits::eContentsSecondaryCommandBuffers;
692 rendering_info.renderArea.offset = vk::Offset2D { 0, 0 };
693 rendering_info.renderArea.extent = vk::Extent2D { width, height };
694 rendering_info.layerCount = 1;
695 rendering_info.colorAttachmentCount = 1;
696 rendering_info.pColorAttachments = &color_attachment;
697 rendering_info.pDepthAttachment = depth_image_view ? &depth_attachment : nullptr;
698 rendering_info.pStencilAttachment = nullptr;
699
700 cmd.beginRendering(rendering_info);
701
703 "Began dynamic rendering for window '{}' ({}x{}, depth: {})",
704 window->get_create_info().title, width, height,
705 depth_image_view ? "yes" : "no");
706}
707
709 CommandBufferID cmd_id,
710 const std::shared_ptr<Core::Window>& window)
711{
713 if (!cmd) {
715 "Invalid command buffer ID: {}", cmd_id);
716 return;
717 }
718
719 cmd.endRendering();
720
721 auto it = m_window_associations.find(window);
722 if (it == m_window_associations.end() || !it->second.swapchain_image) {
724 "No swapchain image tracked for window '{}'",
725 window->get_create_info().title);
726 return;
727 }
728
729 vk::ImageMemoryBarrier post_barrier {};
730 post_barrier.oldLayout = vk::ImageLayout::eColorAttachmentOptimal;
731 post_barrier.newLayout = vk::ImageLayout::ePresentSrcKHR;
732 post_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
733 post_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
734 post_barrier.image = it->second.swapchain_image;
735 post_barrier.subresourceRange.aspectMask = vk::ImageAspectFlagBits::eColor;
736 post_barrier.subresourceRange.baseMipLevel = 0;
737 post_barrier.subresourceRange.levelCount = 1;
738 post_barrier.subresourceRange.baseArrayLayer = 0;
739 post_barrier.subresourceRange.layerCount = 1;
740 post_barrier.srcAccessMask = vk::AccessFlagBits::eColorAttachmentWrite;
741 post_barrier.dstAccessMask = vk::AccessFlagBits::eNone;
742
743 cmd.pipelineBarrier(
744 vk::PipelineStageFlagBits::eColorAttachmentOutput,
745 vk::PipelineStageFlagBits::eBottomOfPipe,
746 vk::DependencyFlags {},
747 0, nullptr,
748 0, nullptr,
749 1, &post_barrier);
750
751 it->second.swapchain_image = nullptr;
752
754 "Ended dynamic rendering for window '{}'",
755 window->get_create_info().title);
756}
757
758//==============================================================================
759// Command Recording
760//==============================================================================
761
763{
764 auto pipeline_it = m_pipelines.find(pipeline_id);
765 if (pipeline_it == m_pipelines.end()) {
767 "Invalid pipeline ID: {}", pipeline_id);
768 return;
769 }
770
772 if (!cmd) {
774 "Invalid command buffer ID: {}", cmd_id);
775 return;
776 }
777
778 pipeline_it->second.pipeline->bind(cmd);
779}
780
782 CommandBufferID cmd_id,
783 const std::vector<std::shared_ptr<Buffers::VKBuffer>>& buffers,
784 uint32_t first_binding)
785{
787 if (!cmd) {
789 "Invalid command buffer ID: {}", cmd_id);
790 return;
791 }
792
793 std::vector<vk::Buffer> vk_buffers;
794 std::vector<vk::DeviceSize> offsets(buffers.size(), 0);
795
796 vk_buffers.reserve(buffers.size());
797 for (const auto& buf : buffers) {
798 vk_buffers.push_back(buf->get_buffer());
799 }
800
801 cmd.bindVertexBuffers(first_binding, vk_buffers, offsets);
802}
803
805 CommandBufferID cmd_id,
806 const std::shared_ptr<Buffers::VKBuffer>& buffer,
807 vk::IndexType index_type)
808{
810 if (!cmd) {
812 "Invalid command buffer ID: {}", cmd_id);
813 return;
814 }
815
816 cmd.bindIndexBuffer(buffer->get_index_buffer(), 0, index_type);
817}
818
820 CommandBufferID cmd_id,
821 RenderPipelineID pipeline_id,
822 const std::vector<DescriptorSetID>& descriptor_sets,
823 uint32_t first_set)
824{
825 auto pipeline_it = m_pipelines.find(pipeline_id);
826 if (pipeline_it == m_pipelines.end()) {
828 "Invalid pipeline ID: {}", pipeline_id);
829 return;
830 }
831
833 if (!cmd) {
835 "Invalid command buffer ID: {}", cmd_id);
836 return;
837 }
838
839 std::vector<vk::DescriptorSet> vk_sets;
840 vk_sets.reserve(descriptor_sets.size());
841 for (auto ds_id : descriptor_sets) {
842 vk_sets.push_back(m_shader_foundry->get_descriptor_set(ds_id));
843 }
844
845 cmd.bindDescriptorSets(
846 vk::PipelineBindPoint::eGraphics,
847 pipeline_it->second.layout,
848 first_set,
849 vk_sets,
850 nullptr);
851}
852
854 CommandBufferID cmd_id,
855 RenderPipelineID pipeline_id,
856 const void* data,
857 size_t size,
858 uint32_t offset)
859{
860 auto pipeline_it = m_pipelines.find(pipeline_id);
861 if (pipeline_it == m_pipelines.end()) {
863 "Invalid pipeline ID: {}", pipeline_id);
864 return;
865 }
866
868 if (!cmd) {
870 "Invalid command buffer ID: {}", cmd_id);
871 return;
872 }
873
874 cmd.pushConstants(
875 pipeline_it->second.layout,
876 pipeline_it->second.push_constant_stages,
877 offset,
878 static_cast<uint32_t>(size),
879 data);
880}
881
883 CommandBufferID cmd_id,
884 uint32_t vertex_count,
885 uint32_t instance_count,
886 uint32_t first_vertex,
887 uint32_t first_instance)
888{
890 if (!cmd) {
892 "Invalid command buffer ID: {}", cmd_id);
893 return;
894 }
895
896 cmd.draw(vertex_count, instance_count, first_vertex, first_instance);
897}
898
900 CommandBufferID cmd_id,
901 uint32_t index_count,
902 uint32_t instance_count,
903 uint32_t first_index,
904 int32_t vertex_offset,
905 uint32_t first_instance)
906{
908 if (!cmd) {
909 return;
910 }
911
912 cmd.drawIndexed(index_count, instance_count, first_index,
913 vertex_offset, first_instance);
914}
915
917 CommandBufferID cmd_id,
918 uint32_t group_count_x,
919 uint32_t group_count_y,
920 uint32_t group_count_z)
921{
923 if (!cmd) {
925 "Invalid command buffer ID: {}", cmd_id);
926 return;
927 }
928
929 cmd.drawMeshTasksEXT(group_count_x, group_count_y, group_count_z);
930}
931
933 CommandBufferID cmd_id,
934 const std::shared_ptr<Buffers::VKBuffer>& buffer,
935 vk::DeviceSize offset,
936 uint32_t draw_count,
937 uint32_t stride)
938{
940 if (!cmd || !buffer)
941 return;
942
943 cmd.drawMeshTasksIndirectEXT(buffer->get_buffer(), offset, draw_count, stride);
944}
945
946//==========================================================================
947// Window Rendering Registration
948//==========================================================================
949
950void RenderFlow::register_window_for_rendering(const std::shared_ptr<Core::Window>& window)
951{
952 if (!window) {
954 "Cannot register null window");
955 return;
956 }
957
958 if (!window->is_graphics_registered()) {
960 "Window '{}' not registered with graphics backend yet.",
961 window->get_create_info().title);
962 }
963
964 if (!m_window_associations.contains(window)) {
965 WindowRenderAssociation association;
966 association.window = window;
967 m_window_associations[window] = std::move(association);
968
970 "Registered window '{}' for dynamic rendering",
971 window->get_create_info().title);
972 }
973}
974
975void RenderFlow::unregister_window(const std::shared_ptr<Core::Window>& window)
976{
977 if (!window) {
978 return;
979 }
980
981 auto it = m_window_associations.find(window);
982
983 if (it != m_window_associations.end()) {
984 m_window_associations.erase(it);
985
987 "Unregistered window '{}' from rendering",
988 window->get_create_info().title);
989 }
990}
991
992bool RenderFlow::is_window_registered(const std::shared_ptr<Core::Window>& window) const
993{
994 return window && m_window_associations.contains(window);
995}
996
997std::vector<std::shared_ptr<Core::Window>> RenderFlow::get_registered_windows() const
998{
999 std::vector<std::shared_ptr<Core::Window>> windows;
1000 windows.reserve(m_window_associations.size());
1001
1002 for (const auto& [key, association] : m_window_associations) {
1003 if (auto window = association.window.lock()) {
1004 windows.push_back(window);
1005 }
1006 }
1007
1008 return windows;
1009}
1010
1011vk::ImageView RenderFlow::get_current_image_view(const std::shared_ptr<Core::Window>& window)
1012{
1014 if (!view_ptr) {
1015 return nullptr;
1016 }
1017 return *static_cast<vk::ImageView*>(view_ptr);
1018}
1019
1020//==============================================================================
1021// Convenience Methods
1022//==============================================================================
1023
1025 RenderPipelineID pipeline_id,
1026 uint32_t first_layout_index)
1027{
1028 auto pipeline_it = m_pipelines.find(pipeline_id);
1029 if (pipeline_it == m_pipelines.end()) {
1031 "Invalid pipeline ID: {}", pipeline_id);
1032 return {};
1033 }
1034
1035 const auto& layouts = pipeline_it->second.layouts;
1036 std::vector<DescriptorSetID> descriptor_set_ids;
1037
1038 for (uint32_t i = first_layout_index; i < layouts.size(); ++i) {
1039 auto ds_id = m_shader_foundry->allocate_descriptor_set(layouts[i]);
1040 if (ds_id == INVALID_DESCRIPTOR_SET) {
1042 "Failed to allocate descriptor set for pipeline {}", pipeline_id);
1043 return {};
1044 }
1045 descriptor_set_ids.push_back(ds_id);
1046 }
1047
1049 "Allocated {} descriptor sets for pipeline {} (starting at layout index {})",
1050 descriptor_set_ids.size(), pipeline_id, first_layout_index);
1051
1052 return descriptor_set_ids;
1053}
1054
1055} // namespace MayaFlux::Portal::Graphics
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_RT_ERROR(comp, ctx,...)
#define MF_TRACE(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
vk::CommandBuffer cmd
uint64_t offset
Definition Composite.cpp:10
bool size
std::shared_ptr< NetworkGeometryBuffer > buffer
std::weak_ptr< Core::Window > window
Definition Picker.cpp:23
uint32_t mode
size_t b
PrimitiveTopology topology
vk::PhysicalDevice device
Definition VKDevice.cpp:121
uint32_t width
uint32_t height
void bind_pipeline(CommandBufferID cmd_id, RenderPipelineID pipeline)
Bind graphics pipeline.
void push_constants(CommandBufferID cmd_id, RenderPipelineID pipeline, const void *data, size_t size, uint32_t offset=0)
Push constants.
void register_window_for_rendering(const std::shared_ptr< Core::Window > &window)
Register a window for dynamic rendering.
std::unordered_map< std::shared_ptr< Core::Window >, WindowRenderAssociation > m_window_associations
std::vector< DescriptorSetID > allocate_pipeline_descriptors(RenderPipelineID pipeline, uint32_t first_layout_index=0)
Allocate descriptor sets for pipeline.
void begin_rendering(CommandBufferID cmd_id, const std::shared_ptr< Core::Window > &window, vk::Image swapchain_image, const std::array< float, 4 > &clear_color=default_color, vk::ImageView depth_image_view=nullptr)
Begin dynamic rendering to a window.
vk::ImageView get_current_image_view(const std::shared_ptr< Core::Window > &window)
Get current image view for window.
void destroy_pipeline(RenderPipelineID pipeline_id)
Destroy a graphics pipeline.
void bind_vertex_buffers(CommandBufferID cmd_id, const std::vector< std::shared_ptr< Buffers::VKBuffer > > &buffers, uint32_t first_binding=0)
Bind vertex buffers.
vk::DescriptorSetLayout get_view_transform_layout(RenderPipelineID pipeline_id) const
Return the reserved ViewTransform UBO layout for a pipeline.
std::vector< std::shared_ptr< Core::Window > > get_registered_windows() const
Get all registered windows.
void end_rendering(CommandBufferID cmd_id, const std::shared_ptr< Core::Window > &window)
End dynamic rendering.
void draw_indexed(CommandBufferID cmd_id, uint32_t index_count, uint32_t instance_count=1, uint32_t first_index=0, int32_t vertex_offset=0, uint32_t first_instance=0)
Indexed draw command.
std::unordered_map< RenderPipelineID, PipelineState > m_pipelines
void bind_index_buffer(CommandBufferID cmd_id, const std::shared_ptr< Buffers::VKBuffer > &buffer, vk::IndexType index_type=vk::IndexType::eUint32)
Bind index buffer.
void draw_mesh_tasks(CommandBufferID cmd_id, uint32_t group_count_x, uint32_t group_count_y=1, uint32_t group_count_z=1)
Draw mesh tasks (mesh shading pipeline only)
bool is_window_registered(const std::shared_ptr< Core::Window > &window) const
Check if a window is registered for rendering.
void draw(CommandBufferID cmd_id, uint32_t vertex_count, uint32_t instance_count=1, uint32_t first_vertex=0, uint32_t first_instance=0)
Draw command.
void draw_mesh_tasks_indirect(CommandBufferID cmd_id, const std::shared_ptr< Buffers::VKBuffer > &buffer, vk::DeviceSize offset=0, uint32_t draw_count=1, uint32_t stride=sizeof(VkDrawMeshTasksIndirectCommandEXT))
Draw mesh tasks indirect.
void unregister_window(const std::shared_ptr< Core::Window > &window)
Unregister a window from rendering.
Registry::Service::DisplayService * m_display_service
std::atomic< uint64_t > m_next_pipeline_id
RenderPipelineID create_pipeline(const RenderPipelineConfig &config, const std::vector< vk::Format > &color_formats, vk::Format depth_format=vk::Format::eUndefined)
Create graphics pipeline for dynamic rendering (no render pass object)
void bind_descriptor_sets(CommandBufferID cmd_id, RenderPipelineID pipeline, const std::vector< DescriptorSetID > &descriptor_sets, uint32_t first_set=0)
Bind descriptor sets.
vk::DescriptorSet get_descriptor_set(DescriptorSetID descriptor_set_id)
Get Vulkan descriptor set handle from DescriptorSetID.
vk::Device get_device() const
Get logical device handle.
std::shared_ptr< Core::VKShaderModule > get_vk_shader_module(ShaderID shader_id)
bool is_initialized() const
Check if compiler is initialized.
vk::CommandBuffer get_command_buffer(CommandBufferID cmd_id)
Get Vulkan command buffer handle from CommandBufferID.
vk::PhysicalDevice get_physical_device() const
Get physical device handle.
DescriptorSetID allocate_descriptor_set(vk::DescriptorSetLayout layout)
Allocate descriptor set for a pipeline.
static std::pair< std::vector< Core::VertexBinding >, std::vector< Core::VertexAttribute > > translate_layout(const Kakshya::VertexLayout &layout, uint32_t binding_index=0)
Translate a semantic vertex layout to Vulkan binding/attribute descriptions.
Interface * get_service()
Query for a backend service.
static BackendRegistry & instance()
Get the global registry instance.
@ Rendering
GPU rendering operations (graphics pipeline, frame rendering)
@ Portal
High-level user-facing API layer.
PolygonMode
Rasterization polygon mode.
constexpr RenderPipelineID INVALID_RENDER_PIPELINE
const std::array< float, 4 > default_color
constexpr ShaderID INVALID_SHADER
PrimitiveTopology
Vertex assembly primitive topology.
CompareOp
Depth/stencil comparison operation.
constexpr DescriptorSetID INVALID_DESCRIPTOR_SET
std::vector< ColorBlendAttachment > color_blend_attachments
std::vector< vk::Format > color_attachment_formats
std::shared_ptr< VKShaderModule > task_shader
std::shared_ptr< VKShaderModule > fragment_shader
std::shared_ptr< VKShaderModule > mesh_shader
std::vector< vk::DynamicState > dynamic_states
std::vector< vk::PushConstantRange > push_constant_ranges
std::shared_ptr< VKShaderModule > vertex_shader
std::vector< vk::DescriptorSetLayout > descriptor_set_layouts
std::vector< VertexBinding > vertex_bindings
std::vector< VertexAttribute > vertex_attributes
std::shared_ptr< VKShaderModule > geometry_shader
std::shared_ptr< VKShaderModule > tess_evaluation_shader
std::shared_ptr< VKShaderModule > tess_control_shader
Configuration for creating a graphics pipeline.
std::vector< vk::DescriptorSetLayout > layouts
std::shared_ptr< Core::VKGraphicsPipeline > pipeline
std::vector< std::vector< DescriptorBindingInfo > > descriptor_sets
std::vector< Core::VertexAttribute > vertex_attributes
std::vector< Core::VertexBinding > vertex_bindings
std::optional< Kakshya::VertexLayout > semantic_vertex_layout
std::vector< BlendAttachmentConfig > blend_attachments
Complete render pipeline configuration.
std::function< void *(const std::shared_ptr< void > &)> get_current_image_view
Get current swapchain image view for rendering.
std::function< void(const std::shared_ptr< void > &, uint32_t &, uint32_t &)> get_swapchain_extent
Get swapchain extent for a window.
Backend display and presentation service interface.