Render square from vertex buffer

This commit is contained in:
WayfinderAK 2026-05-15 20:06:05 -08:00
parent 0e348fe0a0
commit b36f5b0b84
No known key found for this signature in database
6 changed files with 190 additions and 13 deletions

View File

@ -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);
}

12
src/geometry.zig Normal file
View File

@ -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 } },
};

View File

@ -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;

109
src/vulkan/buffer.zig Normal file
View File

@ -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;
}

View File

@ -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);

View File

@ -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{