xcb: add Vulkan init to sample

This commit is contained in:
2026-09-04 16:38:58 +02:00
parent 94c08ff3b6
commit c14fc89c74
5 changed files with 35921 additions and 2 deletions

View File

@@ -0,0 +1,176 @@
const Vulkan = @This();
const std = @import("std");
const vk = @import("vulkan");
const xcb = @import("xcb");
vulkan_loader: *DynamicLibrary,
base: vk.BaseWrapper,
instance: vk.InstanceProxy,
surface: vk.SurfaceKHR,
device: vk.DeviceProxy,
queue_family: u32,
queue: vk.Queue,
command_pool: vk.CommandPool,
pub fn init(allocator: std.mem.Allocator, connection: *xcb.Connection, window: xcb.Window) !Vulkan {
const vulkan_loader = try DynamicLibrary.open("libvulkan.so.1");
errdefer vulkan_loader.close();
const vkGetInstanceProcAddr = try vulkan_loader.sym(vk.PfnGetInstanceProcAddr, "vkGetInstanceProcAddr");
const base = vk.BaseWrapper.load(vkGetInstanceProcAddr);
const instance_handle = blk: {
const enabled_layers: []const [*:0]const u8 = &.{};
const enabled_extensions: []const [*:0]const u8 = &.{
vk.extensions.khr_surface.name,
vk.extensions.khr_xcb_surface.name,
};
break :blk try base.createInstance(&.{
.p_application_info = &.{
.p_application_name = "xcb_sample",
.application_version = vk.makeApiVersion(0, 0, 0, 0).toU32(),
.p_engine_name = "xcb_sample",
.engine_version = vk.makeApiVersion(0, 0, 0, 0).toU32(),
.api_version = vk.API_VERSION_1_0.toU32(),
},
.enabled_layer_count = @intCast(enabled_layers.len),
.pp_enabled_layer_names = enabled_layers.ptr,
.enabled_extension_count = @intCast(enabled_extensions.len),
.pp_enabled_extension_names = enabled_extensions.ptr,
}, null);
};
const instance_wrapper_ptr = try allocator.create(vk.InstanceWrapper);
errdefer allocator.destroy(instance_wrapper_ptr);
instance_wrapper_ptr.* = .load(instance_handle, vkGetInstanceProcAddr);
const instance = vk.InstanceProxy.init(instance_handle, instance_wrapper_ptr);
errdefer instance.destroyInstance(null);
const surface = try instance.createXcbSurfaceKHR(&.{
.connection = connection,
.window = window,
}, null);
errdefer instance.destroySurfaceKHR(surface, null);
const physical_device = blk: {
const physical_devices = try instance.enumeratePhysicalDevicesAlloc(allocator);
defer allocator.free(physical_devices);
if (physical_devices.len > 0) {
break :blk physical_devices[0];
}
return error.NoDevice;
};
const queue_family: u32 = blk: {
const queue_families_properties = try instance.getPhysicalDeviceQueueFamilyPropertiesAlloc(physical_device, allocator);
defer allocator.free(queue_families_properties);
// Look for the first family that has the `graphics_bit` set and
// supports presentation to a given surface.
for (queue_families_properties, 0..) |queue_family_properties, i| {
const graphics_bit = queue_family_properties.queue_flags.graphics_bit;
const supports_presentation = try instance.getPhysicalDeviceSurfaceSupportKHR(physical_device, @intCast(i), surface) != .false;
if (graphics_bit and supports_presentation) {
break :blk @intCast(i);
}
}
return error.NoQueue;
};
const device_handle = blk: {
const queue_create_infos: []const vk.DeviceQueueCreateInfo = &.{
.{
.queue_family_index = queue_family,
.queue_count = 1,
.p_queue_priorities = &.{0},
},
};
const enabled_extensions: []const [*:0]const u8 = &.{
vk.extensions.khr_swapchain.name,
};
break :blk try instance.createDevice(physical_device, &.{
.queue_create_info_count = @intCast(queue_create_infos.len),
.p_queue_create_infos = queue_create_infos.ptr,
.enabled_extension_count = @intCast(enabled_extensions.len),
.pp_enabled_extension_names = enabled_extensions.ptr,
}, null);
};
const device_wrapper_ptr = try allocator.create(vk.DeviceWrapper);
errdefer allocator.destroy(device_wrapper_ptr);
device_wrapper_ptr.* = .load(device_handle, instance.wrapper.dispatch.vkGetDeviceProcAddr.?);
const device = vk.DeviceProxy.init(device_handle, device_wrapper_ptr);
errdefer device.destroyDevice(null);
const queue = device.getDeviceQueue(queue_family, 0);
const command_pool = try device.createCommandPool(&.{
.flags = .{ .transient_bit = true },
.queue_family_index = queue_family,
}, null);
errdefer device.destroyCommandPool(command_pool, null);
return .{
.vulkan_loader = vulkan_loader,
.base = base,
.instance = instance,
.surface = surface,
.device = device,
.queue_family = queue_family,
.queue = queue,
.command_pool = command_pool,
};
}
pub fn deinit(self: *Vulkan, allocator: std.mem.Allocator) void {
self.device.destroyCommandPool(self.command_pool, null);
self.device.destroyDevice(null);
allocator.destroy(self.device.wrapper);
self.instance.destroySurfaceKHR(self.surface, null);
self.instance.destroyInstance(null);
allocator.destroy(self.instance.wrapper);
self.vulkan_loader.close();
self.* = undefined;
}
const DynamicLibrary = opaque {
fn open(path: [*:0]const u8) !*DynamicLibrary {
const ptr = std.c.dlopen(path, .{ .NOW = true }) orelse {
std.debug.print("{s}\n", .{std.c.dlerror().?});
return error.DlOpen;
};
return @ptrCast(ptr);
}
fn close(self: *DynamicLibrary) void {
_ = std.c.dlclose(self);
}
fn sym(self: *DynamicLibrary, comptime Ptr: type, symbol: [*:0]const u8) !Ptr {
const ptr = std.c.dlsym(self, symbol) orelse {
std.debug.print("{s}\n", .{std.c.dlerror().?});
return error.DlSym;
};
return @ptrCast(@alignCast(ptr));
}
};