From f8703fb47ce4abb3de8c560a8b867128d6c7b118 Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Sun, 25 May 2025 16:18:57 +0200 Subject: [PATCH] Refactor + Add swapchain --- .gitignore | 3 +- .../fr/mrdev023/vulkan_java/vk/Device.java | 2 - .../fr/mrdev023/vulkan_java/vk/Instance.java | 51 ++-- .../vulkan_java/vk/InstanceExtensions.java | 128 ++++++++++ .../vk/InstanceValidationLayers.java | 119 +++++++++ .../vulkan_java/vk/PhysicalDevice.java | 76 +++--- .../fr/mrdev023/vulkan_java/vk/Queue.java | 22 +- .../SuitablePhysicalDeviceFinder.java | 5 +- .../fr/mrdev023/vulkan_java/vk/Surface.java | 15 +- .../fr/mrdev023/vulkan_java/vk/Swapchain.java | 232 ++++++++++++++++++ .../fr/mrdev023/vulkan_java/vk/Vulkan.java | 16 +- .../PhysicalDevicePropertiesLogger.java | 7 +- .../vulkan_java/vk/loggers/ScopedLogger.java | 2 +- .../vk/utils/InstanceExtensions.java | 163 ------------ .../vk/utils/InstanceValidationLayers.java | 146 ----------- 15 files changed, 612 insertions(+), 375 deletions(-) create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/InstanceExtensions.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/InstanceValidationLayers.java rename src/main/java/fr/mrdev023/vulkan_java/vk/{utils => }/SuitablePhysicalDeviceFinder.java (92%) create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/Swapchain.java delete mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceExtensions.java delete mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayers.java diff --git a/.gitignore b/.gitignore index a356b9a..2ba47ae 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ target .idea/ *.iml -dependency-reduced-pom.xml \ No newline at end of file +dependency-reduced-pom.xml +hs_err_pid*.log \ No newline at end of file diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java index 30acc49..56f0684 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java @@ -15,8 +15,6 @@ import org.lwjgl.vulkan.VkDeviceCreateInfo; import org.lwjgl.vulkan.VkPhysicalDeviceFeatures; import org.tinylog.Logger; -import fr.mrdev023.vulkan_java.vk.utils.SuitablePhysicalDeviceFinder; - public class Device { private final PhysicalDevice physicalDevice; diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java index 8f84875..7f54db7 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java @@ -1,18 +1,34 @@ package fr.mrdev023.vulkan_java.vk; -import org.lwjgl.PointerBuffer; -import org.lwjgl.system.*; -import org.lwjgl.vulkan.*; -import org.tinylog.Logger; - -import fr.mrdev023.vulkan_java.vk.utils.InstanceExtensions; -import fr.mrdev023.vulkan_java.vk.utils.InstanceValidationLayers; - -import java.nio.*; - import static fr.mrdev023.vulkan_java.vk.VulkanError.vkCheck; -import static org.lwjgl.vulkan.EXTDebugUtils.*; -import static org.lwjgl.vulkan.VK11.*; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; +import static org.lwjgl.vulkan.EXTDebugUtils.vkCreateDebugUtilsMessengerEXT; +import static org.lwjgl.vulkan.EXTDebugUtils.vkDestroyDebugUtilsMessengerEXT; +import static org.lwjgl.vulkan.VK10.VK_FALSE; +import static org.lwjgl.vulkan.VK10.VK_NULL_HANDLE; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_APPLICATION_INFO; +import static org.lwjgl.vulkan.VK10.VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; +import static org.lwjgl.vulkan.VK10.vkCreateInstance; +import static org.lwjgl.vulkan.VK10.vkDestroyInstance; + +import java.nio.ByteBuffer; +import java.nio.LongBuffer; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.system.MemoryUtil; +import org.lwjgl.vulkan.VkApplicationInfo; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCallbackDataEXT; +import org.lwjgl.vulkan.VkDebugUtilsMessengerCreateInfoEXT; +import org.lwjgl.vulkan.VkInstance; +import org.lwjgl.vulkan.VkInstanceCreateInfo; +import org.tinylog.Logger; public class Instance { @@ -23,6 +39,8 @@ public class Instance { VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; private final VkInstance vkInstance; + private final InstanceValidationLayers validationLayers; + private final InstanceExtensions instanceExtensions; private VkDebugUtilsMessengerCreateInfoEXT debugUtils; private long vkDebugHandle; @@ -37,13 +55,8 @@ public class Instance { .applicationVersion(1) .pEngineName(appShortName); - InstanceValidationLayers validationLayers = new InstanceValidationLayers.Selector() - .includeValidationLayers(validate) - .selectValidationLayers(); - - InstanceExtensions instanceExtensions = new InstanceExtensions.Selector() - .withValidationLayers(validationLayers) - .selectInstanceExtensions(); + validationLayers = new InstanceValidationLayers(); + instanceExtensions = new InstanceExtensions(true); PointerBuffer instanceExtensionsBuffer = instanceExtensions.writeToStack(stack); PointerBuffer validationLayersBuffer = validationLayers.writeToStack(stack); diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/InstanceExtensions.java b/src/main/java/fr/mrdev023/vulkan_java/vk/InstanceExtensions.java new file mode 100644 index 0000000..4d6fd3d --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/InstanceExtensions.java @@ -0,0 +1,128 @@ +package fr.mrdev023.vulkan_java.vk; + +import java.nio.IntBuffer; +import java.util.HashSet; +import java.util.Set; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.glfw.GLFWVulkan; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.EXTDebugUtils; +import org.lwjgl.vulkan.KHRPortabilityEnumeration; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkExtensionProperties; +import org.lwjgl.vulkan.VkInstanceCreateInfo; +import org.tinylog.Logger; + +/** + * This class is used to store the instance extensions to use to create the + * Vulkan instance. + * + * @see InstanceExtensions.Selector to select the instance extensions + */ +public class InstanceExtensions { + private Set instanceExtensions; + private Set glfwExtensions; + private Set portabilityExtensions; + private Set selectedExtensions; + + public InstanceExtensions(boolean withValidationLayers) { + this.instanceExtensions = queryVkInstanceExtensions(); + log("Supported instance extensions", instanceExtensions); + + this.glfwExtensions = getGLFWRequiredExtensions(); + this.portabilityExtensions = getVkPortabilityExtensions(instanceExtensions); + + selectedExtensions = new HashSet<>(); + selectedExtensions.addAll(glfwExtensions); + selectedExtensions.addAll(portabilityExtensions); + + if (withValidationLayers) { + selectedExtensions.add(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME); + } + log("Selected instance extensions", selectedExtensions); + } + + public boolean hasInstanceExtensions() { + return !instanceExtensions.isEmpty(); + } + + public Set getInstanceExtensions() { + return instanceExtensions; + } + + public PointerBuffer writeToStack(MemoryStack stack) { + int numExtensions = instanceExtensions.size(); + + PointerBuffer requiredExtensions = stack.mallocPointer(numExtensions); + for (String extension : instanceExtensions) { + requiredExtensions.put(stack.UTF8(extension)); + } + requiredExtensions.flip(); + + return requiredExtensions; + } + + public void writeInstanceCreateInfoFlags(VkInstanceCreateInfo instanceInfo) { + if (instanceExtensions.contains(KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { + instanceInfo.flags(KHRPortabilityEnumeration.VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR); + } + } + + private static Set queryVkInstanceExtensions() { + Set instanceExtensions = new HashSet<>(); + + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer numExtensionsBuf = stack.callocInt(1); + VK10.vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, null); + int numExtensions = numExtensionsBuf.get(0); + + VkExtensionProperties.Buffer instanceExtensionsProps = VkExtensionProperties.calloc(numExtensions, + stack); + VK10.vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, instanceExtensionsProps); + for (int i = 0; i < numExtensions; i++) { + VkExtensionProperties props = instanceExtensionsProps.get(i); + String extensionName = props.extensionNameString(); + instanceExtensions.add(extensionName); + } + } + + return instanceExtensions; + } + + private static Set getVkPortabilityExtensions(Set instanceExtensions) { + Set portabilityExtensions = new HashSet<>(); + + var osType = VulkanUtils.getOS(); + if (osType == VulkanUtils.OSType.MACOS) { + if (!instanceExtensions + .contains(KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { + throw new RuntimeException( + "Vulkan instance does not support portability enumeration extension but it's required for MacOS"); + } + portabilityExtensions.add(KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); + } + + return portabilityExtensions; + } + + private static Set getGLFWRequiredExtensions() { + PointerBuffer glfwExtensionsBuffer = GLFWVulkan.glfwGetRequiredInstanceExtensions(); + if (glfwExtensionsBuffer == null) { + throw new RuntimeException("Failed to find the GLFW platform surface extensions"); + } + + Set glfwExtensions = new HashSet<>(); + for (int i = 0; i < glfwExtensionsBuffer.remaining(); i++) { + glfwExtensions.add(glfwExtensionsBuffer.getStringUTF8(i)); + } + return glfwExtensions; + } + + private void log(String title, Set layers) { + Logger.debug("{} ({})", title, layers.size()); + for (String layer : layers) { + Logger.debug(" - {}", layer); + } + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/InstanceValidationLayers.java b/src/main/java/fr/mrdev023/vulkan_java/vk/InstanceValidationLayers.java new file mode 100644 index 0000000..be5bb1d --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/InstanceValidationLayers.java @@ -0,0 +1,119 @@ +package fr.mrdev023.vulkan_java.vk; + +import java.nio.IntBuffer; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import org.lwjgl.PointerBuffer; +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkLayerProperties; +import org.tinylog.Logger; + +public class InstanceValidationLayers { + private Set validationLayers; + + public InstanceValidationLayers() { + this.validationLayers = selectValidationLayers(); + } + + public boolean hasValidationLayers() { + return !validationLayers.isEmpty(); + } + + public Set getValidationLayers() { + return validationLayers; + } + + public PointerBuffer writeToStack(MemoryStack stack) { + if (validationLayers.isEmpty()) { + return null; + } + + PointerBuffer requiredLayers = stack.mallocPointer(validationLayers.size()); + for (String layer : validationLayers) { + requiredLayers.put(stack.UTF8(layer)); + } + requiredLayers.flip(); + + return requiredLayers; + } + + /** + * Selects the validation layers. + * + * @return the set of validation layers + */ + private static Set selectValidationLayers() { + var supportedLayers = queryVkValidationLayers(); + log("Supported validation layers", supportedLayers); + + Set layersToUse = getValidationLayersFromSupportedLayers(supportedLayers); + log("Selected validation layers", layersToUse); + + if (layersToUse.isEmpty()) { + Logger.warn( + "Request validation but no supported validation layers found. Falling back to no validation"); + } + + return layersToUse; + } + + private static Set queryVkValidationLayers() { + Set supportedLayers = new HashSet<>(); + + try (MemoryStack stack = MemoryStack.stackPush()) { + // Get the number of supported validation layers by the current vulkan instance + IntBuffer numLayersArr = stack.callocInt(1); + VK10.vkEnumerateInstanceLayerProperties(numLayersArr, null); + int numLayers = numLayersArr.get(0); + + // Get the supported validation layers by the current vulkan instance + VkLayerProperties.Buffer propsBuf = VkLayerProperties.calloc(numLayers, stack); + VK10.vkEnumerateInstanceLayerProperties(numLayersArr, propsBuf); + + // Convert the supported validation layers to a list of strings + for (int i = 0; i < numLayers; i++) { + VkLayerProperties props = propsBuf.get(i); + String layerName = props.layerNameString(); + supportedLayers.add(layerName); + } + } + + return supportedLayers; + } + + private static Set getValidationLayersFromSupportedLayers(Set supportedLayers) { + Set layersToUse = new HashSet<>(); + + // Main validation layer + if (supportedLayers.contains("VK_LAYER_KHRONOS_validation")) { + layersToUse.add("VK_LAYER_KHRONOS_validation"); + return layersToUse; + } + + // Fallback 1 + if (supportedLayers.contains("VK_LAYER_LUNARG_standard_validation")) { + layersToUse.add("VK_LAYER_LUNARG_standard_validation"); + return layersToUse; + } + + // Fallback 2 (set) + Set requestedLayers = new HashSet<>(); + requestedLayers.add("VK_LAYER_GOOGLE_threading"); + requestedLayers.add("VK_LAYER_LUNARG_parameter_validation"); + requestedLayers.add("VK_LAYER_LUNARG_object_tracker"); + requestedLayers.add("VK_LAYER_LUNARG_core_validation"); + requestedLayers.add("VK_LAYER_GOOGLE_unique_objects"); + + return requestedLayers.stream().filter(supportedLayers::contains).collect(Collectors.toSet()); + } + + private static void log(String title, Set layers) { + Logger.debug("{} ({})", title, layers.size()); + for (String layer : layers) { + Logger.debug(" - {}", layer); + } + } +} \ No newline at end of file diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/PhysicalDevice.java b/src/main/java/fr/mrdev023/vulkan_java/vk/PhysicalDevice.java index 03404e7..0287770 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/PhysicalDevice.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/PhysicalDevice.java @@ -1,16 +1,27 @@ package fr.mrdev023.vulkan_java.vk; +import static fr.mrdev023.vulkan_java.vk.VulkanError.vkCheck; +import static org.lwjgl.vulkan.VK10.vkEnumerateDeviceExtensionProperties; +import static org.lwjgl.vulkan.VK10.vkEnumeratePhysicalDevices; +import static org.lwjgl.vulkan.VK10.vkGetPhysicalDeviceFeatures; +import static org.lwjgl.vulkan.VK10.vkGetPhysicalDeviceMemoryProperties; +import static org.lwjgl.vulkan.VK10.vkGetPhysicalDeviceProperties; +import static org.lwjgl.vulkan.VK10.vkGetPhysicalDeviceQueueFamilyProperties; + +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.List; + import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.*; +import org.lwjgl.vulkan.VkExtensionProperties; +import org.lwjgl.vulkan.VkPhysicalDevice; +import org.lwjgl.vulkan.VkPhysicalDeviceFeatures; +import org.lwjgl.vulkan.VkPhysicalDeviceMemoryProperties; +import org.lwjgl.vulkan.VkPhysicalDeviceProperties; +import org.lwjgl.vulkan.VkQueueFamilyProperties; import org.tinylog.Logger; -import java.nio.IntBuffer; -import java.util.*; - -import static fr.mrdev023.vulkan_java.vk.VulkanError.vkCheck; -import static org.lwjgl.vulkan.VK11.*; - public class PhysicalDevice { private final VkExtensionProperties.Buffer vkDeviceExtensions; @@ -28,8 +39,8 @@ public class PhysicalDevice { vkPhysicalDeviceProperties = VkPhysicalDeviceProperties.calloc(); vkGetPhysicalDeviceProperties(vkPhysicalDevice, vkPhysicalDeviceProperties); - vkDeviceExtensions = getVulkanPhysicalDeviceExtensions(stack, vkPhysicalDevice); - vkQueueFamilyProps = getVulkanQueueFamilyProperties(stack, vkPhysicalDevice); + vkDeviceExtensions = queryVkPhysicalDeviceExtensions(stack, vkPhysicalDevice); + vkQueueFamilyProps = queryVkQueueFamilyProperties(stack, vkPhysicalDevice); vkPhysicalDeviceFeatures = VkPhysicalDeviceFeatures.calloc(); vkGetPhysicalDeviceFeatures(vkPhysicalDevice, vkPhysicalDeviceFeatures); @@ -41,25 +52,11 @@ public class PhysicalDevice { } public static List getPhysicalDevices(Instance instance, MemoryStack stack) throws VulkanError { - PointerBuffer pPhysicalDevices; - // Get number of physical devices - IntBuffer intBuffer = stack.mallocInt(1); - vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, null), - "Failed to get number of physical devices"); - int numDevices = intBuffer.get(0); - Logger.debug("Detected {} physical device(s)", numDevices); - - // Populate physical devices list pointer - pPhysicalDevices = stack.mallocPointer(numDevices); - vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, pPhysicalDevices), - "Failed to get physical devices"); - + // Streams mapping is not possible because it not propagates the VulkanError List physicalDevices = new ArrayList<>(); - for (int i = 0; i < numDevices; i++) { - VkPhysicalDevice physicalDevice = new VkPhysicalDevice(pPhysicalDevices.get(i), instance.getVkInstance()); - physicalDevices.add(new PhysicalDevice(physicalDevice)); + for (VkPhysicalDevice vkPhysicalDevice : queryVkPhysicalDevices(instance, stack)) { + physicalDevices.add(new PhysicalDevice(vkPhysicalDevice)); } - return physicalDevices; } @@ -102,7 +99,30 @@ public class PhysicalDevice { return vkDeviceExtensions; } - private static VkExtensionProperties.Buffer getVulkanPhysicalDeviceExtensions(MemoryStack stack, + private static List queryVkPhysicalDevices(Instance instance, MemoryStack stack) + throws VulkanError { + PointerBuffer pPhysicalDevices; + // Get number of physical devices + IntBuffer intBuffer = stack.mallocInt(1); + vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, null), + "Failed to get number of physical devices"); + int numDevices = intBuffer.get(0); + Logger.debug("Detected {} physical device(s)", numDevices); + + // Populate physical devices list pointer + pPhysicalDevices = stack.mallocPointer(numDevices); + vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, pPhysicalDevices), + "Failed to get physical devices"); + + List physicalDevices = new ArrayList<>(); + for (int i = 0; i < numDevices; i++) { + physicalDevices.add(new VkPhysicalDevice(pPhysicalDevices.get(i), instance.getVkInstance())); + } + + return physicalDevices; + } + + private static VkExtensionProperties.Buffer queryVkPhysicalDeviceExtensions(MemoryStack stack, VkPhysicalDevice physicalDevice) throws VulkanError { IntBuffer numExtensions = stack.mallocInt(1); @@ -117,7 +137,7 @@ public class PhysicalDevice { return deviceExtensions; } - private static VkQueueFamilyProperties.Buffer getVulkanQueueFamilyProperties(MemoryStack stack, + private static VkQueueFamilyProperties.Buffer queryVkQueueFamilyProperties(MemoryStack stack, VkPhysicalDevice physicalDevice) throws VulkanError { IntBuffer numQueueFamilies = stack.mallocInt(1); diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Queue.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Queue.java index 0513710..41548c0 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Queue.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Queue.java @@ -1,18 +1,22 @@ package fr.mrdev023.vulkan_java.vk; +import static org.lwjgl.vulkan.VK10.vkGetDeviceQueue; +import static org.lwjgl.vulkan.VK10.vkQueueWaitIdle; + import org.lwjgl.PointerBuffer; import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.*; +import org.lwjgl.vulkan.VkQueue; import org.tinylog.Logger; -import static org.lwjgl.vulkan.VK11.*; - public class Queue { private final VkQueue vkQueue; + private final int familyIndex; + private final int queueIndex; public Queue(Device device, int queueFamilyIndex, int queueIndex) { - Logger.debug("Creating queue"); + this.familyIndex = queueFamilyIndex; + this.queueIndex = queueIndex; try (MemoryStack stack = MemoryStack.stackPush()) { PointerBuffer pQueue = stack.mallocPointer(1); @@ -20,12 +24,22 @@ public class Queue { long queue = pQueue.get(0); vkQueue = new VkQueue(queue, device.getVkDevice()); } + + Logger.debug("Queue created on family index {} and queue index {}", familyIndex, queueIndex); } public VkQueue getVkQueue() { return vkQueue; } + public int getFamilyIndex() { + return familyIndex; + } + + public int getQueueIndex() { + return queueIndex; + } + public void waitIdle() { vkQueueWaitIdle(vkQueue); } diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java b/src/main/java/fr/mrdev023/vulkan_java/vk/SuitablePhysicalDeviceFinder.java similarity index 92% rename from src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java rename to src/main/java/fr/mrdev023/vulkan_java/vk/SuitablePhysicalDeviceFinder.java index 829b424..e1db117 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/SuitablePhysicalDeviceFinder.java @@ -1,4 +1,4 @@ -package fr.mrdev023.vulkan_java.vk.utils; +package fr.mrdev023.vulkan_java.vk; import java.util.ArrayList; import java.util.Comparator; @@ -7,9 +7,6 @@ import java.util.List; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.VK10; -import fr.mrdev023.vulkan_java.vk.Instance; -import fr.mrdev023.vulkan_java.vk.PhysicalDevice; -import fr.mrdev023.vulkan_java.vk.VulkanError; import fr.mrdev023.vulkan_java.vk.loggers.PhysicalDeviceCompatibilityLogger; import fr.mrdev023.vulkan_java.vk.validators.PhysicalDeviceCompatibilityValidator; diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Surface.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Surface.java index 34f2a57..3eff6ad 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Surface.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Surface.java @@ -1,21 +1,24 @@ package fr.mrdev023.vulkan_java.vk; +import static fr.mrdev023.vulkan_java.vk.VulkanError.vkCheck; + +import java.nio.LongBuffer; + import org.lwjgl.glfw.GLFWVulkan; import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.KHRSurface; import org.tinylog.Logger; -import static fr.mrdev023.vulkan_java.vk.VulkanError.vkCheck; - -import java.nio.LongBuffer; - public class Surface { private final Instance instance; + private final long windowHandle; private final long vkSurface; public Surface(Instance instance, long windowHandle) throws VulkanError { this.instance = instance; + this.windowHandle = windowHandle; + try (MemoryStack stack = MemoryStack.stackPush()) { LongBuffer pSurface = stack.mallocLong(1); @@ -35,6 +38,10 @@ public class Surface { Logger.debug("Vulkan surface destroyed"); } + public long getWindowHandle() { + return windowHandle; + } + public long getVkSurface() { return vkSurface; } diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Swapchain.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Swapchain.java new file mode 100644 index 0000000..0ddd787 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Swapchain.java @@ -0,0 +1,232 @@ +package fr.mrdev023.vulkan_java.vk; + +import static fr.mrdev023.vulkan_java.vk.VulkanError.vkCheck; + +import java.nio.IntBuffer; +import java.nio.LongBuffer; +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.KHRSurface; +import org.lwjgl.vulkan.KHRSwapchain; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkExtent2D; +import org.lwjgl.vulkan.VkImageViewCreateInfo; +import org.lwjgl.vulkan.VkSurfaceCapabilitiesKHR; +import org.lwjgl.vulkan.VkSurfaceFormatKHR; +import org.lwjgl.vulkan.VkSwapchainCreateInfoKHR; +import org.tinylog.Logger; + +import fr.mrdev023.vulkan_java.window.Display; +import fr.mrdev023.vulkan_java.window.DisplayMode; + +public class Swapchain { + private final Device device; + private final Surface surface; + private final long vkSwapchain; + private final List vkImages; + private final List vkImageViews; + private final VkSurfaceCapabilitiesKHR vkSurfaceCapabilities; + private final VkSurfaceFormatKHR.Buffer vkSurfaceFormats; + private final IntBuffer vkPresentModes; + + private final IntBuffer chosenImageCount; + private final VkSurfaceFormatKHR chosenSurfaceFormat; + private final int chosenPresentMode; + private final VkExtent2D chosenExtent; + + public Swapchain(Device device, Surface surface) throws VulkanError { + this.device = device; + this.surface = surface; + + try (MemoryStack stack = MemoryStack.stackPush()) { + vkSurfaceCapabilities = queryVkSurfaceCapabilities(stack); + vkSurfaceFormats = queryVkPhysicalDeviceSurfaceFormats(stack); + vkPresentModes = queryVkPhysicalDeviceSurfacePresentModes(stack); + + chosenSurfaceFormat = vkSurfaceFormats.get(0); + chosenPresentMode = KHRSurface.VK_PRESENT_MODE_FIFO_KHR; + chosenExtent = chooseSwapExtent(stack); + chosenImageCount = stack.ints(vkSurfaceCapabilities.minImageCount() + 1); + if (vkSurfaceCapabilities.maxImageCount() > 0 + && chosenImageCount.get(0) > vkSurfaceCapabilities.maxImageCount()) { + chosenImageCount.put(0, vkSurfaceCapabilities.maxImageCount()); + } + + VkSwapchainCreateInfoKHR createInfo = createVkSwapchainCreateInfo(stack, false); + LongBuffer pSwapChain = stack.longs(VK10.VK_NULL_HANDLE); + vkCheck(KHRSwapchain.vkCreateSwapchainKHR(device.getVkDevice(), createInfo, null, pSwapChain), + "Failed to create swapchain"); + + vkSwapchain = pSwapChain.get(0); + + IntBuffer imageCount = stack.ints(0); + KHRSwapchain.vkGetSwapchainImagesKHR(device.getVkDevice(), vkSwapchain, imageCount, null); + + LongBuffer pImages = stack.mallocLong(imageCount.get(0)); + KHRSwapchain.vkGetSwapchainImagesKHR(device.getVkDevice(), vkSwapchain, imageCount, pImages); + + vkImages = new ArrayList<>(imageCount.get(0)); + for (int i = 0; i < imageCount.get(0); i++) { + vkImages.add(pImages.get(i)); + } + + vkImageViews = createVkImageViews(stack); + Logger.debug("Swapchain created"); + } + } + + public Device getDevice() { + return device; + } + + public Surface getSurface() { + return surface; + } + + public VkSurfaceCapabilitiesKHR getVkSurfaceCapabilities() { + return vkSurfaceCapabilities; + } + + public VkSurfaceFormatKHR.Buffer getVkSurfaceFormats() { + return vkSurfaceFormats; + } + + public IntBuffer getVkPresentModes() { + return vkPresentModes; + } + + public void destroy() { + for (long imageView : vkImageViews) { + VK10.vkDestroyImageView(device.getVkDevice(), imageView, null); + } + KHRSwapchain.vkDestroySwapchainKHR(device.getVkDevice(), vkSwapchain, null); + } + + private VkSwapchainCreateInfoKHR createVkSwapchainCreateInfo(MemoryStack stack, boolean recreate) { + VkSwapchainCreateInfoKHR createInfo = VkSwapchainCreateInfoKHR.calloc(stack); + + createInfo.sType(KHRSwapchain.VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR); + createInfo.surface(surface.getVkSurface()); + + // Image settings + createInfo.minImageCount(chosenImageCount.get(0)); + createInfo.imageFormat(chosenSurfaceFormat.format()); + createInfo.imageColorSpace(chosenSurfaceFormat.colorSpace()); + createInfo.imageExtent(chosenExtent); + createInfo.imageArrayLayers(1); + createInfo.imageUsage(VK10.VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT); + + // Queue family indices + createInfo.imageSharingMode(VK10.VK_SHARING_MODE_EXCLUSIVE); // If different queues between graphics and + // present, use VK_SHARING_MODE_CONCURRENT + createInfo.pQueueFamilyIndices(stack.ints(device.getGraphicsQueue().orElseThrow().getFamilyIndex())); + + createInfo.preTransform(vkSurfaceCapabilities.currentTransform()); + createInfo.compositeAlpha(KHRSurface.VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR); + createInfo.presentMode(chosenPresentMode); + createInfo.clipped(true); + + if (recreate) { + createInfo.oldSwapchain(vkSwapchain); + } else { + createInfo.oldSwapchain(VK10.VK_NULL_HANDLE); + } + + return createInfo; + } + + private List createVkImageViews(MemoryStack stack) throws VulkanError { + List vkImageViews = new ArrayList<>(); + + for (long image : vkImages) { + VkImageViewCreateInfo createInfo = createVkImageViewCreateInfo(stack, image); + LongBuffer pImageView = stack.mallocLong(1); + vkCheck(VK10.vkCreateImageView(device.getVkDevice(), createInfo, null, pImageView), + "Failed to create image views"); + vkImageViews.add(pImageView.get(0)); + } + + return vkImageViews; + } + + private VkImageViewCreateInfo createVkImageViewCreateInfo(MemoryStack stack, long image) { + VkImageViewCreateInfo createInfo = VkImageViewCreateInfo.calloc(stack); + + createInfo.sType(VK10.VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO); + createInfo.image(image); + createInfo.viewType(VK10.VK_IMAGE_VIEW_TYPE_2D); + createInfo.format(chosenSurfaceFormat.format()); + + createInfo.components().r(VK10.VK_COMPONENT_SWIZZLE_IDENTITY); + createInfo.components().g(VK10.VK_COMPONENT_SWIZZLE_IDENTITY); + createInfo.components().b(VK10.VK_COMPONENT_SWIZZLE_IDENTITY); + createInfo.components().a(VK10.VK_COMPONENT_SWIZZLE_IDENTITY); + + createInfo.subresourceRange().aspectMask(VK10.VK_IMAGE_ASPECT_COLOR_BIT); + createInfo.subresourceRange().baseMipLevel(0); + createInfo.subresourceRange().levelCount(1); + createInfo.subresourceRange().baseArrayLayer(0); + createInfo.subresourceRange().layerCount(1); + + return createInfo; + } + + private VkExtent2D chooseSwapExtent(MemoryStack stack) { + DisplayMode displayMode = Display.getDisplayMode(); + + VkExtent2D actualExtent = VkExtent2D.malloc(stack).set(displayMode.getWidth(), displayMode.getHeight()); + + VkExtent2D minExtent = vkSurfaceCapabilities.minImageExtent(); + VkExtent2D maxExtent = vkSurfaceCapabilities.maxImageExtent(); + + actualExtent.width(Math.clamp(actualExtent.width(), minExtent.width(), maxExtent.width())); + actualExtent.height(Math.clamp(actualExtent.height(), minExtent.height(), maxExtent.height())); + + return actualExtent; + } + + private VkSurfaceCapabilitiesKHR queryVkSurfaceCapabilities(MemoryStack stack) { + VkSurfaceCapabilitiesKHR vkSurfaceCapabilities = VkSurfaceCapabilitiesKHR.malloc(stack); + KHRSurface.vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device.getPhysicalDevice().getVkPhysicalDevice(), + surface.getVkSurface(), + vkSurfaceCapabilities); + + return vkSurfaceCapabilities; + } + + private VkSurfaceFormatKHR.Buffer queryVkPhysicalDeviceSurfaceFormats(MemoryStack stack) { + VkSurfaceFormatKHR.Buffer vkSurfaceFormat; + IntBuffer count = stack.ints(0); + + KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(device.getPhysicalDevice().getVkPhysicalDevice(), + surface.getVkSurface(), count, null); + if (count.get(0) == 0) { + throw new RuntimeException("No surface formats found"); + } + + vkSurfaceFormat = VkSurfaceFormatKHR.malloc(count.get(0), stack); + KHRSurface.vkGetPhysicalDeviceSurfaceFormatsKHR(device.getPhysicalDevice().getVkPhysicalDevice(), + surface.getVkSurface(), count, vkSurfaceFormat); + + return vkSurfaceFormat; + } + + private IntBuffer queryVkPhysicalDeviceSurfacePresentModes(MemoryStack stack) { + IntBuffer vkPresentMode; + IntBuffer count = stack.ints(0); + + KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(device.getPhysicalDevice().getVkPhysicalDevice(), + surface.getVkSurface(), count, null); + if (count.get(0) == 0) { + throw new RuntimeException("No present modes found"); + } + + vkPresentMode = stack.mallocInt(count.get(0)); + KHRSurface.vkGetPhysicalDeviceSurfacePresentModesKHR(device.getPhysicalDevice().getVkPhysicalDevice(), + surface.getVkSurface(), count, vkPresentMode); + + return vkPresentMode; + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java index 8f9c91a..bac30b1 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java @@ -5,12 +5,15 @@ import static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported; import java.util.Optional; import java.util.Set; +import org.lwjgl.vulkan.KHRCreateRenderpass2; +import org.lwjgl.vulkan.KHRDepthStencilResolve; import org.lwjgl.vulkan.KHRDynamicRendering; +import org.lwjgl.vulkan.KHRMaintenance2; +import org.lwjgl.vulkan.KHRMultiview; import org.lwjgl.vulkan.KHRPortabilitySubset; import org.lwjgl.vulkan.KHRSwapchain; import org.lwjgl.vulkan.VK10; -import fr.mrdev023.vulkan_java.vk.utils.SuitablePhysicalDeviceFinder; import fr.mrdev023.vulkan_java.vk.validators.PhysicalDeviceCompatibilityValidator; import fr.mrdev023.vulkan_java.window.Display; @@ -20,6 +23,7 @@ public class Vulkan { private static PhysicalDevice physicalDevice; private static Device device; private static Surface surface; + private static Swapchain swapchain; public static void init() throws VulkanError { if (!glfwVulkanSupported()) { @@ -30,6 +34,14 @@ public class Vulkan { surface = new Surface(instance, Display.getWindow()); var extensions = Set.of(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, + // Dependency of KHRDynamicRendering + KHRDepthStencilResolve.VK_KHR_DEPTH_STENCIL_RESOLVE_EXTENSION_NAME, + // Dependency of KHRDepthStencilResolve + KHRCreateRenderpass2.VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, + // Dependency of KHRCreateRenderpass2 + KHRMaintenance2.VK_KHR_MAINTENANCE_2_EXTENSION_NAME, + KHRMultiview.VK_KHR_MULTIVIEW_EXTENSION_NAME, + // Required to create swapchain KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME); if (VulkanUtils.getOS() == VulkanUtils.OSType.MACOS) { extensions.add(KHRPortabilitySubset.VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); @@ -48,9 +60,11 @@ public class Vulkan { physicalDevice = physicalDeviceMatch.physicalDevice; device = new Device(physicalDeviceMatch); + swapchain = new Swapchain(device, surface); } public static void destroy() { + swapchain.destroy(); surface.destroy(); device.destroy(); physicalDevice.destroy(); diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDevicePropertiesLogger.java b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDevicePropertiesLogger.java index 3819665..8ebae3d 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDevicePropertiesLogger.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDevicePropertiesLogger.java @@ -11,13 +11,16 @@ public class PhysicalDevicePropertiesLogger { ScopedLogger.pushScope("Physical Device Properties"); ScopedLogger.log("Name: {}", properties.deviceNameString()); ScopedLogger.log("API Version: {}", formatApiVersion(properties.apiVersion())); - ScopedLogger.log("Driver Version: {}", formatApiVersion(properties.driverVersion())); // TODO: format driver version because it's depending of the driver + ScopedLogger.log("Driver Version: {}", formatApiVersion(properties.driverVersion())); // TODO: format driver + // version because it's + // depending of the driver ScopedLogger.log("Device Type: {}", formatDeviceType(properties.deviceType())); ScopedLogger.popScope(); } private static String formatApiVersion(int apiVersion) { - return String.format("%d.%d.%d", VK10.VK_API_VERSION_MAJOR(apiVersion), VK10.VK_API_VERSION_MINOR(apiVersion), VK10.VK_API_VERSION_PATCH(apiVersion)); + return String.format("%d.%d.%d", VK10.VK_API_VERSION_MAJOR(apiVersion), VK10.VK_API_VERSION_MINOR(apiVersion), + VK10.VK_API_VERSION_PATCH(apiVersion)); } private static String formatDeviceType(int deviceType) { diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/ScopedLogger.java b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/ScopedLogger.java index b2e5053..38078e6 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/ScopedLogger.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/ScopedLogger.java @@ -8,7 +8,7 @@ import org.tinylog.Logger; public class ScopedLogger { private static Deque scopes = new ArrayDeque<>(); - + public static void pushScope(final String message, final Object... arguments) { log(message, arguments); scopes.push(message); diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceExtensions.java b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceExtensions.java deleted file mode 100644 index 4430c26..0000000 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceExtensions.java +++ /dev/null @@ -1,163 +0,0 @@ -package fr.mrdev023.vulkan_java.vk.utils; - -import java.nio.IntBuffer; -import java.util.HashSet; -import java.util.Set; - -import org.lwjgl.PointerBuffer; -import org.lwjgl.glfw.GLFWVulkan; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.EXTDebugUtils; -import org.lwjgl.vulkan.KHRPortabilityEnumeration; -import org.lwjgl.vulkan.VK10; -import org.lwjgl.vulkan.VkExtensionProperties; -import org.lwjgl.vulkan.VkInstanceCreateInfo; -import org.tinylog.Logger; - -import fr.mrdev023.vulkan_java.vk.VulkanUtils; - -/** - * This class is used to store the instance extensions to use to create the - * Vulkan instance. - * - * @see InstanceExtensions.Selector to select the instance extensions - */ -public class InstanceExtensions { - private Set instanceExtensions; - - private InstanceExtensions(Set instanceExtensions) { - this.instanceExtensions = instanceExtensions; - } - - public boolean hasInstanceExtensions() { - return !instanceExtensions.isEmpty(); - } - - public Set getInstanceExtensions() { - return instanceExtensions; - } - - public PointerBuffer writeToStack(MemoryStack stack) { - int numExtensions = instanceExtensions.size(); - - PointerBuffer requiredExtensions = stack.mallocPointer(numExtensions); - for (String extension : instanceExtensions) { - requiredExtensions.put(stack.UTF8(extension)); - } - requiredExtensions.flip(); - - return requiredExtensions; - } - - public void writeInstanceCreateInfoFlags(VkInstanceCreateInfo instanceInfo) { - if (instanceExtensions.contains(KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { - instanceInfo.flags(KHRPortabilityEnumeration.VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR); - } - } - - /** - * This class is used to automatically select the instance extensions - * - *
Specification
- *
    - *
  • Get the GLFW required extensions to create the Window Surface
  • - *
  • Get the portability extensions if the OS needs it
  • - *
  • Add Debug Utils extension if validation layers are enabled
  • - *
- * - *
See Also
- *
    - *
  • {@link #selectInstanceExtensions()}
  • - *
  • {@link #withValidationLayers(InstanceValidationLayers)}
  • - *
- */ - public static class Selector { - private InstanceValidationLayers validationLayers; - - public Selector withValidationLayers(InstanceValidationLayers validationLayers) { - this.validationLayers = validationLayers; - return this; - } - - /** - * Selects the instance extensions. - * - * @return the set of instance extensions - */ - public InstanceExtensions selectInstanceExtensions() { - Set instanceExtensions = getInstanceExtensions(); - log("Supported instance extensions", instanceExtensions); - - Set glfwExtensions = getGLFWRequiredExtensions(); - Set portabilityExtensions = getPortabilityExtensions(instanceExtensions); - - Set selectedExtensions = new HashSet<>(); - selectedExtensions.addAll(glfwExtensions); - selectedExtensions.addAll(portabilityExtensions); - - if (validationLayers != null && validationLayers.hasValidationLayers()) { - selectedExtensions.add(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME); - } - log("Selected instance extensions", selectedExtensions); - - return new InstanceExtensions(selectedExtensions); - } - - private Set getInstanceExtensions() { - Set instanceExtensions = new HashSet<>(); - - try (MemoryStack stack = MemoryStack.stackPush()) { - IntBuffer numExtensionsBuf = stack.callocInt(1); - VK10.vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, null); - int numExtensions = numExtensionsBuf.get(0); - - VkExtensionProperties.Buffer instanceExtensionsProps = VkExtensionProperties.calloc(numExtensions, - stack); - VK10.vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, instanceExtensionsProps); - for (int i = 0; i < numExtensions; i++) { - VkExtensionProperties props = instanceExtensionsProps.get(i); - String extensionName = props.extensionNameString(); - instanceExtensions.add(extensionName); - } - } - - return instanceExtensions; - } - - private Set getPortabilityExtensions(Set instanceExtensions) { - Set portabilityExtensions = new HashSet<>(); - - var osType = VulkanUtils.getOS(); - if (osType == VulkanUtils.OSType.MACOS) { - if (!instanceExtensions - .contains(KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)) { - throw new RuntimeException( - "Vulkan instance does not support portability enumeration extension but it's required for MacOS"); - } - portabilityExtensions.add(KHRPortabilityEnumeration.VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); - } - - return portabilityExtensions; - } - - private Set getGLFWRequiredExtensions() { - PointerBuffer glfwExtensionsBuffer = GLFWVulkan.glfwGetRequiredInstanceExtensions(); - if (glfwExtensionsBuffer == null) { - throw new RuntimeException("Failed to find the GLFW platform surface extensions"); - } - - Set glfwExtensions = new HashSet<>(); - for (int i = 0; i < glfwExtensionsBuffer.remaining(); i++) { - glfwExtensions.add(glfwExtensionsBuffer.getStringUTF8(i)); - } - return glfwExtensions; - } - - private void log(String title, Set layers) { - Logger.debug("{} ({})", title, layers.size()); - for (String layer : layers) { - Logger.debug(" - {}", layer); - } - } - } -} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayers.java b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayers.java deleted file mode 100644 index 851b9ba..0000000 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayers.java +++ /dev/null @@ -1,146 +0,0 @@ -package fr.mrdev023.vulkan_java.vk.utils; - -import java.nio.IntBuffer; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; -import java.util.stream.Collectors; - -import org.lwjgl.PointerBuffer; -import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.VK10; -import org.lwjgl.vulkan.VkLayerProperties; -import org.tinylog.Logger; - -public class InstanceValidationLayers { - private Set validationLayers; - - private InstanceValidationLayers(Set validationLayers) { - this.validationLayers = validationLayers; - } - - public boolean hasValidationLayers() { - return !validationLayers.isEmpty(); - } - - public Set getValidationLayers() { - return validationLayers; - } - - public PointerBuffer writeToStack(MemoryStack stack) { - if (validationLayers.isEmpty()) { - return null; - } - - PointerBuffer requiredLayers = stack.mallocPointer(validationLayers.size()); - for (String layer : validationLayers) { - requiredLayers.put(stack.UTF8(layer)); - } - requiredLayers.flip(); - - return requiredLayers; - } - - /** - * This class is used to select the validation layers and create an InstanceValidationLayers object. - * - *
Specification
- *
    - *
  • Include the validation layers if the user wants them
  • - *
  • Select the validation layers from the supported validation layers
  • - *
  • Return an InstanceValidationLayers object with the selected validation layers
  • - *
- * - *
See Also
- *
    - *
  • {@link #includeValidationLayers(boolean)}
  • - *
  • {@link #selectValidationLayers()}
  • - *
- */ - public static class Selector { - private boolean includeValidationLayers = false; - - public Selector includeValidationLayers(boolean includeValidationLayers) { - this.includeValidationLayers = includeValidationLayers; - return this; - } - - /** - * Selects the validation layers. - * - * @return the set of validation layers - */ - public InstanceValidationLayers selectValidationLayers() { - var supportedLayers = getSupportedValidationLayers(); - log("Supported validation layers", supportedLayers); - - Set layersToUse = includeValidationLayers - ? selectValidationLayersFromSupportedLayers(supportedLayers) - : Collections.emptySet(); - log("Selected validation layers", layersToUse); - - if (layersToUse.isEmpty() && includeValidationLayers) { - Logger.warn("Request validation but no supported validation layers found. Falling back to no validation"); - } - - return new InstanceValidationLayers(layersToUse); - } - - private Set getSupportedValidationLayers() { - Set supportedLayers = new HashSet<>(); - - try (MemoryStack stack = MemoryStack.stackPush()) { - // Get the number of supported validation layers by the current vulkan instance - IntBuffer numLayersArr = stack.callocInt(1); - VK10.vkEnumerateInstanceLayerProperties(numLayersArr, null); - int numLayers = numLayersArr.get(0); - - // Get the supported validation layers by the current vulkan instance - VkLayerProperties.Buffer propsBuf = VkLayerProperties.calloc(numLayers, stack); - VK10.vkEnumerateInstanceLayerProperties(numLayersArr, propsBuf); - - // Convert the supported validation layers to a list of strings - for (int i = 0; i < numLayers; i++) { - VkLayerProperties props = propsBuf.get(i); - String layerName = props.layerNameString(); - supportedLayers.add(layerName); - } - } - - return supportedLayers; - } - - private Set selectValidationLayersFromSupportedLayers(Set supportedLayers) { - Set layersToUse = new HashSet<>(); - - // Main validation layer - if (supportedLayers.contains("VK_LAYER_KHRONOS_validation")) { - layersToUse.add("VK_LAYER_KHRONOS_validation"); - return layersToUse; - } - - // Fallback 1 - if (supportedLayers.contains("VK_LAYER_LUNARG_standard_validation")) { - layersToUse.add("VK_LAYER_LUNARG_standard_validation"); - return layersToUse; - } - - // Fallback 2 (set) - Set requestedLayers = new HashSet<>(); - requestedLayers.add("VK_LAYER_GOOGLE_threading"); - requestedLayers.add("VK_LAYER_LUNARG_parameter_validation"); - requestedLayers.add("VK_LAYER_LUNARG_object_tracker"); - requestedLayers.add("VK_LAYER_LUNARG_core_validation"); - requestedLayers.add("VK_LAYER_GOOGLE_unique_objects"); - - return requestedLayers.stream().filter(supportedLayers::contains).collect(Collectors.toSet()); - } - - private void log(String title, Set layers) { - Logger.debug("{} ({})", title, layers.size()); - for (String layer : layers) { - Logger.debug(" - {}", layer); - } - } - } -} \ No newline at end of file