Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions layersvt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ else()
add_compile_options(-Wpointer-arith)
endif()

add_subdirectory(common)

if(BUILD_APIDUMP)
find_package(Python3 REQUIRED)

Expand Down Expand Up @@ -104,6 +106,10 @@ if(BUILD_MONITOR)
)
endif ()

if (NOT MSVC)
set_source_files_properties(perfetto/perfetto.cc PROPERTIES COMPILE_OPTIONS "-Wno-deprecated-declarations")
endif()

if(BUILD_SCREENSHOT)
add_library(VkLayer_screenshot MODULE)
set_target_properties(VkLayer_screenshot PROPERTIES FOLDER "layers/screenshot")
Expand Down Expand Up @@ -168,17 +174,13 @@ if(BUILD_DEBUGMARKER)
set_target_properties(VkLayer_DebugMarker PROPERTIES FOLDER "layers/debugmarker")
target_sources(VkLayer_DebugMarker PRIVATE
debug_marker/debug_marker_handwritten_dispatch.cpp
debug_marker/debug_marker_handwritten_functions.h
debug_marker/debug_marker_handwritten_functions_vk_ext_debug_marker.h
debug_marker/debug_marker_handwritten_functions_vk_ext_debug_utils.h
debug_marker/debug_marker.h
debug_marker/debug_marker.cpp
debug_marker/debug_marker_perfetto.h
debug_marker/debug_marker_perfetto.cpp
perfetto/perfetto.cc
vk_layer_table.cpp
vk_layer_table.h
layer_keep_alive.cpp
debug_marker/VkLayer_DebugMarker.json.in
)

Expand All @@ -188,6 +190,8 @@ if(BUILD_DEBUGMARKER)
${CMAKE_CURRENT_BINARY_DIR}
)

target_link_libraries(VkLayer_DebugMarker PRIVATE layersvt_common)

if(CMAKE_SYSTEM_NAME MATCHES "Linux|BSD|DragonFly|GNU")
if (BUILD_WSI_XCB_SUPPORT)
target_compile_definitions(VkLayer_DebugMarker PRIVATE VK_USE_PLATFORM_XLIB_KHR)
Expand Down
47 changes: 47 additions & 0 deletions layersvt/common/CMakeLists.txt
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
)

if (ANDROID)
target_link_libraries(layersvt_common PUBLIC log android)
endif()
82 changes: 82 additions & 0 deletions layersvt/common/device_instance_tracker.cpp
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) {
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
76 changes: 76 additions & 0 deletions layersvt/common/device_instance_tracker.h
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_;
std::unordered_map<VkPhysicalDevice, VkInstance> physical_device_to_instance_map_;
};

} // namespace layersvt
141 changes: 141 additions & 0 deletions layersvt/common/dispatch_table_manager.cpp
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
Loading
Loading