MayaFlux 0.3.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
VKGraphicsPipeline.cpp
Go to the documentation of this file.
3
4namespace MayaFlux::Core {
5
6// ============================================================================
7// GraphicsPipelineConfig - Static Preset Configurations
8// ============================================================================
9
11{
13 config.topology = vk::PrimitiveTopology::eTriangleList;
14 config.polygon_mode = vk::PolygonMode::eFill;
15 config.cull_mode = vk::CullModeFlagBits::eBack;
16 config.front_face = vk::FrontFace::eCounterClockwise;
17 config.depth_test_enable = true;
18 config.depth_write_enable = true;
19 config.depth_compare_op = vk::CompareOp::eLess;
20
21 ColorBlendAttachment attachment;
22 attachment.blend_enable = false;
23 config.color_blend_attachments.push_back(attachment);
24
25 return config;
26}
27
29{
31 config.topology = vk::PrimitiveTopology::eTriangleList;
32 config.polygon_mode = vk::PolygonMode::eFill;
33 config.cull_mode = vk::CullModeFlagBits::eNone;
34 config.depth_test_enable = false;
35 config.depth_write_enable = false;
36
37 ColorBlendAttachment attachment;
38 attachment.blend_enable = false;
39 config.color_blend_attachments.push_back(attachment);
40
41 return config;
42}
43
50
57
59{
61 config.topology = vk::PrimitiveTopology::eTriangleList;
62 config.polygon_mode = vk::PolygonMode::eFill;
63 config.cull_mode = vk::CullModeFlagBits::eBack;
64 config.rasterizer_discard_enable = false;
65 config.depth_test_enable = true;
66 config.depth_write_enable = true;
67 config.depth_compare_op = vk::CompareOp::eLess;
68 config.color_blend_attachments.clear();
69 return config;
70}
71
72// ============================================================================
73// GraphicsPipelineConfig - Fluent Configuration Methods
74// ============================================================================
75
77{
78 if (color_blend_attachments.empty()) {
79 color_blend_attachments.emplace_back();
80 }
81
82 for (auto& attachment : color_blend_attachments) {
83 attachment.blend_enable = true;
84 attachment.src_color_blend_factor = vk::BlendFactor::eSrcAlpha;
85 attachment.dst_color_blend_factor = vk::BlendFactor::eOneMinusSrcAlpha;
86 attachment.color_blend_op = vk::BlendOp::eAdd;
87 attachment.src_alpha_blend_factor = vk::BlendFactor::eOne;
88 attachment.dst_alpha_blend_factor = vk::BlendFactor::eZero;
89 attachment.alpha_blend_op = vk::BlendOp::eAdd;
90 }
91}
92
94{
95 if (color_blend_attachments.empty()) {
96 color_blend_attachments.emplace_back();
97 }
98
99 for (auto& attachment : color_blend_attachments) {
100 attachment.blend_enable = true;
101 attachment.src_color_blend_factor = vk::BlendFactor::eSrcAlpha;
102 attachment.dst_color_blend_factor = vk::BlendFactor::eOne;
103 attachment.color_blend_op = vk::BlendOp::eAdd;
104 attachment.src_alpha_blend_factor = vk::BlendFactor::eOne;
105 attachment.dst_alpha_blend_factor = vk::BlendFactor::eZero;
106 attachment.alpha_blend_op = vk::BlendOp::eAdd;
107 }
108}
109
115
117{
118 polygon_mode = vk::PolygonMode::eLine;
119}
120
122{
123 cull_mode = vk::CullModeFlagBits::eBack;
124}
125
127{
128 cull_mode = vk::CullModeFlagBits::eNone;
129}
130
131// ============================================================================
132// VKGraphicsPipeline - Lifecycle
133// ============================================================================
134
136{
137 if (m_pipeline || m_layout) {
139 "VKGraphicsPipeline destroyed without cleanup() - potential leak");
140 }
141}
142
144 : m_device(other.m_device)
145 , m_pipeline(other.m_pipeline)
146 , m_layout(other.m_layout)
147 , m_config(std::move(other.m_config))
148{
149 other.m_device = nullptr;
150 other.m_pipeline = nullptr;
151 other.m_layout = nullptr;
152}
153
155{
156 if (this != &other) {
157 if (m_pipeline || m_layout) {
159 "VKGraphicsPipeline move-assigned without cleanup() - potential leak");
160 }
161
162 m_device = other.m_device;
163 m_pipeline = other.m_pipeline;
164 m_layout = other.m_layout;
165 m_config = std::move(other.m_config);
166
167 other.m_device = nullptr;
168 other.m_pipeline = nullptr;
169 other.m_layout = nullptr;
170 }
171 return *this;
172}
173
174void VKGraphicsPipeline::cleanup(vk::Device device)
175{
176 if (m_pipeline) {
177 device.destroyPipeline(m_pipeline);
178 m_pipeline = nullptr;
180 "Graphics pipeline destroyed");
181 }
182
183 if (m_layout) {
184 device.destroyPipelineLayout(m_layout);
185 m_layout = nullptr;
187 "Graphics pipeline layout destroyed");
188 }
189}
190
191// ============================================================================
192// Pipeline Creation
193// ============================================================================
194
195bool VKGraphicsPipeline::create(vk::Device device, const GraphicsPipelineConfig& config)
196{
197 if (!device) {
199 "Cannot create graphics pipeline with null device");
200 return false;
201 }
202
203 m_device = device;
204 m_config = config;
205
206 if (!validate_shaders(config)) {
208 "Shader validation failed");
209 return false;
210 }
211
212 m_layout = create_pipeline_layout(device, config);
213 if (!m_layout) {
215 "Failed to create pipeline layout");
216 return false;
217 }
218
219 std::vector<vk::PipelineShaderStageCreateInfo> shader_stages;
220 if (config.mesh_shader) {
221 shader_stages.push_back(config.mesh_shader->get_stage_create_info());
222 if (config.task_shader) {
223 shader_stages.push_back(config.task_shader->get_stage_create_info());
224 }
225 } else {
226 if (config.vertex_shader)
227 shader_stages.push_back(config.vertex_shader->get_stage_create_info());
228 if (config.fragment_shader)
229 shader_stages.push_back(config.fragment_shader->get_stage_create_info());
230 if (config.geometry_shader)
231 shader_stages.push_back(config.geometry_shader->get_stage_create_info());
232 if (config.tess_control_shader)
233 shader_stages.push_back(config.tess_control_shader->get_stage_create_info());
234 if (config.tess_evaluation_shader)
235 shader_stages.push_back(config.tess_evaluation_shader->get_stage_create_info());
236 }
237
238 std::vector<vk::VertexInputBindingDescription> vertex_bindings;
239 std::vector<vk::VertexInputAttributeDescription> vertex_attributes;
240 auto vertex_input_state = build_vertex_input_state(config, vertex_bindings, vertex_attributes);
241
242 auto input_assembly_state = build_input_assembly_state(config);
243 auto tessellation_state = build_tessellation_state(config);
244
245 std::vector<vk::Viewport> viewports;
246 std::vector<vk::Rect2D> scissors;
247 auto viewport_state = build_viewport_state(config, viewports, scissors);
248
249 auto rasterization_state = build_rasterization_state(config);
250 auto multisample_state = build_multisample_state(config);
251 auto depth_stencil_state = build_depth_stencil_state(config);
252
253 std::vector<vk::PipelineColorBlendAttachmentState> blend_attachments;
254 auto color_blend_state = build_color_blend_state(config, blend_attachments);
255
256 auto dynamic_state = build_dynamic_state(config);
257
258 vk::GraphicsPipelineCreateInfo pipeline_info;
259 pipeline_info.stageCount = static_cast<uint32_t>(shader_stages.size());
260 pipeline_info.pStages = shader_stages.data();
261 pipeline_info.pVertexInputState = &vertex_input_state;
262 pipeline_info.pInputAssemblyState = &input_assembly_state;
263 pipeline_info.pTessellationState = (config.tess_control_shader || config.tess_evaluation_shader)
264 ? &tessellation_state
265 : nullptr;
266
267 pipeline_info.pViewportState = &viewport_state;
268 pipeline_info.pRasterizationState = &rasterization_state;
269 pipeline_info.pMultisampleState = &multisample_state;
270 pipeline_info.pDepthStencilState = &depth_stencil_state;
271 pipeline_info.pColorBlendState = &color_blend_state;
272 pipeline_info.pDynamicState = config.dynamic_states.empty() ? nullptr : &dynamic_state;
273 pipeline_info.layout = m_layout;
274 pipeline_info.basePipelineHandle = nullptr;
275 pipeline_info.basePipelineIndex = -1;
276
277 vk::PipelineRenderingCreateInfo rendering_create_info;
278 rendering_create_info.colorAttachmentCount = static_cast<uint32_t>(config.color_attachment_formats.size());
279 rendering_create_info.pColorAttachmentFormats = config.color_attachment_formats.data();
280 rendering_create_info.depthAttachmentFormat = config.depth_attachment_format;
281 rendering_create_info.stencilAttachmentFormat = config.stencil_attachment_format;
282
283 pipeline_info.pNext = &rendering_create_info;
284 pipeline_info.renderPass = nullptr;
285 pipeline_info.subpass = 0;
286
288 "Creating pipeline for dynamic rendering ({} color attachments)",
289 config.color_attachment_formats.size());
290
291 try {
292 auto result = device.createGraphicsPipeline(config.cache, pipeline_info);
293 if (result.result != vk::Result::eSuccess) {
295 "Failed to create graphics pipeline: {}", vk::to_string(result.result));
296 device.destroyPipelineLayout(m_layout);
297 m_layout = nullptr;
298 return false;
299 }
300 m_pipeline = result.value;
301 } catch (const vk::SystemError& e) {
303 "Failed to create graphics pipeline: {}", e.what());
304 device.destroyPipelineLayout(m_layout);
305 m_layout = nullptr;
306 return false;
307 }
308
310 "Graphics pipeline created ({} shader stages)", shader_stages.size());
311
312 return true;
313}
314
316 vk::Device device,
317 const GraphicsPipelineConfig& config)
318{
319 vk::PipelineLayoutCreateInfo layout_info;
320 layout_info.setLayoutCount = static_cast<uint32_t>(config.descriptor_set_layouts.size());
321 layout_info.pSetLayouts = config.descriptor_set_layouts.empty() ? nullptr : config.descriptor_set_layouts.data();
322 layout_info.pushConstantRangeCount = static_cast<uint32_t>(config.push_constant_ranges.size());
323 layout_info.pPushConstantRanges = config.push_constant_ranges.empty() ? nullptr : config.push_constant_ranges.data();
324
325 vk::PipelineLayout layout;
326 try {
327 layout = device.createPipelineLayout(layout_info);
328 } catch (const vk::SystemError& e) {
330 "Failed to create pipeline layout: {}", e.what());
331 return nullptr;
332 }
333
335 "Graphics pipeline layout created ({} sets, {} push constant ranges)",
336 config.descriptor_set_layouts.size(), config.push_constant_ranges.size());
337
338 return layout;
339}
340
342{
343 const bool has_mesh = config.mesh_shader != nullptr;
344
345 if (has_mesh) {
346 if (config.mesh_shader->get_stage() != vk::ShaderStageFlagBits::eMeshEXT) {
348 "Mesh shader must have stage eMeshEXT");
349 return false;
350 }
351 if (config.task_shader && config.task_shader->get_stage() != vk::ShaderStageFlagBits::eTaskEXT) {
353 "Task shader must have stage eTaskEXT");
354 return false;
355 }
356
357 if (config.vertex_shader || config.geometry_shader || config.tess_control_shader || config.tess_evaluation_shader) {
359 "Mesh shading pipeline cannot contain vertex/geometry/tessellation shaders");
360 return false;
361 }
362 return true;
363 }
364
365 if (!config.vertex_shader) {
367 "Graphics pipeline requires a vertex shader");
368 return false;
369 }
370
371 if (!config.vertex_shader->is_valid()) {
373 "Vertex shader is not valid");
374 return false;
375 }
376
377 if (config.vertex_shader->get_stage() != vk::ShaderStageFlagBits::eVertex) {
379 "Vertex shader has wrong stage: {}", vk::to_string(config.vertex_shader->get_stage()));
380 return false;
381 }
382
383 if (config.fragment_shader && config.fragment_shader->get_stage() != vk::ShaderStageFlagBits::eFragment) {
385 "Fragment shader has wrong stage: {}", vk::to_string(config.fragment_shader->get_stage()));
386 return false;
387 }
388
389 if (config.geometry_shader && config.geometry_shader->get_stage() != vk::ShaderStageFlagBits::eGeometry) {
391 "Geometry shader has wrong stage");
392 return false;
393 }
394
395 if (config.tess_control_shader && config.tess_control_shader->get_stage() != vk::ShaderStageFlagBits::eTessellationControl) {
397 "Tessellation control shader has wrong stage");
398 return false;
399 }
400
401 if (config.tess_evaluation_shader && config.tess_evaluation_shader->get_stage() != vk::ShaderStageFlagBits::eTessellationEvaluation) {
403 "Tessellation evaluation shader has wrong stage");
404 return false;
405 }
406
407 return true;
408}
409
410// ============================================================================
411// Pipeline State Builders
412// ============================================================================
413
414vk::PipelineVertexInputStateCreateInfo VKGraphicsPipeline::build_vertex_input_state(
415 const GraphicsPipelineConfig& config,
416 std::vector<vk::VertexInputBindingDescription>& bindings,
417 std::vector<vk::VertexInputAttributeDescription>& attributes)
418{
419 if (!config.vertex_bindings.empty() || !config.vertex_attributes.empty()) {
421 "Using explicit vertex bindings/attributes from config "
422 "({} bindings, {} attributes)",
423 config.vertex_bindings.size(), config.vertex_attributes.size());
424
425 for (const auto& binding : config.vertex_bindings) {
426 vk::VertexInputBindingDescription vk_binding;
427 vk_binding.binding = binding.binding;
428 vk_binding.stride = binding.stride;
429 vk_binding.inputRate = binding.input_rate;
430 bindings.push_back(vk_binding);
431 }
432
433 for (const auto& attribute : config.vertex_attributes) {
434 vk::VertexInputAttributeDescription vk_attr;
435 vk_attr.location = attribute.location;
436 vk_attr.binding = attribute.binding;
437 vk_attr.format = attribute.format;
438 vk_attr.offset = attribute.offset;
439 attributes.push_back(vk_attr);
440 }
441 } else if (
443 && config.vertex_shader
444 && config.vertex_shader->has_vertex_input()) {
445
447 "Using vertex input from shader reflection");
448 const auto& vertex_input = config.vertex_shader->get_vertex_input();
449
450 for (const auto& binding : vertex_input.bindings) {
451 vk::VertexInputBindingDescription vk_binding;
452 vk_binding.binding = binding.binding;
453 vk_binding.stride = binding.stride;
454 vk_binding.inputRate = binding.rate;
455 bindings.push_back(vk_binding);
456 }
457
458 for (const auto& attribute : vertex_input.attributes) {
459 vk::VertexInputAttributeDescription vk_attr;
460 vk_attr.location = attribute.location;
461 vk_attr.binding = 0;
462 vk_attr.format = attribute.format;
463 vk_attr.offset = attribute.offset;
464 attributes.push_back(vk_attr);
465 }
466 } else {
468 "No vertex input: using empty vertex state (full-screen quad or compute)");
469 }
470
471 vk::PipelineVertexInputStateCreateInfo vertex_input;
472 if (config.mesh_shader) {
474 "Mesh shading pipeline — vertex input ignored");
475 vertex_input.vertexBindingDescriptionCount = 0;
476 vertex_input.vertexAttributeDescriptionCount = 0;
477 return vertex_input;
478 }
479
480 vertex_input.vertexBindingDescriptionCount = static_cast<uint32_t>(bindings.size());
481 vertex_input.pVertexBindingDescriptions = bindings.empty() ? nullptr : bindings.data();
482 vertex_input.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributes.size());
483 vertex_input.pVertexAttributeDescriptions = attributes.empty() ? nullptr : attributes.data();
484
485 return vertex_input;
486}
487
488vk::PipelineInputAssemblyStateCreateInfo VKGraphicsPipeline::build_input_assembly_state(
489 const GraphicsPipelineConfig& config)
490{
491 vk::PipelineInputAssemblyStateCreateInfo input_assembly;
492 input_assembly.topology = config.topology;
493 input_assembly.primitiveRestartEnable = config.primitive_restart_enable;
494 return input_assembly;
495}
496
497vk::PipelineTessellationStateCreateInfo VKGraphicsPipeline::build_tessellation_state(
498 const GraphicsPipelineConfig& config)
499{
500 vk::PipelineTessellationStateCreateInfo tessellation;
501 tessellation.patchControlPoints = config.patch_control_points;
502 return tessellation;
503}
504
505vk::PipelineViewportStateCreateInfo VKGraphicsPipeline::build_viewport_state(
506 const GraphicsPipelineConfig& config,
507 std::vector<vk::Viewport>& viewports,
508 std::vector<vk::Rect2D>& scissors)
509{
510 if (!config.dynamic_viewport) {
511 viewports.push_back(config.static_viewport);
512 } else {
513 viewports.emplace_back();
514 }
515
516 if (!config.dynamic_scissor) {
517 scissors.push_back(config.static_scissor);
518 } else {
519 scissors.emplace_back();
520 }
521
522 vk::PipelineViewportStateCreateInfo viewport_state;
523 viewport_state.viewportCount = static_cast<uint32_t>(viewports.size());
524 viewport_state.pViewports = config.dynamic_viewport ? nullptr : viewports.data();
525 viewport_state.scissorCount = static_cast<uint32_t>(scissors.size());
526 viewport_state.pScissors = config.dynamic_scissor ? nullptr : scissors.data();
527
528 return viewport_state;
529}
530
531vk::PipelineRasterizationStateCreateInfo VKGraphicsPipeline::build_rasterization_state(
532 const GraphicsPipelineConfig& config)
533{
534 vk::PipelineRasterizationStateCreateInfo rasterization;
535 rasterization.depthClampEnable = config.depth_clamp_enable;
536 rasterization.rasterizerDiscardEnable = config.rasterizer_discard_enable;
537 rasterization.polygonMode = config.polygon_mode;
538 rasterization.cullMode = config.cull_mode;
539 rasterization.frontFace = config.front_face;
540 rasterization.depthBiasEnable = config.depth_bias_enable;
541 rasterization.depthBiasConstantFactor = config.depth_bias_constant_factor;
542 rasterization.depthBiasClamp = config.depth_bias_clamp;
543 rasterization.depthBiasSlopeFactor = config.depth_bias_slope_factor;
544 rasterization.lineWidth = config.line_width;
545
546 return rasterization;
547}
548
549vk::PipelineMultisampleStateCreateInfo VKGraphicsPipeline::build_multisample_state(
550 const GraphicsPipelineConfig& config)
551{
552 vk::PipelineMultisampleStateCreateInfo multisample;
553 multisample.rasterizationSamples = config.rasterization_samples;
554 multisample.sampleShadingEnable = config.sample_shading_enable;
555 multisample.minSampleShading = config.min_sample_shading;
556 multisample.pSampleMask = config.sample_mask.empty() ? nullptr : config.sample_mask.data();
557 multisample.alphaToCoverageEnable = config.alpha_to_coverage_enable;
558 multisample.alphaToOneEnable = config.alpha_to_one_enable;
559
560 return multisample;
561}
562
563vk::PipelineDepthStencilStateCreateInfo VKGraphicsPipeline::build_depth_stencil_state(
564 const GraphicsPipelineConfig& config)
565{
566 vk::PipelineDepthStencilStateCreateInfo depth_stencil;
567 depth_stencil.depthTestEnable = config.depth_test_enable;
568 depth_stencil.depthWriteEnable = config.depth_write_enable;
569 depth_stencil.depthCompareOp = config.depth_compare_op;
570 depth_stencil.depthBoundsTestEnable = config.depth_bounds_test_enable;
571 depth_stencil.minDepthBounds = config.min_depth_bounds;
572 depth_stencil.maxDepthBounds = config.max_depth_bounds;
573 depth_stencil.stencilTestEnable = config.stencil_test_enable;
574 depth_stencil.front = config.front_stencil;
575 depth_stencil.back = config.back_stencil;
576
577 return depth_stencil;
578}
579
580vk::PipelineColorBlendStateCreateInfo VKGraphicsPipeline::build_color_blend_state(
581 const GraphicsPipelineConfig& config,
582 std::vector<vk::PipelineColorBlendAttachmentState>& attachments)
583{
584 for (const auto& config_attachment : config.color_blend_attachments) {
585 vk::PipelineColorBlendAttachmentState attachment;
586 attachment.blendEnable = config_attachment.blend_enable;
587 attachment.srcColorBlendFactor = config_attachment.src_color_blend_factor;
588 attachment.dstColorBlendFactor = config_attachment.dst_color_blend_factor;
589 attachment.colorBlendOp = config_attachment.color_blend_op;
590 attachment.srcAlphaBlendFactor = config_attachment.src_alpha_blend_factor;
591 attachment.dstAlphaBlendFactor = config_attachment.dst_alpha_blend_factor;
592 attachment.alphaBlendOp = config_attachment.alpha_blend_op;
593 attachment.colorWriteMask = config_attachment.color_write_mask;
594 attachments.push_back(attachment);
595 }
596
597 vk::PipelineColorBlendStateCreateInfo color_blend;
598 color_blend.logicOpEnable = config.logic_op_enable;
599 color_blend.logicOp = config.logic_op;
600 color_blend.attachmentCount = static_cast<uint32_t>(attachments.size());
601 color_blend.pAttachments = attachments.empty() ? nullptr : attachments.data();
602 color_blend.blendConstants[0] = config.blend_constants[0];
603 color_blend.blendConstants[1] = config.blend_constants[1];
604 color_blend.blendConstants[2] = config.blend_constants[2];
605 color_blend.blendConstants[3] = config.blend_constants[3];
606
607 return color_blend;
608}
609
610vk::PipelineDynamicStateCreateInfo VKGraphicsPipeline::build_dynamic_state(
611 const GraphicsPipelineConfig& config)
612{
613 vk::PipelineDynamicStateCreateInfo dynamic_state;
614 dynamic_state.dynamicStateCount = static_cast<uint32_t>(config.dynamic_states.size());
615 dynamic_state.pDynamicStates = config.dynamic_states.data();
616 return dynamic_state;
617}
618
619// ============================================================================
620// Pipeline Binding
621// ============================================================================
622
623void VKGraphicsPipeline::bind(vk::CommandBuffer cmd)
624{
625 if (!m_pipeline) {
627 "Cannot bind invalid graphics pipeline");
628 return;
629 }
630
631 cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, m_pipeline);
632}
633
635 vk::CommandBuffer cmd,
636 std::span<vk::DescriptorSet> sets,
637 uint32_t first_set)
638{
639 if (!m_layout) {
641 "Cannot bind descriptor sets without pipeline layout");
642 return;
643 }
644
645 if (sets.empty()) {
647 "Binding empty descriptor sets");
648 return;
649 }
650
651 cmd.bindDescriptorSets(
652 vk::PipelineBindPoint::eGraphics,
653 m_layout,
654 first_set,
655 static_cast<uint32_t>(sets.size()),
656 sets.data(),
657 0, nullptr);
658}
659
661 vk::CommandBuffer cmd,
662 vk::ShaderStageFlags stages,
663 uint32_t offset,
664 uint32_t size,
665 const void* data)
666{
667 if (!m_layout) {
669 "Cannot push constants without pipeline layout");
670 return;
671 }
672
673 if (!data) {
675 "Cannot push null data");
676 return;
677 }
678
679 cmd.pushConstants(m_layout, stages, offset, size, data);
680}
681
682// ============================================================================
683// Vertex/Index Buffer Binding
684// ============================================================================
685
687 vk::CommandBuffer cmd,
688 uint32_t first_binding,
689 std::span<vk::Buffer> buffers,
690 std::span<vk::DeviceSize> offsets)
691{
692 if (buffers.empty()) {
694 "Binding empty vertex buffers");
695 return;
696 }
697
698 if (buffers.size() != offsets.size()) {
700 "Buffer count ({}) does not match offset count ({})",
701 buffers.size(), offsets.size());
702 return;
703 }
704
705 cmd.bindVertexBuffers(
706 first_binding,
707 static_cast<uint32_t>(buffers.size()),
708 buffers.data(),
709 offsets.data());
710}
711
713 vk::CommandBuffer cmd,
714 vk::Buffer buffer,
715 vk::DeviceSize offset,
716 uint32_t binding)
717{
718 if (!buffer) {
720 "Cannot bind null vertex buffer");
721 return;
722 }
723
724 cmd.bindVertexBuffers(binding, 1, &buffer, &offset);
725}
726
728 vk::CommandBuffer cmd,
729 vk::Buffer buffer,
730 vk::DeviceSize offset,
731 vk::IndexType index_type)
732{
733 if (!buffer) {
735 "Cannot bind null index buffer");
736 return;
737 }
738
739 cmd.bindIndexBuffer(buffer, offset, index_type);
740}
741
742// ============================================================================
743// Dynamic State
744// ============================================================================
745
746void VKGraphicsPipeline::set_viewport(vk::CommandBuffer cmd, const vk::Viewport& viewport)
747{
748 cmd.setViewport(0, 1, &viewport);
749}
750
751void VKGraphicsPipeline::set_scissor(vk::CommandBuffer cmd, const vk::Rect2D& scissor)
752{
753 cmd.setScissor(0, 1, &scissor);
754}
755
756void VKGraphicsPipeline::set_line_width(vk::CommandBuffer cmd, float width)
757{
758 cmd.setLineWidth(width);
759}
760
762 vk::CommandBuffer cmd,
763 float constant_factor,
764 float clamp,
765 float slope_factor)
766{
767 cmd.setDepthBias(constant_factor, clamp, slope_factor);
768}
769
770void VKGraphicsPipeline::set_blend_constants(vk::CommandBuffer cmd, const float constants[4])
771{
772 cmd.setBlendConstants(constants);
773}
774
775// ============================================================================
776// Draw Commands
777// ============================================================================
778
780 vk::CommandBuffer cmd,
781 uint32_t vertex_count,
782 uint32_t instance_count,
783 uint32_t first_vertex,
784 uint32_t first_instance)
785{
786 if (!m_pipeline) {
788 "Cannot draw with invalid pipeline");
789 return;
790 }
791
792 if (vertex_count == 0) {
794 "Drawing with zero vertices");
795 return;
796 }
797
798 cmd.draw(vertex_count, instance_count, first_vertex, first_instance);
799}
800
802 vk::CommandBuffer cmd,
803 uint32_t index_count,
804 uint32_t instance_count,
805 uint32_t first_index,
806 int32_t vertex_offset,
807 uint32_t first_instance)
808{
809 if (!m_pipeline) {
811 "Cannot draw indexed with invalid pipeline");
812 return;
813 }
814
815 if (index_count == 0) {
817 "Drawing with zero indices");
818 return;
819 }
820
821 cmd.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance);
822}
823
825 vk::CommandBuffer cmd,
826 vk::Buffer buffer,
827 vk::DeviceSize offset,
828 uint32_t draw_count,
829 uint32_t stride)
830{
831 if (!m_pipeline) {
833 "Cannot draw indirect with invalid pipeline");
834 return;
835 }
836
837 if (!buffer) {
839 "Cannot draw indirect with null buffer");
840 return;
841 }
842
843 if (draw_count == 0) {
845 "Drawing with zero draw count");
846 return;
847 }
848
849 cmd.drawIndirect(buffer, offset, draw_count, stride);
850}
851
853 vk::CommandBuffer cmd,
854 vk::Buffer buffer,
855 vk::DeviceSize offset,
856 uint32_t draw_count,
857 uint32_t stride)
858{
859 if (!m_pipeline) {
861 "Cannot draw indexed indirect with invalid pipeline");
862 return;
863 }
864
865 if (!buffer) {
867 "Cannot draw indexed indirect with null buffer");
868 return;
869 }
870
871 if (draw_count == 0) {
873 "Drawing with zero draw count");
874 return;
875 }
876
877 cmd.drawIndexedIndirect(buffer, offset, draw_count, stride);
878}
879
881 vk::CommandBuffer cmd,
882 uint32_t groupCountX,
883 uint32_t groupCountY,
884 uint32_t groupCountZ)
885{
886 if (!m_pipeline) {
888 "Cannot draw mesh tasks with invalid pipeline");
889 return;
890 }
891 cmd.drawMeshTasksEXT(groupCountX, groupCountY, groupCountZ);
892}
893
895 vk::CommandBuffer cmd,
896 vk::Buffer buffer,
897 vk::DeviceSize offset,
898 uint32_t drawCount,
899 uint32_t stride)
900{
901 if (!m_pipeline) {
903 "Cannot draw mesh tasks indirect with invalid pipeline");
904 return;
905 }
906 cmd.drawMeshTasksIndirectEXT(buffer, offset, drawCount, stride);
907}
908
909} // namespace MayaFlux::Core
#define MF_INFO(comp, ctx,...)
#define MF_ERROR(comp, ctx,...)
#define MF_WARN(comp, ctx,...)
#define MF_DEBUG(comp, ctx,...)
bool validate_shaders(const GraphicsPipelineConfig &config)
Validate shader stages.
void cleanup(vk::Device device)
Cleanup pipeline resources.
void bind(vk::CommandBuffer cmd)
Bind pipeline to command buffer.
void set_line_width(vk::CommandBuffer cmd, float width)
Set line width (if dynamic)
void set_scissor(vk::CommandBuffer cmd, const vk::Rect2D &scissor)
Set scissor (if dynamic)
void bind_descriptor_sets(vk::CommandBuffer cmd, std::span< vk::DescriptorSet > sets, uint32_t first_set=0)
Bind descriptor sets.
vk::PipelineDepthStencilStateCreateInfo build_depth_stencil_state(const GraphicsPipelineConfig &config)
Build depth stencil state.
void draw(vk::CommandBuffer cmd, uint32_t vertex_count, uint32_t instance_count=1, uint32_t first_vertex=0, uint32_t first_instance=0)
Draw vertices.
void set_depth_bias(vk::CommandBuffer cmd, float constant_factor, float clamp, float slope_factor)
Set depth bias (if dynamic)
vk::PipelineInputAssemblyStateCreateInfo build_input_assembly_state(const GraphicsPipelineConfig &config)
Build input assembly state.
VKGraphicsPipeline & operator=(const VKGraphicsPipeline &)=delete
void bind_vertex_buffers(vk::CommandBuffer cmd, uint32_t first_binding, std::span< vk::Buffer > buffers, std::span< vk::DeviceSize > offsets)
Bind vertex buffers.
void set_blend_constants(vk::CommandBuffer cmd, const float constants[4])
Set blend constants (if dynamic)
void push_constants(vk::CommandBuffer cmd, vk::ShaderStageFlags stages, uint32_t offset, uint32_t size, const void *data)
Push constants.
vk::PipelineMultisampleStateCreateInfo build_multisample_state(const GraphicsPipelineConfig &config)
Build multisample state.
void bind_vertex_buffer(vk::CommandBuffer cmd, vk::Buffer buffer, vk::DeviceSize offset=0, uint32_t binding=0)
Bind single vertex buffer (common case)
vk::PipelineRasterizationStateCreateInfo build_rasterization_state(const GraphicsPipelineConfig &config)
Build rasterization state.
vk::PipelineVertexInputStateCreateInfo build_vertex_input_state(const GraphicsPipelineConfig &config, std::vector< vk::VertexInputBindingDescription > &bindings, std::vector< vk::VertexInputAttributeDescription > &attributes)
Build vertex input state from config.
vk::PipelineTessellationStateCreateInfo build_tessellation_state(const GraphicsPipelineConfig &config)
Build tessellation state.
bool create(vk::Device device, const GraphicsPipelineConfig &config)
Create graphics pipeline from configuration.
void drawMeshTasksIndirect(vk::CommandBuffer cmd, vk::Buffer buffer, vk::DeviceSize offset, uint32_t drawCount, uint32_t stride)
Draw mesh tasks indirect (mesh shading pipeline only)
void set_viewport(vk::CommandBuffer cmd, const vk::Viewport &viewport)
Set viewport (if dynamic)
void draw_indexed_indirect(vk::CommandBuffer cmd, vk::Buffer buffer, vk::DeviceSize offset, uint32_t draw_count, uint32_t stride)
Draw indexed indirect.
vk::PipelineLayout create_pipeline_layout(vk::Device device, const GraphicsPipelineConfig &config)
Create pipeline layout from config.
void draw_indexed(vk::CommandBuffer cmd, uint32_t index_count, uint32_t instance_count=1, uint32_t first_index=0, int32_t vertex_offset=0, uint32_t first_instance=0)
Draw indexed vertices.
vk::PipelineColorBlendStateCreateInfo build_color_blend_state(const GraphicsPipelineConfig &config, std::vector< vk::PipelineColorBlendAttachmentState > &attachments)
Build color blend state.
void drawMeshTasks(vk::CommandBuffer cmd, uint32_t groupCountX, uint32_t groupCountY=1, uint32_t groupCountZ=1)
Draw mesh tasks (mesh shading pipeline only)
vk::PipelineDynamicStateCreateInfo build_dynamic_state(const GraphicsPipelineConfig &config)
Build dynamic state.
vk::PipelineViewportStateCreateInfo build_viewport_state(const GraphicsPipelineConfig &config, std::vector< vk::Viewport > &viewports, std::vector< vk::Rect2D > &scissors)
Build viewport state.
void bind_index_buffer(vk::CommandBuffer cmd, vk::Buffer buffer, vk::DeviceSize offset=0, vk::IndexType index_type=vk::IndexType::eUint32)
Bind index buffer.
void draw_indirect(vk::CommandBuffer cmd, vk::Buffer buffer, vk::DeviceSize offset, uint32_t draw_count, uint32_t stride)
Draw indirect (dispatch from GPU buffer)
Vulkan graphics pipeline wrapper.
@ GraphicsBackend
Graphics/visual rendering backend (Vulkan, OpenGL)
@ Core
Core engine, backend, subsystems.
std::vector< ColorBlendAttachment > color_blend_attachments
std::vector< vk::Format > color_attachment_formats
static GraphicsPipelineConfig default_3d()
std::shared_ptr< VKShaderModule > task_shader
std::shared_ptr< VKShaderModule > fragment_shader
static GraphicsPipelineConfig alpha_blended()
std::shared_ptr< VKShaderModule > mesh_shader
std::vector< vk::DynamicState > dynamic_states
std::vector< vk::PushConstantRange > push_constant_ranges
static GraphicsPipelineConfig additive_blended()
std::shared_ptr< VKShaderModule > vertex_shader
std::vector< vk::DescriptorSetLayout > descriptor_set_layouts
std::vector< vk::SampleMask > sample_mask
std::vector< VertexBinding > vertex_bindings
static GraphicsPipelineConfig depth_only()
std::vector< VertexAttribute > vertex_attributes
static GraphicsPipelineConfig default_2d()
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.