Add SuitablePhysicalDeviceFinder
This commit is contained in:
parent
9ecf98a26d
commit
422b2825ad
5 changed files with 228 additions and 89 deletions
|
@ -35,9 +35,7 @@ public class Instance {
|
|||
.sType(VK_STRUCTURE_TYPE_APPLICATION_INFO)
|
||||
.pApplicationName(appShortName)
|
||||
.applicationVersion(1)
|
||||
.pEngineName(appShortName)
|
||||
.engineVersion(0)
|
||||
.apiVersion(VK_API_VERSION_1_1);
|
||||
.pEngineName(appShortName);
|
||||
|
||||
InstanceValidationLayers validationLayers = new InstanceValidationLayers.Selector()
|
||||
.includeValidationLayers(validate)
|
||||
|
|
|
@ -52,58 +52,7 @@ public class PhysicalDevice {
|
|||
}
|
||||
}
|
||||
|
||||
public static PhysicalDevice createPhysicalDevice(Instance instance, Optional<String> preferredDeviceName)
|
||||
throws VulkanError {
|
||||
Logger.debug("Selecting physical devices");
|
||||
PhysicalDevice selectedPhysicalDevice = null;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
// Get available devices
|
||||
PointerBuffer pPhysicalDevices = getPhysicalDevices(instance, stack);
|
||||
int numDevices = pPhysicalDevices.capacity();
|
||||
if (numDevices <= 0) {
|
||||
throw new RuntimeException("No physical devices found");
|
||||
}
|
||||
|
||||
// Populate available devices
|
||||
List<PhysicalDevice> devices = new ArrayList<>();
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
VkPhysicalDevice vkPhysicalDevice = new VkPhysicalDevice(pPhysicalDevices.get(i),
|
||||
instance.getVkInstance());
|
||||
PhysicalDevice physicalDevice = new PhysicalDevice(vkPhysicalDevice);
|
||||
|
||||
String deviceName = physicalDevice.getDeviceName();
|
||||
if (physicalDevice.hasGraphicsQueueFamily() && physicalDevice.hasKHRSwapChainExtension()) {
|
||||
Logger.debug("Device [{}] supports required extensions", deviceName);
|
||||
if (preferredDeviceName.isPresent() && preferredDeviceName.get().equals(deviceName)) {
|
||||
selectedPhysicalDevice = physicalDevice;
|
||||
break;
|
||||
}
|
||||
devices.add(physicalDevice);
|
||||
} else {
|
||||
Logger.debug("Device [{}] does not support required extensions", deviceName);
|
||||
physicalDevice.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
// No preferred device or it does not meet requirements, just pick the first one
|
||||
selectedPhysicalDevice = selectedPhysicalDevice == null && !devices.isEmpty() ? devices.remove(0)
|
||||
: selectedPhysicalDevice;
|
||||
|
||||
// Clean up non-selected devices
|
||||
for (PhysicalDevice physicalDevice : devices) {
|
||||
physicalDevice.destroy();
|
||||
}
|
||||
|
||||
if (selectedPhysicalDevice == null) {
|
||||
throw new RuntimeException("No suitable physical devices found");
|
||||
}
|
||||
Logger.debug("Selected device: [{}]", selectedPhysicalDevice.getDeviceName());
|
||||
}
|
||||
|
||||
return selectedPhysicalDevice;
|
||||
}
|
||||
|
||||
protected static PointerBuffer getPhysicalDevices(Instance instance, MemoryStack stack) throws VulkanError {
|
||||
public static List<PhysicalDevice> getPhysicalDevices(Instance instance, MemoryStack stack) throws VulkanError {
|
||||
PointerBuffer pPhysicalDevices;
|
||||
// Get number of physical devices
|
||||
IntBuffer intBuffer = stack.mallocInt(1);
|
||||
|
@ -116,7 +65,14 @@ public class PhysicalDevice {
|
|||
pPhysicalDevices = stack.mallocPointer(numDevices);
|
||||
vkCheck(vkEnumeratePhysicalDevices(instance.getVkInstance(), intBuffer, pPhysicalDevices),
|
||||
"Failed to get physical devices");
|
||||
return pPhysicalDevices;
|
||||
|
||||
List<PhysicalDevice> physicalDevices = new ArrayList<>();
|
||||
for (int i = 0; i < numDevices; i++) {
|
||||
VkPhysicalDevice physicalDevice = new VkPhysicalDevice(pPhysicalDevices.get(i), instance.getVkInstance());
|
||||
physicalDevices.add(new PhysicalDevice(physicalDevice));
|
||||
}
|
||||
|
||||
return physicalDevices;
|
||||
}
|
||||
|
||||
public void destroy() {
|
||||
|
@ -154,29 +110,7 @@ public class PhysicalDevice {
|
|||
return vkQueueFamilyProps;
|
||||
}
|
||||
|
||||
private boolean hasGraphicsQueueFamily() {
|
||||
boolean result = false;
|
||||
int numQueueFamilies = vkQueueFamilyProps != null ? vkQueueFamilyProps.capacity() : 0;
|
||||
for (int i = 0; i < numQueueFamilies; i++) {
|
||||
VkQueueFamilyProperties familyProps = vkQueueFamilyProps.get(i);
|
||||
if ((familyProps.queueFlags() & VK_QUEUE_GRAPHICS_BIT) != 0) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean hasKHRSwapChainExtension() {
|
||||
boolean result = false;
|
||||
int numExtensions = vkDeviceExtensions != null ? vkDeviceExtensions.capacity() : 0;
|
||||
for (int i = 0; i < numExtensions; i++) {
|
||||
String extensionName = vkDeviceExtensions.get(i).extensionNameString();
|
||||
if (KHRSwapchain.VK_KHR_SWAPCHAIN_EXTENSION_NAME.equals(extensionName)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
public VkExtensionProperties.Buffer getVkDeviceExtensions() {
|
||||
return vkDeviceExtensions;
|
||||
}
|
||||
}
|
|
@ -11,16 +11,15 @@ import java.nio.LongBuffer;
|
|||
|
||||
public class Surface {
|
||||
|
||||
private final PhysicalDevice physicalDevice;
|
||||
private final Instance instance;
|
||||
private final long vkSurface;
|
||||
|
||||
public Surface(PhysicalDevice physicalDevice, long windowHandle) throws VulkanError {
|
||||
this.physicalDevice = physicalDevice;
|
||||
|
||||
public Surface(Instance instance, long windowHandle) throws VulkanError {
|
||||
this.instance = instance;
|
||||
try (MemoryStack stack = MemoryStack.stackPush()) {
|
||||
LongBuffer pSurface = stack.mallocLong(1);
|
||||
|
||||
var result = GLFWVulkan.glfwCreateWindowSurface(this.physicalDevice.getVkPhysicalDevice().getInstance(),
|
||||
var result = GLFWVulkan.glfwCreateWindowSurface(instance.getVkInstance(),
|
||||
windowHandle,
|
||||
null, pSurface);
|
||||
vkCheck(result, "Failed to create Vulkan Surface");
|
||||
|
@ -32,7 +31,7 @@ public class Surface {
|
|||
}
|
||||
|
||||
public void destroy() {
|
||||
KHRSurface.vkDestroySurfaceKHR(physicalDevice.getVkPhysicalDevice().getInstance(), vkSurface, null);
|
||||
KHRSurface.vkDestroySurfaceKHR(instance.getVkInstance(), vkSurface, null);
|
||||
Logger.debug("Vulkan surface destroyed");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package fr.mrdev023.vulkan_java.vk;
|
||||
|
||||
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 static org.lwjgl.glfw.GLFWVulkan.glfwVulkanSupported;
|
||||
|
||||
|
@ -20,9 +23,22 @@ public class Vulkan {
|
|||
}
|
||||
|
||||
instance = new Instance(true);
|
||||
physicalDevice = PhysicalDevice.createPhysicalDevice(instance, Optional.empty());
|
||||
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 physicalDeviceMatch = SuitablePhysicalDeviceFinder.findBestPhysicalDevice(instance, criteria);
|
||||
if (physicalDeviceMatch == null) {
|
||||
throw new RuntimeException("No suitable physical device found");
|
||||
}
|
||||
|
||||
physicalDevice = physicalDeviceMatch.physicalDevice;
|
||||
device = new Device(physicalDevice);
|
||||
surface = new Surface(physicalDevice, Display.getWindow());
|
||||
graphicsQueue = new Queue.GraphicsQueue(device, 0);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,192 @@
|
|||
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;
|
||||
|
||||
public class SuitablePhysicalDeviceFinder {
|
||||
|
||||
public static MatchResult findBestPhysicalDevice(Instance instance, Criteria criteria) 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return matchedPhysicalDevices.stream().min(Comparator.comparingInt(MatchResult::getScore)).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 ((vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_GRAPHICS_BIT) != 0) {
|
||||
graphicsQueueFamilyIndex = i;
|
||||
}
|
||||
|
||||
if ((vkQueueFamilyProp.queueFlags() & VK10.VK_QUEUE_COMPUTE_BIT) != 0) {
|
||||
computeQueueFamilyIndex = i;
|
||||
}
|
||||
|
||||
if ((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;
|
||||
}
|
||||
}
|
||||
|
||||
return new MatchResult(physicalDevice, graphicsQueueFamilyIndex, computeQueueFamilyIndex, transferQueueFamilyIndex, surfaceSupport);
|
||||
}
|
||||
|
||||
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 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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue