From 6e0b69947cbec5f318869a7fb83769ff070ebcaf Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Thu, 22 May 2025 22:19:43 +0200 Subject: [PATCH] Refactor SuitablePhysicalDeviceFinder --- .../fr/mrdev023/vulkan_java/vk/Device.java | 9 +- .../fr/mrdev023/vulkan_java/vk/Vulkan.java | 17 +- .../requirements/ComputeQueueRequirement.java | 26 +++ .../GraphicsQueueRequirement.java | 26 +++ .../requirements/IQueueFamilyRequirement.java | 13 ++ .../SurfacePresentQueueRequirement.java | 40 ++++ .../TransferQueueRequirement.java | 26 +++ .../validators/DeviceExtensionsValidator.java | 52 +++++ .../PhysicalDeviceCompatibilityValidator.java | 77 +++++++ .../QueueFamilyRequirementsValidator.java | 66 ++++++ .../loggers/DeviceExtensionsResultLogger.java | 12 ++ .../PhysicalDeviceCompatibilityLogger.java | 14 ++ .../PhysicalDevicePropertiesLogger.java | 33 +++ .../QueueFamilyRequirementResultLogger.java | 17 ++ .../vulkan_java/vk/loggers/ScopedLogger.java | 28 +++ .../utils/SuitablePhysicalDeviceFinder.java | 192 ++---------------- 16 files changed, 468 insertions(+), 180 deletions(-) create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/ComputeQueueRequirement.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/GraphicsQueueRequirement.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/IQueueFamilyRequirement.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/SurfacePresentQueueRequirement.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/TransferQueueRequirement.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/DeviceExtensionsValidator.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/PhysicalDeviceCompatibilityValidator.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/QueueFamilyRequirementsValidator.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/loggers/DeviceExtensionsResultLogger.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDeviceCompatibilityLogger.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDevicePropertiesLogger.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/loggers/QueueFamilyRequirementResultLogger.java create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/loggers/ScopedLogger.java 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 c21400c..2bf5634 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Device.java @@ -66,9 +66,12 @@ public class Device { "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); + // graphicsQueue = new Queue.GraphicsQueue(this, physicalDeviceMatch.graphicsQueueFamilyIndex, 0); + // computeQueue = new Queue.ComputeQueue(this, physicalDeviceMatch.computeQueueFamilyIndex, 1); + // transferQueue = new Queue.TransferQueue(this, physicalDeviceMatch.transferQueueFamilyIndex, 2); + graphicsQueue = null; + computeQueue = null; + transferQueue = null; } Logger.debug("Vulkan device created"); 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 a055ec6..a4fe30b 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Vulkan.java @@ -1,11 +1,15 @@ package fr.mrdev023.vulkan_java.vk; +import fr.mrdev023.vulkan_java.vk.compatibilities.validators.PhysicalDeviceCompatibilityValidator; import fr.mrdev023.vulkan_java.vk.utils.SuitablePhysicalDeviceFinder; import fr.mrdev023.vulkan_java.window.Display; +import java.util.Optional; import java.util.Set; import org.lwjgl.vulkan.KHRDynamicRendering; +import org.lwjgl.vulkan.KHRSwapchain; +import org.lwjgl.vulkan.VK10; import static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported; @@ -24,14 +28,13 @@ public class Vulkan { instance = new Instance(true); 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)); + var physicalDeviceCompatibilityValidator = new PhysicalDeviceCompatibilityValidator( + VK10.VK_QUEUE_GRAPHICS_BIT | VK10.VK_QUEUE_COMPUTE_BIT | VK10.VK_QUEUE_TRANSFER_BIT, + Set.of(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME), + Optional.of(surface) + ); - var physicalDeviceMatch = SuitablePhysicalDeviceFinder.findBestPhysicalDevice(instance, criteria); + var physicalDeviceMatch = SuitablePhysicalDeviceFinder.findBestPhysicalDevice(instance, physicalDeviceCompatibilityValidator); if (physicalDeviceMatch == null) { throw new RuntimeException("No suitable physical device found"); } diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/ComputeQueueRequirement.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/ComputeQueueRequirement.java new file mode 100644 index 0000000..de1f05b --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/ComputeQueueRequirement.java @@ -0,0 +1,26 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.requirements; + +import java.util.Optional; + +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; + +public class ComputeQueueRequirement implements IQueueFamilyRequirement { + + @Override + public boolean isFamilySuitable(PhysicalDevice physicalDevice, VkQueueFamilyProperties queueFamilyProperties, int queueFamilyIndex) { + return (queueFamilyProperties.queueFlags() & VK10.VK_QUEUE_COMPUTE_BIT) != 0; + } + + @Override + public String getName() { + return "Compute Queue"; + } + + @Override + public Optional getQueueFlag() { + return Optional.of(VK10.VK_QUEUE_COMPUTE_BIT); + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/GraphicsQueueRequirement.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/GraphicsQueueRequirement.java new file mode 100644 index 0000000..f1505f0 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/GraphicsQueueRequirement.java @@ -0,0 +1,26 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.requirements; + +import java.util.Optional; + +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; + +public class GraphicsQueueRequirement implements IQueueFamilyRequirement { + + @Override + public boolean isFamilySuitable(PhysicalDevice physicalDevice, VkQueueFamilyProperties queueFamilyProperties, int queueFamilyIndex) { + return (queueFamilyProperties.queueFlags() & VK10.VK_QUEUE_GRAPHICS_BIT) != 0; + } + + @Override + public String getName() { + return "Graphics Queue"; + } + + @Override + public Optional getQueueFlag() { + return Optional.of(VK10.VK_QUEUE_GRAPHICS_BIT); + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/IQueueFamilyRequirement.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/IQueueFamilyRequirement.java new file mode 100644 index 0000000..6b5ee7a --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/IQueueFamilyRequirement.java @@ -0,0 +1,13 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.requirements; + +import java.util.Optional; + +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; + +public interface IQueueFamilyRequirement { + boolean isFamilySuitable(PhysicalDevice physicalDevice, VkQueueFamilyProperties queueFamilyProperties, int queueFamilyIndex); + String getName(); + Optional getQueueFlag(); +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/SurfacePresentQueueRequirement.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/SurfacePresentQueueRequirement.java new file mode 100644 index 0000000..ea05999 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/SurfacePresentQueueRequirement.java @@ -0,0 +1,40 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.requirements; + +import java.nio.IntBuffer; +import java.util.Optional; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.KHRSurface; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; +import fr.mrdev023.vulkan_java.vk.Surface; + +public class SurfacePresentQueueRequirement implements IQueueFamilyRequirement { + + private final Surface surface; + + public SurfacePresentQueueRequirement(Surface surface) { + this.surface = surface; + } + + @Override + public boolean isFamilySuitable(PhysicalDevice physicalDevice, VkQueueFamilyProperties queueFamilyProperties, int queueFamilyIndex) { + try (MemoryStack stack = MemoryStack.stackPush()) { + IntBuffer presentSupport = stack.ints(VK10.VK_FALSE); + KHRSurface.vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.getVkPhysicalDevice(), queueFamilyIndex, surface.getVkSurface(), presentSupport); + return presentSupport.get(0) == VK10.VK_TRUE; + } + } + + @Override + public String getName() { + return "Surface Present Queue"; + } + + @Override + public Optional getQueueFlag() { + return Optional.empty(); + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/TransferQueueRequirement.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/TransferQueueRequirement.java new file mode 100644 index 0000000..b318efc --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/requirements/TransferQueueRequirement.java @@ -0,0 +1,26 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.requirements; + +import java.util.Optional; + +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkQueueFamilyProperties; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; + +public class TransferQueueRequirement implements IQueueFamilyRequirement { + + @Override + public boolean isFamilySuitable(PhysicalDevice physicalDevice, VkQueueFamilyProperties queueFamilyProperties, int queueFamilyIndex) { + return (queueFamilyProperties.queueFlags() & VK10.VK_QUEUE_TRANSFER_BIT) != 0; + } + + @Override + public String getName() { + return "Transfer Queue"; + } + + @Override + public Optional getQueueFlag() { + return Optional.of(VK10.VK_QUEUE_TRANSFER_BIT); + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/DeviceExtensionsValidator.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/DeviceExtensionsValidator.java new file mode 100644 index 0000000..fd91b12 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/DeviceExtensionsValidator.java @@ -0,0 +1,52 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.validators; + +import java.util.HashSet; +import java.util.Set; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; + +public class DeviceExtensionsValidator { + private final Set requiredExtensions; + + public DeviceExtensionsValidator(Set requiredExtensions) { + this.requiredExtensions = requiredExtensions; + } + + /** + * Validate the device extensions of the physical device. + * + * Note: This method return a Result to adapt the extensions to the physical device properties after the validation. + * + * @param physicalDevice The physical device to validate the extensions for. + * @return A result object containing the validation results and whether the physical device is suitable. + */ + public Result validate(PhysicalDevice physicalDevice) { + Set matchingExtensions = new HashSet<>(); + Set missingExtensions = new HashSet<>(); + + var vkDeviceExtensions = physicalDevice.getVkDeviceExtensions(); + + for (var extension : requiredExtensions) { + var hasExtension = vkDeviceExtensions.stream().anyMatch(e -> e.extensionNameString().equals(extension)); + if (hasExtension) { + matchingExtensions.add(extension); + } else { + missingExtensions.add(extension); + } + } + + return new Result(matchingExtensions, missingExtensions, missingExtensions.isEmpty()); + } + + public static class Result { + public final Set matchingExtensions; + public final Set missingExtensions; + public final boolean isSuitable; + + public Result(Set matchingExtensions, Set missingExtensions, boolean isSuitable) { + this.matchingExtensions = matchingExtensions; + this.missingExtensions = missingExtensions; + this.isSuitable = isSuitable; + } + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/PhysicalDeviceCompatibilityValidator.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/PhysicalDeviceCompatibilityValidator.java new file mode 100644 index 0000000..e16c2b8 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/PhysicalDeviceCompatibilityValidator.java @@ -0,0 +1,77 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.validators; + +import java.util.ArrayList; +import java.util.Optional; +import java.util.Set; + +import org.lwjgl.vulkan.VK10; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; +import fr.mrdev023.vulkan_java.vk.Surface; +import fr.mrdev023.vulkan_java.vk.compatibilities.requirements.ComputeQueueRequirement; +import fr.mrdev023.vulkan_java.vk.compatibilities.requirements.GraphicsQueueRequirement; +import fr.mrdev023.vulkan_java.vk.compatibilities.requirements.IQueueFamilyRequirement; +import fr.mrdev023.vulkan_java.vk.compatibilities.requirements.SurfacePresentQueueRequirement; +import fr.mrdev023.vulkan_java.vk.compatibilities.requirements.TransferQueueRequirement; + +public class PhysicalDeviceCompatibilityValidator { + private int requiredQueueFlags; + private Set requiredExtensions; + private final Optional surface; + + public PhysicalDeviceCompatibilityValidator(int requiredQueueFlags, Set requiredExtensions, Optional surface) { + this.requiredQueueFlags = requiredQueueFlags; + this.requiredExtensions = requiredExtensions; + this.surface = surface; + } + + public Result validate(PhysicalDevice physicalDevice) { + var queueFamilyRequirementsResult = validateQueueFamilyRequirements(physicalDevice); + var deviceExtensionsResult = validateDeviceExtensions(physicalDevice); + + return new Result(queueFamilyRequirementsResult, deviceExtensionsResult); + } + + private QueueFamilyRequirementsValidator.Result validateQueueFamilyRequirements(PhysicalDevice physicalDevice) { + var queueFamilyRequirements = new ArrayList(); + + if ((requiredQueueFlags & VK10.VK_QUEUE_GRAPHICS_BIT) != 0) { + queueFamilyRequirements.add(new GraphicsQueueRequirement()); + } + + if ((requiredQueueFlags & VK10.VK_QUEUE_COMPUTE_BIT) != 0) { + queueFamilyRequirements.add(new ComputeQueueRequirement()); + } + + if ((requiredQueueFlags & VK10.VK_QUEUE_TRANSFER_BIT) != 0) { + queueFamilyRequirements.add(new TransferQueueRequirement()); + } + + if (surface.isPresent()) { + queueFamilyRequirements.add(new SurfacePresentQueueRequirement(surface.get())); + } + + var queueFamilyRequirementsValidator = new QueueFamilyRequirementsValidator(queueFamilyRequirements); + var result = queueFamilyRequirementsValidator.validate(physicalDevice); + + return result; + } + + private DeviceExtensionsValidator.Result validateDeviceExtensions(PhysicalDevice physicalDevice) { + var deviceExtensionsValidator = new DeviceExtensionsValidator(requiredExtensions); + var result = deviceExtensionsValidator.validate(physicalDevice); + return result; + } + + public static class Result { + public final boolean isSuitable; + public final QueueFamilyRequirementsValidator.Result queueFamilyRequirementsResult; + public final DeviceExtensionsValidator.Result deviceExtensionsResult; + + public Result(QueueFamilyRequirementsValidator.Result queueFamilyRequirementsResult, DeviceExtensionsValidator.Result deviceExtensionsResult) { + this.isSuitable = queueFamilyRequirementsResult.isSuitable && deviceExtensionsResult.isSuitable; + this.queueFamilyRequirementsResult = queueFamilyRequirementsResult; + this.deviceExtensionsResult = deviceExtensionsResult; + } + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/QueueFamilyRequirementsValidator.java b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/QueueFamilyRequirementsValidator.java new file mode 100644 index 0000000..ffd77ae --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/compatibilities/validators/QueueFamilyRequirementsValidator.java @@ -0,0 +1,66 @@ +package fr.mrdev023.vulkan_java.vk.compatibilities.validators; + +import java.util.HashMap; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; +import fr.mrdev023.vulkan_java.vk.compatibilities.requirements.IQueueFamilyRequirement; + +public class QueueFamilyRequirementsValidator { + private List requirements; + + public QueueFamilyRequirementsValidator(List requirements) { + this.requirements = requirements; + } + + /** + * Validate the queue family requirements of the physical device. + * + * Note: This method return a Result to adapt the requirements to the physical device properties after the validation. + * + * @param physicalDevice The physical device to validate the requirements for. + * @return A result object containing the validation results and whether the physical device is suitable. + */ + public Result validate(PhysicalDevice physicalDevice) { + var vkQueueFamilyProps = physicalDevice.getVkQueueFamilyProps(); + + HashMap> result = requirements.stream() + .collect(Collectors.toMap( + requirement -> requirement, + requirement -> Optional.empty(), + (a, b) -> a, + HashMap::new + )); + + for (int i = 0; i < vkQueueFamilyProps.capacity(); i++) { + var vkQueueFamilyProp = vkQueueFamilyProps.get(i); + + for (var requirement : requirements) { + if (result.get(requirement).isPresent()) { + continue; + } + + + if (requirement.isFamilySuitable(physicalDevice, vkQueueFamilyProp, i)) { + result.put(requirement, Optional.of(i)); + } + } + } + + boolean isSuitable = result.values().stream().allMatch(Optional::isPresent); + + return new Result(result, isSuitable); + } + + public static class Result { + public final HashMap> queueFamilyIndices; + public final boolean isSuitable; + + public Result(HashMap> queueFamilyIndices, boolean isSuitable) { + this.queueFamilyIndices = queueFamilyIndices; + this.isSuitable = isSuitable; + } + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/DeviceExtensionsResultLogger.java b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/DeviceExtensionsResultLogger.java new file mode 100644 index 0000000..9a3fcec --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/DeviceExtensionsResultLogger.java @@ -0,0 +1,12 @@ +package fr.mrdev023.vulkan_java.vk.loggers; + +import fr.mrdev023.vulkan_java.vk.compatibilities.validators.DeviceExtensionsValidator; + +public class DeviceExtensionsResultLogger { + public static void log(DeviceExtensionsValidator.Result result) { + ScopedLogger.pushScope("Device Extensions (Is suitable: {})", result.isSuitable); + ScopedLogger.log("Matching extensions: {}", result.matchingExtensions); + ScopedLogger.log("Missing extensions: {}", result.missingExtensions); + ScopedLogger.popScope(); + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDeviceCompatibilityLogger.java b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDeviceCompatibilityLogger.java new file mode 100644 index 0000000..94bf9bf --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDeviceCompatibilityLogger.java @@ -0,0 +1,14 @@ +package fr.mrdev023.vulkan_java.vk.loggers; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; +import fr.mrdev023.vulkan_java.vk.compatibilities.validators.PhysicalDeviceCompatibilityValidator; + +public class PhysicalDeviceCompatibilityLogger { + public static void log(PhysicalDevice physicalDevice, PhysicalDeviceCompatibilityValidator.Result result) { + ScopedLogger.pushScope("Physical device support (Is suitable: {})", result.isSuitable); + PhysicalDevicePropertiesLogger.log(physicalDevice); + QueueFamilyRequirementResultLogger.log(result.queueFamilyRequirementsResult); + DeviceExtensionsResultLogger.log(result.deviceExtensionsResult); + ScopedLogger.popScope(); + } +} 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 new file mode 100644 index 0000000..3819665 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/PhysicalDevicePropertiesLogger.java @@ -0,0 +1,33 @@ +package fr.mrdev023.vulkan_java.vk.loggers; + +import org.lwjgl.vulkan.VK10; + +import fr.mrdev023.vulkan_java.vk.PhysicalDevice; + +public class PhysicalDevicePropertiesLogger { + public static void log(PhysicalDevice physicalDevice) { + var properties = physicalDevice.getVkPhysicalDeviceProperties(); + + 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("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)); + } + + private static String formatDeviceType(int deviceType) { + return switch (deviceType) { + case VK10.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU -> "Discrete GPU"; + case VK10.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU -> "Integrated GPU"; + case VK10.VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU -> "Virtual GPU"; + case VK10.VK_PHYSICAL_DEVICE_TYPE_CPU -> "CPU"; + case VK10.VK_PHYSICAL_DEVICE_TYPE_OTHER -> "Other"; + default -> "Unknown"; + }; + } +} diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/QueueFamilyRequirementResultLogger.java b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/QueueFamilyRequirementResultLogger.java new file mode 100644 index 0000000..7fd5080 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/QueueFamilyRequirementResultLogger.java @@ -0,0 +1,17 @@ +package fr.mrdev023.vulkan_java.vk.loggers; + +import fr.mrdev023.vulkan_java.vk.compatibilities.validators.QueueFamilyRequirementsValidator; + +public class QueueFamilyRequirementResultLogger { + public static void log(QueueFamilyRequirementsValidator.Result result) { + ScopedLogger.pushScope("Queue Family Requirements (Is suitable: {})", result.isSuitable); + result.queueFamilyIndices.forEach((requirement, index) -> { + if (index.isPresent()) { + ScopedLogger.log("{}: [SUPPORTED]", requirement.getName()); + } else { + ScopedLogger.log("{}: [UNSUPPORTED]", requirement.getName(), requirement.getQueueFlag().orElse(null)); + } + }); + ScopedLogger.popScope(); + } +} 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 new file mode 100644 index 0000000..b2e5053 --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/loggers/ScopedLogger.java @@ -0,0 +1,28 @@ +package fr.mrdev023.vulkan_java.vk.loggers; + +import java.util.ArrayDeque; +import java.util.Deque; +import java.util.stream.Collectors; + +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); + } + + public static void log(final String message, final Object... arguments) { + Logger.info(String.format("%s%s", getIndentation(), message), arguments); + } + + public static void popScope() { + scopes.pop(); + } + + private static String getIndentation() { + return scopes.stream().map(s -> " ").collect(Collectors.joining()); + } +} 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 de4c92b..d9830d7 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 @@ -1,205 +1,57 @@ package fr.mrdev023.vulkan_java.vk.utils; -import java.nio.IntBuffer; import java.util.ArrayList; import java.util.Comparator; -import java.util.HashSet; import java.util.List; -import java.util.Set; import org.lwjgl.system.MemoryStack; -import org.lwjgl.vulkan.KHRSurface; import org.lwjgl.vulkan.VK10; -import org.tinylog.Logger; import fr.mrdev023.vulkan_java.vk.Instance; import fr.mrdev023.vulkan_java.vk.PhysicalDevice; -import fr.mrdev023.vulkan_java.vk.Surface; import fr.mrdev023.vulkan_java.vk.VulkanError; +import fr.mrdev023.vulkan_java.vk.compatibilities.validators.PhysicalDeviceCompatibilityValidator; +import fr.mrdev023.vulkan_java.vk.loggers.PhysicalDeviceCompatibilityLogger; public class SuitablePhysicalDeviceFinder { - public static MatchResult findBestPhysicalDevice(Instance instance, Criteria criteria) throws VulkanError { + public static MatchResult findBestPhysicalDevice(Instance instance, PhysicalDeviceCompatibilityValidator validator) throws VulkanError { List matchedPhysicalDevices = new ArrayList<>(); try (MemoryStack stack = MemoryStack.stackPush()) { List physicalDevices = PhysicalDevice.getPhysicalDevices(instance, stack); for (PhysicalDevice physicalDevice : physicalDevices) { - var matchResult = checkPhysicalDevice(stack, physicalDevice, criteria); - if (matchResult.isSuitable(criteria)) { - matchedPhysicalDevices.add(matchResult); + var matchResult = validator.validate(physicalDevice); + if (matchResult.isSuitable) { + matchedPhysicalDevices.add(new MatchResult(physicalDevice, matchResult)); } + PhysicalDeviceCompatibilityLogger.log(physicalDevice, matchResult); } } - return matchedPhysicalDevices.stream().min(Comparator.comparingInt(MatchResult::getScore)).orElse(null); + return matchedPhysicalDevices.stream().min(Comparator.comparingInt(matchResult -> getScore(matchResult))).orElse(null); } - 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 (graphicsQueueFamilyIndex == -1 && (vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_GRAPHICS_BIT) != 0) { - graphicsQueueFamilyIndex = i; - } - - if (computeQueueFamilyIndex == -1 && (vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_COMPUTE_BIT) != 0) { - computeQueueFamilyIndex = i; - } - - if (transferQueueFamilyIndex == -1 && (vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_TRANSFER_BIT) != 0) { - transferQueueFamilyIndex = i; - } - - 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); + private static int getScore(MatchResult matchResult) { + int deviceType = matchResult.physicalDevice.getVkPhysicalDeviceProperties().deviceType(); + + return switch (deviceType) { + case VK10.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU -> 0; + case VK10.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU -> 1; + case VK10.VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU -> 2; + case VK10.VK_PHYSICAL_DEVICE_TYPE_CPU -> 3; + case VK10.VK_PHYSICAL_DEVICE_TYPE_OTHER -> 4; + default -> 5; + }; } public static class MatchResult { public final PhysicalDevice physicalDevice; - public final boolean surfaceSupport; - public final int graphicsQueueFamilyIndex; - public final int computeQueueFamilyIndex; - public final int transferQueueFamilyIndex; + public final PhysicalDeviceCompatibilityValidator.Result validatorResult; - public MatchResult(PhysicalDevice physicalDevice, int graphicsQueueFamilyIndex, int computeQueueFamilyIndex, - int transferQueueFamilyIndex, boolean surfaceSupport) { + public MatchResult(PhysicalDevice physicalDevice, PhysicalDeviceCompatibilityValidator.Result validatorResult) { this.physicalDevice = physicalDevice; - this.graphicsQueueFamilyIndex = graphicsQueueFamilyIndex; - this.computeQueueFamilyIndex = computeQueueFamilyIndex; - this.transferQueueFamilyIndex = transferQueueFamilyIndex; - this.surfaceSupport = surfaceSupport; - } - - public boolean isSuitable(Criteria criteria) { - Logger.debug("Checking physical device"); - 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\tDevice type: {}", physicalDevice.getVkPhysicalDeviceProperties().deviceType()); - Logger.debug("\tRequired supports checking report"); - - if (!criteria.withGraphicsQueue) { - Logger.debug("\t\t[SKIPPED] Graphics queue is not required"); - } else if (graphicsQueueFamilyIndex == -1) { - Logger.debug("\t\t[FAILED] Graphics queue is not supported"); - return false; - } else { - Logger.debug("\t\t[OK] Graphics queue is supported (family index: {})", graphicsQueueFamilyIndex); - } - - if (!criteria.withComputeQueue) { - Logger.debug("\t\t[SKIPPED] Compute queue is not required"); - } else if (computeQueueFamilyIndex == -1) { - Logger.debug("\t\t[FAILED] Compute queue is not supported"); - return false; - } else { - Logger.debug("\t\t[OK] Compute queue is supported (family index: {})", computeQueueFamilyIndex); - } - - if (!criteria.withTransferQueue) { - Logger.debug("\t\t[SKIPPED] Transfer queue is not required"); - } else if (transferQueueFamilyIndex == -1) { - Logger.debug("\t\t[FAILED] Transfer queue is not supported"); - return false; - } else { - Logger.debug("\t\t[OK] Transfer queue is supported (family index: {})", transferQueueFamilyIndex); - } - - if (criteria.withSurfaceSupport == null) { - Logger.debug("\t\t[SKIPPED] Surface support is not required"); - } else if (!surfaceSupport) { - Logger.debug("\t\t[FAILED] Surface support is not supported"); - return false; - } else { - Logger.debug("\t\t[OK] Surface support is supported"); - } - - 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)); - return false; - } else { - Logger.debug("\t\t[OK] Required extensions are supported [{}]", String.join(", ", criteria.extensions)); - } - - return true; - } - - public int getScore() { - int deviceType = physicalDevice.getVkPhysicalDeviceProperties().deviceType(); - - return switch (deviceType) { - case VK10.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU -> 0; - case VK10.VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU -> 1; - case VK10.VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU -> 2; - case VK10.VK_PHYSICAL_DEVICE_TYPE_CPU -> 3; - case VK10.VK_PHYSICAL_DEVICE_TYPE_OTHER -> 4; - default -> 5; - }; - } - } - - public static class Criteria { - private boolean withGraphicsQueue = false; - private boolean withComputeQueue = false; - private boolean withTransferQueue = false; - private Surface withSurfaceSupport = null; - private Set extensions = new HashSet<>(); - - public Criteria withGraphicsQueue(boolean withGraphicsQueue) { - this.withGraphicsQueue = withGraphicsQueue; - return this; - } - - public Criteria withSurfaceSupport(Surface withSurfaceSupport) { - this.withSurfaceSupport = withSurfaceSupport; - return this; - } - - public Criteria withComputeQueue(boolean withComputeQueue) { - this.withComputeQueue = withComputeQueue; - return this; - } - - public Criteria withTransferQueue(boolean withTransferQueue) { - this.withTransferQueue = withTransferQueue; - return this; - } - - public Criteria withExtensions(Set extensions) { - this.extensions = extensions; - return this; + this.validatorResult = validatorResult; } } }