Refactor descriptor arrays, add sampler descriptor array

This commit is contained in:
sergeypdev 2024-12-21 19:28:02 +04:00
parent e229da9315
commit 2215d082c4
6 changed files with 299 additions and 285 deletions

View File

@ -1 +0,0 @@
out.spv: post_process.glsl

View File

@ -58,10 +58,15 @@ loaded_assets: std.AutoHashMapUnmanaged(AssetId, LoadedAsset) = .{},
rw_lock: std.Thread.RwLock.DefaultRwLock = .{}, rw_lock: std.Thread.RwLock.DefaultRwLock = .{},
asset_watcher: AssetWatcher = undefined, asset_watcher: AssetWatcher = undefined,
// texture_heap: BuddyAllocator,
vertex_heap: VertexBufferHeap, vertex_heap: VertexBufferHeap,
gc: *GraphicsContext, gc: *GraphicsContext,
descriptorman: *DescriptorManager, descriptorman: *DescriptorManager,
// For uploads
queue: *GraphicsContext.QueueInstance,
command_pool: GraphicsContext.CommandPool,
const AssetWatcher = struct { const AssetWatcher = struct {
assetman: *AssetManager, assetman: *AssetManager,
fba: std.heap.FixedBufferAllocator, 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; 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_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 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 .{ return .{
.allocator = allocator, .allocator = allocator,
.frame_arena = frame_arena, .frame_arena = frame_arena,
.exe_dir = exe_dir, .exe_dir = exe_dir,
// .texture_heap = BuddyAllocator.init(allocator, 64, 22), // 256MB
.vertex_heap = VertexBufferHeap.init(allocator) catch @panic("OOM"), .vertex_heap = VertexBufferHeap.init(allocator) catch @panic("OOM"),
.gc = gc, .gc = gc,
.descriptorman = descriptorman, .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); var texture = try formats.Texture.fromBuffer(self.allocator, data.bytes);
defer texture.free(self.allocator); 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; var name: gl.GLuint = 0;
gl.createTextures(gl.TEXTURE_2D, 1, &name); gl.createTextures(gl.TEXTURE_2D, 1, &name);
if (name == 0) { if (name == 0) {
@ -1600,6 +1663,7 @@ fn freeAsset(self: *AssetManager, asset: *LoadedAsset) void {
.shaderProgram => |*program| { .shaderProgram => |*program| {
self.gc.queues.graphics.mu.lock(); self.gc.queues.graphics.mu.lock();
defer self.gc.queues.graphics.mu.unlock(); 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"); self.gc.device.queueWaitIdle(self.gc.queues.graphics.handle) catch @panic("Wait Idle failed");

View File

@ -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 { pub const SampledImageDescriptorData = struct {
view: vk.ImageView, view: vk.ImageView,
layout: vk.ImageLayout, layout: vk.ImageLayout,
}; };
pub const SampledImageDescriptor = DescriptorT(SampledImageDescriptorData); pub fn DescriptorArray(comptime Type: ResourceTypeEnum, comptime MAX_RESOURCES: usize) type {
pub fn DescriptorArray(comptime T: type, comptime MAX_RESOURCES: usize) type {
return struct { return struct {
const Self = @This(); const Self = @This();
const T = std.meta.TagPayload(ResourceType, Type); // extract tag type from tagged union
pub const Resource = DescriptorT(T); pub const Resource = DescriptorT(T);
resources: [MAX_RESOURCES]Resource = [_]Resource{.{}} ** MAX_RESOURCES, resources: [MAX_RESOURCES]Resource = [_]Resource{.{}} ** MAX_RESOURCES,
first_free: ?*Resource = null, first_free: ?*Resource = null,
last_index: u32 = 1, // index of the last allocated image 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; 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, gc: *GraphicsContext,
frame_allocator: std.mem.Allocator,
descriptor_set_layouts: DescriptorSetLayouts = .{}, 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, 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{ var self = DescriptorManager{
.gc = gc, .gc = gc,
.frame_allocator = frame_allocator,
}; };
self.image_descriptor_array_2d.init(.{ .view = .null_handle, .layout = .undefined }); self.image_descriptor_array_2d.init(.{ .view = .null_handle, .layout = .undefined });
self.sampler_descriptor_array.init(.null_handle);
// Global Descriptor Set Layout // Global Descriptor Set Layout
{ {
@ -221,5 +297,43 @@ pub fn init(gc: *GraphicsContext) !DescriptorManager {
}, },
}, null); }, 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; 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());
}

View File

@ -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_variable_descriptor_count == vk.TRUE);
std.debug.assert(self.device_info.features.descriptor_indexing_features.descriptor_binding_uniform_buffer_update_after_bind == 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); 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 // p_next is not const in PhysicalDeviceVulkan12Features, so pass it like this
var vulkan13_device_features = vk.PhysicalDeviceVulkan13Features{ var vulkan13_device_features = vk.PhysicalDeviceVulkan13Features{
.dynamic_rendering = vk.TRUE, .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{ const device_create_config = vk.DeviceCreateInfo{
.p_queue_create_infos = &queue_config.queue_create_info, .p_queue_create_infos = queue_create_infos.slice().ptr,
.queue_create_info_count = queue_config.queue_count, .queue_create_info_count = queue_create_infos.len,
.p_enabled_features = &self.device_info.features.features, .p_enabled_features = &self.device_info.features.features,
.pp_enabled_layer_names = @ptrCast((&vk_layers).ptr), .pp_enabled_layer_names = @ptrCast((&vk_layers).ptr),
.enabled_layer_count = @intCast(vk_layers.len), .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(); try self.maybeResizeSwapchain();
errdefer self.device.destroySwapchainKHR(self.swapchain, null); 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{ self.queues = DeviceQueues{
.graphics = graphics_queue, .graphics = QueueInstance{
.compute = compute_queue, .device = self.device,
.host_to_device = host_to_device_queue, .family = queue_config.graphics.family,
.device_to_host = device_to_host_queue, .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); self.pipeline_cache = try self.device.createPipelineCache(&.{}, null);
@ -552,9 +562,8 @@ fn maybeResizeSwapchain(self: *GraphicsContext) !void {
pub const DeviceQueues = struct { pub const DeviceQueues = struct {
graphics: QueueInstance, graphics: QueueInstance,
compute: QueueInstance, compute: ?QueueInstance,
host_to_device: QueueInstance, transfer: ?QueueInstance,
device_to_host: QueueInstance,
}; };
pub const SubmitInfo = struct { pub const SubmitInfo = struct {
@ -570,6 +579,7 @@ pub const QueueInstance = struct {
mu: std.Thread.Mutex = .{}, mu: std.Thread.Mutex = .{},
device: Device, device: Device,
handle: vk.Queue, handle: vk.Queue,
// TODO: store min_image_transfer_granularity and other useful stuff here
family: u32, family: u32,
pub fn createCommandPool(self: *Self, flags: vk.CommandPoolCreateFlags) !CommandPool { 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 props = vki.getPhysicalDeviceProperties(device);
const features = getDeviceFeatures(vki, device); const features = getDeviceFeatures(vki, device);
const surface_caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(device, surface); const surface_caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(device, surface);
std.debug.print("Device Name: {s}\n", .{&props.device_name});
return SelectedPhysicalDevice{ return SelectedPhysicalDevice{
.physical_device = device, .physical_device = device,
.properties = props, .properties = props,
@ -654,19 +666,60 @@ fn selectPhysicalDevice(vki: Instance, surface: vk.SurfaceKHR, devices: []vk.Phy
return error.NoDeviceFound; return error.NoDeviceFound;
} }
const DeviceQueueConfig = struct { const DeviceQueueFamilies = struct {
const Config = struct { pub const QueueFamilyInfo = struct {
family: u32, family: u32,
index: u32, /// At least one
count: u32,
min_image_transfer_granularity: vk.Extent3D,
}; };
queue_create_info: [4]vk.DeviceQueueCreateInfo = undefined, /// Graphics/Compute/Transfer/Present
queue_count: u32 = 0, graphics: QueueFamilyInfo = undefined,
graphics: Config, /// Async compute
compute: Config, compute: ?QueueFamilyInfo = null,
host_to_device: Config, /// Dedicated transfer queue
device_to_host: Config, 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 { pub const VulkanMemoryType = struct {
type_index: u32 = 0, type_index: u32 = 0,
size: u64 = 0, size: u64 = 0,
@ -681,111 +734,6 @@ const DeviceMemoryConfig = struct {
cpu_to_gpu: VulkanMemoryType = .{}, 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 { fn selectMemoryPools(instance: Instance, device: vk.PhysicalDevice) !DeviceMemoryConfig {
const mem_props = instance.getPhysicalDeviceMemoryProperties(device); const mem_props = instance.getPhysicalDeviceMemoryProperties(device);

View File

@ -40,14 +40,12 @@ command_pool: GraphicsContext.CommandPool,
vulkan_frame_arena: VulkanPerFrameArena, vulkan_frame_arena: VulkanPerFrameArena,
camera: *Camera = &default_camera, camera: *Camera = &default_camera,
global_descriptor_pool: vk.DescriptorPool = .null_handle,
global_descriptor_set: vk.DescriptorSet = .null_handle,
frame: u32 = 0, frame: u32 = 0,
frame_data: [MAX_FRAME_LAG]FrameData = undefined, frame_data: [MAX_FRAME_LAG]FrameData = undefined,
// Global sampler to use for reading screen color in post processing // Global sampler to use for reading screen color in post processing
screen_color_sampler: vk.Sampler = .null_handle, screen_color_sampler: vk.Sampler = .null_handle,
screen_color_sampler_descriptor_handle: DescriptorManager.DescriptorHandle = .{},
// Ring buffer/arena for per frame data // Ring buffer/arena for per frame data
pub const VulkanPerFrameArena = struct { pub const VulkanPerFrameArena = struct {
@ -206,7 +204,7 @@ pub const VulkanPerFrameArena = struct {
try device.bindBufferMemory(buffer, self.memory, out_addr.*); try device.bindBufferMemory(buffer, self.memory, out_addr.*);
frame.addBuffer(buffer); frame.queueDestroyBuffer(buffer);
return buffer; return buffer;
} }
@ -224,7 +222,7 @@ pub const VulkanPerFrameArena = struct {
try gc.device.bindImageMemory(image, self.memory, out_addr.*); try gc.device.bindImageMemory(image, self.memory, out_addr.*);
frame.addImage(image); frame.queueDestroyImage(image);
return image; return image;
} }
@ -273,39 +271,7 @@ pub fn init(self: *Render2, frame_allocator: std.mem.Allocator, gc: *GraphicsCon
.border_color = .int_transparent_black, .border_color = .int_transparent_black,
.unnormalized_coordinates = vk.FALSE, .unnormalized_coordinates = vk.FALSE,
}, null); }, null);
self.screen_color_sampler_descriptor_handle = self.descriptorman.sampler_descriptor_array.alloc(self.screen_color_sampler).handle;
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];
for (0..MAX_FRAME_LAG) |i| { for (0..MAX_FRAME_LAG) |i| {
self.frame_data[i] = try FrameData.init(gc, self.command_pool); self.frame_data[i] = try FrameData.init(gc, self.command_pool);
@ -316,7 +282,7 @@ pub const MainRenderTarget = struct {
color: GraphicsContext.Image, color: GraphicsContext.Image,
depth: GraphicsContext.Image, depth: GraphicsContext.Image,
color_descriptor: *DescriptorManager.SampledImageDescriptor, color_descriptor: *DescriptorManager.DescriptorT(DescriptorManager.SampledImageDescriptorData),
}; };
fn allocateRenderTarget(self: *Render2) !MainRenderTarget { 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 { fn createPerFrameImageView(self: *Render2, create_info: *const vk.ImageViewCreateInfo) !vk.ImageView {
const view = try self.gc.device.createImageView(create_info, null); 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; 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 }); 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; return result;
} }
@ -503,9 +469,10 @@ pub fn draw(self: *Render2) !void {
cmds.updateBuffer(global_uniform_buffer.handle, 0, @sizeOf(GlobalUniform), &global_uniform); 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 } }); 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{ vk.WriteDescriptorSet{
.dst_set = global_descriptor_set, .dst_set = global_descriptor_set,
.dst_binding = 0, .dst_binding = 0,
@ -522,56 +489,9 @@ pub fn draw(self: *Render2) !void {
.p_image_info = &[_]vk.DescriptorImageInfo{}, .p_image_info = &[_]vk.DescriptorImageInfo{},
.p_texel_buffer_view = &[_]vk.BufferView{}, .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); }, 0, null);
// TODO: move this into descriptorman? try self.descriptorman.commitDescriptorArrayUpdates();
// 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 color_image.sync( try color_image.sync(
cmds, cmds,
@ -710,7 +630,7 @@ pub fn draw(self: *Render2) !void {
self.pushConstants(cmds, .{ .vertex_bit = true, .fragment_bit = true }, PostProcessPushConstants{ self.pushConstants(cmds, .{ .vertex_bit = true, .fragment_bit = true }, PostProcessPushConstants{
.scene_color_texture = main_render_target.color_descriptor.index(), .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{ cmds.setViewportWithCount(1, &.{vk.Viewport{
@ -771,7 +691,7 @@ fn uploadData(self: *Render2, cmds: GraphicsContext.CommandBuffer, dst: Graphics
} }
// Per frame stuff // Per frame stuff
const FrameData = struct { pub const FrameData = struct {
// Sync // Sync
acquire_swapchain_image: vk.Semaphore, acquire_swapchain_image: vk.Semaphore,
draw_sema: 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.buffers[self.buffer_count] = buffer;
self.buffer_count += 1; 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.images[self.image_count] = image;
self.image_count += 1; 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_views[self.image_view_count] = view;
self.image_view_count += 1; 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_descriptors[self.image_descriptor_count] = descriptor;
self.image_descriptor_count += 1; self.image_descriptor_count += 1;
} }
@ -838,38 +758,7 @@ const FrameData = struct {
} }
self.image_descriptor_count = 0; self.image_descriptor_count = 0;
// TODO: move this into descriptorman? try render.descriptorman.commitDescriptorArrayUpdates();
// 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);
}
for (self.buffers[0..self.buffer_count]) |buf| { for (self.buffers[0..self.buffer_count]) |buf| {
device.destroyBuffer(buf, null); device.destroyBuffer(buf, null);

View File

@ -175,8 +175,8 @@ export fn game_init(global_allocator: *std.mem.Allocator) void {
globals.g_mem.* = .{ globals.g_mem.* = .{
.global_allocator = global_allocator.*, .global_allocator = global_allocator.*,
.frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer), .frame_fba = std.heap.FixedBufferAllocator.init(frame_arena_buffer),
.descriptorman = DescriptorManager.init(&globals.g_init.gc) catch @panic("DescriptorManager.init"), .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), .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), // .render = Render.init(global_allocator.*, globals.g_mem.frame_fba.allocator(), &globals.g_mem.assetman),
.world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() }, .world = .{ .frame_arena = globals.g_mem.frame_fba.allocator() },
}; };