MayaFlux 0.2.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.vertex_shader) {
221 shader_stages.push_back(config.vertex_shader->get_stage_create_info());
222 }
223 if (config.fragment_shader) {
224 shader_stages.push_back(config.fragment_shader->get_stage_create_info());
225 }
226 if (config.geometry_shader) {
227 shader_stages.push_back(config.geometry_shader->get_stage_create_info());
228 }
229 if (config.tess_control_shader) {
230 shader_stages.push_back(config.tess_control_shader->get_stage_create_info());
231 }
232 if (config.tess_evaluation_shader) {
233 shader_stages.push_back(config.tess_evaluation_shader->get_stage_create_info());
234 }
235
236 std::vector<vk::VertexInputBindingDescription> vertex_bindings;
237 std::vector<vk::VertexInputAttributeDescription> vertex_attributes;
238 auto vertex_input_state = build_vertex_input_state(config, vertex_bindings, vertex_attributes);
239
240 auto input_assembly_state = build_input_assembly_state(config);
241 auto tessellation_state = build_tessellation_state(config);
242
243 std::vector<vk::Viewport> viewports;
244 std::vector<vk::Rect2D> scissors;
245 auto viewport_state = build_viewport_state(config, viewports, scissors);
246
247 auto rasterization_state = build_rasterization_state(config);
248 auto multisample_state = build_multisample_state(config);
249 auto depth_stencil_state = build_depth_stencil_state(config);
250
251 std::vector<vk::PipelineColorBlendAttachmentState> blend_attachments;
252 auto color_blend_state = build_color_blend_state(config, blend_attachments);
253
254 auto dynamic_state = build_dynamic_state(config);
255
256 vk::GraphicsPipelineCreateInfo pipeline_info;
257 pipeline_info.stageCount = static_cast<uint32_t>(shader_stages.size());
258 pipeline_info.pStages = shader_stages.data();
259 pipeline_info.pVertexInputState = &vertex_input_state;
260 pipeline_info.pInputAssemblyState = &input_assembly_state;
261 pipeline_info.pTessellationState = (config.tess_control_shader || config.tess_evaluation_shader)
262 ? &tessellation_state
263 : nullptr;
264
265 pipeline_info.pViewportState = &viewport_state;
266 pipeline_info.pRasterizationState = &rasterization_state;
267 pipeline_info.pMultisampleState = &multisample_state;
268 pipeline_info.pDepthStencilState = &depth_stencil_state;
269 pipeline_info.pColorBlendState = &color_blend_state;
270 pipeline_info.pDynamicState = config.dynamic_states.empty() ? nullptr : &dynamic_state;
271 pipeline_info.layout = m_layout;
272 pipeline_info.basePipelineHandle = nullptr;
273 pipeline_info.basePipelineIndex = -1;
274
275 vk::PipelineRenderingCreateInfo rendering_create_info;
276 rendering_create_info.colorAttachmentCount = static_cast<uint32_t>(config.color_attachment_formats.size());
277 rendering_create_info.pColorAttachmentFormats = config.color_attachment_formats.data();
278 rendering_create_info.depthAttachmentFormat = config.depth_attachment_format;
279 rendering_create_info.stencilAttachmentFormat = config.stencil_attachment_format;
280
281 pipeline_info.pNext = &rendering_create_info;
282 pipeline_info.renderPass = nullptr;
283 pipeline_info.subpass = 0;
284
286 "Creating pipeline for dynamic rendering ({} color attachments)",
287 config.color_attachment_formats.size());
288
289 try {
290 auto result = device.createGraphicsPipeline(config.cache, pipeline_info);
291 if (result.result != vk::Result::eSuccess) {
293 "Failed to create graphics pipeline: {}", vk::to_string(result.result));
294 device.destroyPipelineLayout(m_layout);
295 m_layout = nullptr;
296 return false;
297 }
298 m_pipeline = result.value;
299 } catch (const vk::SystemError& e) {
301 "Failed to create graphics pipeline: {}", e.what());
302 device.destroyPipelineLayout(m_layout);
303 m_layout = nullptr;
304 return false;
305 }
306
308 "Graphics pipeline created ({} shader stages)", shader_stages.size());
309
310 return true;
311}
312
314 vk::Device device,
315 const GraphicsPipelineConfig& config)
316{
317 vk::PipelineLayoutCreateInfo layout_info;
318 layout_info.setLayoutCount = static_cast<uint32_t>(config.descriptor_set_layouts.size());
319 layout_info.pSetLayouts = config.descriptor_set_layouts.empty() ? nullptr : config.descriptor_set_layouts.data();
320 layout_info.pushConstantRangeCount = static_cast<uint32_t>(config.push_constant_ranges.size());
321 layout_info.pPushConstantRanges = config.push_constant_ranges.empty() ? nullptr : config.push_constant_ranges.data();
322
323 vk::PipelineLayout layout;
324 try {
325 layout = device.createPipelineLayout(layout_info);
326 } catch (const vk::SystemError& e) {
328 "Failed to create pipeline layout: {}", e.what());
329 return nullptr;
330 }
331
333 "Graphics pipeline layout created ({} sets, {} push constant ranges)",
334 config.descriptor_set_layouts.size(), config.push_constant_ranges.size());
335
336 return layout;
337}
338
340{
341 if (!config.vertex_shader) {
343 "Graphics pipeline requires a vertex shader");
344 return false;
345 }
346
347 if (!config.vertex_shader->is_valid()) {
349 "Vertex shader is not valid");
350 return false;
351 }
352
353 if (config.vertex_shader->get_stage() != vk::ShaderStageFlagBits::eVertex) {
355 "Vertex shader has wrong stage: {}", vk::to_string(config.vertex_shader->get_stage()));
356 return false;
357 }
358
359 if (config.fragment_shader && config.fragment_shader->get_stage() != vk::ShaderStageFlagBits::eFragment) {
361 "Fragment shader has wrong stage: {}", vk::to_string(config.fragment_shader->get_stage()));
362 return false;
363 }
364
365 if (config.geometry_shader && config.geometry_shader->get_stage() != vk::ShaderStageFlagBits::eGeometry) {
367 "Geometry shader has wrong stage");
368 return false;
369 }
370
371 if (config.tess_control_shader && config.tess_control_shader->get_stage() != vk::ShaderStageFlagBits::eTessellationControl) {
373 "Tessellation control shader has wrong stage");
374 return false;
375 }
376
377 if (config.tess_evaluation_shader && config.tess_evaluation_shader->get_stage() != vk::ShaderStageFlagBits::eTessellationEvaluation) {
379 "Tessellation evaluation shader has wrong stage");
380 return false;
381 }
382
383 return true;
384}
385
386// ============================================================================
387// Pipeline State Builders
388// ============================================================================
389
390vk::PipelineVertexInputStateCreateInfo VKGraphicsPipeline::build_vertex_input_state(
391 const GraphicsPipelineConfig& config,
392 std::vector<vk::VertexInputBindingDescription>& bindings,
393 std::vector<vk::VertexInputAttributeDescription>& attributes)
394{
395 if (!config.vertex_bindings.empty() || !config.vertex_attributes.empty()) {
397 "Using explicit vertex bindings/attributes from config "
398 "({} bindings, {} attributes)",
399 config.vertex_bindings.size(), config.vertex_attributes.size());
400
401 for (const auto& binding : config.vertex_bindings) {
402 vk::VertexInputBindingDescription vk_binding;
403 vk_binding.binding = binding.binding;
404 vk_binding.stride = binding.stride;
405 vk_binding.inputRate = binding.input_rate;
406 bindings.push_back(vk_binding);
407 }
408
409 for (const auto& attribute : config.vertex_attributes) {
410 vk::VertexInputAttributeDescription vk_attr;
411 vk_attr.location = attribute.location;
412 vk_attr.binding = attribute.binding;
413 vk_attr.format = attribute.format;
414 vk_attr.offset = attribute.offset;
415 attributes.push_back(vk_attr);
416 }
417 } else if (
419 && config.vertex_shader
420 && config.vertex_shader->has_vertex_input()) {
421
423 "Using vertex input from shader reflection");
424 const auto& vertex_input = config.vertex_shader->get_vertex_input();
425
426 for (const auto& binding : vertex_input.bindings) {
427 vk::VertexInputBindingDescription vk_binding;
428 vk_binding.binding = binding.binding;
429 vk_binding.stride = binding.stride;
430 vk_binding.inputRate = binding.rate;
431 bindings.push_back(vk_binding);
432 }
433
434 for (const auto& attribute : vertex_input.attributes) {
435 vk::VertexInputAttributeDescription vk_attr;
436 vk_attr.location = attribute.location;
437 vk_attr.binding = 0;
438 vk_attr.format = attribute.format;
439 vk_attr.offset = attribute.offset;
440 attributes.push_back(vk_attr);
441 }
442 } else {
444 "No vertex input: using empty vertex state (full-screen quad or compute)");
445 }
446
447 vk::PipelineVertexInputStateCreateInfo vertex_input;
448 vertex_input.vertexBindingDescriptionCount = static_cast<uint32_t>(bindings.size());
449 vertex_input.pVertexBindingDescriptions = bindings.empty() ? nullptr : bindings.data();
450 vertex_input.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributes.size());
451 vertex_input.pVertexAttributeDescriptions = attributes.empty() ? nullptr : attributes.data();
452
453 return vertex_input;
454}
455
456vk::PipelineInputAssemblyStateCreateInfo VKGraphicsPipeline::build_input_assembly_state(
457 const GraphicsPipelineConfig& config)
458{
459 vk::PipelineInputAssemblyStateCreateInfo input_assembly;
460 input_assembly.topology = config.topology;
461 input_assembly.primitiveRestartEnable = config.primitive_restart_enable;
462 return input_assembly;
463}
464
465vk::PipelineTessellationStateCreateInfo VKGraphicsPipeline::build_tessellation_state(
466 const GraphicsPipelineConfig& config)
467{
468 vk::PipelineTessellationStateCreateInfo tessellation;
469 tessellation.patchControlPoints = config.patch_control_points;
470 return tessellation;
471}
472
473vk::PipelineViewportStateCreateInfo VKGraphicsPipeline::build_viewport_state(
474 const GraphicsPipelineConfig& config,
475 std::vector<vk::Viewport>& viewports,
476 std::vector<vk::Rect2D>& scissors)
477{
478 if (!config.dynamic_viewport) {
479 viewports.push_back(config.static_viewport);
480 } else {
481 viewports.emplace_back();
482 }
483
484 if (!config.dynamic_scissor) {
485 scissors.push_back(config.static_scissor);
486 } else {
487 scissors.emplace_back();
488 }
489
490 vk::PipelineViewportStateCreateInfo viewport_state;
491 viewport_state.viewportCount = static_cast<uint32_t>(viewports.size());
492 viewport_state.pViewports = config.dynamic_viewport ? nullptr : viewports.data();
493 viewport_state.scissorCount = static_cast<uint32_t>(scissors.size());
494 viewport_state.pScissors = config.dynamic_scissor ? nullptr : scissors.data();
495
496 return viewport_state;
497}
498
499vk::PipelineRasterizationStateCreateInfo VKGraphicsPipeline::build_rasterization_state(
500 const GraphicsPipelineConfig& config)
501{
502 vk::PipelineRasterizationStateCreateInfo rasterization;
503 rasterization.depthClampEnable = config.depth_clamp_enable;
504 rasterization.rasterizerDiscardEnable = config.rasterizer_discard_enable;
505 rasterization.polygonMode = config.polygon_mode;
506 rasterization.cullMode = config.cull_mode;
507 rasterization.frontFace = config.front_face;
508 rasterization.depthBiasEnable = config.depth_bias_enable;
509 rasterization.depthBiasConstantFactor = config.depth_bias_constant_factor;
510 rasterization.depthBiasClamp = config.depth_bias_clamp;
511 rasterization.depthBiasSlopeFactor = config.depth_bias_slope_factor;
512 rasterization.lineWidth = config.line_width;
513
514 return rasterization;
515}
516
517vk::PipelineMultisampleStateCreateInfo VKGraphicsPipeline::build_multisample_state(
518 const GraphicsPipelineConfig& config)
519{
520 vk::PipelineMultisampleStateCreateInfo multisample;
521 multisample.rasterizationSamples = config.rasterization_samples;
522 multisample.sampleShadingEnable = config.sample_shading_enable;
523 multisample.minSampleShading = config.min_sample_shading;
524 multisample.pSampleMask = config.sample_mask.empty() ? nullptr : config.sample_mask.data();
525 multisample.alphaToCoverageEnable = config.alpha_to_coverage_enable;
526 multisample.alphaToOneEnable = config.alpha_to_one_enable;
527
528 return multisample;
529}
530
531vk::PipelineDepthStencilStateCreateInfo VKGraphicsPipeline::build_depth_stencil_state(
532 const GraphicsPipelineConfig& config)
533{
534 vk::PipelineDepthStencilStateCreateInfo depth_stencil;
535 depth_stencil.depthTestEnable = config.depth_test_enable;
536 depth_stencil.depthWriteEnable = config.depth_write_enable;
537 depth_stencil.depthCompareOp = config.depth_compare_op;
538 depth_stencil.depthBoundsTestEnable = config.depth_bounds_test_enable;
539 depth_stencil.minDepthBounds = config.min_depth_bounds;
540 depth_stencil.maxDepthBounds = config.max_depth_bounds;
541 depth_stencil.stencilTestEnable = config.stencil_test_enable;
542 depth_stencil.front = config.front_stencil;
543 depth_stencil.back = config.back_stencil;
544
545 return depth_stencil;
546}
547
548vk::PipelineColorBlendStateCreateInfo VKGraphicsPipeline::build_color_blend_state(
549 const GraphicsPipelineConfig& config,
550 std::vector<vk::PipelineColorBlendAttachmentState>& attachments)
551{
552 for (const auto& config_attachment : config.color_blend_attachments) {
553 vk::PipelineColorBlendAttachmentState attachment;
554 attachment.blendEnable = config_attachment.blend_enable;
555 attachment.srcColorBlendFactor = config_attachment.src_color_blend_factor;
556 attachment.dstColorBlendFactor = config_attachment.dst_color_blend_factor;
557 attachment.colorBlendOp = config_attachment.color_blend_op;
558 attachment.srcAlphaBlendFactor = config_attachment.src_alpha_blend_factor;
559 attachment.dstAlphaBlendFactor = config_attachment.dst_alpha_blend_factor;
560 attachment.alphaBlendOp = config_attachment.alpha_blend_op;
561 attachment.colorWriteMask = config_attachment.color_write_mask;
562 attachments.push_back(attachment);
563 }
564
565 vk::PipelineColorBlendStateCreateInfo color_blend;
566 color_blend.logicOpEnable = config.logic_op_enable;
567 color_blend.logicOp = config.logic_op;
568 color_blend.attachmentCount = static_cast<uint32_t>(attachments.size());
569 color_blend.pAttachments = attachments.empty() ? nullptr : attachments.data();
570 color_blend.blendConstants[0] = config.blend_constants[0];
571 color_blend.blendConstants[1] = config.blend_constants[1];
572 color_blend.blendConstants[2] = config.blend_constants[2];
573 color_blend.blendConstants[3] = config.blend_constants[3];
574
575 return color_blend;
576}
577
578vk::PipelineDynamicStateCreateInfo VKGraphicsPipeline::build_dynamic_state(
579 const GraphicsPipelineConfig& config)
580{
581 vk::PipelineDynamicStateCreateInfo dynamic_state;
582 dynamic_state.dynamicStateCount = static_cast<uint32_t>(config.dynamic_states.size());
583 dynamic_state.pDynamicStates = config.dynamic_states.data();
584 return dynamic_state;
585}
586
587// ============================================================================
588// Pipeline Binding
589// ============================================================================
590
591void VKGraphicsPipeline::bind(vk::CommandBuffer cmd)
592{
593 if (!m_pipeline) {
595 "Cannot bind invalid graphics pipeline");
596 return;
597 }
598
599 cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, m_pipeline);
600}
601
603 vk::CommandBuffer cmd,
604 std::span<vk::DescriptorSet> sets,
605 uint32_t first_set)
606{
607 if (!m_layout) {
609 "Cannot bind descriptor sets without pipeline layout");
610 return;
611 }
612
613 if (sets.empty()) {
615 "Binding empty descriptor sets");
616 return;
617 }
618
619 cmd.bindDescriptorSets(
620 vk::PipelineBindPoint::eGraphics,
621 m_layout,
622 first_set,
623 static_cast<uint32_t>(sets.size()),
624 sets.data(),
625 0, nullptr);
626}
627
629 vk::CommandBuffer cmd,
630 vk::ShaderStageFlags stages,
631 uint32_t offset,
632 uint32_t size,
633 const void* data)
634{
635 if (!m_layout) {
637 "Cannot push constants without pipeline layout");
638 return;
639 }
640
641 if (!data) {
643 "Cannot push null data");
644 return;
645 }
646
647 cmd.pushConstants(m_layout, stages, offset, size, data);
648}
649
650// ============================================================================
651// Vertex/Index Buffer Binding
652// ============================================================================
653
655 vk::CommandBuffer cmd,
656 uint32_t first_binding,
657 std::span<vk::Buffer> buffers,
658 std::span<vk::DeviceSize> offsets)
659{
660 if (buffers.empty()) {
662 "Binding empty vertex buffers");
663 return;
664 }
665
666 if (buffers.size() != offsets.size()) {
668 "Buffer count ({}) does not match offset count ({})",
669 buffers.size(), offsets.size());
670 return;
671 }
672
673 cmd.bindVertexBuffers(
674 first_binding,
675 static_cast<uint32_t>(buffers.size()),
676 buffers.data(),
677 offsets.data());
678}
679
681 vk::CommandBuffer cmd,
682 vk::Buffer buffer,
683 vk::DeviceSize offset,
684 uint32_t binding)
685{
686 if (!buffer) {
688 "Cannot bind null vertex buffer");
689 return;
690 }
691
692 cmd.bindVertexBuffers(binding, 1, &buffer, &offset);
693}
694
696 vk::CommandBuffer cmd,
697 vk::Buffer buffer,
698 vk::DeviceSize offset,
699 vk::IndexType index_type)
700{
701 if (!buffer) {
703 "Cannot bind null index buffer");
704 return;
705 }
706
707 cmd.bindIndexBuffer(buffer, offset, index_type);
708}
709
710// ============================================================================
711// Dynamic State
712// ============================================================================
713
714void VKGraphicsPipeline::set_viewport(vk::CommandBuffer cmd, const vk::Viewport& viewport)
715{
716 cmd.setViewport(0, 1, &viewport);
717}
718
719void VKGraphicsPipeline::set_scissor(vk::CommandBuffer cmd, const vk::Rect2D& scissor)
720{
721 cmd.setScissor(0, 1, &scissor);
722}
723
724void VKGraphicsPipeline::set_line_width(vk::CommandBuffer cmd, float width)
725{
726 cmd.setLineWidth(width);
727}
728
730 vk::CommandBuffer cmd,
731 float constant_factor,
732 float clamp,
733 float slope_factor)
734{
735 cmd.setDepthBias(constant_factor, clamp, slope_factor);
736}
737
738void VKGraphicsPipeline::set_blend_constants(vk::CommandBuffer cmd, const float constants[4])
739{
740 cmd.setBlendConstants(constants);
741}
742
743// ============================================================================
744// Draw Commands
745// ============================================================================
746
748 vk::CommandBuffer cmd,
749 uint32_t vertex_count,
750 uint32_t instance_count,
751 uint32_t first_vertex,
752 uint32_t first_instance)
753{
754 if (!m_pipeline) {
756 "Cannot draw with invalid pipeline");
757 return;
758 }
759
760 if (vertex_count == 0) {
762 "Drawing with zero vertices");
763 return;
764 }
765
766 cmd.draw(vertex_count, instance_count, first_vertex, first_instance);
767}
768
770 vk::CommandBuffer cmd,
771 uint32_t index_count,
772 uint32_t instance_count,
773 uint32_t first_index,
774 int32_t vertex_offset,
775 uint32_t first_instance)
776{
777 if (!m_pipeline) {
779 "Cannot draw indexed with invalid pipeline");
780 return;
781 }
782
783 if (index_count == 0) {
785 "Drawing with zero indices");
786 return;
787 }
788
789 cmd.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance);
790}
791
793 vk::CommandBuffer cmd,
794 vk::Buffer buffer,
795 vk::DeviceSize offset,
796 uint32_t draw_count,
797 uint32_t stride)
798{
799 if (!m_pipeline) {
801 "Cannot draw indirect with invalid pipeline");
802 return;
803 }
804
805 if (!buffer) {
807 "Cannot draw indirect with null buffer");
808 return;
809 }
810
811 if (draw_count == 0) {
813 "Drawing with zero draw count");
814 return;
815 }
816
817 cmd.drawIndirect(buffer, offset, draw_count, stride);
818}
819
821 vk::CommandBuffer cmd,
822 vk::Buffer buffer,
823 vk::DeviceSize offset,
824 uint32_t draw_count,
825 uint32_t stride)
826{
827 if (!m_pipeline) {
829 "Cannot draw indexed indirect with invalid pipeline");
830 return;
831 }
832
833 if (!buffer) {
835 "Cannot draw indexed indirect with null buffer");
836 return;
837 }
838
839 if (draw_count == 0) {
841 "Drawing with zero draw count");
842 return;
843 }
844
845 cmd.drawIndexedIndirect(buffer, offset, draw_count, stride);
846}
847
848} // 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 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.
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 > fragment_shader
static GraphicsPipelineConfig alpha_blended()
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.