diff --git a/generator/vk_layer/source/device.cpp b/generator/vk_layer/source/device.cpp index 3371cff..c145efa 100644 --- a/generator/vk_layer/source/device.cpp +++ b/generator/vk_layer/source/device.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -39,6 +39,9 @@ */ static std::unordered_map> g_devices; +/* See header for documentation. */ +const std::vector Device::extraExtensions { }; + /* See header for documentation. */ void Device::store( VkDevice handle, diff --git a/generator/vk_layer/source/device.hpp b/generator/vk_layer/source/device.hpp index c0e1f0a..569699e 100644 --- a/generator/vk_layer/source/device.hpp +++ b/generator/vk_layer/source/device.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -132,6 +132,17 @@ class Device ~Device() = default; public: + /** + * @brief The driver function dispatch table. + */ + DeviceDispatchTable driver {}; + + /** + * @brief The minimum set of device extensions needed by this layer. + */ + static const std::vector extraExtensions; + +private: /** * @brief The instance this device is created with. */ @@ -146,9 +157,4 @@ class Device * @brief The device handle this device is created with. */ const VkDevice device; - - /** - * @brief The driver function dispatch table. - */ - DeviceDispatchTable driver {}; }; diff --git a/generator/vk_layer/source/instance.cpp b/generator/vk_layer/source/instance.cpp index 0b62857..8fb5e88 100644 --- a/generator/vk_layer/source/instance.cpp +++ b/generator/vk_layer/source/instance.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -34,6 +34,14 @@ */ static std::unordered_map> g_instances; +/* See header for documentation. */ +const APIVersion Instance::minAPIVersion { 1, 1 }; + +/* See header for documentation. */ +const std::vector Instance::extraExtensions { + "VK_EXT_debug_utils" +}; + /* See header for documentation. */ void Instance::store( VkInstance handle, diff --git a/generator/vk_layer/source/instance.hpp b/generator/vk_layer/source/instance.hpp index fc6af6b..b6bdea1 100644 --- a/generator/vk_layer/source/instance.hpp +++ b/generator/vk_layer/source/instance.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -131,4 +131,14 @@ class Instance * @brief The driver function dispatch table. */ InstanceDispatchTable driver {}; + + /** + * @brief The minimum API version needed by this layer. + */ + static const APIVersion minAPIVersion; + + /** + * @brief The minimum set of instance extensions needed by this layer. + */ + static const std::vector extraExtensions; }; diff --git a/layer_example/source/device.cpp b/layer_example/source/device.cpp index 3371cff..c145efa 100644 --- a/layer_example/source/device.cpp +++ b/layer_example/source/device.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -39,6 +39,9 @@ */ static std::unordered_map> g_devices; +/* See header for documentation. */ +const std::vector Device::extraExtensions { }; + /* See header for documentation. */ void Device::store( VkDevice handle, diff --git a/layer_example/source/device.hpp b/layer_example/source/device.hpp index c0e1f0a..569699e 100644 --- a/layer_example/source/device.hpp +++ b/layer_example/source/device.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -132,6 +132,17 @@ class Device ~Device() = default; public: + /** + * @brief The driver function dispatch table. + */ + DeviceDispatchTable driver {}; + + /** + * @brief The minimum set of device extensions needed by this layer. + */ + static const std::vector extraExtensions; + +private: /** * @brief The instance this device is created with. */ @@ -146,9 +157,4 @@ class Device * @brief The device handle this device is created with. */ const VkDevice device; - - /** - * @brief The driver function dispatch table. - */ - DeviceDispatchTable driver {}; }; diff --git a/layer_example/source/instance.cpp b/layer_example/source/instance.cpp index 0b62857..8fb5e88 100644 --- a/layer_example/source/instance.cpp +++ b/layer_example/source/instance.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -34,6 +34,14 @@ */ static std::unordered_map> g_instances; +/* See header for documentation. */ +const APIVersion Instance::minAPIVersion { 1, 1 }; + +/* See header for documentation. */ +const std::vector Instance::extraExtensions { + "VK_EXT_debug_utils" +}; + /* See header for documentation. */ void Instance::store( VkInstance handle, diff --git a/layer_example/source/instance.hpp b/layer_example/source/instance.hpp index fc6af6b..b6bdea1 100644 --- a/layer_example/source/instance.hpp +++ b/layer_example/source/instance.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -131,4 +131,14 @@ class Instance * @brief The driver function dispatch table. */ InstanceDispatchTable driver {}; + + /** + * @brief The minimum API version needed by this layer. + */ + static const APIVersion minAPIVersion; + + /** + * @brief The minimum set of instance extensions needed by this layer. + */ + static const std::vector extraExtensions; }; diff --git a/layer_gpu_support/source/device.cpp b/layer_gpu_support/source/device.cpp index cf669a4..83f5208 100644 --- a/layer_gpu_support/source/device.cpp +++ b/layer_gpu_support/source/device.cpp @@ -33,6 +33,11 @@ */ static std::unordered_map> g_devices; +/* See header for documentation. */ +const std::vector Device::extraExtensions { + "VK_KHR_timeline_semaphore" +}; + /* See header for documentation. */ void Device::store( VkDevice handle, @@ -88,4 +93,25 @@ Device::Device( device(_device) { initDriverDeviceDispatchTable(device, nlayerGetProcAddress, driver); + + VkSemaphoreTypeCreateInfo timelineCreateInfo { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO, + .pNext = nullptr, + .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE, + .initialValue = queueSerializationTimelineSemCount + }; + + VkSemaphoreCreateInfo createInfo { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, + .pNext = &timelineCreateInfo, + .flags = 0 + }; + + auto result = driver.vkCreateSemaphore( + device, &createInfo, nullptr, &queueSerializationTimelineSem); + if (result != VK_SUCCESS) + { + LAYER_ERR("Failed vkCreateSemaphore() for queue serialization"); + queueSerializationTimelineSem = nullptr; + } } diff --git a/layer_gpu_support/source/device.hpp b/layer_gpu_support/source/device.hpp index c3dd12b..bb5c959 100644 --- a/layer_gpu_support/source/device.hpp +++ b/layer_gpu_support/source/device.hpp @@ -137,6 +137,27 @@ class Device */ const Instance* instance; + /** + * @brief The driver function dispatch table. + */ + DeviceDispatchTable driver {}; + + /** + * @brief The minimum set of device extensions needed by this layer. + */ + static const std::vector extraExtensions; + + /** + * @brief The timeline sem use for queue serialization. + */ + VkSemaphore queueSerializationTimelineSem { nullptr }; + + /** + * @brief The current timeline sem target value the next use waits for. + */ + uint64_t queueSerializationTimelineSemCount { 0 }; + +private: /** * @brief The physical device this device is created with. */ @@ -146,9 +167,4 @@ class Device * @brief The device handle this device is created with. */ const VkDevice device; - - /** - * @brief The driver function dispatch table. - */ - DeviceDispatchTable driver {}; }; diff --git a/layer_gpu_support/source/instance.cpp b/layer_gpu_support/source/instance.cpp index add2605..8fb5e88 100644 --- a/layer_gpu_support/source/instance.cpp +++ b/layer_gpu_support/source/instance.cpp @@ -34,6 +34,14 @@ */ static std::unordered_map> g_instances; +/* See header for documentation. */ +const APIVersion Instance::minAPIVersion { 1, 1 }; + +/* See header for documentation. */ +const std::vector Instance::extraExtensions { + "VK_EXT_debug_utils" +}; + /* See header for documentation. */ void Instance::store( VkInstance handle, diff --git a/layer_gpu_support/source/instance.hpp b/layer_gpu_support/source/instance.hpp index 5791a44..081b26d 100644 --- a/layer_gpu_support/source/instance.hpp +++ b/layer_gpu_support/source/instance.hpp @@ -137,4 +137,14 @@ class Instance * @brief The layer configuration. */ const LayerConfig config; + + /** + * @brief The minimum API version needed by this layer. + */ + static const APIVersion minAPIVersion; + + /** + * @brief The minimum set of instance extensions needed by this layer. + */ + static const std::vector extraExtensions; }; diff --git a/layer_gpu_support/source/layer_device_functions_queue.cpp b/layer_gpu_support/source/layer_device_functions_queue.cpp index 01f4c25..2a99868 100644 --- a/layer_gpu_support/source/layer_device_functions_queue.cpp +++ b/layer_gpu_support/source/layer_device_functions_queue.cpp @@ -44,9 +44,65 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueSubmit( std::unique_lock lock { g_vulkanLock }; auto* layer = Device::retrieve(queue); + // Serialize in the order submits are called + // TODO: This assumes a forward progress guarantee which is no longer + // guaranteed if the user is using timeline semaphores for syncs + + const uint64_t waitValue = layer->queueSerializationTimelineSemCount; + layer->queueSerializationTimelineSemCount++; + const uint64_t signalValue = layer->queueSerializationTimelineSemCount; + + VkPipelineStageFlags waitMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT; + + VkTimelineSemaphoreSubmitInfo timelineInfo { + .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .waitSemaphoreValueCount = 1, + .pWaitSemaphoreValues = &waitValue, + .signalSemaphoreValueCount = 1, + .pSignalSemaphoreValues = &signalValue + }; + + VkSubmitInfo submitInfoPre { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = &timelineInfo, + .waitSemaphoreCount = 1, + .pWaitSemaphores = &(layer->queueSerializationTimelineSem), + .pWaitDstStageMask = &waitMask, + .commandBufferCount = 0, + .pCommandBuffers = 0, + .signalSemaphoreCount = 0, + .pSignalSemaphores = nullptr + }; + + VkSubmitInfo submitInfoPost { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO, + .pNext = &timelineInfo, + .waitSemaphoreCount = 0, + .pWaitSemaphores = 0, + .pWaitDstStageMask = 0, + .commandBufferCount = 0, + .pCommandBuffers = 0, + .signalSemaphoreCount = 1, + .pSignalSemaphores = &(layer->queueSerializationTimelineSem) + }; + // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit(queue, submitCount, pSubmits, fence); + + if (layer->instance->config.serialize_queue()) + { + layer->driver.vkQueueSubmit(queue, 1, &submitInfoPre, VK_NULL_HANDLE); + } + + auto result = layer->driver.vkQueueSubmit(queue, submitCount, pSubmits, fence); + + if (layer->instance->config.serialize_queue()) + { + layer->driver.vkQueueSubmit(queue, 1, &submitInfoPost, VK_NULL_HANDLE); + } + + return result; } /* See Vulkan API for documentation. */ @@ -63,9 +119,73 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueSubmit2( std::unique_lock lock { g_vulkanLock }; auto* layer = Device::retrieve(queue); + // Serialize in the order submits are called + // TODO: This assumes a forward progress guarantee which is no longer + // guaranteed if the user is using timeline semaphores for syncs + + const uint64_t waitValue = layer->queueSerializationTimelineSemCount; + layer->queueSerializationTimelineSemCount++; + const uint64_t signalValue = layer->queueSerializationTimelineSemCount; + + VkSemaphoreSubmitInfo timelineInfoPre { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = layer->queueSerializationTimelineSem, + .value = waitValue, + .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + .deviceIndex = 0 + }; + + VkSemaphoreSubmitInfo timelineInfoPost { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = layer->queueSerializationTimelineSem, + .value = signalValue, + .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + .deviceIndex = 0 + }; + + VkSubmitInfo2 submitInfoPre { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .pNext = nullptr, + .flags = 0, + .waitSemaphoreInfoCount = 1, + .pWaitSemaphoreInfos = &timelineInfoPre, + .commandBufferInfoCount = 0, + .pCommandBufferInfos = nullptr, + .signalSemaphoreInfoCount = 0, + .pSignalSemaphoreInfos = nullptr + }; + + VkSubmitInfo2 submitInfoPost { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .pNext = nullptr, + .flags = 0, + .waitSemaphoreInfoCount = 0, + .pWaitSemaphoreInfos = nullptr, + .commandBufferInfoCount = 0, + .pCommandBufferInfos = nullptr, + .signalSemaphoreInfoCount = 1, + .pSignalSemaphoreInfos = &timelineInfoPost + }; + // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit2(queue, submitCount, pSubmits, fence); + + if (layer->instance->config.serialize_queue()) + { + layer->driver.vkQueueSubmit2(queue, 1, &submitInfoPre, VK_NULL_HANDLE); + } + + auto result = layer->driver.vkQueueSubmit2(queue, submitCount, pSubmits, fence); + + if (layer->instance->config.serialize_queue()) + { + layer->driver.vkQueueSubmit2(queue, 1, &submitInfoPost, VK_NULL_HANDLE); + } + + return result; + } /* See Vulkan API for documentation. */ @@ -82,7 +202,70 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkQueueSubmit2KHR( std::unique_lock lock { g_vulkanLock }; auto* layer = Device::retrieve(queue); + // Serialize in the order submits are called + // TODO: This assumes a forward progress guarantee which is no longer + // guaranteed if the user is using timeline semaphores for syncs + + const uint64_t waitValue = layer->queueSerializationTimelineSemCount; + layer->queueSerializationTimelineSemCount++; + const uint64_t signalValue = layer->queueSerializationTimelineSemCount; + + VkSemaphoreSubmitInfo timelineInfoPre { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = layer->queueSerializationTimelineSem, + .value = waitValue, + .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + .deviceIndex = 0 + }; + + VkSemaphoreSubmitInfo timelineInfoPost { + .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, + .pNext = nullptr, + .semaphore = layer->queueSerializationTimelineSem, + .value = signalValue, + .stageMask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT, + .deviceIndex = 0 + }; + + VkSubmitInfo2 submitInfoPre { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .pNext = nullptr, + .flags = 0, + .waitSemaphoreInfoCount = 1, + .pWaitSemaphoreInfos = &timelineInfoPre, + .commandBufferInfoCount = 0, + .pCommandBufferInfos = nullptr, + .signalSemaphoreInfoCount = 0, + .pSignalSemaphoreInfos = nullptr + }; + + VkSubmitInfo2 submitInfoPost { + .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2, + .pNext = nullptr, + .flags = 0, + .waitSemaphoreInfoCount = 0, + .pWaitSemaphoreInfos = nullptr, + .commandBufferInfoCount = 0, + .pCommandBufferInfos = nullptr, + .signalSemaphoreInfoCount = 1, + .pSignalSemaphoreInfos = &timelineInfoPost + }; + // Release the lock to call into the driver lock.unlock(); - return layer->driver.vkQueueSubmit2KHR(queue, submitCount, pSubmits, fence); + + if (layer->instance->config.serialize_queue()) + { + layer->driver.vkQueueSubmit2KHR(queue, 1, &submitInfoPre, VK_NULL_HANDLE); + } + + auto result = layer->driver.vkQueueSubmit2KHR(queue, submitCount, pSubmits, fence); + + if (layer->instance->config.serialize_queue()) + { + layer->driver.vkQueueSubmit2KHR(queue, 1, &submitInfoPost, VK_NULL_HANDLE); + } + + return result; } diff --git a/layer_gpu_timeline/source/device.cpp b/layer_gpu_timeline/source/device.cpp index 1e140ff..5905f78 100644 --- a/layer_gpu_timeline/source/device.cpp +++ b/layer_gpu_timeline/source/device.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -40,6 +40,9 @@ */ static std::unordered_map> g_devices; +/* See header for documentation. */ +const std::vector Device::extraExtensions { }; + /* See header for documentation. */ std::unique_ptr Device::commsModule; diff --git a/layer_gpu_timeline/source/device.hpp b/layer_gpu_timeline/source/device.hpp index b56de83..bec7885 100644 --- a/layer_gpu_timeline/source/device.hpp +++ b/layer_gpu_timeline/source/device.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -170,6 +170,11 @@ class Device */ DeviceDispatchTable driver {}; + /** + * @brief The minimum set of device extensions needed by this layer. + */ + static const std::vector extraExtensions; + private: /** * @brief The instance this device is created with. diff --git a/layer_gpu_timeline/source/instance.cpp b/layer_gpu_timeline/source/instance.cpp index 0b62857..8fb5e88 100644 --- a/layer_gpu_timeline/source/instance.cpp +++ b/layer_gpu_timeline/source/instance.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -34,6 +34,14 @@ */ static std::unordered_map> g_instances; +/* See header for documentation. */ +const APIVersion Instance::minAPIVersion { 1, 1 }; + +/* See header for documentation. */ +const std::vector Instance::extraExtensions { + "VK_EXT_debug_utils" +}; + /* See header for documentation. */ void Instance::store( VkInstance handle, diff --git a/layer_gpu_timeline/source/instance.hpp b/layer_gpu_timeline/source/instance.hpp index fc6af6b..b6bdea1 100644 --- a/layer_gpu_timeline/source/instance.hpp +++ b/layer_gpu_timeline/source/instance.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -131,4 +131,14 @@ class Instance * @brief The driver function dispatch table. */ InstanceDispatchTable driver {}; + + /** + * @brief The minimum API version needed by this layer. + */ + static const APIVersion minAPIVersion; + + /** + * @brief The minimum set of instance extensions needed by this layer. + */ + static const std::vector extraExtensions; }; diff --git a/source_common/framework/manual_functions.cpp b/source_common/framework/manual_functions.cpp index bcae148..e56dda0 100644 --- a/source_common/framework/manual_functions.cpp +++ b/source_common/framework/manual_functions.cpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -94,6 +94,29 @@ VkLayerInstanceCreateInfo* getChainInfo( return const_cast(info); } +/** + * @brief Helper to search a Vulkan "pNext" list for a matching structure. + */ +template +T* searchNextList( + VkStructureType sType, + const void* pNext +) { + const auto* pStruct = reinterpret_cast(pNext); + while(pStruct) + { + if (pStruct->sType == sType) + { + break; + } + pStruct = reinterpret_cast(pStruct->pNext); + } + + // Const cast is not ideal here but we don't have functionality to + // clone a writable copy of the entire pNext chain yet ... + return const_cast(pStruct); +} + /* See header for documentation. */ VkLayerDeviceCreateInfo* getChainInfo( const VkDeviceCreateInfo* pCreateInfo @@ -181,6 +204,131 @@ PFN_vkVoidFunction getDeviceLayerFunction( return nullptr; } +/* See header for documentation. */ +APIVersion getInstanceAPIVersion( + PFN_vkGetInstanceProcAddr fpGetProcAddr +) { + auto fpFunctionRaw = fpGetProcAddr(nullptr, "vkEnumerateInstanceVersion"); + auto fpFunction = reinterpret_cast(fpFunctionRaw); + if (!fpFunction) + { + LAYER_ERR("Failed to get vkEnumerateInstanceVersion()"); + return { 0 , 0 }; + } + + uint32_t apiVersion = 0; + auto result = fpFunction(&apiVersion); + if (result != VK_SUCCESS) + { + LAYER_ERR("Failed to call vkEnumerateInstanceVersion()"); + return { 0 , 0 }; + } + + uint32_t major = VK_API_VERSION_MAJOR(apiVersion); + uint32_t minor = VK_API_VERSION_MINOR(apiVersion); + return { major, minor }; +} + +/* See header for documentation. */ +APIVersion getApplicationAPIVersion( + const VkInstanceCreateInfo* pCreateInfo +) { + uint32_t apiVersion = pCreateInfo->pApplicationInfo->apiVersion; + uint32_t major = VK_API_VERSION_MAJOR(apiVersion); + uint32_t minor = VK_API_VERSION_MINOR(apiVersion); + return { major, minor }; +} + +/* See header for documentation. */ +APIVersion getDeviceAPIVersion( + PFN_vkGetInstanceProcAddr fpGetProcAddr, + VkInstance instance, + VkPhysicalDevice physicalDevice +) { + auto fpFunctionRaw = fpGetProcAddr(instance, "vkGetPhysicalDeviceProperties"); + auto fpFunction = reinterpret_cast(fpFunctionRaw); + if (!fpFunction) + { + LAYER_ERR("Failed to get vkGetPhysicalDeviceProperties()"); + return { 0 , 0 }; + } + + VkPhysicalDeviceProperties properties {}; + fpFunction(physicalDevice, &properties); + + uint32_t major = VK_API_VERSION_MAJOR(properties.apiVersion); + uint32_t minor = VK_API_VERSION_MINOR(properties.apiVersion); + return { major, minor }; +} + +/** + * @brief Is version A >= version B. + */ +static bool isVersionGreaterEqual( + const APIVersion& a, + const APIVersion& b +) { + // Different major version + if(a.first != b.first) + { + return a.first > b.first; + } + + // Same major version, so test minor version + return a.second >= b.second; +} + +/** + * @brief Is version A > version B. + */ +static bool isVersionGreater( + const APIVersion& a, + const APIVersion& b +) { + // Different major version + if(a.first != b.first) + { + return a.first > b.first; + } + + // Same major version, so test minor version + return a.second > b.second; +} + +/* See header for documentation. */ +APIVersion increaseAPIVersion( + const APIVersion& userVersion, + const APIVersion& maxVersion, + const APIVersion& requiredVersion +) { + // User version is good enough to support the layer, so stick with that + if(isVersionGreaterEqual(userVersion, requiredVersion)) + { + LAYER_LOG( + "Instance API version %u.%u (user setting meets layer minimum)", + userVersion.first, userVersion.second); + + return userVersion; + } + + // Required version is higher than the max version so log a warning + // and try to continue using maxVersion but it may fail ... + if(isVersionGreater(requiredVersion, maxVersion)) + { + LAYER_ERR( + "Instance API version %u.%u (lower than layer minimum)", + maxVersion.first, maxVersion.second); + + return maxVersion; + } + + LAYER_LOG( + "Instance API version %u.%u (increased to layer minimum)", + requiredVersion.first, requiredVersion.second); + + return requiredVersion; +} + /* See header for documentation. */ std::vector getInstanceExtensionList( const VkInstanceCreateInfo* pCreateInfo @@ -253,7 +401,7 @@ std::vector getDeviceExtensionList( /* See header for documentation. */ bool isInExtensionList( - const std::string& target, + std::string target, uint32_t extensionCount, const char* const* extensionList ) { @@ -282,6 +430,123 @@ std::vector cloneExtensionList( return data; } +/** + * Enable VK_EXT_debug_utils if not enabled. + * + * Enabling this requires passing the extension string to vkCreateInstance(). + * + * @param supported The list of supported extension, or empty if unknown. + * @param active The list of active extensions. + */ +static void enableInstanceVkExtDebugUtils( + const std::vector& supported, + std::vector& active +) { + const std::string target { "VK_EXT_debug_utils" }; + + // Test if the desired extension is supported. If supported list is + // empty then we didn't query and assume extension is supported. + if (supported.size() && !isIn(target, supported)) + { + LAYER_ERR("Instance extension not available: %s", target.c_str()); + return; + } + + // If it is already enabled then do nothing + if (isIn(target, active)) + { + LAYER_LOG("Instance extension already enabled: %s", target.c_str()); + return; + } + + // Else add it to the list of enable extensions + LAYER_LOG("Instance extension added: %s", target.c_str()); + active.push_back(target.c_str()); +} + +/** + * Enable VK_KHR_timeline_semaphore if not enabled. + * + * Enabling this requires passing the extension string to vkCreateDevice(), + * and passing either VkPhysicalDeviceTimelineSemaphoreFeatures or + * VkPhysicalDeviceVulkan12Features with the feature enabled. + * + * If the user has the extension enabled we patch t + * + * @param createInfo The createInfo we can search to find user config. + * @param supported The list of supported extensions. + * @param active The list of active extensions. + * @param newFeatures Pre-allocated struct we can use if we need to add it. + */ +static void enableDeviceVkKhrTimelineSemaphore( + VkDeviceCreateInfo& createInfo, + std::vector& supported, + std::vector& active, + VkPhysicalDeviceTimelineSemaphoreFeatures newFeatures +) { + const std::string target { "VK_KHR_timeline_semaphore" }; + + // Test if the desired extension is supported + if (!isIn(target, supported)) + { + LAYER_LOG("Device extension not available: %s", target.c_str()); + return; + } + + // If it is not already enabled then add to the list + if (!isIn(target, active)) + { + LAYER_LOG("Instance extension added: %s", target.c_str()); + active.push_back(target.c_str()); + } + + // Check if user provided a VkPhysicalDeviceTimelineSemaphoreFeatures + auto* config1 = searchNextList( + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + createInfo.pNext); + + if (config1) + { + if (!config1->timelineSemaphore) + { + LAYER_LOG("Instance extension force enabled: %s", target.c_str()); + config1->timelineSemaphore = true; + } + else + { + LAYER_LOG("Instance extension already enabled: %s", target.c_str()); + } + } + + // Check if user provided a VkPhysicalDeviceVulkan12Features + auto* config2 = searchNextList( + VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES, + createInfo.pNext); + + if (config2) + { + if (!config2->timelineSemaphore) + { + LAYER_LOG("Instance extension force enabled: %s", target.c_str()); + config2->timelineSemaphore = true; + } + else + { + LAYER_LOG("Instance extension already enabled: %s", target.c_str()); + } + } + + // Add a config if not configured by the application + if (!config1 && !config2) + { + newFeatures.pNext = const_cast(createInfo.pNext); + newFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES; + newFeatures.timelineSemaphore = true; + createInfo.pNext = reinterpret_cast(&newFeatures); + LAYER_LOG("Instance extension config added: %s", target.c_str()); + } +} + /** See Vulkan API for documentation. */ PFN_vkVoidFunction layer_vkGetInstanceProcAddr_default( VkInstance instance, @@ -478,6 +743,13 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateInstance_default( auto* chainInfo = getChainInfo(pCreateInfo); auto fpGetInstanceProcAddr = chainInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; + + // Work out what version we should use, promoting to meet layer requirement + APIVersion appVersion = getApplicationAPIVersion(pCreateInfo); + APIVersion maxVersion = getInstanceAPIVersion(fpGetInstanceProcAddr); + APIVersion reqVersion = Instance::minAPIVersion; + APIVersion newVersion = increaseAPIVersion(appVersion, maxVersion, reqVersion); + auto fpCreateInstanceRaw = fpGetInstanceProcAddr(nullptr, "vkCreateInstance"); auto fpCreateInstance = reinterpret_cast(fpCreateInstanceRaw); if (!fpCreateInstance) @@ -485,52 +757,51 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateInstance_default( return VK_ERROR_INITIALIZATION_FAILED; } - // Create a copy we can write + // Create structure copies we can modify VkInstanceCreateInfo newCreateInfo = *pCreateInfo; + VkApplicationInfo newAppInfo = *pCreateInfo->pApplicationInfo; - // Query extension state - std::string targetExt("VK_EXT_debug_utils"); - // Assume common extensions are available (see comment at start of function) - bool targetSupported = true; - if (queryExtensions) - { - targetSupported = isIn(targetExt, supportedExtensions); - } + // Write the new application info + newAppInfo.apiVersion = VK_MAKE_API_VERSION(0, newVersion.first, newVersion.second, 0); + newCreateInfo.pApplicationInfo = &newAppInfo; - bool targetEnabled = isInExtensionList( - targetExt, - pCreateInfo->enabledExtensionCount, - pCreateInfo->ppEnabledExtensionNames); + // Create a copy of the extension list we can patch + std::vector newExtensions; + const auto start = pCreateInfo->ppEnabledExtensionNames; + const auto end = pCreateInfo->ppEnabledExtensionNames + pCreateInfo->enabledExtensionCount; + newExtensions.insert(newExtensions.end(), start, end); - if (!targetSupported) + // Enable extra extensions + for (const auto& newExt : Instance::extraExtensions) { - LAYER_LOG("WARNING: Cannot enable additional extension: %s", targetExt.c_str()); + if (newExt == "VK_EXT_debug_utils") + { + enableInstanceVkExtDebugUtils( + supportedExtensions, + newExtensions); + } + else + { + LAYER_ERR("Unknown instance extension: %s", newExt.c_str()); + } } - // Enable the extension if we need to - std::vector newExtList; - if (targetSupported && !targetEnabled) - { - LAYER_LOG("Enabling additional extension: %s", targetExt.c_str()); - newExtList = cloneExtensionList( - pCreateInfo->enabledExtensionCount, - pCreateInfo->ppEnabledExtensionNames); - - newExtList.push_back(targetExt.c_str()); - - newCreateInfo.enabledExtensionCount = newExtList.size(); - newCreateInfo.ppEnabledExtensionNames = newExtList.data(); - } + // Patch extension pointer and size after extending it + newCreateInfo.enabledExtensionCount = newExtensions.size(); + newCreateInfo.ppEnabledExtensionNames = newExtensions.data(); chainInfo->u.pLayerInfo = chainInfo->u.pLayerInfo->pNext; - auto res = fpCreateInstance(&newCreateInfo, pAllocator, pInstance); - if (res != VK_SUCCESS) + auto result = fpCreateInstance(&newCreateInfo, pAllocator, pInstance); + if (result != VK_SUCCESS) { - return res; + return result; } // Retake the lock to access layer-wide global store - auto instance = std::make_unique(*pInstance, fpGetInstanceProcAddr); + auto instance = std::make_unique( + *pInstance, + fpGetInstanceProcAddr); + { std::lock_guard lock { g_vulkanLock }; Instance::store(*pInstance, instance); @@ -576,10 +847,55 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateDevice_default( // Release the lock to call into the driver lock.unlock(); + // Get the list is supported extensions + auto supportedExtensions = getDeviceExtensionList(layer->instance, physicalDevice, pCreateInfo); + + // Query the supported Vulkan version auto* chainInfo = getChainInfo(pCreateInfo); auto fpGetInstanceProcAddr = chainInfo->u.pLayerInfo->pfnNextGetInstanceProcAddr; auto fpGetDeviceProcAddr = chainInfo->u.pLayerInfo->pfnNextGetDeviceProcAddr; - auto fpCreateDevice = reinterpret_cast(fpGetInstanceProcAddr(layer->instance, "vkCreateDevice")); + + // Log this for support purposes ... + APIVersion apiVersion = getDeviceAPIVersion( + fpGetInstanceProcAddr, layer->instance, physicalDevice); + + LAYER_LOG("Device API version %u.%u", apiVersion.first, apiVersion.second); + + // Create structure copies we can modify + VkDeviceCreateInfo newCreateInfo = *pCreateInfo; + + // Create structures we allocate here, but populated elsewhere + VkPhysicalDeviceTimelineSemaphoreFeatures newTimelineFeatures; + + // Create a copy of the extension list we can patch + std::vector newExtensions; + const auto start = pCreateInfo->ppEnabledExtensionNames; + const auto end = pCreateInfo->ppEnabledExtensionNames + pCreateInfo->enabledExtensionCount; + newExtensions.insert(newExtensions.end(), start, end); + + // Enable extra extensions + for (const auto& newExt : Device::extraExtensions) + { + if (newExt == "VK_KHR_timeline_semaphore") + { + enableDeviceVkKhrTimelineSemaphore( + newCreateInfo, + supportedExtensions, + newExtensions, + newTimelineFeatures); + } + else + { + LAYER_ERR("Unknown instance extension: %s", newExt.c_str()); + } + } + + // Patch extension pointer and size after extending it + newCreateInfo.enabledExtensionCount = newExtensions.size(); + newCreateInfo.ppEnabledExtensionNames = newExtensions.data(); + + auto fpCreateDeviceRaw = fpGetInstanceProcAddr(layer->instance, "vkCreateDevice"); + auto fpCreateDevice = reinterpret_cast(fpCreateDeviceRaw); if (!fpCreateDevice) { return VK_ERROR_INITIALIZATION_FAILED; @@ -587,15 +903,16 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateDevice_default( // Advance the link info for the next element on the chain chainInfo->u.pLayerInfo = chainInfo->u.pLayerInfo->pNext; - auto res = fpCreateDevice(physicalDevice, pCreateInfo, pAllocator, pDevice); + auto res = fpCreateDevice(physicalDevice, &newCreateInfo, pAllocator, pDevice); if (res != VK_SUCCESS) { return res; } + auto device = std::make_unique(layer, physicalDevice, *pDevice, fpGetDeviceProcAddr); + // Retake the lock to access layer-wide global store lock.lock(); - auto device = std::make_unique(layer, physicalDevice, *pDevice, fpGetDeviceProcAddr); Device::store(*pDevice, std::move(device)); return VK_SUCCESS; diff --git a/source_common/framework/manual_functions.hpp b/source_common/framework/manual_functions.hpp index d66d70c..c53500c 100644 --- a/source_common/framework/manual_functions.hpp +++ b/source_common/framework/manual_functions.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -40,11 +40,9 @@ #include "instance.hpp" #include "version.hpp" - -#include "framework/instance_functions.hpp" - #include "framework/device_dispatch_table.hpp" #include "framework/device_functions.hpp" +#include "framework/instance_functions.hpp" #include "framework/utils.hpp" /** @@ -109,6 +107,54 @@ PFN_vkVoidFunction getInstanceLayerFunction( PFN_vkVoidFunction getDeviceLayerFunction( const char* name); +/** + * @brief Fetch the maximum supported instance API version. + * + * @param fpGetProcAddr vkGetInstanceProcAddr() function pointer. + * + * @return The major/minor version numbers, or zeros on error. + */ +APIVersion getInstanceAPIVersion( + PFN_vkGetInstanceProcAddr fpGetProcAddr); + +/** + * @brief Fetch the application requested instance API version. + * + * @param pCreateInfo The application instance creation info. + * + * @return The major/minor version numbers. + */ +APIVersion getApplicationAPIVersion( + const VkInstanceCreateInfo* pCreateInfo); + +/** + * @brief Fetch the maximum supported device API version. + * + * @param fpGetProcAddr vkGetInstanceProcAddr() function pointer. + * @param instance The instance. + * @param physicalDevice The physical device. + * + * @return The major/minor version numbers, or zeros on error. + */ +APIVersion getDeviceAPIVersion( + PFN_vkGetInstanceProcAddr fpGetProcAddr, + VkInstance instance, + VkPhysicalDevice physicalDevice); + +/** + * @brief Return an increased API version, if supported. + * + * @param userVersion The user-requested version. + * @param maxVersion The max version supported by the platform. + * @param requiredVersion The layer-requested min version + * + * @return The major/minor version numbers, or zeros on error. + */ +APIVersion increaseAPIVersion( + const APIVersion& userVersion, + const APIVersion& maxVersion, + const APIVersion& requiredVersion); + /** * @brief Fetch the list of supported extensions from the instance. * @@ -148,7 +194,7 @@ std::vector getDeviceExtensionList( * @return @c true if @c target is found in @c extensionList. */ bool isInExtensionList( - const std::string& target, + std::string target, uint32_t extensionCount, const char* const* extensionList); diff --git a/source_common/framework/utils.hpp b/source_common/framework/utils.hpp index 43338fc..0a6913b 100644 --- a/source_common/framework/utils.hpp +++ b/source_common/framework/utils.hpp @@ -1,7 +1,7 @@ /* * SPDX-License-Identifier: MIT * ---------------------------------------------------------------------------- - * Copyright (c) 2024 Arm Limited + * Copyright (c) 2024-2025 Arm Limited * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to @@ -56,7 +56,12 @@ #endif /** - * Tag type used for template function dispatch; + * @brief Type for a API version major/minor pair. + */ +using APIVersion = std::pair; + +/** + * @brief Tag type used for template function dispatch; */ struct user_tag {};