forked from vv/efemra
1
0
Fork 0
efemra/src/rendering/vulkan/Swapchain.zig

257 lines
8.9 KiB
Zig

const std = @import("std");
const vk = @import("vulkan");
const math = @import("std").math;
const settings = @import("settings.zig");
const dev = @import("device.zig");
const vkd = dev.DeviceDispatch;
const sync = @import("sync.zig");
const queues = @import("queues.zig");
const Image = @import("image.zig").Image;
// TODO memory
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
const preferred_present_modes = [_]vk.PresentModeKHR{
.mailbox_khr,
.immediate_khr,
.fifo_relaxed_khr,
.fifo_khr,
};
const preferred_surface_formats = [_]vk.PreferredSurfaceFormats{
// PQ Rec2100
// https://en.wikipedia.org/wiki/Rec._2100
// https://en.wikipedia.org/wiki/High-dynamic-range_video#Perceptual_quantizer
if (settings.hdr_on) .{ .a2r10g10b10_unorm_pack32, .hdr10_st2084_ext } else void,
if (settings.hdr_on) .{ .a2b10g10r10_unorm_pack32, .hdr10_st2084_ext } else void,
// 10-bit sRGB
if (settings.hdr_on) .{ .a2r10g10b10_unorm_pack32, .srgb_nonlinear_khr } else void,
if (settings.hdr_on) .{ .a2b10g10r10_unorm_pack32, .srgb_nonlinear_khr } else void,
// 8 bit sRGB
.{ .r8g8b8a8_srgb, .srgb_nonlinear_khr },
.{ .b8g8r8a8_srgb, .srgb_nonlinear_khr },
};
const Support = struct {
const Self = @This();
caps: vk.SurfaceCapabilitiesKHR,
format_count: u32,
formats: []vk.SurfaceFormatKHR,
mode_count: u32,
modes: []vk.PresentModeKHR,
pub fn init(device: *dev.Device, window: *dev.Window) !Self {
const self = Self{};
self.capabilities = try vkd.getPhysicalDeviceSurfaceCapabilitiesKHR(device.dev, window.surface);
try vkd.getPhysicalDeviceSurfaceFormatsKHR(device.dev, window.surface, &self.format_count, null);
self.formats = allocator.alloc(vk.SurfaceFormatKHR, self.format_count);
errdefer allocator.free(self.formats);
try vkd.getPhysicalDeviceSurfaceFormatsKHR(device.dev, window.surface, &self.format_count, &self.formats);
try vkd.getPhysicalDeviceSurfacePresentModesKHR(device.dev, window.surface, &self.mode_count);
self.modes = allocator.alloc(vk.PresentModeKHR, self.format_count);
errdefer allocator.free(self.modes);
try vkd.getPhysicalDeviceSurfacePresentModesKHR(device.dev, window.surface, &self.mode_count, &self.modes);
return self;
}
pub fn deinit(self: *Self) void {
allocator.free(self.formats);
}
pub fn selectSwapFormat(self: *Self) !vk.SurfaceFormatKHR {
if (self.formats.len > 0) {
return error.NoSurfaceFormats;
}
for (preferred_surface_formats) |preferred_format| {
for (self.formats) |format| {
if (std.mem.eql(vk.SurfaceFormatKHR, format, preferred_format)) {
return format;
}
}
}
return self.formats[0];
}
pub fn selectSwapMode(self: *Self) !vk.PresentModeKHR {
if (self.modes.len <= 0) {
return error.NoPresentModes;
}
for (preferred_present_modes) |preferred_mode| {
for (self.modes) |mode| {
if (preferred_mode == mode) {
return mode;
}
}
}
return self.modes[0];
}
pub fn selectSwapExtent(self: *Self, window_width: i32, window_height: i32) vk.Extent2D {
if (self.capabilities.current_extent.width != ~0) {
return self.capabilities.current_extent;
}
const min_ext = self.capabilities.min_image_extent;
const max_ext = self.capabilities.max_image_extent;
return vk.Extent2D{
.width = math.clamp(window_width, min_ext.width, max_ext.width),
.height = math.clamp(window_height, min_ext.height, max_ext.height),
};
}
};
pub const SubmitId = packed struct {
counter: u32,
queue_id: u4,
valid: bool,
};
pub const Swapchain = struct {
const Self = @This();
handle: vk.SwapchainKHR,
color_format: vk.Format,
color_space: vk.ColorSpaceKHR,
mode: vk.PresentModeKHR,
width: i32,
height: i32,
length: i32,
image_index: u32,
image_submits: [settings.max_swapchain_len]SubmitId,
images: [settings.max_swapchain_len]Image,
sync_index: u32,
sync_submits: [settings.resource_sets]SubmitId,
available_semas: [settings.resource_sets]vk.Semaphore,
rendered_semas: [settings.resource_sets]vk.Semaphore,
pub fn init(window: *dev.Window, device: *dev.Device, previous: ?*Self) !void {
const support = try Support.init(dev, window.surface);
defer support.deinit();
const queue_support = try queues.QueueSupport.init(device, window);
defer queue_support.deinit();
const format = try support.selectSwapFormat();
const mode = try support.selectSwapMode();
const ext = support.selectSwapExtent(window);
const img_count = math.clamp(settings.desired_swapchain_len, support.capabilities.min_image_count, math.min(settings.max_swapchain_len, support.capabilities.max_image_count));
const families = [_]u32{
queue_support.family[queues.QueueId.graphics],
queue_support.family[queues.QueueId.present],
};
const concurrent = families[0] != families[1];
const usage: vk.ImageUsage = .color_attachment_bit;
const swap_info = vk.SwapInfo{
.s_type = .swapchain_create_info_khr,
.surface = window.surface,
.present_mode = mode,
.min_image_count = img_count,
.image_format = format.format,
.image_color_space = format.color_space,
.image_extent = ext,
.image_array_layers = 1,
.image_usage = usage,
.image_sharing_mode = if (concurrent) .concurrent else .exclusive,
.queue_family_index_count = if (concurrent) families.len else 0,
.p_queue_family_indices = families,
.pre_transform = support.capabilities.current_transform,
// no compositing with window manager / desktop background
.composite_alpha = .opaque_bit_khr,
// don't render pixels behind other windows
.clipped = true,
// prev swapchain, if recreating
.old_swapchain = previous.handle orelse null,
};
const handle = try vkd.createSwapchainKHR(device.dev, &swap_info, null);
if (previous == null) {
std.debug.print("Present mode: '{s}'", @tagName(mode));
std.debug.print("Present extent: '{} x {}'", ext.width, ext.height);
std.debug.print("Present images: '{}'", img_count);
std.debug.print("Present sharing mode: '{s}'", if (concurrent) "Concurrent" else "Exclusive");
std.debug.print("Color space: '{s}'", @tagName(format.color_space));
std.debug.print("Format: '{s}'", @tagName(format.format));
}
const images = [settings.max_swapchain_len]vk.Images{};
try vkd.getSwapchainImagesKHR(device.dev, handle, &img_count, null);
if (img_count > settings.max_swapchain_len) {
std.debug.assert(false);
vkd.destroySwapchainKHR(device.dev, handle, null) catch {};
return error.TooManyImages;
}
try vkd.getSwapchainImagesKHR(device.dev, handle, &img_count, images);
const self = Self{
.handle = handle,
.mode = mode,
.color_format = format.format,
.color_space = format.color_space,
.width = ext.width,
.height = ext.height,
.length = img_count,
};
for (images) |img, i| {
try Image.import(&self.images[i], &.{
.s_type = .image_create_info,
.image_type = .@"2d",
.format = self.color_format,
.extent = .{
.width = ext.width,
.height = ext.height,
.depth = 1,
},
.mip_levels = 1,
.array_layers = 1,
.samples = .@"1_bit",
.tiling = .optimal,
.usage = swap_info.image_sharing_mode,
.queue_family_index_count = swap_info.queue_family_index_count,
.p_queue_family_indices = swap_info.p_queue_family_indices,
.initial_layout = .undefined,
}, img);
}
{
var i = 0;
while (i < settings.resource_sets) : (i += 1) {
self.available_semas[i] = try sync.Semaphore.init();
self.rendered_semas[i] = try sync.Semaphore.init();
}
}
return self;
}
pub fn deinit(self: *Self, device: *dev.Device) void {
device.waitIdle();
for (self.available_semas) |_, i| {
self.available_semas[i].deinit();
self.rendered_semas[i].deinit();
}
for (self.images) |image| {
image.deinit();
}
}
};