Pass uniform buffer via push constants, yay

This commit is contained in:
sergeypdev 2024-12-05 02:40:12 +04:00
parent 3cab3f7946
commit 4b6b859f40
7 changed files with 82 additions and 146 deletions

View File

@ -28,7 +28,8 @@ layout(location = 0) out vec3 VertexColor;
void main() { void main() {
VertexColor = colors[gl_VertexIndex]; VertexColor = colors[gl_VertexIndex];
gl_Position = vec4(positions[gl_VertexIndex], 0.0, 1.0);
gl_Position = PushConstants.camera_matrices.view_projection * vec4(positions[gl_VertexIndex], 0.0, 1.0);
} }
#endif #endif

View File

@ -324,11 +324,12 @@ fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !LoadedShaderProgram {
try program.serialize(&serializer); try program.serialize(&serializer);
const pipeline_layout = try self.gc.device.createPipelineLayout(&.{ const pipeline_layout = try self.gc.device.createPipelineLayout(&.{
.push_constant_range_count = 1,
.p_push_constant_ranges = &.{ .p_push_constant_ranges = &.{
vk.PushConstantRange{ vk.PushConstantRange{
.stage_flags = .{ .vertex_bit = true }, .stage_flags = .{ .vertex_bit = true },
.offset = 0, .offset = 0,
.size = 1, .size = 8,
}, },
}, },
}, null); }, null);
@ -473,6 +474,7 @@ const NullShader = LoadedShader{
const NullShaderProgram = LoadedShaderProgram{ const NullShaderProgram = LoadedShaderProgram{
.pipeline = .null_handle, .pipeline = .null_handle,
.layout = .null_handle,
}; };
const NullMesh = LoadedMesh{ const NullMesh = LoadedMesh{
@ -762,6 +764,7 @@ const LoadedShader = struct {
const LoadedShaderProgram = struct { const LoadedShaderProgram = struct {
pipeline: vk.Pipeline, pipeline: vk.Pipeline,
layout: vk.PipelineLayout,
}; };
pub const LoadedMesh = struct { pub const LoadedMesh = struct {

View File

@ -272,9 +272,9 @@ pub const Buffer = struct {
allocation: vma.Allocation, allocation: vma.Allocation,
allocation_info: vma.c.VmaAllocationInfo, allocation_info: vma.c.VmaAllocationInfo,
sync_state: SyncState, sync_state: SyncState = .{},
pub fn sync(self: *const Buffer, cmds: CommandBuffer, masks: SyncBarrierMasks) !void { pub fn sync(self: *Buffer, cmds: CommandBuffer, masks: SyncBarrierMasks) !void {
if (self.sync_state.sync(masks, false)) |req| { if (self.sync_state.sync(masks, false)) |req| {
cmds.pipelineBarrier2(&vk.DependencyInfo{ cmds.pipelineBarrier2(&vk.DependencyInfo{
.buffer_memory_barrier_count = 1, .buffer_memory_barrier_count = 1,
@ -299,9 +299,9 @@ pub const Buffer = struct {
try vma.flushAllocation(self.gc.vma_allocator, self.allocation, offset, size); try vma.flushAllocation(self.gc.vma_allocator, self.allocation, offset, size);
} }
pub fn getAllocationMemoryProperties(self: *Buffer) !vk.MemoryPropertyFlags { pub fn getAllocationMemoryProperties(self: *Buffer) vk.MemoryPropertyFlags {
var mem_prop_flags = vk.MemoryPropertyFlags{}; var mem_prop_flags = vk.MemoryPropertyFlags{};
try vma.getAllocationMemoryProperties(self.gc.vma_allocator, self.allocation, &mem_prop_flags); vma.getAllocationMemoryProperties(self.gc.vma_allocator, self.allocation, &mem_prop_flags);
return mem_prop_flags; return mem_prop_flags;
} }
@ -362,12 +362,14 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL
self.device_info = try selectPhysicalDevice(self.instance, self.surface, physical_devices); self.device_info = try selectPhysicalDevice(self.instance, self.surface, physical_devices);
const queue_config = try selectQueues(self.instance, self.device_info.physical_device); const queue_config = try selectQueues(self.instance, self.device_info.physical_device);
const device_create_config = vk.DeviceCreateInfo{ // p_next is not const in PhysicalDeviceVulkan12Features, so pass it like this
.p_next = &vk.PhysicalDeviceVulkan13Features{ var vulkan13_device_features = vk.PhysicalDeviceVulkan13Features{
.dynamic_rendering = vk.TRUE, .dynamic_rendering = vk.TRUE,
.synchronization_2 = vk.TRUE, .synchronization_2 = vk.TRUE,
.maintenance_4 = vk.TRUE, .maintenance_4 = vk.TRUE,
}, };
const device_create_config = vk.DeviceCreateInfo{
.p_queue_create_infos = &queue_config.queue_create_info, .p_queue_create_infos = &queue_config.queue_create_info,
.queue_create_info_count = queue_config.queue_count, .queue_create_info_count = queue_config.queue_count,
.p_enabled_features = &self.device_info.features, .p_enabled_features = &self.device_info.features,
@ -375,6 +377,10 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL
.enabled_layer_count = @intCast(vk_layers.len), .enabled_layer_count = @intCast(vk_layers.len),
.pp_enabled_extension_names = @ptrCast((&device_extensions).ptr), .pp_enabled_extension_names = @ptrCast((&device_extensions).ptr),
.enabled_extension_count = @intCast(device_extensions.len), .enabled_extension_count = @intCast(device_extensions.len),
.p_next = &vk.PhysicalDeviceVulkan12Features{
.buffer_device_address = vk.TRUE,
.p_next = &vulkan13_device_features,
},
}; };
const device_handle = try self.instance.createDevice(self.device_info.physical_device, &device_create_config, null); const device_handle = try self.instance.createDevice(self.device_info.physical_device, &device_create_config, null);
@ -384,6 +390,9 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL
self.device = Device.init(device_handle, &self.vkd); self.device = Device.init(device_handle, &self.vkd);
self.vma_allocator = vma.createAllocator(&.{ self.vma_allocator = vma.createAllocator(&.{
.flags = .{
.buffer_device_address_bit = true,
},
.instance = instance_handle, .instance = instance_handle,
.physical_device = self.device_info.physical_device, .physical_device = self.device_info.physical_device,
.device = device_handle, .device = device_handle,
@ -432,7 +441,9 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL
pub fn createBuffer(self: *GraphicsContext, create_info: *const vk.BufferCreateInfo, allocation_create_info: *const vma.AllocationCreateInfo) !Buffer { pub fn createBuffer(self: *GraphicsContext, create_info: *const vk.BufferCreateInfo, allocation_create_info: *const vma.AllocationCreateInfo) !Buffer {
var result: Buffer = undefined; var result: Buffer = undefined;
result.handle = try vma.createBuffer(self.vma_allocator, create_info, allocation_create_info, result.allocation, result.allocation_info); result.gc = self;
result.handle = try vma.createBuffer(self.vma_allocator, create_info, allocation_create_info, &result.allocation, &result.allocation_info);
result.sync_state = .{};
return result; return result;
} }

View File

@ -4,6 +4,7 @@ const GraphicsContext = @import("GraphicsContext.zig");
const vk = @import("vk"); const vk = @import("vk");
const a = @import("asset_manifest"); const a = @import("asset_manifest");
const za = @import("zalgebra"); const za = @import("zalgebra");
const Vec3 = za.Vec3;
const Mat4 = za.Mat4; const Mat4 = za.Mat4;
const Render2 = @This(); const Render2 = @This();
@ -24,6 +25,7 @@ pub fn init(assetman: *AssetManager, gc: *GraphicsContext) !Render2 {
.assetman = assetman, .assetman = assetman,
.gc = gc, .gc = gc,
.command_pool = try gc.queues.graphics.createCommandPool(.{ .reset_command_buffer_bit = true }), .command_pool = try gc.queues.graphics.createCommandPool(.{ .reset_command_buffer_bit = true }),
.camera_matrices_buffer = undefined,
}; };
errdefer self.command_pool.deinit(); errdefer self.command_pool.deinit();
@ -44,11 +46,10 @@ pub fn init(assetman: *AssetManager, gc: *GraphicsContext) !Render2 {
.mapped_bit = true, .mapped_bit = true,
}, },
}); });
errdefer self.camera_matrices_buffer.deinit(self.gc); // const mem_props = self.camera_matrices_buffer.getAllocationMemoryProperties();
const mem_props = try self.camera_matrices_buffer.getAllocationMemoryProperties();
// TODO: Assuming unified memory or resizable bar right now, should not assume that // // TODO: Assuming unified memory or resizable bar right now, should not assume that
std.debug.assert(mem_props.contains(.{ .host_visible_bit = true })); // std.debug.assert(mem_props.contains(.{ .host_visible_bit = true }));
return self; return self;
} }
@ -73,10 +74,12 @@ pub fn draw(self: *Render2) !void {
{ {
try self.camera_matrices_buffer.sync(cmds, .{ .stage_mask = .{ .host_bit = true }, .access_mask = .{ .host_write_bit = true } }); try self.camera_matrices_buffer.sync(cmds, .{ .stage_mask = .{ .host_bit = true }, .access_mask = .{ .host_write_bit = true } });
const c_matrices: [*c]CameraMatrices = @alignCast(@ptrCast(self.camera_matrices_buffer.allocation_info.pMappedData.?)); const c_matrices: [*c]CameraMatrices = @alignCast(@ptrCast(self.camera_matrices_buffer.allocation_info.pMappedData.?));
const matrices: *CameraMatrices = c_matrices[0..MAX_FRAME_LAG][self.frame]; const matrices: *CameraMatrices = &c_matrices[0..MAX_FRAME_LAG][self.frame];
const view = Mat4.identity(); const view = Mat4.lookAt(Vec3.new(0, 0, 1), Vec3.new(0, 0, 0), Vec3.new(0, 1, 0));
const projection = Mat4.perspective(60, 1.0 / 16.0, 0.1, 1000); const fwidth: f32 = @floatFromInt(self.gc.swapchain_extent.width);
const fheight: f32 = @floatFromInt(self.gc.swapchain_extent.height);
const projection = Mat4.perspective(60, fwidth / fheight, 0.1, 1000);
const view_projection = projection.mul(view); const view_projection = projection.mul(view);
matrices.* = .{ matrices.* = .{
.view = view, .view = view,
@ -109,7 +112,12 @@ pub fn draw(self: *Render2) !void {
}); });
defer cmds.endRendering(); defer cmds.endRendering();
cmds.bindPipeline(.graphics, self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.triangle).pipeline); const triangle = self.assetman.resolveShaderProgram(a.ShaderPrograms.shaders.triangle);
cmds.bindPipeline(.graphics, triangle.pipeline);
const device_address = self.gc.device.getBufferDeviceAddress(&.{ .buffer = self.camera_matrices_buffer.handle });
cmds.pushConstants(triangle.layout, .{ .vertex_bit = true }, 0, 8, &device_address);
cmds.setViewportWithCount(1, &.{vk.Viewport{ cmds.setViewportWithCount(1, &.{vk.Viewport{
.x = 0, .x = 0,
@ -124,7 +132,6 @@ pub fn draw(self: *Render2) !void {
.extent = self.gc.swapchain_extent, .extent = self.gc.swapchain_extent,
}}); }});
try self.camera_matrices_buffer.sync(cmds, .{ .stage_mask = .{ .vertex_shader_bit = true }, .access_mask = .{ .shader_read_bit = true } });
cmds.draw(3, 1, 0, 0); cmds.draw(3, 1, 0, 0);
} }

28
src/RenderGraph.zig Normal file
View File

@ -0,0 +1,28 @@
const std = @import("std");
const vk = @import("vk");
const GraphicsContext = @import("GraphicsContext.zig");
pub const RenderGraph = @This();
pub const PassSetupContext = struct {};
pub const Texture2DHandle = struct { id: u64 };
pub fn declareTexture2D(comptime name: []const u8) Texture2DHandle {
return Texture2DHandle{ .id = std.hash.Wyhash.hash(0, name) };
}
test "RenderGraph" {
var GraphBuilder: *RenderGraph = undefined;
const GBuffer = RenderGraph.declareTexture2D("GBuffer");
GraphBuilder.addPass(
struct {
fn setup(ctx: *PassSetupContext) void {
ctx.createTexture2D(GBuffer, 1024, 1024, vk.Format.r8g8b8a8_unorm);
}
fn render(ctx: *GraphicsContext.CommandBuffer) void {
ctx.getTexture2D(GBuffer);
}
},
);
}

View File

@ -16,7 +16,7 @@ pub const AllocatorCreateFlags = packed struct {
khr_bind_memory2_bit: bool = false, khr_bind_memory2_bit: bool = false,
ext_memory_budget_bit: bool = false, ext_memory_budget_bit: bool = false,
amd_device_coherent_memory_bit: bool = false, amd_device_coherent_memory_bit: bool = false,
jbuffer_device_address_bit: bool = false, buffer_device_address_bit: bool = false,
ext_memory_priority_bit: bool = false, ext_memory_priority_bit: bool = false,
khr_maintenance4_bit: bool = false, khr_maintenance4_bit: bool = false,
khr_maintenance5_bit: bool = false, khr_maintenance5_bit: bool = false,
@ -206,7 +206,7 @@ pub fn destroyImage(allocator: Allocator, image: vk.Image, allocation: Allocatio
c.vmaDestroyImage(allocator, @as(*const c.VkImage, @ptrCast(&image)).*, allocation); c.vmaDestroyImage(allocator, @as(*const c.VkImage, @ptrCast(&image)).*, allocation);
} }
pub fn flushAllocation(allocator: Allocator, allocation: Allocation, offset: vk.DeviceSize, size: vk.DeviceSize) void { pub fn flushAllocation(allocator: Allocator, allocation: Allocation, offset: vk.DeviceSize, size: vk.DeviceSize) Error!void {
try checkError(c.vmaFlushAllocation(allocator, allocation, offset, size)); try checkError(c.vmaFlushAllocation(allocator, allocation, offset, size));
} }
@ -216,5 +216,7 @@ pub fn flushAllocations(allocator: Allocator, allocations: []const Allocation, o
} }
pub fn getAllocationMemoryProperties(allocator: Allocator, allocation: Allocation, mem_prop_flags: *vk.MemoryPropertyFlags) void { pub fn getAllocationMemoryProperties(allocator: Allocator, allocation: Allocation, mem_prop_flags: *vk.MemoryPropertyFlags) void {
try checkError(c.vmaGetAllocationMemoryProperties(allocator, allocation, mem_prop_flags)); var result: c.VkMemoryPropertyFlags = 0;
c.vmaGetAllocationMemoryProperties(allocator, allocation, &result);
mem_prop_flags.* = @bitCast(result);
} }

View File

@ -21,8 +21,6 @@ const c = @cImport({
@cInclude("stb_image.h"); @cInclude("stb_image.h");
@cInclude("ispc_texcomp.h"); @cInclude("ispc_texcomp.h");
@cInclude("spirv-cross/spirv_cross_c.h");
}); });
const ASSET_MAX_BYTES = 1024 * 1024 * 1024; const ASSET_MAX_BYTES = 1024 * 1024 * 1024;
@ -55,83 +53,6 @@ const Args = struct {
output_dir: []const u8 = "", output_dir: []const u8 = "",
}; };
// Single producer, multiple consumers
fn JobQueue(comptime JobPayload: type) type {
return struct {
const Self = @This();
pub const JobFn = fn (payload: *JobPayload) void;
pub const Job = struct {
payload: JobPayload,
func: JobFn,
};
running: std.atomic.Value(bool) = std.atomic.Value(bool).init(true),
read: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
write: std.atomic.Value(u32) = std.atomic.Value(u32).init(0),
buffer: []Job,
pub fn init(buffer: []Job) JobQueue {
return JobQueue{
.buffer = buffer,
};
}
pub fn pushJobs(self: *Self, jobs: []const Job) void {
var left_to_write: usize = jobs.len;
while (left_to_write > 0) {
const write = self.write.load(.unordered);
if (write >= (self.read.load(.unordered) + self.buffer.len)) {
continue;
}
const read = self.read.load(.acquire);
const to_write = @min(self.buffer.len - (write - read), left_to_write);
for (0..to_write) |i| {
self.buffer[(write + i) % self.buffer.len] = jobs[i];
}
_ = self.write.fetchAdd(to_write, .release);
left_to_write -= to_write;
}
}
pub fn takeJob(self: *Self) Job {
while (true) {
const read = self.read.load(.acquire);
if (self.write.load(.acquire) - read > 0) {
if (self.read.cmpxchgStrong(read, read + 1, .release, .acquire)) {
return self.buffer[read % self.buffer.len];
}
}
}
}
pub fn workerEntry(userdata: *anyopaque) void {
const self: Self = @ptrCast(userdata);
while (self.running.load(.acquire)) {
const job = self.takeJob();
job.func(job.payload);
}
}
};
}
const ProcessAssetJobPayload = struct {
allocator: std.mem.Allocator,
asset_type: AssetType,
rel_input: []const u8,
output_dir: std.fs.Dir,
dep_file: ?[]const u8,
};
const ProcessAssetJobQueue = JobQueue(ProcessAssetJobPayload);
fn parseArgs(allocator: std.mem.Allocator) !Args { fn parseArgs(allocator: std.mem.Allocator) !Args {
var args = try std.process.argsWithAllocator(allocator); var args = try std.process.argsWithAllocator(allocator);
defer args.deinit(); defer args.deinit();
@ -209,27 +130,14 @@ pub fn main() !void {
var buf_asset_list_writer = std.io.bufferedWriter(std.io.getStdOut().writer()); var buf_asset_list_writer = std.io.bufferedWriter(std.io.getStdOut().writer());
const asset_list_writer = buf_asset_list_writer.writer(); const asset_list_writer = buf_asset_list_writer.writer();
var queue = ProcessAssetJobQueue.init(try allocator.alloc(ProcessAssetJobQueue.Job, 1024));
const num_workers = std.Thread.getCpuCount() - 1;
const worker_threads = allocator.alloc(std.Thread, num_workers);
for (0..num_workers) |i| {
worker_threads[i] = try std.Thread.spawn(.{}, ProcessAssetJobQueue.workerEntry, .{&queue});
}
std.log.debug("type: {s}, rel_input: {s}, output_dir: {s}", .{ @tagName(asset_type), rel_input, rel_output }); std.log.debug("type: {s}, rel_input: {s}, output_dir: {s}", .{ @tagName(asset_type), rel_input, rel_output });
queue.pushJobs(&[]ProcessAssetJobQueue.Job{.{
.payload = .{
.allocator = allocator,
.asset_type = asset_type,
.rel_input = rel_input,
.output_dir = output_dir,
.dep_file = args.dep_file,
},
.func = processAsset,
}});
switch (asset_type) {
.Scene => try processScene(allocator, rel_input, output_dir, asset_list_writer),
.ShaderProgram => try processShaderProgram(allocator, rel_input, output_dir, args.dep_file, asset_list_writer),
.Texture => try processTextureFromFile(allocator, rel_input, output_dir, asset_list_writer),
else => unreachable,
}
try buf_asset_list_writer.flush(); try buf_asset_list_writer.flush();
if (args.dep_file) |dep_file_path| { if (args.dep_file) |dep_file_path| {
@ -238,15 +146,6 @@ pub fn main() !void {
} }
} }
fn processAsset(payload: ProcessAssetJobPayload) void {
switch (payload.asset_type) {
.Scene => try processScene(payload.allocator, payload.rel_input, payload.output_dir, payload.asset_list_writer),
.ShaderProgram => try processShaderProgram(payload.allocator, payload.rel_input, payload.output_dir, payload.dep_file, payload.asset_list_writer),
.Texture => try processTextureFromFile(payload.allocator, payload.rel_input, payload.output_dir, payload.asset_list_writer),
else => unreachable,
}
}
fn copyFile(_type: AssetType, input: []const u8, output_dir: std.fs.Dir, asset_list_writer: anytype) !void { fn copyFile(_type: AssetType, input: []const u8, output_dir: std.fs.Dir, asset_list_writer: anytype) !void {
const asset_path = AssetPath{ .simple = input }; const asset_path = AssetPath{ .simple = input };
@ -723,21 +622,6 @@ fn processShader(allocator: std.mem.Allocator, flags: []const []const u8, input:
try file.writeAll(old_depfile_contents); try file.writeAll(old_depfile_contents);
} }
// {
// var spvc_context: c.spvc_context = null;
// _ = c.spvc_context_create(&spvc_context);
// defer c.spvc_context_destroy(spvc_context);
// var ir: c.spvc_parsed_ir = null;
// // c.spvc_context_parse_spirv(spvc_context, spirv: [*c]const c.SpvId, word_count: usize, &ir);
// var compiler_glsl: c.spvc_compiler = null;
// c.spvc_context_create_compiler(spvc_context, c.SPVC_BACKEND_GLSL, ir, c.SPVC_CAPTURE_MODE_TAKE_OWNERSHIP, &compiler_glsl);
// var resources: c.spvc_resources = null;
// c.spvc_compiler_create_shader_resources(compiler_glsl, &resources);
// }
return result.stdout; return result.stdout;
} }