Add a vulkan instance and prepare for starting the vulkan rewrite

This commit is contained in:
IntegratedQuantum 2025-06-08 16:07:16 +02:00
parent dde261750a
commit 830ef09dd6
3 changed files with 166 additions and 0 deletions

View File

@ -58,10 +58,12 @@ fn linkLibraries(b: *std.Build, exe: *std.Build.Step.Compile, useLocalDeps: bool
exe.linkSystemLibrary("gdi32");
exe.linkSystemLibrary("opengl32");
exe.linkSystemLibrary("ws2_32");
exe.linkSystemLibrary("vulkan-1");
} else if(t.os.tag == .linux) {
exe.linkSystemLibrary("asound");
exe.linkSystemLibrary("X11");
exe.linkSystemLibrary("GL");
exe.linkSystemLibrary("vulkan");
} else if(t.os.tag == .macos) {
exe.linkFramework("AudioUnit");
exe.linkFramework("AudioToolbox");
@ -74,6 +76,7 @@ fn linkLibraries(b: *std.Build, exe: *std.Build.Step.Compile, useLocalDeps: bool
exe.addRPath(.{.cwd_relative = "/usr/local/GL/lib"});
exe.root_module.addRPathSpecial("@executable_path/../Library");
exe.addRPath(.{.cwd_relative = "/opt/X11/lib"});
exe.linkSystemLibrary("vulkan.1");
} else {
std.log.err("Unsupported target: {}\n", .{t.os.tag});
}

View File

@ -6,8 +6,11 @@ const files = main.files;
const vec = main.vec;
const Vec2f = vec.Vec2f;
const vulkan = @import("vulkan.zig");
pub const c = @cImport({
@cInclude("glad/glad.h");
@cDefine("GLFW_INCLUDE_VULKAN", "");
@cInclude("GLFW/glfw3.h");
});
@ -634,6 +637,12 @@ pub fn init() void { // MARK: init()
@panic("Failed to initialize GLFW");
}
if(c.glfwVulkanSupported() == c.GLFW_FALSE) {
std.log.err("Vulkan is not supported. Please update your drivers if you want to keep playing Cubyz in the future.", .{});
}
vulkan.Instance.init();
c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, 1);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4);
c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6);
@ -677,6 +686,7 @@ pub fn init() void { // MARK: init()
pub fn deinit() void {
Gamepad.deinit();
c.glfwDestroyWindow(window);
vulkan.Instance.deinit();
c.glfwTerminate();
}
var cursorVisible: bool = true;

153
src/graphics/vulkan.zig Normal file
View File

@ -0,0 +1,153 @@
const std = @import("std");
const main = @import("main");
const c = main.Window.c;
const Errors = struct { // MARK: Errors
pub const VK_SUCCESS: c_int = 0;
pub const VK_NOT_READY: c_int = 1;
pub const VK_TIMEOUT: c_int = 2;
pub const VK_EVENT_SET: c_int = 3;
pub const VK_EVENT_RESET: c_int = 4;
pub const VK_INCOMPLETE: c_int = 5;
pub const VK_ERROR_OUT_OF_HOST_MEMORY: c_int = -1;
pub const VK_ERROR_OUT_OF_DEVICE_MEMORY: c_int = -2;
pub const VK_ERROR_INITIALIZATION_FAILED: c_int = -3;
pub const VK_ERROR_DEVICE_LOST: c_int = -4;
pub const VK_ERROR_MEMORY_MAP_FAILED: c_int = -5;
pub const VK_ERROR_LAYER_NOT_PRESENT: c_int = -6;
pub const VK_ERROR_EXTENSION_NOT_PRESENT: c_int = -7;
pub const VK_ERROR_FEATURE_NOT_PRESENT: c_int = -8;
pub const VK_ERROR_INCOMPATIBLE_DRIVER: c_int = -9;
pub const VK_ERROR_TOO_MANY_OBJECTS: c_int = -10;
pub const VK_ERROR_FORMAT_NOT_SUPPORTED: c_int = -11;
pub const VK_ERROR_FRAGMENTED_POOL: c_int = -12;
pub const VK_ERROR_UNKNOWN: c_int = -13;
pub const VK_ERROR_OUT_OF_POOL_MEMORY: c_int = -1000069000;
pub const VK_ERROR_INVALID_EXTERNAL_HANDLE: c_int = -1000072003;
pub const VK_ERROR_FRAGMENTATION: c_int = -1000161000;
pub const VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS: c_int = -1000257000;
pub const VK_PIPELINE_COMPILE_REQUIRED: c_int = 1000297000;
pub const VK_ERROR_NOT_PERMITTED: c_int = -1000174001;
pub const VK_ERROR_SURFACE_LOST_KHR: c_int = -1000000000;
pub const VK_ERROR_NATIVE_WINDOW_IN_USE_KHR: c_int = -1000000001;
pub const VK_SUBOPTIMAL_KHR: c_int = 1000001003;
pub const VK_ERROR_OUT_OF_DATE_KHR: c_int = -1000001004;
pub const VK_ERROR_INCOMPATIBLE_DISPLAY_KHR: c_int = -1000003001;
pub const VK_ERROR_VALIDATION_FAILED_EXT: c_int = -1000011001;
pub const VK_ERROR_INVALID_SHADER_NV: c_int = -1000012000;
pub const VK_ERROR_IMAGE_USAGE_NOT_SUPPORTED_KHR: c_int = -1000023000;
pub const VK_ERROR_VIDEO_PICTURE_LAYOUT_NOT_SUPPORTED_KHR: c_int = -1000023001;
pub const VK_ERROR_VIDEO_PROFILE_OPERATION_NOT_SUPPORTED_KHR: c_int = -1000023002;
pub const VK_ERROR_VIDEO_PROFILE_FORMAT_NOT_SUPPORTED_KHR: c_int = -1000023003;
pub const VK_ERROR_VIDEO_PROFILE_CODEC_NOT_SUPPORTED_KHR: c_int = -1000023004;
pub const VK_ERROR_VIDEO_STD_VERSION_NOT_SUPPORTED_KHR: c_int = -1000023005;
pub const VK_ERROR_INVALID_DRM_FORMAT_MODIFIER_PLANE_LAYOUT_EXT: c_int = -1000158000;
pub const VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT: c_int = -1000255000;
pub const VK_THREAD_IDLE_KHR: c_int = 1000268000;
pub const VK_THREAD_DONE_KHR: c_int = 1000268001;
pub const VK_OPERATION_DEFERRED_KHR: c_int = 1000268002;
pub const VK_OPERATION_NOT_DEFERRED_KHR: c_int = 1000268003;
pub const VK_ERROR_INVALID_VIDEO_STD_PARAMETERS_KHR: c_int = -1000299000;
pub const VK_ERROR_COMPRESSION_EXHAUSTED_EXT: c_int = -1000338000;
pub const VK_INCOMPATIBLE_SHADER_BINARY_EXT: c_int = 1000482000;
pub const VK_PIPELINE_BINARY_MISSING_KHR: c_int = 1000483000;
pub const VK_ERROR_NOT_ENOUGH_SPACE_KHR: c_int = -1000483000;
pub const VK_ERROR_OUT_OF_POOL_MEMORY_KHR: c_int = -1000069000;
pub const VK_ERROR_INVALID_EXTERNAL_HANDLE_KHR: c_int = -1000072003;
pub const VK_ERROR_FRAGMENTATION_EXT: c_int = -1000161000;
pub const VK_ERROR_NOT_PERMITTED_EXT: c_int = -1000174001;
pub const VK_ERROR_NOT_PERMITTED_KHR: c_int = -1000174001;
pub const VK_ERROR_INVALID_DEVICE_ADDRESS_EXT: c_int = -1000257000;
pub const VK_ERROR_INVALID_OPAQUE_CAPTURE_ADDRESS_KHR: c_int = -1000257000;
pub const VK_PIPELINE_COMPILE_REQUIRED_EXT: c_int = 1000297000;
pub const VK_ERROR_PIPELINE_COMPILE_REQUIRED_EXT: c_int = 1000297000;
pub const VK_ERROR_INCOMPATIBLE_SHADER_BINARY_EXT: c_int = 1000482000;
pub const VK_RESULT_MAX_ENUM: c_int = 2147483647;
};
fn checkResult(result: c.VkResult) void {
if(result != c.VK_SUCCESS) {
inline for(@typeInfo(Errors).@"struct".decls) |decl| {
if(result == @field(c, decl.name)) {
std.log.err("Encountered a vulkan error: {s}", .{decl.name});
return;
}
}
std.log.err("Encountered a vulkan error with unknown error code {}", .{result});
}
}
fn fakeCheckResult(result: anytype) void {
if(@TypeOf(result) != void) {
checkResult(result);
}
}
fn allocEnumeration(function: anytype, allocator: main.heap.NeverFailingAllocator, args: anytype) []@typeInfo(@typeInfo(@TypeOf(function)).@"fn".params[@typeInfo(@TypeOf(function)).@"fn".params.len - 1].type.?).pointer.child {
const T = @typeInfo(@typeInfo(@TypeOf(function)).@"fn".params[@typeInfo(@TypeOf(function)).@"fn".params.len - 1].type.?).pointer.child;
var count: u32 = 0;
fakeCheckResult(@call(.auto, function, args ++ .{&count, null}));
const list = allocator.alloc(T, count);
fakeCheckResult(@call(.auto, function, args ++ .{&count, list.ptr}));
return list;
}
pub const Instance = struct { // MARK: Instance
var instance: c.VkInstance = undefined;
const validationLayers: []const [*:0]const u8 = &.{
"VK_LAYER_KHRONOS_validation",
};
fn checkValidationLayerSupport() bool {
const availableLayers: []c.VkLayerProperties = allocEnumeration(c.vkEnumerateInstanceLayerProperties, main.stackAllocator, .{});
defer main.stackAllocator.free(availableLayers);
for(validationLayers) |layerName| continueOuter: {
for(availableLayers) |layerProperties| {
if(std.mem.eql(u8, std.mem.span(layerName), std.mem.span(@as([*:0]const u8, @ptrCast(&layerProperties.layerName))))) {
break :continueOuter;
}
}
std.log.err("Couldn't find validation layer {s}", .{layerName});
return false;
}
return true;
}
pub fn init() void {
const appInfo = c.VkApplicationInfo {
.sType = c.VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = "Cubyz",
.applicationVersion = c.VK_MAKE_VERSION(0, 0, 0),
.pEngineName = "custom",
.engineVersion = c.VK_MAKE_VERSION(0, 0, 0),
.apiVersion = c.VK_API_VERSION_1_3,
};
var glfwExtensionCount: u32 = 0;
const glfwExtensions: [*c][*c]const u8 = c.glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
const availableExtensions: []c.VkExtensionProperties = allocEnumeration(c.vkEnumerateInstanceExtensionProperties, main.stackAllocator, .{null});
defer main.stackAllocator.free(availableExtensions);
std.log.debug("Availabe vulkan instance extensions:", .{});
for(availableExtensions) |ext| {
std.log.debug("\t{s}", .{ext.extensionName});
}
const createInfo = c.VkInstanceCreateInfo {
.sType = c.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &appInfo,
.enabledExtensionCount = glfwExtensionCount,
.ppEnabledExtensionNames = glfwExtensions,
.ppEnabledLayerNames = validationLayers.ptr,
.enabledLayerCount = if(checkValidationLayerSupport()) validationLayers.len else 0,
};
// TODO: Use the debug callback when validation layers are enabled to write messages into the logger.
// https://docs.vulkan.org/tutorial/latest/03_Drawing_a_triangle/00_Setup/02_Validation_layers.html#_message_callback
checkResult(c.vkCreateInstance(&createInfo, null, &instance));
}
pub fn deinit() void {
c.vkDestroyInstance(instance, null);
}
};