Refactor descriptor arrays, add sampler descriptor array
This commit is contained in:
parent
e229da9315
commit
2215d082c4
@ -1 +0,0 @@
|
||||
out.spv: post_process.glsl
|
@ -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");
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
|
||||
|
149
src/Render2.zig
149
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);
|
||||
|
@ -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() },
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user