diff --git a/.gitignore b/.gitignore index 61df001..118e39e 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ __pycache__ build .vscode/ **/.*.swp +external \ No newline at end of file diff --git a/BUILD.md b/BUILD.md index e69de29..21a43ab 100644 --- a/BUILD.md +++ b/BUILD.md @@ -0,0 +1,34 @@ +# Build Instructions +This document contains the instructions for building this repository on Linux, macOS and Windows. + +## Getting Started Build Instructions + +### 64-bit Windows Build +``` +git clone git@github.com:KhronosGroup/Vulkan-Profiles.git +cd Vulkan-Profiles +cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Debug -D UPDATE_DEPS=ON -A x64 +cmake --build . --config Debug +``` + +### Windows Unit Tests + +``` +cd build/ +ctest -C Debug --output-on-failure --parallel 16 +``` + +### Linux and macOS Build +``` +git clone git@github.com:KhronosGroup/Vulkan-Profiles.git +cd Vulkan-Profiles +cmake -S . -B build/ -D CMAKE_BUILD_TYPE=Debug -D UPDATE_DEPS=ON +cmake --build . +``` + +### Linux and macOS Unit Tests + +``` +cd build/ +ctest --parallel 8 --output-on-failure +``` diff --git a/CMakeLists.txt b/CMakeLists.txt index e69de29..2e7effc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -0,0 +1,72 @@ +# +# Copyright (c) 2023-2023 Valve Corporation +# Copyright (c) 2023-2023 LunarG, 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. +# + +cmake_minimum_required(VERSION 3.17.2) + +project(VULKAN_UTILITY_LIBRARIES LANGUAGES CXX) + +include(GNUInstallDirs) + +option(UTILITY_LIBRARIES_BUILD_TESTS "Build utility libraries tests" ON) + +set(UTILITY_LIBRARIES_CPP_STANDARD 17 CACHE STRING "Set the C++ standard to build against.") +set(CMAKE_CXX_STANDARD ${UTILITY_LIBRARIES_CPP_STANDARD}) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_CXX_VISIBILITY_PRESET "hidden") +set(CMAKE_VISIBILITY_INLINES_HIDDEN "YES") + +find_package(PythonInterp 3.7.2 REQUIRED) + +add_subdirectory(scripts) + +find_package(VulkanHeaders REQUIRED CONFIG QUIET) + +if(UTILITY_LIBRARIES_BUILD_TESTS) + enable_testing() + find_package(GTest REQUIRED CONFIG) +endif() + +# Enable GUI folders +set_property(GLOBAL PROPERTY USE_FOLDERS ON) + +# NOTE: The idiom for open source projects is to not enable warnings as errors. +# This reduces problems for users who simply want to build the repository. +option(BUILD_WERROR "Treat compiler warnings as errors") +if (BUILD_WERROR) + add_compile_options("$,/WX,-Werror>") +endif() + +# Platform-specific compiler switches +if(${CMAKE_C_COMPILER_ID} MATCHES "(GNU|Clang)") + add_compile_options(-Werror) +elseif(MSVC) + add_compile_options(/W4 /WX) + add_link_options(/WX) + # Ignore some warnings that we know we'll generate. In the future the + # code that generates these warnings should be fixed properly. + # vk_loader_platform.h provokes: + # warning C4505: unreferenced local function has been removed + # gtest.h provokes: + # warning C4389: '==': signed/unsigned mismatch + # warning C4018: '>=': signed/unsigned mismatch + add_compile_options(/wd4505 /wd4389 /wd4018) +endif() + +add_subdirectory(layer_utils) + diff --git a/layer_utils/CMakeLists.txt b/layer_utils/CMakeLists.txt new file mode 100644 index 0000000..4dcafff --- /dev/null +++ b/layer_utils/CMakeLists.txt @@ -0,0 +1,31 @@ +# +# Copyright (c) 2023-2023 Valve Corporation +# Copyright (c) 2023-2023 LunarG, 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(VulkanLayerUtils STATIC) +target_sources(VulkanLayerUtils PRIVATE + vk_layer_settings.cpp + vk_layer_settings.h +) +target_link_Libraries(VulkanLayerUtils PRIVATE Vulkan::Headers) + +if(WIN32) + target_compile_definitions(VulkanLayerUtils PUBLIC _CRT_SECURE_NO_WARNINGS) +endif() + +if(UTILITY_LIBRARIES_BUILD_TESTS) + add_subdirectory(tests) +endif() diff --git a/layer_utils/tests/CMakeLists.txt b/layer_utils/tests/CMakeLists.txt new file mode 100644 index 0000000..1a92dd5 --- /dev/null +++ b/layer_utils/tests/CMakeLists.txt @@ -0,0 +1,16 @@ +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(test_libraries GTest::gtest GTest::gtest_main Vulkan::Headers VulkanLayerUtils) + +function(add_unit_test_simple NAME) + set(TEST_FILE ./${NAME}.cpp) + set(TEST_NAME VulkanLayerUtils_${NAME}) + + add_executable(${TEST_NAME} ${TEST_FILE}) + target_include_directories(${TEST_NAME} PUBLIC "${vulkan-headers_SOURCE_DIR}/include") + target_link_libraries(${TEST_NAME} PRIVATE ${test_libraries}) + add_dependencies(${TEST_NAME} VulkanLayerUtils) + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME} --gtest_catch_exceptions=0) + set_target_properties(${TEST_NAME} PROPERTIES FOLDER "VulkanLayerUtils-Tests") +endfunction(add_unit_test_simple) + +add_unit_test_simple(test_util) diff --git a/layer_utils/tests/test_util.cpp b/layer_utils/tests/test_util.cpp new file mode 100644 index 0000000..c89f05e --- /dev/null +++ b/layer_utils/tests/test_util.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2023-2023 Valve Corporation + * Copyright (c) 2023-2023 LunarG, 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. + * + * Authors: + * - Christophe Riccio + */ + +#include +#include + +#include "../vk_layer_settings.h" + +TEST(test_library_util, Something) { + EXPECT_TRUE(true); + EXPECT_FALSE(vku::IsLayerSetting("layer_name", "setting_key")); +} diff --git a/layer_utils/vk_layer_settings.cpp b/layer_utils/vk_layer_settings.cpp new file mode 100644 index 0000000..72b91c7 --- /dev/null +++ b/layer_utils/vk_layer_settings.cpp @@ -0,0 +1,632 @@ +/* + * Copyright (c) 2015-2022 The Khronos Group Inc. + * Copyright (c) 2015-2022 Valve Corporation + * Copyright (c) 2015-2022 LunarG, 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. + * + * Authors: + * - Christophe Riccio + * - Mark Lobodzinski + * - Jon Ashburn + * - Courtney Goeltzenleuchter + * - Tobin Ehlis + */ + +#include "vk_layer_settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#if defined(_WIN32) +#include +#include +#define GetCurrentDir _getcwd +#else +#include +#define GetCurrentDir getcwd +#endif + +#ifdef __ANDROID__ +#include +#endif + +namespace vku { + +static std::string format(const char *message, ...) { + std::size_t const STRING_BUFFER(4096); + + assert(message != nullptr); + assert(strlen(message) >= 0 && strlen(message) < STRING_BUFFER); + + char buffer[STRING_BUFFER]; + va_list list; + + va_start(list, message); + vsnprintf(buffer, STRING_BUFFER, message, list); + va_end(list); + + return buffer; +} + +static bool IsFrames(const std::string &s) { + static const std::regex FRAME_REGEX("^([0-9]+([-][0-9]+){0,2})(,([0-9]+([-][0-9]+){0,2}))*$"); + + return std::regex_search(s, FRAME_REGEX); +} + +static bool IsNumber(const std::string &s) { + static const std::regex FRAME_REGEX("^-?[0-9]*$"); + + return std::regex_search(s, FRAME_REGEX); +} + +static bool IsFloat(const std::string &s) { + static const std::regex FRAME_REGEX("^-?[0-9]*([.][0-9]*)?$"); + + return std::regex_search(s, FRAME_REGEX); +} + +enum Source { + SOURCE_VKCONFIG, + SOURCE_ENV_VAR, + SOURCE_LOCAL, +}; + +struct SettingsFileInfo { + SettingsFileInfo() : file_found(false), source(SOURCE_LOCAL) {} + + bool file_found; + std::string location; + Source source; +}; + +class LayerSettings { + public: + LayerSettings(); + ~LayerSettings(){}; + + void SetCallback(LAYER_SETTING_LOG_CALLBACK callback) { this->callback_ = callback; } + void Log(const std::string &setting_key, const std::string &message); + + bool Is(const std::string &setting_key); + const char *Get(const std::string &setting_key); + void Set(const std::string &setting_key, const std::string &setting_value); + + std::string vk_layer_disables_env_var; + SettingsFileInfo settings_info; + + private: + bool file_is_parsed_; + std::map value_map_; + + std::string last_log_setting; + std::string last_log_message; + + std::string FindSettings(); + void ParseFile(const char *filename); + LAYER_SETTING_LOG_CALLBACK callback_; +}; + +static LayerSettings vk_layer_settings; + +#if defined(__ANDROID__) +std::string GetAndroidProperty(const char* name) { + std::string output; + const prop_info* pi = __system_property_find(name); + if (pi) { + __system_property_read_callback( + pi, + [](void* cookie, const char* name, const char* value, uint32_t serial) { + reinterpret_cast(cookie)->assign(value); + }, + reinterpret_cast(&output)); + } + return output; +} +#endif + +static bool IsEnvironment(const char *variable) { +#if defined(__ANDROID__) + return !GetAndroidProperty(variable).empty(); +#else + return std::getenv(variable) != NULL; +#endif +} + +static std::string GetEnvironment(const char *variable) { +#if defined(__ANDROID__) + return GetAndroidProperty(variable); +#else + const char *output = std::getenv(variable); + return output == NULL ? "" : output; +#endif +} + +static std::string string_tolower(const std::string &s) { + std::string result = s; + for (auto &c : result) { + c = (char) std::tolower(c); + } + return result; +} + +static std::string string_toupper(const std::string &s) { + std::string result = s; + for (auto &c : result) { + c = (char) std::toupper(c); + } + return result; +} + +const char *GetLayerEnvVar(const char *setting_env) { + vk_layer_settings.vk_layer_disables_env_var = GetEnvironment(setting_env); + return vk_layer_settings.vk_layer_disables_env_var.c_str(); +} + +static std::string TrimPrefix(const std::string &layer_key) { + std::string key {}; + if (layer_key.find("VK_LAYER_") == 0) { + std::size_t prefix = std::strlen("VK_LAYER_"); + key = layer_key.substr(prefix, layer_key.size() - prefix); + } else { + key = layer_key; + } + return key; +} + +static std::string GetSettingKey(const char *layer_key, const char *setting_key) { + std::stringstream result; + result << string_tolower(TrimPrefix(layer_key)) << "." << setting_key; + return result.str(); +} + +static inline std::string TrimVendor(const std::string &layer_key) { + static const char *separator = "_"; + + const std::string &namespace_key = TrimPrefix(layer_key); + + const auto trimmed_beg = namespace_key.find_first_of(separator); + if (trimmed_beg == std::string::npos) return namespace_key; + + assert(namespace_key.find_last_not_of(separator) != std::string::npos && + trimmed_beg <= namespace_key.find_last_not_of(separator)); + + return namespace_key.substr(trimmed_beg + 1, namespace_key.size()); +} + +enum TrimMode { + TRIM_NONE, + TRIM_VENDOR, + TRIM_NAMESPACE, + + TRIM_FIRST = TRIM_NONE, + TRIM_LAST = TRIM_NAMESPACE, +}; + +static std::string GetEnvVarKey(const char *layer_key, const char *setting_key, TrimMode trim_mode) { + std::stringstream result; + +#if defined(__ANDROID__) + switch (trim_mode) { + default: + case TRIM_NONE: { + result << "debug.vulkan." << GetSettingKey(layer_key, setting_key); + break; + } + case TRIM_VENDOR: { + result << "debug.vulkan." << GetSettingKey(TrimVendor(layer_key).c_str(), setting_key); + break; + } + case TRIM_NAMESPACE: { + result << "debug.vulkan." << setting_key; + break; + } + } +#else + switch (trim_mode) { + default: + case TRIM_NONE: { + result << "VK_" << string_toupper(TrimPrefix(layer_key)) << "_" << string_toupper(setting_key); + break; + } + case TRIM_VENDOR: { + result << "VK_" << string_toupper(TrimVendor(layer_key)) << "_" << string_toupper(setting_key); + break; + } + case TRIM_NAMESPACE: { + result << "VK_" << string_toupper(setting_key); + break; + } + } + +#endif + return result.str(); +} + +void InitLayerSettingsLogCallback(LAYER_SETTING_LOG_CALLBACK callback) { + vk_layer_settings.SetCallback(callback); + return; +} + +bool IsLayerSetting(const char *layer_key, const char *setting_key) { + assert(layer_key); + assert(!std::string(layer_key).empty()); + assert(setting_key); + assert(!std::string(setting_key).empty()); + + for (int i = TRIM_FIRST, n = TRIM_LAST; i <= n; ++i) { + if (IsEnvironment(GetEnvVarKey(layer_key, setting_key, static_cast(i)).c_str())) return true; + } + + return vk_layer_settings.Is(GetSettingKey(layer_key, setting_key).c_str()); +} + +static std::string GetLayerSettingData(const char *layer_key, const char *setting_key) { + // First search in the environment variables + for (int i = TRIM_FIRST, n = TRIM_LAST; i <= n; ++i) { + std::string setting = GetLayerEnvVar(GetEnvVarKey(layer_key, setting_key, static_cast(i)).c_str()); + if (!setting.empty()) return setting; + } + + // Second search in vk_layer_settings.txt + return vk_layer_settings.Get(GetSettingKey(layer_key, setting_key).c_str()); +} + +bool GetLayerSettingBool(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + bool result = false; // default value + + std::string setting = string_tolower(GetLayerSettingData(layer_key, setting_key)); + if (setting.empty()) { + vk_layer_settings.Log(setting_key, + "The setting is used but the value is empty which is invalid for a boolean setting type."); + } else if (IsNumber(setting)) { + result = std::atoi(setting.c_str()) != 0; + } else if (setting == "true" || setting == "false") { + result = setting == "true"; + } else { + std::string message = format("The data provided (%s) is not a boolean value.", setting.c_str()); + vk_layer_settings.Log(setting_key, message); + } + + return result; +} + +int GetLayerSettingInt(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + int result = 0; // default value + + std::string setting = GetLayerSettingData(layer_key, setting_key); + if (setting.empty()) { + std::string message = "The setting is used but the value is empty which is invalid for a integer setting type."; + vk_layer_settings.Log(setting_key, message); + } else if (!IsNumber(setting)) { + std::string message = format("The data provided (%s) is not an integer value.", setting.c_str()); + vk_layer_settings.Log(setting_key, message); + } else { + result = std::atoi(setting.c_str()); + } + + return result; +} + +double GetLayerSettingFloat(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + double result = 0.0; // default value + + std::string setting = GetLayerSettingData(layer_key, setting_key); + if (setting.empty()) { + std::string message = "The setting is used but the value is empty which is invalid for a floating-point setting type."; + vk_layer_settings.Log(setting_key, message); + } else if (!IsFloat(setting)) { + std::string message = format("The data provided (%s) is not a floating-point value.", setting.c_str()); + vk_layer_settings.Log(setting_key, message); + } else { + result = std::atof(setting.c_str()); + } + + return result; +} + +std::string GetLayerSettingString(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + std::string setting = GetLayerSettingData(layer_key, setting_key); + if (setting.empty()) { + std::string message = "The setting is used but the value is empty which is invalid for a string setting type."; + vk_layer_settings.Log(setting_key, message); + } + + return setting; +} + +std::string GetLayerSettingFrames(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + std::string setting = GetLayerSettingData(layer_key, setting_key); + if (!setting.empty() && !IsFrames(setting)) { + std::string message = format("The data provided (%s) is not a frames value.", setting.c_str()); + vk_layer_settings.Log(setting_key, message); + } + + return setting; +} + +static inline std::vector Split(const std::string &value, const std::string &delimiter) { + std::vector result; + + std::string parse = value; + + std::size_t start = 0; + std::size_t end = parse.find(delimiter); + while (end != std::string::npos) { + result.push_back(parse.substr(start, end - start)); + start = end + delimiter.length(); + end = parse.find(delimiter, start); + } + + const std::string last = parse.substr(start, end); + if (!last.empty()) { + result.push_back(last); + } + + return result; +} + +Strings GetLayerSettingStrings(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + std::string setting = GetLayerSettingData(layer_key, setting_key); + if (setting.find_first_of(",") != std::string::npos) { + return Split(setting, ","); + } else { +#ifdef _WIN32 + const char *delimiter = ";"; +#else + const char *delimiter = ":"; +#endif + return Split(setting, delimiter); + } +} + +List GetLayerSettingList(const char *layer_key, const char *setting_key) { + assert(IsLayerSetting(layer_key, setting_key)); + + std::vector inputs = GetLayerSettingStrings(layer_key, setting_key); + + List result; + for (std::size_t i = 0, n = inputs.size(); i < n; ++i) { + std::pair value; + if (IsNumber(inputs[i])) { + value.second = atoi(inputs[i].c_str()); + } else { + value.first = inputs[i]; + } + result.push_back(value); + } + return result; +} + +// Constructor for ConfigFile. Initialize layers to log error messages to stdout by default. If a vk_layer_settings file is present, +// its settings will override the defaults. +LayerSettings::LayerSettings() : file_is_parsed_(false), callback_(nullptr) {} + +void LayerSettings::Log(const std::string &setting_key, const std::string &message) { + this->last_log_setting = setting_key; + this->last_log_message = message; + + if (this->callback_ == nullptr) { + fprintf(stderr, "LAYER SETTING (%s) error: %s\n", this->last_log_setting.c_str(), this->last_log_message.c_str()); + } else { + this->callback_(this->last_log_setting.c_str(), this->last_log_message.c_str()); + } +} + +bool LayerSettings::Is(const std::string &setting_key) { + std::map::const_iterator it; + if (!file_is_parsed_) { + std::string settings_file = FindSettings(); + ParseFile(settings_file.c_str()); + } + + return value_map_.find(setting_key) != value_map_.end(); +} + +const char *LayerSettings::Get(const std::string &setting_key) { + std::map::const_iterator it; + if (!file_is_parsed_) { + std::string settings_file = FindSettings(); + ParseFile(settings_file.c_str()); + } + + if ((it = value_map_.find(setting_key)) == value_map_.end()) { + return ""; + } else { + return it->second.c_str(); + } +} + +void LayerSettings::Set(const std::string &setting_key, const std::string &value) { + if (!file_is_parsed_) { + std::string settings_file = FindSettings(); + ParseFile(settings_file.c_str()); + } + + value_map_[setting_key] = value; +} + +#if defined(WIN32) +// Check for admin rights +static inline bool IsHighIntegrity() { + HANDLE process_token; + if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_QUERY_SOURCE, &process_token)) { + // Maximum possible size of SID_AND_ATTRIBUTES is maximum size of a SID + size of attributes DWORD. + uint8_t mandatory_label_buffer[SECURITY_MAX_SID_SIZE + sizeof(DWORD)]; + DWORD buffer_size; + if (GetTokenInformation(process_token, TokenIntegrityLevel, mandatory_label_buffer, sizeof(mandatory_label_buffer), + &buffer_size) != 0) { + const TOKEN_MANDATORY_LABEL *mandatory_label = (const TOKEN_MANDATORY_LABEL *)mandatory_label_buffer; + const DWORD sub_authority_count = *GetSidSubAuthorityCount(mandatory_label->Label.Sid); + const DWORD integrity_level = *GetSidSubAuthority(mandatory_label->Label.Sid, sub_authority_count - 1); + + CloseHandle(process_token); + return integrity_level > SECURITY_MANDATORY_MEDIUM_RID; + } + + CloseHandle(process_token); + } + + return false; +} +#endif + +std::string LayerSettings::FindSettings() { + struct stat info; + +#if defined(WIN32) + // Look for VkConfig-specific settings location specified in the windows registry + HKEY key; + + const std::array hives = {HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER}; + const size_t hives_to_check_count = IsHighIntegrity() ? 1 : hives.size(); // Admin checks only the default hive + + for (size_t hive_index = 0; hive_index < hives_to_check_count; ++hive_index) { + LSTATUS err = RegOpenKeyEx(hives[hive_index], "Software\\Khronos\\Vulkan\\Settings", 0, KEY_READ, &key); + if (err == ERROR_SUCCESS) { + char name[2048]; + DWORD i = 0, name_size, type, value, value_size; + while (ERROR_SUCCESS == RegEnumValue(key, i++, name, &(name_size = sizeof(name)), nullptr, &type, + reinterpret_cast(&value), &(value_size = sizeof(value)))) { + // Check if the registry entry is a dword with a value of zero + if (type != REG_DWORD || value != 0) { + continue; + } + + // Check if this actually points to a file + if ((stat(name, &info) != 0) || !(info.st_mode & S_IFREG)) { + continue; + } + + // Use this file + RegCloseKey(key); + settings_info.source = SOURCE_VKCONFIG; + settings_info.location = name; + return name; + } + + RegCloseKey(key); + } + } + +#else + // Look for VkConfig-specific settings location specified in a specific spot in the linux settings store + std::string search_path = GetEnvironment("XDG_DATA_HOME"); + if (search_path == "") { + search_path = GetEnvironment("HOME"); + if (search_path != "") { + search_path += "/.local/share"; + } + } + // Use the vk_layer_settings.txt file from here, if it is present + if (search_path != "") { + std::string home_file = search_path + "/vulkan/settings.d/vk_layer_settings.txt"; + if (stat(home_file.c_str(), &info) == 0) { + if (info.st_mode & S_IFREG) { + settings_info.source = SOURCE_VKCONFIG; + settings_info.location = home_file; + return home_file; + } + } + } + +#endif + +#ifdef __ANDROID__ + std::string env_path = GetEnvironment("debug.vulkan.khronos_profiles.settings_path"); +#else + // Look for an environment variable override for the settings file location + std::string env_path = GetEnvironment("VK_LAYER_SETTINGS_PATH"); +#endif + + // If the path exists use it, else use vk_layer_settings + if (stat(env_path.c_str(), &info) == 0) { + // If this is a directory, append settings file name + if (info.st_mode & S_IFDIR) { + env_path.append("/vk_layer_settings.txt"); + } + settings_info.source = SOURCE_ENV_VAR; + settings_info.location = env_path; + return env_path; + } + + // Default -- use the current working directory for the settings file location + settings_info.source = SOURCE_LOCAL; + char buff[512]; + auto buf_ptr = GetCurrentDir(buff, 512); + if (buf_ptr) { + settings_info.location = buf_ptr; + settings_info.location.append("/vk_layer_settings.txt"); + } + return "vk_layer_settings.txt"; +} + +static inline std::string TrimWhitespace(const std::string &s) { + const char *whitespace = " \t\f\v\n\r"; + + const auto trimmed_beg = s.find_first_not_of(whitespace); + if (trimmed_beg == std::string::npos) return ""; + + const auto trimmed_end = s.find_last_not_of(whitespace); + assert(trimmed_end != std::string::npos && trimmed_beg <= trimmed_end); + + return s.substr(trimmed_beg, trimmed_end - trimmed_beg + 1); +} + +void LayerSettings::ParseFile(const char *filename) { + file_is_parsed_ = true; + + // Extract option = value pairs from a file + std::ifstream file(filename); + if (file.good()) { + settings_info.file_found = true; + for (std::string line; std::getline(file, line);) { + // discard comments, which start with '#' + const auto comments_pos = line.find_first_of('#'); + if (comments_pos != std::string::npos) line.erase(comments_pos); + + const auto value_pos = line.find_first_of('='); + if (value_pos != std::string::npos) { + const std::string setting_key = TrimWhitespace(line.substr(0, value_pos)); + const std::string setting_value = TrimWhitespace(line.substr(value_pos + 1)); + value_map_[setting_key] = setting_value; + } + } + } +} + +} // namespace vku diff --git a/layer_utils/vk_layer_settings.h b/layer_utils/vk_layer_settings.h new file mode 100644 index 0000000..a9dfa7c --- /dev/null +++ b/layer_utils/vk_layer_settings.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015-2022 The Khronos Group Inc. + * Copyright (c) 2015-2022 Valve Corporation + * Copyright (c) 2015-2022 LunarG, 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. + * + * Authors: + * - Christophe Riccio + */ + +#pragma once + +#include +#include + +#include +#include + +namespace vku { + +typedef std::vector Strings; +typedef std::vector> List; +typedef void *(*LAYER_SETTING_LOG_CALLBACK)(const char *setting_key, const char *message); + +// Initialize the callback function to get error messages. By default the error messages are outputed to stderr. Use nullptr to +// return to the default behavior. +void InitLayerSettingsLogCallback(LAYER_SETTING_LOG_CALLBACK callback); + +// Check whether a setting was set either from vk_layer_settings.txt or an environment variable +bool IsLayerSetting(const char *layer_key, const char *setting_key); + +// Query setting data for BOOL setting type in the layer manifest +bool GetLayerSettingBool(const char *layer_key, const char *setting_key); + +// Query setting data for INT setting type in the layer manifest +int GetLayerSettingInt(const char *layer_key, const char *setting_key); + +// Query setting data for FLOAT setting type in the layer manifest +double GetLayerSettingFloat(const char *layer_key, const char *setting_key); + +// Query setting data for FRAMES setting type in the layer manifest +std::string GetLayerSettingFrames(const char *layer_key, const char *setting_key); + +// Query setting data for STRING, ENUM, LOAD_FILE, SAVE_FILE and SAVE_FOLDER setting types in the layer manifest +std::string GetLayerSettingString(const char *layer_key, const char *setting_key); + +// Query setting data for FLAGS setting type in the layer manifest +Strings GetLayerSettingStrings(const char *layer_key, const char *setting_key); + +// Query setting data for LIST setting type in the layer manifest +List GetLayerSettingList(const char *layer_key, const char *setting_key); +} // namespace vku diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index a023ec3..7181195 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -34,13 +34,8 @@ if (UPDATE_DEPS) set(_build_type ${CMAKE_BUILD_TYPE}) endif() - message("********************************************************************************") - message("* NOTE: Adding target vvl_update_deps to run as needed for updating *") - message("* dependencies. *") - message("********************************************************************************") - set(optional_args) - if (NOT BUILD_TESTS) + if (NOT UTILITY_LIBRARIES_BUILD_TESTS) set(optional_args "--optional=tests") endif() @@ -53,39 +48,29 @@ if (UPDATE_DEPS) set(optional_args ${optional_args} "CMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}") endif() - set(_update_deps_dir ${VVL_SOURCE_DIR}/external/${_build_type}) - if (UPDATE_DEPS_DIR) - set(_update_deps_dir ${UPDATE_DEPS_DIR}) - endif() - set(_helper_file "${_update_deps_dir}/helper.cmake") - - set(update_dep_py "${CMAKE_CURRENT_LIST_DIR}/update_deps.py") - set(known_good_json "${CMAKE_CURRENT_LIST_DIR}/known_good.json") - # Add a target so that update_deps.py will run when necessary # NOTE: This is triggered off of the timestamps of known_good.json and helper.cmake add_custom_command( - OUTPUT ${_helper_file} - COMMAND ${PYTHON_EXECUTABLE} ${update_dep_py} - --dir ${_update_deps_dir} --arch ${_target_arch} --config ${_build_type} --generator "${CMAKE_GENERATOR}" ${optional_args} - DEPENDS ${known_good_json} + OUTPUT ${PROJECT_SOURCE_DIR}/external/helper.cmake + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/update_deps.py + --dir ${PROJECT_SOURCE_DIR}/external --arch ${_target_arch} --config ${_build_type} --generator "${CMAKE_GENERATOR}" ${optional_args} + DEPENDS ${CMAKE_CURRENT_LIST_DIR}/known_good.json ) - add_custom_target(vvl_update_deps ALL DEPENDS ${_helper_file}) # Check if update_deps.py needs to be run on first cmake run - if (${known_good_json} IS_NEWER_THAN ${_helper_file}) + if (${CMAKE_CURRENT_LIST_DIR}/known_good.json IS_NEWER_THAN ${PROJECT_SOURCE_DIR}/external/helper.cmake) execute_process( - COMMAND ${PYTHON_EXECUTABLE} ${update_dep_py} - --dir ${_update_deps_dir} --arch ${_target_arch} --config ${_build_type} --generator "${CMAKE_GENERATOR}" ${optional_args} - WORKING_DIRECTORY ${VVL_SOURCE_DIR} + COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/update_deps.py + --dir ${PROJECT_SOURCE_DIR}/external --arch ${_target_arch} --config ${_build_type} --generator "${CMAKE_GENERATOR}" ${optional_args} + WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} RESULT_VARIABLE _update_deps_result ) if (NOT (${_update_deps_result} EQUAL 0)) message(FATAL_ERROR "Could not run update_deps.py which is necessary to download dependencies.") endif() endif() - include(${_helper_file}) + include(${PROJECT_SOURCE_DIR}/external/helper.cmake) else() message("********************************************************************************") message("* NOTE: Not adding target to run update_deps.py automatically. *") diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt deleted file mode 100644 index e69de29..0000000