From c09b04dc81e19c0fe54b6c0b8b522a0a57c556cb Mon Sep 17 00:00:00 2001 From: Florian RICHER Date: Tue, 20 May 2025 18:46:16 +0200 Subject: [PATCH] Refactor instance validation layers selection --- .../fr/mrdev023/vulkan_java/vk/Instance.java | 72 +++----------- .../InstanceValidationLayersSelector.java | 94 +++++++++++++++++++ 2 files changed, 106 insertions(+), 60 deletions(-) create mode 100644 src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayersSelector.java diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java b/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java index ecdd177..ae0f6e9 100644 --- a/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/Instance.java @@ -6,6 +6,8 @@ import org.lwjgl.system.*; import org.lwjgl.vulkan.*; import org.tinylog.Logger; +import fr.mrdev023.vulkan_java.vk.utils.InstanceValidationLayersSelector; + import java.nio.*; import java.util.*; @@ -42,23 +44,16 @@ public class Instance { .apiVersion(VK_API_VERSION_1_1); // Validation layers - List validationLayers = getSupportedValidationLayers(); - int numValidationLayers = validationLayers.size(); - boolean supportsValidation = validate; - if (validate && numValidationLayers == 0) { - supportsValidation = false; - Logger.warn( - "Request validation but no supported validation layers found. Falling back to no validation"); - } - Logger.debug("Validation: {}", supportsValidation); + var validationLayers = new InstanceValidationLayersSelector() + .withValidationLayers(validate) + .selectValidationLayers(); // Set required layers PointerBuffer requiredLayers = null; - if (supportsValidation) { - requiredLayers = stack.mallocPointer(numValidationLayers); - for (int i = 0; i < numValidationLayers; i++) { - Logger.debug("Using validation layer [{}]", validationLayers.get(i)); - requiredLayers.put(i, stack.ASCII(validationLayers.get(i))); + if (!validationLayers.isEmpty()) { + requiredLayers = stack.mallocPointer(validationLayers.size()); + for (String layer : validationLayers) { + requiredLayers.put(stack.ASCII(layer)); } } @@ -74,7 +69,7 @@ public class Instance { boolean usePortability = instanceExtensions.contains(PORTABILITY_EXTENSION) && VulkanUtils.getOS() == VulkanUtils.OSType.MACOS; - if (supportsValidation) { + if (!validationLayers.isEmpty()) { ByteBuffer vkDebugUtilsExtension = stack.UTF8(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME); int numExtensions = usePortability ? glfwExtensions.remaining() + 2 : glfwExtensions.remaining() + 1; requiredExtensions = stack.mallocPointer(numExtensions); @@ -93,7 +88,7 @@ public class Instance { requiredExtensions.flip(); long extension = MemoryUtil.NULL; - if (supportsValidation) { + if (!validationLayers.isEmpty()) { debugUtils = createDebugCallBack(); extension = debugUtils.address(); } @@ -114,7 +109,7 @@ public class Instance { vkInstance = new VkInstance(pInstance.get(0), instanceInfo); vkDebugHandle = VK_NULL_HANDLE; - if (supportsValidation) { + if (!validationLayers.isEmpty()) { LongBuffer longBuff = stack.mallocLong(1); vkCheck(vkCreateDebugUtilsMessengerEXT(vkInstance, debugUtils, null, longBuff), "Error creating debug utils"); @@ -177,49 +172,6 @@ public class Instance { return instanceExtensions; } - private List getSupportedValidationLayers() { - try (MemoryStack stack = MemoryStack.stackPush()) { - IntBuffer numLayersArr = stack.callocInt(1); - vkEnumerateInstanceLayerProperties(numLayersArr, null); - int numLayers = numLayersArr.get(0); - Logger.debug("Instance supports [{}] layers", numLayers); - - VkLayerProperties.Buffer propsBuf = VkLayerProperties.calloc(numLayers, stack); - vkEnumerateInstanceLayerProperties(numLayersArr, propsBuf); - List supportedLayers = new ArrayList<>(); - for (int i = 0; i < numLayers; i++) { - VkLayerProperties props = propsBuf.get(i); - String layerName = props.layerNameString(); - supportedLayers.add(layerName); - Logger.debug("Supported layer [{}]", layerName); - } - - List layersToUse = new ArrayList<>(); - - // Main validation layer - if (supportedLayers.contains("VK_LAYER_KHRONOS_validation")) { - layersToUse.add("VK_LAYER_KHRONOS_validation"); - return layersToUse; - } - - // Fallback 1 - if (supportedLayers.contains("VK_LAYER_LUNARG_standard_validation")) { - layersToUse.add("VK_LAYER_LUNARG_standard_validation"); - return layersToUse; - } - - // Fallback 2 (set) - List requestedLayers = new ArrayList<>(); - requestedLayers.add("VK_LAYER_GOOGLE_threading"); - requestedLayers.add("VK_LAYER_LUNARG_parameter_validation"); - requestedLayers.add("VK_LAYER_LUNARG_object_tracker"); - requestedLayers.add("VK_LAYER_LUNARG_core_validation"); - requestedLayers.add("VK_LAYER_GOOGLE_unique_objects"); - - return requestedLayers.stream().filter(supportedLayers::contains).toList(); - } - } - public VkInstance getVkInstance() { return vkInstance; } diff --git a/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayersSelector.java b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayersSelector.java new file mode 100644 index 0000000..7e8f04e --- /dev/null +++ b/src/main/java/fr/mrdev023/vulkan_java/vk/utils/InstanceValidationLayersSelector.java @@ -0,0 +1,94 @@ +package fr.mrdev023.vulkan_java.vk.utils; + +import java.nio.IntBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.lwjgl.system.MemoryStack; +import org.lwjgl.vulkan.VK10; +import org.lwjgl.vulkan.VkLayerProperties; +import org.tinylog.Logger; + +public class InstanceValidationLayersSelector { + private boolean withValidationLayers = false; + + public InstanceValidationLayersSelector withValidationLayers(boolean withValidationLayers) { + this.withValidationLayers = withValidationLayers; + return this; + } + + public List selectValidationLayers() { + if (!withValidationLayers) { + return Collections.emptyList(); + } + + var supportedLayers = getSupportedValidationLayers(); + log("Supported validation layers", supportedLayers); + + var layersToUse = selectValidationLayersFromSupportedLayers(supportedLayers); + log("Selected validation layers", layersToUse); + + if (layersToUse.isEmpty() && withValidationLayers) { + Logger.warn("Request validation but no supported validation layers found. Falling back to no validation"); + } + + return layersToUse; + } + + private List getSupportedValidationLayers() { + try (MemoryStack stack = MemoryStack.stackPush()) { + // Get the number of supported validation layers by the current vulkan instance + IntBuffer numLayersArr = stack.callocInt(1); + VK10.vkEnumerateInstanceLayerProperties(numLayersArr, null); + int numLayers = numLayersArr.get(0); + + // Get the supported validation layers by the current vulkan instance + VkLayerProperties.Buffer propsBuf = VkLayerProperties.calloc(numLayers, stack); + VK10.vkEnumerateInstanceLayerProperties(numLayersArr, propsBuf); + + // Convert the supported validation layers to a list of strings + List supportedLayers = new ArrayList<>(); + for (int i = 0; i < numLayers; i++) { + VkLayerProperties props = propsBuf.get(i); + String layerName = props.layerNameString(); + supportedLayers.add(layerName); + } + + return supportedLayers; + } + } + + private List selectValidationLayersFromSupportedLayers(List supportedLayers) { + List layersToUse = new ArrayList<>(); + + // Main validation layer + if (supportedLayers.contains("VK_LAYER_KHRONOS_validation")) { + layersToUse.add("VK_LAYER_KHRONOS_validation"); + return layersToUse; + } + + // Fallback 1 + if (supportedLayers.contains("VK_LAYER_LUNARG_standard_validation")) { + layersToUse.add("VK_LAYER_LUNARG_standard_validation"); + return layersToUse; + } + + // Fallback 2 (set) + List requestedLayers = new ArrayList<>(); + requestedLayers.add("VK_LAYER_GOOGLE_threading"); + requestedLayers.add("VK_LAYER_LUNARG_parameter_validation"); + requestedLayers.add("VK_LAYER_LUNARG_object_tracker"); + requestedLayers.add("VK_LAYER_LUNARG_core_validation"); + requestedLayers.add("VK_LAYER_GOOGLE_unique_objects"); + + return requestedLayers.stream().filter(supportedLayers::contains).toList(); + } + + private void log(String title, List layers) { + Logger.debug("{} ({})", title, layers.size()); + for (String layer : layers) { + Logger.debug(" - {}", layer); + } + } +} \ No newline at end of file