diff --git a/assets/shaders/triangle.glsl b/assets/shaders/triangle.glsl index 177792f..ba6fc46 100644 --- a/assets/shaders/triangle.glsl +++ b/assets/shaders/triangle.glsl @@ -28,7 +28,8 @@ layout(location = 0) out vec3 VertexColor; void main() { 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 diff --git a/src/AssetManager.zig b/src/AssetManager.zig index d707f61..a437eb2 100644 --- a/src/AssetManager.zig +++ b/src/AssetManager.zig @@ -324,11 +324,12 @@ fn loadShaderProgramErr(self: *AssetManager, id: AssetId) !LoadedShaderProgram { try program.serialize(&serializer); const pipeline_layout = try self.gc.device.createPipelineLayout(&.{ + .push_constant_range_count = 1, .p_push_constant_ranges = &.{ vk.PushConstantRange{ .stage_flags = .{ .vertex_bit = true }, .offset = 0, - .size = 1, + .size = 8, }, }, }, null); @@ -473,6 +474,7 @@ const NullShader = LoadedShader{ const NullShaderProgram = LoadedShaderProgram{ .pipeline = .null_handle, + .layout = .null_handle, }; const NullMesh = LoadedMesh{ @@ -762,6 +764,7 @@ const LoadedShader = struct { const LoadedShaderProgram = struct { pipeline: vk.Pipeline, + layout: vk.PipelineLayout, }; pub const LoadedMesh = struct { diff --git a/src/GraphicsContext.zig b/src/GraphicsContext.zig index 137f603..3fff4f5 100644 --- a/src/GraphicsContext.zig +++ b/src/GraphicsContext.zig @@ -272,9 +272,9 @@ pub const Buffer = struct { allocation: vma.Allocation, 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| { cmds.pipelineBarrier2(&vk.DependencyInfo{ .buffer_memory_barrier_count = 1, @@ -299,9 +299,9 @@ pub const Buffer = struct { 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{}; - 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; } @@ -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); const queue_config = try selectQueues(self.instance, self.device_info.physical_device); + // p_next is not const in PhysicalDeviceVulkan12Features, so pass it like this + var vulkan13_device_features = vk.PhysicalDeviceVulkan13Features{ + .dynamic_rendering = vk.TRUE, + .synchronization_2 = vk.TRUE, + .maintenance_4 = vk.TRUE, + }; + const device_create_config = vk.DeviceCreateInfo{ - .p_next = &vk.PhysicalDeviceVulkan13Features{ - .dynamic_rendering = vk.TRUE, - .synchronization_2 = vk.TRUE, - .maintenance_4 = vk.TRUE, - }, .p_queue_create_infos = &queue_config.queue_create_info, .queue_create_info_count = queue_config.queue_count, .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), .pp_enabled_extension_names = @ptrCast((&device_extensions).ptr), .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); @@ -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.vma_allocator = vma.createAllocator(&.{ + .flags = .{ + .buffer_device_address_bit = true, + }, .instance = instance_handle, .physical_device = self.device_info.physical_device, .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 { 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; } diff --git a/src/Render2.zig b/src/Render2.zig index 7aace72..bf107a8 100644 --- a/src/Render2.zig +++ b/src/Render2.zig @@ -4,6 +4,7 @@ const GraphicsContext = @import("GraphicsContext.zig"); const vk = @import("vk"); const a = @import("asset_manifest"); const za = @import("zalgebra"); +const Vec3 = za.Vec3; const Mat4 = za.Mat4; const Render2 = @This(); @@ -24,6 +25,7 @@ pub fn init(assetman: *AssetManager, gc: *GraphicsContext) !Render2 { .assetman = assetman, .gc = gc, .command_pool = try gc.queues.graphics.createCommandPool(.{ .reset_command_buffer_bit = true }), + .camera_matrices_buffer = undefined, }; errdefer self.command_pool.deinit(); @@ -44,11 +46,10 @@ pub fn init(assetman: *AssetManager, gc: *GraphicsContext) !Render2 { .mapped_bit = true, }, }); - errdefer self.camera_matrices_buffer.deinit(self.gc); - const mem_props = try self.camera_matrices_buffer.getAllocationMemoryProperties(); + // const mem_props = self.camera_matrices_buffer.getAllocationMemoryProperties(); - // TODO: Assuming unified memory or resizable bar right now, should not assume that - std.debug.assert(mem_props.contains(.{ .host_visible_bit = true })); + // // TODO: Assuming unified memory or resizable bar right now, should not assume that + // std.debug.assert(mem_props.contains(.{ .host_visible_bit = true })); 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 } }); 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 projection = Mat4.perspective(60, 1.0 / 16.0, 0.1, 1000); + const view = Mat4.lookAt(Vec3.new(0, 0, 1), Vec3.new(0, 0, 0), Vec3.new(0, 1, 0)); + 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); matrices.* = .{ .view = view, @@ -109,7 +112,12 @@ pub fn draw(self: *Render2) !void { }); 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{ .x = 0, @@ -124,7 +132,6 @@ pub fn draw(self: *Render2) !void { .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); } diff --git a/src/RenderGraph.zig b/src/RenderGraph.zig new file mode 100644 index 0000000..0ccecf3 --- /dev/null +++ b/src/RenderGraph.zig @@ -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); + } + }, + ); +} diff --git a/src/vma.zig b/src/vma.zig index bc84e8c..f2efbf4 100644 --- a/src/vma.zig +++ b/src/vma.zig @@ -16,7 +16,7 @@ pub const AllocatorCreateFlags = packed struct { khr_bind_memory2_bit: bool = false, ext_memory_budget_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, khr_maintenance4_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); } -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)); } @@ -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 { - try checkError(c.vmaGetAllocationMemoryProperties(allocator, allocation, mem_prop_flags)); + var result: c.VkMemoryPropertyFlags = 0; + c.vmaGetAllocationMemoryProperties(allocator, allocation, &result); + mem_prop_flags.* = @bitCast(result); } diff --git a/tools/asset_compiler.zig b/tools/asset_compiler.zig index 55aeced..12f014f 100644 --- a/tools/asset_compiler.zig +++ b/tools/asset_compiler.zig @@ -21,8 +21,6 @@ const c = @cImport({ @cInclude("stb_image.h"); @cInclude("ispc_texcomp.h"); - - @cInclude("spirv-cross/spirv_cross_c.h"); }); const ASSET_MAX_BYTES = 1024 * 1024 * 1024; @@ -55,83 +53,6 @@ const Args = struct { 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 { var args = try std.process.argsWithAllocator(allocator); defer args.deinit(); @@ -209,27 +130,14 @@ pub fn main() !void { var buf_asset_list_writer = std.io.bufferedWriter(std.io.getStdOut().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 }); - 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(); 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 { 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); } - // { - // 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; }