Refactor SuitablePhysicalDeviceFinder
This commit is contained in:
parent
224b41f31f
commit
6e0b69947c
16 changed files with 468 additions and 180 deletions
|
@ -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");
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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";
|
||||
};
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue