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");
|
"Failed to create device");
|
||||||
vkDevice = new VkDevice(pp.get(0), physicalDevice.getVkPhysicalDevice(), deviceCreateInfo);
|
vkDevice = new VkDevice(pp.get(0), physicalDevice.getVkPhysicalDevice(), deviceCreateInfo);
|
||||||
|
|
||||||
graphicsQueue = new Queue.GraphicsQueue(this, physicalDeviceMatch.graphicsQueueFamilyIndex, 0);
|
// graphicsQueue = new Queue.GraphicsQueue(this, physicalDeviceMatch.graphicsQueueFamilyIndex, 0);
|
||||||
computeQueue = new Queue.ComputeQueue(this, physicalDeviceMatch.computeQueueFamilyIndex, 1);
|
// computeQueue = new Queue.ComputeQueue(this, physicalDeviceMatch.computeQueueFamilyIndex, 1);
|
||||||
transferQueue = new Queue.TransferQueue(this, physicalDeviceMatch.transferQueueFamilyIndex, 2);
|
// transferQueue = new Queue.TransferQueue(this, physicalDeviceMatch.transferQueueFamilyIndex, 2);
|
||||||
|
graphicsQueue = null;
|
||||||
|
computeQueue = null;
|
||||||
|
transferQueue = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger.debug("Vulkan device created");
|
Logger.debug("Vulkan device created");
|
||||||
|
|
|
@ -1,11 +1,15 @@
|
||||||
package fr.mrdev023.vulkan_java.vk;
|
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.vk.utils.SuitablePhysicalDeviceFinder;
|
||||||
import fr.mrdev023.vulkan_java.window.Display;
|
import fr.mrdev023.vulkan_java.window.Display;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.lwjgl.vulkan.KHRDynamicRendering;
|
import org.lwjgl.vulkan.KHRDynamicRendering;
|
||||||
|
import org.lwjgl.vulkan.KHRSwapchain;
|
||||||
|
import org.lwjgl.vulkan.VK10;
|
||||||
|
|
||||||
import static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported;
|
import static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported;
|
||||||
|
|
||||||
|
@ -24,14 +28,13 @@ public class Vulkan {
|
||||||
instance = new Instance(true);
|
instance = new Instance(true);
|
||||||
surface = new Surface(instance, Display.getWindow());
|
surface = new Surface(instance, Display.getWindow());
|
||||||
|
|
||||||
var criteria = new SuitablePhysicalDeviceFinder.Criteria()
|
var physicalDeviceCompatibilityValidator = new PhysicalDeviceCompatibilityValidator(
|
||||||
.withGraphicsQueue(true)
|
VK10.VK_QUEUE_GRAPHICS_BIT | VK10.VK_QUEUE_COMPUTE_BIT | VK10.VK_QUEUE_TRANSFER_BIT,
|
||||||
.withComputeQueue(true)
|
Set.of(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME, KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME),
|
||||||
.withTransferQueue(true)
|
Optional.of(surface)
|
||||||
.withSurfaceSupport(surface)
|
);
|
||||||
.withExtensions(Set.of(KHRDynamicRendering.VK_KHR_DYNAMIC_RENDERING_EXTENSION_NAME));
|
|
||||||
|
|
||||||
var physicalDeviceMatch = SuitablePhysicalDeviceFinder.findBestPhysicalDevice(instance, criteria);
|
var physicalDeviceMatch = SuitablePhysicalDeviceFinder.findBestPhysicalDevice(instance, physicalDeviceCompatibilityValidator);
|
||||||
if (physicalDeviceMatch == null) {
|
if (physicalDeviceMatch == null) {
|
||||||
throw new RuntimeException("No suitable physical device found");
|
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,163 +1,39 @@
|
||||||
package fr.mrdev023.vulkan_java.vk.utils;
|
package fr.mrdev023.vulkan_java.vk.utils;
|
||||||
|
|
||||||
import java.nio.IntBuffer;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
import org.lwjgl.system.MemoryStack;
|
import org.lwjgl.system.MemoryStack;
|
||||||
import org.lwjgl.vulkan.KHRSurface;
|
|
||||||
import org.lwjgl.vulkan.VK10;
|
import org.lwjgl.vulkan.VK10;
|
||||||
import org.tinylog.Logger;
|
|
||||||
|
|
||||||
import fr.mrdev023.vulkan_java.vk.Instance;
|
import fr.mrdev023.vulkan_java.vk.Instance;
|
||||||
import fr.mrdev023.vulkan_java.vk.PhysicalDevice;
|
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.VulkanError;
|
||||||
|
import fr.mrdev023.vulkan_java.vk.compatibilities.validators.PhysicalDeviceCompatibilityValidator;
|
||||||
|
import fr.mrdev023.vulkan_java.vk.loggers.PhysicalDeviceCompatibilityLogger;
|
||||||
|
|
||||||
public class SuitablePhysicalDeviceFinder {
|
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<>();
|
List<MatchResult> matchedPhysicalDevices = new ArrayList<>();
|
||||||
|
|
||||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||||
List<PhysicalDevice> physicalDevices = PhysicalDevice.getPhysicalDevices(instance, stack);
|
List<PhysicalDevice> physicalDevices = PhysicalDevice.getPhysicalDevices(instance, stack);
|
||||||
for (PhysicalDevice physicalDevice : physicalDevices) {
|
for (PhysicalDevice physicalDevice : physicalDevices) {
|
||||||
var matchResult = checkPhysicalDevice(stack, physicalDevice, criteria);
|
var matchResult = validator.validate(physicalDevice);
|
||||||
if (matchResult.isSuitable(criteria)) {
|
if (matchResult.isSuitable) {
|
||||||
matchedPhysicalDevices.add(matchResult);
|
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class MatchResult {
|
private static int getScore(MatchResult matchResult) {
|
||||||
public final PhysicalDevice physicalDevice;
|
int deviceType = matchResult.physicalDevice.getVkPhysicalDeviceProperties().deviceType();
|
||||||
public final boolean surfaceSupport;
|
|
||||||
public final int graphicsQueueFamilyIndex;
|
|
||||||
public final int computeQueueFamilyIndex;
|
|
||||||
public final int transferQueueFamilyIndex;
|
|
||||||
|
|
||||||
public MatchResult(PhysicalDevice physicalDevice, int graphicsQueueFamilyIndex, int computeQueueFamilyIndex,
|
|
||||||
int transferQueueFamilyIndex, boolean surfaceSupport) {
|
|
||||||
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) {
|
return switch (deviceType) {
|
||||||
case VK10.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU -> 0;
|
case VK10.VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU -> 0;
|
||||||
|
@ -168,38 +44,14 @@ public class SuitablePhysicalDeviceFinder {
|
||||||
default -> 5;
|
default -> 5;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public static class Criteria {
|
public static class MatchResult {
|
||||||
private boolean withGraphicsQueue = false;
|
public final PhysicalDevice physicalDevice;
|
||||||
private boolean withComputeQueue = false;
|
public final PhysicalDeviceCompatibilityValidator.Result validatorResult;
|
||||||
private boolean withTransferQueue = false;
|
|
||||||
private Surface withSurfaceSupport = null;
|
|
||||||
private Set<String> extensions = new HashSet<>();
|
|
||||||
|
|
||||||
public Criteria withGraphicsQueue(boolean withGraphicsQueue) {
|
public MatchResult(PhysicalDevice physicalDevice, PhysicalDeviceCompatibilityValidator.Result validatorResult) {
|
||||||
this.withGraphicsQueue = withGraphicsQueue;
|
this.physicalDevice = physicalDevice;
|
||||||
return this;
|
this.validatorResult = validatorResult;
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue