-
Notifications
You must be signed in to change notification settings - Fork 5
layersvt: Add common layer foundation library and unit tests #31
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| # Copyright (C) 2026 Google Inc. | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| add_library(layersvt_common OBJECT | ||
| device_instance_tracker.h | ||
| device_instance_tracker.cpp | ||
| dispatch_table_manager.h | ||
| dispatch_table_manager.cpp | ||
| layer_manifest.h | ||
| layer_manifest.cpp | ||
| layer_base.h | ||
| layer_base.cpp | ||
| layer_keep_alive.h | ||
| layer_keep_alive.cpp | ||
| log.h | ||
| ) | ||
|
|
||
| set_target_properties(layersvt_common PROPERTIES | ||
| FOLDER "layers/common" | ||
| POSITION_INDEPENDENT_CODE ON | ||
| ) | ||
|
|
||
| target_include_directories(layersvt_common PUBLIC | ||
| ${CMAKE_CURRENT_SOURCE_DIR} | ||
| ${CMAKE_CURRENT_SOURCE_DIR}/.. | ||
| ) | ||
|
|
||
| target_link_libraries(layersvt_common PUBLIC | ||
| Vulkan::Headers | ||
| Vulkan::UtilityHeaders | ||
| Vulkan::LayerSettings | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. is this needed? |
||
| ) | ||
|
|
||
| if (ANDROID) | ||
| target_link_libraries(layersvt_common PUBLIC log android) | ||
| endif() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,82 @@ | ||
| /* Copyright (C) 2026 Google Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| #include "device_instance_tracker.h" | ||
| #include <mutex> | ||
|
|
||
| #include <vector> | ||
|
|
||
| namespace layersvt { | ||
|
|
||
| DeviceInstanceTracker& DeviceInstanceTracker::Get() { | ||
| static DeviceInstanceTracker instance; | ||
| return instance; | ||
| } | ||
|
|
||
| void DeviceInstanceTracker::SetVkInstance(VkPhysicalDevice physical_device, VkInstance instance) { | ||
| std::unique_lock<std::shared_mutex> lock(mutex_); | ||
| physical_device_to_instance_map_[physical_device] = instance; | ||
| } | ||
|
|
||
| VkInstance DeviceInstanceTracker::GetVkInstance(VkPhysicalDevice physical_device) const { | ||
| std::shared_lock<std::shared_mutex> lock(mutex_); | ||
| auto it = physical_device_to_instance_map_.find(physical_device); | ||
| if (it != physical_device_to_instance_map_.end()) { | ||
| return it->second; | ||
| } | ||
| return VK_NULL_HANDLE; | ||
| } | ||
|
|
||
| void DeviceInstanceTracker::EagerMapDevices(VkInstance instance, PFN_vkGetInstanceProcAddr get_instance_proc_addr) { | ||
| if (!instance || !get_instance_proc_addr) { | ||
| return; | ||
| } | ||
| auto enumerate_physical_devices = | ||
| reinterpret_cast<PFN_vkEnumeratePhysicalDevices>(get_instance_proc_addr(instance, "vkEnumeratePhysicalDevices")); | ||
| if (!enumerate_physical_devices) { | ||
| return; | ||
| } | ||
|
|
||
| uint32_t count = 0; | ||
| VkResult result = enumerate_physical_devices(instance, &count, nullptr); | ||
| if ((result == VK_SUCCESS || result == VK_INCOMPLETE) && count > 0) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When |
||
| std::vector<VkPhysicalDevice> devices(count); | ||
| result = enumerate_physical_devices(instance, &count, devices.data()); | ||
| if (result == VK_SUCCESS || result == VK_INCOMPLETE) { | ||
| std::unique_lock<std::shared_mutex> lock(mutex_); | ||
| for (uint32_t i = 0; i < count; ++i) { | ||
| physical_device_to_instance_map_[devices[i]] = instance; | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void DeviceInstanceTracker::RemoveInstance(VkInstance instance) { | ||
| std::unique_lock<std::shared_mutex> lock(mutex_); | ||
| for (auto it = physical_device_to_instance_map_.begin(); it != physical_device_to_instance_map_.end();) { | ||
| if (it->second == instance) { | ||
| it = physical_device_to_instance_map_.erase(it); | ||
| } else { | ||
| ++it; | ||
| } | ||
| } | ||
| } | ||
|
|
||
| void DeviceInstanceTracker::Clear() { | ||
| std::unique_lock<std::shared_mutex> lock(mutex_); | ||
| physical_device_to_instance_map_.clear(); | ||
| } | ||
|
|
||
| } // namespace layersvt | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| /* Copyright (C) 2026 Google Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| #pragma once | ||
|
|
||
| #include <vulkan/vulkan.h> | ||
| #include <shared_mutex> | ||
| #include <unordered_map> | ||
|
|
||
| namespace layersvt { | ||
|
|
||
| /** | ||
| * @brief Thread-safe manager for tracking associations between Vulkan physical | ||
| * devices and their corresponding parent instances. | ||
| * | ||
| * Vulkan layers require the VkInstance handle to resolve downstream function | ||
| * pointers in vkCreateDevice via pfnNextGetInstanceProcAddr. This class provides | ||
| * synchronized, low-latency lookups using a reader-writer lock (std::shared_mutex). | ||
| */ | ||
| class DeviceInstanceTracker { | ||
| public: | ||
| static DeviceInstanceTracker& Get(); | ||
|
|
||
| /** | ||
| * @brief Associates a physical device handle with its parent VkInstance. | ||
| */ | ||
| void SetVkInstance(VkPhysicalDevice physical_device, VkInstance instance); | ||
|
|
||
| /** | ||
| * @brief Retrieves the VkInstance associated with a given physical device. | ||
| * @return The associated VkInstance, or VK_NULL_HANDLE if not registered. | ||
| */ | ||
| VkInstance GetVkInstance(VkPhysicalDevice physical_device) const; | ||
|
|
||
| /** | ||
| * @brief Eagerly enumerates and associates all physical devices under an instance. | ||
| * | ||
| * Calling this during vkCreateInstance guarantees that all physical devices | ||
| * are mapped to their parent instance before any vkCreateDevice call, even if | ||
| * the application acquires physical devices prior to layer hooking or across threads. | ||
| */ | ||
| void EagerMapDevices(VkInstance instance, PFN_vkGetInstanceProcAddr get_instance_proc_addr); | ||
|
|
||
| /** | ||
| * @brief Removes all physical device mappings associated with a specific VkInstance. | ||
| */ | ||
| void RemoveInstance(VkInstance instance); | ||
|
|
||
| /** | ||
| * @brief Clears all registered mappings. | ||
| */ | ||
| void Clear(); | ||
|
|
||
| private: | ||
| DeviceInstanceTracker() = default; | ||
| ~DeviceInstanceTracker() = default; | ||
| DeviceInstanceTracker(const DeviceInstanceTracker&) = delete; | ||
| DeviceInstanceTracker& operator=(const DeviceInstanceTracker&) = delete; | ||
|
|
||
| mutable std::shared_mutex mutex_; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using std::shared_mutex seems over-engineering. It may even make it worse because it's slower than std::mutex. |
||
| std::unordered_map<VkPhysicalDevice, VkInstance> physical_device_to_instance_map_; | ||
| }; | ||
|
|
||
| } // namespace layersvt | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,141 @@ | ||
| /* Copyright (C) 2026 Google Inc. | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| #include "dispatch_table_manager.h" | ||
| #include <mutex> | ||
| #include <cassert> | ||
|
|
||
| namespace layersvt { | ||
|
|
||
| DispatchTableManager& DispatchTableManager::Get() { | ||
| static DispatchTableManager instance; | ||
| return instance; | ||
| } | ||
|
|
||
| DispatchTableManager::~DispatchTableManager() { Clear(); } | ||
|
|
||
| void DispatchTableManager::Clear() { | ||
| { | ||
| std::unique_lock<std::shared_mutex> lock(instance_mutex_); | ||
| instance_tables_.clear(); | ||
| } | ||
| { | ||
| std::unique_lock<std::shared_mutex> lock(device_mutex_); | ||
| device_tables_.clear(); | ||
| } | ||
| { | ||
| std::unique_lock<std::shared_mutex> lock(callback_mutex_); | ||
| loader_callbacks_.clear(); | ||
| } | ||
| } | ||
|
|
||
| VkuInstanceDispatchTable* DispatchTableManager::InitInstanceTable(VkInstance instance, | ||
| PFN_vkGetInstanceProcAddr get_instance_proc_addr) { | ||
| auto table = std::make_unique<VkuInstanceDispatchTable>(); | ||
| vkuInitInstanceDispatchTable(instance, table.get(), get_instance_proc_addr); | ||
|
|
||
| DispatchKey key = GetDispatchKey(instance); | ||
| std::unique_lock<std::shared_mutex> lock(instance_mutex_); | ||
| auto raw_ptr = table.get(); | ||
| instance_tables_[key] = std::move(table); | ||
| return raw_ptr; | ||
| } | ||
|
|
||
| VkuInstanceDispatchTable* DispatchTableManager::GetInstanceDispatchTable(const void* object) const { | ||
| DispatchKey key = GetDispatchKey(object); | ||
| std::shared_lock<std::shared_mutex> lock(instance_mutex_); | ||
| auto it = instance_tables_.find(key); | ||
| if (it != instance_tables_.end()) { | ||
| return it->second.get(); | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| void DispatchTableManager::DestroyInstanceTable(DispatchKey key) { | ||
| std::unique_lock<std::shared_mutex> lock(instance_mutex_); | ||
| instance_tables_.erase(key); | ||
| } | ||
|
|
||
| VkuDeviceDispatchTable* DispatchTableManager::InitDeviceTable(VkDevice device, PFN_vkGetDeviceProcAddr get_device_proc_addr) { | ||
| auto table = std::make_unique<VkuDeviceDispatchTable>(); | ||
| vkuInitDeviceDispatchTable(device, table.get(), get_device_proc_addr); | ||
|
|
||
| DispatchKey key = GetDispatchKey(device); | ||
| std::unique_lock<std::shared_mutex> lock(device_mutex_); | ||
| auto raw_ptr = table.get(); | ||
| device_tables_[key] = std::move(table); | ||
| return raw_ptr; | ||
| } | ||
|
|
||
| VkuDeviceDispatchTable* DispatchTableManager::GetDeviceDispatchTable(const void* object) const { | ||
| DispatchKey key = GetDispatchKey(object); | ||
| std::shared_lock<std::shared_mutex> lock(device_mutex_); | ||
| auto it = device_tables_.find(key); | ||
| if (it != device_tables_.end()) { | ||
| return it->second.get(); | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| void DispatchTableManager::DestroyDeviceTable(DispatchKey key) { | ||
| { | ||
| std::unique_lock<std::shared_mutex> lock(device_mutex_); | ||
| device_tables_.erase(key); | ||
| } | ||
| { | ||
| std::unique_lock<std::shared_mutex> lock(callback_mutex_); | ||
| loader_callbacks_.erase(key); | ||
| } | ||
| } | ||
|
|
||
| void DispatchTableManager::SetDeviceLoaderDataCallback(VkDevice device, PFN_vkSetDeviceLoaderData callback) { | ||
| DispatchKey key = GetDispatchKey(device); | ||
| std::unique_lock<std::shared_mutex> lock(callback_mutex_); | ||
| loader_callbacks_[key] = callback; | ||
| } | ||
|
|
||
| PFN_vkSetDeviceLoaderData DispatchTableManager::GetDeviceLoaderDataCallback(VkDevice device) const { | ||
| DispatchKey key = GetDispatchKey(device); | ||
| std::shared_lock<std::shared_mutex> lock(callback_mutex_); | ||
| auto it = loader_callbacks_.find(key); | ||
| if (it != loader_callbacks_.end()) { | ||
| return it->second; | ||
| } | ||
| return nullptr; | ||
| } | ||
|
|
||
| VkLayerInstanceCreateInfo* GetChainInfo(const VkInstanceCreateInfo* create_info, VkLayerFunction function) { | ||
| if (!create_info) { | ||
| return nullptr; | ||
| } | ||
| auto* chain_info = static_cast<const VkLayerInstanceCreateInfo*>(create_info->pNext); | ||
| while (chain_info && (chain_info->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || chain_info->function != function)) { | ||
| chain_info = static_cast<const VkLayerInstanceCreateInfo*>(chain_info->pNext); | ||
| } | ||
| return const_cast<VkLayerInstanceCreateInfo*>(chain_info); | ||
| } | ||
|
|
||
| VkLayerDeviceCreateInfo* GetChainInfo(const VkDeviceCreateInfo* create_info, VkLayerFunction function) { | ||
| if (!create_info) { | ||
| return nullptr; | ||
| } | ||
| auto* chain_info = static_cast<const VkLayerDeviceCreateInfo*>(create_info->pNext); | ||
| while (chain_info && (chain_info->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || chain_info->function != function)) { | ||
| chain_info = static_cast<const VkLayerDeviceCreateInfo*>(chain_info->pNext); | ||
| } | ||
| return const_cast<VkLayerDeviceCreateInfo*>(chain_info); | ||
| } | ||
|
|
||
| } // namespace layersvt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need this line? Seems redundant.