Compare commits

..

No commits in common. "849f6b2dae3d799769a31077b0fa1f86bc830a33" and "6f4f600fdaab341a463d459f27fd56a0933c52c0" have entirely different histories.

7 changed files with 337 additions and 445 deletions

View file

@ -1,5 +1,5 @@
const std = @import("std"); const std = @import("std");
const sdl = @import("libs/sdl/build.zig"); const sdl = @import("sdl");
const vkgen = @import("vulkan_zig"); const vkgen = @import("vulkan_zig");
pub fn build(b: *std.Build) void { pub fn build(b: *std.Build) void {
@ -34,7 +34,7 @@ pub fn build(b: *std.Build) void {
// SDL2 // SDL2
const sdl_sdk = sdl.init(b, null, null); const sdl_sdk = sdl.init(b, null, null);
sdl_sdk.link(exe, .dynamic, sdl.Library.SDL2); sdl_sdk.link(exe, .dynamic, .SDL2);
exe.root_module.addImport("sdl2", sdl_sdk.getWrapperModuleVulkan(vkzig_bindings)); exe.root_module.addImport("sdl2", sdl_sdk.getWrapperModuleVulkan(vkzig_bindings));
// zmath // zmath

View file

@ -9,15 +9,18 @@
.url = "https://github.com/Snektron/vulkan-zig/archive/9f6e6177b1fdb3ed22231d9216a24480e84cfa5e.tar.gz", .url = "https://github.com/Snektron/vulkan-zig/archive/9f6e6177b1fdb3ed22231d9216a24480e84cfa5e.tar.gz",
.hash = "1220f2961df224f7d35dee774b26194b8b937cc252fa8e4023407776c58521d53e38", .hash = "1220f2961df224f7d35dee774b26194b8b937cc252fa8e4023407776c58521d53e38",
}, },
// .sdl = .{ .sdl = .{
// .url = "https://github.com/ikskuh/SDL.zig/archive/9663dc70c19b13afcb4b9f596c928d7b2838e548.tar.gz", .url = "https://github.com/MasterQ32/SDL.zig/archive/1432ed3f6a020973906fbc996868131ae1d631be.tar.gz",
// .hash = "12202141beb92d68ef5088538ff761d5c3ecd2d4e11867c89fbbdcd9f814b8cba8ee", .hash = "1220ebeeaade31e207a56977aff537a65e6338cddc68d50217ddf30bbc58fb27d367",
// }, },
}, },
.paths = .{ .paths = .{
"build.zig", "build.zig",
"build.zig.zon", "build.zig.zon",
"src", "src",
// For example...
//"LICENSE",
//"README.md",
}, },
} }

@ -1 +0,0 @@
Subproject commit 9663dc70c19b13afcb4b9f596c928d7b2838e548

View file

@ -6,11 +6,11 @@ const Utilities = @import("utilities.zig");
const Vertex = Utilities.Vertex; const Vertex = Utilities.Vertex;
const Device = @import("vulkan_renderer.zig").Device; const Device = @import("vulkan_renderer.zig").Device;
const Instance = @import("vulkan_renderer.zig").Instance; const Instance = @import("vulkan_renderer.zig").Instance;
const Model = @import("vulkan_renderer.zig").Model; const UboModel = @import("vulkan_renderer.zig").UboModel;
const Self = @This(); const Self = @This();
ubo_model: Model, ubo_model: UboModel,
vertex_count: u32, vertex_count: u32,
vertex_buffer: vk.Buffer, vertex_buffer: vk.Buffer,

View file

