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.
vertex_shader->get_stage_create_info());
224 shader_stages.push_back(config.
fragment_shader->get_stage_create_info());
227 shader_stages.push_back(config.
geometry_shader->get_stage_create_info());
236 std::vector<vk::VertexInputBindingDescription> vertex_bindings;
237 std::vector<vk::VertexInputAttributeDescription> vertex_attributes;
243 std::vector<vk::Viewport> viewports;
244 std::vector<vk::Rect2D> scissors;
251 std::vector<vk::PipelineColorBlendAttachmentState> blend_attachments;
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;
262 ? &tessellation_state
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;
272 pipeline_info.basePipelineHandle =
nullptr;
273 pipeline_info.basePipelineIndex = -1;
275 vk::PipelineRenderingCreateInfo rendering_create_info;
281 pipeline_info.pNext = &rendering_create_info;
282 pipeline_info.renderPass =
nullptr;
283 pipeline_info.subpass = 0;
286 "Creating pipeline for dynamic rendering ({} color attachments)",
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);
299 }
catch (
const vk::SystemError& e) {
301 "Failed to create graphics pipeline: {}", e.what());
302 device.destroyPipelineLayout(
m_layout);
308 "Graphics pipeline created ({} shader stages)", shader_stages.size());
317 vk::PipelineLayoutCreateInfo layout_info;
323 vk::PipelineLayout layout;
325 layout = device.createPipelineLayout(layout_info);
326 }
catch (
const vk::SystemError& e) {
328 "Failed to create pipeline layout: {}", e.what());
333 "Graphics pipeline layout created ({} sets, {} push constant ranges)",
343 "Graphics pipeline requires a vertex shader");
349 "Vertex shader is not valid");
353 if (config.
vertex_shader->get_stage() != vk::ShaderStageFlagBits::eVertex) {
355 "Vertex shader has wrong stage: {}", vk::to_string(config.
vertex_shader->get_stage()));
361 "Fragment shader has wrong stage: {}", vk::to_string(config.
fragment_shader->get_stage()));
367 "Geometry shader has wrong stage");
373 "Tessellation control shader has wrong stage");
379 "Tessellation evaluation shader has wrong stage");
392 std::vector<vk::VertexInputBindingDescription>& bindings,
393 std::vector<vk::VertexInputAttributeDescription>& attributes)
397 "Using explicit vertex bindings/attributes from config "
398 "({} bindings, {} attributes)",
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);
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);
423 "Using vertex input from shader reflection");
424 const auto& vertex_input = config.
vertex_shader->get_vertex_input();
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);
434 for (
const auto& attribute : vertex_input.attributes) {
435 vk::VertexInputAttributeDescription vk_attr;
436 vk_attr.location = attribute.location;
438 vk_attr.format = attribute.format;
439 vk_attr.offset = attribute.offset;
440 attributes.push_back(vk_attr);
444 "No vertex input: using empty vertex state (full-screen quad or compute)");
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();
459 vk::PipelineInputAssemblyStateCreateInfo input_assembly;
460 input_assembly.topology = config.
topology;
462 return input_assembly;
468 vk::PipelineTessellationStateCreateInfo tessellation;
475 std::vector<vk::Viewport>& viewports,
476 std::vector<vk::Rect2D>& scissors)
481 viewports.emplace_back();
487 scissors.emplace_back();
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();
496 return viewport_state;
502 vk::PipelineRasterizationStateCreateInfo rasterization;
506 rasterization.cullMode = config.
cull_mode;
514 return rasterization;
520 vk::PipelineMultisampleStateCreateInfo multisample;
534 vk::PipelineDepthStencilStateCreateInfo depth_stencil;
545 return depth_stencil;
550 std::vector<vk::PipelineColorBlendAttachmentState>& 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);
565 vk::PipelineColorBlendStateCreateInfo color_blend;
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();
581 vk::PipelineDynamicStateCreateInfo dynamic_state;
582 dynamic_state.dynamicStateCount =
static_cast<uint32_t
>(config.
dynamic_states.size());
584 return dynamic_state;
595 "Cannot bind invalid graphics pipeline");
599 cmd.bindPipeline(vk::PipelineBindPoint::eGraphics,
m_pipeline);
603 vk::CommandBuffer cmd,
604 std::span<vk::DescriptorSet> sets,
609 "Cannot bind descriptor sets without pipeline layout");
615 "Binding empty descriptor sets");
619 cmd.bindDescriptorSets(
620 vk::PipelineBindPoint::eGraphics,
623 static_cast<uint32_t
>(sets.size()),
629 vk::CommandBuffer cmd,
630 vk::ShaderStageFlags stages,
637 "Cannot push constants without pipeline layout");
643 "Cannot push null data");
647 cmd.pushConstants(
m_layout, stages, offset, size, data);
655 vk::CommandBuffer cmd,
656 uint32_t first_binding,
657 std::span<vk::Buffer> buffers,
658 std::span<vk::DeviceSize> offsets)
660 if (buffers.empty()) {
662 "Binding empty vertex buffers");
666 if (buffers.size() != offsets.size()) {
668 "Buffer count ({}) does not match offset count ({})",
669 buffers.size(), offsets.size());
673 cmd.bindVertexBuffers(
675 static_cast<uint32_t
>(buffers.size()),
681 vk::CommandBuffer cmd,
683 vk::DeviceSize offset,
688 "Cannot bind null vertex buffer");
692 cmd.bindVertexBuffers(binding, 1, &buffer, &offset);
696 vk::CommandBuffer cmd,
698 vk::DeviceSize offset,
699 vk::IndexType index_type)
703 "Cannot bind null index buffer");
707 cmd.bindIndexBuffer(buffer, offset, index_type);
716 cmd.setViewport(0, 1, &viewport);
721 cmd.setScissor(0, 1, &scissor);
726 cmd.setLineWidth(width);
730 vk::CommandBuffer cmd,
731 float constant_factor,
735 cmd.setDepthBias(constant_factor, clamp, slope_factor);
740 cmd.setBlendConstants(constants);
748 vk::CommandBuffer cmd,
749 uint32_t vertex_count,
750 uint32_t instance_count,
751 uint32_t first_vertex,
752 uint32_t first_instance)
756 "Cannot draw with invalid pipeline");
760 if (vertex_count == 0) {
762 "Drawing with zero vertices");
766 cmd.draw(vertex_count, instance_count, first_vertex, first_instance);
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)
779 "Cannot draw indexed with invalid pipeline");
783 if (index_count == 0) {
785 "Drawing with zero indices");
789 cmd.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance);
793 vk::CommandBuffer cmd,
795 vk::DeviceSize offset,
801 "Cannot draw indirect with invalid pipeline");
807 "Cannot draw indirect with null buffer");
811 if (draw_count == 0) {
813 "Drawing with zero draw count");
817 cmd.drawIndirect(buffer, offset, draw_count, stride);
821 vk::CommandBuffer cmd,
823 vk::DeviceSize offset,
829 "Cannot draw indexed indirect with invalid pipeline");
835 "Cannot draw indexed indirect with null buffer");
839 if (draw_count == 0) {
841 "Drawing with zero draw count");
845 cmd.drawIndexedIndirect(buffer, offset, draw_count, 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 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.
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 > 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::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.