diff --git a/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java b/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java index 4827f9043..a10b949da 100644 --- a/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java +++ b/src/main/java/net/vulkanmod/mixin/window/WindowMixin.java @@ -61,6 +61,15 @@ private void redirect(int hint, int value) { } private void vulkanHint(WindowEventHandler windowEventHandler, ScreenManager screenManager, DisplayData displayData, String string, String string2, CallbackInfo ci) { GLFW.glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); + // Enable 32bit P3 wide color gamut on macOS + if (Platform.isMacOS()) { + GLFW.glfwWindowHint(GLFW_COCOA_RETINA_FRAMEBUFFER, GLFW_TRUE); + GLFW.glfwWindowHint(GLFW_RED_BITS, 10); + GLFW.glfwWindowHint(GLFW_GREEN_BITS, 10); + GLFW.glfwWindowHint(GLFW_BLUE_BITS, 10); + GLFW.glfwWindowHint(GLFW_ALPHA_BITS, 2); + } + //Fix Gnome Client-Side Decorators boolean b = (Platform.isGnome() | Platform.isWeston() | Platform.isGeneric()) && Platform.isWayLand(); GLFW.glfwWindowHint(GLFW_DECORATED, (b ? GLFW_FALSE : GLFW_TRUE)); diff --git a/src/main/java/net/vulkanmod/vulkan/Vulkan.java b/src/main/java/net/vulkanmod/vulkan/Vulkan.java index f2f0a1646..8d457a4bc 100644 --- a/src/main/java/net/vulkanmod/vulkan/Vulkan.java +++ b/src/main/java/net/vulkanmod/vulkan/Vulkan.java @@ -13,6 +13,7 @@ import net.vulkanmod.vulkan.util.VkResult; import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; +import static org.lwjgl.vulkan.EXTSwapchainColorspace.VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME; import org.lwjgl.util.vma.VmaAllocatorCreateInfo; import org.lwjgl.util.vma.VmaVulkanFunctions; import org.lwjgl.vulkan.*; @@ -109,19 +110,11 @@ private static void destroyDebugUtilsMessengerEXT(VkInstance instance, long debu } - public static VkDevice getVkDevice() { - return DeviceManager.vkDevice; - } - - public static long getAllocator() { - return allocator; - } - public static long window; - private static VkInstance instance; private static long debugMessenger; private static long surface; + private static List supportedInstanceExtensions; private static long commandPool; private static VkCommandBuffer immediateCmdBuffer; @@ -131,6 +124,7 @@ public static long getAllocator() { private static StagingBuffer[] stagingBuffers; + private static boolean colorSpaceExtSupport; public static boolean use24BitsDepthFormat = true; private static int DEFAULT_DEPTH_FORMAT = 0; @@ -200,17 +194,16 @@ private static void freeStagingBuffers() { } private static void createInstance() { - if (ENABLE_VALIDATION_LAYERS && !checkValidationLayerSupport()) { throw new RuntimeException("Validation requested but not supported"); } - try (MemoryStack stack = stackPush()) { + supportedInstanceExtensions = getAvailableInstanceExtension(); + colorSpaceExtSupport = supportedInstanceExtensions.contains(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); + try (MemoryStack stack = stackPush()) { // Use calloc to initialize the structs with 0s. Otherwise, the program can crash due to random values - VkApplicationInfo appInfo = VkApplicationInfo.calloc(stack); - appInfo.sType(VK_STRUCTURE_TYPE_APPLICATION_INFO); appInfo.pApplicationName(stack.UTF8Safe("VulkanMod")); appInfo.applicationVersion(VK_MAKE_VERSION(1, 0, 0)); @@ -219,13 +212,11 @@ private static void createInstance() { appInfo.apiVersion(VK_API_VERSION_1_2); VkInstanceCreateInfo createInfo = VkInstanceCreateInfo.calloc(stack); - createInfo.sType(VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO); createInfo.pApplicationInfo(appInfo); createInfo.ppEnabledExtensionNames(getRequiredInstanceExtensions()); if (ENABLE_VALIDATION_LAYERS) { - createInfo.ppEnabledLayerNames(asPointerBuffer(VALIDATION_LAYERS)); VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo = VkDebugUtilsMessengerCreateInfoEXT.calloc(stack); @@ -244,9 +235,7 @@ private static void createInstance() { } static boolean checkValidationLayerSupport() { - try (MemoryStack stack = stackPush()) { - IntBuffer layerCount = stack.ints(0); vkEnumerateInstanceLayerProperties(layerCount, null); @@ -273,13 +262,11 @@ private static void populateDebugMessengerCreateInfo(VkDebugUtilsMessengerCreate } private static void setupDebugMessenger() { - if (!ENABLE_VALIDATION_LAYERS) { return; } try (MemoryStack stack = stackPush()) { - VkDebugUtilsMessengerCreateInfoEXT createInfo = VkDebugUtilsMessengerCreateInfoEXT.calloc(stack); populateDebugMessengerCreateInfo(createInfo); @@ -308,7 +295,6 @@ private static void createSurface(long handle) { window = handle; try (MemoryStack stack = stackPush()) { - LongBuffer pSurface = stack.longs(VK_NULL_HANDLE); checkResult(glfwCreateWindowSurface(instance, window, null, pSurface), @@ -320,7 +306,6 @@ private static void createSurface(long handle) { private static void createVma() { try (MemoryStack stack = stackPush()) { - VmaVulkanFunctions vulkanFunctions = VmaVulkanFunctions.calloc(stack); vulkanFunctions.set(instance, DeviceManager.vkDevice); @@ -341,9 +326,7 @@ private static void createVma() { } private static void createCommandPool() { - try (MemoryStack stack = stackPush()) { - Queue.QueueFamilyIndices queueFamilyIndices = getQueueFamilies(); VkCommandPoolCreateInfo poolInfo = VkCommandPoolCreateInfo.calloc(stack); @@ -360,24 +343,44 @@ private static void createCommandPool() { } } - private static PointerBuffer getRequiredInstanceExtensions() { + private static List getAvailableInstanceExtension() { + try (MemoryStack stack = MemoryStack.stackPush()) { + // Query first for extension count + IntBuffer extensionCount = stack.ints(0); + vkEnumerateInstanceExtensionProperties((String) null, extensionCount, null); + + VkExtensionProperties.Buffer availableExtensions = VkExtensionProperties.malloc(extensionCount.get(0), stack); + vkEnumerateInstanceExtensionProperties((String) null, extensionCount, availableExtensions); + + return availableExtensions.stream() + .map(VkExtensionProperties::extensionNameString) + .toList(); + } + } + private static PointerBuffer getRequiredInstanceExtensions() { PointerBuffer glfwExtensions = glfwGetRequiredInstanceExtensions(); + MemoryStack stack = stackGet(); - if (ENABLE_VALIDATION_LAYERS) { + List requestedExtensions = new ArrayList<>(); - MemoryStack stack = stackGet(); + // Check for VK_EXT_SWAPCHAIN_COLOR_SPACE support + if (supportedInstanceExtensions.contains(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME)) { + requestedExtensions.add(VK_EXT_SWAPCHAIN_COLOR_SPACE_EXTENSION_NAME); + } - PointerBuffer extensions = stack.mallocPointer(glfwExtensions.capacity() + 1); + if (ENABLE_VALIDATION_LAYERS) { + requestedExtensions.add(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } - extensions.put(glfwExtensions); - extensions.put(stack.UTF8(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)); + PointerBuffer extensions = stack.mallocPointer(glfwExtensions.capacity() + requestedExtensions.size()); + extensions.put(glfwExtensions); - // Rewind the buffer before returning it to reset its position back to 0 - return extensions.rewind(); + for (String extName : requestedExtensions) { + extensions.put(stack.UTF8(extName)); } - return glfwExtensions; + return extensions.rewind(); } public static void checkResult(int result, String errorMessage) { @@ -394,8 +397,12 @@ public static void setVsync(boolean b) { } } - public static int getDefaultDepthFormat() { - return DEFAULT_DEPTH_FORMAT; + public static VkDevice getVkDevice() { + return DeviceManager.vkDevice; + } + + public static long getAllocator() { + return allocator; } public static long getSurface() { @@ -413,5 +420,13 @@ public static StagingBuffer getStagingBuffer() { public static Device getDevice() { return DeviceManager.device; } + + public static boolean colorSpaceExtSupport() { + return colorSpaceExtSupport; + } + + public static int getDefaultDepthFormat() { + return DEFAULT_DEPTH_FORMAT; + } } diff --git a/src/main/java/net/vulkanmod/vulkan/device/Device.java b/src/main/java/net/vulkanmod/vulkan/device/Device.java index da850d3b9..94768dfb6 100644 --- a/src/main/java/net/vulkanmod/vulkan/device/Device.java +++ b/src/main/java/net/vulkanmod/vulkan/device/Device.java @@ -1,19 +1,15 @@ package net.vulkanmod.vulkan.device; -import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.*; -import oshi.SystemInfo; -import oshi.hardware.CentralProcessor; import java.nio.IntBuffer; import java.util.HashSet; +import java.util.List; import java.util.Set; -import static java.util.stream.Collectors.toSet; import static org.lwjgl.glfw.GLFW.GLFW_PLATFORM_WIN32; import static org.lwjgl.glfw.GLFW.glfwGetPlatform; -import static org.lwjgl.system.MemoryStack.stackPush; import static org.lwjgl.vulkan.VK10.*; import static org.lwjgl.vulkan.VK11.vkEnumerateInstanceVersion; import static org.lwjgl.vulkan.VK11.vkGetPhysicalDeviceFeatures2; @@ -31,15 +27,13 @@ public class Device { public final VkPhysicalDeviceFeatures2 availableFeatures; public final VkPhysicalDeviceVulkan11Features availableFeatures11; -// public final VkPhysicalDeviceVulkan13Features availableFeatures13; -// public final boolean vulkan13Support; - - private boolean drawIndirectSupported; + public final List supportedExtensions; + public final boolean drawIndirectSupported; public Device(VkPhysicalDevice device) { this.physicalDevice = device; - properties = VkPhysicalDeviceProperties.malloc(); + this.properties = VkPhysicalDeviceProperties.malloc(); vkGetPhysicalDeviceProperties(physicalDevice, properties); this.vendorId = properties.vendorID(); @@ -55,18 +49,40 @@ public Device(VkPhysicalDevice device) { this.availableFeatures11.sType$Default(); this.availableFeatures.pNext(this.availableFeatures11); - //Vulkan 1.3 -// this.availableFeatures13 = VkPhysicalDeviceVulkan13Features.malloc(); -// this.availableFeatures13.sType$Default(); -// this.availableFeatures11.pNext(this.availableFeatures13.address()); -// -// this.vulkan13Support = this.device.getCapabilities().apiVersion == VK_API_VERSION_1_3; - vkGetPhysicalDeviceFeatures2(this.physicalDevice, this.availableFeatures); - if (this.availableFeatures.features().multiDrawIndirect() && this.availableFeatures11.shaderDrawParameters()) - this.drawIndirectSupported = true; + this.drawIndirectSupported = this.availableFeatures.features().multiDrawIndirect(); + + this.supportedExtensions = getAvailableExtension(device); + } + + public List getSupportedExtensions() { + return supportedExtensions; + } + + public Set getUnsupportedExtensions(Set requiredExtensions) { + Set unsupportedExtensions = new HashSet<>(requiredExtensions); + supportedExtensions.forEach(unsupportedExtensions::remove); + + return unsupportedExtensions; + } + + public boolean isDrawIndirectSupported() { + return drawIndirectSupported; + } + + // Added these to allow detecting GPU vendor, to allow handling vendor specific circumstances: + // (e.g. such as in case we encounter a vendor specific driver bug) + public boolean isAMD() { + return vendorId == 0x1022; + } + public boolean isNvidia() { + return vendorId == 0x10DE; + } + + public boolean isIntel() { + return vendorId == 0x8086; } private static String decodeVendor(int i) { @@ -108,7 +124,7 @@ private static String decodeNvidia(int v) { return (v >>> 22 & 0x3FF) + "." + (v >>> 14 & 0xff) + "." + (v >>> 6 & 0xff) + "." + (v & 0xff); } - static int getVkVer() { + private static int getVkVer() { try (MemoryStack stack = MemoryStack.stackPush()) { var a = stack.mallocInt(1); vkEnumerateInstanceVersion(a); @@ -120,43 +136,18 @@ static int getVkVer() { } } - public Set getUnsupportedExtensions(Set requiredExtensions) { - try (MemoryStack stack = stackPush()) { - + private static List getAvailableExtension(VkPhysicalDevice device) { + try (MemoryStack stack = MemoryStack.stackPush()) { + // Query first for extension count IntBuffer extensionCount = stack.ints(0); - - vkEnumerateDeviceExtensionProperties(physicalDevice, (String) null, extensionCount, null); + vkEnumerateDeviceExtensionProperties(device, (String) null, extensionCount, null); VkExtensionProperties.Buffer availableExtensions = VkExtensionProperties.malloc(extensionCount.get(0), stack); + vkEnumerateDeviceExtensionProperties(device, (String) null, extensionCount, availableExtensions); - vkEnumerateDeviceExtensionProperties(physicalDevice, (String) null, extensionCount, availableExtensions); - - Set extensions = availableExtensions.stream() - .map(VkExtensionProperties::extensionNameString) - .collect(toSet()); - - Set unsupportedExtensions = new HashSet<>(requiredExtensions); - unsupportedExtensions.removeAll(extensions); - - return unsupportedExtensions; + return availableExtensions.stream() + .map(VkExtensionProperties::extensionNameString) + .toList(); } } - - public boolean isDrawIndirectSupported() { - return drawIndirectSupported; - } - - // Added these to allow detecting GPU vendor, to allow handling vendor specific circumstances: - // (e.g. such as in case we encounter a vendor specific driver bug) - public boolean isAMD() { - return vendorId == 0x1022; - } - - public boolean isNvidia() { - return vendorId == 0x10DE; - } - - public boolean isIntel() { - return vendorId == 0x8086; - } } diff --git a/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java b/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java index 18fd202a0..58489b9d0 100644 --- a/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java +++ b/src/main/java/net/vulkanmod/vulkan/device/DeviceManager.java @@ -11,9 +11,9 @@ import java.nio.IntBuffer; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; -import static java.util.stream.Collectors.toSet; import static net.vulkanmod.vulkan.queue.Queue.findQueueFamilies; import static net.vulkanmod.vulkan.util.VUtil.asPointerBuffer; import static org.lwjgl.glfw.GLFWVulkan.glfwGetRequiredInstanceExtensions; @@ -87,7 +87,7 @@ static void getSuitableDevices(VkInstance instance) { List devices = new ObjectArrayList<>(); for (Device device : availableDevices) { - if (isDeviceSuitable(device.physicalDevice)) { + if (isDeviceSuitable(device)) { devices.add(device); } } @@ -154,7 +154,7 @@ else if (!otherDevices.isEmpty()) public static void createLogicalDevice() { try (MemoryStack stack = stackPush()) { - net.vulkanmod.vulkan.queue.Queue.QueueFamilyIndices indices = findQueueFamilies(physicalDevice); + Queue.QueueFamilyIndices indices = findQueueFamilies(physicalDevice); int[] uniqueQueueFamilies = indices.unique(); @@ -248,39 +248,38 @@ private static PointerBuffer getRequiredExtensions() { return glfwExtensions; } - private static boolean isDeviceSuitable(VkPhysicalDevice device) { + private static boolean isDeviceSuitable(Device device) { try (MemoryStack stack = stackPush()) { - Queue.QueueFamilyIndices indices = findQueueFamilies(device); - - VkExtensionProperties.Buffer availableExtensions = getAvailableExtension(stack, device); - boolean extensionsSupported = availableExtensions.stream() - .map(VkExtensionProperties::extensionNameString) - .collect(toSet()) - .containsAll(Vulkan.REQUIRED_EXTENSION); + var supportedExtension = device.getSupportedExtensions(); + boolean extensionsSupported = new HashSet<>(supportedExtension).containsAll(Vulkan.REQUIRED_EXTENSION); boolean swapChainAdequate = false; if (extensionsSupported) { - SurfaceProperties surfaceProperties = querySurfaceProperties(device, stack); + SurfaceProperties surfaceProperties = querySurfaceProperties(device.physicalDevice, stack); swapChainAdequate = surfaceProperties.formats.hasRemaining() && surfaceProperties.presentModes.hasRemaining(); } VkPhysicalDeviceFeatures supportedFeatures = VkPhysicalDeviceFeatures.malloc(stack); - vkGetPhysicalDeviceFeatures(device, supportedFeatures); + vkGetPhysicalDeviceFeatures(device.physicalDevice, supportedFeatures); boolean anisotropicFilterSupported = supportedFeatures.samplerAnisotropy(); + Queue.QueueFamilyIndices indices = findQueueFamilies(device.physicalDevice); + return indices.isSuitable() && extensionsSupported && swapChainAdequate; } } - private static VkExtensionProperties.Buffer getAvailableExtension(MemoryStack stack, VkPhysicalDevice device) { + private static List getAvailableExtension(MemoryStack stack, VkPhysicalDevice device) { IntBuffer extensionCount = stack.ints(0); vkEnumerateDeviceExtensionProperties(device, (String) null, extensionCount, null); VkExtensionProperties.Buffer availableExtensions = VkExtensionProperties.malloc(extensionCount.get(0), stack); vkEnumerateDeviceExtensionProperties(device, (String) null, extensionCount, availableExtensions); - return availableExtensions; + return availableExtensions.stream() + .map(VkExtensionProperties::extensionNameString) + .toList(); } // Use the optimal most performant depth format for the specific GPU diff --git a/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java b/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java index 9fe29eec7..1120a0a30 100644 --- a/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java +++ b/src/main/java/net/vulkanmod/vulkan/framebuffer/SwapChain.java @@ -9,6 +9,7 @@ import net.vulkanmod.vulkan.queue.Queue; import net.vulkanmod.vulkan.texture.SamplerManager; import net.vulkanmod.vulkan.texture.VulkanImage; +import net.vulkanmod.config.Platform; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.*; @@ -24,6 +25,7 @@ import static org.lwjgl.glfw.GLFW.glfwGetFramebufferSize; import static org.lwjgl.system.MemoryStack.stackGet; import static org.lwjgl.system.MemoryStack.stackPush; +import static org.lwjgl.vulkan.EXTSwapchainColorspace.*; import static org.lwjgl.vulkan.KHRSurface.*; import static org.lwjgl.vulkan.KHRSwapchain.*; import static org.lwjgl.vulkan.VK10.*; @@ -161,9 +163,7 @@ private void createSwapChain() { } private long[] createFramebuffers(RenderPass renderPass) { - try (MemoryStack stack = MemoryStack.stackPush()) { - long[] framebuffers = new long[this.swapChainImages.size()]; for (int i = 0; i < this.swapChainImages.size(); ++i) { @@ -243,19 +243,40 @@ public long getImageView(int i) { private VkSurfaceFormatKHR getFormat(VkSurfaceFormatKHR.Buffer availableFormats) { List list = availableFormats.stream().toList(); - VkSurfaceFormatKHR format = list.get(0); + int colorSpace; + if (Platform.isMacOS() && colorSpaceExtSupport()) { + colorSpace = VK_COLOR_SPACE_DISPLAY_P3_NONLINEAR_EXT; + } else { + colorSpace = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; + } + // Try RGBA format first + VkSurfaceFormatKHR format = null; for (VkSurfaceFormatKHR availableFormat : list) { - if (availableFormat.format() == VK_FORMAT_R8G8B8A8_UNORM && availableFormat.colorSpace() == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + if (availableFormat.format() == VK_FORMAT_R8G8B8A8_UNORM && availableFormat.colorSpace() == colorSpace) return availableFormat; - if (availableFormat.format() == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace() == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { + // Fallback to RGBA sRBG in case of no match + if (availableFormat.format() == VK_FORMAT_R8G8B8A8_UNORM && availableFormat.colorSpace() == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + format = availableFormat; + } + + for (VkSurfaceFormatKHR availableFormat : list) { + if (availableFormat.format() == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace() == colorSpace) { format = availableFormat; } + + if (format == null && availableFormat.format() == VK_FORMAT_B8G8R8A8_UNORM && availableFormat.colorSpace() == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) + format = availableFormat; + } + + if (format == null) { + format = list.get(0); } if (format.format() == VK_FORMAT_B8G8R8A8_UNORM) isBGRAformat = true; + return format; }