@ -3,18 +3,18 @@
layout(location = 0) in vec3 pos; layout(location = 0) in vec3 pos;
layout(location = 1) in vec3 col; layout(location = 1) in vec3 col;
layout(location = 0) out vec3 fragCol;
layout(binding = 0) uniform UboViewProjection { layout(binding = 0) uniform UboViewProjection {
mat4 projection; mat4 projection;
mat4 view; mat4 view;
} uboViewProjection; } uboViewProjection;
layout(push_constant) uniform PushModel { layout(binding = 1) uniform UboModel {
mat4 model; mat4 model;
} pushModel; } uboModel;
layout(location = 0) out vec3 fragCol;
void main() { void main() {
gl_Position = uboViewProjection.projection * uboViewProjection.view * pushModel.model * vec4(pos, 1.0); gl_Position = uboViewProjection.projection * uboViewProjection.view * uboModel.model * vec4(pos, 1.0);
fragCol = col; fragCol = col;
} }

View file

@ -43,7 +43,7 @@ const UboViewProjection = struct {
view: zm.Mat align(16), view: zm.Mat align(16),
}; };
pub const Model = struct { pub const UboModel = struct {
model: zm.Mat align(16), model: zm.Mat align(16),
}; };
@ -81,7 +81,6 @@ pub const VulkanRenderer = struct {
// Descriptors // Descriptors
descriptor_set_layout: vk.DescriptorSetLayout, descriptor_set_layout: vk.DescriptorSetLayout,
push_constant_range: vk.PushConstantRange,
descriptor_pool: vk.DescriptorPool, descriptor_pool: vk.DescriptorPool,
descriptor_sets: []vk.DescriptorSet, descriptor_sets: []vk.DescriptorSet,
@ -89,6 +88,9 @@ pub const VulkanRenderer = struct {
vp_uniform_buffer: []vk.Buffer, vp_uniform_buffer: []vk.Buffer,
vp_uniform_buffer_memory: []vk.DeviceMemory, vp_uniform_buffer_memory: []vk.DeviceMemory,
model_duniform_buffer: []vk.Buffer,
model_duniform_buffer_memory: []vk.DeviceMemory,
// Pipeline // Pipeline
graphics_pipeline: vk.Pipeline, graphics_pipeline: vk.Pipeline,
pipeline_layout: vk.PipelineLayout, pipeline_layout: vk.PipelineLayout,
@ -101,6 +103,10 @@ pub const VulkanRenderer = struct {
swapchain_image_format: vk.Format, swapchain_image_format: vk.Format,
extent: vk.Extent2D, extent: vk.Extent2D,
min_uniform_buffer_offset: vk.DeviceSize,
model_uniform_alignment: usize,
model_transfer_space: [MAX_OBJECTS]UboModel,
// Synchronisation // Synchronisation
image_available: [MAX_FRAME_DRAWS]vk.Semaphore, image_available: [MAX_FRAME_DRAWS]vk.Semaphore,
render_finished: [MAX_FRAME_DRAWS]vk.Semaphore, render_finished: [MAX_FRAME_DRAWS]vk.Semaphore,
@ -128,7 +134,6 @@ pub const VulkanRenderer = struct {
try self.createSwapchain(); try self.createSwapchain();
try self.createRenderPass(); try self.createRenderPass();
try self.createDescriptorSetLayout(); try self.createDescriptorSetLayout();
try self.createPushConstantRange();
try self.createGraphicsPipeline(); try self.createGraphicsPipeline();
try self.createFramebuffers(); try self.createFramebuffers();
try self.createCommandPool(); try self.createCommandPool();
@ -196,10 +201,12 @@ pub const VulkanRenderer = struct {
self.meshes = [_]Mesh{ first_mesh, second_mesh }; self.meshes = [_]Mesh{ first_mesh, second_mesh };
try self.createCommandBuffers(); try self.createCommandBuffers();
try self.allocateDynamicBufferTransferSpace();
try self.createUniformBuffers(); try self.createUniformBuffers();
try self.createDescriptorPool(); try self.createDescriptorPool();
try self.createDescriptorSets(); try self.createDescriptorSets();
try self.recordCommands();
try self.createSynchronisation(); try self.createSynchronisation();
return self; return self;
@ -213,12 +220,7 @@ pub const VulkanRenderer = struct {
pub fn draw(self: *Self) !void { pub fn draw(self: *Self) !void {
// Wait for given fence to signal (open) from last draw before continuing // Wait for given fence to signal (open) from last draw before continuing
_ = try self.device.waitForFences( _ = try self.device.waitForFences(1, @ptrCast(&self.draw_fences[self.current_frame]), vk.TRUE, std.math.maxInt(u64));
1,
@ptrCast(&self.draw_fences[self.current_frame]),
vk.TRUE,
std.math.maxInt(u64),
);
// Manually reset (close) fences // Manually reset (close) fences
try self.device.resetFences(1, @ptrCast(&self.draw_fences[self.current_frame])); try self.device.resetFences(1, @ptrCast(&self.draw_fences[self.current_frame]));
@ -231,7 +233,6 @@ pub const VulkanRenderer = struct {
.null_handle, .null_handle,
); );
try self.recordCommands(image_index_result.image_index);
try self.updateUniformBuffers(image_index_result.image_index); try self.updateUniformBuffers(image_index_result.image_index);
// -- Submit command buffer to render // -- Submit command buffer to render
@ -280,9 +281,13 @@ pub const VulkanRenderer = struct {
for (0..self.swapchain_images.len) |i| { for (0..self.swapchain_images.len) |i| {
self.device.destroyBuffer(self.vp_uniform_buffer[i], null); self.device.destroyBuffer(self.vp_uniform_buffer[i], null);
self.device.freeMemory(self.vp_uniform_buffer_memory[i], null); self.device.freeMemory(self.vp_uniform_buffer_memory[i], null);
self.device.destroyBuffer(self.model_duniform_buffer[i], null);
self.device.freeMemory(self.model_duniform_buffer_memory[i], null);
} }
self.allocator.free(self.vp_uniform_buffer); self.allocator.free(self.vp_uniform_buffer);
self.allocator.free(self.vp_uniform_buffer_memory); self.allocator.free(self.vp_uniform_buffer_memory);
self.allocator.free(self.model_duniform_buffer);
self.allocator.free(self.model_duniform_buffer_memory);
self.allocator.free(self.descriptor_sets); self.allocator.free(self.descriptor_sets);
for (self.meshes) |mesh| { for (self.meshes) |mesh| {
@ -571,7 +576,16 @@ pub const VulkanRenderer = struct {
.p_immutable_samplers = null, // For texture: can make smapler data immutable by specifying in layout .p_immutable_samplers = null, // For texture: can make smapler data immutable by specifying in layout
}; };
const layout_bindings = [_]vk.DescriptorSetLayoutBinding{vp_layout_binding}; // Model binding info
const model_layout_binding: vk.DescriptorSetLayoutBinding = .{
.binding = 1,
.descriptor_type = .uniform_buffer_dynamic,
.descriptor_count = 1,
.stage_flags = .{ .vertex_bit = true },
.p_immutable_samplers = null,
};
const layout_bindings = [_]vk.DescriptorSetLayoutBinding{ vp_layout_binding, model_layout_binding };
// Create descriptor set layout with given bindings // Create descriptor set layout with given bindings
const layout_create_info: vk.DescriptorSetLayoutCreateInfo = .{ const layout_create_info: vk.DescriptorSetLayoutCreateInfo = .{
@ -583,15 +597,6 @@ pub const VulkanRenderer = struct {
self.descriptor_set_layout = try self.device.createDescriptorSetLayout(&layout_create_info, null); self.descriptor_set_layout = try self.device.createDescriptorSetLayout(&layout_create_info, null);
} }
fn createPushConstantRange(self: *Self) !void {
// Define push constant values (no 'create' needed)
self.push_constant_range = .{
.stage_flags = .{ .vertex_bit = true }, // Shader stage push constant will go to
.offset = 0, // Offset into given data to pass to push constant
.size = @sizeOf(Model), // Size of data being passed
};
}
fn createGraphicsPipeline(self: *Self) !void { fn createGraphicsPipeline(self: *Self) !void {
// Create shader modules // Create shader modules
const vert = try self.device.createShaderModule(&.{ const vert = try self.device.createShaderModule(&.{
@ -752,8 +757,6 @@ pub const VulkanRenderer = struct {
const pipeline_layout_create_info: vk.PipelineLayoutCreateInfo = .{ const pipeline_layout_create_info: vk.PipelineLayoutCreateInfo = .{
.set_layout_count = 1, .set_layout_count = 1,
.p_set_layouts = @ptrCast(&self.descriptor_set_layout), .p_set_layouts = @ptrCast(&self.descriptor_set_layout),
.push_constant_range_count = 1,
.p_push_constant_ranges = @ptrCast(&self.push_constant_range),
}; };
self.pipeline_layout = try self.device.createPipelineLayout(&pipeline_layout_create_info, null); self.pipeline_layout = try self.device.createPipelineLayout(&pipeline_layout_create_info, null);
@ -817,7 +820,6 @@ pub const VulkanRenderer = struct {
const pool_create_info: vk.CommandPoolCreateInfo = .{ const pool_create_info: vk.CommandPoolCreateInfo = .{
// Queue family type that buffers from this command pool will use // Queue family type that buffers from this command pool will use
.queue_family_index = queue_family_indices.graphics_family.?, .queue_family_index = queue_family_indices.graphics_family.?,
.flags = .{ .reset_command_buffer_bit = true },
}; };
// Create a graphics queue family command pool // Create a graphics queue family command pool
@ -859,9 +861,14 @@ pub const VulkanRenderer = struct {
// View projection buffer size // View projection buffer size
const vp_buffer_size: vk.DeviceSize = @sizeOf(UboViewProjection); const vp_buffer_size: vk.DeviceSize = @sizeOf(UboViewProjection);
// Model buffer size
const model_buffer_size: vk.DeviceSize = self.model_uniform_alignment * MAX_OBJECTS;
// One uniform buffer for each image (and by extension, command buffer) // One uniform buffer for each image (and by extension, command buffer)
self.vp_uniform_buffer = try self.allocator.alloc(vk.Buffer, self.swapchain_images.len); self.vp_uniform_buffer = try self.allocator.alloc(vk.Buffer, self.swapchain_images.len);
self.vp_uniform_buffer_memory = try self.allocator.alloc(vk.DeviceMemory, self.swapchain_images.len); self.vp_uniform_buffer_memory = try self.allocator.alloc(vk.DeviceMemory, self.swapchain_images.len);
self.model_duniform_buffer = try self.allocator.alloc(vk.Buffer, self.swapchain_images.len);
self.model_duniform_buffer_memory = try self.allocator.alloc(vk.DeviceMemory, self.swapchain_images.len);
// Create the uniform buffers // Create the uniform buffers
for (0..self.vp_uniform_buffer.len) |i| { for (0..self.vp_uniform_buffer.len) |i| {
@ -875,6 +882,17 @@ pub const VulkanRenderer = struct {
&self.vp_uniform_buffer[i], &self.vp_uniform_buffer[i],
&self.vp_uniform_buffer_memory[i], &self.vp_uniform_buffer_memory[i],
); );
try Utilities.createBuffer(
self.physical_device,
self.instance,
self.device,
model_buffer_size,
.{ .uniform_buffer_bit = true },
.{ .host_visible_bit = true, .host_coherent_bit = true },
&self.model_duniform_buffer[i],
&self.model_duniform_buffer_memory[i],
);
} }
} }
@ -886,8 +904,14 @@ pub const VulkanRenderer = struct {
.descriptor_count = @intCast(self.vp_uniform_buffer.len), .descriptor_count = @intCast(self.vp_uniform_buffer.len),
}; };
// Model pool (dynamic)
const model_pool_size: vk.DescriptorPoolSize = .{
.type = .uniform_buffer_dynamic,
.descriptor_count = @intCast(self.model_duniform_buffer.len),
};
// List of pool sizes // List of pool sizes
const descriptor_pool_sizes = [_]vk.DescriptorPoolSize{vp_pool_size}; const descriptor_pool_sizes = [_]vk.DescriptorPoolSize{ vp_pool_size, model_pool_size };
// Data to create descriptor pool // Data to create descriptor pool
const pool_create_info: vk.DescriptorPoolCreateInfo = .{ const pool_create_info: vk.DescriptorPoolCreateInfo = .{
@ -942,8 +966,27 @@ pub const VulkanRenderer = struct {
.p_texel_buffer_view = undefined, .p_texel_buffer_view = undefined,
}; };
// -- Model descriptor
// Model buffer binding info
const model_buffer_info: vk.DescriptorBufferInfo = .{
.buffer = self.model_duniform_buffer[i],
.offset = 0,
.range = self.model_uniform_alignment,
};
const model_set_write: vk.WriteDescriptorSet = .{
.dst_set = self.descriptor_sets[i],
.dst_binding = 1,
.dst_array_element = 0,
.descriptor_type = .uniform_buffer_dynamic,
.descriptor_count = 1,
.p_buffer_info = @ptrCast(&model_buffer_info),
.p_image_info = undefined,
.p_texel_buffer_view = undefined,
};
// List of descriptor set writes // List of descriptor set writes
const set_writes = [_]vk.WriteDescriptorSet{vp_set_write}; const set_writes = [_]vk.WriteDescriptorSet{ vp_set_write, model_set_write };
// Update the descriptor sets with new buffer/binding info // Update the descriptor sets with new buffer/binding info
self.device.updateDescriptorSets(@intCast(set_writes.len), &set_writes, 0, null); self.device.updateDescriptorSets(@intCast(set_writes.len), &set_writes, 0, null);
@ -952,7 +995,7 @@ pub const VulkanRenderer = struct {
fn updateUniformBuffers(self: *Self, image_index: u32) !void { fn updateUniformBuffers(self: *Self, image_index: u32) !void {
// Copy VP data // Copy VP data
const data = try self.device.mapMemory( var data = try self.device.mapMemory(
self.vp_uniform_buffer_memory[image_index], self.vp_uniform_buffer_memory[image_index],
0, 0,
@sizeOf(UboViewProjection), @sizeOf(UboViewProjection),
@ -962,9 +1005,26 @@ pub const VulkanRenderer = struct {
const vp_data: *UboViewProjection = @ptrCast(@alignCast(data)); const vp_data: *UboViewProjection = @ptrCast(@alignCast(data));
vp_data.* = self.ubo_view_projection; vp_data.* = self.ubo_view_projection;
self.device.unmapMemory(self.vp_uniform_buffer_memory[image_index]); self.device.unmapMemory(self.vp_uniform_buffer_memory[image_index]);
// Copy model data
for (self.meshes, 0..) |mesh, i| {
self.model_transfer_space[i] = mesh.ubo_model;
} }
fn recordCommands(self: *Self, current_image: u32) !void { // Map the list of model data
data = try self.device.mapMemory(
self.model_duniform_buffer_memory[image_index],
0,
self.model_uniform_alignment * self.meshes.len,
.{},
);
const model_data: [*]UboModel = @ptrCast(@alignCast(data));
@memcpy(model_data, self.model_transfer_space[0..self.meshes.len]);
self.device.unmapMemory(self.model_duniform_buffer_memory[image_index]);
}
fn recordCommands(self: *Self) !void {
// Information about how to begin each command // Information about how to begin each command
const buffer_begin_info: vk.CommandBufferBeginInfo = .{ const buffer_begin_info: vk.CommandBufferBeginInfo = .{
// Buffer can be resubmitted when it has already been submitted and is awaiting execution // Buffer can be resubmitted when it has already been submitted and is awaiting execution
@ -987,8 +1047,8 @@ pub const VulkanRenderer = struct {
.framebuffer = undefined, .framebuffer = undefined,
}; };
render_pass_begin_info.framebuffer = self.swapchain_framebuffers[current_image]; for (self.command_buffers, 0..) |command_buffer, i| {
const command_buffer = self.command_buffers[current_image]; render_pass_begin_info.framebuffer = self.swapchain_framebuffers[i];
// Start recording commands to command buffer // Start recording commands to command buffer
try command_buffer.beginCommandBuffer(&buffer_begin_info); try command_buffer.beginCommandBuffer(&buffer_begin_info);
@ -1001,7 +1061,7 @@ pub const VulkanRenderer = struct {
command_buffer.setViewport(0, 1, @ptrCast(&self.viewport)); command_buffer.setViewport(0, 1, @ptrCast(&self.viewport));
command_buffer.setScissor(0, 1, @ptrCast(&self.scissor)); command_buffer.setScissor(0, 1, @ptrCast(&self.scissor));
for (self.meshes) |mesh| { for (self.meshes, 0..) |mesh, j| {
// Bind pipeline to be used in render pass // Bind pipeline to be used in render pass
command_buffer.bindPipeline(.graphics, self.graphics_pipeline); command_buffer.bindPipeline(.graphics, self.graphics_pipeline);
@ -1015,14 +1075,8 @@ pub const VulkanRenderer = struct {
// Bind mesh index buffer, with 0 offset and using the uint32 type // Bind mesh index buffer, with 0 offset and using the uint32 type
command_buffer.bindIndexBuffer(mesh.index_buffer, 0, .uint32); command_buffer.bindIndexBuffer(mesh.index_buffer, 0, .uint32);
// Push constants to given shader stage directly (no buffer) // Dynamic offset amount
command_buffer.pushConstants( const dynamic_offset: u32 = @intCast(self.model_uniform_alignment * j);
self.pipeline_layout,
.{ .vertex_bit = true }, // Stage to push constants to
0, // Offset of push constants to update
@sizeOf(Model), // Size of data being pushed
@ptrCast(&mesh.ubo_model.model), // Actual data being pushed (can be array)
);
// Bind descriptor sets // Bind descriptor sets
command_buffer.bindDescriptorSets( command_buffer.bindDescriptorSets(
@ -1030,9 +1084,9 @@ pub const VulkanRenderer = struct {
self.pipeline_layout, self.pipeline_layout,
0, 0,
1, 1,
@ptrCast(&self.descriptor_sets[current_image]), @ptrCast(&self.descriptor_sets[i]),
0, 1,
null, @ptrCast(&dynamic_offset),
); );
// Execute a pipeline // Execute a pipeline
@ -1046,6 +1100,7 @@ pub const VulkanRenderer = struct {
// Stop recording to command buffer // Stop recording to command buffer
try command_buffer.endCommandBuffer(); try command_buffer.endCommandBuffer();
} }
}
fn getPhysicalDevice(self: *Self) !void { fn getPhysicalDevice(self: *Self) !void {
var pdev_count: u32 = 0; var pdev_count: u32 = 0;
@ -1065,6 +1120,21 @@ pub const VulkanRenderer = struct {
// TODO Obviously needs to be something else // TODO Obviously needs to be something else
unreachable; unreachable;
} }
// Get properties of our new device
const device_props = self.instance.getPhysicalDeviceProperties(self.physical_device);
self.min_uniform_buffer_offset = device_props.limits.min_uniform_buffer_offset_alignment;
}
fn allocateDynamicBufferTransferSpace(self: *Self) !void {
// TODO Needed in zig (we have align())?
// Calculate alignment of model data
self.model_uniform_alignment =
(@sizeOf(UboModel) + self.min_uniform_buffer_offset - 1) & ~(self.min_uniform_buffer_offset - 1);
// Create space in memory to hold dynamic buffer that is aligned to our required alignment and holds MAX_OBJECTS
// self.model_transfer_space = try self.allocator.create(UboModel);
} }
fn getRequiredExtensions(self: Self) ![][*:0]const u8 { fn getRequiredExtensions(self: Self) ![][*:0]const u8 {

532
vk.xml

File diff suppressed because it is too large Load diff