Refactor SuitablePhysicalDeviceFinder

This commit is contained in:
Florian RICHER 2025-05-22 22:19:43 +02:00
parent 224b41f31f
commit 6e0b69947c
Signed by: florian.richer
GPG key ID: C73D37CBED7BFC77
16 changed files with 468 additions and 180 deletions

View file

@ -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");

View file

@ -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");
}

View file

@ -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<Integer> getQueueFlag() {
return Optional.of(VK10.VK_QUEUE_COMPUTE_BIT);
}
}

View file

@ -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<Integer> getQueueFlag() {
return Optional.of(VK10.VK_QUEUE_GRAPHICS_BIT);
}
}

View file

@ -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<Integer> getQueueFlag();
}

View file

@ -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<Integer> getQueueFlag() {
return Optional.empty();
}
}

View file

@ -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<Integer> getQueueFlag() {
return Optional.of(VK10.VK_QUEUE_TRANSFER_BIT);
}
}

View file

@ -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<String> requiredExtensions;
public DeviceExtensionsValidator(Set<String> requiredExtensions) {
this.requiredExtensions = requiredExtensions;
}
/**
* Validate the device extensions of the physical device.
*
* <i>Note: This method return a Result to adapt the extensions to the physical device properties after the validation.</i>
*
* @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<String> matchingExtensions = new HashSet<>();
Set<String> 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<String> matchingExtensions;
public final Set<String> missingExtensions;
public final boolean isSuitable;
public Result(Set<String> matchingExtensions, Set<String> missingExtensions, boolean isSuitable) {
this.matchingExtensions = matchingExtensions;
this.missingExtensions = missingExtensions;
this.isSuitable = isSuitable;
}
}
}

View file

@ -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<String> requiredExtensions;
private final Optional<Surface> surface;
public PhysicalDeviceCompatibilityValidator(int requiredQueueFlags, Set<String> requiredExtensions, Optional<Surface> 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<IQueueFamilyRequirement>();
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;
}
}
}

View file

@ -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<IQueueFamilyRequirement> requirements;
public QueueFamilyRequirementsValidator(List<IQueueFamilyRequirement> requirements) {
this.requirements = requirements;
}
/**
* Validate the queue family requirements of the physical device.
*
* <i>Note: This method return a Result to adapt the requirements to the physical device properties after the validation.</i>
*
* @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<IQueueFamilyRequirement, Optional<Integer>> 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<IQueueFamilyRequirement, Optional<Integer>> queueFamilyIndices;
public final boolean isSuitable;
public Result(HashMap<IQueueFamilyRequirement, Optional<Integer>> queueFamilyIndices, boolean isSuitable) {
this.queueFamilyIndices = queueFamilyIndices;
this.isSuitable = isSuitable;
}
}
}

View file

@ -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();
}
}

View file

@ -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();
}
}

View file

@ -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";
};
}
}

View file

@ -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();
}
}

View file

@ -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<String> 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());
}
}

View file

@ -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<MatchResult> matchedPhysicalDevices = new ArrayList<>();
try (MemoryStack stack = MemoryStack.stackPush()) {
List<PhysicalDevice> 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<String> 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<String> extensions) {
this.extensions = extensions;
return this;
this.validatorResult = validatorResult;
}
}
}