13 config.
topology = vk::PrimitiveTopology::eTriangleList;
15 config.
cull_mode = vk::CullModeFlagBits::eBack;
16 config.
front_face = vk::FrontFace::eCounterClockwise;
31 config.
topology = vk::PrimitiveTopology::eTriangleList;
33 config.
cull_mode = vk::CullModeFlagBits::eNone;
61 config.
topology = vk::PrimitiveTopology::eTriangleList;
63 config.
cull_mode = vk::CullModeFlagBits::eBack;
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;
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;
139 "VKGraphicsPipeline destroyed without cleanup() - potential leak");
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))
149 other.m_device =
nullptr;
150 other.m_pipeline =
nullptr;
151 other.m_layout =
nullptr;
156 if (
this != &other) {
157 if (m_pipeline || m_layout) {
159 "VKGraphicsPipeline move-assigned without cleanup() - potential leak");
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);
167 other.m_device =
nullptr;
168 other.m_pipeline =
nullptr;
169 other.m_layout =
nullptr;
180 "Graphics pipeline destroyed");
184 device.destroyPipelineLayout(
m_layout);
187 "Graphics pipeline layout destroyed");
199 "Cannot create graphics pipeline with null device");
208 "Shader validation failed");
215 "Failed to create pipeline layout");
219 std::vector<vk::PipelineShaderStageCreateInfo> shader_stages;
221 shader_stages.push_back(config.
mesh_shader->get_stage_create_info());
223 shader_stages.push_back(config.
task_shader->get_stage_create_info());
227 shader_stages.push_back(config.
vertex_shader->get_stage_create_info());
229 shader_stages.push_back(config.
fragment_shader->get_stage_create_info());
231 shader_stages.push_back(config.
geometry_shader->get_stage_create_info());
238 std::vector<vk::VertexInputBindingDescription> vertex_bindings;
239 std::vector<vk::VertexInputAttributeDescription> vertex_attributes;
245 std::vector<vk::Viewport> viewports;
246 std::vector<vk::Rect2D> scissors;
253 std::vector<vk::PipelineColorBlendAttachmentState> blend_attachments;
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;
264 ? &tessellation_state
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;
274 pipeline_info.basePipelineHandle =
nullptr;
275 pipeline_info.basePipelineIndex = -1;
277 vk::PipelineRenderingCreateInfo rendering_create_info;
283 pipeline_info.pNext = &rendering_create_info;
284 pipeline_info.renderPass =
nullptr;
285 pipeline_info.subpass = 0;
288 "Creating pipeline for dynamic rendering ({} color attachments)",
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);
301 }
catch (
const vk::SystemError& e) {
303 "Failed to create graphics pipeline: {}", e.what());
304 device.destroyPipelineLayout(
m_layout);
310 "Graphics pipeline created ({} shader stages)", shader_stages.size());
319 vk::PipelineLayoutCreateInfo layout_info;
325 vk::PipelineLayout layout;
327 layout = device.createPipelineLayout(layout_info);
328 }
catch (
const vk::SystemError& e) {
330 "Failed to create pipeline layout: {}", e.what());
335 "Graphics pipeline layout created ({} sets, {} push constant ranges)",
343 const bool has_mesh = config.
mesh_shader !=
nullptr;
346 if (config.
mesh_shader->get_stage() != vk::ShaderStageFlagBits::eMeshEXT) {
348 "Mesh shader must have stage eMeshEXT");
353 "Task shader must have stage eTaskEXT");
359 "Mesh shading pipeline cannot contain vertex/geometry/tessellation shaders");
367 "Graphics pipeline requires a vertex shader");
373 "Vertex shader is not valid");
377 if (config.
vertex_shader->get_stage() != vk::ShaderStageFlagBits::eVertex) {
379 "Vertex shader has wrong stage: {}", vk::to_string(config.
vertex_shader->get_stage()));
385 "Fragment shader has wrong stage: {}", vk::to_string(config.
fragment_shader->get_stage()));
391 "Geometry shader has wrong stage");
397 "Tessellation control shader has wrong stage");
403 "Tessellation evaluation shader has wrong stage");
416 std::vector<vk::VertexInputBindingDescription>& bindings,
417 std::vector<vk::VertexInputAttributeDescription>& attributes)
421 "Using explicit vertex bindings/attributes from config "
422 "({} bindings, {} attributes)",
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);
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);
447 "Using vertex input from shader reflection");
448 const auto& vertex_input = config.
vertex_shader->get_vertex_input();
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);
458 for (
const auto& attribute : vertex_input.attributes) {
459 vk::VertexInputAttributeDescription vk_attr;
460 vk_attr.location = attribute.location;
462 vk_attr.format = attribute.format;
463 vk_attr.offset = attribute.offset;
464 attributes.push_back(vk_attr);
468 "No vertex input: using empty vertex state (full-screen quad or compute)");
471 vk::PipelineVertexInputStateCreateInfo vertex_input;
474 "Mesh shading pipeline — vertex input ignored");
475 vertex_input.vertexBindingDescriptionCount = 0;
476 vertex_input.vertexAttributeDescriptionCount = 0;
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();
491 vk::PipelineInputAssemblyStateCreateInfo input_assembly;
492 input_assembly.topology = config.
topology;
494 return input_assembly;
500 vk::PipelineTessellationStateCreateInfo tessellation;
507 std::vector<vk::Viewport>& viewports,
508 std::vector<vk::Rect2D>& scissors)
513 viewports.emplace_back();
519 scissors.emplace_back();
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();
528 return viewport_state;
534 vk::PipelineRasterizationStateCreateInfo rasterization;
538 rasterization.cullMode = config.
cull_mode;
546 return rasterization;
552 vk::PipelineMultisampleStateCreateInfo multisample;
566 vk::PipelineDepthStencilStateCreateInfo depth_stencil;
577 return depth_stencil;
582 std::vector<vk::PipelineColorBlendAttachmentState>& 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);
597 vk::PipelineColorBlendStateCreateInfo color_blend;
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();
613 vk::PipelineDynamicStateCreateInfo dynamic_state;
614 dynamic_state.dynamicStateCount =
static_cast<uint32_t
>(config.
dynamic_states.size());
616 return dynamic_state;
627 "Cannot bind invalid graphics pipeline");
631 cmd.bindPipeline(vk::PipelineBindPoint::eGraphics,
m_pipeline);
635 vk::CommandBuffer cmd,
636 std::span<vk::DescriptorSet> sets,
641 "Cannot bind descriptor sets without pipeline layout");
647 "Binding empty descriptor sets");
651 cmd.bindDescriptorSets(
652 vk::PipelineBindPoint::eGraphics,
655 static_cast<uint32_t
>(sets.size()),
661 vk::CommandBuffer cmd,
662 vk::ShaderStageFlags stages,
669 "Cannot push constants without pipeline layout");
675 "Cannot push null data");
679 cmd.pushConstants(
m_layout, stages, offset, size, data);
687 vk::CommandBuffer cmd,
688 uint32_t first_binding,
689 std::span<vk::Buffer> buffers,
690 std::span<vk::DeviceSize> offsets)
692 if (buffers.empty()) {
694 "Binding empty vertex buffers");
698 if (buffers.size() != offsets.size()) {
700 "Buffer count ({}) does not match offset count ({})",
701 buffers.size(), offsets.size());
705 cmd.bindVertexBuffers(
707 static_cast<uint32_t
>(buffers.size()),
713 vk::CommandBuffer cmd,
715 vk::DeviceSize offset,
720 "Cannot bind null vertex buffer");
724 cmd.bindVertexBuffers(binding, 1, &buffer, &offset);
728 vk::CommandBuffer cmd,
730 vk::DeviceSize offset,
731 vk::IndexType index_type)
735 "Cannot bind null index buffer");
739 cmd.bindIndexBuffer(buffer, offset, index_type);
748 cmd.setViewport(0, 1, &viewport);
753 cmd.setScissor(0, 1, &scissor);
758 cmd.setLineWidth(width);
762 vk::CommandBuffer cmd,
763 float constant_factor,
767 cmd.setDepthBias(constant_factor, clamp, slope_factor);
772 cmd.setBlendConstants(constants);
780 vk::CommandBuffer cmd,
781 uint32_t vertex_count,
782 uint32_t instance_count,
783 uint32_t first_vertex,
784 uint32_t first_instance)
788 "Cannot draw with invalid pipeline");
792 if (vertex_count == 0) {
794 "Drawing with zero vertices");
798 cmd.draw(vertex_count, instance_count, first_vertex, first_instance);
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)
811 "Cannot draw indexed with invalid pipeline");
815 if (index_count == 0) {
817 "Drawing with zero indices");
821 cmd.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance);
825 vk::CommandBuffer cmd,
827 vk::DeviceSize offset,
833 "Cannot draw indirect with invalid pipeline");
839 "Cannot draw indirect with null buffer");
843 if (draw_count == 0) {
845 "Drawing with zero draw count");
849 cmd.drawIndirect(buffer, offset, draw_count, stride);
853 vk::CommandBuffer cmd,
855 vk::DeviceSize offset,
861 "Cannot draw indexed indirect with invalid pipeline");
867 "Cannot draw indexed indirect with null buffer");
871 if (draw_count == 0) {
873 "Drawing with zero draw count");
877 cmd.drawIndexedIndirect(buffer, offset, draw_count, stride);
881 vk::CommandBuffer cmd,
882 uint32_t groupCountX,
883 uint32_t groupCountY,
884 uint32_t groupCountZ)
888 "Cannot draw mesh tasks with invalid pipeline");
891 cmd.drawMeshTasksEXT(groupCountX, groupCountY, groupCountZ);
895 vk::CommandBuffer cmd,
897 vk::DeviceSize offset,
903 "Cannot draw mesh tasks indirect with invalid pipeline");
906 cmd.drawMeshTasksIndirectEXT(buffer, offset, drawCount, stride);
#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.
GraphicsPipelineConfig m_config
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.
vk::PipelineLayout m_layout
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.
VKGraphicsPipeline()=default
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
bool use_vertex_shader_reflection
vk::StencilOpState front_stencil
float depth_bias_slope_factor
vk::StencilOpState back_stencil
vk::SampleCountFlagBits rasterization_samples
vk::CompareOp depth_compare_op
std::vector< vk::Format > color_attachment_formats
float depth_bias_constant_factor
static GraphicsPipelineConfig default_3d()
vk::PrimitiveTopology topology
std::shared_ptr< VKShaderModule > task_shader
std::shared_ptr< VKShaderModule > fragment_shader
vk::Rect2D static_scissor
void enable_back_face_culling()
uint32_t patch_control_points
static GraphicsPipelineConfig alpha_blended()
bool depth_bounds_test_enable
std::shared_ptr< VKShaderModule > mesh_shader
std::vector< vk::DynamicState > dynamic_states
vk::Viewport static_viewport
std::vector< vk::PushConstantRange > push_constant_ranges
void disable_depth_test()
void enable_additive_blending()
static GraphicsPipelineConfig additive_blended()
vk::PolygonMode polygon_mode
std::array< float, 4 > blend_constants
void enable_alpha_blending()
std::shared_ptr< VKShaderModule > vertex_shader
vk::Format stencil_attachment_format
std::vector< vk::DescriptorSetLayout > descriptor_set_layouts
std::vector< vk::SampleMask > sample_mask
bool rasterizer_discard_enable
bool alpha_to_coverage_enable
std::vector< VertexBinding > vertex_bindings
bool primitive_restart_enable
static GraphicsPipelineConfig depth_only()
vk::CullModeFlags cull_mode
std::vector< VertexAttribute > vertex_attributes
static GraphicsPipelineConfig default_2d()
std::shared_ptr< VKShaderModule > geometry_shader
bool sample_shading_enable
vk::Format depth_attachment_format
std::shared_ptr< VKShaderModule > tess_evaluation_shader
std::shared_ptr< VKShaderModule > tess_control_shader
Configuration for creating a graphics pipeline.