From 2215d082c4814a98dcd7ed2ca70239b369a37ce0 Mon Sep 17 00:00:00 2001 From: sergeypdev Date: Sat, 21 Dec 2024 19:28:02 +0400 Subject: [PATCH] Refactor descriptor arrays, add sampler descriptor array --- assets/shaders/out.spv.d | 1 - src/AssetManager.zig | 66 ++++++++++- src/DescriptorManager.zig | 124 +++++++++++++++++++- src/GraphicsContext.zig | 240 +++++++++++++++----------------------- src/Render2.zig | 149 +++-------------------- src/game.zig | 4 +- 6 files changed, 299 insertions(+), 285 deletions(-) delete mode 100644 assets/shaders/out.spv.d diff --git a/assets/shaders/out.spv.d b/assets/shaders/out.spv.d deleted file mode 100644 index 784f2bb..0000000 --- a/assets/shaders/out.spv.d +++ /dev/null @@ -1 +0,0 @@ -out.spv: post_process.glsl diff --git a/src/AssetManager.zig b/src/AssetManager.zig index e94e694..c90ee42 100644 --- a/src/AssetManager.zig +++ b/src/AssetManager.zig @@ -58,10 +58,15 @@ loaded_assets: std.AutoHashMapUnmanaged(AssetId, LoadedAsset) = .{}, rw_lock: std.Thread.RwLock.DefaultRwLock = .{}, asset_watcher: AssetWatcher = undefined, +// texture_heap: BuddyAllocator, vertex_heap: VertexBufferHeap, gc: *GraphicsContext, descriptorman: *DescriptorManager, +// For uploads +queue: *GraphicsContext.QueueInstance, +command_pool: GraphicsContext.CommandPool, + const AssetWatcher = struct { assetman: *AssetManager, fba: std.heap.FixedBufferAllocator, @@ -150,18 +155,31 @@ const AssetWatcher = struct { } }; -pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, gc: *GraphicsContext, descriptorman: *DescriptorManager) AssetManager { +pub fn init(allocator: std.mem.Allocator, frame_arena: std.mem.Allocator, gc: *GraphicsContext, descriptorman: *DescriptorManager) !AssetManager { var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; const exe_dir_path = std.fs.selfExeDirPath(&buf) catch @panic("can't find self exe dir path"); const exe_dir = std.fs.openDirAbsolute(exe_dir_path, .{}) catch @panic("can't open self exe dir path"); + const queue = blk: { + if (gc.queues.transfer) |*q| { + break :blk q; + } + if (gc.queues.compute) |*q| { + break :blk q; + } + break :blk &gc.queues.graphics; + }; + return .{ .allocator = allocator, .frame_arena = frame_arena, .exe_dir = exe_dir, + // .texture_heap = BuddyAllocator.init(allocator, 64, 22), // 256MB .vertex_heap = VertexBufferHeap.init(allocator) catch @panic("OOM"), .gc = gc, .descriptorman = descriptorman, + .queue = queue, + .command_pool = try queue.createCommandPool(.{ .transient_bit = true }), }; } @@ -631,6 +649,51 @@ fn loadTextureErr(self: *AssetManager, id: AssetId) !LoadedTexture { var texture = try formats.Texture.fromBuffer(self.allocator, data.bytes); defer texture.free(self.allocator); + const format: vk.Format = switch (texture.header.format) { + .bc7 => vk.Format.bc7_unorm_block, + .bc6 => vk.Format.bc6h_ufloat_block, + .bc5 => vk.Format.bc5_unorm_block, + }; + + const image = try self.gc.device.createImage(&.{ + .image_type = .@"2d", + .format = format, + .extent = vk.Extent3D{ .width = texture.header.padded_width, .height = texture.header.padded_height, .depth = 1 }, + .mip_levels = texture.header.mip_count, + .array_layers = 1, + .samples = .{ .@"1_bit" = true }, + .tiling = .optimal, + .usage = .{ + .sampled_bit = true, + .transfer_dst_bit = true, + }, + .sharing_mode = .exclusive, + .initial_layout = .undefined, + }, null); + errdefer self.gc.device.destroyImage(image, null); + + const mem_reqs = self.gc.getImageMemoryRequirements(image); + const allocation = try self.texture_heap.alloc(mem_reqs.size); + errdefer self.texture_heap.free(allocation); + + // TODO: for now just pray alignment requirements are not crazy + std.debug.assert(std.mem.isAligned(allocation.offset, mem_reqs.alignment)); + + var data_size: u64 = 0; + for (texture.data) |bytes| { + // TODO: handle copy offset alignment + data_size += bytes.len; + } + + const staging_buffer = try self.gc.device.createBuffer(&.{ + .usage = .{ .transfer_src_bit = true }, + .sharing_mode = .exclusive, + .size = data_size, + }, null); + errdefer self.gc.device.destroyBuffer(staging_buffer, null); + + // const staging_mem_reqs = self.gc.device.getBufferMemoryRequirements(staging_buffer); + var name: gl.GLuint = 0; gl.createTextures(gl.TEXTURE_2D, 1, &name); if (name == 0) { @@ -1600,6 +1663,7 @@ fn freeAsset(self: *AssetManager, asset: *LoadedAsset) void { .shaderProgram => |*program| { self.gc.queues.graphics.mu.lock(); defer self.gc.queues.graphics.mu.unlock(); + // TODO: wait for async compute idle to self.gc.device.queueWaitIdle(self.gc.queues.graphics.handle) catch @panic("Wait Idle failed"); diff --git a/src/DescriptorManager.zig b/src/DescriptorManager.zig index 8bb783e..5aa89e9 100644 --- a/src/DescriptorManager.zig +++ b/src/DescriptorManager.zig @@ -44,18 +44,36 @@ pub fn DescriptorT(comptime T: type) type { }; } +pub const ResourceTypeEnum = enum { + sampled_image, + sampler, + + pub fn getDescriptorType(self: ResourceTypeEnum) vk.DescriptorType { + return switch (self) { + .sampled_image => vk.DescriptorType.sampled_image, + .sampler => vk.DescriptorType.sampler, + }; + } +}; + +pub const ResourceType = union(ResourceTypeEnum) { + sampled_image: SampledImageDescriptorData, + sampler: vk.Sampler, +}; + pub const SampledImageDescriptorData = struct { view: vk.ImageView, layout: vk.ImageLayout, }; -pub const SampledImageDescriptor = DescriptorT(SampledImageDescriptorData); - -pub fn DescriptorArray(comptime T: type, comptime MAX_RESOURCES: usize) type { +pub fn DescriptorArray(comptime Type: ResourceTypeEnum, comptime MAX_RESOURCES: usize) type { return struct { const Self = @This(); + const T = std.meta.TagPayload(ResourceType, Type); // extract tag type from tagged union + pub const Resource = DescriptorT(T); + resources: [MAX_RESOURCES]Resource = [_]Resource{.{}} ** MAX_RESOURCES, first_free: ?*Resource = null, last_index: u32 = 1, // index of the last allocated image @@ -149,21 +167,79 @@ pub fn DescriptorArray(comptime T: type, comptime MAX_RESOURCES: usize) type { self.resources_to_update_count += 1; } } + + pub fn commitUpdates(self: *Self, allocator: std.mem.Allocator, device: GraphicsContext.Device, descriptor_set: vk.DescriptorSet, binding: u32) !void { + var writes = try allocator.alloc(vk.WriteDescriptorSet, self.resources_to_update_count); + defer allocator.free(writes); + + var offset: u32 = 0; + var iter = self.iterator(); + while (iter.next()) |entry| { + // TODO: merge WriteDescriptorSets by ranges somehow + const write = &writes[offset]; + + write.* = .{ + .dst_set = descriptor_set, + .dst_binding = binding, + .descriptor_type = Type.getDescriptorType(), + .dst_array_element = entry.handle.index, + .descriptor_count = 1, + .p_image_info = &[_]vk.DescriptorImageInfo{}, + .p_buffer_info = &[_]vk.DescriptorBufferInfo{}, + .p_texel_buffer_view = &[_]vk.BufferView{}, + }; + + const val: ResourceType = @unionInit(ResourceType, @tagName(Type), entry.value); + + switch (val) { + .sampled_image => |image| { + write.p_image_info = &[_]vk.DescriptorImageInfo{ + vk.DescriptorImageInfo{ + .image_layout = image.layout, + .image_view = image.view, + .sampler = .null_handle, + }, + }; + }, + .sampler => |sampler| { + write.p_image_info = &[_]vk.DescriptorImageInfo{ + vk.DescriptorImageInfo{ + .image_layout = .undefined, + .image_view = .null_handle, + .sampler = sampler, + }, + }; + }, + } + + offset += 1; + } + self.resetUpdates(); + + device.updateDescriptorSets(@intCast(writes.len), writes.ptr, 0, null); + } }; } gc: *GraphicsContext, +frame_allocator: std.mem.Allocator, descriptor_set_layouts: DescriptorSetLayouts = .{}, -image_descriptor_array_2d: DescriptorArray(SampledImageDescriptorData, 1024) = .{}, +image_descriptor_array_2d: DescriptorArray(.sampled_image, MAX_TEXTURES) = .{}, +sampler_descriptor_array: DescriptorArray(.sampler, MAX_SAMPLERS) = .{}, pipeline_layout: vk.PipelineLayout = .null_handle, -pub fn init(gc: *GraphicsContext) !DescriptorManager { +global_descriptor_pool: vk.DescriptorPool = .null_handle, +global_descriptor_set: vk.DescriptorSet = .null_handle, + +pub fn init(gc: *GraphicsContext, frame_allocator: std.mem.Allocator) !DescriptorManager { var self = DescriptorManager{ .gc = gc, + .frame_allocator = frame_allocator, }; self.image_descriptor_array_2d.init(.{ .view = .null_handle, .layout = .undefined }); + self.sampler_descriptor_array.init(.null_handle); // Global Descriptor Set Layout { @@ -221,5 +297,43 @@ pub fn init(gc: *GraphicsContext) !DescriptorManager { }, }, null); + self.global_descriptor_pool = try gc.device.createDescriptorPool( + &vk.DescriptorPoolCreateInfo{ + .flags = .{ + .update_after_bind_bit = true, + }, + .max_sets = 1, + .p_pool_sizes = &.{ + vk.DescriptorPoolSize{ + .type = .uniform_buffer, + .descriptor_count = 8, + }, + vk.DescriptorPoolSize{ + .type = .sampled_image, + .descriptor_count = 1024, // TODO: don't hardcode + }, + vk.DescriptorPoolSize{ + .type = .sampler, + .descriptor_count = 256, // TODO: dont hardcode + }, + }, + .pool_size_count = 3, + }, + null, + ); + + var descriptor_set_buf = [_]vk.DescriptorSet{.null_handle}; + try gc.device.allocateDescriptorSets(&.{ + .descriptor_pool = self.global_descriptor_pool, + .descriptor_set_count = 1, + .p_set_layouts = &.{self.descriptor_set_layouts.global}, + }, &descriptor_set_buf); + self.global_descriptor_set = descriptor_set_buf[0]; + return self; } + +pub fn commitDescriptorArrayUpdates(self: *DescriptorManager) !void { + try self.image_descriptor_array_2d.commitUpdates(self.frame_allocator, self.gc.device, self.global_descriptor_set, DescriptorSets.Global.Bindings.GlobalTextures2D.value()); + try self.sampler_descriptor_array.commitUpdates(self.frame_allocator, self.gc.device, self.global_descriptor_set, DescriptorSets.Global.Bindings.GlobalSamplers.value()); +} diff --git a/src/GraphicsContext.zig b/src/GraphicsContext.zig index a88bc0a..9c501e8 100644 --- a/src/GraphicsContext.zig +++ b/src/GraphicsContext.zig @@ -358,9 +358,30 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL std.debug.assert(self.device_info.features.descriptor_indexing_features.descriptor_binding_variable_descriptor_count == vk.TRUE); std.debug.assert(self.device_info.features.descriptor_indexing_features.descriptor_binding_uniform_buffer_update_after_bind == vk.TRUE); - const queue_config = try selectQueues(self.instance, self.device_info.physical_device); + const queue_config = try findQueueFamilies(self.instance, self.device_info.physical_device); self.memory_config = try selectMemoryPools(self.instance, self.device_info.physical_device); + var queue_create_infos = std.BoundedArray(vk.DeviceQueueCreateInfo, 3).init(0) catch unreachable; + queue_create_infos.append(vk.DeviceQueueCreateInfo{ + .queue_family_index = queue_config.graphics.family, + .queue_count = 1, // One of each, don't overcomplicate things + .p_queue_priorities = &.{1.0}, + }) catch unreachable; + if (queue_config.compute) |info| { + queue_create_infos.append(vk.DeviceQueueCreateInfo{ + .queue_family_index = info.family, + .queue_count = 1, + .p_queue_priorities = &.{1.0}, + }) catch unreachable; + } + if (queue_config.transfer) |info| { + queue_create_infos.append(vk.DeviceQueueCreateInfo{ + .queue_family_index = info.family, + .queue_count = 1, + .p_queue_priorities = &.{1.0}, + }) catch unreachable; + } + // p_next is not const in PhysicalDeviceVulkan12Features, so pass it like this var vulkan13_device_features = vk.PhysicalDeviceVulkan13Features{ .dynamic_rendering = vk.TRUE, @@ -369,8 +390,8 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL }; const device_create_config = vk.DeviceCreateInfo{ - .p_queue_create_infos = &queue_config.queue_create_info, - .queue_create_info_count = queue_config.queue_count, + .p_queue_create_infos = queue_create_infos.slice().ptr, + .queue_create_info_count = queue_create_infos.len, .p_enabled_features = &self.device_info.features.features, .pp_enabled_layer_names = @ptrCast((&vk_layers).ptr), .enabled_layer_count = @intCast(vk_layers.len), @@ -412,33 +433,22 @@ pub fn init(self: *GraphicsContext, allocator: std.mem.Allocator, window: *c.SDL try self.maybeResizeSwapchain(); errdefer self.device.destroySwapchainKHR(self.swapchain, null); - // TODO: handle the case when different queue instance map to the same queue - const graphics_queue = QueueInstance{ - .device = self.device, - .family = queue_config.graphics.family, - .handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.graphics.index), - }; - const compute_queue = QueueInstance{ - .device = self.device, - .family = queue_config.compute.family, - .handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.compute.index), - }; - const host_to_device_queue = QueueInstance{ - .device = self.device, - .family = queue_config.host_to_device.family, - .handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.host_to_device.index), - }; - const device_to_host_queue = QueueInstance{ - .device = self.device, - .family = queue_config.device_to_host.family, - .handle = self.device.getDeviceQueue(queue_config.graphics.family, queue_config.device_to_host.index), - }; - self.queues = DeviceQueues{ - .graphics = graphics_queue, - .compute = compute_queue, - .host_to_device = host_to_device_queue, - .device_to_host = device_to_host_queue, + .graphics = QueueInstance{ + .device = self.device, + .family = queue_config.graphics.family, + .handle = self.device.getDeviceQueue(queue_config.graphics.family, 0), + }, + .compute = if (queue_config.compute) |info| QueueInstance{ + .device = self.device, + .family = info.family, + .handle = self.device.getDeviceQueue(info.family, 0), + } else null, + .transfer = if (queue_config.transfer) |info| QueueInstance{ + .device = self.device, + .family = info.family, + .handle = self.device.getDeviceQueue(info.family, 0), + } else null, }; self.pipeline_cache = try self.device.createPipelineCache(&.{}, null); @@ -552,9 +562,8 @@ fn maybeResizeSwapchain(self: *GraphicsContext) !void { pub const DeviceQueues = struct { graphics: QueueInstance, - compute: QueueInstance, - host_to_device: QueueInstance, - device_to_host: QueueInstance, + compute: ?QueueInstance, + transfer: ?QueueInstance, }; pub const SubmitInfo = struct { @@ -570,6 +579,7 @@ pub const QueueInstance = struct { mu: std.Thread.Mutex = .{}, device: Device, handle: vk.Queue, + // TODO: store min_image_transfer_granularity and other useful stuff here family: u32, pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !CommandPool { @@ -643,6 +653,8 @@ fn selectPhysicalDevice(vki: Instance, surface: vk.SurfaceKHR, devices: []vk.Phy const props = vki.getPhysicalDeviceProperties(device); const features = getDeviceFeatures(vki, device); const surface_caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(device, surface); + + std.debug.print("Device Name: {s}\n", .{&props.device_name}); return SelectedPhysicalDevice{ .physical_device = device, .properties = props, @@ -654,19 +666,60 @@ fn selectPhysicalDevice(vki: Instance, surface: vk.SurfaceKHR, devices: []vk.Phy return error.NoDeviceFound; } -const DeviceQueueConfig = struct { - const Config = struct { +const DeviceQueueFamilies = struct { + pub const QueueFamilyInfo = struct { family: u32, - index: u32, + /// At least one + count: u32, + min_image_transfer_granularity: vk.Extent3D, }; - queue_create_info: [4]vk.DeviceQueueCreateInfo = undefined, - queue_count: u32 = 0, - graphics: Config, - compute: Config, - host_to_device: Config, - device_to_host: Config, + /// Graphics/Compute/Transfer/Present + graphics: QueueFamilyInfo = undefined, + /// Async compute + compute: ?QueueFamilyInfo = null, + /// Dedicated transfer queue + transfer: ?QueueFamilyInfo = null, }; +fn findQueueFamilies(instance: Instance, device: vk.PhysicalDevice) !DeviceQueueFamilies { + var scratch: [1024]u8 = undefined; + var fba = std.heap.FixedBufferAllocator.init(&scratch); + const queue_family_props = try instance.getPhysicalDeviceQueueFamilyPropertiesAlloc(device, fba.allocator()); + + var result: DeviceQueueFamilies = .{}; + var found_graphics = false; + var found_async_compute = false; + var found_transfer = false; + + for (queue_family_props, 0..) |qprops, i| { + if (found_graphics and found_async_compute and found_transfer) break; + + if (!found_graphics and qprops.queue_flags.graphics_bit) { + found_graphics = true; + result.graphics = .{ .family = @intCast(i), .count = qprops.queue_count, .min_image_transfer_granularity = qprops.min_image_transfer_granularity }; + continue; + } + + // Assuming async compute can transfer too, otherwise can't use it + if (!found_async_compute and qprops.queue_flags.contains(.{ .compute_bit = true, .transfer_bit = true })) { + found_async_compute = true; + result.compute = .{ .family = @intCast(i), .count = qprops.queue_count, .min_image_transfer_granularity = qprops.min_image_transfer_granularity }; + continue; + } + + if (!found_transfer and qprops.queue_flags.transfer_bit) { + found_transfer = true; + result.transfer = .{ .family = @intCast(i), .count = qprops.queue_count, .min_image_transfer_granularity = qprops.min_image_transfer_granularity }; + continue; + } + } + + // Can't run without this + std.debug.assert(found_graphics); + + return result; +} + pub const VulkanMemoryType = struct { type_index: u32 = 0, size: u64 = 0, @@ -681,111 +734,6 @@ const DeviceMemoryConfig = struct { cpu_to_gpu: VulkanMemoryType = .{}, }; -// Hardcode queue priorities, no idea why I would need to use them -const queue_priorities = [_]f32{ 1.0, 1.0, 1.0, 1.0, 1.0 }; - -fn selectQueues(instance: Instance, device: vk.PhysicalDevice) !DeviceQueueConfig { - var scratch: [1024]u8 = undefined; - var fba = std.heap.FixedBufferAllocator.init(&scratch); - const queue_family_props = try instance.getPhysicalDeviceQueueFamilyPropertiesAlloc(device, fba.allocator()); - - if (queue_family_props.len == 0) { - return error.NoQueues; - } - - var queue_create_info: [4]vk.DeviceQueueCreateInfo = undefined; - var queue_count: u32 = 0; - var graphics: ?DeviceQueueConfig.Config = null; - var compute: ?DeviceQueueConfig.Config = null; - var host_to_device: ?DeviceQueueConfig.Config = null; - var device_to_host: ?DeviceQueueConfig.Config = null; - - // We're on Intel most likely, just a single queue for everything :( - if (queue_family_props.len == 1) { - if (!queue_family_props[0].queue_flags.contains(.{ .graphics_bit = true, .compute_bit = true, .transfer_bit = true })) { - return error.InvalidQueue; - } - graphics = .{ .family = 0, .index = 0 }; - compute = graphics; - device_to_host = graphics; - host_to_device = graphics; - queue_create_info[0] = .{ - .queue_family_index = 0, - .queue_count = 1, - .p_queue_priorities = &queue_priorities, - }; - queue_count = 1; - } else { - for (queue_family_props, 0..) |props, family_idx| { - // Jackpot, generous Jensen provided us with an all powerfull queue family, use it for everything - // TODO: actually, still need to use the dedicated transfer queue for CPU->GPU transfers to be async for sure - if (props.queue_flags.contains(.{ .graphics_bit = true, .compute_bit = true, .transfer_bit = true }) and props.queue_count >= 4) { - graphics = .{ - .family = @intCast(family_idx), - .index = 0, - }; - compute = .{ - .family = @intCast(family_idx), - .index = 1, - }; - host_to_device = .{ - .family = @intCast(family_idx), - .index = 2, - }; - device_to_host = .{ - .family = @intCast(family_idx), - .index = 3, - }; - queue_create_info[0] = .{ - .queue_family_index = 0, - .queue_count = 4, - .p_queue_priorities = &queue_priorities, - }; - queue_count = 1; - break; - } - - // TODO: make queue create info for AMD - // Probably AMD, one graphics+compute queue, 2 separate compute queues, one pure transfer queue - if (props.queue_flags.graphics_bit) { - graphics = .{ - .family = @intCast(family_idx), - .index = 0, - }; - } - if (props.queue_flags.compute_bit and (compute == null or !props.queue_flags.graphics_bit)) { - compute = .{ - .family = @intCast(family_idx), - .index = 0, - }; - } - if (props.queue_flags.transfer_bit and (host_to_device == null or !props.queue_flags.graphics_bit or !props.queue_flags.compute_bit)) { - device_to_host = .{ - .family = @intCast(family_idx), - .index = 0, - }; - host_to_device = .{ - .family = @intCast(family_idx), - .index = 0, - }; - } - } - } - - if (graphics == null or compute == null or device_to_host == null or host_to_device == null) { - return error.MissingQueueFeatures; - } - - return .{ - .queue_create_info = queue_create_info, - .queue_count = queue_count, - .graphics = graphics.?, - .compute = compute.?, - .host_to_device = host_to_device.?, - .device_to_host = device_to_host.?, - }; -} - fn selectMemoryPools(instance: Instance, device: vk.PhysicalDevice) !DeviceMemoryConfig { const mem_props = instance.getPhysicalDeviceMemoryProperties(device); diff --git a/src/Render2.zig b/src/Render2.zig index 4ba1faf..a5cf1ea 100644 --- a/src/Render2.zig +++ b/src/Render2.zig @@ -40,14 +40,12 @@ command_pool: GraphicsContext.CommandPool, vulkan_frame_arena: VulkanPerFrameArena, camera: *Camera = &default_camera, -global_descriptor_pool: vk.DescriptorPool = .null_handle, -global_descriptor_set: vk.DescriptorSet = .null_handle, - frame: u32 = 0, frame_data: [MAX_FRAME_LAG]FrameData = undefined, // Global sampler to use for reading screen color in post processing screen_color_sampler: vk.Sampler = .null_handle, +screen_color_sampler_descriptor_handle: DescriptorManager.DescriptorHandle = .{}, // Ring buffer/arena for per frame data pub const VulkanPerFrameArena = struct { @@ -206,7 +204,7 @@ pub const VulkanPerFrameArena = struct { try device.bindBufferMemory(buffer, self.memory, out_addr.*); - frame.addBuffer(buffer); + frame.queueDestroyBuffer(buffer); return buffer; } @@ -224,7 +222,7 @@ pub const VulkanPerFrameArena = struct { try gc.device.bindImageMemory(image, self.memory, out_addr.*); - frame.addImage(image); + frame.queueDestroyImage(image); return image; } @@ -273,39 +271,7 @@ pub fn init(self: *Render2, frame_allocator: std.mem.Allocator, gc: *GraphicsCon .border_color = .int_transparent_black, .unnormalized_coordinates = vk.FALSE, }, null); - - self.global_descriptor_pool = try gc.device.createDescriptorPool( - &vk.DescriptorPoolCreateInfo{ - .flags = .{ - .update_after_bind_bit = true, - }, - .max_sets = 1, - .p_pool_sizes = &.{ - vk.DescriptorPoolSize{ - .type = .uniform_buffer, - .descriptor_count = 8, - }, - vk.DescriptorPoolSize{ - .type = .sampled_image, - .descriptor_count = 1024, // TODO: don't hardcode - }, - vk.DescriptorPoolSize{ - .type = .sampler, - .descriptor_count = 256, // TODO: dont hardcode - }, - }, - .pool_size_count = 3, - }, - null, - ); - - var descriptor_set_buf = [_]vk.DescriptorSet{.null_handle}; - try gc.device.allocateDescriptorSets(&.{ - .descriptor_pool = self.global_descriptor_pool, - .descriptor_set_count = 1, - .p_set_layouts = &.{descriptorman.descriptor_set_layouts.global}, - }, &descriptor_set_buf); - self.global_descriptor_set = descriptor_set_buf[0]; + self.screen_color_sampler_descriptor_handle = self.descriptorman.sampler_descriptor_array.alloc(self.screen_color_sampler).handle; for (0..MAX_FRAME_LAG) |i| { self.frame_data[i] = try FrameData.init(gc, self.command_pool); @@ -316,7 +282,7 @@ pub const MainRenderTarget = struct { color: GraphicsContext.Image, depth: GraphicsContext.Image, - color_descriptor: *DescriptorManager.SampledImageDescriptor, + color_descriptor: *DescriptorManager.DescriptorT(DescriptorManager.SampledImageDescriptorData), }; fn allocateRenderTarget(self: *Render2) !MainRenderTarget { @@ -440,14 +406,14 @@ fn createPerFrameImage(self: *Render2, create_info: *const vk.ImageCreateInfo, o fn createPerFrameImageView(self: *Render2, create_info: *const vk.ImageViewCreateInfo) !vk.ImageView { const view = try self.gc.device.createImageView(create_info, null); - self.frame_data[self.frame].addImageView(view); + self.frame_data[self.frame].queueDestroyImageView(view); return view; } -fn createPerFrameImageDescriptor(self: *Render2, view: vk.ImageView, layout: vk.ImageLayout) *DescriptorManager.SampledImageDescriptor { +fn createPerFrameImageDescriptor(self: *Render2, view: vk.ImageView, layout: vk.ImageLayout) *DescriptorManager.DescriptorT(DescriptorManager.SampledImageDescriptorData) { const result = self.descriptorman.image_descriptor_array_2d.alloc(.{ .view = view, .layout = layout }); - self.frame_data[self.frame].addImageDescriptor(result.handle); + self.frame_data[self.frame].queueDestroyImageDescriptor(result.handle); return result; } @@ -503,9 +469,10 @@ pub fn draw(self: *Render2) !void { cmds.updateBuffer(global_uniform_buffer.handle, 0, @sizeOf(GlobalUniform), &global_uniform); try global_uniform_buffer.sync(cmds, .{ .stage_mask = .{ .vertex_shader_bit = true }, .access_mask = .{ .shader_read_bit = true } }); - const global_descriptor_set = self.global_descriptor_set; + const global_descriptor_set = self.descriptorman.global_descriptor_set; - device.updateDescriptorSets(2, &.{ + // TODO: move this into descriptorman + device.updateDescriptorSets(1, &.{ vk.WriteDescriptorSet{ .dst_set = global_descriptor_set, .dst_binding = 0, @@ -522,56 +489,9 @@ pub fn draw(self: *Render2) !void { .p_image_info = &[_]vk.DescriptorImageInfo{}, .p_texel_buffer_view = &[_]vk.BufferView{}, }, - vk.WriteDescriptorSet{ - .dst_set = global_descriptor_set, - .dst_binding = 1, - .dst_array_element = 0, - .descriptor_type = .sampler, - .descriptor_count = 1, - .p_image_info = &.{ - vk.DescriptorImageInfo{ - .sampler = self.screen_color_sampler, - .image_view = .null_handle, - .image_layout = .undefined, - }, - }, - .p_buffer_info = &[_]vk.DescriptorBufferInfo{}, - .p_texel_buffer_view = &[_]vk.BufferView{}, - }, }, 0, null); - // TODO: move this into descriptorman? - // Update image descriptors - { - var offset: u32 = 0; - var writes: []vk.WriteDescriptorSet = try self.frame_allocator.alloc(vk.WriteDescriptorSet, self.descriptorman.image_descriptor_array_2d.resources_to_update_count); - var iter = self.descriptorman.image_descriptor_array_2d.iterator(); - while (iter.next()) |image| { - // TODO: merge WriteDescriptorSets by ranges maybe? - const write = &writes[offset]; - - write.* = .{ - .dst_set = global_descriptor_set, - .dst_binding = 2, - .dst_array_element = image.handle.index, - .descriptor_type = .sampled_image, - .descriptor_count = 1, - .p_image_info = &[_]vk.DescriptorImageInfo{ - vk.DescriptorImageInfo{ - .image_layout = image.value.layout, - .image_view = image.value.view, - .sampler = .null_handle, - }, - }, - .p_buffer_info = &[_]vk.DescriptorBufferInfo{}, - .p_texel_buffer_view = &[_]vk.BufferView{}, - }; - offset += 1; - } - self.descriptorman.image_descriptor_array_2d.resetUpdates(); - - device.updateDescriptorSets(@intCast(writes.len), writes.ptr, 0, null); - } + try self.descriptorman.commitDescriptorArrayUpdates(); try color_image.sync( cmds, @@ -710,7 +630,7 @@ pub fn draw(self: *Render2) !void { self.pushConstants(cmds, .{ .vertex_bit = true, .fragment_bit = true }, PostProcessPushConstants{ .scene_color_texture = main_render_target.color_descriptor.index(), - .scene_color_sampler = 0, + .scene_color_sampler = self.screen_color_sampler_descriptor_handle.index, }); cmds.setViewportWithCount(1, &.{vk.Viewport{ @@ -771,7 +691,7 @@ fn uploadData(self: *Render2, cmds: GraphicsContext.CommandBuffer, dst: Graphics } // Per frame stuff -const FrameData = struct { +pub const FrameData = struct { // Sync acquire_swapchain_image: vk.Semaphore, draw_sema: vk.Semaphore, @@ -803,22 +723,22 @@ const FrameData = struct { }; } - pub fn addBuffer(self: *FrameData, buffer: vk.Buffer) void { + pub fn queueDestroyBuffer(self: *FrameData, buffer: vk.Buffer) void { self.buffers[self.buffer_count] = buffer; self.buffer_count += 1; } - pub fn addImage(self: *FrameData, image: vk.Image) void { + pub fn queueDestroyImage(self: *FrameData, image: vk.Image) void { self.images[self.image_count] = image; self.image_count += 1; } - pub fn addImageView(self: *FrameData, view: vk.ImageView) void { + pub fn queueDestroyImageView(self: *FrameData, view: vk.ImageView) void { self.image_views[self.image_view_count] = view; self.image_view_count += 1; } - pub fn addImageDescriptor(self: *FrameData, descriptor: DescriptorManager.DescriptorHandle) void { + pub fn queueDestroyImageDescriptor(self: *FrameData, descriptor: DescriptorManager.DescriptorHandle) void { self.image_descriptors[self.image_descriptor_count] = descriptor; self.image_descriptor_count += 1; } @@ -838,38 +758,7 @@ const FrameData = struct { } self.image_descriptor_count = 0; - // TODO: move this into descriptorman? - // Update image descriptors - { - var offset: u32 = 0; - var writes: []vk.WriteDescriptorSet = try render.frame_allocator.alloc(vk.WriteDescriptorSet, render.descriptorman.image_descriptor_array_2d.resources_to_update_count); - var iter = render.descriptorman.image_descriptor_array_2d.iterator(); - while (iter.next()) |image| { - // TODO: merge WriteDescriptorSets by ranges maybe? - const write = &writes[offset]; - - write.* = .{ - .dst_set = render.global_descriptor_set, - .dst_binding = 2, - .dst_array_element = image.handle.index, - .descriptor_type = .sampled_image, - .descriptor_count = 1, - .p_image_info = &[_]vk.DescriptorImageInfo{ - vk.DescriptorImageInfo{ - .image_layout = image.value.layout, - .image_view = image.value.view, - .sampler = .null_handle, - }, - }, - .p_buffer_info = &[_]vk.DescriptorBufferInfo{}, - .p_texel_buffer_view = &[_]vk.BufferView{}, - }; - offset += 1; - } - render.descriptorman.image_descriptor_array_2d.resetUpdates(); - - device.updateDescriptorSets(@intCast(writes.len), writes.ptr, 0, null); - } + try render.descriptorman.commitDescriptorArrayUpdates(); for (self.buffers[0..self.buffer_count]) |buf| { device.destroyBuffer(buf, null); diff --git a/src/game.zig b/src/game.zig index 9c603d5..82a6fe1 100644 --- a/src/game.zig +++ b/src/game.zig @@ -175,8 +175,8 @@ export fn game_init(global_allocator: *std.mem.Allocator) void { globals.g_mem.* = .{ .global_allocator = global_allocator.*, .frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer), - .descriptorman = DescriptorManager.init(&globals.g_init.gc) catch @panic("DescriptorManager.init"), - .assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_init.gc, &globals.g_mem.descriptorman), + .descriptorman = DescriptorManager.init(&globals.g_init.gc, globals.g_mem.frame_fba.allocator()) catch @panic("DescriptorManager.init"), + .assetman = AssetManager.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_init.gc, &globals.g_mem.descriptorman) catch @panic("AssetManager.init"), // .render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman), .world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() }, };