Add uniform buffers
This commit is contained in:
parent
8c60f79f7d
commit
b1bbd65aaa
13 changed files with 5695 additions and 4 deletions
20
src/main.zig
20
src/main.zig
|
@ -1,7 +1,10 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const sdl = @import("sdl2");
|
||||
const zm = @import("zmath");
|
||||
|
||||
const VulkanRenderer = @import("vulkan_renderer.zig").VulkanRenderer;
|
||||
const Vector3 = @import("utilities.zig").Vector3;
|
||||
|
||||
pub fn main() !void {
|
||||
const window = try initWindow();
|
||||
|
@ -16,6 +19,11 @@ pub fn main() !void {
|
|||
var vulkan_renderer = try VulkanRenderer.init(window, allocator);
|
||||
defer vulkan_renderer.deinit();
|
||||
|
||||
var angle: f32 = 0.0;
|
||||
var now: u64 = sdl.getPerformanceCounter();
|
||||
var last: u64 = 0;
|
||||
var delta_time: f32 = 0.0;
|
||||
|
||||
mainLoop: while (true) {
|
||||
while (sdl.pollEvent()) |ev| {
|
||||
switch (ev) {
|
||||
|
@ -29,6 +37,18 @@ pub fn main() !void {
|
|||
}
|
||||
}
|
||||
|
||||
last = now;
|
||||
now = sdl.getPerformanceCounter();
|
||||
delta_time = @as(f32, @floatFromInt(now - last)) * 1000.0 / @as(f32, @floatFromInt(now));
|
||||
|
||||
angle += 10.0 * delta_time;
|
||||
|
||||
if (angle > 360.0) {
|
||||
angle -= 360.0;
|
||||
}
|
||||
|
||||
try vulkan_renderer.updateModel(zm.rotationZ(angle));
|
||||
|
||||
try vulkan_renderer.draw();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,9 +3,15 @@
|
|||
layout(location = 0) in vec3 pos;
|
||||
layout(location = 1) in vec3 col;
|
||||
|
||||
layout(binding = 0) uniform MVP {
|
||||
mat4 projection;
|
||||
mat4 view;
|
||||
mat4 model;
|
||||
} mvp;
|
||||
|
||||
layout(location = 0) out vec3 fragCol;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(pos, 1.0);
|
||||
gl_Position = mvp.projection * mvp.view * mvp.model * vec4(pos, 1.0);
|
||||
fragCol = col;
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ const sdl = @import("sdl2");
|
|||
const vk = @import("vulkan");
|
||||
const builtin = @import("builtin");
|
||||
const shaders = @import("shaders");
|
||||
const zm = @import("zmath");
|
||||
|
||||
const Utilities = @import("utilities.zig");
|
||||
const QueueFamilyIndices = Utilities.QueueFamilyIndices;
|
||||
|
@ -39,6 +40,12 @@ pub const CommandBuffer = vk.CommandBufferProxy(apis);
|
|||
pub const VulkanRenderer = struct {
|
||||
const Self = @This();
|
||||
|
||||
const Mvp = struct {
|
||||
projection: zm.Mat,
|
||||
view: zm.Mat,
|
||||
model: zm.Mat,
|
||||
};
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
vkb: BaseDispatch,
|
||||
|
@ -50,6 +57,9 @@ pub const VulkanRenderer = struct {
|
|||
// Scene objects
|
||||
meshes: [2]Mesh,
|
||||
|
||||
// Scene settings
|
||||
mvp: Mvp,
|
||||
|
||||
// Main
|
||||
instance: Instance,
|
||||
physical_device: vk.PhysicalDevice,
|
||||
|
@ -65,6 +75,15 @@ pub const VulkanRenderer = struct {
|
|||
swapchain_framebuffers: []vk.Framebuffer,
|
||||
command_buffers: []CommandBuffer,
|
||||
|
||||
// Descriptors
|
||||
descriptor_set_layout: vk.DescriptorSetLayout,
|
||||
|
||||
descriptor_pool: vk.DescriptorPool,
|
||||
descriptor_sets: []vk.DescriptorSet,
|
||||
|
||||
uniform_buffer: []vk.Buffer,
|
||||
uniform_buffer_memory: []vk.DeviceMemory,
|
||||
|
||||
// Pipeline
|
||||
graphics_pipeline: vk.Pipeline,
|
||||
pipeline_layout: vk.PipelineLayout,
|
||||
|
@ -103,10 +122,28 @@ pub const VulkanRenderer = struct {
|
|||
try self.createLogicalDevice();
|
||||
try self.createSwapchain();
|
||||
try self.createRenderPass();
|
||||
try self.createDescriptorSetLayout();
|
||||
try self.createGraphicsPipeline();
|
||||
try self.createFramebuffers();
|
||||
try self.createCommandPool();
|
||||
|
||||
const aspect: f32 = @as(f32, @floatFromInt(self.extent.width)) / @as(f32, @floatFromInt(self.extent.height));
|
||||
self.mvp.projection = zm.perspectiveFovRh(
|
||||
std.math.degreesToRadians(45.0),
|
||||
aspect,
|
||||
0.1,
|
||||
100.0,
|
||||
);
|
||||
self.mvp.view = zm.lookAtRh(
|
||||
zm.Vec{ 0.0, 0.0, 2.0, 0.0 },
|
||||
zm.Vec{ 0.0, 0.0, 0.0, 0.0 },
|
||||
zm.Vec{ 0.0, 1.0, 0.0, 0.0 },
|
||||
);
|
||||
self.mvp.model = zm.identity();
|
||||
|
||||
// Invert y scale
|
||||
self.mvp.projection[1][1] *= -1;
|
||||
|
||||
// Create meshes
|
||||
// Vertex Data
|
||||
var mesh_vertices = [_]Vertex{
|
||||
|
@ -154,12 +191,21 @@ pub const VulkanRenderer = struct {
|
|||
self.meshes = [_]Mesh{ first_mesh, second_mesh };
|
||||
|
||||
try self.createCommandBuffers();
|
||||
|
||||
try self.createUniformBuffers();
|
||||
try self.createDescriptorPool();
|
||||
try self.createDescriptorSets();
|
||||
|
||||
try self.recordCommands();
|
||||
try self.createSynchronisation();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn updateModel(self: *Self, new_model: zm.Mat) !void {
|
||||
self.mvp.model = new_model;
|
||||
}
|
||||
|
||||
pub fn draw(self: *Self) !void {
|
||||
// Wait for given fence to signal (open) from last draw before continuing
|
||||
_ = try self.device.waitForFences(1, @ptrCast(&self.draw_fences[self.current_frame]), vk.TRUE, std.math.maxInt(u64));
|
||||
|
@ -175,6 +221,8 @@ pub const VulkanRenderer = struct {
|
|||
.null_handle,
|
||||
);
|
||||
|
||||
try self.updateUniformBuffer(image_index_result.image_index);
|
||||
|
||||
// -- Submit command buffer to render
|
||||
// Queue submission information
|
||||
const wait_stages = [_]vk.PipelineStageFlags{.{ .color_attachment_output_bit = true }};
|
||||
|
@ -215,6 +263,17 @@ pub const VulkanRenderer = struct {
|
|||
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
|
||||
}
|
||||
|
||||
self.device.destroyDescriptorPool(self.descriptor_pool, null);
|
||||
self.device.destroyDescriptorSetLayout(self.descriptor_set_layout, null);
|
||||
|
||||
for (self.uniform_buffer, self.uniform_buffer_memory) |buffer, buffer_memory| {
|
||||
self.device.destroyBuffer(buffer, null);
|
||||
self.device.freeMemory(buffer_memory, null);
|
||||
}
|
||||
self.allocator.free(self.uniform_buffer);
|
||||
self.allocator.free(self.uniform_buffer_memory);
|
||||
self.allocator.free(self.descriptor_sets);
|
||||
|
||||
for (self.meshes) |mesh| {
|
||||
mesh.destroyBuffers();
|
||||
}
|
||||
|
@ -491,6 +550,26 @@ pub const VulkanRenderer = struct {
|
|||
self.render_pass = try self.device.createRenderPass(&render_pass_create_info, null);
|
||||
}
|
||||
|
||||
fn createDescriptorSetLayout(self: *Self) !void {
|
||||
// MVP binding info
|
||||
const mvp_layout_binding: vk.DescriptorSetLayoutBinding = .{
|
||||
.binding = 0, // Binding point in shader (designated by binding number in shader)
|
||||
.descriptor_type = .uniform_buffer, // Type of descriptor (unifor, dynamic uniform, image sampler, etc)
|
||||
.descriptor_count = 1, // Number of descriptors for binding
|
||||
.stage_flags = .{ .vertex_bit = true }, // Shader stage to bind to
|
||||
.p_immutable_samplers = null, // For texture: can make smapler data immutable by specifying in layout
|
||||
};
|
||||
|
||||
// Create descriptor set layout with given bindings
|
||||
const layout_create_info: vk.DescriptorSetLayoutCreateInfo = .{
|
||||
.binding_count = 1, // Number of binding infos
|
||||
.p_bindings = @ptrCast(&mvp_layout_binding), // Array of binding infos
|
||||
};
|
||||
|
||||
// Create descriptor set layout
|
||||
self.descriptor_set_layout = try self.device.createDescriptorSetLayout(&layout_create_info, null);
|
||||
}
|
||||
|
||||
fn createGraphicsPipeline(self: *Self) !void {
|
||||
// Create shader modules
|
||||
const vert = try self.device.createShaderModule(&.{
|
||||
|
@ -606,7 +685,7 @@ pub const VulkanRenderer = struct {
|
|||
.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
|
||||
.front_face = .counter_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,
|
||||
|
@ -647,8 +726,11 @@ pub const VulkanRenderer = struct {
|
|||
.blend_constants = [_]f32{ 0, 0, 0, 0 },
|
||||
};
|
||||
|
||||
// -- Pipeline layout (TODO: Apply future descriptor set layouts) --
|
||||
const pipeline_layout_create_info: vk.PipelineLayoutCreateInfo = .{};
|
||||
// -- Pipeline layout --
|
||||
const pipeline_layout_create_info: vk.PipelineLayoutCreateInfo = .{
|
||||
.set_layout_count = 1,
|
||||
.p_set_layouts = @ptrCast(&self.descriptor_set_layout),
|
||||
};
|
||||
|
||||
self.pipeline_layout = try self.device.createPipelineLayout(&pipeline_layout_create_info, null);
|
||||
|
||||
|
@ -748,6 +830,101 @@ pub const VulkanRenderer = struct {
|
|||
}
|
||||
}
|
||||
|
||||
fn createUniformBuffers(self: *Self) !void {
|
||||
// Buffer size will be size of all three variables (will offset to access)
|
||||
const buffer_size: vk.DeviceSize = @sizeOf(@TypeOf(self.mvp));
|
||||
|
||||
// One uniform buffer for each image (and by extension, command buffer)
|
||||
self.uniform_buffer = try self.allocator.alloc(vk.Buffer, self.swapchain_images.len);
|
||||
self.uniform_buffer_memory = try self.allocator.alloc(vk.DeviceMemory, self.swapchain_images.len);
|
||||
|
||||
// Create the uniform buffers
|
||||
for (0..self.uniform_buffer.len) |i| {
|
||||
try Utilities.createBuffer(
|
||||
self.physical_device,
|
||||
self.instance,
|
||||
self.device,
|
||||
buffer_size,
|
||||
.{ .uniform_buffer_bit = true },
|
||||
.{ .host_visible_bit = true, .host_coherent_bit = true },
|
||||
&self.uniform_buffer[i],
|
||||
&self.uniform_buffer_memory[i],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn createDescriptorPool(self: *Self) !void {
|
||||
// Type of descriptors + how many descriptors (!= descriptor sets) (combined makes the pool size)
|
||||
const pool_size: vk.DescriptorPoolSize = .{
|
||||
.type = .uniform_buffer,
|
||||
.descriptor_count = @intCast(self.uniform_buffer.len),
|
||||
};
|
||||
|
||||
// Data to create descriptor pool
|
||||
const pool_create_info: vk.DescriptorPoolCreateInfo = .{
|
||||
.max_sets = @intCast(self.uniform_buffer.len), // Maximum number of descriptor sets that can be created from pool
|
||||
.pool_size_count = 1, // Amount of pool sizes being passed
|
||||
.p_pool_sizes = @ptrCast(&pool_size), // Pool sizes to create pool with
|
||||
};
|
||||
|
||||
// Create descriptor pool
|
||||
self.descriptor_pool = try self.device.createDescriptorPool(&pool_create_info, null);
|
||||
}
|
||||
|
||||
fn createDescriptorSets(self: *Self) !void {
|
||||
// One descriptor set for every buffer
|
||||
self.descriptor_sets = try self.allocator.alloc(vk.DescriptorSet, self.uniform_buffer.len);
|
||||
|
||||
var set_layouts = try self.allocator.alloc(vk.DescriptorSetLayout, self.uniform_buffer.len);
|
||||
defer self.allocator.free(set_layouts);
|
||||
for (0..set_layouts.len) |i| {
|
||||
set_layouts[i] = self.descriptor_set_layout;
|
||||
}
|
||||
|
||||
// Descriptor set allocation info
|
||||
const set_alloc_info: vk.DescriptorSetAllocateInfo = .{
|
||||
.descriptor_pool = self.descriptor_pool, // Pool to allocate descriptor set from
|
||||
.descriptor_set_count = @intCast(self.descriptor_sets.len), // Number of sets to allocate
|
||||
.p_set_layouts = set_layouts.ptr, // Layouts to use to allocate sets (1:1 relationship)
|
||||
};
|
||||
|
||||
// Allocate descriptor sets (multiple)
|
||||
try self.device.allocateDescriptorSets(&set_alloc_info, self.descriptor_sets.ptr);
|
||||
|
||||
// Update all of descriptor set buffer bindings
|
||||
for (0..self.descriptor_sets.len) |i| {
|
||||
// Buffer info and data offset info
|
||||
const mvp_buffer_info: vk.DescriptorBufferInfo = .{
|
||||
.buffer = self.uniform_buffer[i], // Bufer to get data from
|
||||
.offset = 0, // Position of start of data
|
||||
.range = @sizeOf(@TypeOf(self.mvp)), // Size of data
|
||||
};
|
||||
|
||||
// Data about connection between binding and buffer
|
||||
const mvp_set_write: vk.WriteDescriptorSet = .{
|
||||
.dst_set = self.descriptor_sets[i], // Descriptor set to update
|
||||
.dst_binding = 0, // Binding to update (matches with binding on layout/shader)
|
||||
.dst_array_element = 0, // Index in array to update
|
||||
.descriptor_type = .uniform_buffer, // Type of descriptor
|
||||
.descriptor_count = 1, // Amount to update
|
||||
.p_buffer_info = @ptrCast(&mvp_buffer_info), // Information about buffer data to bind
|
||||
.p_image_info = undefined,
|
||||
.p_texel_buffer_view = undefined,
|
||||
};
|
||||
|
||||
// Update the descriptor sets with new buffer/binding info
|
||||
self.device.updateDescriptorSets(1, @ptrCast(&mvp_set_write), 0, null);
|
||||
}
|
||||
}
|
||||
|
||||
fn updateUniformBuffer(self: Self, image_index: u32) !void {
|
||||
const data = try self.device.mapMemory(self.uniform_buffer_memory[image_index], 0, @sizeOf(Mvp), .{});
|
||||
|
||||
const mvp_data: *Mvp = @ptrCast(@alignCast(data));
|
||||
mvp_data.* = self.mvp;
|
||||
self.device.unmapMemory(self.uniform_buffer_memory[image_index]);
|
||||
}
|
||||
|
||||
fn recordCommands(self: *Self) !void {
|
||||
// Information about how to begin each command
|
||||
const buffer_begin_info: vk.CommandBufferBeginInfo = .{
|
||||
|
@ -799,6 +976,17 @@ pub const VulkanRenderer = struct {
|
|||
// Bind mesh index buffer, with 0 offset and using the uint32 type
|
||||
command_buffer.bindIndexBuffer(mesh.index_buffer, 0, .uint32);
|
||||
|
||||
// Bind descriptor sets
|
||||
command_buffer.bindDescriptorSets(
|
||||
.graphics,
|
||||
self.pipeline_layout,
|
||||
0,
|
||||
1,
|
||||
@ptrCast(&self.descriptor_sets[i]),
|
||||
0,
|
||||
null,
|
||||
);
|
||||
|
||||
// Execute a pipeline
|
||||
command_buffer.drawIndexed(mesh.index_count, 1, 0, 0, 0);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue