From a9cf0cf4cfecdbefb762278d4e43576f416eb14d Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Wed, 21 May 2025 13:57:14 +0200 Subject: [PATCH] Fix Physical Device Queue selector --- .../fr/mrdev023/vulkan_java/vk/Device.java | 25 +++++++- .../fr/mrdev023/vulkan_java/vk/Queue.java | 31 ++++------ .../fr/mrdev023/vulkan_java/vk/Vulkan.java | 14 ++--- .../vk/utils/InstanceExtensions.java | 59 +++++++++++-------- .../utils/SuitablePhysicalDeviceFinder.java | 51 ++++++++++------ 5 files changed, 106 insertions(+), 74 deletions(-) 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 a527d0b..c21400c 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java @@ -5,6 +5,8 @@ import org.lwjgl.system.MemoryStack; import org.lwjgl.vulkan.*; import org.tinylog.Logger; +import fr.mrdev023.vulkan_java.vk.utils.SuitablePhysicalDeviceFinder; + import java.nio.*; import java.util.*; @@ -15,9 +17,12 @@ public class Device { private final PhysicalDevice physicalDevice; private final VkDevice vkDevice; + private final Queue.GraphicsQueue graphicsQueue; + private final Queue.ComputeQueue computeQueue; + private final Queue.TransferQueue transferQueue; - public Device(PhysicalDevice physicalDevice) throws VulkanError { - this.physicalDevice = physicalDevice; + public Device(SuitablePhysicalDeviceFinder.MatchResult physicalDeviceMatch) throws VulkanError { + this.physicalDevice = physicalDeviceMatch.physicalDevice; try (MemoryStack stack = MemoryStack.stackPush()) { // Define required extensions @@ -60,6 +65,10 @@ public class Device { vkCheck(vkCreateDevice(physicalDevice.getVkPhysicalDevice(), deviceCreateInfo, null, pp), "Failed to create device"); vkDevice = new VkDevice(pp.get(0), physicalDevice.getVkPhysicalDevice(), deviceCreateInfo); + + graphicsQueue = new Queue.GraphicsQueue(this, physicalDeviceMatch.graphicsQueueFamilyIndex, 0); + computeQueue = new Queue.ComputeQueue(this, physicalDeviceMatch.computeQueueFamilyIndex, 1); + transferQueue = new Queue.TransferQueue(this, physicalDeviceMatch.transferQueueFamilyIndex, 2); } Logger.debug("Vulkan device created"); @@ -111,4 +120,16 @@ public class Device { public void waitIdle() { vkDeviceWaitIdle(vkDevice); } + + public Queue.GraphicsQueue getGraphicsQueue() { + return graphicsQueue; + } + + public Queue.ComputeQueue getComputeQueue() { + return computeQueue; + } + + public Queue.TransferQueue getTransferQueue() { + return transferQueue; + } } \ No newline at end of file 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 3f61387..e56eabb 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Queue.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Queue.java @@ -31,29 +31,20 @@ public class Queue { } public static class GraphicsQueue extends Queue { - - public GraphicsQueue(Device device, int queueIndex) { - super(device, getGraphicsQueueFamilyIndex(device), queueIndex); + public GraphicsQueue(Device device, int queueFamilyIndex, int queueIndex) { + super(device, queueFamilyIndex, queueIndex); } + } - private static int getGraphicsQueueFamilyIndex(Device device) { - int index = -1; - PhysicalDevice physicalDevice = device.getPhysicalDevice(); - VkQueueFamilyProperties.Buffer queuePropsBuff = physicalDevice.getVkQueueFamilyProps(); - int numQueuesFamilies = queuePropsBuff.capacity(); - for (int i = 0; i < numQueuesFamilies; i++) { - VkQueueFamilyProperties props = queuePropsBuff.get(i); - boolean graphicsQueue = (props.queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0; - if (graphicsQueue) { - index = i; - break; - } - } + public static class ComputeQueue extends Queue { + public ComputeQueue(Device device, int queueFamilyIndex, int queueIndex) { + super(device, queueFamilyIndex, queueIndex); + } + } - if (index < 0) { - throw new RuntimeException("Failed to get graphics Queue family index"); - } - return index; + public static class TransferQueue extends Queue { + public TransferQueue(Device device, int queueFamilyIndex, int queueIndex) { + super(device, queueFamilyIndex, queueIndex); } } } \ No newline at end of file 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 15873b8..a055ec6 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java @@ -15,7 +15,6 @@ public class Vulkan { private static PhysicalDevice physicalDevice; private static Device device; private static Surface surface; - private static Queue.GraphicsQueue graphicsQueue; public static void init() throws VulkanError { if (!glfwVulkanSupported()) { @@ -26,11 +25,11 @@ public class Vulkan { surface = new Surface(instance, Display.getWindow()); var criteria = new SuitablePhysicalDeviceFinder.Criteria() - .withGraphicsQueue(true) - .withComputeQueue(true) - .withTransferQueue(true) - .withSurfaceSupport(surface) - .withExtensions(Set.of(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)); + .withGraphicsQueue(true) + .withComputeQueue(true) + .withTransferQueue(true) + .withSurfaceSupport(surface) + .withExtensions(Set.of(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME)); var physicalDeviceMatch = SuitablePhysicalDeviceFinder.findBestPhysicalDevice(instance, criteria); if (physicalDeviceMatch == null) { @@ -38,8 +37,7 @@ public class Vulkan { } physicalDevice = physicalDeviceMatch.physicalDevice; - device = new Device(physicalDevice); - graphicsQueue = new Queue.GraphicsQueue(device, 0); + device = new Device(physicalDeviceMatch); } public static void destroy() { 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 index 582526c..4430c26 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceExtensions.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceExtensions.java @@ -17,17 +17,16 @@ 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. + * 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 PointerBuffer glfwRequiredExtensions; - private InstanceExtensions(Set instanceExtensions, PointerBuffer glfwRequiredExtensions) { + private InstanceExtensions(Set instanceExtensions) { this.instanceExtensions = instanceExtensions; - this.glfwRequiredExtensions = glfwRequiredExtensions; } public boolean hasInstanceExtensions() { @@ -39,10 +38,9 @@ public class InstanceExtensions { } public PointerBuffer writeToStack(MemoryStack stack) { - int numExtensions = instanceExtensions.size() + glfwRequiredExtensions.remaining(); - PointerBuffer requiredExtensions = stack.mallocPointer(numExtensions); + int numExtensions = instanceExtensions.size(); - requiredExtensions.put(glfwRequiredExtensions); + PointerBuffer requiredExtensions = stack.mallocPointer(numExtensions); for (String extension : instanceExtensions) { requiredExtensions.put(stack.UTF8(extension)); } @@ -62,15 +60,15 @@ public class InstanceExtensions { * *
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
  • + *
  • 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)}
  • + *
  • {@link #selectInstanceExtensions()}
  • + *
  • {@link #withValidationLayers(InstanceValidationLayers)}
  • *
*/ public static class Selector { @@ -90,16 +88,11 @@ public class InstanceExtensions { Set instanceExtensions = getInstanceExtensions(); log("Supported instance extensions", instanceExtensions); - // GLFW Extension - PointerBuffer glfwExtensions = GLFWVulkan.glfwGetRequiredInstanceExtensions(); - if (glfwExtensions == null) { - throw new RuntimeException("Failed to find the GLFW platform surface extensions"); - } - + Set glfwExtensions = getGLFWRequiredExtensions(); Set portabilityExtensions = getPortabilityExtensions(instanceExtensions); - log("Portability extensions used", portabilityExtensions); - + Set selectedExtensions = new HashSet<>(); + selectedExtensions.addAll(glfwExtensions); selectedExtensions.addAll(portabilityExtensions); if (validationLayers != null && validationLayers.hasValidationLayers()) { @@ -107,7 +100,7 @@ public class InstanceExtensions { } log("Selected instance extensions", selectedExtensions); - return new InstanceExtensions(selectedExtensions, glfwExtensions); + return new InstanceExtensions(selectedExtensions); } private Set getInstanceExtensions() { @@ -117,8 +110,9 @@ public class InstanceExtensions { IntBuffer numExtensionsBuf = stack.callocInt(1); VK10.vkEnumerateInstanceExtensionProperties((String) null, numExtensionsBuf, null); int numExtensions = numExtensionsBuf.get(0); - - VkExtensionProperties.Buffer instanceExtensionsProps = VkExtensionProperties.calloc(numExtensions, stack); + + 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); @@ -135,8 +129,10 @@ public class InstanceExtensions { 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"); + 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); } @@ -144,6 +140,19 @@ public class InstanceExtensions { 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) { diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java index 4d25d67..de4c92b 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/SuitablePhysicalDeviceFinder.java @@ -35,37 +35,47 @@ public class SuitablePhysicalDeviceFinder { return matchedPhysicalDevices.stream().min(Comparator.comparingInt(MatchResult::getScore)).orElse(null); } - private static MatchResult checkPhysicalDevice(MemoryStack stack, PhysicalDevice physicalDevice, Criteria criteria) { + private static MatchResult checkPhysicalDevice(MemoryStack stack, PhysicalDevice physicalDevice, + Criteria criteria) { int graphicsQueueFamilyIndex = -1; int computeQueueFamilyIndex = -1; int transferQueueFamilyIndex = -1; boolean surfaceSupport = false; - IntBuffer presentSupport = stack.ints(VK10.VK_FALSE); var vkQueueFamilyProps = physicalDevice.getVkQueueFamilyProps(); for (int i = 0; i < vkQueueFamilyProps.capacity(); i++) { var vkQueueFamilyProp = vkQueueFamilyProps.get(i); - - if ((vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_GRAPHICS_BIT) != 0) { + + if (graphicsQueueFamilyIndex == -1 && (vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_GRAPHICS_BIT) != 0) { graphicsQueueFamilyIndex = i; } - if ((vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_COMPUTE_BIT) != 0) { + if (computeQueueFamilyIndex == -1 && (vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_COMPUTE_BIT) != 0) { computeQueueFamilyIndex = i; } - if ((vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_TRANSFER_BIT) != 0) { + if (transferQueueFamilyIndex == -1 && (vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_TRANSFER_BIT) != 0) { transferQueueFamilyIndex = i; } - KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.getVkPhysicalDevice(), i, criteria.withSurfaceSupport.getVkSurface(), presentSupport); + KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.getVkPhysicalDevice(), i, + criteria.withSurfaceSupport.getVkSurface(), presentSupport); if (presentSupport.get(0) == VK10.VK_TRUE) { surfaceSupport = true; } + + if ((!criteria.withGraphicsQueue || graphicsQueueFamilyIndex != -1) && + (!criteria.withComputeQueue || computeQueueFamilyIndex != -1) && + (!criteria.withTransferQueue || transferQueueFamilyIndex != -1) && + (criteria.withSurfaceSupport == null || surfaceSupport)) { + // We found a suitable queue family, we can break the loop + break; + } } - return new MatchResult(physicalDevice, graphicsQueueFamilyIndex, computeQueueFamilyIndex, transferQueueFamilyIndex, surfaceSupport); + return new MatchResult(physicalDevice, graphicsQueueFamilyIndex, computeQueueFamilyIndex, + transferQueueFamilyIndex, surfaceSupport); } public static class MatchResult { @@ -75,7 +85,8 @@ public class SuitablePhysicalDeviceFinder { public final int computeQueueFamilyIndex; public final int transferQueueFamilyIndex; - public MatchResult(PhysicalDevice physicalDevice, int graphicsQueueFamilyIndex, int computeQueueFamilyIndex, int transferQueueFamilyIndex, boolean surfaceSupport) { + public MatchResult(PhysicalDevice physicalDevice, int graphicsQueueFamilyIndex, int computeQueueFamilyIndex, + int transferQueueFamilyIndex, boolean surfaceSupport) { this.physicalDevice = physicalDevice; this.graphicsQueueFamilyIndex = graphicsQueueFamilyIndex; this.computeQueueFamilyIndex = computeQueueFamilyIndex; @@ -88,10 +99,10 @@ public class SuitablePhysicalDeviceFinder { Logger.debug("\tProperties"); Logger.debug("\t\tName: {}", physicalDevice.getVkPhysicalDeviceProperties().deviceNameString()); int apiVersion = physicalDevice.getVkPhysicalDeviceProperties().apiVersion(); - Logger.debug("\t\tAPI version: {}.{}.{}", - VK10.VK_API_VERSION_MAJOR(apiVersion), - VK10.VK_API_VERSION_MINOR(apiVersion), - VK10.VK_API_VERSION_PATCH(apiVersion)); + Logger.debug("\t\tAPI version: {}.{}.{}", + VK10.VK_API_VERSION_MAJOR(apiVersion), + VK10.VK_API_VERSION_MINOR(apiVersion), + VK10.VK_API_VERSION_PATCH(apiVersion)); Logger.debug("\t\tDevice type: {}", physicalDevice.getVkPhysicalDeviceProperties().deviceType()); Logger.debug("\tRequired supports checking report"); @@ -101,7 +112,7 @@ public class SuitablePhysicalDeviceFinder { Logger.debug("\t\t[FAILED] Graphics queue is not supported"); return false; } else { - Logger.debug("\t\t[OK] Graphics queue is supported"); + Logger.debug("\t\t[OK] Graphics queue is supported (family index: {})", graphicsQueueFamilyIndex); } if (!criteria.withComputeQueue) { @@ -110,7 +121,7 @@ public class SuitablePhysicalDeviceFinder { Logger.debug("\t\t[FAILED] Compute queue is not supported"); return false; } else { - Logger.debug("\t\t[OK] Compute queue is supported"); + Logger.debug("\t\t[OK] Compute queue is supported (family index: {})", computeQueueFamilyIndex); } if (!criteria.withTransferQueue) { @@ -119,7 +130,7 @@ public class SuitablePhysicalDeviceFinder { Logger.debug("\t\t[FAILED] Transfer queue is not supported"); return false; } else { - Logger.debug("\t\t[OK] Transfer queue is supported"); + Logger.debug("\t\t[OK] Transfer queue is supported (family index: {})", transferQueueFamilyIndex); } if (criteria.withSurfaceSupport == null) { @@ -133,13 +144,15 @@ public class SuitablePhysicalDeviceFinder { if (criteria.extensions == null) { Logger.debug("\t\t[SKIPPED] Required extensions is empty"); - } else if (physicalDevice.getVkDeviceExtensions().stream().allMatch(extension -> criteria.extensions.contains(extension.extensionNameString()))) { - Logger.debug("\t\t[FAILED] Required extensions are not supported [{}]", String.join(", ", criteria.extensions)); + } else if (physicalDevice.getVkDeviceExtensions().stream() + .allMatch(extension -> criteria.extensions.contains(extension.extensionNameString()))) { + Logger.debug("\t\t[FAILED] Required extensions are not supported [{}]", + String.join(", ", criteria.extensions)); return false; } else { Logger.debug("\t\t[OK] Required extensions are supported [{}]", String.join(", ", criteria.extensions)); } - + return true; }