WIP: Start refactoring
This commit is contained in:
parent
61a18e6f0b
commit
401f82c523
8 changed files with 640 additions and 572 deletions
|
@ -41,7 +41,7 @@ pub fn build(b: *std.Build) void {
|
|||
// SDL2
|
||||
const sdl_sdk = sdl.init(b, .{});
|
||||
sdl_sdk.link(exe, .dynamic, sdl.Library.SDL2);
|
||||
exe.root_module.addImport("sdl2", sdl_sdk.getWrapperModuleVulkan(vkzig_bindings));
|
||||
exe.root_module.addImport("sdl", sdl_sdk.getWrapperModuleVulkan(vkzig_bindings));
|
||||
|
||||
// zmath
|
||||
const zmath = b.dependency("zmath", .{});
|
||||
|
|
368
src/Context.zig
Normal file
368
src/Context.zig
Normal file
|
@ -0,0 +1,368 @@
|
|||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const vk = @import("vulkan");
|
||||
const sdl = @import("sdl");
|
||||
const img = @import("zstbi");
|
||||
|
||||
const validation = @import("./validation_layers.zig");
|
||||
const Swapchain = @import("Swapchain.zig");
|
||||
|
||||
const device_extensions = [_][*:0]const u8{vk.extensions.khr_swapchain.name};
|
||||
|
||||
pub const apis: []const vk.ApiInfo = &.{
|
||||
vk.features.version_1_0,
|
||||
vk.features.version_1_1,
|
||||
vk.features.version_1_2,
|
||||
vk.features.version_1_3,
|
||||
vk.extensions.khr_surface,
|
||||
vk.extensions.khr_swapchain,
|
||||
vk.extensions.ext_debug_utils,
|
||||
};
|
||||
|
||||
const enable_validation_layers = builtin.mode == .Debug;
|
||||
const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"};
|
||||
|
||||
const QueueFamilyIndices = struct {
|
||||
graphics_family: ?u32 = null,
|
||||
presentation_family: ?u32 = null,
|
||||
|
||||
fn isValid(self: QueueFamilyIndices) bool {
|
||||
return self.graphics_family != null and self.presentation_family != null;
|
||||
}
|
||||
};
|
||||
|
||||
const BaseDispatch = vk.BaseWrapper(apis);
|
||||
const InstanceDispatch = vk.InstanceWrapper(apis);
|
||||
const DeviceDispatch = vk.DeviceWrapper(apis);
|
||||
|
||||
pub const Instance = vk.InstanceProxy(apis);
|
||||
pub const Device = vk.DeviceProxy(apis);
|
||||
pub const Queue = vk.QueueProxy(apis);
|
||||
|
||||
// ---
|
||||
|
||||
const Self = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
vkb: BaseDispatch,
|
||||
|
||||
window: sdl.Window,
|
||||
|
||||
instance: Instance,
|
||||
physical_device: vk.PhysicalDevice,
|
||||
device: Device,
|
||||
|
||||
graphics_queue: Queue,
|
||||
presentation_queue: Queue,
|
||||
surface: vk.SurfaceKHR,
|
||||
|
||||
debug_utils: ?vk.DebugUtilsMessengerEXT,
|
||||
|
||||
pub fn init(allocator: std.mem.Allocator, window: sdl.Window) !Self {
|
||||
var self: Self = undefined;
|
||||
|
||||
self.window = window;
|
||||
self.allocator = allocator;
|
||||
self.vkb = try BaseDispatch.load(try sdl.vulkan.getVkGetInstanceProcAddr());
|
||||
|
||||
img.init(allocator);
|
||||
|
||||
try self.createInstance();
|
||||
|
||||
if (enable_validation_layers) {
|
||||
self.debug_utils = try validation.createDebugMessenger(self.instance);
|
||||
}
|
||||
|
||||
try self.createSurface();
|
||||
|
||||
try self.getPhysicalDevice();
|
||||
try self.createLogicalDevice();
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
if (enable_validation_layers) {
|
||||
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
|
||||
}
|
||||
|
||||
self.device.destroyDevice(null);
|
||||
self.instance.destroySurfaceKHR(self.surface, null);
|
||||
self.instance.destroyInstance(null);
|
||||
|
||||
self.allocator.destroy(self.device.wrapper);
|
||||
self.allocator.destroy(self.instance.wrapper);
|
||||
|
||||
img.deinit();
|
||||
}
|
||||
|
||||
fn createInstance(self: *Self) !void {
|
||||
if (enable_validation_layers and !self.checkValidationLayersSupport()) {
|
||||
// TODO Better error
|
||||
return error.LayerNotPresent;
|
||||
}
|
||||
|
||||
const extensions = try self.getRequiredExtensions();
|
||||
defer self.allocator.free(extensions);
|
||||
|
||||
std.debug.print("[Required instance extensions]\n", .{});
|
||||
for (extensions) |ext| {
|
||||
std.debug.print("\t- {s}\n", .{ext});
|
||||
}
|
||||
|
||||
if (!try self.checkInstanceExtensions(&extensions)) {
|
||||
return error.ExtensionNotPresent;
|
||||
}
|
||||
|
||||
const app_info = vk.ApplicationInfo{
|
||||
.p_application_name = "Vulkan SDL Test",
|
||||
.application_version = vk.makeApiVersion(0, 0, 1, 0),
|
||||
.p_engine_name = "Vulkan SDL Test",
|
||||
.engine_version = vk.makeApiVersion(0, 0, 1, 0),
|
||||
.api_version = vk.API_VERSION_1_3,
|
||||
};
|
||||
|
||||
var instance_create_info: vk.InstanceCreateInfo = .{
|
||||
.p_application_info = &app_info,
|
||||
.enabled_extension_count = @intCast(extensions.len),
|
||||
.pp_enabled_extension_names = @ptrCast(extensions),
|
||||
};
|
||||
|
||||
if (enable_validation_layers) {
|
||||
const debug_create_info = validation.getDebugUtilsCreateInfo();
|
||||
|
||||
instance_create_info.enabled_layer_count = @intCast(validation_layers.len);
|
||||
instance_create_info.pp_enabled_layer_names = &validation_layers;
|
||||
instance_create_info.p_next = &debug_create_info;
|
||||
}
|
||||
|
||||
const instance_handle = try self.vkb.createInstance(&instance_create_info, null);
|
||||
const vki = try self.allocator.create(InstanceDispatch);
|
||||
errdefer self.allocator.destroy(vki);
|
||||
vki.* = try InstanceDispatch.load(instance_handle, self.vkb.dispatch.vkGetInstanceProcAddr);
|
||||
|
||||
self.instance = Instance.init(instance_handle, vki);
|
||||
}
|
||||
|
||||
fn createSurface(self: *Self) !void {
|
||||
self.surface = try sdl.vulkan.createSurface(self.window, self.instance.handle);
|
||||
}
|
||||
|
||||
fn getPhysicalDevice(self: *Self) !void {
|
||||
var pdev_count: u32 = 0;
|
||||
_ = try self.instance.enumeratePhysicalDevices(&pdev_count, null);
|
||||
|
||||
const pdevs = try self.allocator.alloc(vk.PhysicalDevice, pdev_count);
|
||||
defer self.allocator.free(pdevs);
|
||||
|
||||
_ = try self.instance.enumeratePhysicalDevices(&pdev_count, pdevs.ptr);
|
||||
|
||||
for (pdevs) |pdev| {
|
||||
if (self.checkDeviceSuitable(pdev)) {
|
||||
self.physical_device = pdev;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO Obviously needs to be something else
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
fn createLogicalDevice(self: *Self) !void {
|
||||
const indices = try self.getQueueFamilies(self.physical_device);
|
||||
// 1 is the highest priority
|
||||
const priority = [_]f32{1};
|
||||
|
||||
const qci = [_]vk.DeviceQueueCreateInfo{
|
||||
.{
|
||||
.queue_family_index = indices.graphics_family.?,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = &priority,
|
||||
},
|
||||
.{
|
||||
.queue_family_index = indices.presentation_family.?,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = &priority,
|
||||
},
|
||||
};
|
||||
|
||||
const queue_count: u32 = if (indices.graphics_family.? == indices.presentation_family.?)
|
||||
1
|
||||
else
|
||||
2;
|
||||
|
||||
// Device features
|
||||
const device_features: vk.PhysicalDeviceFeatures = .{
|
||||
.sampler_anisotropy = vk.TRUE, // Enable anisotropy
|
||||
};
|
||||
|
||||
const device_create_info: vk.DeviceCreateInfo = .{
|
||||
.queue_create_info_count = queue_count,
|
||||
.p_queue_create_infos = &qci,
|
||||
.pp_enabled_extension_names = &device_extensions,
|
||||
.enabled_extension_count = @intCast(device_extensions.len),
|
||||
.p_enabled_features = &device_features,
|
||||
};
|
||||
|
||||
const device_handle = try self.instance.createDevice(self.physical_device, &device_create_info, null);
|
||||
|
||||
const vkd = try self.allocator.create(DeviceDispatch);
|
||||
errdefer self.allocator.destroy(vkd);
|
||||
vkd.* = try DeviceDispatch.load(device_handle, self.instance.wrapper.dispatch.vkGetDeviceProcAddr);
|
||||
|
||||
self.device = Device.init(device_handle, vkd);
|
||||
|
||||
const queues = try self.getDeviceQueues();
|
||||
|
||||
self.graphics_queue = Queue.init(queues[0], self.device.wrapper);
|
||||
self.presentation_queue = Queue.init(queues[1], self.device.wrapper);
|
||||
}
|
||||
|
||||
fn getRequiredExtensions(self: Self) ![][*:0]const u8 {
|
||||
var ext_count = sdl.vulkan.getInstanceExtensionsCount(self.window);
|
||||
|
||||
if (enable_validation_layers) {
|
||||
ext_count += 1;
|
||||
}
|
||||
|
||||
var extensions = try self.allocator.alloc([*:0]const u8, ext_count);
|
||||
_ = try sdl.vulkan.getInstanceExtensions(self.window, extensions);
|
||||
|
||||
if (enable_validation_layers) {
|
||||
extensions[extensions.len - 1] = vk.extensions.ext_debug_utils.name;
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
fn getQueueFamilies(self: Self, pdev: vk.PhysicalDevice) !QueueFamilyIndices {
|
||||
var indices: QueueFamilyIndices = .{ .graphics_family = null };
|
||||
|
||||
var queue_family_count: u32 = 0;
|
||||
self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, null);
|
||||
|
||||
const queue_family_list = try self.allocator.alloc(vk.QueueFamilyProperties, queue_family_count);
|
||||
defer self.allocator.free(queue_family_list);
|
||||
|
||||
self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, queue_family_list.ptr);
|
||||
|
||||
for (queue_family_list, 0..) |queue_family, i| {
|
||||
if (queue_family.queue_count > 0 and queue_family.queue_flags.graphics_bit) {
|
||||
indices.graphics_family = @intCast(i);
|
||||
}
|
||||
|
||||
const presentation_support = try self.instance.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(i), self.surface);
|
||||
if (queue_family.queue_count > 0 and presentation_support == vk.TRUE) {
|
||||
indices.presentation_family = @intCast(i);
|
||||
}
|
||||
|
||||
if (indices.isValid()) {
|
||||
return indices;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn getDeviceQueues(self: Self) ![2]vk.Queue {
|
||||
const indices = try self.getQueueFamilies(self.physical_device);
|
||||
|
||||
const graphics_queue = self.device.getDeviceQueue(indices.graphics_family.?, 0);
|
||||
const presentation_queue = self.device.getDeviceQueue(indices.presentation_family.?, 0);
|
||||
return .{ graphics_queue, presentation_queue };
|
||||
}
|
||||
|
||||
fn checkInstanceExtensions(self: Self, required_extensions: *const [][*:0]const u8) !bool {
|
||||
var prop_count: u32 = 0;
|
||||
_ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, null);
|
||||
|
||||
const props = try self.allocator.alloc(vk.ExtensionProperties, prop_count);
|
||||
defer self.allocator.free(props);
|
||||
|
||||
_ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, props.ptr);
|
||||
|
||||
for (required_extensions.*) |required_extension| {
|
||||
for (props) |prop| {
|
||||
if (std.mem.eql(u8, std.mem.sliceTo(&prop.extension_name, 0), std.mem.span(required_extension))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn checkDeviceExtensions(self: Self, pdev: vk.PhysicalDevice) !bool {
|
||||
var prop_count: u32 = 0;
|
||||
_ = try self.instance.enumerateDeviceExtensionProperties(pdev, null, &prop_count, null);
|
||||
|
||||
if (prop_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const props = try self.allocator.alloc(vk.ExtensionProperties, prop_count);
|
||||
defer self.allocator.free(props);
|
||||
|
||||
_ = try self.instance.enumerateDeviceExtensionProperties(pdev, null, &prop_count, props.ptr);
|
||||
|
||||
for (device_extensions) |device_extension| {
|
||||
for (props) |prop| {
|
||||
if (std.mem.eql(u8, std.mem.sliceTo(&prop.extension_name, 0), std.mem.span(device_extension))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn checkDeviceSuitable(self: Self, pdev: vk.PhysicalDevice) bool {
|
||||
const pdev_properties = self.instance.getPhysicalDeviceProperties(pdev);
|
||||
|
||||
if (pdev_properties.device_type == .cpu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pdev_features = self.instance.getPhysicalDeviceFeatures(pdev);
|
||||
const queue_family_indices = self.getQueueFamilies(pdev) catch return false;
|
||||
const extension_support = self.checkDeviceExtensions(pdev) catch return false;
|
||||
|
||||
const swapchain_details = Swapchain.getSwapchainDetails(
|
||||
self.allocator,
|
||||
self.instance,
|
||||
self.physical_device,
|
||||
self.surface,
|
||||
) catch return false;
|
||||
defer self.allocator.free(swapchain_details.formats);
|
||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||
|
||||
const swapchain_valid = swapchain_details.formats.len != 0 and swapchain_details.formats.len != 0;
|
||||
|
||||
return queue_family_indices.isValid() and extension_support and swapchain_valid and pdev_features.sampler_anisotropy == vk.TRUE;
|
||||
}
|
||||
|
||||
fn checkValidationLayersSupport(self: Self) bool {
|
||||
var layer_count: u32 = undefined;
|
||||
_ = self.vkb.enumerateInstanceLayerProperties(&layer_count, null) catch return false;
|
||||
|
||||
const available_layers = self.allocator.alloc(vk.LayerProperties, layer_count) catch unreachable;
|
||||
defer self.allocator.free(available_layers);
|
||||
|
||||
_ = self.vkb.enumerateInstanceLayerProperties(&layer_count, available_layers.ptr) catch return false;
|
||||
|
||||
for (validation_layers) |validation_layer| {
|
||||
for (available_layers) |available_layer| {
|
||||
if (std.mem.eql(u8, std.mem.span(validation_layer), std.mem.sliceTo(&available_layer.layer_name, 0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -4,8 +4,8 @@ const zm = @import("zmath");
|
|||
|
||||
const Utilities = @import("utilities.zig");
|
||||
const Vertex = Utilities.Vertex;
|
||||
const Device = @import("vulkan_renderer.zig").Device;
|
||||
const Instance = @import("vulkan_renderer.zig").Instance;
|
||||
const Device = @import("Context.zig").Device;
|
||||
const Instance = @import("Context.zig").Instance;
|
||||
const Model = @import("vulkan_renderer.zig").Model;
|
||||
|
||||
const Self = @This();
|
||||
|
|
189
src/Swapchain.zig
Normal file
189
src/Swapchain.zig
Normal file
|
@ -0,0 +1,189 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const sdl = @import("sdl");
|
||||
|
||||
const Context = @import("Context.zig");
|
||||
const Instance = Context.Instance;
|
||||
|
||||
pub const SwapchainDetails = struct {
|
||||
surface_capabilities: vk.SurfaceCapabilitiesKHR,
|
||||
formats: []vk.SurfaceFormatKHR,
|
||||
presentation_modes: []vk.PresentModeKHR,
|
||||
};
|
||||
|
||||
pub const SwapchainImage = struct {
|
||||
image: vk.Image,
|
||||
image_view: vk.ImageView,
|
||||
};
|
||||
|
||||
const Self = @This();
|
||||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
ctx: Context,
|
||||
|
||||
swapchain: vk.SwapchainKHR,
|
||||
|
||||
swapchain_images: []SwapchainImage,
|
||||
swapchain_framebuffers: []vk.Framebuffer,
|
||||
|
||||
pub fn create(allocator: std.mem.Allocator, context: Context) !Self {
|
||||
var self: Self = undefined;
|
||||
|
||||
self.allocator = allocator;
|
||||
self.ctx = context;
|
||||
|
||||
const swapchain_details = try getSwapchainDetails(allocator, context.instance, context.physical_device, context.surface);
|
||||
defer self.allocator.free(swapchain_details.formats);
|
||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||
|
||||
// 1. Choose best surface format
|
||||
const surface_format = chooseBestSurfaceFormat(swapchain_details.formats);
|
||||
// 2. Choose best presentation mode
|
||||
const present_mode = chooseBestPresentationMode(swapchain_details.presentation_modes);
|
||||
// 3. Choose swapchain image resolution
|
||||
const extent = chooseSwapExtent(&context.window, swapchain_details.surface_capabilities);
|
||||
|
||||
// How many images are in the swapchain? Get 1 more than the minimum to allow triple buffering
|
||||
var image_count: u32 = swapchain_details.surface_capabilities.min_image_count + 1;
|
||||
const max_image_count = swapchain_details.surface_capabilities.max_image_count;
|
||||
|
||||
// Clamp down if higher
|
||||
// If 0, it means it's limitless
|
||||
if (max_image_count != 0 and image_count > max_image_count) {
|
||||
image_count = max_image_count;
|
||||
}
|
||||
|
||||
var swapchain_create_info: vk.SwapchainCreateInfoKHR = .{
|
||||
.image_format = surface_format.format,
|
||||
.image_color_space = surface_format.color_space,
|
||||
.present_mode = present_mode,
|
||||
.image_extent = extent,
|
||||
.min_image_count = image_count,
|
||||
.image_array_layers = 1, // Number of layers for each image
|
||||
.image_usage = .{ .color_attachment_bit = true }, // What attachment will images be used as
|
||||
.pre_transform = swapchain_details.surface_capabilities.current_transform, // Transform to perform on swapchain images
|
||||
.composite_alpha = .{ .opaque_bit_khr = true }, // How to handle blending images with external graphics (e.g.: other windows)
|
||||
.clipped = vk.TRUE, // Whether to clip parts of images not in view (e.g.: behind another window, off-screen, etc...)
|
||||
.old_swapchain = .null_handle, // Links old one to quickly share responsibilities in case it's been destroyed and replaced
|
||||
.surface = context.surface,
|
||||
.image_sharing_mode = .exclusive,
|
||||
};
|
||||
|
||||
// Get queue family indices
|
||||
const family_indices = try self.getQueueFamilies(self.physical_device);
|
||||
|
||||
// If graphic and presentation families are different, then swapchain must let images be shared between families
|
||||
|
||||
if (family_indices.graphics_family.? != family_indices.presentation_family.?) {
|
||||
const qfi = [_]u32{
|
||||
family_indices.graphics_family.?,
|
||||
family_indices.presentation_family.?,
|
||||
};
|
||||
|
||||
swapchain_create_info.image_sharing_mode = .concurrent;
|
||||
swapchain_create_info.queue_family_index_count = @intCast(qfi.len); // Number of queues to share images between
|
||||
swapchain_create_info.p_queue_family_indices = &qfi;
|
||||
}
|
||||
|
||||
self.swapchain = try self.device.createSwapchainKHR(&swapchain_create_info, null);
|
||||
self.swapchain_image_format = surface_format.format;
|
||||
self.extent = extent;
|
||||
|
||||
// Swapchain images
|
||||
var swapchain_image_count: u32 = 0;
|
||||
_ = try self.device.getSwapchainImagesKHR(self.swapchain, &swapchain_image_count, null);
|
||||
|
||||
const images = try self.allocator.alloc(vk.Image, swapchain_image_count);
|
||||
defer self.allocator.free(images);
|
||||
|
||||
_ = try self.device.getSwapchainImagesKHR(self.swapchain, &swapchain_image_count, images.ptr);
|
||||
|
||||
self.swapchain_images = try self.allocator.alloc(SwapchainImage, swapchain_image_count);
|
||||
|
||||
for (images, 0..) |image, i| {
|
||||
self.swapchain_images[i] = .{
|
||||
.image = image,
|
||||
.image_view = try self.createImageView(image, self.swapchain_image_format, .{ .color_bit = true }),
|
||||
};
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
pub fn getSwapchainDetails(allocator: std.mem.Allocator, instance: Instance, pdev: vk.PhysicalDevice, surface: vk.SurfaceKHR) !SwapchainDetails {
|
||||
// Capabilities
|
||||
const surface_capabilities = try instance.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface);
|
||||
|
||||
// Formats
|
||||
var format_count: u32 = 0;
|
||||
_ = try instance.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null);
|
||||
|
||||
const formats = try allocator.alloc(vk.SurfaceFormatKHR, format_count);
|
||||
_ = try instance.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, formats.ptr);
|
||||
|
||||
// Presentation modes
|
||||
var present_mode_count: u32 = 0;
|
||||
_ = try instance.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &present_mode_count, null);
|
||||
|
||||
const presentation_modes = try allocator.alloc(vk.PresentModeKHR, format_count);
|
||||
_ = try instance.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &present_mode_count, presentation_modes.ptr);
|
||||
|
||||
return .{
|
||||
.surface_capabilities = surface_capabilities,
|
||||
.formats = formats,
|
||||
.presentation_modes = presentation_modes,
|
||||
};
|
||||
}
|
||||
|
||||
// Format: VK_FORMAT_R8G8B8A8_UNORM (VK_FORMAT_B8G8R8A8_UNORM as backup)
|
||||
// Color space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||
fn chooseBestSurfaceFormat(formats: []vk.SurfaceFormatKHR) vk.SurfaceFormatKHR {
|
||||
// If only one format available and is undefined, then this means all formats are available
|
||||
if (formats.len == 1 and formats[0].format == vk.Format.undefined) {
|
||||
return .{
|
||||
.format = vk.Format.r8g8b8a8_srgb,
|
||||
.color_space = vk.ColorSpaceKHR.srgb_nonlinear_khr,
|
||||
};
|
||||
}
|
||||
|
||||
for (formats) |format| {
|
||||
if ((format.format == vk.Format.r8g8b8a8_srgb or format.format == vk.Format.b8g8r8a8_srgb) and format.color_space == vk.ColorSpaceKHR.srgb_nonlinear_khr) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return formats[0];
|
||||
}
|
||||
|
||||
fn chooseBestPresentationMode(presentation_modes: []vk.PresentModeKHR) vk.PresentModeKHR {
|
||||
for (presentation_modes) |presentation_mode| {
|
||||
if (presentation_mode == vk.PresentModeKHR.mailbox_khr) {
|
||||
return presentation_mode;
|
||||
}
|
||||
}
|
||||
|
||||
// Use FIFO as Vulkan spec says it must be present
|
||||
return vk.PresentModeKHR.fifo_khr;
|
||||
}
|
||||
|
||||
fn chooseSwapExtent(window: *sdl.Window, surface_capabilities: vk.SurfaceCapabilitiesKHR) vk.Extent2D {
|
||||
// If the current extent is at max value, the extent can vary. Otherwise it's the size of the window
|
||||
if (surface_capabilities.current_extent.width != std.math.maxInt(u32)) {
|
||||
return surface_capabilities.current_extent;
|
||||
}
|
||||
|
||||
// If value can very, need to set the extent manually
|
||||
const framebuffer_size = sdl.vulkan.getDrawableSize(window);
|
||||
|
||||
var extent: vk.Extent2D = .{
|
||||
.width = @intCast(framebuffer_size.width),
|
||||
.height = @intCast(framebuffer_size.height),
|
||||
};
|
||||
|
||||
// Surface also defines max and min, so make sure it's within boundaries by clamping values
|
||||
extent.width = @max(surface_capabilities.min_image_extent.width, @min(surface_capabilities.max_image_extent.width, extent.width));
|
||||
extent.height = @max(surface_capabilities.min_image_extent.height, @min(surface_capabilities.max_image_extent.height, extent.height));
|
||||
|
||||
return extent;
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
const std = @import("std");
|
||||
const vk = @import("vulkan");
|
||||
const sdl = @import("sdl2");
|
||||
const sdl = @import("sdl");
|
||||
const zm = @import("zmath");
|
||||
|
||||
const VulkanRenderer = @import("vulkan_renderer.zig").VulkanRenderer;
|
||||
|
@ -62,7 +62,7 @@ pub fn main() !void {
|
|||
defer _ = gpa.deinit();
|
||||
const allocator = gpa.allocator();
|
||||
|
||||
var vulkan_renderer = try VulkanRenderer.init(window, allocator);
|
||||
var vulkan_renderer = try VulkanRenderer.init(allocator, window);
|
||||
defer vulkan_renderer.deinit();
|
||||
|
||||
var delta = Delta.new();
|
||||
|
|
|
@ -5,8 +5,6 @@ const Instance = @import("vulkan_renderer.zig").Instance;
|
|||
const Device = @import("vulkan_renderer.zig").Device;
|
||||
const CommandBuffer = @import("vulkan_renderer.zig").CommandBuffer;
|
||||
|
||||
pub const device_extensions = [_][*:0]const u8{vk.extensions.khr_swapchain.name};
|
||||
|
||||
pub const Vector3 = @Vector(3, f32);
|
||||
pub const Vector2 = @Vector(2, f32);
|
||||
|
||||
|
@ -17,26 +15,6 @@ pub const Vertex = struct {
|
|||
tex: Vector2, // Texture coords (u, v)
|
||||
};
|
||||
|
||||
pub const QueueFamilyIndices = struct {
|
||||
graphics_family: ?u32 = null,
|
||||
presentation_family: ?u32 = null,
|
||||
|
||||
pub fn isValid(self: QueueFamilyIndices) bool {
|
||||
return self.graphics_family != null and self.presentation_family != null;
|
||||
}
|
||||
};
|
||||
|
||||
pub const SwapchainDetails = struct {
|
||||
surface_capabilities: vk.SurfaceCapabilitiesKHR,
|
||||
formats: []vk.SurfaceFormatKHR,
|
||||
presentation_modes: []vk.PresentModeKHR,
|
||||
};
|
||||
|
||||
pub const SwapchainImage = struct {
|
||||
image: vk.Image,
|
||||
image_view: vk.ImageView,
|
||||
};
|
||||
|
||||
pub fn findMemoryTypeIndex(pdev: vk.PhysicalDevice, instance: Instance, allowed_types: u32, properties: vk.MemoryPropertyFlags) u32 {
|
||||
// Get properties of physical device memory
|
||||
const memory_properties = instance.getPhysicalDeviceMemoryProperties(pdev);
|
||||
|
|
61
src/validation_layers.zig
Normal file
61
src/validation_layers.zig
Normal file
|
@ -0,0 +1,61 @@
|
|||
const std = @import("std");
|
||||
|
||||
const vk = @import("vulkan");
|
||||
const Instance = @import("Context.zig").Instance;
|
||||
|
||||
// Validation layers stuff
|
||||
pub fn createDebugMessenger(instance: Instance) !vk.DebugUtilsMessengerEXT {
|
||||
const debug_create_info = getDebugUtilsCreateInfo();
|
||||
|
||||
return try instance.createDebugUtilsMessengerEXT(&debug_create_info, null);
|
||||
}
|
||||
|
||||
pub fn getDebugUtilsCreateInfo() vk.DebugUtilsMessengerCreateInfoEXT {
|
||||
return vk.DebugUtilsMessengerCreateInfoEXT{
|
||||
.message_severity = .{ .verbose_bit_ext = true, .warning_bit_ext = true, .error_bit_ext = true },
|
||||
.message_type = .{ .general_bit_ext = true, .validation_bit_ext = true, .performance_bit_ext = true },
|
||||
.pfn_user_callback = debugCallback,
|
||||
};
|
||||
}
|
||||
|
||||
fn debugCallback(
|
||||
message_severity: vk.DebugUtilsMessageSeverityFlagsEXT,
|
||||
message_types: vk.DebugUtilsMessageTypeFlagsEXT,
|
||||
p_callback_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT,
|
||||
p_user_data: ?*anyopaque,
|
||||
) callconv(vk.vulkan_call_conv) vk.Bool32 {
|
||||
_ = p_user_data;
|
||||
const severity = getMessageSeverityLabel(message_severity);
|
||||
const message_type = getMessageTypeLabel(message_types);
|
||||
|
||||
std.debug.print("[{s}] ({s}): {s}\n=====\n", .{ severity, message_type, p_callback_data.?.p_message.? });
|
||||
return vk.TRUE;
|
||||
}
|
||||
|
||||
inline fn getMessageSeverityLabel(message_severity: vk.DebugUtilsMessageSeverityFlagsEXT) []const u8 {
|
||||
if (message_severity.verbose_bit_ext) {
|
||||
return "VERBOSE";
|
||||
} else if (message_severity.info_bit_ext) {
|
||||
return "INFO";
|
||||
} else if (message_severity.warning_bit_ext) {
|
||||
return "WARNING";
|
||||
} else if (message_severity.error_bit_ext) {
|
||||
return "ERROR";
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
inline fn getMessageTypeLabel(message_types: vk.DebugUtilsMessageTypeFlagsEXT) []const u8 {
|
||||
if (message_types.general_bit_ext) {
|
||||
return "general";
|
||||
} else if (message_types.validation_bit_ext) {
|
||||
return "validation";
|
||||
} else if (message_types.performance_bit_ext) {
|
||||
return "performance";
|
||||
} else if (message_types.device_address_binding_bit_ext) {
|
||||
return "device_address_binding";
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
const std = @import("std");
|
||||
const sdl = @import("sdl2");
|
||||
const sdl = @import("sdl");
|
||||
const vk = @import("vulkan");
|
||||
const builtin = @import("builtin");
|
||||
const shaders = @import("shaders");
|
||||
|
@ -9,39 +9,19 @@ const ai = @import("assimp.zig").c;
|
|||
|
||||
const StringUtils = @import("string_utils.zig");
|
||||
const Utilities = @import("utilities.zig");
|
||||
const QueueFamilyIndices = Utilities.QueueFamilyIndices;
|
||||
const SwapchainDetails = Utilities.SwapchainDetails;
|
||||
const SwapchainImage = Utilities.SwapchainImage;
|
||||
const Vertex = Utilities.Vertex;
|
||||
const Vector3 = Utilities.Vector3;
|
||||
|
||||
const Context = @import("Context.zig");
|
||||
const Instance = @import("Context.zig").Instance;
|
||||
const Swapchain = @import("Swapchain.zig");
|
||||
|
||||
const Mesh = @import("Mesh.zig");
|
||||
const MeshModel = @import("MeshModel.zig");
|
||||
|
||||
const enable_validation_layers = builtin.mode == .Debug;
|
||||
const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"};
|
||||
|
||||
const MAX_FRAME_DRAWS: u32 = 2;
|
||||
const MAX_OBJECTS: u32 = 20;
|
||||
|
||||
const apis: []const vk.ApiInfo = &.{
|
||||
vk.features.version_1_0,
|
||||
vk.features.version_1_1,
|
||||
vk.features.version_1_2,
|
||||
vk.features.version_1_3,
|
||||
vk.extensions.khr_surface,
|
||||
vk.extensions.khr_swapchain,
|
||||
vk.extensions.ext_debug_utils,
|
||||
};
|
||||
|
||||
const BaseDispatch = vk.BaseWrapper(apis);
|
||||
const InstanceDispatch = vk.InstanceWrapper(apis);
|
||||
const DeviceDispatch = vk.DeviceWrapper(apis);
|
||||
|
||||
pub const Instance = vk.InstanceProxy(apis);
|
||||
pub const Device = vk.DeviceProxy(apis);
|
||||
pub const Queue = vk.QueueProxy(apis);
|
||||
pub const CommandBuffer = vk.CommandBufferProxy(apis);
|
||||
pub const CommandBuffer = vk.CommandBufferProxy(Context.apis);
|
||||
|
||||
const UboViewProjection = struct {
|
||||
projection: zm.Mat align(16),
|
||||
|
@ -57,31 +37,20 @@ pub const VulkanRenderer = struct {
|
|||
|
||||
allocator: std.mem.Allocator,
|
||||
|
||||
vkb: BaseDispatch,
|
||||
|
||||
window: sdl.Window,
|
||||
|
||||
current_frame: u32 = 0,
|
||||
|
||||
context: Context,
|
||||
|
||||
swapchain: Swapchain,
|
||||
|
||||
// Scene settings
|
||||
ubo_view_projection: UboViewProjection,
|
||||
|
||||
// Main
|
||||
instance: Instance,
|
||||
physical_device: vk.PhysicalDevice,
|
||||
device: Device,
|
||||
graphics_queue: Queue,
|
||||
presentation_queue: Queue,
|
||||
surface: vk.SurfaceKHR,
|
||||
swapchain: vk.SwapchainKHR,
|
||||
viewport: vk.Viewport,
|
||||
scissor: vk.Rect2D,
|
||||
texture_sampler: vk.Sampler,
|
||||
|
||||
swapchain_images: []SwapchainImage,
|
||||
swapchain_framebuffers: []vk.Framebuffer,
|
||||
command_buffers: []CommandBuffer,
|
||||
|
||||
depth_buffer_image: []vk.Image,
|
||||
depth_buffer_image_memory: []vk.DeviceMemory,
|
||||
depth_buffer_image_view: []vk.ImageView,
|
||||
|
@ -106,6 +75,9 @@ pub const VulkanRenderer = struct {
|
|||
vp_uniform_buffer: []vk.Buffer,
|
||||
vp_uniform_buffer_memory: []vk.DeviceMemory,
|
||||
|
||||
// TODO
|
||||
command_buffers: []CommandBuffer,
|
||||
|
||||
// Assets
|
||||
image_files: std.ArrayList(img.Image),
|
||||
texture_images: std.ArrayList(vk.Image),
|
||||
|
@ -135,28 +107,13 @@ pub const VulkanRenderer = struct {
|
|||
render_finished: [MAX_FRAME_DRAWS]vk.Semaphore,
|
||||
draw_fences: [MAX_FRAME_DRAWS]vk.Fence,
|
||||
|
||||
debug_utils: ?vk.DebugUtilsMessengerEXT,
|
||||
|
||||
pub fn init(window: sdl.Window, allocator: std.mem.Allocator) !Self {
|
||||
pub fn init(allocator: std.mem.Allocator, window: sdl.Window) !Self {
|
||||
var self: Self = undefined;
|
||||
|
||||
self.window = window;
|
||||
self.context = try Context.init(allocator, window);
|
||||
self.current_frame = 0;
|
||||
self.allocator = allocator;
|
||||
self.vkb = try BaseDispatch.load(try sdl.vulkan.getVkGetInstanceProcAddr());
|
||||
self.swapchain = try Swapchain.create(allocator, self.context);
|
||||
|
||||
img.init(allocator);
|
||||
|
||||
try self.createInstance();
|
||||
try self.createSurface();
|
||||
|
||||
if (enable_validation_layers) {
|
||||
self.debug_utils = try createDebugMessenger(self.instance);
|
||||
}
|
||||
|
||||
try self.getPhysicalDevice();
|
||||
try self.createLogicalDevice();
|
||||
try self.createSwapchain();
|
||||
try self.createColourBufferImage();
|
||||
try self.createDepthBufferImage();
|
||||
try self.createRenderPass();
|
||||
|
@ -272,10 +229,6 @@ pub const VulkanRenderer = struct {
|
|||
pub fn deinit(self: *Self) void {
|
||||
self.device.deviceWaitIdle() catch undefined;
|
||||
|
||||
if (enable_validation_layers) {
|
||||
self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null);
|
||||
}
|
||||
|
||||
for (0..self.model_list.items.len) |i| {
|
||||
self.model_list.items[i].destroy();
|
||||
}
|
||||
|
@ -366,193 +319,8 @@ pub const VulkanRenderer = struct {
|
|||
|
||||
self.allocator.free(self.swapchain_images);
|
||||
self.device.destroySwapchainKHR(self.swapchain, null);
|
||||
self.device.destroyDevice(null);
|
||||
self.instance.destroySurfaceKHR(self.surface, null);
|
||||
self.instance.destroyInstance(null);
|
||||
|
||||
self.allocator.destroy(self.device.wrapper);
|
||||
self.allocator.destroy(self.instance.wrapper);
|
||||
|
||||
img.deinit();
|
||||
}
|
||||
|
||||
fn createInstance(self: *Self) !void {
|
||||
if (enable_validation_layers and !self.checkValidationLayersSupport()) {
|
||||
// TODO Better error
|
||||
return error.LayerNotPresent;
|
||||
}
|
||||
|
||||
const extensions = try self.getRequiredExtensions();
|
||||
defer self.allocator.free(extensions);
|
||||
|
||||
std.debug.print("[Required instance extensions]\n", .{});
|
||||
for (extensions) |ext| {
|
||||
std.debug.print("\t- {s}\n", .{ext});
|
||||
}
|
||||
|
||||
if (!try self.checkInstanceExtensions(&extensions)) {
|
||||
return error.ExtensionNotPresent;
|
||||
}
|
||||
|
||||
const app_info = vk.ApplicationInfo{
|
||||
.p_application_name = "Vulkan SDL Test",
|
||||
.application_version = vk.makeApiVersion(0, 0, 1, 0),
|
||||
.p_engine_name = "Vulkan SDL Test",
|
||||
.engine_version = vk.makeApiVersion(0, 0, 1, 0),
|
||||
.api_version = vk.API_VERSION_1_3,
|
||||
};
|
||||
|
||||
var instance_create_info: vk.InstanceCreateInfo = .{
|
||||
.p_application_info = &app_info,
|
||||
.enabled_extension_count = @intCast(extensions.len),
|
||||
.pp_enabled_extension_names = @ptrCast(extensions),
|
||||
};
|
||||
|
||||
if (enable_validation_layers) {
|
||||
const debug_create_info = getDebugUtilsCreateInfo();
|
||||
|
||||
instance_create_info.enabled_layer_count = @intCast(validation_layers.len);
|
||||
instance_create_info.pp_enabled_layer_names = &validation_layers;
|
||||
instance_create_info.p_next = &debug_create_info;
|
||||
}
|
||||
|
||||
const instance_handle = try self.vkb.createInstance(&instance_create_info, null);
|
||||
const vki = try self.allocator.create(InstanceDispatch);
|
||||
errdefer self.allocator.destroy(vki);
|
||||
vki.* = try InstanceDispatch.load(instance_handle, self.vkb.dispatch.vkGetInstanceProcAddr);
|
||||
|
||||
self.instance = Instance.init(instance_handle, vki);
|
||||
}
|
||||
|
||||
fn createSurface(self: *Self) !void {
|
||||
self.surface = try sdl.vulkan.createSurface(self.window, self.instance.handle);
|
||||
}
|
||||
|
||||
fn createLogicalDevice(self: *Self) !void {
|
||||
const indices = try self.getQueueFamilies(self.physical_device);
|
||||
// 1 is the highest priority
|
||||
const priority = [_]f32{1};
|
||||
|
||||
const qci = [_]vk.DeviceQueueCreateInfo{
|
||||
.{
|
||||
.queue_family_index = indices.graphics_family.?,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = &priority,
|
||||
},
|
||||
.{
|
||||
.queue_family_index = indices.presentation_family.?,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = &priority,
|
||||
},
|
||||
};
|
||||
|
||||
const queue_count: u32 = if (indices.graphics_family.? == indices.presentation_family.?)
|
||||
1
|
||||
else
|
||||
2;
|
||||
|
||||
// Device features
|
||||
const device_features: vk.PhysicalDeviceFeatures = .{
|
||||
.sampler_anisotropy = vk.TRUE, // Enable anisotropy
|
||||
};
|
||||
|
||||
const device_create_info: vk.DeviceCreateInfo = .{
|
||||
.queue_create_info_count = queue_count,
|
||||
.p_queue_create_infos = &qci,
|
||||
.pp_enabled_extension_names = &Utilities.device_extensions,
|
||||
.enabled_extension_count = @intCast(Utilities.device_extensions.len),
|
||||
.p_enabled_features = &device_features,
|
||||
};
|
||||
|
||||
const device_handle = try self.instance.createDevice(self.physical_device, &device_create_info, null);
|
||||
|
||||
const vkd = try self.allocator.create(DeviceDispatch);
|
||||
errdefer self.allocator.destroy(vkd);
|
||||
vkd.* = try DeviceDispatch.load(device_handle, self.instance.wrapper.dispatch.vkGetDeviceProcAddr);
|
||||
|
||||
self.device = Device.init(device_handle, vkd);
|
||||
|
||||
const queues = try self.getDeviceQueues();
|
||||
|
||||
self.graphics_queue = Queue.init(queues[0], self.device.wrapper);
|
||||
self.presentation_queue = Queue.init(queues[1], self.device.wrapper);
|
||||
}
|
||||
|
||||
fn createSwapchain(self: *Self) !void {
|
||||
const swapchain_details = try self.getSwapchainDetails(self.physical_device);
|
||||
defer self.allocator.free(swapchain_details.formats);
|
||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||
|
||||
// 1. Choose best surface format
|
||||
const surface_format = chooseBestSurfaceFormat(swapchain_details.formats);
|
||||
// 2. Choose best presentation mode
|
||||
const present_mode = chooseBestPresentationMode(swapchain_details.presentation_modes);
|
||||
// 3. Choose swapchain image resolution
|
||||
const extent = chooseSwapExtent(&self.window, swapchain_details.surface_capabilities);
|
||||
|
||||
// How many images are in the swapchain? Get 1 more than the minimum to allow triple buffering
|
||||
var image_count: u32 = swapchain_details.surface_capabilities.min_image_count + 1;
|
||||
const max_image_count = swapchain_details.surface_capabilities.max_image_count;
|
||||
|
||||
// Clamp down if higher
|
||||
// If 0, it means it's limitless
|
||||
if (max_image_count != 0 and image_count > max_image_count) {
|
||||
image_count = max_image_count;
|
||||
}
|
||||
|
||||
var swapchain_create_info: vk.SwapchainCreateInfoKHR = .{
|
||||
.image_format = surface_format.format,
|
||||
.image_color_space = surface_format.color_space,
|
||||
.present_mode = present_mode,
|
||||
.image_extent = extent,
|
||||
.min_image_count = image_count,
|
||||
.image_array_layers = 1, // Number of layers for each image
|
||||
.image_usage = .{ .color_attachment_bit = true }, // What attachment will images be used as
|
||||
.pre_transform = swapchain_details.surface_capabilities.current_transform, // Transform to perform on swapchain images
|
||||
.composite_alpha = .{ .opaque_bit_khr = true }, // How to handle blending images with external graphics (e.g.: other windows)
|
||||
.clipped = vk.TRUE, // Whether to clip parts of images not in view (e.g.: behind another window, off-screen, etc...)
|
||||
.old_swapchain = .null_handle, // Links old one to quickly share responsibilities in case it's been destroyed and replaced
|
||||
.surface = self.surface,
|
||||
.image_sharing_mode = .exclusive,
|
||||
};
|
||||
|
||||
// Get queue family indices
|
||||
const family_indices = try self.getQueueFamilies(self.physical_device);
|
||||
|
||||
// If graphic and presentation families are different, then swapchain must let images be shared between families
|
||||
|
||||
if (family_indices.graphics_family.? != family_indices.presentation_family.?) {
|
||||
const qfi = [_]u32{
|
||||
family_indices.graphics_family.?,
|
||||
family_indices.presentation_family.?,
|
||||
};
|
||||
|
||||
swapchain_create_info.image_sharing_mode = .concurrent;
|
||||
swapchain_create_info.queue_family_index_count = @intCast(qfi.len); // Number of queues to share images between
|
||||
swapchain_create_info.p_queue_family_indices = &qfi;
|
||||
}
|
||||
|
||||
self.swapchain = try self.device.createSwapchainKHR(&swapchain_create_info, null);
|
||||
self.swapchain_image_format = surface_format.format;
|
||||
self.extent = extent;
|
||||
|
||||
// Swapchain images
|
||||
var swapchain_image_count: u32 = 0;
|
||||
_ = try self.device.getSwapchainImagesKHR(self.swapchain, &swapchain_image_count, null);
|
||||
|
||||
const images = try self.allocator.alloc(vk.Image, swapchain_image_count);
|
||||
defer self.allocator.free(images);
|
||||
|
||||
_ = try self.device.getSwapchainImagesKHR(self.swapchain, &swapchain_image_count, images.ptr);
|
||||
|
||||
self.swapchain_images = try self.allocator.alloc(SwapchainImage, swapchain_image_count);
|
||||
|
||||
for (images, 0..) |image, i| {
|
||||
self.swapchain_images[i] = .{
|
||||
.image = image,
|
||||
.image_view = try self.createImageView(image, self.swapchain_image_format, .{ .color_bit = true }),
|
||||
};
|
||||
}
|
||||
self.context.deinit();
|
||||
}
|
||||
|
||||
fn createRenderPass(self: *Self) !void {
|
||||
|
@ -1539,193 +1307,6 @@ pub const VulkanRenderer = struct {
|
|||
try command_buffer.endCommandBuffer();
|
||||
}
|
||||
|
||||
fn getPhysicalDevice(self: *Self) !void {
|
||||
var pdev_count: u32 = 0;
|
||||
_ = try self.instance.enumeratePhysicalDevices(&pdev_count, null);
|
||||
|
||||
const pdevs = try self.allocator.alloc(vk.PhysicalDevice, pdev_count);
|
||||
defer self.allocator.free(pdevs);
|
||||
|
||||
_ = try self.instance.enumeratePhysicalDevices(&pdev_count, pdevs.ptr);
|
||||
|
||||
for (pdevs) |pdev| {
|
||||
if (self.checkDeviceSuitable(pdev)) {
|
||||
self.physical_device = pdev;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
// TODO Obviously needs to be something else
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
fn getRequiredExtensions(self: Self) ![][*:0]const u8 {
|
||||
var ext_count = sdl.vulkan.getInstanceExtensionsCount(self.window);
|
||||
|
||||
if (enable_validation_layers) {
|
||||
ext_count += 1;
|
||||
}
|
||||
|
||||
var extensions = try self.allocator.alloc([*:0]const u8, ext_count);
|
||||
_ = try sdl.vulkan.getInstanceExtensions(self.window, extensions);
|
||||
|
||||
if (enable_validation_layers) {
|
||||
extensions[extensions.len - 1] = vk.extensions.ext_debug_utils.name;
|
||||
}
|
||||
|
||||
return extensions;
|
||||
}
|
||||
|
||||
fn getQueueFamilies(self: Self, pdev: vk.PhysicalDevice) !QueueFamilyIndices {
|
||||
var indices: QueueFamilyIndices = .{ .graphics_family = null };
|
||||
|
||||
var queue_family_count: u32 = 0;
|
||||
self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, null);
|
||||
|
||||
const queue_family_list = try self.allocator.alloc(vk.QueueFamilyProperties, queue_family_count);
|
||||
defer self.allocator.free(queue_family_list);
|
||||
|
||||
self.instance.getPhysicalDeviceQueueFamilyProperties(pdev, &queue_family_count, queue_family_list.ptr);
|
||||
|
||||
for (queue_family_list, 0..) |queue_family, i| {
|
||||
if (queue_family.queue_count > 0 and queue_family.queue_flags.graphics_bit) {
|
||||
indices.graphics_family = @intCast(i);
|
||||
}
|
||||
|
||||
const presentation_support = try self.instance.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(i), self.surface);
|
||||
if (queue_family.queue_count > 0 and presentation_support == vk.TRUE) {
|
||||
indices.presentation_family = @intCast(i);
|
||||
}
|
||||
|
||||
if (indices.isValid()) {
|
||||
return indices;
|
||||
}
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
|
||||
fn getDeviceQueues(self: Self) ![2]vk.Queue {
|
||||
const indices = try self.getQueueFamilies(self.physical_device);
|
||||
|
||||
const graphics_queue = self.device.getDeviceQueue(indices.graphics_family.?, 0);
|
||||
const presentation_queue = self.device.getDeviceQueue(indices.presentation_family.?, 0);
|
||||
return .{ graphics_queue, presentation_queue };
|
||||
}
|
||||
|
||||
fn checkInstanceExtensions(self: Self, required_extensions: *const [][*:0]const u8) !bool {
|
||||
var prop_count: u32 = 0;
|
||||
_ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, null);
|
||||
|
||||
const props = try self.allocator.alloc(vk.ExtensionProperties, prop_count);
|
||||
defer self.allocator.free(props);
|
||||
|
||||
_ = try self.vkb.enumerateInstanceExtensionProperties(null, &prop_count, props.ptr);
|
||||
|
||||
for (required_extensions.*) |required_extension| {
|
||||
for (props) |prop| {
|
||||
if (std.mem.eql(u8, std.mem.sliceTo(&prop.extension_name, 0), std.mem.span(required_extension))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn checkDeviceExtensions(self: Self, pdev: vk.PhysicalDevice) !bool {
|
||||
var prop_count: u32 = 0;
|
||||
_ = try self.instance.enumerateDeviceExtensionProperties(pdev, null, &prop_count, null);
|
||||
|
||||
if (prop_count == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const props = try self.allocator.alloc(vk.ExtensionProperties, prop_count);
|
||||
defer self.allocator.free(props);
|
||||
|
||||
_ = try self.instance.enumerateDeviceExtensionProperties(pdev, null, &prop_count, props.ptr);
|
||||
|
||||
for (Utilities.device_extensions) |device_extension| {
|
||||
for (props) |prop| {
|
||||
if (std.mem.eql(u8, std.mem.sliceTo(&prop.extension_name, 0), std.mem.span(device_extension))) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
fn checkDeviceSuitable(self: Self, pdev: vk.PhysicalDevice) bool {
|
||||
const pdev_properties = self.instance.getPhysicalDeviceProperties(pdev);
|
||||
|
||||
if (pdev_properties.device_type == .cpu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const pdev_features = self.instance.getPhysicalDeviceFeatures(pdev);
|
||||
const queue_family_indices = self.getQueueFamilies(pdev) catch return false;
|
||||
const extension_support = self.checkDeviceExtensions(pdev) catch return false;
|
||||
|
||||
const swapchain_details = self.getSwapchainDetails(pdev) catch return false;
|
||||
defer self.allocator.free(swapchain_details.formats);
|
||||
defer self.allocator.free(swapchain_details.presentation_modes);
|
||||
|
||||
const swapchain_valid = swapchain_details.formats.len != 0 and swapchain_details.formats.len != 0;
|
||||
|
||||
return queue_family_indices.isValid() and extension_support and swapchain_valid and pdev_features.sampler_anisotropy == vk.TRUE;
|
||||
}
|
||||
|
||||
fn checkValidationLayersSupport(self: Self) bool {
|
||||
var layer_count: u32 = undefined;
|
||||
_ = self.vkb.enumerateInstanceLayerProperties(&layer_count, null) catch return false;
|
||||
|
||||
const available_layers = self.allocator.alloc(vk.LayerProperties, layer_count) catch unreachable;
|
||||
defer self.allocator.free(available_layers);
|
||||
|
||||
_ = self.vkb.enumerateInstanceLayerProperties(&layer_count, available_layers.ptr) catch return false;
|
||||
|
||||
for (validation_layers) |validation_layer| {
|
||||
for (available_layers) |available_layer| {
|
||||
if (std.mem.eql(u8, std.mem.span(validation_layer), std.mem.sliceTo(&available_layer.layer_name, 0))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
fn getSwapchainDetails(self: Self, pdev: vk.PhysicalDevice) !SwapchainDetails {
|
||||
// Capabilities
|
||||
const surface_capabilities = try self.instance.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, self.surface);
|
||||
|
||||
// Formats
|
||||
var format_count: u32 = 0;
|
||||
_ = try self.instance.getPhysicalDeviceSurfaceFormatsKHR(pdev, self.surface, &format_count, null);
|
||||
|
||||
const formats = try self.allocator.alloc(vk.SurfaceFormatKHR, format_count);
|
||||
_ = try self.instance.getPhysicalDeviceSurfaceFormatsKHR(pdev, self.surface, &format_count, formats.ptr);
|
||||
|
||||
// Presentation modes
|
||||
var present_mode_count: u32 = 0;
|
||||
_ = try self.instance.getPhysicalDeviceSurfacePresentModesKHR(pdev, self.surface, &present_mode_count, null);
|
||||
|
||||
const presentation_modes = try self.allocator.alloc(vk.PresentModeKHR, format_count);
|
||||
_ = try self.instance.getPhysicalDeviceSurfacePresentModesKHR(pdev, self.surface, &present_mode_count, presentation_modes.ptr);
|
||||
|
||||
return .{
|
||||
.surface_capabilities = surface_capabilities,
|
||||
.formats = formats,
|
||||
.presentation_modes = presentation_modes,
|
||||
};
|
||||
}
|
||||
|
||||
fn createImage(
|
||||
self: *Self,
|
||||
width: u32,
|
||||
|
@ -2016,58 +1597,6 @@ pub const VulkanRenderer = struct {
|
|||
}
|
||||
};
|
||||
|
||||
// Format: VK_FORMAT_R8G8B8A8_UNORM (VK_FORMAT_B8G8R8A8_UNORM as backup)
|
||||
// Color space: VK_COLOR_SPACE_SRGB_NONLINEAR_KHR
|
||||
fn chooseBestSurfaceFormat(formats: []vk.SurfaceFormatKHR) vk.SurfaceFormatKHR {
|
||||
// If only one format available and is undefined, then this means all formats are available
|
||||
if (formats.len == 1 and formats[0].format == vk.Format.undefined) {
|
||||
return .{
|
||||
.format = vk.Format.r8g8b8a8_srgb,
|
||||
.color_space = vk.ColorSpaceKHR.srgb_nonlinear_khr,
|
||||
};
|
||||
}
|
||||
|
||||
for (formats) |format| {
|
||||
if ((format.format == vk.Format.r8g8b8a8_srgb or format.format == vk.Format.b8g8r8a8_srgb) and format.color_space == vk.ColorSpaceKHR.srgb_nonlinear_khr) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return formats[0];
|
||||
}
|
||||
|
||||
fn chooseBestPresentationMode(presentation_modes: []vk.PresentModeKHR) vk.PresentModeKHR {
|
||||
for (presentation_modes) |presentation_mode| {
|
||||
if (presentation_mode == vk.PresentModeKHR.mailbox_khr) {
|
||||
return presentation_mode;
|
||||
}
|
||||
}
|
||||
|
||||
// Use FIFO as Vulkan spec says it must be present
|
||||
return vk.PresentModeKHR.fifo_khr;
|
||||
}
|
||||
|
||||
fn chooseSwapExtent(window: *sdl.Window, surface_capabilities: vk.SurfaceCapabilitiesKHR) vk.Extent2D {
|
||||
// If the current extent is at max value, the extent can vary. Otherwise it's the size of the window
|
||||
if (surface_capabilities.current_extent.width != std.math.maxInt(u32)) {
|
||||
return surface_capabilities.current_extent;
|
||||
}
|
||||
|
||||
// If value can very, need to set the extent manually
|
||||
const framebuffer_size = sdl.vulkan.getDrawableSize(window);
|
||||
|
||||
var extent: vk.Extent2D = .{
|
||||
.width = @intCast(framebuffer_size.width),
|
||||
.height = @intCast(framebuffer_size.height),
|
||||
};
|
||||
|
||||
// Surface also defines max and min, so make sure it's within boundaries by clamping values
|
||||
extent.width = @max(surface_capabilities.min_image_extent.width, @min(surface_capabilities.max_image_extent.width, extent.width));
|
||||
extent.height = @max(surface_capabilities.min_image_extent.height, @min(surface_capabilities.max_image_extent.height, extent.height));
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
fn chooseSupportedFormat(
|
||||
pdev: vk.PhysicalDevice,
|
||||
instance: Instance,
|
||||
|
@ -2091,60 +1620,3 @@ fn chooseSupportedFormat(
|
|||
|
||||
return null;
|
||||
}
|
||||
|
||||
// Validation layers stuff
|
||||
fn createDebugMessenger(instance: Instance) !vk.DebugUtilsMessengerEXT {
|
||||
const debug_create_info = getDebugUtilsCreateInfo();
|
||||
|
||||
return try instance.createDebugUtilsMessengerEXT(&debug_create_info, null);
|
||||
}
|
||||
|
||||
fn getDebugUtilsCreateInfo() vk.DebugUtilsMessengerCreateInfoEXT {
|
||||
return vk.DebugUtilsMessengerCreateInfoEXT{
|
||||
.message_severity = .{ .verbose_bit_ext = true, .warning_bit_ext = true, .error_bit_ext = true },
|
||||
.message_type = .{ .general_bit_ext = true, .validation_bit_ext = true, .performance_bit_ext = true },
|
||||
.pfn_user_callback = debugCallback,
|
||||
};
|
||||
}
|
||||
|
||||
fn debugCallback(
|
||||
message_severity: vk.DebugUtilsMessageSeverityFlagsEXT,
|
||||
message_types: vk.DebugUtilsMessageTypeFlagsEXT,
|
||||
p_callback_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT,
|
||||
p_user_data: ?*anyopaque,
|
||||
) callconv(vk.vulkan_call_conv) vk.Bool32 {
|
||||
_ = p_user_data;
|
||||
const severity = getMessageSeverityLabel(message_severity);
|
||||
const message_type = getMessageTypeLabel(message_types);
|
||||
|
||||
std.debug.print("[{s}] ({s}): {s}\n=====\n", .{ severity, message_type, p_callback_data.?.p_message.? });
|
||||
return vk.TRUE;
|
||||
}
|
||||
|
||||
inline fn getMessageSeverityLabel(message_severity: vk.DebugUtilsMessageSeverityFlagsEXT) []const u8 {
|
||||
if (message_severity.verbose_bit_ext) {
|
||||
return "VERBOSE";
|
||||
} else if (message_severity.info_bit_ext) {
|
||||
return "INFO";
|
||||
} else if (message_severity.warning_bit_ext) {
|
||||
return "WARNING";
|
||||
} else if (message_severity.error_bit_ext) {
|
||||
return "ERROR";
|
||||
} else {
|
||||
unreachable;
|
||||
}
|
||||
}
|
||||
|
||||
inline fn getMessageTypeLabel(message_types: vk.DebugUtilsMessageTypeFlagsEXT) []const u8 {
|
||||
if (message_types.general_bit_ext) {
|
||||
return "general";
|
||||
} else if (message_types.validation_bit_ext) {
|
||||
return "validation";
|
||||
} else if (message_types.performance_bit_ext) {
|
||||
return "performance";
|
||||
} else if (message_types.device_address_binding_bit_ext) {
|
||||
return "device_address_binding";
|
||||
} else {
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue