Create the graphics pipeline

This commit is contained in:
Przemyslaw Gasinski 2024-06-30 23:43:19 +02:00
parent 34ca91f6f7
commit f008f67921

View file

@ -49,6 +49,11 @@ pub const VulkanRenderer = struct {
swapchain: vk.SwapchainKHR,
swapchain_images: []SwapchainImage,
// Pipeline
graphics_pipeline: vk.Pipeline,
pipeline_layout: vk.PipelineLayout,
render_pass: vk.RenderPass,
// Utilities
swapchain_image_format: vk.Format,
extent: vk.Extent2D,
@ -72,6 +77,7 @@ pub const VulkanRenderer = struct {
try self.getPhysicalDevice();
try self.createLogicalDevice();
try self.createSwapchain();
try self.createRenderPass();
try self.createGraphicsPipeline();
return self;
@ -250,6 +256,72 @@ pub const VulkanRenderer = struct {
}
}
fn createRenderPass(self: *Self) !void {
// Colour attachment of the render pass
const colour_attachment: vk.AttachmentDescription = .{
.format = self.swapchain_image_format, // Format to use for attachment
.samples = .{ .@"1_bit" = true }, // Number of samples to write for multisampling
.load_op = .clear, // Describes what to do with attachment before rendering
.store_op = .store, // Describes what to do with attachment after rendering
.stencil_load_op = .dont_care, // Describes what to do with stencil before rendering
.stencil_store_op = .dont_care, // Describes what to do with stencil after rendering
// Framebuffer data will be stored as an image, but images can be given different data layouts
// to give optimal use for certain operations
.initial_layout = vk.ImageLayout.undefined, // Image data layout before render pass starts
.final_layout = vk.ImageLayout.present_src_khr, // Image data layout after render pass (to change to)
};
// Attachment reference uses an attachment index that refers to index in the attachment list passed to render pass create info
const colour_attachment_reference: vk.AttachmentReference = .{
.attachment = 0,
.layout = vk.ImageLayout.color_attachment_optimal,
};
// Information about a particular subpass the render pass is using
const subpass: vk.SubpassDescription = .{
.pipeline_bind_point = .graphics, // Pipeline type subpass is to be bound to
.color_attachment_count = 1,
.p_color_attachments = @ptrCast(&colour_attachment_reference),
};
// Need to determine when layout transitions occur using subpass dependencies
const subpass_dependencies = [2]vk.SubpassDependency{
// Conversion from VK_IMAGE_LAYOUT_UNDEFINED to VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL
vk.SubpassDependency{
// Transition must happen after...
.src_subpass = vk.SUBPASS_EXTERNAL, // Subpass index (VK_SUBPASS_EXTERNAL = outside of renderpass)
.src_stage_mask = .{ .bottom_of_pipe_bit = true }, // Pipeline stage
.src_access_mask = .{ .memory_read_bit = true }, // Stage access mask (memory access)
// But must happen before...
.dst_subpass = 0,
.dst_stage_mask = .{ .color_attachment_output_bit = true },
.dst_access_mask = .{ .memory_read_bit = true, .memory_write_bit = true },
},
// Conversion from VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL to VK_IMAGE_LAYOUT_PRESENT_SRC_KHR
vk.SubpassDependency{
// Transition must happen after...
.src_subpass = 0,
.src_stage_mask = .{ .color_attachment_output_bit = true },
.src_access_mask = .{ .memory_read_bit = true, .memory_write_bit = true },
// But must happen before...
.dst_subpass = vk.SUBPASS_EXTERNAL,
.dst_stage_mask = .{ .bottom_of_pipe_bit = true },
.dst_access_mask = .{ .memory_read_bit = true },
},
};
const render_pass_create_info: vk.RenderPassCreateInfo = .{
.attachment_count = 1,
.p_attachments = @ptrCast(&colour_attachment),
.subpass_count = 1,
.p_subpasses = @ptrCast(&subpass),
.dependency_count = @intCast(subpass_dependencies.len),
.p_dependencies = &subpass_dependencies,
};
self.render_pass = try self.device.createRenderPass(&render_pass_create_info, null);
}
fn createGraphicsPipeline(self: *Self) !void {
// Create shader modules
const vert = try self.device.createShaderModule(&.{
@ -264,7 +336,7 @@ pub const VulkanRenderer = struct {
}, null);
defer self.device.destroyShaderModule(frag, null);
// -- Shader stage creation information
// -- Shader stage creation information --
// Vertex stage creation information
const vertex_shader_create_info: vk.PipelineShaderStageCreateInfo = .{
@ -284,7 +356,135 @@ pub const VulkanRenderer = struct {
vertex_shader_create_info,
fragment_shader_create_info,
};
_ = shader_create_infos;
// -- Vertex input (TODO: Put in vertex descriptions when resources created) --
const vertex_input_create_info: vk.PipelineVertexInputStateCreateInfo = .{
.p_vertex_binding_descriptions = null, // List of vertex binding descriptions (data spacing, stride info)
.p_vertex_attribute_descriptions = null, // List of vertex attribute descriptions (data format and where to bind to/from)
};
// -- Input assembly --
const assembly_create_info: vk.PipelineInputAssemblyStateCreateInfo = .{
.topology = .triangle_list, // Primitive type to assemble vertices as
.primitive_restart_enable = vk.FALSE, // Allow overrinding of strip topology to start new primitives
};
// -- Viewport & scissor --
const viewport: vk.Viewport = .{
.x = 0.0,
.y = 0.0,
.width = @floatFromInt(self.extent.width),
.height = @floatFromInt(self.extent.height),
.min_depth = 0.0,
.max_depth = 1.0,
};
const scissor: vk.Rect2D = .{
.offset = .{ .x = 0, .y = 0 },
.extent = self.extent,
};
const viewport_state_create_info: vk.PipelineViewportStateCreateInfo = .{
.viewport_count = 1,
.p_viewports = @ptrCast(&viewport),
.scissor_count = 1,
.p_scissors = @ptrCast(&scissor),
};
// -- Dynamic states --
// Dynamic states to enable (TODO: To investigate later)
const dynamic_states = [_]vk.DynamicState{ .viewport, .scissor };
const dynamic_state_create_info: vk.PipelineDynamicStateCreateInfo = .{
.dynamic_state_count = 2,
.p_dynamic_states = &dynamic_states,
};
// -- Rasterizer --
const rasterizer_create_info: vk.PipelineRasterizationStateCreateInfo = .{
.depth_clamp_enable = vk.FALSE, // Change if fragments beyond near/far planes are clipped (default) or clamped to planed
.rasterizer_discard_enable = vk.FALSE, // Whether to discard data and skip rasterizer (never creates fragments)
.polygon_mode = .fill, // How to handle filling points between vertices
.line_width = 1.0, // How thick the lines should be when drawn
.cull_mode = .{ .back_bit = true }, // Which face of a triangle to cull
.front_face = .clockwise, // Winding to determine which side is front
.depth_bias_enable = vk.FALSE, // Whether to add depth bias to fragments (good for stopping "shadow acne" in shadow mapping)
.depth_bias_constant_factor = 0,
.depth_bias_clamp = 0,
.depth_bias_slope_factor = 0,
};
// -- Multisampling --
const multisampling_create_info: vk.PipelineMultisampleStateCreateInfo = .{
.sample_shading_enable = vk.FALSE, // Enable multisample shading or not
.rasterization_samples = .{ .@"1_bit" = true }, // Number of samples to use per fragment
.min_sample_shading = 1,
.alpha_to_coverage_enable = vk.FALSE,
.alpha_to_one_enable = vk.FALSE,
};
// -- Blending --
// Blend attachment state (how blending is handled)
const colour_state: vk.PipelineColorBlendAttachmentState = .{
.color_write_mask = .{ .r_bit = true, .g_bit = true, .b_bit = true, .a_bit = true }, // Colours to apply blending to
.blend_enable = vk.TRUE, // Enable blending
.src_color_blend_factor = vk.BlendFactor.src_alpha,
.dst_color_blend_factor = vk.BlendFactor.one_minus_src_alpha,
.color_blend_op = vk.BlendOp.add,
// Summary: (VK_BLEND_FACTOR_SRC_ALPHA * new colour) + (VK_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA * old colour)
.src_alpha_blend_factor = vk.BlendFactor.one,
.dst_alpha_blend_factor = vk.BlendFactor.zero,
.alpha_blend_op = vk.BlendOp.add,
// Summary (1 * new alpha) + (0 * old alpha) = new alpha
};
// Blending uses equation: (srcColorBlendFactor * new colour) colorBlendOp (dstColorBlendFactor * old colour)
const colour_blending_create_info: vk.PipelineColorBlendStateCreateInfo = .{
.logic_op_enable = vk.FALSE, // Alternative to calculations is to use logical operations
.logic_op = .copy,
.attachment_count = 1,
.p_attachments = @ptrCast(&colour_state),
.blend_constants = [_]f32{ 0, 0, 0, 0 },
};
// -- Pipeline layout (TODO: Apply future descriptor set layouts) --
const pipeline_layout_create_info: vk.PipelineLayoutCreateInfo = .{};
self.pipeline_layout = try self.device.createPipelineLayout(&pipeline_layout_create_info, null);
// -- Depth stencil testing --
// TODO: Set a depth stencil testing
// -- Graphics pipeline creation --
const pipeline_create_info: vk.GraphicsPipelineCreateInfo = .{
.stage_count = 2, // Number of shader stages
.p_stages = &shader_create_infos, // List of shader stages
.p_vertex_input_state = &vertex_input_create_info,
.p_input_assembly_state = &assembly_create_info,
.p_viewport_state = &viewport_state_create_info,
.p_dynamic_state = &dynamic_state_create_info,
.p_rasterization_state = &rasterizer_create_info,
.p_multisample_state = &multisampling_create_info,
.p_color_blend_state = &colour_blending_create_info,
.p_depth_stencil_state = null,
.layout = self.pipeline_layout, // Pipeline layout the pipeline should use
.render_pass = self.render_pass, // Renderpass description the pipeline is compatible with
.subpass = 0, // Subpass of renderpass to use with pipeline
// Pipeline derivatives: can create multiple pipelines that derive from one another for optimisation
.base_pipeline_handle = .null_handle, // Existing pipeline to derive from...
.base_pipeline_index = -1, // Or index of pipeline being created to derive from (in case creating multiple at once)
};
_ = try self.device.createGraphicsPipelines(
.null_handle,
1,
@ptrCast(&pipeline_create_info),
null,
@ptrCast(&self.graphics_pipeline),
);
}
fn getPhysicalDevice(self: *Self) !void {
@ -503,6 +703,10 @@ pub const VulkanRenderer = struct {
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
}
self.device.destroyPipeline(self.graphics_pipeline, null);
self.device.destroyPipelineLayout(self.pipeline_layout, null);
self.device.destroyRenderPass(self.render_pass, null);
for (self.swapchain_images) |swapchain_image| {
self.device.destroyImageView(swapchain_image.image_view, null);
}