Skip to content

Commit

Permalink
Make device extensions configurable
Browse files Browse the repository at this point in the history
  • Loading branch information
solidpixel committed Jan 20, 2025
1 parent 6f708e6 commit a4f2e57
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 90 deletions.
3 changes: 3 additions & 0 deletions generator/vk_layer/source/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
*/
static std::unordered_map<void*, std::unique_ptr<Device>> g_devices;

/* See header for documentation. */
const std::vector<std::string> Device::extraExtensions { };

/* See header for documentation. */
void Device::store(
VkDevice handle,
Expand Down
8 changes: 7 additions & 1 deletion generator/vk_layer/source/device.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -132,6 +132,12 @@ class Device
~Device() = default;

public:
/**
* @brief The minimum set of device extensions needed by this layer.
*/
static const std::vector<std::string> extraExtensions;

private:
/**
* @brief The instance this device is created with.
*/
Expand Down
3 changes: 3 additions & 0 deletions layer_example/source/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@
*/
static std::unordered_map<void*, std::unique_ptr<Device>> g_devices;

/* See header for documentation. */
const std::vector<std::string> Device::extraExtensions { };

/* See header for documentation. */
void Device::store(
VkDevice handle,
Expand Down
8 changes: 7 additions & 1 deletion layer_example/source/device.hpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -132,6 +132,12 @@ class Device
~Device() = default;

public:
/**
* @brief The minimum set of device extensions needed by this layer.
*/
static const std::vector<std::string> extraExtensions;

private:
/**
* @brief The instance this device is created with.
*/
Expand Down
5 changes: 5 additions & 0 deletions layer_gpu_support/source/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
*/
static std::unordered_map<void*, std::unique_ptr<Device>> g_devices;

/* See header for documentation. */
const std::vector<std::string> Device::extraExtensions {
"VK_KHR_timeline_semaphore"
};

/* See header for documentation. */
void Device::store(
VkDevice handle,
Expand Down
6 changes: 6 additions & 0 deletions layer_gpu_support/source/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,12 @@ class Device
~Device() = default;

public:
/**
* @brief The minimum set of device extensions needed by this layer.
*/
static const std::vector<std::string> extraExtensions;

private:
/**
* @brief The instance this device is created with.
*/
Expand Down
3 changes: 3 additions & 0 deletions layer_gpu_timeline/source/device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@
*/
static std::unordered_map<void*, std::unique_ptr<Device>> g_devices;

/* See header for documentation. */
const std::vector<std::string> Device::extraExtensions { };

/* See header for documentation. */
std::unique_ptr<Comms::CommsModule> Device::commsModule;

Expand Down
5 changes: 5 additions & 0 deletions layer_gpu_timeline/source/device.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,11 @@ class Device
*/
DeviceDispatchTable driver {};

/**
* @brief The minimum set of device extensions needed by this layer.
*/
static const std::vector<std::string> extraExtensions;

private:
/**
* @brief The instance this device is created with.
Expand Down
210 changes: 122 additions & 88 deletions source_common/framework/manual_functions.cpp
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -94,6 +94,29 @@ VkLayerInstanceCreateInfo* getChainInfo(
return const_cast<VkLayerInstanceCreateInfo*>(info);
}

/**
* @brief Helper to search a Vulkan "pNext" list for a matching structure.
*/
template <typename T>
T* searchNextList(
VkStructureType sType,
const void* pNext
) {
const auto* pStruct = reinterpret_cast<const T*>(pNext);
while(pStruct)
{
if (pStruct->sType == sType)
{
break;
}
pStruct = reinterpret_cast<const T*>(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<T*>(pStruct);
}

/* See header for documentation. */
VkLayerDeviceCreateInfo* getChainInfo(
const VkDeviceCreateInfo* pCreateInfo
Expand Down Expand Up @@ -407,129 +430,120 @@ std::vector<const char*> 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<std::string>& supportedExtensions,
std::vector<const char*>& newExtensions
const std::vector<std::string>& supported,
std::vector<const char*>& active
) {
const std::string target { "VK_EXT_debug_utils" };

// Test if the desired extension is supported. If supportedExtensions
// is empty then we didn't query and assume it is supported.
if (supportedExtensions.size() && !isIn(target, supportedExtensions))
// 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, newExtensions))
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());
newExtensions.push_back(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<std::string>& supportedExtensions,
std::vector<const char*>& newEnabledExtensions,
VkPhysicalDeviceTimelineSemaphoreFeatures newTimelineFeatures
std::vector<std::string>& supported,
std::vector<const char*>& active,
VkPhysicalDeviceTimelineSemaphoreFeatures newFeatures
) {
static const char* target = "VK_KHR_timeline_semaphore";
const std::string target { "VK_KHR_timeline_semaphore" };

// Extension is not supported ...
bool isSupported = isIn(target, supportedExtensions);
if (!isSupported)
// Test if the desired extension is supported
if (!isIn(target, supported))
{
LAYER_LOG("Device extension not available: %s", target);
LAYER_LOG("Device extension not available: %s", target.c_str());
return;
}

// Extension is not enabled ...
bool isEnabled = isInExtensionList(
target,
createInfo.enabledExtensionCount,
createInfo.ppEnabledExtensionNames);

// Clone the extension list into our copy, if needed
if (!isEnabled && !newEnabledExtensions.size())
// If it is not already enabled then add to the list
if (!isIn(target, active))
{
newEnabledExtensions = cloneExtensionList(
createInfo.enabledExtensionCount,
createInfo.ppEnabledExtensionNames);
LAYER_LOG("Instance extension added: %s", target.c_str());
active.push_back(target.c_str());
}

// Add the extension to the end of the list
newEnabledExtensions.push_back(target);
createInfo.enabledExtensionCount = newEnabledExtensions.size();
createInfo.ppEnabledExtensionNames = newEnabledExtensions.data();

// Enable the extension/feature
bool timeline_enabled { false };
// Check if user provided a VkPhysicalDeviceTimelineSemaphoreFeatures
auto* config1 = searchNextList<VkPhysicalDeviceTimelineSemaphoreFeatures>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
createInfo.pNext);

// Check VkPhysicalDeviceTimelineSemaphoreFeatures
auto* pTSF = reinterpret_cast<const VkPhysicalDeviceTimelineSemaphoreFeatures*>(createInfo.pNext);
while(pTSF)
if (config1)
{
if (pTSF->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES)
if (!config1->timelineSemaphore)
{
break;
LAYER_LOG("Instance extension force enabled: %s", target.c_str());
config1->timelineSemaphore = true;
}
pTSF = reinterpret_cast<const VkPhysicalDeviceTimelineSemaphoreFeatures*>(pTSF->pNext);
}

// Make sure it is enabled in the existing structure, if present
if (pTSF)
{
if (!pTSF->timelineSemaphore) {
// TODO: Const cast is not safe and we should be cloning the entire pNext chain if we
// need modify anything, but this is painful and const_cast works most of the time
auto* pWritableTSF = const_cast<VkPhysicalDeviceTimelineSemaphoreFeatures*>(pTSF);
pWritableTSF->timelineSemaphore = true;
LAYER_LOG("Enabling additional extension: %s", target);
else
{
LAYER_LOG("Instance extension already enabled: %s", target.c_str());
}

timeline_enabled = true;
}

// Check VkPhysicalDeviceVulkan12Features
auto* pV12F = reinterpret_cast<const VkPhysicalDeviceVulkan12Features*>(createInfo.pNext);
while(pV12F)
// Check if user provided a VkPhysicalDeviceVulkan12Features
auto* config2 = searchNextList<VkPhysicalDeviceTimelineSemaphoreFeatures>(
VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
createInfo.pNext);

if (config2)
{
if (pV12F->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES)
if (!config2->timelineSemaphore)
{
break;
LAYER_LOG("Instance extension force enabled: %s", target.c_str());
config2->timelineSemaphore = true;
}

pV12F = reinterpret_cast<const VkPhysicalDeviceVulkan12Features*>(pV12F->pNext);
}

// Make sure it is enabled in the existing structure, if present
if (pV12F)
{
if (!pV12F->timelineSemaphore) {
// TODO: Const cast is not safe and we should be cloning the entire pNext chain if we
// need modify anything, but this is painful and const_cast works most of the time
auto* pWritableV12F = const_cast<VkPhysicalDeviceVulkan12Features*>(pV12F);
pWritableV12F->timelineSemaphore = true;
LAYER_LOG("Enabling additional extension: %s", target);
else
{
LAYER_LOG("Instance extension already enabled: %s", target.c_str());
}

timeline_enabled = true;
}

// Enable it if not enabled already by the application
if (!timeline_enabled)
// Add a config if not configured by the application
if (!config1 && !config2)
{
newTimelineFeatures.pNext = const_cast<void*>(createInfo.pNext);
newTimelineFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
newTimelineFeatures.timelineSemaphore = true;
createInfo.pNext = reinterpret_cast<const void*>(&newTimelineFeatures);
LAYER_LOG("Enabling additional extension: %s", target);
newFeatures.pNext = const_cast<void*>(createInfo.pNext);
newFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES;
newFeatures.timelineSemaphore = true;
createInfo.pNext = reinterpret_cast<const void*>(&newFeatures);
LAYER_LOG("Instance extension config added: %s", target.c_str());
}
}

Expand Down Expand Up @@ -852,16 +866,36 @@ VKAPI_ATTR VkResult VKAPI_CALL layer_vkCreateDevice_default(

// Create structures we allocate here, but populated elsewhere
VkPhysicalDeviceTimelineSemaphoreFeatures newTimelineFeatures;
std::vector<const char*> newEnabledExtensions;

// Enable timeline semaphores
enableDeviceVkKhrTimelineSemaphore(
newCreateInfo,
supportedExtensions,
newEnabledExtensions,
newTimelineFeatures);
// Create a copy of the extension list we can patch
std::vector<const char *> 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 fpCreateDevice = reinterpret_cast<PFN_vkCreateDevice>(fpGetInstanceProcAddr(layer->instance, "vkCreateDevice"));
auto fpCreateDeviceRaw = fpGetInstanceProcAddr(layer->instance, "vkCreateDevice");
auto fpCreateDevice = reinterpret_cast<PFN_vkCreateDevice>(fpCreateDeviceRaw);
if (!fpCreateDevice)
{
return VK_ERROR_INITIALIZATION_FAILED;
Expand Down

0 comments on commit a4f2e57

Please sign in to comment.