/* Copyright (c) 2020-2026 The Khronos Group Inc.
 * Copyright (c) 2020-2026 Valve Corporation
 * Copyright (c) 2020-2026 LunarG, Inc.
 * Copyright (c) 2026 RasterGrid Kft.
 * Modifications Copyright (C) 2020 Advanced Micro Devices, Inc. All rights reserved.
 *
 * 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 "layer_options.h"
#include <vulkan/vulkan_core.h>
#include "chassis/validation_object.h"
#include "containers/span.h"
#include "error_message/log_message_type.h"
#include "error_message/logging.h"
#include "error_message/error_location.h"
#include "generated/error_location_helper.h"
#include "generated/vk_validation_error_messages.h"
#include "utils/hash_util.h"
#include <cstring>
#include <string>
#include <vector>
#include <vulkan/layer/vk_layer_settings.hpp>

#include "gpuav/core/gpuav_settings.h"
#include "sync/sync_settings.h"
#include "gpu_dump/gpu_dump_settings.h"
#include "legacy/legacy_settings.h"

#include "utils/math_utils.h"
#include "vk_layer_config.h"

// Include new / delete overrides if using mimalloc. This needs to be include exactly once in a file that is
// part of the VVL but not the layer utils library.
#if defined(USE_MIMALLOC) && defined(_WIN64)
#include "mimalloc-new-delete.h"
#endif

const auto& VkValFeatureDisableLookup() {
    static const vvl::unordered_map<std::string, VkValidationFeatureDisableEXT> vk_val_feature_disable_lookup = {
        {"VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT", VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT", VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT", VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT", VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT", VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT", VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT", VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT},
        {"VK_VALIDATION_FEATURE_DISABLE_ALL_EXT", VK_VALIDATION_FEATURE_DISABLE_ALL_EXT},
    };
    return vk_val_feature_disable_lookup;
}

const auto& VkValFeatureEnableLookup() {
    static const vvl::unordered_map<std::string, VkValidationFeatureEnableEXT> vk_val_feature_enable_lookup = {
        {"VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT", VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT},
        {"VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT",
         VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT},
        {"VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT", VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT},
        {"VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT", VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT},
        {"VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT",
         VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT},
    };
    return vk_val_feature_enable_lookup;
}

const auto& ValidationDisableLookup() {
    static const vvl::unordered_map<std::string, ValidationCheckDisables> validation_disable_lookup = {
        {"VALIDATION_CHECK_DISABLE_COMMAND_BUFFER_STATE", VALIDATION_CHECK_DISABLE_COMMAND_BUFFER_STATE},
        {"VALIDATION_CHECK_DISABLE_OBJECT_IN_USE", VALIDATION_CHECK_DISABLE_OBJECT_IN_USE},
        {"VALIDATION_CHECK_DISABLE_QUERY_VALIDATION", VALIDATION_CHECK_DISABLE_QUERY_VALIDATION},
        {"VALIDATION_CHECK_DISABLE_IMAGE_LAYOUT_VALIDATION", VALIDATION_CHECK_DISABLE_IMAGE_LAYOUT_VALIDATION},
    };
    return validation_disable_lookup;
}

const auto& ValidationEnableLookup() {
    static const vvl::unordered_map<std::string, ValidationCheckEnables> validation_enable_lookup = {
        {"VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ARM", VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ARM},
        {"VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD", VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD},
        {"VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_IMG", VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_IMG},
        {"VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA", VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA},
        {"VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ALL", VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ALL},
    };
    return validation_enable_lookup;
}

// To enable "my_setting",
// Set env var VK_LAYER_MY_SETTING to 1
//
// The ["VK_LAYER_" + toUpper(key)] logic is don in vk_layer_setting (VUL)
//
// To quickly be able to find the env var corresponding to a setting,
// the following `const char*` holding setting names match their corresponding environment variable

// Corresponding to VkValidationFeatureEnableEXT
// ---
const char* VK_LAYER_ENABLES = "enables";
const char* VK_LAYER_VALIDATE_BEST_PRACTICES = "validate_best_practices";
const char* VK_LAYER_VALIDATE_BEST_PRACTICES_ARM = "validate_best_practices_arm";
const char* VK_LAYER_VALIDATE_BEST_PRACTICES_AMD = "validate_best_practices_amd";
const char* VK_LAYER_VALIDATE_BEST_PRACTICES_IMG = "validate_best_practices_img";
const char* VK_LAYER_VALIDATE_BEST_PRACTICES_NVIDIA = "validate_best_practices_nvidia";
const char* VK_LAYER_VALIDATE_SYNC = "validate_sync";

// Corresponding to VkValidationFeatureDisableEXT
// ---
const char* VK_LAYER_DISABLES = "disables";
const char* VK_LAYER_CHECK_SHADERS = "check_shaders";
const char* VK_LAYER_THREAD_SAFETY = "thread_safety";
const char* VK_LAYER_STATELESS_PARAM = "stateless_param";
const char* VK_LAYER_OBJECT_LIFETIME = "object_lifetime";
const char* VK_LAYER_VALIDATE_CORE = "validate_core";
const char* VK_LAYER_UNIQUE_HANDLES = "unique_handles";
const char* VK_LAYER_CHECK_SHADERS_CACHING = "check_shaders_caching";

// Additional checks exposed in vkconfig, but not in VkValidationFeatureDisableEXT
// ---
// [DEPRECATED]
// After the 1.4.350 SDK we found these were 10 years old and not sure if valuable anymore
// These have not been tested and seem no one is properly checking they are used for newer extensions
// Plan is to remove them, unless we find people are indeed using them
const char* VK_LAYER_CHECK_COMMAND_BUFFER = "check_command_buffer";
const char* VK_LAYER_CHECK_OBJECT_IN_USE = "check_object_in_use";
const char* VK_LAYER_CHECK_QUERY = "check_query";
const char* VK_LAYER_CHECK_IMAGE_LAYOUT = "check_image_layout";

// Options related to debug reporting
// ---
const char* VK_LAYER_MESSAGE_ID_FILTER = "message_id_filter";
const char* VK_LAYER_CUSTOM_STYPE_LIST = "custom_stype_list";
const char* VK_LAYER_ENABLE_MESSAGE_LIMIT = "enable_message_limit";
const char* VK_LAYER_DUPLICATE_MESSAGE_LIMIT = "duplicate_message_limit";

// Global settings
// ---
// Debug settings used for internal development
const char* VK_LAYER_DEBUG_DISABLE_SPIRV_VAL = "debug_disable_spirv_val";
// Used both with GPU-AV and GPU Dump
const char* VK_LAYER_DESCRIPTOR_HASHING = "descriptor_hashing";
const char* VK_LAYER_DESCRIPTOR_HASHING_TOTAL_DESCRIPTORS = "descriptor_hashing_total_descriptors";

// DebugPrintf (which is now part of GPU-AV internally)
// ---
// Quick, single setting to turn on DebugPrintf
const char* VK_LAYER_PRINTF_ONLY_PRESET = "printf_only_preset";
// Was added a new way to set things without having to use VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT
const char* VK_LAYER_PRINTF_ENABLE = "printf_enable";
const char* VK_LAYER_PRINTF_TO_STDOUT = "printf_to_stdout";
const char* VK_LAYER_PRINTF_VERBOSE = "printf_verbose";
const char* VK_LAYER_PRINTF_BUFFER_SIZE = "printf_buffer_size";

// GPU-AV
// ---
const char* VK_LAYER_GPUAV_ENABLE = "gpuav_enable";
const char* VK_LAYER_GPUAV_SAFE_MODE = "gpuav_safe_mode";
const char* VK_LAYER_GPUAV_SHADER_INSTRUMENTATION = "gpuav_shader_instrumentation";
const char* VK_LAYER_GPUAV_DESCRIPTOR_CHECKS = "gpuav_descriptor_checks";
const char* VK_LAYER_GPUAV_BUFFER_ADDRESS_OOB = "gpuav_buffer_address_oob";
const char* VK_LAYER_GPUAV_VALIDATE_TRACE_RAY = "gpuav_validate_trace_ray";
const char* VK_LAYER_GPUAV_MESH_SHADING = "gpuav_mesh_shading";
const char* VK_LAYER_GPUAV_POST_PROCESS_DESCRIPTOR_INDEXING = "gpuav_post_process_descriptor_indexing";
const char* VK_LAYER_GPUAV_VERTEX_ATTRIBUTE_FETCH_OOB = "gpuav_vertex_attribute_fetch_oob";
const char* VK_LAYER_GPUAV_SHADER_SANITIZER = "gpuav_shader_sanitizer";
const char* VK_LAYER_GPUAV_SHARED_MEMORY_DATA_RACE = "gpuav_shared_memory_data_race";
const char* VK_LAYER_GPUAV_MAX_INDICES_COUNT = "gpuav_max_indices_count";
const char* VK_LAYER_GPUAV_SELECT_INSTRUMENTED_SHADERS = "gpuav_select_instrumented_shaders";
const char* VK_LAYER_GPUAV_SHADERS_TO_INSTRUMENT = "gpuav_shaders_to_instrument";
const char* VK_LAYER_GPUAV_CDL_DUMP_PATH = "gpuav_cdl_dump_path";
// This was removed right after 1.4.350 SDK
const char* REMOVED_VK_LAYER_GPUAV_VALIDATE_RAY_QUERY = "gpuav_validate_ray_query";

const char* VK_LAYER_GPUAV_BUFFERS_VALIDATION = "gpuav_buffers_validation";
const char* VK_LAYER_GPUAV_INDIRECT_DRAWS_BUFFERS = "gpuav_indirect_draws_buffers";
const char* VK_LAYER_GPUAV_INDIRECT_DISPATCHES_BUFFERS = "gpuav_indirect_dispatches_buffers";
const char* VK_LAYER_GPUAV_INDIRECT_TRACE_RAYS_BUFFERS = "gpuav_indirect_trace_rays_buffers";
const char* VK_LAYER_GPUAV_BUFFER_COPIES = "gpuav_buffer_copies";
const char* VK_LAYER_GPUAV_COPY_MEMORY_INDIRECT = "gpuav_copy_memory_indirect";
const char* VK_LAYER_GPUAV_INDEX_BUFFERS = "gpuav_index_buffers";
const char* VK_LAYER_GPUAV_ACCELERATION_STRUCTURES_BUILDS = "gpuav_acceleration_structures_builds";
const char* VK_LAYER_GPUAV_RAY_TRACING_BUFFERS_CONSISTENCY = "gpuav_ray_tracing_buffers_consistency";

const char* VK_LAYER_GPUAV_FORCE_ON_ROBUSTNESS = "gpuav_force_on_robustness";

const char* VK_LAYER_GPUAV_DEBUG_DISABLE_ALL = "gpuav_debug_disable_all";
const char* VK_LAYER_GPUAV_DEBUG_VALIDATE_INSTRUMENTED_SHADERS = "gpuav_debug_validate_instrumented_shaders";
const char* VK_LAYER_GPUAV_DEBUG_DUMP_INSTRUMENTED_SHADERS = "gpuav_debug_dump_instrumented_shaders";
const char* VK_LAYER_GPUAV_DEBUG_MAX_INSTRUMENTATIONS_COUNT = "gpuav_debug_max_instrumentations_count";
const char* VK_LAYER_GPUAV_DEBUG_PRINT_INSTRUMENTATION_INFO = "gpuav_debug_print_instrumentation_info";
const char* VK_LAYER_GPUAV_DEBUG_DISABLE_DONTINLINE = "gpuav_debug_disable_dontinline";

// SyncVal
// ---
const char* VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION = "syncval_submit_time_validation";
const char* VK_LAYER_SYNCVAL_SHADER_ACCESSES_HEURISTIC = "syncval_shader_accesses_heuristic";
const char* VK_LAYER_SYNCVAL_LOAD_OP_AFTER_STORE_OP_VALIDATION = "syncval_load_op_after_store_op_validation";
const char* VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES = "syncval_message_extra_properties";

// Message Formatting
// ---
const char* VK_LAYER_MESSAGE_FORMAT_JSON = "message_format_json";
const char* VK_LAYER_MESSAGE_FORMAT_DISPLAY_APPLICATION_NAME = "message_format_display_application_name";
// Until post 1.3.290 SDK release, these were not possible to set via environment variables
const char* VK_LAYER_LOG_FILENAME = "log_filename";
const char* VK_LAYER_DEBUG_ACTION = "debug_action";
const char* VK_LAYER_REPORT_FLAGS = "report_flags";

// GPU Dump
// ---
// Used for working with VK_EXT_descriptor_buffer/VK_EXT_descriptor_heap
// Every draw/dispatch/traceRays dump out information about the bound descriptor buffer/heap
const char* VK_LAYER_GPU_DUMP_DESCRIPTORS = "gpu_dump_descriptors";
const char* VK_LAYER_GPU_DUMP_COPY_MEMORY_INDIRECT = "gpu_dump_copy_memory_indirect";
const char* VK_LAYER_GPU_DUMP_DEVICE_GENERATED_COMMANDS = "gpu_dump_device_generated_commands";
// Print to stdout
const char* VK_LAYER_GPU_DUMP_TO_STDOUT = "gpu_dump_to_stdout";

// Legacy Detection
// ---
const char* VK_LAYER_LEGACY_DETECTION = "legacy_detection";
const char* VK_LAYER_LEGACY_DETECTION_MODE = "legacy_detection_mode";

// Don't need any setting helper when using self vvl and don't want unused function warnings
#if !defined(BUILD_SELF_VVL)

// Set the local disable flag for the appropriate VALIDATION_CHECK_DISABLE enum
void SetValidationDisable(ValidationDisabled& disable_data, const ValidationCheckDisables disable_id) {
    switch (disable_id) {
        case VALIDATION_CHECK_DISABLE_COMMAND_BUFFER_STATE:
            disable_data[command_buffer_state] = true;
            break;
        case VALIDATION_CHECK_DISABLE_OBJECT_IN_USE:
            disable_data[object_in_use] = true;
            break;
        case VALIDATION_CHECK_DISABLE_QUERY_VALIDATION:
            disable_data[query_validation] = true;
            break;
        case VALIDATION_CHECK_DISABLE_IMAGE_LAYOUT_VALIDATION:
            disable_data[image_layout_validation] = true;
            break;
        default:
            assert(false);
    }
}

// Set the local disable flag for a single VK_VALIDATION_FEATURE_DISABLE_* flag
void SetValidationFeatureDisable(ValidationDisabled& disable_data, const VkValidationFeatureDisableEXT feature_disable) {
    switch (feature_disable) {
        case VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT:
            disable_data[shader_validation] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT:
            disable_data[thread_safety] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT:
            disable_data[stateless_checks] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT:
            disable_data[object_tracking] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT:
            disable_data[core_checks] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT:
            disable_data[handle_wrapping] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT:
            disable_data[shader_validation_caching] = true;
            break;
        case VK_VALIDATION_FEATURE_DISABLE_ALL_EXT:
            // Set all disabled flags to true
            std::fill(disable_data.begin(), disable_data.end(), true);
            break;
        default:
            break;
    }
}

// Set the local enable flag for the appropriate VALIDATION_CHECK_ENABLE enum
void SetValidationEnable(ValidationEnabled& enable_data, const ValidationCheckEnables enable_id) {
    switch (enable_id) {
        case VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ARM:
            enable_data[vendor_specific_arm] = true;
            break;
        case VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD:
            enable_data[vendor_specific_amd] = true;
            break;
        case VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_IMG:
            enable_data[vendor_specific_img] = true;
            break;
        case VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA:
            enable_data[vendor_specific_nvidia] = true;
            break;
        case VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ALL:
            enable_data[vendor_specific_arm] = true;
            enable_data[vendor_specific_amd] = true;
            enable_data[vendor_specific_img] = true;
            enable_data[vendor_specific_nvidia] = true;
            break;
        default:
            assert(false);
    }
}

// Set the local enable flag for a single VK_VALIDATION_FEATURE_ENABLE_* flag
void SetValidationFeatureEnable(ValidationEnabled& enable_data, const VkValidationFeatureEnableEXT feature_enable) {
    switch (feature_enable) {
        case VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT:
            enable_data[gpu_validation] = true;
            break;
        case VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT:
            enable_data[gpu_validation_reserve_binding_slot] = true;
            break;
        case VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT:
            enable_data[best_practices] = true;
            break;
        case VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT:
            enable_data[debug_printf_validation] = true;
            break;
        case VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT:
            enable_data[sync_validation] = true;
            break;
        default:
            break;
    }
}

// Set the local disable flag for settings specified through the VK_EXT_validation_flags extension
void SetValidationFlags(ValidationDisabled& disabled, const VkValidationFlagsEXT* val_flags_struct) {
    for (uint32_t i = 0; i < val_flags_struct->disabledValidationCheckCount; ++i) {
        switch (val_flags_struct->pDisabledValidationChecks[i]) {
            case VK_VALIDATION_CHECK_SHADERS_EXT:
                disabled[shader_validation] = true;
                break;
            case VK_VALIDATION_CHECK_ALL_EXT:
                // Set all disabled flags to true
                disabled[shader_validation] = true;
                break;
            default:
                break;
        }
    }
}

// Process Validation Features flags specified through the ValidationFeature extension
void SetValidationFeatures(ValidationDisabled& disable_data, ValidationEnabled& enable_data,
                           const VkValidationFeaturesEXT* val_features_struct) {
    for (uint32_t i = 0; i < val_features_struct->disabledValidationFeatureCount; ++i) {
        SetValidationFeatureDisable(disable_data, val_features_struct->pDisabledValidationFeatures[i]);
    }
    for (uint32_t i = 0; i < val_features_struct->enabledValidationFeatureCount; ++i) {
        SetValidationFeatureEnable(enable_data, val_features_struct->pEnabledValidationFeatures[i]);
    }
}

std::string GetNextToken(std::string* token_list, const std::string& delimiter, size_t* pos) {
    std::string token;
    *pos = token_list->find(delimiter);
    if (*pos != std::string::npos) {
        token = token_list->substr(0, *pos);
    } else {
        *pos = token_list->length() - delimiter.length();
        token = *token_list;
    }
    token_list->erase(0, *pos + delimiter.length());

    // Remove quotes from quoted strings
    if ((token.length() > 0) && (token[0] == '\"')) {
        token.erase(token.begin());
        if ((token.length() > 0) && (token[token.length() - 1] == '\"')) {
            token.erase(--token.end());
        }
    }
    return token;
}

// Given a string representation of a list of enable enum values, call the appropriate setter function
bool SetLocalEnableSetting(std::string list_of_enabled, const std::string& delimiter, ValidationEnabled& enabled) {
    bool used = false;
    size_t pos = 0;
    std::string token;
    while (list_of_enabled.length() != 0) {
        token = GetNextToken(&list_of_enabled, delimiter, &pos);
        if (token.find("VK_VALIDATION_FEATURE_ENABLE_") != std::string::npos) {
            auto result = VkValFeatureEnableLookup().find(token);
            if (result != VkValFeatureEnableLookup().end()) {
                used = true;
                SetValidationFeatureEnable(enabled, result->second);
            }
        } else if (token.find("VALIDATION_CHECK_ENABLE_") != std::string::npos) {
            auto result = ValidationEnableLookup().find(token);
            if (result != ValidationEnableLookup().end()) {
                used = true;
                SetValidationEnable(enabled, result->second);
            }
        }
    }
    return used;
}

// Given a string representation of a list of disable enum values, call the appropriate setter function
bool SetLocalDisableSetting(std::string list_of_disabled, const std::string& delimiter, ValidationDisabled& disabled) {
    bool used = false;
    size_t pos = 0;
    std::string token;
    while (list_of_disabled.length() != 0) {
        token = GetNextToken(&list_of_disabled, delimiter, &pos);
        if (token.find("VK_VALIDATION_FEATURE_DISABLE_") != std::string::npos) {
            auto result = VkValFeatureDisableLookup().find(token);
            if (result != VkValFeatureDisableLookup().end()) {
                SetValidationFeatureDisable(disabled, result->second);
                used = true;
            }
        } else if (token.find("VALIDATION_CHECK_DISABLE_") != std::string::npos) {
            auto result = ValidationDisableLookup().find(token);
            if (result != ValidationDisableLookup().end()) {
                SetValidationDisable(disabled, result->second);
                used = true;
            }
        }
    }
    return used;
}

uint32_t TokenToUint(const std::string& token) {
    uint32_t int_id = 0;
    if ((token.find("0x") == 0) || token.find("0X") == 0) {  // Handle hex format
        int_id = static_cast<uint32_t>(std::strtoul(token.c_str(), nullptr, 16));
    } else {
        int_id = static_cast<uint32_t>(std::strtoul(token.c_str(), nullptr, 10));  // Decimal format
    }
    return int_id;
}

void CreateFilterMessageIdList(const std::vector<std::string>& message_id_filter, vvl::unordered_set<uint32_t>& filter_list) {
    for (const std::string& message_id : message_id_filter) {
        // There are 3 forms VUID can be filtered
        // 1. The VUID hash is provided (can be in hex or dec format)
        // 2. Pass in the entire VUID string so we can hash it
        // 3. Pass in the VUID final 5 digits and we "glob" all VUs that match
        //    This mainly is helpful for draw VUs where we have 40 variations
        uint32_t filter_id = TokenToUint(message_id);
        // Will be non-zero if hash was provided
        if (filter_id == 0) {
            filter_id = hash_util::VuidHash(message_id);
            if (filter_id != 0) {
                filter_list.insert(filter_id);  // option 2
            }
        } else if (message_id.length() == 5) {
            // User provided the 5 digit, search the VUID list
            const vvl::unordered_map<std::string_view, vuid_info> vuid_map = GetVuidMap();
            for (const auto& [key, info] : vuid_map) {
                std::string_view vuid_digit = key.substr(key.length() - 5);
                if (message_id == vuid_digit) {
                    filter_id = hash_util::VuidHash(key);
                    if (filter_id != 0) {
                        filter_list.insert(filter_id);  // option 3
                    }
                }
            }
        } else {
            filter_list.insert(filter_id);  // option 1
        }
    }
}

// Because VkLayerSettingsCreateInfoEXT/VkLayerSettingEXT are passed in and used before everything else, need to do the stateless
// validation here as a special exception
//
// We want to use the DebugReport object to print VU error messages in here, but we need to parse the settings in
// VkLayerSettingsCreateInfoEXT in order to configure the DebugReport object. Therefor we just settle with printf in here. These are
// very unlikely things to hit validation layers messages (as everything after will likely crumble) so should be ok.
//
// Returns if valid
static bool ValidateLayerSettingsCreateInfo(const VkLayerSettingsCreateInfoEXT* layer_settings) {
    bool valid = true;
    if (!layer_settings) return valid;
    const Location loc(vvl::Func::vkCreateInstance, vvl::Field::pCreateInfo);
    const Location create_info_loc = loc.pNext(vvl::Struct::VkLayerSettingsCreateInfoEXT);
    std::ostringstream ss;

    if (layer_settings->pSettings) {
        for (const auto [i, setting] : vvl::enumerate(layer_settings->pSettings, layer_settings->settingCount)) {
            const Location setting_loc = create_info_loc.dot(vvl::Field::pSettings, i);
            if (setting.valueCount > 0 && !setting.pValues) {
                ss << "[ VUID-VkLayerSettingEXT-valueCount-10070 ] " << setting_loc.dot(vvl::Field::pValues).Message()
                   << " is NULL";
                printf("Validation Layer Error: %s\n", ss.str().c_str());
                valid = false;
            }
            if (!setting.pLayerName) {
                ss << "[ VUID-VkLayerSettingEXT-pLayerName-parameter ] " << setting_loc.dot(vvl::Field::pLayerName).Message()
                   << " is NULL";
                printf("Validation Layer Error: %s\n", ss.str().c_str());
                valid = false;
            }
            if (!setting.pSettingName) {
                ss << "[ VUID-VkLayerSettingEXT-pSettingName-parameter ] " << setting_loc.dot(vvl::Field::pSettingName).Message()
                   << " is NULL";
                printf("Validation Layer Error: %s\n", ss.str().c_str());
                valid = false;
            }
            // Can assume no new VkLayerSettingTypeEXT will be added
            const uint32_t casted_type = (uint32_t)setting.type;
            const uint32_t last_setting_type = (uint32_t)VK_LAYER_SETTING_TYPE_STRING_EXT;
            if (casted_type > last_setting_type) {
                ss << "[ VUID-VkLayerSettingEXT-type-parameter ] " << setting_loc.dot(vvl::Field::type).Message() << " is "
                   << casted_type << " which is not a valid VkLayerSettingTypeEXT enum value";
                printf("Validation Layer Error: %s\n", ss.str().c_str());
                valid = false;
            }
        }
    } else if (layer_settings->settingCount > 0) {
        ss << "[ VUID-VkLayerSettingsCreateInfoEXT-pSettings-parameter ] " << create_info_loc.dot(vvl::Field::pSettings).Message()
           << " is NULL";
        printf("Validation Layer Error: %s\n", ss.str().c_str());
        valid = false;
    }
    return valid;
}

static void SetValidationSetting(VkuLayerSettingSet layer_setting_set, ValidationDisabled& disable_data,
                                 const DisableFlags feature_disable, const char* setting) {
    if (vkuHasLayerSetting(layer_setting_set, setting)) {
        bool enabled = true;
        vkuGetLayerSettingValue(layer_setting_set, setting, enabled);
        disable_data[feature_disable] = !enabled;
    }
}

static void SetValidationSetting(VkuLayerSettingSet layer_setting_set, ValidationEnabled& enable_data,
                                 const EnableFlags feature_enable, const char* setting) {
    if (vkuHasLayerSetting(layer_setting_set, setting)) {
        bool enabled = true;
        vkuGetLayerSettingValue(layer_setting_set, setting, enabled);
        enable_data[feature_enable] = enabled;
    }
}

static std::string Merge(const std::vector<std::string>& strings) {
    std::string result;

    for (std::size_t i = 0, n = strings.size(); i < n; ++i) {
        if (!result.empty()) {
            result += ",";
        }
        result += strings[i];
    }

    return result;
}

// If log_filename is NULL or stdout, return stdout, otherwise try to open log_filename
// as a filename. If successful, return file handle, otherwise stdout
FILE* GetLayerLogOutput(const char* log_filename, std::vector<std::string>& setting_warnings) {
    FILE* log_output = NULL;
    if (!log_filename || !strcmp("stdout", log_filename)) {
        log_output = stdout;
    } else {
        log_output = fopen(log_filename, "w");
        if (log_output == NULL) {
            if (log_filename) {
                setting_warnings.emplace_back("log_filename (" + std::string(log_filename) +
                                              ") could not be opened, falling back to stdout instead.");
            }
            log_output = stdout;
        }
    }
    return log_output;
}

// Definitions for Debug Actions
enum VkLayerDbgActionBits {
    VK_DBG_LAYER_ACTION_IGNORE = 0x00000000,
    VK_DBG_LAYER_ACTION_CALLBACK = 0x00000001,
    VK_DBG_LAYER_ACTION_LOG_MSG = 0x00000002,
    VK_DBG_LAYER_ACTION_BREAK = 0x00000004,
    VK_DBG_LAYER_ACTION_DEBUG_OUTPUT = 0x00000008,
    VK_DBG_LAYER_ACTION_FAIL = 0x00000010,
    VK_DBG_LAYER_ACTION_DEFAULT = 0x40000000,
};
using VkLayerDbgActionFlags = VkFlags;

static void ProcessDebugReportSettings(ConfigAndEnvSettings* settings_data, VkuLayerSettingSet& layer_setting_set,
                                       std::vector<std::string>& setting_warnings) {
    DebugReport* debug_report = settings_data->debug_report;
    // Message ID Filtering
    std::vector<std::string> message_id_filter;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_MESSAGE_ID_FILTER)) {
        vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_MESSAGE_ID_FILTER, message_id_filter);
    }
    CreateFilterMessageIdList(message_id_filter, debug_report->filter_message_ids);

    // Duplicate message limit
    bool enable_message_limit = true;       // default in JSON
    uint32_t duplicate_message_limit = 10;  // default in JSON
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_ENABLE_MESSAGE_LIMIT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_ENABLE_MESSAGE_LIMIT, enable_message_limit);
    }
    if (enable_message_limit) {
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DUPLICATE_MESSAGE_LIMIT)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_DUPLICATE_MESSAGE_LIMIT, duplicate_message_limit);
        }
    } else {
        duplicate_message_limit = 0;
    }
    debug_report->duplicate_message_limit = duplicate_message_limit;

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_MESSAGE_FORMAT_JSON)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_MESSAGE_FORMAT_JSON, debug_report->message_format_settings.json);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_MESSAGE_FORMAT_DISPLAY_APPLICATION_NAME)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_MESSAGE_FORMAT_DISPLAY_APPLICATION_NAME,
                                debug_report->message_format_settings.display_application_name);
    }

    std::string log_filename = "stdout";  // Default
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_LOG_FILENAME)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_LOG_FILENAME, log_filename);
    }
    const bool is_stdout = log_filename.compare("stdout") == 0;

    // Default
    std::vector<std::string> debug_actions_list = {"VK_DBG_LAYER_ACTION_DEFAULT", "VK_DBG_LAYER_ACTION_LOG_MSG"};
#ifdef _WIN32
    // For Windows, enable message logging AND OutputDebugString
    debug_actions_list.push_back("VK_DBG_LAYER_ACTION_DEBUG_OUTPUT");
#endif  // _WIN32

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DEBUG_ACTION)) {
        vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_DEBUG_ACTION, debug_actions_list);
    }

    VkLayerDbgActionFlags debug_action = 0;
    const vvl::unordered_map<std::string, VkFlags> debug_actions_option = {
        {std::string("VK_DBG_LAYER_ACTION_IGNORE"), VK_DBG_LAYER_ACTION_IGNORE},
        {std::string("VK_DBG_LAYER_ACTION_CALLBACK"), VK_DBG_LAYER_ACTION_CALLBACK},
        {std::string("VK_DBG_LAYER_ACTION_LOG_MSG"), VK_DBG_LAYER_ACTION_LOG_MSG},
        {std::string("VK_DBG_LAYER_ACTION_BREAK"), VK_DBG_LAYER_ACTION_BREAK},
        {std::string("VK_DBG_LAYER_ACTION_DEBUG_OUTPUT"), VK_DBG_LAYER_ACTION_DEBUG_OUTPUT},
        {std::string("VK_DBG_LAYER_ACTION_FAIL"), VK_DBG_LAYER_ACTION_FAIL},
        {std::string("VK_DBG_LAYER_ACTION_DEFAULT"), VK_DBG_LAYER_ACTION_DEFAULT}};
    for (const auto& element : debug_actions_list) {
        auto enum_value = debug_actions_option.find(element);
        if (enum_value != debug_actions_option.end()) {
            debug_action |= enum_value->second;
        } else {
            if (element.find(',') != std::string::npos) {
                setting_warnings.emplace_back("\"" + element +
                                              "\" was not a valid option for VK_LAYER_DEBUG_ACTION (ignoring).\nIf using "
                                              "VkLayerSettings, each string needs to be its own VkLayerSettingEXT::pValues.");
            } else {
                setting_warnings.emplace_back(
                    "\"" + element +
                    "\" was not a valid option for VK_LAYER_DEBUG_ACTION (ignoring).\nValid options are "
                    "[VK_DBG_LAYER_ACTION_IGNORE, VK_DBG_LAYER_ACTION_CALLBACK, VK_DBG_LAYER_ACTION_LOG_MSG, "
                    "VK_DBG_LAYER_ACTION_BREAK, VK_DBG_LAYER_ACTION_DEBUG_OUTPUT, VK_DBG_LAYER_ACTION_FAIL, "
                    "VK_DBG_LAYER_ACTION_DEFAULT]");
            }
        }
    }

    std::vector<std::string> report_flags_list = {"error", "warn"};  // Default
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_REPORT_FLAGS)) {
        vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_REPORT_FLAGS, report_flags_list);
    }

    VkLayerDbgActionFlags report_flags = 0;
    const vvl::unordered_map<std::string, VkFlags> report_flags_options = {{std::string("warn"), kWarningBit},
                                                                           {std::string("info"), kInformationBit},
                                                                           {std::string("perf"), kPerformanceWarningBit},
                                                                           {std::string("error"), kErrorBit},
                                                                           {std::string("verbose"), kVerboseBit}};
    for (const auto& element : report_flags_list) {
        auto enum_value = report_flags_options.find(element);
        if (enum_value != report_flags_options.end()) {
            report_flags |= enum_value->second;
        } else {
            if (element.find(',') != std::string::npos) {
                setting_warnings.emplace_back("\"" + element +
                                              "\" was not a valid option for VK_LAYER_REPORT_FLAGS (ignoring)\nIf using "
                                              "VkLayerSettings, each string needs to be its own VkLayerSettingEXT::pValues.");
            } else {
                setting_warnings.emplace_back("\"" + element +
                                              "\" was not a valid option for VK_LAYER_REPORT_FLAGS (ignoring)\nValid options are "
                                              "[error, warn, info, perf, verbose].");
            }
        }
    }

    // Before creating the debug callback, see if other settings interfere
    if (settings_data->gpuav_settings->debug_printf_enabled) {
        if (settings_data->gpuav_settings->debug_printf_to_stdout && (debug_action & VK_DBG_LAYER_ACTION_LOG_MSG)) {
            if (is_stdout) {
                setting_warnings.emplace_back(
                    "The debug callback is already logging to stdout, but " + std::string(VK_LAYER_PRINTF_TO_STDOUT) +
                    " is also enabled. DebugPrintf will skip the debug callback in favor of a direct stdout write.");
            } else {
                setting_warnings.emplace_back("The logging to " + log_filename + " will not contain any DebugPrintf info because " +
                                              std::string(VK_LAYER_PRINTF_TO_STDOUT) + " is enabled.");
            }
        }
        if (!settings_data->gpuav_settings->debug_printf_to_stdout && ((report_flags & kInformationBit) == 0)) {
            // Normally it is a lot of spam to use kInformationBit, but if only using DebugPrintf, it should be minimal information
            // printed
            setting_warnings.emplace_back(
                "DebugPrintf logs to the Information message severity, enabling Information level logging otherwise the message "
                "will not be seen.");
            report_flags |= kInformationBit;
        }
    }

    if (settings_data->enabled[legacy_detection] && ((report_flags & kWarningBit) == 0)) {
        setting_warnings.emplace_back(
            "Legacy Detection logs to the Warning message severity, enabling Warning level logging otherwise the message "
            "will not be seen.");
        report_flags |= kWarningBit;
    }

    if (settings_data->enabled[gpu_dump]) {
        const bool has_info_bit = (report_flags & kInformationBit) != 0;
        const bool has_warn_bit = (report_flags & kWarningBit) != 0;
        if (settings_data->gpu_dump_settings->to_stdout) {
            if ((debug_action & VK_DBG_LAYER_ACTION_LOG_MSG)) {
                if (is_stdout) {
                    setting_warnings.emplace_back(
                        "The debug callback is already logging to stdout, but " + std::string(VK_LAYER_GPU_DUMP_TO_STDOUT) +
                        " is also enabled. GPU Dump will skip the debug callback in favor of a direct stdout write.");
                } else {
                    setting_warnings.emplace_back("The logging to " + log_filename +
                                                  " will not contain any GPU Dump info because " +
                                                  std::string(VK_LAYER_GPU_DUMP_TO_STDOUT) + " is enabled.");
                }
            }
        } else if (!settings_data->gpu_dump_settings->to_stdout) {
            // If the user ONLY wants warning, don't turn on info
            // If they forgot both, turn both on
            if (!has_info_bit && !has_warn_bit) {
                setting_warnings.emplace_back(
                    "GPU Dump logs to both the Warning and Information message severity, enabling both Warning and Information "
                    "level logging otherwise the message "
                    "will not be seen.");
                report_flags |= kInformationBit;
                report_flags |= kWarningBit;
            }
        }

        // If the user has only info on to get information, that could be seen as a legit usecase
        if (has_warn_bit && settings_data->enabled[gpu_validation]) {
            setting_warnings.emplace_back(
                "GPU Dump and GPU-AV are both enabled and this is NOT desired. GPU Dump warnings are designed to find everything "
                "GPU-AV does but in a 'CPU best attempt' fashion. Only use GPU Dump or GPU-AV, not both together.");
        }
    }

    // Flag as default if these settings are not from a vk_layer_settings.txt file
    const bool default_layer_callback = (debug_action & VK_DBG_LAYER_ACTION_DEFAULT) != 0;

    VkDebugUtilsMessengerCreateInfoEXT dbg_create_info = vku::InitStructHelper();
    dbg_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
    if (report_flags & kErrorBit) {
        dbg_create_info.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
    }
    if (report_flags & kWarningBit) {
        dbg_create_info.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
    }
    if (report_flags & kPerformanceWarningBit) {
        dbg_create_info.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
        dbg_create_info.messageType |= VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
    }
    if (report_flags & kInformationBit) {
        dbg_create_info.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
    }
    if (report_flags & kVerboseBit) {
        dbg_create_info.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
    }
    settings_data->global_settings->only_report_errors = report_flags == kErrorBit;

    VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE;
    if (debug_action & VK_DBG_LAYER_ACTION_LOG_MSG) {
        FILE* log_output = GetLayerLogOutput(log_filename.c_str(), setting_warnings);
        if (log_output != stdout) {
            // This particular warning is designed to show the user where the debug callback is going (which is important to know!),
            // so it makes no sense to put the warning in the callback location. For this one only we attempt to print to the
            // everywhere else possible
            const std::string tmp = "Validation Layer Info - Logging validation error to " + log_filename + "\n";
            const char* cstr = tmp.c_str();
            printf("%s", cstr);
#ifdef VK_USE_PLATFORM_WIN32_KHR
            OutputDebugString(cstr);
#endif
#ifdef VK_USE_PLATFORM_ANDROID_KHR
            __android_log_print(ANDROID_LOG_INFO, "VALIDATION", "%s", cstr);
#endif
        }
        dbg_create_info.pfnUserCallback = MessengerLogCallback;
        dbg_create_info.pUserData = (void*)log_output;
        LayerCreateMessengerCallback(debug_report, default_layer_callback, &dbg_create_info, &messenger);
    } else if (!is_stdout) {
        setting_warnings.emplace_back("The log_filename was set to " + log_filename +
                                      " but VK_DBG_LAYER_ACTION_LOG_MSG was not set, so it won't be sent to the file.");
    }

#ifdef VK_USE_PLATFORM_WIN32_KHR
    messenger = VK_NULL_HANDLE;
    if (debug_action & VK_DBG_LAYER_ACTION_DEBUG_OUTPUT) {
        dbg_create_info.pfnUserCallback = MessengerWin32DebugOutputMsg;
        dbg_create_info.pUserData = nullptr;
        LayerCreateMessengerCallback(debug_report, default_layer_callback, &dbg_create_info, &messenger);
    }
#endif

    messenger = VK_NULL_HANDLE;
    if (debug_action & VK_DBG_LAYER_ACTION_BREAK) {
        dbg_create_info.pfnUserCallback = MessengerBreakCallback;
        dbg_create_info.pUserData = nullptr;
        LayerCreateMessengerCallback(debug_report, default_layer_callback, &dbg_create_info, &messenger);
    }

    messenger = VK_NULL_HANDLE;
    if (debug_action & VK_DBG_LAYER_ACTION_FAIL) {
        dbg_create_info.pfnUserCallback = MessengerFailCallback;
        dbg_create_info.pUserData = nullptr;
        LayerCreateMessengerCallback(debug_report, default_layer_callback, &dbg_create_info, &messenger);
    }
}

// Set as deprecated for the first time in the 1.4.335 SDK release
static std::string GetDeprecatedEnabledDisabledWarning(const std::vector<std::string>& enabled,
                                                       const std::vector<std::string>& disabled) {
    std::ostringstream ss;
    ss << "Application is using deprecated";
    if (!enabled.empty()) {
        ss << " \"enables\" (VK_LAYER_ENABLES)";
        if (!disabled.empty()) {
            ss << " and";
        }
    }
    if (!disabled.empty()) {
        ss << " \"disables\" (VK_LAYER_DISABLES)";
    }
    ss << " layer settings.\nDeprecated settings and new settings cannot be mixed, and deprecated ones take precedence. Consider "
          "only using the new settings:\n";

    // We tried to have a more clever way to do this, but was hitting strange compiler issues...
    // This for loop will NEVER grow, these are the only deprecated settings we have because they are baked into the Vulkan Spec
    for (const std::string& deprecated_string : enabled) {
        if (deprecated_string == "VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT | New: \"" << VK_LAYER_GPUAV_ENABLE
               << "\" (VK_LAYER_GPUAV_ENABLE=1)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT | New: \"" << VK_LAYER_VALIDATE_BEST_PRACTICES
               << "\" (VK_LAYER_VALIDATE_BEST_PRACTICES=1)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT | New: \"" << VK_LAYER_PRINTF_ENABLE
               << "\" (VK_LAYER_PRINTF_ENABLE=1)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT | New: \"" << VK_LAYER_VALIDATE_SYNC
               << "\" (VK_LAYER_VALIDATE_SYNC=1)\n";
        }
    }
    for (const std::string& deprecated_string : disabled) {
        if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT | New: \"" << VK_LAYER_CHECK_SHADERS
               << "\" (VK_LAYER_CHECK_SHADERS=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT | New: \"" << VK_LAYER_THREAD_SAFETY
               << "\" (VK_LAYER_THREAD_SAFETY=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT | New: \"" << VK_LAYER_STATELESS_PARAM
               << "\" (VK_LAYER_STATELESS_PARAM=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT | New: \"" << VK_LAYER_OBJECT_LIFETIME
               << "\" (VK_LAYER_OBJECT_LIFETIME=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT | New: \"" << VK_LAYER_VALIDATE_CORE
               << "\" (VK_LAYER_VALIDATE_CORE=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT | New: \"" << VK_LAYER_UNIQUE_HANDLES
               << "\" (VK_LAYER_UNIQUE_HANDLES=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT | New: \""
               << VK_LAYER_CHECK_SHADERS_CACHING << "\" (VK_LAYER_CHECK_SHADERS_CACHING=0)\n";
        } else if (deprecated_string == "VK_VALIDATION_FEATURE_DISABLE_ALL_EXT") {
            ss << "  Deprecated: VK_VALIDATION_FEATURE_DISABLE_ALL_EXT | Currently has no replacement, disabling the layer has the "
                  "same effects.\n";
        }
    }

    return ss.str();
}

static const char* GetDefaultPrefix() {
#ifdef __ANDROID__
    return "vvl";
#else
    return "LAYER";
#endif
}
#endif  // !defined(BUILD_SELF_VVL)

// This is a (less than desired) way to allow Statless validation tracking a setting
bool& HasCustomStypeInfo() {
    static bool allow_custom_types = false;
    return allow_custom_types;
}

#if !defined(BUILD_SELF_VVL)
// Generated, put here to mimic where it would have been if manually written
// Can't move up to top of file until we generate the VK_LAYER_* names as well
#include "layer_options_validation.h"
#endif

// Process enables and disables set though the vk_layer_settings.txt config file or through an environment variable
void ProcessConfigAndEnvSettings(ConfigAndEnvSettings* settings_data) {
    // When compiling a build for self validation, ProcessConfigAndEnvSettings immediately returns,
    // so that the layer always defaults to the standard validation options we want,
    // and does not try to process option coming from the VVL we are debugging
#if defined(BUILD_SELF_VVL)
    // Found having sync val on is very important for catching GPU-AV bugs
    settings_data->enabled[sync_validation] = true;

    // Setup default messenger callback to stdout and just error validation messages
    FILE* log_output = stdout;
    VkDebugUtilsMessengerEXT messenger = VK_NULL_HANDLE;
    VkDebugUtilsMessengerCreateInfoEXT dbg_create_info = vku::InitStructHelper();
    dbg_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
    dbg_create_info.messageSeverity |= VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
    dbg_create_info.pfnUserCallback = MessengerLogCallback;
    dbg_create_info.pUserData = (void*)log_output;
    LayerCreateMessengerCallback(settings_data->debug_report, true, &dbg_create_info, &messenger);

#ifdef _WIN32
    messenger = VK_NULL_HANDLE;
    dbg_create_info.pfnUserCallback = MessengerWin32DebugOutputMsg;
    dbg_create_info.pUserData = nullptr;
    LayerCreateMessengerCallback(settings_data->debug_report, true, &dbg_create_info, &messenger);
#endif  // _WIN32

    return;
#else

    // We need to send all warnings through the DebugCallback (printf will go into the void and likely is not seen for people), but
    // we also need to get settings setup before creating the DebugCallback. This is the only spot that needs this, so we create a
    // temp list of strings here and shove them through DebugCallback right after we call it. This function should not be a
    // bottleneck so some extra string building should be ok here.
    std::vector<std::string> setting_warnings;

    // If not cleared, garbage has been seen in some Android run effecting the error message
    HasCustomStypeInfo() = false;

    VkuLayerSettingSet layer_setting_set = VK_NULL_HANDLE;
    auto layer_setting_create_info = vkuFindLayerSettingsCreateInfo(settings_data->create_info);
    if (!ValidateLayerSettingsCreateInfo(layer_setting_create_info)) {
        return;  // nullptr will crash things
    }
    vkuCreateLayerSettingSet(OBJECT_LAYER_NAME, layer_setting_create_info, nullptr, nullptr, &layer_setting_set);

    vkuSetLayerSettingCompatibilityNamespace(layer_setting_set, GetDefaultPrefix());

    if (layer_setting_create_info) {
        ValidateLayerSettingsProvided(*layer_setting_create_info, setting_warnings);
    }

    // Read legacy "enables" flags for backward compatibility
    std::vector<std::string> enabled;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_ENABLES)) {
        vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_ENABLES, enabled);
    }
    const std::string string_enabled = Merge(enabled);
    const bool used_enabled = SetLocalEnableSetting(string_enabled, ",", settings_data->enabled);

    // Read legacy "disables" flags for backward compatibility
    std::vector<std::string> disabled;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DISABLES)) {
        vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_DISABLES, disabled);
    }
    const std::string string_disabled = Merge(disabled);
    const bool used_disabled = SetLocalDisableSetting(string_disabled, ",", settings_data->disabled);

    // Check if actually used, or will give bogus warning if the user set VK_LAYER_ENABLES to some garbage setting
    if (used_enabled || used_disabled) {
        setting_warnings.emplace_back(GetDeprecatedEnabledDisabledWarning(enabled, disabled));
    }

    GlobalSettings& global_settings = *settings_data->global_settings;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DEBUG_DISABLE_SPIRV_VAL)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_DEBUG_DISABLE_SPIRV_VAL, global_settings.debug_disable_spirv_val);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DESCRIPTOR_HASHING)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_DESCRIPTOR_HASHING, global_settings.descriptor_hashing);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_DESCRIPTOR_HASHING_TOTAL_DESCRIPTORS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_DESCRIPTOR_HASHING_TOTAL_DESCRIPTORS,
                                global_settings.descriptor_hashing_total_descriptors);
        if (!IsPowerOfTwo(global_settings.descriptor_hashing_total_descriptors)) {
            uint32_t old_value = global_settings.descriptor_hashing_total_descriptors;
            uint32_t new_value = GetSmallestGreaterOrEquallPowerOfTwo(old_value);
            global_settings.descriptor_hashing_total_descriptors = new_value;

            setting_warnings.emplace_back(
                "VK_LAYER_DESCRIPTOR_HASHING_TOTAL_DESCRIPTORS (descriptor_hashing_total_descriptors) was set to " +
                std::to_string(old_value) + " but being set to " + std::to_string(new_value) + " as it needs to be a power of two");
        }
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_CUSTOM_STYPE_LIST)) {
        // We use to support this, but feel no one is using this
        // As a transition, we will allow to be used and just skip the 1 VU check if anything is set
        // (added after SDK 1.4.341)
        setting_warnings.emplace_back(
            "VK_LAYER_CUSTOM_STYPE_LIST is set, but currently the feature seems unused, we plan to drop this support, so please "
            "report if seeing this warning");
        HasCustomStypeInfo() = true;
    }

    GpuAVSettings& gpuav_settings = *settings_data->gpuav_settings;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_SAFE_MODE)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_SAFE_MODE, gpuav_settings.safe_mode);
    }

    bool shader_instrumentation_enabled = true;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_SHADER_INSTRUMENTATION)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_SHADER_INSTRUMENTATION, shader_instrumentation_enabled);
    }

    if (!shader_instrumentation_enabled) {
        gpuav_settings.DisableShaderInstrumentationAndOptions();
    } else {
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DESCRIPTOR_CHECKS)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DESCRIPTOR_CHECKS,
                                    gpuav_settings.shader_instrumentation.descriptor_checks);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_POST_PROCESS_DESCRIPTOR_INDEXING)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_POST_PROCESS_DESCRIPTOR_INDEXING,
                                    gpuav_settings.shader_instrumentation.post_process_descriptor_indexing);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_MESH_SHADING)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_MESH_SHADING,
                                    gpuav_settings.shader_instrumentation.mesh_shading);
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_VERTEX_ATTRIBUTE_FETCH_OOB)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_VERTEX_ATTRIBUTE_FETCH_OOB,
                                    gpuav_settings.shader_instrumentation.vertex_attribute_fetch_oob);
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_BUFFER_ADDRESS_OOB)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_BUFFER_ADDRESS_OOB,
                                    gpuav_settings.shader_instrumentation.buffer_device_address);
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_VALIDATE_TRACE_RAY)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_VALIDATE_TRACE_RAY,
                                    gpuav_settings.shader_instrumentation.trace_ray);
        }

        if (vkuHasLayerSetting(layer_setting_set, REMOVED_VK_LAYER_GPUAV_VALIDATE_RAY_QUERY)) {
            setting_warnings.emplace_back(std::string(REMOVED_VK_LAYER_GPUAV_VALIDATE_RAY_QUERY) +
                                          " advanced setting was removed, just use " +
                                          std::string(VK_LAYER_GPUAV_VALIDATE_TRACE_RAY) + " now.");
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_SHADER_SANITIZER)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_SHADER_SANITIZER,
                                    gpuav_settings.shader_instrumentation.sanitizer);
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_SHARED_MEMORY_DATA_RACE)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_SHARED_MEMORY_DATA_RACE,
                                    gpuav_settings.shader_instrumentation.shared_memory_data_race);
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_MAX_INDICES_COUNT)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_MAX_INDICES_COUNT, gpuav_settings.invalid_index_command);
            // Hard limit is set by the fact we have kActionId_Mask used... which is also arbitrary
            // ... we don't need 64k slots for the kErrorLoggerId_Mask, so could increase 64k if really wanted
            // (If we update, please update the SetMaxIndicesCountAtTheLimit test)
            if (gpuav_settings.invalid_index_command >= 65535) {
                setting_warnings.emplace_back(
                    "VK_LAYER_GPUAV_MAX_INDICES_COUNT (gpuav_max_indices_count) is being set to 65534, the max value supported "
                    "currently.");
                gpuav_settings.invalid_index_command = 65534;
            }
            gpuav_settings.indices_buffer_count = gpuav_settings.invalid_index_command + 1;
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_SELECT_INSTRUMENTED_SHADERS)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_SELECT_INSTRUMENTED_SHADERS,
                                    gpuav_settings.select_instrumented_shaders);
        }
        if (gpuav_settings.select_instrumented_shaders) {
            if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_CDL_DUMP_PATH)) {
                std::string cdl_dump_path;
                vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_CDL_DUMP_PATH, cdl_dump_path);
                gpuav_settings.LoadCDLDump(std::move(cdl_dump_path), setting_warnings);
            }

            if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_SHADERS_TO_INSTRUMENT)) {
                std::vector<std::string> shaders_to_instrument;
                vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_GPUAV_SHADERS_TO_INSTRUMENT, shaders_to_instrument);
                gpuav_settings.SetShaderSelectionRegexes(std::move(shaders_to_instrument));
            }
        }

        // No need to enable shader instrumentation options is no instrumentation is done
        if (!gpuav_settings.IsShaderInstrumentationEnabled()) {
            gpuav_settings.DisableShaderInstrumentationAndOptions();
        }
    }

    bool buffers_validation_enabled = true;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_BUFFERS_VALIDATION)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_BUFFERS_VALIDATION, buffers_validation_enabled);
    }
    if (!buffers_validation_enabled) {
        gpuav_settings.SetBufferValidationEnabled(false);
    } else {
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_INDIRECT_DRAWS_BUFFERS)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_INDIRECT_DRAWS_BUFFERS,
                                    gpuav_settings.validate_indirect_draws_buffers);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_INDIRECT_DISPATCHES_BUFFERS)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_INDIRECT_DISPATCHES_BUFFERS,
                                    gpuav_settings.validate_indirect_dispatches_buffers);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_INDIRECT_TRACE_RAYS_BUFFERS)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_INDIRECT_TRACE_RAYS_BUFFERS,
                                    gpuav_settings.validate_indirect_trace_rays_buffers);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_BUFFER_COPIES)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_BUFFER_COPIES, gpuav_settings.validate_buffer_copies);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_COPY_MEMORY_INDIRECT)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_COPY_MEMORY_INDIRECT,
                                    gpuav_settings.validate_copy_memory_indirect);
        }
        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_INDEX_BUFFERS)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_INDEX_BUFFERS, gpuav_settings.validate_index_buffers);
        }
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_ACCELERATION_STRUCTURES_BUILDS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_ACCELERATION_STRUCTURES_BUILDS,
                                gpuav_settings.validate_acceleration_structures_builds);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_RAY_TRACING_BUFFERS_CONSISTENCY)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_RAY_TRACING_BUFFERS_CONSISTENCY,
                                gpuav_settings.ray_tracing_buffers_consistency);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_FORCE_ON_ROBUSTNESS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_FORCE_ON_ROBUSTNESS, gpuav_settings.force_on_robustness);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DEBUG_VALIDATE_INSTRUMENTED_SHADERS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DEBUG_VALIDATE_INSTRUMENTED_SHADERS,
                                gpuav_settings.debug_validate_instrumented_shaders);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DEBUG_DUMP_INSTRUMENTED_SHADERS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DEBUG_DUMP_INSTRUMENTED_SHADERS,
                                gpuav_settings.debug_dump_instrumented_shaders);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DEBUG_MAX_INSTRUMENTATIONS_COUNT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DEBUG_MAX_INSTRUMENTATIONS_COUNT,
                                gpuav_settings.debug_max_instrumentations_count);
    }

    // Debug Printf - (which we bundle into GPU-AV internally)
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_PRINTF_ENABLE)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_PRINTF_ENABLE, gpuav_settings.debug_printf_enabled);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DEBUG_PRINT_INSTRUMENTATION_INFO)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DEBUG_PRINT_INSTRUMENTATION_INFO,
                                gpuav_settings.debug_print_instrumentation_info);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DEBUG_DISABLE_DONTINLINE)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DEBUG_DISABLE_DONTINLINE,
                                gpuav_settings.debug_disable_dontinline);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_PRINTF_TO_STDOUT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_PRINTF_TO_STDOUT, gpuav_settings.debug_printf_to_stdout);
    }

    // This option was published when DebugPrintf came out, leave to not break people's flow
    // Deprecated right after the 1.3.280 SDK release
    if (!GetEnvironment("DEBUG_PRINTF_TO_STDOUT").empty()) {
        setting_warnings.emplace_back("DEBUG_PRINTF_TO_STDOUT was set, this is deprecated, please use VK_LAYER_PRINTF_TO_STDOUT");
        gpuav_settings.debug_printf_to_stdout = true;
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_PRINTF_VERBOSE)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_PRINTF_VERBOSE, gpuav_settings.debug_printf_verbose);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_PRINTF_BUFFER_SIZE)) {
        const uint32_t default_buffer_size = gpuav_settings.debug_printf_buffer_size;
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_PRINTF_BUFFER_SIZE, gpuav_settings.debug_printf_buffer_size);
        if (gpuav_settings.debug_printf_buffer_size == 0) {
            gpuav_settings.debug_printf_buffer_size = default_buffer_size;
            setting_warnings.emplace_back(std::string(VK_LAYER_PRINTF_BUFFER_SIZE) +
                                          " was set to zero, which is invalid, setting to the default of " +
                                          std::to_string(default_buffer_size));
        }
    }

    SyncValSettings& syncval_settings = *settings_data->syncval_settings;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION,
                                syncval_settings.submit_time_validation);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_SYNCVAL_SHADER_ACCESSES_HEURISTIC)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_SHADER_ACCESSES_HEURISTIC,
                                syncval_settings.shader_accesses_heuristic);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_SYNCVAL_LOAD_OP_AFTER_STORE_OP_VALIDATION)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_LOAD_OP_AFTER_STORE_OP_VALIDATION,
                                syncval_settings.load_op_after_store_op_validation);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES,
                                syncval_settings.message_extra_properties);
    }

    GpuDumpSettings& gpu_dump_settings = *settings_data->gpu_dump_settings;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPU_DUMP_DESCRIPTORS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPU_DUMP_DESCRIPTORS, gpu_dump_settings.descriptors);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPU_DUMP_COPY_MEMORY_INDIRECT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPU_DUMP_COPY_MEMORY_INDIRECT, gpu_dump_settings.copy_memory_indirect);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPU_DUMP_DEVICE_GENERATED_COMMANDS)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPU_DUMP_DEVICE_GENERATED_COMMANDS,
                                gpu_dump_settings.device_generated_commands);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPU_DUMP_TO_STDOUT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPU_DUMP_TO_STDOUT, gpu_dump_settings.to_stdout);
    }

    LegacyDetectionSettings& legacy_detection_settings = *settings_data->legacy_detection_settings;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_LEGACY_DETECTION_MODE)) {
        std::string mode;
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_LEGACY_DETECTION_MODE, mode);
        if (mode == "ONLY_SUPPORTED") {
            legacy_detection_settings.only_supported = true;
        } else if (mode == "ONLY_ENABLED") {
            legacy_detection_settings.only_enabled = true;
        }
    }
    legacy_detection_settings.always = !legacy_detection_settings.only_supported && !legacy_detection_settings.only_enabled;

    // Feel safe to remove by end of 2026, will have enough SDK cycles by then
    const char* REMOVED_VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT = "syncval_message_extra_properties_pretty_print";
    if (vkuHasLayerSetting(layer_setting_set, REMOVED_VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT)) {
        setting_warnings.emplace_back(std::string(REMOVED_VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT) +
                                      " was removed. The main purpose of the extra properties section is to filter syncval error "
                                      "messages. The priority is to have a fixed and easy-to-parse layout.");
    }

    const auto* validation_features_ext = vku::FindStructInPNextChain<VkValidationFeaturesEXT>(settings_data->create_info);
    if (validation_features_ext) {
        SetValidationFeatures(settings_data->disabled, settings_data->enabled, validation_features_ext);
    }
    const auto* validation_flags_ext = vku::FindStructInPNextChain<VkValidationFlagsEXT>(settings_data->create_info);
    if (validation_flags_ext) {
        SetValidationFlags(settings_data->disabled, validation_flags_ext);
    }

    const bool use_fine_grained_settings = disabled.empty() && enabled.empty();

    // Only read the legacy enables flags when used, not their replacement.
    // Avoid Android C.I. performance regression from reading Android env variables
    if (use_fine_grained_settings) {
        SetValidationSetting(layer_setting_set, settings_data->enabled, best_practices, VK_LAYER_VALIDATE_BEST_PRACTICES);
        SetValidationSetting(layer_setting_set, settings_data->enabled, vendor_specific_arm, VK_LAYER_VALIDATE_BEST_PRACTICES_ARM);
        SetValidationSetting(layer_setting_set, settings_data->enabled, vendor_specific_amd, VK_LAYER_VALIDATE_BEST_PRACTICES_AMD);
        SetValidationSetting(layer_setting_set, settings_data->enabled, vendor_specific_img, VK_LAYER_VALIDATE_BEST_PRACTICES_IMG);
        SetValidationSetting(layer_setting_set, settings_data->enabled, vendor_specific_nvidia,
                             VK_LAYER_VALIDATE_BEST_PRACTICES_NVIDIA);
        SetValidationSetting(layer_setting_set, settings_data->enabled, sync_validation, VK_LAYER_VALIDATE_SYNC);
    }

    // These don't have a VkValidationFeaturesEXT equivalent, so always check these
    SetValidationSetting(layer_setting_set, settings_data->enabled, legacy_detection, VK_LAYER_LEGACY_DETECTION);
    settings_data->enabled[gpu_dump] = gpu_dump_settings.EnableLayer();

    // Only read the legacy disables flags when used, not their replacement.
    // Avoid Android C.I. performance regression from reading Android env variables
    if (use_fine_grained_settings) {
        SetValidationSetting(layer_setting_set, settings_data->disabled, stateless_checks, VK_LAYER_STATELESS_PARAM);
        SetValidationSetting(layer_setting_set, settings_data->disabled, thread_safety, VK_LAYER_THREAD_SAFETY);
        SetValidationSetting(layer_setting_set, settings_data->disabled, core_checks, VK_LAYER_VALIDATE_CORE);
        SetValidationSetting(layer_setting_set, settings_data->disabled, command_buffer_state, VK_LAYER_CHECK_COMMAND_BUFFER);
        SetValidationSetting(layer_setting_set, settings_data->disabled, object_in_use, VK_LAYER_CHECK_OBJECT_IN_USE);
        SetValidationSetting(layer_setting_set, settings_data->disabled, query_validation, VK_LAYER_CHECK_QUERY);
        SetValidationSetting(layer_setting_set, settings_data->disabled, image_layout_validation, VK_LAYER_CHECK_IMAGE_LAYOUT);
        SetValidationSetting(layer_setting_set, settings_data->disabled, handle_wrapping, VK_LAYER_UNIQUE_HANDLES);
        SetValidationSetting(layer_setting_set, settings_data->disabled, object_tracking, VK_LAYER_OBJECT_LIFETIME);
        SetValidationSetting(layer_setting_set, settings_data->disabled, shader_validation, VK_LAYER_CHECK_SHADERS);
        SetValidationSetting(layer_setting_set, settings_data->disabled, shader_validation_caching, VK_LAYER_CHECK_SHADERS_CACHING);
    }

    // This is the "original" way to use DebugPrintf before you could use it with GPU-AV
    // In this case, we want to emulate supporting only for DebugPrintf with GPU-AV disabled
    if (settings_data->enabled[debug_printf_validation]) {
        gpuav_settings.debug_printf_enabled = true;
    } else if (gpuav_settings.debug_printf_enabled) {
        // enabled the new way, but chassis uses this to create Validation Object
        settings_data->enabled[debug_printf_validation] = true;
    }

    // New way to override everything to make it easy to use DebugPrintf when VkConfig isn't available
    bool printf_only_preset = false;
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_PRINTF_ONLY_PRESET)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_PRINTF_ONLY_PRESET, printf_only_preset);
        if (printf_only_preset) {
            gpuav_settings.SetOnlyDebugPrintf();
            // chassis uses this to create Validation Object
            settings_data->enabled[debug_printf_validation] = true;
            settings_data->enabled[gpu_validation] = false;

            // Tried to use VK_VALIDATION_FEATURE_DISABLE_ALL_EXT but when running on Android
            // (--gtest_filter="NegativeGpuAVDebugPrintf.BasicLayerSettingsPrintfPreset:NegativeWsi.UseDestroyedSwapchain")
            // Was getting asserts in VulkanTypedHandle::Cast() Simple fix was to just not disable
            // VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT
            SetValidationFeatureDisable(settings_data->disabled, VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT);
            SetValidationFeatureDisable(settings_data->disabled, VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT);
            SetValidationFeatureDisable(settings_data->disabled, VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT);
            SetValidationFeatureDisable(settings_data->disabled, VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT);
            SetValidationFeatureDisable(settings_data->disabled, VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT);
            SetValidationFeatureDisable(settings_data->disabled, VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHE_EXT);
            setting_warnings.emplace_back(
                "Disabling as much of normal validation as possible so that only DebugPrintf will be running.");
        }
    }

    // This is the "new" way to enable GPU-AV
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_ENABLE)) {
        bool gpuav_enable = false;
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_ENABLE, gpuav_enable);
        if (printf_only_preset) {
            setting_warnings.emplace_back(std::string(VK_LAYER_PRINTF_ONLY_PRESET) + " was set, so ignoring " +
                                          std::string(VK_LAYER_GPUAV_ENABLE) + ".");
        } else if (gpuav_enable) {
            // enabled the new way, but chassis uses this to create Validation Object
            settings_data->enabled[gpu_validation] = true;
        }
    }

    // Only truly needed for pipelines (see GpuShaderInstrumentor::PreCallRecordSetDebugUtilsObjectNameEXT)
    // In the future, could be removed in favor of debug names supplied at pipeline creation time.
    if (gpuav_settings.select_instrumented_shaders) {
        if (settings_data->disabled[handle_wrapping]) {
            setting_warnings.emplace_back(
                "Handle wrapping has been disabled, but GPU-AV selective shader instrumentation is enabled. Forcing activation of "
                "handle wrapping.");
            settings_data->disabled[handle_wrapping] = false;
        }
    }

    // After checking the various ways to enable both DebugPrintf and GPU-AV, disable non-DebugPrintf portion if not used
    if (settings_data->enabled[debug_printf_validation] && !settings_data->enabled[gpu_validation]) {
        gpuav_settings.SetOnlyDebugPrintf();
    }

    // if app is setting VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT, we can use this to disable it for debugging
    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_DEBUG_DISABLE_ALL)) {
        bool disable_gpuav = false;
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_DEBUG_DISABLE_ALL, disable_gpuav);
        if (disable_gpuav) {
            settings_data->enabled[gpu_validation] = false;
        }
    }

    if (settings_data->enabled[gpu_validation] && !settings_data->disabled[core_checks]) {
        setting_warnings.emplace_back(
            "Both GPU Assisted Validation and Normal Core Check Validation are enabled, this is not recommend as it  will be very "
            "slow. Once all "
            "errors in Core Check are solved, please disable, then only use GPU-AV for best performance.");
    }

    // Set at the end once we decide what settings are actually on
    if (settings_data->disabled[shader_validation] || settings_data->disabled[core_checks]) {
        // only is used for core validation checks
        global_settings.spirv_const_fold = false;

        // sync val relies on the information from spirv::Module being parsed and stored
        if (!settings_data->enabled[sync_validation]) {
            // GPU-AV/DebugPrintf relies on spirv::Module to hold the original SPIR-V
            if (!settings_data->enabled[gpu_validation] && !settings_data->enabled[debug_printf_validation] &&
                !settings_data->enabled[gpu_dump]) {
                global_settings.spirv_store = false;
            } else if (settings_data->disabled[shader_validation]) {
                setting_warnings.emplace_back(
                    "Shader Validation was explicitly turned off, but the SPIR-V still needs to be stored, but not validated, in "
                    "order for GPU-AV/DebugPrintf to get information from the original SPIR-V.");
            }
        } else if (settings_data->disabled[shader_validation]) {
            setting_warnings.emplace_back(
                "Shader Validation was explicitly turned off, but the SPIR-V still needs to be parsed/stored, but not validated, "
                "in order for Sync Validation to get read/write information out the SPIR-V.");
        }
    }

    if (global_settings.descriptor_hashing && !settings_data->enabled[gpu_validation] && !gpu_dump_settings.descriptors) {
        setting_warnings.emplace_back(
            "Descriptor Hashing was turned on, but neither GPU-AV nor GPU Dump (for descriptors) is enabled. Turning off as this "
            "setting has no "
            "effect.");
        global_settings.descriptor_hashing = false;
    }

    // Our way to to try and get rid of these settings
    // Detect if people have core, but manually turned off these settings
    //
    // These were added 10 years ago and were not properly maintained nor tested
    // We added this warning at 1.4.355 and want to give a few SDK cycles before fully removing
    if (!settings_data->disabled[core_checks]) {
        if (settings_data->disabled[command_buffer_state]) {
            setting_warnings.emplace_back(
                "VK_LAYER_CHECK_COMMAND_BUFFER (check_command_buffer) is deprecated and planned to be removed in the future. "
                "Please "
                "inform us if you are still using this setting!");
        }
        if (settings_data->disabled[object_in_use]) {
            setting_warnings.emplace_back(
                "VK_LAYER_CHECK_OBJECT_IN_USE (check_object_in_use) is deprecated and planned to be removed in the future. Please "
                "inform us if you are still using this setting!");
        }
        if (settings_data->disabled[query_validation]) {
            setting_warnings.emplace_back(
                "VK_LAYER_CHECK_QUERY (check_query) is deprecated and planned to be removed in the future. Please inform us if you "
                "are "
                "still using this setting!");
        }
        if (settings_data->disabled[image_layout_validation]) {
            setting_warnings.emplace_back(
                "VK_LAYER_CHECK_IMAGE_LAYOUT (check_image_layout) is deprecated and planned to be removed in the future. Please "
                "inform "
                "us if you are still using this setting!");
        }
    }

    // Last as previous settings are needed so we can make sure they line up with the DebugReport settings
    ProcessDebugReportSettings(settings_data, layer_setting_set, setting_warnings);

    // Grab application name here while we have access to it and know if to save it or not
    if (settings_data->debug_report->message_format_settings.display_application_name) {
        const VkApplicationInfo* app_info = settings_data->create_info->pApplicationInfo;
        settings_data->debug_report->message_format_settings.application_name =
            (app_info && app_info->pApplicationName) ? app_info->pApplicationName : "";
    }

    for (const auto& warning : setting_warnings) {
        Location loc(vvl::Func::vkCreateInstance);
        settings_data->debug_report->LogMessage(kWarningBit, "VALIDATION-SETTINGS", {}, loc, warning);
    }

    vkuDestroyLayerSettingSet(layer_setting_set, nullptr);
#endif  // !BUILD_SELF_VVL
}
