Skip to content
Merged
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
3 changes: 2 additions & 1 deletion icd/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ The behavior of the Vulkan SC Emulation ICD can be controlled with the following

* `VKSC_EMULATION_DEBUG` works similarly to the loader environment variable `VK_LOADER_DEBUG` and enables different levels of debug output to be generated to `stderr` and/or to API debug callbacks. The variable can be set to a comma-delimited list of log level options (available options: error, warn, info, debug).
* `VKSC_EMULATION_RECYCLE_PIPELINE_MEMORY` controls whether the ICD reports support for the `recyclePipelineMemory` Vulkan SC 1.0 device property. If set to `0`, then the ICD will report `recyclePipelineMemory` as `VK_FALSE`, otherwise (by default) as `VK_TRUE`.
* `VKSC_EMULATION_MAX_LOGICAL_DEVICES` can be used to set the maximum number of logical devices that can be createad for a single physical device. The default limit is `65536`. Setting this to `1` can be used to emulate implementations that only support creating a single `VkDevice` in a single `VkInstance`. Note that this limit applies per `VkInstance`, therefore creating more logical devices across multiple `VkInstance` objects is still allowed.
* `VKSC_EMULATION_GLOBAL_MAX_INSTANCES` (Linux only) can be used to set the maximum number of instances that can be created on the system (across all processes). There is no limit by default. Setting this can be used to emulate implementations that only support creating a limited number of `VkInstance` objects.
* `VKSC_EMULATION_MAX_LOGICAL_DEVICES` can be used to set the maximum number of logical devices that can be created for a single physical device. The default limit is `65536`. Setting this to `1` can be used to emulate implementations that only support creating a single `VkDevice` in a single `VkInstance` for a given physical device. Note that this limit applies per `VkInstance`, therefore creating more logical devices across multiple `VkInstance` objects is still allowed.
* `VKSC_EMULATION_DISPLAYS` controls the number of `VkDisplayKHR` objects to emulate using Win32/X11 windows on supported platforms and implementations. Setting the variable to `0` disables `VkDisplayKHR` emulation. The default value is `4`.
* `VKSC_EMULATION_DISPLAY_CONFIG` can be used to specify a JSON configuration to source for the number and properties of the `VkDisplayKHR` objects to emulate using Win32/X11 windows on supported platforms and implementations. If specified, the JSON configuration overrides the default emulated display parameters and any value specified through the `VKSC_EMULATION_DISPLAYS` environment variable. An example display configuration file can be found in [sample_display_config.json](sample_display_config.json).
9 changes: 9 additions & 0 deletions icd/icd_env_helper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ namespace icd {
EnvironmentHelper::EnvironmentHelper()
: log_severity_(ParseLogSeverity()),
recycle_pipeline_memory_(ParseRecyclePipelineMemory()),
global_max_instances_(ParseGlobalMaxInstances()),
max_logical_devices_(ParseMaxLogicalDevices()),
emulated_display_count_(ParseEmulatedDisplayCount()),
emulated_display_config_(getenv("VKSC_EMULATION_DISPLAY_CONFIG")),
Expand Down Expand Up @@ -97,6 +98,14 @@ bool EnvironmentHelper::ParseRecyclePipelineMemory() {
return true;
}

std::optional<uint32_t> EnvironmentHelper::ParseGlobalMaxInstances() {
auto env_var_value = getenv("VKSC_EMULATION_GLOBAL_MAX_INSTANCES");
if (env_var_value != nullptr) {
return atoi(env_var_value);
}
return {};
}

uint32_t EnvironmentHelper::ParseMaxLogicalDevices() {
// Default is to allow up to 64K logical devices for each physical device
uint32_t value = 65536;
Expand Down
4 changes: 4 additions & 0 deletions icd/icd_env_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <vector>
#include <unordered_map>
#include <mutex>
#include <optional>

namespace icd {

Expand All @@ -22,6 +23,7 @@ class EnvironmentHelper {

VkDebugUtilsMessageSeverityFlagsEXT LogSeverityEnv() const { return log_severity_; }
bool RecyclePipelineMemory() const { return recycle_pipeline_memory_; }
const std::optional<uint32_t>& GetGlobalMaxInstances() const { return global_max_instances_; }
uint32_t GetMaxLogicalDevices() const { return max_logical_devices_; }
uint32_t GetEmulatedDisplayCount() const { return emulated_display_count_; }
const char* GetEmulatedDisplayConfig() const { return emulated_display_config_; }
Expand All @@ -32,6 +34,7 @@ class EnvironmentHelper {
private:
VkDebugUtilsMessageSeverityFlagsEXT ParseLogSeverity();
bool ParseRecyclePipelineMemory();
std::optional<uint32_t> ParseGlobalMaxInstances();
uint32_t ParseMaxLogicalDevices();
uint32_t ParseEmulatedDisplayCount();
const std::unordered_map<const char*, std::string> InitPrivateEnvs();
Expand All @@ -40,6 +43,7 @@ class EnvironmentHelper {

const VkDebugUtilsMessageSeverityFlagsEXT log_severity_;
const bool recycle_pipeline_memory_;
const std::optional<uint32_t> global_max_instances_;
const uint32_t max_logical_devices_;
const uint32_t emulated_display_count_;
const char* emulated_display_config_;
Expand Down
56 changes: 56 additions & 0 deletions icd/vksc_global.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,20 @@
#include <windows.h>
#else
#include <dlfcn.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#endif

#include <stdlib.h>

namespace vksc {

#ifndef _WIN32
static const char* g_global_instance_count_sema_name = "/vksconvk.global_instance_count";
static sem_t* g_global_instance_count_sema = nullptr;
#endif

Global ICD;

Global::Global() : environment_(), logger_(Environment().LogSeverityEnv()), display_manager_(logger_) {
Expand Down Expand Up @@ -167,6 +175,20 @@ Global::Global() : environment_(), logger_(Environment().LogSeverityEnv()), disp
return;
}
}

#ifndef _WIN32
// A named semaphore is used to track global instance count
auto global_max_instances = Environment().GetGlobalMaxInstances();
if (global_max_instances.has_value()) {
g_global_instance_count_sema = sem_open(g_global_instance_count_sema_name, O_CREAT, 0644, global_max_instances.value());
if (g_global_instance_count_sema == SEM_FAILED) {
Log().Fatal("VKSC-EMU-GlobalMaxInstances", "Failed to create/open named semaphore to enfore global instance count");
g_global_instance_count_sema = nullptr;
valid_ = false;
return;
}
}
#endif
}

Global::~Global() {
Expand All @@ -177,6 +199,14 @@ Global::~Global() {
dlclose(vk_loader_module_);
#endif
}

#ifndef _WIN32
if (g_global_instance_count_sema) {
sem_close(g_global_instance_count_sema);
sem_unlink(g_global_instance_count_sema_name);
g_global_instance_count_sema = nullptr;
}
#endif
}

VkResult Global::EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount,
Expand All @@ -203,6 +233,23 @@ VkResult Global::EnumerateInstanceExtensionProperties(const char* pLayerName, ui
return result;
}

bool Global::AllocateInstance() const {
#ifndef _WIN32
if (g_global_instance_count_sema) {
return sem_trywait(g_global_instance_count_sema) == 0;
}
#endif
return true;
}

void Global::FreeInstance() const {
#ifndef _WIN32
if (g_global_instance_count_sema) {
sem_post(g_global_instance_count_sema);
}
#endif
}

} // namespace vksc

extern "C" {
Expand Down Expand Up @@ -302,6 +349,15 @@ VKAPI_ATTR VkResult VKAPI_CALL vkCreateInstance(const VkInstanceCreateInfo* pCre
}
}

if (result >= VK_SUCCESS) {
if (!vksc::ICD.AllocateInstance()) {
vksc::ICD.Log().Error("VKSC-EMU-GlobalInstanceLimitExceeded", "Global instance limit (%u) exceeded",
vksc::ICD.Environment().GetGlobalMaxInstances().value());
vksc::Instance::FromHandle(instance)->DestroyInstance(pAllocator);
result = VK_ERROR_TOO_MANY_OBJECTS;
}
}

if (result >= VK_SUCCESS) {
*pInstance = instance;
}
Expand Down
3 changes: 3 additions & 0 deletions icd/vksc_global.h
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ class Global {
const DispatchTable& VkDispatch() const { return vk_dispatch_table_; }
PFN_vkGetInstanceProcAddr VkGetProcAddr() const { return vk_get_instance_proc_addr_; }

bool AllocateInstance() const;
void FreeInstance() const;

VkResult EnumerateInstanceExtensionProperties(const char* pLayerName, uint32_t* pPropertyCount,
VkExtensionProperties* pProperties);

Expand Down
1 change: 1 addition & 0 deletions icd/vksc_instance.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ VkResult Instance::SetupInstance(const VkInstanceCreateInfo& create_info) {
}

void Instance::DestroyInstance(const VkAllocationCallbacks* pAllocator) {
vksc::ICD.FreeInstance();
Destroy(VkDispatch().DestroyInstance, VkHandle(), pAllocator);
}

Expand Down
2 changes: 1 addition & 1 deletion icd/vksc_physical_device.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ VkResult PhysicalDevice::CreateDevice(const VkDeviceCreateInfo* pCreateInfo, con
// Check device count against the limit only after all other checks completed and we would have otherwise succeeded device
// creation (this matches the behavior of other Vulkan SC implementations that have logical device count limits)
if (logical_device_count_.fetch_add(1) >= ICD.Environment().GetMaxLogicalDevices()) {
Log().Error("VKSC-EMU-DeviceCountLimitExceeeded", "Logical device limit (%u) exceeded",
Log().Error("VKSC-EMU-DeviceCountLimitExceeded", "Logical device limit (%u) exceeded",
ICD.Environment().GetMaxLogicalDevices());
vksc::Device::FromHandle(device)->DestroyDevice(pAllocator);
result = VK_ERROR_TOO_MANY_OBJECTS;
Expand Down
Loading