Refactored source code structure.

This commit is contained in:
Lorenzo Torres 2025-03-17 19:44:08 +01:00
parent 5bab2c4bcf
commit 1d64275dee
9 changed files with 114 additions and 6 deletions

119
src/rendering/mesh.zig Normal file
View file

@ -0,0 +1,119 @@
const c = @import("../c.zig");
const std = @import("std");
const vk = @import("vulkan.zig");
const Allocator = std.mem.Allocator;
pub const Vertex = struct {
position: [3]f32,
pub fn create(x: f32, y: f32, z: f32) Vertex {
return Vertex{
.position = .{ x, y, z },
};
}
pub fn bindingDescription() c.VkVertexInputBindingDescription {
const binding_description: c.VkVertexInputBindingDescription = .{
.binding = 0,
.stride = @sizeOf(Vertex),
.inputRate = c.VK_VERTEX_INPUT_RATE_VERTEX,
};
return binding_description;
}
pub fn attributeDescription() c.VkVertexInputAttributeDescription {
const attribute_description: c.VkVertexInputAttributeDescription = .{
.location = 0,
.binding = 0,
.format = c.VK_FORMAT_R32G32B32_SFLOAT,
.offset = 0,
};
return attribute_description;
}
};
pub const Mesh = struct {
vertex_buffer: vk.Buffer,
index_buffer: vk.Buffer,
pub fn createVertexBuffer(device: anytype) !vk.Buffer {
const vertices = [_]Vertex{
Vertex.create(0.5, -0.5, 0.0),
Vertex.create(0.5, 0.5, 0.0),
Vertex.create(-0.5, 0.5, 0.0),
Vertex.create(-0.5, -0.5, 0.0),
};
var data: [*c]?*anyopaque = null;
const buffer = try device.createBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf(Vertex) * vertices.len);
try vk.mapError(c.vkMapMemory(
device.handle,
buffer.memory,
0,
buffer.size,
0,
@ptrCast(&data),
));
if (data) |ptr| {
const gpu_vertices: [*]Vertex = @ptrCast(@alignCast(ptr));
@memcpy(gpu_vertices, vertices[0..]);
}
c.vkUnmapMemory(device.handle, buffer.memory);
const vertex_buffer = try device.createBuffer(vk.BufferUsage{ .vertex_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(Vertex) * vertices.len);
try buffer.copyTo(device, vertex_buffer);
buffer.destroy(device.handle);
return vertex_buffer;
}
pub fn createIndexBuffer(device: anytype) !vk.Buffer {
const indices = [_]u16{ 0, 1, 2, 3, 0, 2 };
var data: [*c]?*anyopaque = null;
const buffer = try device.createBuffer(vk.BufferUsage{ .transfer_src = true }, vk.BufferFlags{ .host_visible = true, .host_coherent = true }, @sizeOf(u16) * indices.len);
try vk.mapError(c.vkMapMemory(
device.handle,
buffer.memory,
0,
buffer.size,
0,
@ptrCast(&data),
));
if (data) |ptr| {
const gpu_indices: [*]u16 = @ptrCast(@alignCast(ptr));
@memcpy(gpu_indices, indices[0..]);
}
c.vkUnmapMemory(device.handle, buffer.memory);
const index_buffer = try device.createBuffer(vk.BufferUsage{ .index_buffer = true, .transfer_dst = true }, vk.BufferFlags{ .device_local = true }, @sizeOf(u16) * indices.len);
try buffer.copyTo(device, index_buffer);
buffer.destroy(device.handle);
return index_buffer;
}
pub fn create(device: anytype) !Mesh {
const vertex_buffer = try Mesh.createVertexBuffer(device);
const index_buffer = try Mesh.createIndexBuffer(device);
return Mesh{
.vertex_buffer = vertex_buffer,
.index_buffer = index_buffer,
};
}
};

View file

