diff --git a/shaders/square.vert b/shaders/square.vert index 0ced442..bb7c500 100644 --- a/shaders/square.vert +++ b/shaders/square.vert @@ -1,11 +1,7 @@ #version 450 -vec2 positions[3] = vec2[]( - vec2(0.0, -0.5), - vec2(0.5, 0.5), - vec2(-0.5, 0.5) -); +layout(location=0) in vec2 in_position; void main() { - gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0); + gl_Position = vec4(in_position, 0.0, 1.0); } diff --git a/src/geometry.zig b/src/geometry.zig new file mode 100644 index 0000000..12140ba --- /dev/null +++ b/src/geometry.zig @@ -0,0 +1,12 @@ +pub const Vertex = extern struct { + position: [2]f32, +}; + +const square_vertices = [_]Vertex{ + .{ .position = .{ -0.5, -0.5 } }, + .{ .position = .{ 0.5, -0.5 } }, + .{ .position = .{ 0.5, 0.5 } }, + .{ .position = .{ -0.5, -0.5 } }, + .{ .position = .{ 0.5, 0.5 } }, + .{ .position = .{ -0.5, 0.5 } }, +}; diff --git a/src/main.zig b/src/main.zig index 7bc7b92..b9b0f4d 100644 --- a/src/main.zig +++ b/src/main.zig @@ -12,6 +12,8 @@ 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"); +const geometry = @import("geometry.zig"); +const buffer_mod = @import("vulkan/buffer.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 @@ -97,12 +99,29 @@ pub fn main() !void { ); defer framebuffer_context.destroy(&ldc); + const square_vertices = [_]geometry.Vertex{ + .{ .position = .{ -0.5, -0.5 } }, + .{ .position = .{ 0.5, -0.5 } }, + .{ .position = .{ 0.5, 0.5 } }, + .{ .position = .{ -0.5, -0.5 } }, + .{ .position = .{ 0.5, 0.5 } }, + .{ .position = .{ -0.5, 0.5 } }, + }; + + var vertex_buffer_context = try buffer_mod.initVertexBuffer( + vc, + ldc, + &square_vertices, + ); + defer vertex_buffer_context.destroy(&ldc); + var command_context = try commands_mod.initCommandBuffers( ldc, render_pass_context, framebuffer_context, pipeline_context, swapchain_context, + vertex_buffer_context, std.heap.page_allocator, ); defer command_context.destroy(&ldc); @@ -129,7 +148,9 @@ pub fn main() !void { &render_pass_context, &framebuffer_context, &pipeline_context, + &vertex_buffer_context, &command_context, + &square_vertices, std.heap.page_allocator, ); } @@ -147,7 +168,9 @@ fn recreateSwapchain( render_pass_context: *render_pass_mod.RenderPassContext, framebuffer_context: *framebuffers_mod.FramebufferContext, pipeline_context: *pipeline_mod.PipelineContext, + vertex_buffer_context: *buffer_mod.VertexBufferContext, command_context: *commands_mod.CommandContext, + vertices: []const geometry.Vertex, allocator: std.mem.Allocator, ) !void { // A minimized window can report a zero-sized framebuffer. Vulkan swapchain @@ -178,8 +201,11 @@ 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_pipeline_context = try pipeline_mod.initPipelineContext(ldc, new_swapchain_context.extent, new_render_pass_context.render_pass, square_vert_spv, square_frag_spv, allocator); + errdefer new_pipeline_context.destroy(&ldc); + + const new_vertex_buffer_context = try buffer_mod.initVertexBuffer(vc, ldc, vertices); + errdefer new_vertex_buffer_context.destroy(&ldc); const new_framebuffer_context = try framebuffers_mod.initFramebuffers( ldc, @@ -195,6 +221,7 @@ fn recreateSwapchain( new_framebuffer_context, new_pipeline_context, new_swapchain_context, + new_vertex_buffer_context, allocator, ); errdefer new_command_context.destroy(&ldc); @@ -203,6 +230,7 @@ fn recreateSwapchain( // dependency order: command buffers/pool -> framebuffers -> render pass -> // swapchain/image views/images. command_context.destroy(&ldc); + vertex_buffer_context.destroy(&ldc); framebuffer_context.destroy(&ldc); pipeline_context.destroy(&ldc); render_pass_context.destroy(&ldc); @@ -211,6 +239,7 @@ fn recreateSwapchain( swapchain_context.* = new_swapchain_context; render_pass_context.* = new_render_pass_context; pipeline_context.* = new_pipeline_context; + vertex_buffer_context.* = new_vertex_buffer_context; framebuffer_context.* = new_framebuffer_context; command_context.* = new_command_context; diff --git a/src/vulkan/buffer.zig b/src/vulkan/buffer.zig new file mode 100644 index 0000000..bd78cfb --- /dev/null +++ b/src/vulkan/buffer.zig @@ -0,0 +1,109 @@ +const std = @import("std"); +const vk = @import("vulkan"); + +const geometry = @import("../geometry.zig"); +const context = @import("context.zig"); +const device = @import("device.zig"); + +pub const VertexBufferContext = struct { + buffer: vk.Buffer, + memory: vk.DeviceMemory, + vertex_count: u32, + + pub fn destroy(self: *const VertexBufferContext, ldc: *const device.LogicalDeviceContext) void { + ldc.vkd.destroyBuffer(ldc.device, self.buffer, null); + ldc.vkd.freeMemory(ldc.device, self.memory, null); + } +}; + +pub fn initVertexBuffer( + vc: context.VulkanContext, + ldc: device.LogicalDeviceContext, + vertices: []const geometry.Vertex, +) !VertexBufferContext { + const buffer_size: vk.DeviceSize = @intCast(@sizeOf(geometry.Vertex) * vertices.len); + + const buffer_create_info = vk.BufferCreateInfo{ + .size = buffer_size, + .usage = .{ + .vertex_buffer_bit = true, + }, + .sharing_mode = .exclusive, + }; + + const buffer = try ldc.vkd.createBuffer(ldc.device, &buffer_create_info, null); + errdefer ldc.vkd.destroyBuffer(ldc.device, buffer, null); + + const memory_requirements = ldc.vkd.getBufferMemoryRequirements( + ldc.device, + buffer, + ); + const memory_index = try findMemoryType( + vc, + ldc, + memory_requirements.memory_type_bits, + .{ + .host_visible_bit = true, + .host_coherent_bit = true, + }, + ); + + const alloc_info = vk.MemoryAllocateInfo{ + .allocation_size = memory_requirements.size, + .memory_type_index = memory_index, + }; + + const memory = try ldc.vkd.allocateMemory(ldc.device, &alloc_info, null); + errdefer ldc.vkd.freeMemory(ldc.device, memory, null); + + try ldc.vkd.bindBufferMemory(ldc.device, buffer, memory, 0); + + const mapped = try ldc.vkd.mapMemory( + ldc.device, + memory, + 0, + buffer_size, + .{}, + ); + defer ldc.vkd.unmapMemory(ldc.device, memory); + + const dst: [*]u8 = @ptrCast(mapped); + const dst_slice = dst[0..buffer_size]; + + const src_bytes = std.mem.sliceAsBytes(vertices); + std.mem.copyForwards(u8, dst_slice, src_bytes); + + return .{ + .buffer = buffer, + .memory = memory, + .vertex_count = @intCast(vertices.len), + }; +} + +fn findMemoryType( + vc: context.VulkanContext, + ldc: device.LogicalDeviceContext, + type_filter: u32, + properties: vk.MemoryPropertyFlags, +) !u32 { + const memory_properties = vc.vki.getPhysicalDeviceMemoryProperties(ldc.physical_device); + + var i: u5 = 0; + while (i < memory_properties.memory_type_count) : (i += 1) { + const type_supported = (type_filter & (@as(u32, 1) << i)) != 0; + const flags = memory_properties.memory_types[i].property_flags; + + const has_properties = + (!properties.device_local_bit or flags.device_local_bit) and + (!properties.host_visible_bit or flags.host_visible_bit) and + (!properties.host_coherent_bit or flags.host_coherent_bit) and + (!properties.host_cached_bit or flags.host_cached_bit) and + (!properties.lazily_allocated_bit or flags.lazily_allocated_bit); + + if (type_supported and has_properties) { + return i; + } + } + + return error.NoSuitableMemoryType; +} diff --git a/src/vulkan/commands.zig b/src/vulkan/commands.zig index 7fb3ee4..ad2778d 100644 --- a/src/vulkan/commands.zig +++ b/src/vulkan/commands.zig @@ -6,6 +6,7 @@ const framebuffers = @import("framebuffers.zig"); const render_pass = @import("render_pass.zig"); const swapchain = @import("swapchain.zig"); const pipeline = @import("pipeline.zig"); +const buffer = @import("buffer.zig"); pub const CommandContext = struct { command_pool: vk.CommandPool, @@ -24,6 +25,7 @@ pub fn initCommandBuffers( framebuffer_context: framebuffers.FramebufferContext, pipeline_context: pipeline.PipelineContext, swapchain_context: swapchain.SwapchainContext, + vertex_buffer_context: buffer.VertexBufferContext, allocator: std.mem.Allocator, ) !CommandContext { const command_pool_create_info = vk.CommandPoolCreateInfo{ @@ -94,7 +96,22 @@ pub fn initCommandBuffers( pipeline_context.graphics_pipeline, ); - ldc.vkd.cmdDraw(command_buffer, 3, 1, 0, 0); + const vertex_buffers = [_]vk.Buffer{ + vertex_buffer_context.buffer, + }; + + const offsets = [_]vk.DeviceSize{ + 0, + }; + + ldc.vkd.cmdBindVertexBuffers( + command_buffer, + 0, + &vertex_buffers, + &offsets, + ); + + ldc.vkd.cmdDraw(command_buffer, vertex_buffer_context.vertex_count, 1, 0, 0); ldc.vkd.cmdEndRenderPass(command_buffer); diff --git a/src/vulkan/pipeline.zig b/src/vulkan/pipeline.zig index 48dda26..e2b4c23 100644 --- a/src/vulkan/pipeline.zig +++ b/src/vulkan/pipeline.zig @@ -2,6 +2,7 @@ const std = @import("std"); const vk = @import("vulkan"); const device = @import("device.zig"); +const geometry = @import("../geometry.zig"); pub const PipelineContext = struct { graphics_pipeline: vk.Pipeline, @@ -63,11 +64,24 @@ pub fn initPipelineContext(ldc: device.LogicalDeviceContext, extent: vk.Extent2D frag_shader_stage_info, }; + const binding_description = vk.VertexInputBindingDescription{ + .binding = 0, + .stride = @sizeOf(geometry.Vertex), + .input_rate = .vertex, + }; + + const attribute_description = vk.VertexInputAttributeDescription{ + .location = 0, + .binding = 0, + .format = .r32g32_sfloat, + .offset = @offsetOf(geometry.Vertex, "position"), + }; + 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, + .vertex_binding_description_count = 1, + .p_vertex_binding_descriptions = @ptrCast(&binding_description), + .vertex_attribute_description_count = 1, + .p_vertex_attribute_descriptions = @ptrCast(&attribute_description), }; const input_assembly = vk.PipelineInputAssemblyStateCreateInfo{