diff --git a/src/main.zig b/src/main.zig index 8bcc287..7bc7b92 100644 --- a/src/main.zig +++ b/src/main.zig @@ -11,6 +11,7 @@ const framebuffers_mod = @import("vulkan/framebuffers.zig"); const render_pass_mod = @import("vulkan/render_pass.zig"); const swapchain_mod = @import("vulkan/swapchain.zig"); const sync_mod = @import("vulkan/sync.zig"); +const pipeline_mod = @import("vulkan/pipeline.zig"); // The build script compiles these GLSL files to SPIR-V and exposes them as // anonymous imports. They are currently only loaded and printed; the program @@ -85,6 +86,9 @@ pub fn main() !void { var render_pass_context = try render_pass_mod.initRenderPass(ldc, swapchain_context.format.format); defer render_pass_context.destroy(&ldc); + var pipeline_context = try pipeline_mod.initPipelineContext(ldc, swapchain_context.extent, render_pass_context.render_pass, square_vert_spv, square_frag_spv, std.heap.page_allocator); + defer pipeline_context.destroy(&ldc); + var framebuffer_context = try framebuffers_mod.initFramebuffers( ldc, render_pass_context, @@ -97,6 +101,7 @@ pub fn main() !void { ldc, render_pass_context, framebuffer_context, + pipeline_context, swapchain_context, std.heap.page_allocator, ); @@ -123,6 +128,7 @@ pub fn main() !void { &swapchain_context, &render_pass_context, &framebuffer_context, + &pipeline_context, &command_context, std.heap.page_allocator, ); @@ -140,6 +146,7 @@ fn recreateSwapchain( swapchain_context: *swapchain_mod.SwapchainContext, render_pass_context: *render_pass_mod.RenderPassContext, framebuffer_context: *framebuffers_mod.FramebufferContext, + pipeline_context: *pipeline_mod.PipelineContext, command_context: *commands_mod.CommandContext, allocator: std.mem.Allocator, ) !void { @@ -171,6 +178,9 @@ fn recreateSwapchain( const new_render_pass_context = try render_pass_mod.initRenderPass(ldc, new_swapchain_context.format.format); errdefer new_render_pass_context.destroy(&ldc); + const new_pipeline_context = try pipeline_mod.initPipelineContext(ldc, swapchain_context.extent, render_pass_context.render_pass, square_vert_spv, square_frag_spv, std.heap.page_allocator); + errdefer pipeline_context.destroy(&ldc); + const new_framebuffer_context = try framebuffers_mod.initFramebuffers( ldc, new_render_pass_context, @@ -183,6 +193,7 @@ fn recreateSwapchain( ldc, new_render_pass_context, new_framebuffer_context, + new_pipeline_context, new_swapchain_context, allocator, ); @@ -193,11 +204,13 @@ fn recreateSwapchain( // swapchain/image views/images. command_context.destroy(&ldc); framebuffer_context.destroy(&ldc); + pipeline_context.destroy(&ldc); render_pass_context.destroy(&ldc); swapchain_context.destroy(&ldc); swapchain_context.* = new_swapchain_context; render_pass_context.* = new_render_pass_context; + pipeline_context.* = new_pipeline_context; framebuffer_context.* = new_framebuffer_context; command_context.* = new_command_context; diff --git a/src/vulkan/commands.zig b/src/vulkan/commands.zig index ff5afc8..7fb3ee4 100644 --- a/src/vulkan/commands.zig +++ b/src/vulkan/commands.zig @@ -5,6 +5,7 @@ const device = @import("device.zig"); const framebuffers = @import("framebuffers.zig"); const render_pass = @import("render_pass.zig"); const swapchain = @import("swapchain.zig"); +const pipeline = @import("pipeline.zig"); pub const CommandContext = struct { command_pool: vk.CommandPool, @@ -21,6 +22,7 @@ pub fn initCommandBuffers( ldc: device.LogicalDeviceContext, render_pass_context: render_pass.RenderPassContext, framebuffer_context: framebuffers.FramebufferContext, + pipeline_context: pipeline.PipelineContext, swapchain_context: swapchain.SwapchainContext, allocator: std.mem.Allocator, ) !CommandContext { @@ -86,6 +88,14 @@ pub fn initCommandBuffers( .@"inline", ); + ldc.vkd.cmdBindPipeline( + command_buffer, + .graphics, + pipeline_context.graphics_pipeline, + ); + + ldc.vkd.cmdDraw(command_buffer, 3, 1, 0, 0); + ldc.vkd.cmdEndRenderPass(command_buffer); try ldc.vkd.endCommandBuffer(command_buffer); diff --git a/src/vulkan/pipeline.zig b/src/vulkan/pipeline.zig index b2e47bb..48dda26 100644 --- a/src/vulkan/pipeline.zig +++ b/src/vulkan/pipeline.zig @@ -1,14 +1,183 @@ +const std = @import("std"); const vk = @import("vulkan"); const device = @import("device.zig"); -pub fn createShaderModule(ldc: device.LogicalDeviceContext, spv: []const u8) !vk.ShaderModule { +pub const PipelineContext = struct { + graphics_pipeline: vk.Pipeline, + pipeline_layout: vk.PipelineLayout, + + pub fn destroy(self: *const PipelineContext, ldc: *const device.LogicalDeviceContext) void { + ldc.vkd.destroyPipeline(ldc.device, self.graphics_pipeline, null); + ldc.vkd.destroyPipelineLayout(ldc.device, self.pipeline_layout, null); + } +}; + +pub fn createShaderModule(ldc: device.LogicalDeviceContext, allocator: std.mem.Allocator, spv: []const u8) !vk.ShaderModule { if (spv.len % 4 != 0) return error.InvalidSpirVSize; + const words = try allocator.alloc(u32, spv.len / 4); + defer allocator.free(words); + + const word_bytes = std.mem.sliceAsBytes(words); + std.mem.copyForwards(u8, word_bytes, spv); + const create_info = vk.ShaderModuleCreateInfo{ .code_size = spv.len, - .p_code = @ptrCast(@alignCast(spv.ptr)), + .p_code = words.ptr, }; return try ldc.vkd.createShaderModule(ldc.device, &create_info, null); } + +pub fn initPipelineContext(ldc: device.LogicalDeviceContext, extent: vk.Extent2D, render_pass: vk.RenderPass, vert_spv: []const u8, frag_spv: []const u8, allocator: std.mem.Allocator) !PipelineContext { + const pipeline_layout_create_info = vk.PipelineLayoutCreateInfo{}; + + const pipeline_layout = try ldc.vkd.createPipelineLayout( + ldc.device, + &pipeline_layout_create_info, + null, + ); + errdefer ldc.vkd.destroyPipelineLayout(ldc.device, pipeline_layout, null); + + const vert_shader_module = try createShaderModule(ldc, allocator, vert_spv); + defer ldc.vkd.destroyShaderModule(ldc.device, vert_shader_module, null); + + const frag_shader_module = try createShaderModule(ldc, allocator, frag_spv); + defer ldc.vkd.destroyShaderModule(ldc.device, frag_shader_module, null); + + const vert_shader_stage_info = vk.PipelineShaderStageCreateInfo{ + .stage = .{ .vertex_bit = true }, + .module = vert_shader_module, + .p_name = "main", + }; + + const frag_shader_stage_info = vk.PipelineShaderStageCreateInfo{ + .stage = .{ .fragment_bit = true }, + .module = frag_shader_module, + .p_name = "main", + }; + + const shader_stages = [_]vk.PipelineShaderStageCreateInfo{ + vert_shader_stage_info, + frag_shader_stage_info, + }; + + const vertex_input_info = vk.PipelineVertexInputStateCreateInfo{ + .vertex_binding_description_count = 0, + .p_vertex_binding_descriptions = null, + .vertex_attribute_description_count = 0, + .p_vertex_attribute_descriptions = null, + }; + + const input_assembly = vk.PipelineInputAssemblyStateCreateInfo{ + .topology = .triangle_list, + .primitive_restart_enable = .false, + }; + + const viewport = vk.Viewport{ + .x = 0.0, + .y = 0.0, + .width = @floatFromInt(extent.width), + .height = @floatFromInt(extent.height), + .min_depth = 0.0, + .max_depth = 1.0, + }; + + const scissor = vk.Rect2D{ + .offset = .{ .x = 0, .y = 0 }, + .extent = extent, + }; + + const viewport_state = vk.PipelineViewportStateCreateInfo{ + .viewport_count = 1, + .p_viewports = @ptrCast(&viewport), + .scissor_count = 1, + .p_scissors = @ptrCast(&scissor), + }; + + const rasterizer = vk.PipelineRasterizationStateCreateInfo{ + .depth_clamp_enable = .false, + .rasterizer_discard_enable = .false, + .polygon_mode = .fill, + .line_width = 1.0, + .cull_mode = .{ + .back_bit = true, + }, + .front_face = .clockwise, + .depth_bias_enable = .false, + .depth_bias_constant_factor = 0.0, + .depth_bias_clamp = 0.0, + .depth_bias_slope_factor = 0.0, + }; + + const multisampling = vk.PipelineMultisampleStateCreateInfo{ + .sample_shading_enable = .false, + .rasterization_samples = .{ .@"1_bit" = true }, + .min_sample_shading = 1.0, + .p_sample_mask = null, + .alpha_to_coverage_enable = .false, + .alpha_to_one_enable = .false, + }; + + const color_blend_attachment = vk.PipelineColorBlendAttachmentState{ + .color_write_mask = .{ + .r_bit = true, + .g_bit = true, + .b_bit = true, + .a_bit = true, + }, + .blend_enable = .false, + .src_color_blend_factor = .one, + .dst_color_blend_factor = .zero, + .color_blend_op = .add, + .src_alpha_blend_factor = .one, + .dst_alpha_blend_factor = .zero, + .alpha_blend_op = .add, + }; + + const color_blending = vk.PipelineColorBlendStateCreateInfo{ + .logic_op_enable = .false, + .attachment_count = 1, + .p_attachments = @ptrCast(&color_blend_attachment), + .blend_constants = .{ 0.0, 0.0, 0.0, 0.0 }, + .logic_op = .copy, + }; + + const pipeline_info = vk.GraphicsPipelineCreateInfo{ + .stage_count = shader_stages.len, + .p_stages = &shader_stages, + .p_vertex_input_state = &vertex_input_info, + .p_input_assembly_state = &input_assembly, + .p_viewport_state = &viewport_state, + .p_rasterization_state = &rasterizer, + .p_multisample_state = &multisampling, + .p_depth_stencil_state = null, + .p_color_blend_state = &color_blending, + .p_dynamic_state = null, + .layout = pipeline_layout, + .render_pass = render_pass, + .subpass = 0, + .base_pipeline_handle = .null_handle, + .base_pipeline_index = -1, + }; + + const pipeline_infos = [_]vk.GraphicsPipelineCreateInfo{ + pipeline_info, + }; + + var pipelines: [1]vk.Pipeline = undefined; + + _ = try ldc.vkd.createGraphicsPipelines( + ldc.device, + .null_handle, + &pipeline_infos, + null, + &pipelines, + ); + + return .{ + .graphics_pipeline = pipelines[0], + .pipeline_layout = pipeline_layout, + }; +}