From c11aa3aca2bc2f97041f027a723f805c810c3ecb Mon Sep 17 00:00:00 2001 From: Christophe Date: Tue, 5 Sep 2023 17:02:55 +0200 Subject: [PATCH] api-dump: Use layer settings library --- CMakeLists.txt | 2 + layersvt/CMakeLists.txt | 14 +- layersvt/VkLayer_api_dump.json.in | 2 +- layersvt/api_dump.h | 477 +++++++++++-------------- layersvt/test/CMakeLists.txt | 59 +++ layersvt/test/layer_test_framework.cpp | 25 ++ layersvt/test/layer_test_framework.h | 41 +++ layersvt/test/layer_test_helper.cpp | 148 ++++++++ layersvt/test/layer_test_helper.h | 77 ++++ layersvt/test/layer_test_main.cpp | 47 +++ layersvt/test/test_api_dump.cpp | 50 +++ layersvt/test/test_monitor.cpp | 40 +++ layersvt/test/test_screenshot.cpp | 47 +++ scripts/api_dump_generator.py | 3 + scripts/known_good.json | 2 +- 15 files changed, 766 insertions(+), 268 deletions(-) create mode 100644 layersvt/test/CMakeLists.txt create mode 100644 layersvt/test/layer_test_framework.cpp create mode 100644 layersvt/test/layer_test_framework.h create mode 100644 layersvt/test/layer_test_helper.cpp create mode 100644 layersvt/test/layer_test_helper.h create mode 100644 layersvt/test/layer_test_main.cpp create mode 100644 layersvt/test/test_api_dump.cpp create mode 100644 layersvt/test/test_monitor.cpp create mode 100644 layersvt/test/test_screenshot.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c344b37e8..c3e39ef2ed 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -109,6 +109,8 @@ elseif (CMAKE_SYSTEM_NAME STREQUAL "Darwin") endif() option(BUILD_TESTS "Build tests") +option(BUILD_TESTS_DEBUG "Build tests for debugging layers" OFF) + if(BUILD_TESTS) enable_testing() add_subdirectory(tests) diff --git a/layersvt/CMakeLists.txt b/layersvt/CMakeLists.txt index 7abbc7e3df..6657c4398c 100644 --- a/layersvt/CMakeLists.txt +++ b/layersvt/CMakeLists.txt @@ -54,7 +54,7 @@ if (BUILD_APIDUMP) set_target_properties(generate_api_cpp generate_api_text_h generate_api_html_h generate_api_json_h generate_api_video_text_h generate_api_video_html_h generate_api_video_json_h - PROPERTIES FOLDER ${VULKANTOOLS_TARGET_FOLDER}) + PROPERTIES FOLDER VkLayer_api_dump/${VULKANTOOLS_TARGET_FOLDER}) endif() if (NOT APPLE) @@ -90,7 +90,8 @@ if (WIN32) CXX_STANDARD_REQUIRED ON CXX_EXTENSIONS OFF ) - set_target_properties(copy-${target}-def-file PROPERTIES FOLDER ${VULKANTOOLS_TARGET_FOLDER}) + set_target_properties(copy-${target}-def-file PROPERTIES FOLDER "VkLayer_${target}") + set_target_properties(VkLayer_${target} PROPERTIES FOLDER "VkLayer_${target}") endmacro() else() macro(add_vk_layer target) @@ -162,7 +163,7 @@ if(BUILD_APIDUMP) run_vulkantools_video_xml_generate(api_dump_generator.py api_dump_video_html.h) run_vulkantools_video_xml_generate(api_dump_generator.py api_dump_video_json.h) - add_vk_layer(api_dump api_dump.cpp api_dump.h vk_layer_table.cpp vk_layer_table.h VkLayer_api_dump.def api_dump_layer.md VkLayer_api_dump.json.in) + add_vk_layer(api_dump api_dump.cpp api_dump.h vk_layer_table.cpp vk_layer_table.h VkLayer_api_dump.def api_dump_layer.md VkLayer_api_dump.json.in ../scripts/api_dump_generator.py) add_dependencies(VkLayer_api_dump generate_api_cpp generate_api_text_h generate_api_html_h generate_api_json_h generate_api_video_text_h generate_api_video_html_h generate_api_video_json_h) endif () @@ -202,6 +203,7 @@ foreach(TARGET_NAME ${TARGET_NAMES}) ) add_custom_target(${TARGET_NAME}-json ALL COMMAND ${CMAKE_COMMAND} ${CONFIG_DEFINES} -P "${CMAKE_CURRENT_BINARY_DIR}/generator.cmake") + set_target_properties(${TARGET_NAME}-json PROPERTIES FOLDER "${TARGET_NAME}") # Add target for JSON file install. if(NOT WIN32) @@ -218,3 +220,9 @@ foreach(TARGET_NAME ${TARGET_NAMES}) endif() endforeach() + +if (NOT ANDROID) + if (BUILD_TESTS_DEBUG) + add_subdirectory(test) + endif () +endif () diff --git a/layersvt/VkLayer_api_dump.json.in b/layersvt/VkLayer_api_dump.json.in index 2b0ab24c20..ffd96fe27f 100644 --- a/layersvt/VkLayer_api_dump.json.in +++ b/layersvt/VkLayer_api_dump.json.in @@ -211,7 +211,7 @@ ] }, { - "key": "show_timestamp", + "key": "timestamp", "env": "VK_APIDUMP_TIMESTAMP", "label": "Show Timestamp", "description": "Show the timestamp of function calls since start in microseconds", diff --git a/layersvt/api_dump.h b/layersvt/api_dump.h index ec81542c02..fcadef488c 100644 --- a/layersvt/api_dump.h +++ b/layersvt/api_dump.h @@ -33,6 +33,8 @@ #include "utils/vk_layer_utils.h" #include +#include + // Include the video headers so we can print types that come from them #include "vk_video/vulkan_video_codecs_common.h" #include "vk_video/vulkan_video_codec_h264std.h" @@ -79,13 +81,21 @@ #define MAX_STRING_LENGTH 1024 // Defines for utilized environment variables. -#define API_DUMP_ENV_VAR_LOG_FILE "VK_APIDUMP_LOG_FILENAME" -#define API_DUMP_ENV_VAR_OUTPUT_FMT "VK_APIDUMP_OUTPUT_FORMAT" -#define API_DUMP_ENV_VAR_DETAILED_OUTPUT "VK_APIDUMP_DETAILED" -#define API_DUMP_ENV_VAR_NO_ADDRESSES "VK_APIDUMP_NO_ADDR" -#define API_DUMP_ENV_VAR_FLUSH_FILE "VK_APIDUMP_FLUSH" -#define API_DUMP_ENV_VAR_OUTPUT_RANGE "VK_APIDUMP_OUTPUT_RANGE" -#define API_DUMP_ENV_VAR_TIMESTAMP "VK_APIDUMP_TIMESTAMP" +#define kSettingsKeyFile "file" +#define kSettingsKeyLogFilename "log_filename" +#define kSettingsKeyOutputFormat "output_format" +#define kSettingsKeyDetailedOutput "detailed" +#define kSettingsKeyNoAddr "no_addr" +#define kSettingsKeyFlush "flush" +#define kSettingsKeyOutputRange "output_range" +#define kSettingsKeyTimestamp "timestamp" +#define kSettingsKeyIndentSize "indent_size" +#define kSettingsKeyShowTypes "show_types" +#define kSettingsKeyNameSize "name_size" +#define kSettingsKeyTypeSize "type_size" +#define kSettingsKeyUseSpaces "use_spaces" +#define kSettingsKeyShowShader "show_shader" +#define kSettingsKeyShowThreadAndFrame "show_thread_and_frame" enum class ApiDumpFormat { Text, @@ -333,88 +343,227 @@ class ApiDumpSettings { android_logcat_buf = std::make_unique>(std::make_unique()); output_stream.rdbuf(android_logcat_buf.get()); #endif - std::string filename_string = ""; + } + + ~ApiDumpSettings() { + if (output_format == ApiDumpFormat::Html) { + // Close off html + output_stream << ""; + } else if (output_format == ApiDumpFormat::Json) { + // Close off json + output_stream << "\n]" << std::endl; + } + } + + void setupInterFrameOutputFormatting(uint64_t frame_count) const /*name change? */ + { + static bool hasPrintedAFrame = false; + switch (format()) { + case (ApiDumpFormat::Html): + if (frame_count > 0) { + if (condFrameOutput.isFrameInRange(frame_count - 1)) output_stream << ""; + } + if (condFrameOutput.isFrameInRange(frame_count)) { + output_stream << "
Frame "; + if (show_thread_and_frame) { + output_stream << frame_count; + } + output_stream << ""; + } + break; + + case (ApiDumpFormat::Json): + + if (frame_count > 0) { + if (condFrameOutput.isFrameInRange(frame_count - 1)) output_stream << "\n" << indentation(1) << "]\n}"; + } + if (condFrameOutput.isFrameInRange(frame_count)) { + if (!hasPrintedAFrame) { + hasPrintedAFrame = true; + } else { + output_stream << ",\n"; + } + output_stream << "{\n"; + if (show_thread_and_frame) { + output_stream << indentation(1) << "\"frameNumber\" : \"" << frame_count << "\",\n"; + } + output_stream << indentation(1) << "\"apiCalls\" :\n"; + output_stream << indentation(1) << "[\n"; + } + break; + case (ApiDumpFormat::Text): + break; + default: + break; + } + } + + void closeFrameOutput() const { + switch (format()) { + case (ApiDumpFormat::Html): + output_stream << "
"; + break; + case (ApiDumpFormat::Json): + output_stream << "\n" << indentation(1) << "]\n}"; + break; + case (ApiDumpFormat::Text): + break; + default: + break; + } + } + + ApiDumpFormat format() const { return output_format; } + + void formatNameType(int indents, const char *name, const char *type) const { + output_stream << indentation(indents) << name << ": "; + // We have to 'print' an empty string for the setw to actually add the desired padding. + if (use_spaces) + output_stream << std::setw(name_size - (int)strlen(name) - 2) << ""; + else + output_stream << std::setw((name_size - (int)strlen(name) - 3 + tab_size) / tab_size) << ""; + + if (show_type) { + if (use_spaces) + output_stream << std::left << std::setw(type_size) << type << " = "; + else + output_stream << type << std::setw((type_size - (int)strlen(type) - 1 + tab_size) / tab_size) << "" + << " = "; + } else { + output_stream << " = "; + } + } + + inline const char *indentation(int indents) const { + // We have to 'print' an empty string for the setw to actually add the desired padding. + output_stream << std::setw(indents * indent_size) << ""; + return ""; + } + + bool shouldFlush() const { return should_flush; } + + bool showAddress() const { return show_address; } + + bool showParams() const { return show_params; } + + bool showShader() const { return show_shader; } + + bool showType() const { return show_type; } + + bool showTimestamp() const { return show_timestamp; } + + bool showThreadAndFrame() const { return show_thread_and_frame; } + + // The const cast is necessary because everyone who 'writes' to the stream necessarily must be able to modify it. + // Since basically every function in this struct is const, we have to work around that. + std::ostream &stream() const { return output_stream; } + + bool isFrameInRange(uint64_t frame) const { return condFrameOutput.isFrameInRange(frame); } + + void init(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator) { + VlLayerSettingSet layerSettingSet = VK_NULL_HANDLE; + vlCreateLayerSettingSet("VK_LAYER_LUNARG_api_dump", vlFindLayerSettingsCreateInfo(pCreateInfo), pAllocator, nullptr, + &layerSettingSet); + // If the layer settings file has a flag indicating to output to a file, // do so, to the appropriate filename. - const char *file_option = getLayerOption("lunarg_api_dump.file"); - if (file_option != NULL) { - std::string lowered_option = ToLowerString(std::string(file_option)); - if (lowered_option == "true") { - const char *filename_option = getLayerOption("lunarg_api_dump.log_filename"); - if (filename_option != NULL && strcmp(filename_option, "") != 0) { - filename_string = filename_option; - } else { - filename_string = "vk_apidump.txt"; + std::string filename_string = ""; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyFile)) { + bool file = false; + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyFile, file); + + if (file) { + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyLogFilename)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyLogFilename, filename_string); + if (filename_string.empty()) { + filename_string = "vk_apidump.txt"; + } } } } - // If an environment variable is set, always output to that filename instead, - // whether or not the settings file enables the option. Just assume a non-empty - // string is asking for the file output to the given name. - std::string env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_LOG_FILE); - if (!env_value.empty()) { - filename_string = env_value; - } + // If one of the above has set a filename, open the file as an output stream. if (!filename_string.empty()) { output_file_stream.open(filename_string, std::ofstream::out | std::ostream::trunc); output_stream.rdbuf(output_file_stream.rdbuf()); } - // Get the remaining settings (some we also want to provide the ability to override - // using environment variables). - - output_format = readFormatOption("lunarg_api_dump.output_format", ApiDumpFormat::Text); - env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_OUTPUT_FMT); - if (!env_value.empty()) { - if (ToLowerString(env_value) == "html") { + output_format = ApiDumpFormat::Text; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyOutputFormat)) { + std::string value; + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyOutputFormat, value); + value = ToLowerString(value); + if (value == "html") { output_format = ApiDumpFormat::Html; - } else if (ToLowerString(env_value) == "json") { + } else if (value == "json") { output_format = ApiDumpFormat::Json; } else { output_format = ApiDumpFormat::Text; } } - show_params = readBoolOption("lunarg_api_dump.detailed", true); - env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_DETAILED_OUTPUT); - if (!env_value.empty()) { - show_params = GetStringBooleanValue(env_value); + show_params = true; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyDetailedOutput)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyDetailedOutput, show_params); } - show_address = !readBoolOption("lunarg_api_dump.no_addr", false); - env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_NO_ADDRESSES); - if (!env_value.empty()) { - show_address = !GetStringBooleanValue(env_value); + show_address = false; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyNoAddr)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyNoAddr, show_address); } - should_flush = readBoolOption("lunarg_api_dump.flush", true); - env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_FLUSH_FILE); - if (!env_value.empty()) { - should_flush = !GetStringBooleanValue(env_value); + should_flush = true; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyFlush)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyFlush, should_flush); } - show_timestamp = readBoolOption("lunarg_api_dump.show_timestamp", false); - env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_TIMESTAMP); - if (!env_value.empty()) { - show_timestamp = GetStringBooleanValue(env_value); + show_timestamp = false; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyTimestamp)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyTimestamp, show_timestamp); } - indent_size = std::max(readIntOption("lunarg_api_dump.indent_size", 4), 0); + indent_size = 4; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyIndentSize)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyIndentSize, indent_size); + indent_size = std::max(indent_size, 0); + } tab_size = indent_size; - show_type = readBoolOption("lunarg_api_dump.show_types", true); - name_size = std::max(readIntOption("lunarg_api_dump.name_size", 32), 0); - type_size = std::max(readIntOption("lunarg_api_dump.type_size", 0), 0); - use_spaces = readBoolOption("lunarg_api_dump.use_spaces", true); - show_shader = readBoolOption("lunarg_api_dump.show_shader", false); - show_thread_and_frame = readBoolOption("lunarg_api_dump.show_thread_and_frame", true); + + show_type = true; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyShowTypes)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyShowTypes, show_type); + } + + name_size = 32; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyNameSize)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyNameSize, name_size); + name_size = std::max(name_size, 0); + } + + type_size = 0; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyTypeSize)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyTypeSize, type_size); + type_size = std::max(type_size, 0); + } + + use_spaces = true; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyUseSpaces)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyUseSpaces, use_spaces); + } + + show_shader = false; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyShowShader)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyShowShader, show_shader); + } + + show_thread_and_frame = true; + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyShowThreadAndFrame)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyShowThreadAndFrame, show_thread_and_frame); + } std::string cond_range_string; - env_value = GetPlatformEnvVar(API_DUMP_ENV_VAR_OUTPUT_RANGE); - if (!env_value.empty()) { - cond_range_string = env_value; - } else { - cond_range_string = getLayerOption("lunarg_api_dump.output_range"); + if (vlHasLayerSetting(layerSettingSet, kSettingsKeyOutputRange)) { + vlGetLayerSettingValue(layerSettingSet, kSettingsKeyOutputRange, cond_range_string); } if (cond_range_string == "" || cond_range_string == "0-0") { //"0-0" is every frame, no need to check @@ -544,123 +693,10 @@ class ApiDumpSettings { if (isFrameInRange(0)) { setupInterFrameOutputFormatting(0); } - } - ~ApiDumpSettings() { - if (output_format == ApiDumpFormat::Html) { - // Close off html - output_stream << ""; - } else if (output_format == ApiDumpFormat::Json) { - // Close off json - output_stream << "\n]" << std::endl; - } - } - - void setupInterFrameOutputFormatting(uint64_t frame_count) const /*name change? */ - { - static bool hasPrintedAFrame = false; - switch (format()) { - case (ApiDumpFormat::Html): - if (frame_count > 0) { - if (condFrameOutput.isFrameInRange(frame_count - 1)) output_stream << ""; - } - if (condFrameOutput.isFrameInRange(frame_count)) { - output_stream << "
Frame "; - if (show_thread_and_frame) { - output_stream << frame_count; - } - output_stream << ""; - } - break; - - case (ApiDumpFormat::Json): - - if (frame_count > 0) { - if (condFrameOutput.isFrameInRange(frame_count - 1)) output_stream << "\n" << indentation(1) << "]\n}"; - } - if (condFrameOutput.isFrameInRange(frame_count)) { - if (!hasPrintedAFrame) { - hasPrintedAFrame = true; - } else { - output_stream << ",\n"; - } - output_stream << "{\n"; - if (show_thread_and_frame) { - output_stream << indentation(1) << "\"frameNumber\" : \"" << frame_count << "\",\n"; - } - output_stream << indentation(1) << "\"apiCalls\" :\n"; - output_stream << indentation(1) << "[\n"; - } - break; - case (ApiDumpFormat::Text): - break; - default: - break; - } - } - - void closeFrameOutput() const { - switch (format()) { - case (ApiDumpFormat::Html): - output_stream << "
"; - break; - case (ApiDumpFormat::Json): - output_stream << "\n" << indentation(1) << "]\n}"; - break; - case (ApiDumpFormat::Text): - break; - default: - break; - } + vlDestroyLayerSettingSet(layerSettingSet, pAllocator); } - ApiDumpFormat format() const { return output_format; } - - void formatNameType(int indents, const char *name, const char *type) const { - output_stream << indentation(indents) << name << ": "; - // We have to 'print' an empty string for the setw to actually add the desired padding. - if (use_spaces) - output_stream << std::setw(name_size - (int)strlen(name) - 2) << ""; - else - output_stream << std::setw((name_size - (int)strlen(name) - 3 + tab_size) / tab_size) << ""; - - if (show_type) { - if (use_spaces) - output_stream << std::left << std::setw(type_size) << type << " = "; - else - output_stream << type << std::setw((type_size - (int)strlen(type) - 1 + tab_size) / tab_size) << "" - << " = "; - } else { - output_stream << " = "; - } - } - - inline const char *indentation(int indents) const { - // We have to 'print' an empty string for the setw to actually add the desired padding. - output_stream << std::setw(indents * indent_size) << ""; - return ""; - } - - bool shouldFlush() const { return should_flush; } - - bool showAddress() const { return show_address; } - - bool showParams() const { return show_params; } - - bool showShader() const { return show_shader; } - - bool showType() const { return show_type; } - - bool showTimestamp() const { return show_timestamp; } - - bool showThreadAndFrame() const { return show_thread_and_frame; } - - // The const cast is necessary because everyone who 'writes' to the stream necessarily must be able to modify it. - // Since basically every function in this struct is const, we have to work around that. - std::ostream &stream() const { return output_stream; } - - bool isFrameInRange(uint64_t frame) const { return condFrameOutput.isFrameInRange(frame); } - private: // Utility member to enable easier comparison by forcing a string to all lower-case static std::string ToLowerString(const std::string &value) { @@ -669,95 +705,6 @@ class ApiDumpSettings { return lower_value; } - // Utility member for getting a platform environment variable on various platforms. - std::string GetPlatformEnvVar(const std::string &var) { - std::string ret_string = ""; -#ifdef _WIN32 - char temp[MAX_STRING_LENGTH]; - int bytes = GetEnvironmentVariableA(var.c_str(), temp, MAX_STRING_LENGTH - 1); - if (0 < bytes) { - ret_string = temp; - } -#elif defined(__ANDROID__) - std::string command = "getprop debug."; - std::string lower_var = ToLowerString(var); - - // Remove any prefix "VK_" for Android properties - if (lower_var.rfind("vk_", 0) == 0) { - lower_var = lower_var.substr(3); - } - command += lower_var; - - FILE *pipe = popen(command.c_str(), "r"); - if (pipe != nullptr) { - char result[255]; - result[0] = '\0'; - fgets(result, 255, pipe); - pclose(pipe); - size_t count = strcspn(result, "\r\n"); - if (count > 0) { - ret_string = std::string(result, count); - } - } -#else - const char *ret_value = getenv(var.c_str()); - if (nullptr != ret_value) { - ret_string = ret_value; - } -#endif - return ret_string; - } - - // Utility member to convert from string to a boolean - bool GetStringBooleanValue(const std::string &value) { - auto lower_str = ToLowerString(value); - if (lower_str == "true") { - return true; - } - if (lower_str == "on") { - return true; - } - if (lower_str == "1") { - return true; - } - return false; - } - - static bool readBoolOption(const char *option, bool default_value) { - const char *string_option = getLayerOption(option); - if (string_option == NULL) return default_value; - std::string lowered_option = ToLowerString(std::string(string_option)); - if (lowered_option == "true") - return true; - else if (lowered_option == "false") - return false; - else - return default_value; - } - - static int readIntOption(const char *option, int default_value) { - const char *string_option = getLayerOption(option); - int value; - if (sscanf(string_option, "%d", &value) != 1) { - return default_value; - } else { - return value; - } - } - - static ApiDumpFormat readFormatOption(const char *option, ApiDumpFormat default_value) { - const char *string_option = getLayerOption(option); - std::string lowered_option = ToLowerString(std::string(string_option)); - if (lowered_option == "text") - return ApiDumpFormat::Text; - else if (lowered_option == "html") - return ApiDumpFormat::Html; - else if (lowered_option == "json") - return ApiDumpFormat::Json; - else - return default_value; - } - // The mutable is necessary because everyone who 'writes' to the stream necessarily must be able to modify it. // Since basically every function in this struct is const, we have to work around that. mutable std::ostream output_stream; @@ -798,6 +745,10 @@ class ApiDumpInstance { if (!first_func_call_on_frame) settings().closeFrameOutput(); } + void initLayerSettings(const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator) { + this->dump_settings.init(pCreateInfo, pAllocator); + } + uint64_t frameCount() { std::lock_guard lg(frame_mutex); uint64_t count = frame_count; diff --git a/layersvt/test/CMakeLists.txt b/layersvt/test/CMakeLists.txt new file mode 100644 index 0000000000..4f2dc6256b --- /dev/null +++ b/layersvt/test/CMakeLists.txt @@ -0,0 +1,59 @@ +# ~~~ +# 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. +# ~~~ + +if (APPLE) + set(LAYER_TEST_FILES + api_dump + ) +else() + set(LAYER_TEST_FILES + api_dump + monitor + screenshot + ) +endif() + +function(LayerTest NAME) + set(TEST_FILENAME ./test_${NAME}.cpp) + set(TEST_NAME test_${NAME}_layer) + + file(GLOB TEST_JSON_FILES ${CMAKE_SOURCE_DIR}/profiles/test/data/) + + add_executable(${TEST_NAME} + ${TEST_FILENAME} + layer_test_helper.h + layer_test_helper.cpp + layer_test_main.cpp + layer_test_framework.cpp + layer_test_framework.h) + add_dependencies(${TEST_NAME} VkLayer_${NAME}) + target_link_libraries(${TEST_NAME} Vulkan::Headers Vulkan::Vulkan GTest::gtest GTest::gtest_main Vulkan::LayerSettings) + target_compile_definitions(${TEST_NAME} PUBLIC TEST_BINARY_PATH="$") + add_test(NAME ${TEST_NAME} COMMAND ${TEST_NAME}) + + set_tests_properties(${TEST_NAME} PROPERTIES ENVIRONMENT + "VK_LAYER_PATH=$" + ) + + set_target_properties(${TEST_NAME} PROPERTIES FOLDER "VkLayer_${NAME}/Test") +endfunction(LayerTest) + +if (NOT ANDROID) + foreach(test_item ${LAYER_TEST_FILES}) + LayerTest(${test_item}) + endforeach() +endif() diff --git a/layersvt/test/layer_test_framework.cpp b/layersvt/test/layer_test_framework.cpp new file mode 100644 index 0000000000..357b0ee0c5 --- /dev/null +++ b/layersvt/test/layer_test_framework.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#include "layer_test_framework.h" + +VkTestFramework::VkTestFramework() { +#ifdef _WIN32 + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); +#endif // _WIN32 +} +VkTestFramework::~VkTestFramework() {} diff --git a/layersvt/test/layer_test_framework.h b/layersvt/test/layer_test_framework.h new file mode 100644 index 0000000000..a54130a280 --- /dev/null +++ b/layersvt/test/layer_test_framework.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#pragma once + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#include + +// Can be used by tests to record additional details / description of test +#define TEST_DESCRIPTION(desc) RecordProperty("description", desc) + +class VkTestFramework : public ::testing::Test { + public: + VkTestFramework(); + ~VkTestFramework() = 0; + + // Per-test-suite set-up. + // Called before the first test in this test suite. + static void SetUpTestSuite(); + + // Per-test-suite tear-down. + // Called after the last test in this test suite. + static void TearDownTestSuite(); +}; diff --git a/layersvt/test/layer_test_helper.cpp b/layersvt/test/layer_test_helper.cpp new file mode 100644 index 0000000000..bf79f1ab49 --- /dev/null +++ b/layersvt/test/layer_test_helper.cpp @@ -0,0 +1,148 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#include "layer_test_helper.h" + +#ifdef _WIN32 +#include +#elif __ANDROID__ +#include +#endif + +#include +#include +#include + +// On Android, VK_MAKE_API_VERSION doesn't yet exist. +#ifndef VK_MAKE_API_VERSION +#define VK_MAKE_API_VERSION(variant, major, minor, patch) VK_MAKE_VERSION(major, minor, patch) +#endif + +// TODO: When the layer path issue is resolved with CI (set outside of the tests) remove this function +void layer_test::SetEnvironmentSetting(std::string setting, const char* val) { +#ifdef _WIN32 + _putenv_s(setting.c_str(), val); +#else + setenv(setting.c_str(), val, 1); +#endif +} + +void layer_test::UnsetEnvironmentSetting(std::string setting) { +#ifdef _WIN32 + _putenv_s(setting.c_str(), ""); +#else + unsetenv(setting.c_str()); +#endif +} + +std::string layer_test::GetAbsolutePath(std::string filepath) { + std::string out; +#ifdef _WIN32 + char abs_path[_MAX_PATH]; + _fullpath(abs_path, filepath.c_str(), _MAX_PATH); + out = abs_path; +#else + char* abs_path = realpath(filepath.c_str(), nullptr); + out = abs_path; + delete[] abs_path; +#endif + return out; +} + +VkApplicationInfo layer_test::GetDefaultApplicationInfo() { + VkApplicationInfo out{VK_STRUCTURE_TYPE_APPLICATION_INFO}; + out.apiVersion = VK_API_VERSION_1_3; + out.applicationVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); + out.pApplicationName = "layer_tests"; + out.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0); + out.pEngineName = "layer_tests"; + + return out; +} + +VkResult layer_test::VulkanInstanceBuilder::GetPhysicalDevice(VkPhysicalDevice* phys_dev) { + *phys_dev = VK_NULL_HANDLE; + VkResult res; + uint32_t gpu_count = 0; + res = vkEnumeratePhysicalDevices(_instances, &gpu_count, nullptr); + if (res != VK_SUCCESS) { + return res; + } + std::vector gpus(gpu_count); + res = vkEnumeratePhysicalDevices(_instances, &gpu_count, gpus.data()); + if (res != VK_SUCCESS) { + return res; + } + *phys_dev = gpus[0]; + return res; +} + +VkResult layer_test::VulkanInstanceBuilder::Init(const char* layer_name) { + _layer_names.push_back(layer_name); + return this->Init(std::vector()); +} + +VkResult layer_test::VulkanInstanceBuilder::Init(const std::vector& settings) { + for (std::size_t i = 0, n = settings.size(); i < n; ++i) { + if (std::find(_layer_names.begin(), _layer_names.end(), settings[i].pLayerName) == _layer_names.end()) { + _layer_names.push_back(settings[i].pLayerName); + } + } + + layer_test::SetEnvironmentSetting("VK_LAYER_PATH", TEST_BINARY_PATH); + + VkApplicationInfo app_info{GetDefaultApplicationInfo()}; + + const VkLayerSettingsCreateInfoEXT layer_settings_create_info{VK_STRUCTURE_TYPE_LAYER_SETTINGS_CREATE_INFO_EXT, nullptr, + settings.empty() ? 0 : static_cast(settings.size()), + settings.empty() ? nullptr : &settings[0]}; + + VkInstanceCreateInfo inst_create_info = {}; + inst_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + inst_create_info.pApplicationInfo = &app_info; + inst_create_info.pNext = &layer_settings_create_info; + inst_create_info.enabledLayerCount = static_cast(_layer_names.size()); + inst_create_info.ppEnabledLayerNames = _layer_names.data(); + inst_create_info.enabledExtensionCount = _extension_names.empty() ? 0 : static_cast(_extension_names.size()); + inst_create_info.ppEnabledExtensionNames = _extension_names.empty() ? nullptr : _extension_names.data(); + + return vkCreateInstance(&inst_create_info, nullptr, &_instances); +} + +void layer_test::VulkanInstanceBuilder::Reset() { + if (_instances != VK_NULL_HANDLE) { + vkDestroyInstance(_instances, nullptr); + _instances = VK_NULL_HANDLE; + } + + _layer_names.clear(); + _extension_names.clear(); +} + +bool layer_test::IsExtensionSupported(VkPhysicalDevice physical_device, const char* extension_name) { + uint32_t property_count = 0; + vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &property_count, nullptr); + if (property_count == 0) return false; + + std::vector properties(static_cast(property_count)); + vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &property_count, &properties[0]); + + for (std::size_t i = 0, n = properties.size(); i < n; ++i) { + if (strcmp(properties[i].extensionName, extension_name) == 0) { + return true; + } + } + + return false; +} diff --git a/layersvt/test/layer_test_helper.h b/layersvt/test/layer_test_helper.h new file mode 100644 index 0000000000..6dd20f4307 --- /dev/null +++ b/layersvt/test/layer_test_helper.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#pragma once + +#include "layer_test_framework.h" + +#include + +#include + +#include +#include +#include +#include +#include + +namespace layer_test { + +inline 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; +} + +void SetEnvironmentSetting(std::string setting, const char* val); +void UnsetEnvironmentSetting(std::string setting); + +std::string GetAbsolutePath(std::string filepath); + +VkApplicationInfo GetDefaultApplicationInfo(); + +bool IsExtensionSupported(VkPhysicalDevice physical_device, const char* extension_name); + +class VulkanInstanceBuilder { + public: + ~VulkanInstanceBuilder() { this->Reset(); } + + void AddExtension(const char* extension_name) { _extension_names.push_back(extension_name); } + + VkResult Init(const char* layer_name); + VkResult Init(const std::vector& settings); + VkResult GetPhysicalDevice(VkPhysicalDevice* phys_dev); + + void Reset(); + + VkInstance GetInstance() { return _instances; } + + protected: + VkInstance _instances; + + std::vector _layer_names; + std::vector _extension_names; +}; + +} // namespace layer_test diff --git a/layersvt/test/layer_test_main.cpp b/layersvt/test/layer_test_main.cpp new file mode 100644 index 0000000000..bc3383b6b2 --- /dev/null +++ b/layersvt/test/layer_test_main.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Ricico + */ + +#include + +#ifdef _WIN32 +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#endif + +#if defined(_WIN32) && !defined(NDEBUG) +#include +#endif + +int main(int argc, char **argv) { + int result; + +#if defined(_WIN32) +#if !defined(NDEBUG) + _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); +#endif + // Avoid "Abort, Retry, Ignore" dialog boxes + _set_abort_behavior(0, _WRITE_ABORT_MSG | _CALL_REPORTFAULT); + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); + _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); +#endif + ::testing::InitGoogleTest(&argc, argv); + + result = RUN_ALL_TESTS(); + + return result; +} diff --git a/layersvt/test/test_api_dump.cpp b/layersvt/test/test_api_dump.cpp new file mode 100644 index 0000000000..118525bfec --- /dev/null +++ b/layersvt/test/test_api_dump.cpp @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#include "layer_test_helper.h" + +#include +#include + +#include + +#include + +static const char* kLayerName = "VK_LAYER_LUNARG_api_dump"; + +class ApiDumpTests : public VkTestFramework { + public: + ApiDumpTests(){}; + ~ApiDumpTests(){}; + + static void SetUpTestSuite() {} + static void TearDownTestSuite(){}; +}; + +TEST_F(ApiDumpTests, init_layer) { + TEST_DESCRIPTION("Test Creating a Vulkan Instance with a layer"); + + VkBool32 file = VK_TRUE; + const char* filename_string = "api_dump_output.html"; + const char* output_format = "html"; + + const std::vector settings = { + {kLayerName, "file", VK_LAYER_SETTING_TYPE_BOOL32_EXT, 1, &file}, + {kLayerName, "log_filename", VK_LAYER_SETTING_TYPE_STRING_EXT, 1, &filename_string}, + {kLayerName, "output_format", VK_LAYER_SETTING_TYPE_STRING_EXT, 1, &output_format}}; + + layer_test::VulkanInstanceBuilder inst_builder; + VkResult err = inst_builder.Init(settings); + EXPECT_EQ(err, VK_SUCCESS); +} diff --git a/layersvt/test/test_monitor.cpp b/layersvt/test/test_monitor.cpp new file mode 100644 index 0000000000..223b8ba261 --- /dev/null +++ b/layersvt/test/test_monitor.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#include +#include + +#include +#include "layer_test_helper.h" + +#include + +static const char* kLayerName = "VK_LAYER_LUNARG_monitor"; + +class MonitorTests : public VkTestFramework { + public: + MonitorTests(){}; + ~MonitorTests(){}; + + static void SetUpTestSuite() {} + static void TearDownTestSuite(){}; +}; + +TEST_F(MonitorTests, init_layer) { + TEST_DESCRIPTION("Test Creating a Vulkan Instance with a layer"); + + layer_test::VulkanInstanceBuilder inst_builder; + VkResult err = inst_builder.Init(kLayerName); + EXPECT_EQ(err, VK_SUCCESS); +} diff --git a/layersvt/test/test_screenshot.cpp b/layersvt/test/test_screenshot.cpp new file mode 100644 index 0000000000..6665357737 --- /dev/null +++ b/layersvt/test/test_screenshot.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2023 The Khronos Group Inc. + * Copyright (c) 2023 Valve Corporation + * Copyright (c) 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 + * + * Author: Christophe Riccio + */ + +#include +#include + +#include +#include "layer_test_helper.h" + +#include + +static const char* kLayerName = "VK_LAYER_LUNARG_screenshot"; + +class ScreenshotTests : public VkTestFramework { + public: + ScreenshotTests(){}; + ~ScreenshotTests(){}; + + static void SetUpTestSuite() {} + static void TearDownTestSuite(){}; +}; + +TEST_F(ScreenshotTests, init_layer) { + TEST_DESCRIPTION("Test Creating a Vulkan Instance with a layer"); + + VkBool32 file = VK_TRUE; + const char* frames = "8-2"; + const char* format = "UNORM"; + + const std::vector settings = {{kLayerName, "frames", VK_LAYER_SETTING_TYPE_STRING_EXT, 1, &frames}, + {kLayerName, "format", VK_LAYER_SETTING_TYPE_STRING_EXT, 1, &format}}; + + layer_test::VulkanInstanceBuilder inst_builder; + VkResult err = inst_builder.Init(settings); + EXPECT_EQ(err, VK_SUCCESS); +} diff --git a/scripts/api_dump_generator.py b/scripts/api_dump_generator.py index c7e18bdd7d..f8b207b7f0 100644 --- a/scripts/api_dump_generator.py +++ b/scripts/api_dump_generator.py @@ -115,6 +115,9 @@ if(result == VK_SUCCESS) {{ initInstanceTable(*pInstance, fpGetInstanceProcAddr); }} + + ApiDumpInstance::current().initLayerSettings(pCreateInfo, pAllocator); + // Output the API dump if (ApiDumpInstance::current().shouldDumpOutput()) {{ switch(ApiDumpInstance::current().settings().format()) diff --git a/scripts/known_good.json b/scripts/known_good.json index ffd1959d1d..4174f39be7 100644 --- a/scripts/known_good.json +++ b/scripts/known_good.json @@ -14,7 +14,7 @@ "sub_dir": "Vulkan-Utility-Libraries", "build_dir": "Vulkan-Utility-Libraries/build", "install_dir": "Vulkan-Utility-Libraries/build/install", - "commit": "87801a6c479399889eea37069f83437d0ea9458e", + "commit": "dca404674dfb987751c3bbba3237e3d6cda55751", "deps": [ { "var_name": "VULKAN_HEADERS_INSTALL_DIR",