Create the graphics pipeline
This commit is contained in:
parent
34ca91f6f7
commit
f008f67921
1 changed files with 206 additions and 2 deletions
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue