diff --git a/build.zig.zon b/build.zig.zon index ed8410d7c..3d11c80fa 100644 --- a/build.zig.zon +++ b/build.zig.zon @@ -9,37 +9,37 @@ .lazy = true, }, .cubyz_deps_headers = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_headers.tar.gz", - .hash = "N-V-__8AAKJ9OwA8jY0yp1Lokn0e8tdmOaz1MLUCFh-azTZq", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_headers.tar.gz", + .hash = "N-V-__8AAI-aOwAGCfJiF1xWZSQ0yxGSyyuj-VO5P_UqqyJ0", }, .cubyz_deps_aarch64_macos = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_aarch64-macos-none.tar.gz", - .hash = "N-V-__8AACiCRALoq18Einwt-YUa-hM441UctNoLBgclCjS8", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_aarch64-macos-none.tar.gz", + .hash = "N-V-__8AAI-aOwAGCfJiF1xWZSQ0yxGSyyuj-VO5P_UqqyJ0", .lazy = true, }, .cubyz_deps_aarch64_linux = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_aarch64-linux-musl.tar.gz", - .hash = "N-V-__8AAHg2lgJB3mcjhodFwzlJJ8_Nm1SmGackQ5268vg-", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_aarch64-linux-musl.tar.gz", + .hash = "N-V-__8AAI-aOwAGCfJiF1xWZSQ0yxGSyyuj-VO5P_UqqyJ0", .lazy = true, }, .cubyz_deps_aarch64_windows = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_aarch64-windows-gnu.tar.gz", - .hash = "N-V-__8AAPYXtQJNrOeyHuVk3vNvOh8XMQ3p23ZXhlzf-yRK", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_aarch64-windows-gnu.tar.gz", + .hash = "N-V-__8AAAI8tQKULcx4VW98BqluDNYJhHtN2OBlFw2Cm19f", .lazy = true, }, .cubyz_deps_x86_64_macos = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_x86_64-macos-none.tar.gz", - .hash = "N-V-__8AAIydPQJgYAFNXcw3ytC6-hFSUf-cJPUmcD26cPWd", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_x86_64-macos-none.tar.gz", + .hash = "N-V-__8AANi9PQLVH2WpYTmNnlcdBHDkNZI9yJz6fAznklHu", .lazy = true, }, .cubyz_deps_x86_64_linux = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_x86_64-linux-musl.tar.gz", - .hash = "N-V-__8AANZnlAJC50QAqnYfjyySyZkE0iiuvozhG4-ZSIiY", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_x86_64-linux-musl.tar.gz", + .hash = "N-V-__8AAIKQlALN_67_ilCxZcxIGddSBBi7A4lVVa0jFeW9", .lazy = true, }, .cubyz_deps_x86_64_windows = .{ - .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/7/cubyz_deps_x86_64-windows-gnu.tar.gz", - .hash = "N-V-__8AAKwH1wLR7xuDyeH7WPnmJKSQWpj4LAcg_EV-wAX7", + .url = "https://github.com/PixelGuys/Cubyz-Libs/releases/download/8/cubyz_deps_x86_64-windows-gnu.tar.gz", + .hash = "N-V-__8AAM4p1wKrLLOhfB8egk7fpA7WnEGIU46h_pKk8Xou", .lazy = true, }, .cubyz_large_assets = .{ diff --git a/src/graphics/Window.zig b/src/graphics/Window.zig index 967d11853..5cea42bea 100644 --- a/src/graphics/Window.zig +++ b/src/graphics/Window.zig @@ -20,6 +20,7 @@ pub var lastUsedMouse: bool = true; pub var width: u31 = 1280; pub var height: u31 = 720; pub var window: *c.GLFWwindow = undefined; +pub var vulkanWindow: *c.GLFWwindow = undefined; pub var grabbed: bool = false; pub var scrollOffset: f32 = 0; @@ -646,9 +647,16 @@ pub fn init() void { // MARK: init() 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.", .{}); } else { - vulkan.init(); + c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API); + c.glfwWindowHint(c.GLFW_VISIBLE, c.GLFW_FALSE); + vulkanWindow = c.glfwCreateWindow(width, height, "Cubyz", null, null) orelse @panic("Failed to create GLFW window"); + vulkan.init(vulkanWindow) catch |err| { + std.log.err("Error while initializing Vulkan: {s}", .{@errorName(err)}); + }; } + c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_OPENGL_API); + c.glfwWindowHint(c.GLFW_VISIBLE, c.GLFW_TRUE); c.glfwWindowHint(c.GLFW_OPENGL_DEBUG_CONTEXT, 1); c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MAJOR, 4); c.glfwWindowHint(c.GLFW_CONTEXT_VERSION_MINOR, 6); @@ -692,6 +700,7 @@ pub fn init() void { // MARK: init() pub fn deinit() void { Gamepad.deinit(); c.glfwDestroyWindow(window); + c.glfwDestroyWindow(vulkanWindow); vulkan.deinit(); c.glfwTerminate(); } diff --git a/src/graphics/vulkan.zig b/src/graphics/vulkan.zig index 6f4beee1b..e31a48ee0 100644 --- a/src/graphics/vulkan.zig +++ b/src/graphics/vulkan.zig @@ -75,31 +75,63 @@ fn checkResultIfAvailable(result: anytype) void { fn allocEnumerationGeneric(function: anytype, allocator: 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; - checkResultIfAvailable(@call(.auto, function, args ++ .{&count, null})); - const list = allocator.alloc(T, count); - checkResultIfAvailable(@call(.auto, function, args ++ .{&count, list.ptr})); - return list; + while(true) { + checkResultIfAvailable(@call(.auto, function, args ++ .{&count, null})); + const list = allocator.alloc(T, count); + const result = @call(.auto, function, args ++ .{&count, list.ptr}); + if(@TypeOf(result) != void and result == c.VK_INCOMPLETE) { + allocator.free(list); + continue; + } + checkResultIfAvailable(result); + + if(count < list.len) return allocator.realloc(list, count); + return list; + } } +// MARK: Enumerators + pub fn enumerateInstanceLayerProperties(allocator: NeverFailingAllocator) []c.VkLayerProperties { return allocEnumerationGeneric(c.vkEnumerateInstanceLayerProperties, allocator, .{}); } -pub fn enumerateInstanceExtensionProperties(allocator: NeverFailingAllocator, layerName: ?[*:0]u8) []c.VkExtensionProperties { +pub fn enumerateInstanceExtensionProperties(allocator: NeverFailingAllocator, layerName: ?[*:0]const u8) []c.VkExtensionProperties { return allocEnumerationGeneric(c.vkEnumerateInstanceExtensionProperties, allocator, .{layerName}); } +pub fn enumeratePhysicalDevices(allocator: NeverFailingAllocator) []c.VkPhysicalDevice { + return allocEnumerationGeneric(c.vkEnumeratePhysicalDevices, allocator, .{instance}); +} + +pub fn enumerateDeviceExtensionProperties(allocator: NeverFailingAllocator, dev: c.VkPhysicalDevice, layerName: ?[*:0]const u8) []c.VkExtensionProperties { + return allocEnumerationGeneric(c.vkEnumerateDeviceExtensionProperties, allocator, .{dev, layerName}); +} + +pub fn getPhysicalDeviceQueueFamilyProperties(allocator: NeverFailingAllocator, dev: c.VkPhysicalDevice) []c.VkQueueFamilyProperties { + return allocEnumerationGeneric(c.vkGetPhysicalDeviceQueueFamilyProperties, allocator, .{dev}); +} + +pub fn getPhysicalDeviceSurfaceFormatsKHR(allocator: NeverFailingAllocator, dev: c.VkPhysicalDevice) []c.VkSurfaceFormatKHR { + return allocEnumerationGeneric(c.vkGetPhysicalDeviceSurfaceFormatsKHR, allocator, .{dev, surface}); +} + // MARK: globals var instance: c.VkInstance = undefined; +var surface: c.VkSurfaceKHR = undefined; +var physicalDevice: c.VkPhysicalDevice = undefined; // MARK: init -pub fn init() void { +pub fn init(window: ?*c.GLFWwindow) !void { createInstance(); + checkResult(c.glfwCreateWindowSurface(instance, window, null, &surface)); + try pickPhysicalDevice(); } pub fn deinit() void { + c.vkDestroySurfaceKHR(instance, surface, null); c.vkDestroyInstance(instance, null); } @@ -156,3 +188,111 @@ pub fn createInstance() void { }; checkResult(c.vkCreateInstance(&createInfo, null, &instance)); } + +// MARK: Physical Device + +const deviceExtensions = [_][*:0]const u8{ + c.VK_KHR_SWAPCHAIN_EXTENSION_NAME, +}; + +const QueueFamilyIndidices = struct { + graphicsFamily: ?u32 = null, + presentFamily: ?u32 = null, + + fn isComplete(self: QueueFamilyIndidices) bool { + return self.graphicsFamily != null and self.presentFamily != null; + } +}; + +fn findQueueFamilies(dev: c.VkPhysicalDevice) QueueFamilyIndidices { + var result: QueueFamilyIndidices = .{}; + const queueFamilies = getPhysicalDeviceQueueFamilyProperties(main.stackAllocator, dev); + defer main.stackAllocator.free(queueFamilies); + for(queueFamilies, 0..) |family, i| { + if(family.queueFlags & c.VK_QUEUE_GRAPHICS_BIT != 0) { + result.graphicsFamily = @intCast(i); + } + var presentSupport: u32 = 0; + checkResult(c.vkGetPhysicalDeviceSurfaceSupportKHR(dev, @intCast(i), surface, &presentSupport)); + if(presentSupport != 0) { + result.presentFamily = @intCast(i); + } + } + return result; +} + +fn checkDeviceExtensionSupport(dev: c.VkPhysicalDevice) bool { + const availableExtension = enumerateDeviceExtensionProperties(main.stackAllocator, dev, null); + defer main.stackAllocator.free(availableExtension); + for(deviceExtensions) |requiredName| continueOuter: { + for(availableExtension) |available| { + if(std.mem.eql(u8, std.mem.span(requiredName), std.mem.span(@as([*:0]const u8, @ptrCast(&available.extensionName))))) { + break :continueOuter; + } + } + std.log.warn("Rejecting device because extension {s} was not found", .{requiredName}); + return false; + } + return true; +} + +fn getDeviceScore(dev: c.VkPhysicalDevice) f32 { + var properties: c.VkPhysicalDeviceProperties = undefined; + c.vkGetPhysicalDeviceProperties(dev, &properties); + var features: c.VkPhysicalDeviceFeatures = undefined; + c.vkGetPhysicalDeviceFeatures(dev, &features); + std.log.debug("Device: {s}", .{@as([*:0]const u8, @ptrCast(&properties.deviceName))}); + std.log.debug("Properties: {}", .{properties}); + std.log.debug("Features: {}", .{features}); + + const baseScore: f32 = switch(properties.deviceType) { + c.VK_PHYSICAL_DEVICE_TYPE_CPU => 1e-9, + c.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU => 1e9, + c.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU => 1, + else => 0.1, + }; + + const availableExtension = enumerateDeviceExtensionProperties(main.stackAllocator, dev, null); + defer main.stackAllocator.free(availableExtension); + std.log.debug("Device extensions:", .{}); + for(availableExtension) |ext| { + std.log.debug("\t{s}", .{ext.extensionName}); + } + if(!findQueueFamilies(dev).isComplete() or !checkDeviceExtensionSupport(dev)) return 0; + + if(features.multiDrawIndirect != c.VK_TRUE) { + std.log.warn("Rejecting device: multDrawIndirect is not supported", .{}); + return 0; + } + + if(features.dualSrcBlend != c.VK_TRUE) { + std.log.warn("Rejecting device: dual source blending is not supported", .{}); + return 0; + } + + return baseScore; +} + +fn pickPhysicalDevice() !void { + const devices = enumeratePhysicalDevices(main.stackAllocator); + defer main.stackAllocator.free(devices); + if(devices.len == 0) { + return error.NoDevicesFound; + } + var bestScore: f32 = 0; + for(devices) |dev| { + const score = getDeviceScore(dev); + if(score > bestScore) { + bestScore = score; + physicalDevice = dev; + } + } + + if(bestScore == 0) { + return error.NoCapableDeviceFound; + } + + var properties: c.VkPhysicalDeviceProperties = undefined; + c.vkGetPhysicalDeviceProperties(physicalDevice, &properties); + std.log.info("Selected device {s}", .{@as([*:0]const u8, @ptrCast(&properties.deviceName))}); +}