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 pipeline_info.pViewportState = &viewport_state;
263 pipeline_info.pRasterizationState = &rasterization_state;
264 pipeline_info.pMultisampleState = &multisample_state;
265 pipeline_info.pDepthStencilState = &depth_stencil_state;
266 pipeline_info.pColorBlendState = &color_blend_state;
267 pipeline_info.pDynamicState = config.
dynamic_states.empty() ? nullptr : &dynamic_state;
270 pipeline_info.subpass = config.
subpass;
271 pipeline_info.basePipelineHandle =
nullptr;
272 pipeline_info.basePipelineIndex = -1;
275 auto result = device.createGraphicsPipeline(config.
cache, pipeline_info);
276 if (result.result != vk::Result::eSuccess) {
278 "Failed to create graphics pipeline: {}", vk::to_string(result.result));
279 device.destroyPipelineLayout(
m_layout);
284 }
catch (
const vk::SystemError& e) {
286 "Failed to create graphics pipeline: {}", e.what());
287 device.destroyPipelineLayout(
m_layout);
293 "Graphics pipeline created ({} shader stages)", shader_stages.size());
302 vk::PipelineLayoutCreateInfo layout_info;
308 vk::PipelineLayout layout;
310 layout = device.createPipelineLayout(layout_info);
311 }
catch (
const vk::SystemError& e) {
313 "Failed to create pipeline layout: {}", e.what());
318 "Graphics pipeline layout created ({} sets, {} push constant ranges)",
328 "Graphics pipeline requires a vertex shader");
334 "Vertex shader is not valid");
338 if (config.
vertex_shader->get_stage() != vk::ShaderStageFlagBits::eVertex) {
340 "Vertex shader has wrong stage: {}", vk::to_string(config.
vertex_shader->get_stage()));
346 "Fragment shader has wrong stage: {}", vk::to_string(config.
fragment_shader->get_stage()));
352 "Geometry shader has wrong stage");
358 "Tessellation control shader has wrong stage");
364 "Tessellation evaluation shader has wrong stage");
377 std::vector<vk::VertexInputBindingDescription>& bindings,
378 std::vector<vk::VertexInputAttributeDescription>& attributes)
382 "Using explicit vertex bindings/attributes from config "
383 "({} bindings, {} attributes)",
387 vk::VertexInputBindingDescription vk_binding;
388 vk_binding.binding = binding.binding;
389 vk_binding.stride = binding.stride;
390 vk_binding.inputRate = binding.input_rate;
391 bindings.push_back(vk_binding);
395 vk::VertexInputAttributeDescription vk_attr;
396 vk_attr.location = attribute.location;
397 vk_attr.binding = attribute.binding;
398 vk_attr.format = attribute.format;
399 vk_attr.offset = attribute.offset;
400 attributes.push_back(vk_attr);
408 "Using vertex input from shader reflection");
409 const auto& vertex_input = config.
vertex_shader->get_vertex_input();
411 for (
const auto& binding : vertex_input.bindings) {
412 vk::VertexInputBindingDescription vk_binding;
413 vk_binding.binding = binding.binding;
414 vk_binding.stride = binding.stride;
415 vk_binding.inputRate = binding.rate;
416 bindings.push_back(vk_binding);
419 for (
const auto& attribute : vertex_input.attributes) {
420 vk::VertexInputAttributeDescription vk_attr;
421 vk_attr.location = attribute.location;
423 vk_attr.format = attribute.format;
424 vk_attr.offset = attribute.offset;
425 attributes.push_back(vk_attr);
429 "No vertex input: using empty vertex state (full-screen quad or compute)");
432 vk::PipelineVertexInputStateCreateInfo vertex_input;
433 vertex_input.vertexBindingDescriptionCount =
static_cast<uint32_t
>(bindings.size());
434 vertex_input.pVertexBindingDescriptions = bindings.empty() ? nullptr : bindings.data();
435 vertex_input.vertexAttributeDescriptionCount =
static_cast<uint32_t
>(attributes.size());
436 vertex_input.pVertexAttributeDescriptions = attributes.empty() ? nullptr : attributes.data();
444 vk::PipelineInputAssemblyStateCreateInfo input_assembly;
445 input_assembly.topology = config.
topology;
447 return input_assembly;
453 vk::PipelineTessellationStateCreateInfo tessellation;
460 std::vector<vk::Viewport>& viewports,
461 std::vector<vk::Rect2D>& scissors)
466 viewports.emplace_back();
472 scissors.emplace_back();
475 vk::PipelineViewportStateCreateInfo viewport_state;
476 viewport_state.viewportCount =
static_cast<uint32_t
>(viewports.size());
477 viewport_state.pViewports = config.
dynamic_viewport ? nullptr : viewports.data();
478 viewport_state.scissorCount =
static_cast<uint32_t
>(scissors.size());
479 viewport_state.pScissors = config.
dynamic_scissor ? nullptr : scissors.data();
481 return viewport_state;
487 vk::PipelineRasterizationStateCreateInfo rasterization;
491 rasterization.cullMode = config.
cull_mode;
499 return rasterization;
505 vk::PipelineMultisampleStateCreateInfo multisample;
519 vk::PipelineDepthStencilStateCreateInfo depth_stencil;
530 return depth_stencil;
535 std::vector<vk::PipelineColorBlendAttachmentState>& attachments)
538 vk::PipelineColorBlendAttachmentState attachment;
539 attachment.blendEnable = config_attachment.blend_enable;
540 attachment.srcColorBlendFactor = config_attachment.src_color_blend_factor;
541 attachment.dstColorBlendFactor = config_attachment.dst_color_blend_factor;
542 attachment.colorBlendOp = config_attachment.color_blend_op;
543 attachment.srcAlphaBlendFactor = config_attachment.src_alpha_blend_factor;
544 attachment.dstAlphaBlendFactor = config_attachment.dst_alpha_blend_factor;
545 attachment.alphaBlendOp = config_attachment.alpha_blend_op;
546 attachment.colorWriteMask = config_attachment.color_write_mask;
547 attachments.push_back(attachment);
550 vk::PipelineColorBlendStateCreateInfo color_blend;
552 color_blend.logicOp = config.
logic_op;
553 color_blend.attachmentCount =
static_cast<uint32_t
>(attachments.size());
554 color_blend.pAttachments = attachments.empty() ? nullptr : attachments.data();
566 vk::PipelineDynamicStateCreateInfo dynamic_state;
567 dynamic_state.dynamicStateCount =
static_cast<uint32_t
>(config.
dynamic_states.size());
569 return dynamic_state;
580 "Cannot bind invalid graphics pipeline");
584 cmd.bindPipeline(vk::PipelineBindPoint::eGraphics,
m_pipeline);
588 vk::CommandBuffer cmd,
589 std::span<vk::DescriptorSet> sets,
594 "Cannot bind descriptor sets without pipeline layout");
600 "Binding empty descriptor sets");
604 cmd.bindDescriptorSets(
605 vk::PipelineBindPoint::eGraphics,
608 static_cast<uint32_t
>(sets.size()),
614 vk::CommandBuffer cmd,
615 vk::ShaderStageFlags stages,
622 "Cannot push constants without pipeline layout");
628 "Cannot push null data");
632 cmd.pushConstants(
m_layout, stages, offset, size, data);
640 vk::CommandBuffer cmd,
641 uint32_t first_binding,
642 std::span<vk::Buffer> buffers,
643 std::span<vk::DeviceSize> offsets)
645 if (buffers.empty()) {
647 "Binding empty vertex buffers");
651 if (buffers.size() != offsets.size()) {
653 "Buffer count ({}) does not match offset count ({})",
654 buffers.size(), offsets.size());
658 cmd.bindVertexBuffers(
660 static_cast<uint32_t
>(buffers.size()),
666 vk::CommandBuffer cmd,
668 vk::DeviceSize offset,
673 "Cannot bind null vertex buffer");
677 cmd.bindVertexBuffers(binding, 1, &buffer, &offset);
681 vk::CommandBuffer cmd,
683 vk::DeviceSize offset,
684 vk::IndexType index_type)
688 "Cannot bind null index buffer");
692 cmd.bindIndexBuffer(buffer, offset, index_type);
701 cmd.setViewport(0, 1, &viewport);
706 cmd.setScissor(0, 1, &scissor);
711 cmd.setLineWidth(width);
715 vk::CommandBuffer cmd,
716 float constant_factor,
720 cmd.setDepthBias(constant_factor, clamp, slope_factor);
725 cmd.setBlendConstants(constants);
733 vk::CommandBuffer cmd,
734 uint32_t vertex_count,
735 uint32_t instance_count,
736 uint32_t first_vertex,
737 uint32_t first_instance)
741 "Cannot draw with invalid pipeline");
745 if (vertex_count == 0) {
747 "Drawing with zero vertices");
751 cmd.draw(vertex_count, instance_count, first_vertex, first_instance);
755 vk::CommandBuffer cmd,
756 uint32_t index_count,
757 uint32_t instance_count,
758 uint32_t first_index,
759 int32_t vertex_offset,
760 uint32_t first_instance)
764 "Cannot draw indexed with invalid pipeline");
768 if (index_count == 0) {
770 "Drawing with zero indices");
774 cmd.drawIndexed(index_count, instance_count, first_index, vertex_offset, first_instance);
778 vk::CommandBuffer cmd,
780 vk::DeviceSize offset,
786 "Cannot draw indirect with invalid pipeline");
792 "Cannot draw indirect with null buffer");
796 if (draw_count == 0) {
798 "Drawing with zero draw count");
802 cmd.drawIndirect(buffer, offset, draw_count, stride);
806 vk::CommandBuffer cmd,
808 vk::DeviceSize offset,
814 "Cannot draw indexed indirect with invalid pipeline");
820 "Cannot draw indexed indirect with null buffer");
824 if (draw_count == 0) {
826 "Drawing with zero draw count");
830 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
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::RenderPass render_pass
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
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
std::shared_ptr< VKShaderModule > tess_evaluation_shader
std::shared_ptr< VKShaderModule > tess_control_shader
Configuration for creating a graphics pipeline.