@ -0,0 +1,94 @@
const c = @import("../c.zig");
const std = @import("std");
const vk = @import("vulkan.zig");
const window = @import("window.zig");
const mesh = @import("mesh.zig");
const Allocator = std.mem.Allocator;
const Renderer = @This();
instance: vk.Instance,
surface: vk.Surface,
physical_device: vk.PhysicalDevice,
device: vk.Device(2),
render_pass: vk.RenderPass(2),
swapchain: vk.Swapchain(2),
graphics_pipeline: vk.GraphicsPipeline(2),
current_frame: u32,
vertex_buffer: vk.Buffer,
index_buffer: vk.Buffer,
pub fn create(allocator: Allocator, w: window.Window) !Renderer {
const instance = try vk.Instance.create(allocator);
const surface = try vk.Surface.create(instance, w);
var physical_device = try vk.PhysicalDevice.pick(allocator, instance);
const device = try physical_device.create_device(surface, allocator, 2);
const vertex_shader = try device.createShader("shader_vert");
defer device.destroyShader(vertex_shader);
const fragment_shader = try device.createShader("shader_frag");
defer device.destroyShader(fragment_shader);
const render_pass = try vk.RenderPass(2).create(allocator, device, surface, physical_device);
const swapchain = try vk.Swapchain(2).create(allocator, surface, device, physical_device, w, render_pass);
const graphics_pipeline = try vk.GraphicsPipeline(2).create(device, swapchain, render_pass, vertex_shader, fragment_shader);
// TODO: I think the renderer shouldn't have to interact with buffers. I think the API should change to
// something along the lines of
// renderer.begin()
// renderer.render(triangle);
// renderer.render(some_other_thing);
// ...
// renderer.submit()
const triangle = try mesh.Mesh.create(device);
return Renderer{
.instance = instance,
.surface = surface,
.physical_device = physical_device,
.device = device,
.render_pass = render_pass,
.swapchain = swapchain,
.graphics_pipeline = graphics_pipeline,
.current_frame = 0,
// TODO: Why are we storing the buffer and not the Mesh?
.vertex_buffer = triangle.vertex_buffer,
.index_buffer = triangle.index_buffer,
};
}
pub fn destroy(self: Renderer) void {
self.device.waitIdle();
self.index_buffer.destroy(self.device.handle);
self.vertex_buffer.destroy(self.device.handle);
self.graphics_pipeline.destroy(self.device);
self.swapchain.destroy(self.device);
self.render_pass.destroy(self.device);
self.device.destroy();
self.surface.destroy(self.instance);
self.instance.destroy();
}
// TODO: tick is maybe a bad name? something like present() or submit() is better?
pub fn tick(self: *Renderer) !void {
try self.device.waitFence(self.current_frame);
const image = try self.swapchain.nextImage(self.device, self.current_frame);
try self.device.resetCommand(self.current_frame);
try self.device.beginCommand(self.current_frame);
self.render_pass.begin(self.swapchain, self.device, image, self.current_frame);
self.graphics_pipeline.bind(self.device, self.current_frame);
self.device.bindVertexBuffer(self.vertex_buffer, self.current_frame);
self.device.bindIndexBuffer(self.index_buffer, self.current_frame);
self.device.bindDescriptorSets(self.graphics_pipeline, self.current_frame);
self.device.draw(@intCast(self.index_buffer.size / @sizeOf(u16)), self.current_frame);
self.render_pass.end(self.device, self.current_frame);
try self.device.endCommand(self.current_frame);
try self.device.submit(self.swapchain, image, self.current_frame);
self.current_frame = (self.current_frame + 1) % 2;
}

1151
src/rendering/vulkan.zig Normal file

File diff suppressed because it is too large Load diff

64
src/rendering/window.zig Normal file
View file

@ -0,0 +1,64 @@
const c = @import("../c.zig");
const std = @import("std");
pub const Error = error{
platform_unavailable,
platform_error,
};
pub fn getExtensions() [][*c]const u8 {
var extension_count: u32 = undefined;
const raw: [*c][*c]const u8 = c.glfwGetRequiredInstanceExtensions(&extension_count);
const extensions = raw[0..extension_count];
return extensions;
}
pub const Window = struct {
title: []const u8,
width: usize,
height: usize,
raw: *c.GLFWwindow,
pub fn create(width: usize, height: usize, title: []const u8) !Window {
if (c.glfwInit() != c.GLFW_TRUE) {
const status = c.glfwGetError(null);
return switch (status) {
c.GLFW_PLATFORM_UNAVAILABLE => Error.platform_unavailable,
c.GLFW_PLATFORM_ERROR => Error.platform_error,
else => unreachable,
};
}
c.glfwWindowHint(c.GLFW_RESIZABLE, c.GLFW_FALSE);
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
const raw = c.glfwCreateWindow(@intCast(width), @intCast(height), title.ptr, null, null);
c.glfwShowWindow(raw);
return Window{
.title = title,
.width = width,
.height = height,
.raw = raw.?,
};
}
pub fn shouldClose(self: Window) bool {
return c.glfwWindowShouldClose(self.raw) == c.GLFW_TRUE;
}
pub fn size(self: Window) struct { usize, usize } {
var width: u32 = undefined;
var height: u32 = undefined;
c.glfwGetFramebufferSize(self.raw, @ptrCast(&width), @ptrCast(&height));
return .{ @intCast(width), @intCast(height) };
}
pub fn destroy(self: Window) void {
c.glfwDestroyWindow(self.raw);
c.glfwTerminate();
}
};