forked from mirror/vulkan-zig
This unifies command info even more, by making the user first construct a list of `ApiInfo` to represent the api that they want to use. This allows for much easier composition of different extensions and features.
293 lines
9.5 KiB
Zig
293 lines
9.5 KiB
Zig
const std = @import("std");
|
|
const vk = @import("vulkan");
|
|
const c = @import("c.zig");
|
|
const Allocator = std.mem.Allocator;
|
|
|
|
const required_device_extensions = [_][*:0]const u8{vk.extensions.khr_swapchain.name};
|
|
|
|
/// To construct base, instance and device wrappers for vulkan-zig, you need to pass a list of 'apis' to it.
|
|
const apis: []const vk.ApiInfo = &.{
|
|
// You can either add invidiual functions by manually creating an 'api'
|
|
.{
|
|
.base_commands = .{
|
|
.createInstance = true,
|
|
},
|
|
.instance_commands = .{
|
|
.createDevice = true,
|
|
},
|
|
},
|
|
// Or you can add entire feature sets or extensions
|
|
vk.features.version_1_0,
|
|
vk.extensions.khr_surface,
|
|
vk.extensions.khr_swapchain,
|
|
};
|
|
|
|
/// Next, pass the `apis` to the wrappers to create dispatch tables.
|
|
const BaseDispatch = vk.BaseWrapper(apis);
|
|
const InstanceDispatch = vk.InstanceWrapper(apis);
|
|
const DeviceDispatch = vk.DeviceWrapper(apis);
|
|
|
|
pub const GraphicsContext = struct {
|
|
vkb: BaseDispatch,
|
|
vki: InstanceDispatch,
|
|
vkd: DeviceDispatch,
|
|
|
|
instance: vk.Instance,
|
|
surface: vk.SurfaceKHR,
|
|
pdev: vk.PhysicalDevice,
|
|
props: vk.PhysicalDeviceProperties,
|
|
mem_props: vk.PhysicalDeviceMemoryProperties,
|
|
|
|
dev: vk.Device,
|
|
graphics_queue: Queue,
|
|
present_queue: Queue,
|
|
|
|
pub fn init(allocator: Allocator, app_name: [*:0]const u8, window: *c.GLFWwindow) !GraphicsContext {
|
|
var self: GraphicsContext = undefined;
|
|
self.vkb = try BaseDispatch.load(c.glfwGetInstanceProcAddress);
|
|
|
|
var glfw_exts_count: u32 = 0;
|
|
const glfw_exts = c.glfwGetRequiredInstanceExtensions(&glfw_exts_count);
|
|
|
|
const app_info = vk.ApplicationInfo{
|
|
.p_application_name = app_name,
|
|
.application_version = vk.makeApiVersion(0, 0, 0, 0),
|
|
.p_engine_name = app_name,
|
|
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
|
|
.api_version = vk.API_VERSION_1_2,
|
|
};
|
|
|
|
self.instance = try self.vkb.createInstance(&.{
|
|
.p_application_info = &app_info,
|
|
.enabled_extension_count = glfw_exts_count,
|
|
.pp_enabled_extension_names = @as([*]const [*:0]const u8, @ptrCast(glfw_exts)),
|
|
}, null);
|
|
|
|
self.vki = try InstanceDispatch.load(self.instance, self.vkb.dispatch.vkGetInstanceProcAddr);
|
|
errdefer self.vki.destroyInstance(self.instance, null);
|
|
|
|
self.surface = try createSurface(self.instance, window);
|
|
errdefer self.vki.destroySurfaceKHR(self.instance, self.surface, null);
|
|
|
|
const candidate = try pickPhysicalDevice(self.vki, self.instance, allocator, self.surface);
|
|
self.pdev = candidate.pdev;
|
|
self.props = candidate.props;
|
|
self.dev = try initializeCandidate(self.vki, candidate);
|
|
self.vkd = try DeviceDispatch.load(self.dev, self.vki.dispatch.vkGetDeviceProcAddr);
|
|
errdefer self.vkd.destroyDevice(self.dev, null);
|
|
|
|
self.graphics_queue = Queue.init(self.vkd, self.dev, candidate.queues.graphics_family);
|
|
self.present_queue = Queue.init(self.vkd, self.dev, candidate.queues.present_family);
|
|
|
|
self.mem_props = self.vki.getPhysicalDeviceMemoryProperties(self.pdev);
|
|
|
|
return self;
|
|
}
|
|
|
|
pub fn deinit(self: GraphicsContext) void {
|
|
self.vkd.destroyDevice(self.dev, null);
|
|
self.vki.destroySurfaceKHR(self.instance, self.surface, null);
|
|
self.vki.destroyInstance(self.instance, null);
|
|
}
|
|
|
|
pub fn deviceName(self: *const GraphicsContext) []const u8 {
|
|
return std.mem.sliceTo(&self.props.device_name, 0);
|
|
}
|
|
|
|
pub fn findMemoryTypeIndex(self: GraphicsContext, memory_type_bits: u32, flags: vk.MemoryPropertyFlags) !u32 {
|
|
for (self.mem_props.memory_types[0..self.mem_props.memory_type_count], 0..) |mem_type, i| {
|
|
if (memory_type_bits & (@as(u32, 1) << @truncate(i)) != 0 and mem_type.property_flags.contains(flags)) {
|
|
return @truncate(i);
|
|
}
|
|
}
|
|
|
|
return error.NoSuitableMemoryType;
|
|
}
|
|
|
|
pub fn allocate(self: GraphicsContext, requirements: vk.MemoryRequirements, flags: vk.MemoryPropertyFlags) !vk.DeviceMemory {
|
|
return try self.vkd.allocateMemory(self.dev, &.{
|
|
.allocation_size = requirements.size,
|
|
.memory_type_index = try self.findMemoryTypeIndex(requirements.memory_type_bits, flags),
|
|
}, null);
|
|
}
|
|
};
|
|
|
|
pub const Queue = struct {
|
|
handle: vk.Queue,
|
|
family: u32,
|
|
|
|
fn init(vkd: DeviceDispatch, dev: vk.Device, family: u32) Queue {
|
|
return .{
|
|
.handle = vkd.getDeviceQueue(dev, family, 0),
|
|
.family = family,
|
|
};
|
|
}
|
|
};
|
|
|
|
fn createSurface(instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR {
|
|
var surface: vk.SurfaceKHR = undefined;
|
|
if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) {
|
|
return error.SurfaceInitFailed;
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
fn initializeCandidate(vki: InstanceDispatch, candidate: DeviceCandidate) !vk.Device {
|
|
const priority = [_]f32{1};
|
|
const qci = [_]vk.DeviceQueueCreateInfo{
|
|
.{
|
|
.queue_family_index = candidate.queues.graphics_family,
|
|
.queue_count = 1,
|
|
.p_queue_priorities = &priority,
|
|
},
|
|
.{
|
|
.queue_family_index = candidate.queues.present_family,
|
|
.queue_count = 1,
|
|
.p_queue_priorities = &priority,
|
|
},
|
|
};
|
|
|
|
const queue_count: u32 = if (candidate.queues.graphics_family == candidate.queues.present_family)
|
|
1
|
|
else
|
|
2;
|
|
|
|
return try vki.createDevice(candidate.pdev, &.{
|
|
.queue_create_info_count = queue_count,
|
|
.p_queue_create_infos = &qci,
|
|
.enabled_extension_count = required_device_extensions.len,
|
|
.pp_enabled_extension_names = @as([*]const [*:0]const u8, @ptrCast(&required_device_extensions)),
|
|
}, null);
|
|
}
|
|
|
|
const DeviceCandidate = struct {
|
|
pdev: vk.PhysicalDevice,
|
|
props: vk.PhysicalDeviceProperties,
|
|
queues: QueueAllocation,
|
|
};
|
|
|
|
const QueueAllocation = struct {
|
|
graphics_family: u32,
|
|
present_family: u32,
|
|
};
|
|
|
|
fn pickPhysicalDevice(
|
|
vki: InstanceDispatch,
|
|
instance: vk.Instance,
|
|
allocator: Allocator,
|
|
surface: vk.SurfaceKHR,
|
|
) !DeviceCandidate {
|
|
var device_count: u32 = undefined;
|
|
_ = try vki.enumeratePhysicalDevices(instance, &device_count, null);
|
|
|
|
const pdevs = try allocator.alloc(vk.PhysicalDevice, device_count);
|
|
defer allocator.free(pdevs);
|
|
|
|
_ = try vki.enumeratePhysicalDevices(instance, &device_count, pdevs.ptr);
|
|
|
|
for (pdevs) |pdev| {
|
|
if (try checkSuitable(vki, pdev, allocator, surface)) |candidate| {
|
|
return candidate;
|
|
}
|
|
}
|
|
|
|
return error.NoSuitableDevice;
|
|
}
|
|
|
|
fn checkSuitable(
|
|
vki: InstanceDispatch,
|
|
pdev: vk.PhysicalDevice,
|
|
allocator: Allocator,
|
|
surface: vk.SurfaceKHR,
|
|
) !?DeviceCandidate {
|
|
const props = vki.getPhysicalDeviceProperties(pdev);
|
|
|
|
if (!try checkExtensionSupport(vki, pdev, allocator)) {
|
|
return null;
|
|
}
|
|
|
|
if (!try checkSurfaceSupport(vki, pdev, surface)) {
|
|
return null;
|
|
}
|
|
|
|
if (try allocateQueues(vki, pdev, allocator, surface)) |allocation| {
|
|
return DeviceCandidate{
|
|
.pdev = pdev,
|
|
.props = props,
|
|
.queues = allocation,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
fn allocateQueues(vki: InstanceDispatch, pdev: vk.PhysicalDevice, allocator: Allocator, surface: vk.SurfaceKHR) !?QueueAllocation {
|
|
var family_count: u32 = undefined;
|
|
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
|
|
|
const families = try allocator.alloc(vk.QueueFamilyProperties, family_count);
|
|
defer allocator.free(families);
|
|
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
|
|
|
|
var graphics_family: ?u32 = null;
|
|
var present_family: ?u32 = null;
|
|
|
|
for (families, 0..) |properties, i| {
|
|
const family: u32 = @intCast(i);
|
|
|
|
if (graphics_family == null and properties.queue_flags.graphics_bit) {
|
|
graphics_family = family;
|
|
}
|
|
|
|
if (present_family == null and (try vki.getPhysicalDeviceSurfaceSupportKHR(pdev, family, surface)) == vk.TRUE) {
|
|
present_family = family;
|
|
}
|
|
}
|
|
|
|
if (graphics_family != null and present_family != null) {
|
|
return QueueAllocation{
|
|
.graphics_family = graphics_family.?,
|
|
.present_family = present_family.?,
|
|
};
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
fn checkSurfaceSupport(vki: InstanceDispatch, pdev: vk.PhysicalDevice, surface: vk.SurfaceKHR) !bool {
|
|
var format_count: u32 = undefined;
|
|
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null);
|
|
|
|
var present_mode_count: u32 = undefined;
|
|
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &present_mode_count, null);
|
|
|
|
return format_count > 0 and present_mode_count > 0;
|
|
}
|
|
|
|
fn checkExtensionSupport(
|
|
vki: InstanceDispatch,
|
|
pdev: vk.PhysicalDevice,
|
|
allocator: Allocator,
|
|
) !bool {
|
|
var count: u32 = undefined;
|
|
_ = try vki.enumerateDeviceExtensionProperties(pdev, null, &count, null);
|
|
|
|
const propsv = try allocator.alloc(vk.ExtensionProperties, count);
|
|
defer allocator.free(propsv);
|
|
|
|
_ = try vki.enumerateDeviceExtensionProperties(pdev, null, &count, propsv.ptr);
|
|
|
|
for (required_device_extensions) |ext| {
|
|
for (propsv) |props| {
|
|
if (std.mem.eql(u8, std.mem.span(ext), std.mem.sliceTo(&props.extension_name, 0))) {
|
|
break;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|