/* Copyright (c) 2020-2024 The Khronos Group Inc.
 * Copyright (c) 2020-2024 Valve Corporation
 * Copyright (c) 2020-2024 LunarG, Inc.
 * 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 "error_message/log_message_type.h"
#include "error_message/logging.h"
#include "error_message/error_location.h"
#include "generated/error_location_helper.h"
#include "utils/hash_util.h"
#include <cstring>
#include <string>
#include <unordered_set>
#include <vector>
#include <vulkan/layer/vk_layer_settings.hpp>

#include "gpu/core/gpuav_settings.h"

#include "sync/sync_settings.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;
}

// This should mirror the 'DisableFlags' enumerated type
const std::vector<std::string> &GetDisableFlagNameHelper() {
    static const std::vector<std::string> disable_flag_name_helper = {
        "VALIDATION_CHECK_DISABLE_COMMAND_BUFFER_STATE",                // command_buffer_state,
        "VALIDATION_CHECK_DISABLE_OBJECT_IN_USE",                       // object_in_use,
        "VALIDATION_CHECK_DISABLE_QUERY_VALIDATION",                    // query_validation,
        "VALIDATION_CHECK_DISABLE_IMAGE_LAYOUT_VALIDATION",             // image_layout_validation,
        "VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT",           // object_tracking,
        "VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT",                // core_checks,
        "VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT",              // thread_safety,
        "VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT",             // stateless_checks,
        "VK_VALIDATION_FEATURE_DISABLE_UNIQUE_HANDLES_EXT",             // handle_wrapping,
        "VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT",                    // shader_validation,
        "VK_VALIDATION_FEATURE_DISABLE_SHADER_VALIDATION_CACHING_EXT",  // shader_validation_caching
    };
    return disable_flag_name_helper;
}

const std::vector<std::string> &GetEnableFlagNameHelper() {
    // This should mirror the 'EnableFlags' enumerated type
    static const std::vector<std::string> enable_flag_name_helper = {
        "VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT",                       // gpu_validation,
        "VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_RESERVE_BINDING_SLOT_EXT",  // gpu_validation_reserve_binding_slot,
        "VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT",                     // best_practices,
        "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_ARM",                         // vendor_specific_arm,
        "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_AMD",                         // vendor_specific_amd,
        "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_IMG",                         // vendor_specific_img,
        "VALIDATION_CHECK_ENABLE_VENDOR_SPECIFIC_NVIDIA",                      // vendor_specific_nvidia,
        "VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT",                       // debug_printf,
        "VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT",         // sync_validation,
    };
    return enable_flag_name_helper;
}

// 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";
// These were deprecated after the 1.3.296 SDK release (because it was a flag and now is a boolean)
const char *DEPRECATED_VK_LAYER_VALIDATE_GPU_BASED = "validate_gpu_based";

// 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
// ---
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";

// GloablSettings
// ---
const char *VK_LAYER_FINE_GRAINED_LOCKING = "fine_grained_locking";
// Debug settings used for internal development
const char *VK_LAYER_DEBUG_DISABLE_SPIRV_VAL = "debug_disable_spirv_val";

// 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_SHADER_INSTRUMENTATION = "gpuav_shader_instrumentation";
const char *VK_LAYER_GPUAV_DESCRIPTOR_CHECKS = "gpuav_descriptor_checks";
const char *VK_LAYER_GPUAV_WARN_ON_ROBUST_OOB = "gpuav_warn_on_robust_oob";
const char *VK_LAYER_GPUAV_BUFFER_ADDRESS_OOB = "gpuav_buffer_address_oob";
const char *VK_LAYER_GPUAV_MAX_BUFFER_DEVICE_ADDRESSES = "gpuav_max_buffer_device_addresses";
const char *VK_LAYER_GPUAV_VALIDATE_RAY_QUERY = "gpuav_validate_ray_query";
// Post Process are designed to allow the user to "assume" the access is valid and want to know after the GPU executes what
// happened. These are much lighter checks and can be used while the rest of GPU-AV is turned off
const char *VK_LAYER_GPUAV_POST_PROCESS_DESCRIPTOR_INDEXING = "gpuav_post_process_descriptor_indexing";
const char *VK_LAYER_GPUAV_SELECT_INSTRUMENTED_SHADERS = "gpuav_select_instrumented_shaders";

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_INDEX_BUFFERS = "gpuav_index_buffers";
const char *VK_LAYER_GPUAV_IMAGE_LAYOUT = "gpuav_image_layout";

const char *VK_LAYER_GPUAV_RESERVE_BINDING_SLOT = "gpuav_reserve_binding_slot";
const char *VK_LAYER_GPUAV_VMA_LINEAR_OUTPUT = "gpuav_vma_linear_output";

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";

// 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_MESSAGE_EXTRA_PROPERTIES = "syncval_message_extra_properties";
const char *VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT = "syncval_message_extra_properties_pretty_print";

// Message Formatting
// ---
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";

// These were deprecated after the 1.3.280 SDK release
const char *DEPRECATED_VK_LAYER_GPUAV_VALIDATE_COPIES = "gpuav_validate_copies";
const char *DEPRECATED_VK_LAYER_GPUAV_VALIDATE_INDIRECT_BUFFER = "gpuav_validate_indirect_buffer";
const char *DEPRECATED_VK_LAYER_RESERVE_BINDING_SLOT = "reserve_binding_slot";
const char *DEPRECATED_GPUAV_VMA_LINEAR_OUTPUT = "vma_linear_output";
const char *DEPRECATED_GPUAV_WARN_ON_ROBUST_OOB = "warn_on_robust_oob";
const char *DEPRECATED_GPUAV_SELECT_INSTRUMENTED_SHADERS = "select_instrumented_shaders";

// These were deprecated after the 1.3.283 SDK release
const char *DEPRECATED_VK_LAYER_VALIDATE_SYNC_QUEUE_SUBMIT = "sync_queue_submit";

// 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(CHECK_DISABLED &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(CHECK_DISABLED &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(CHECK_ENABLED &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(CHECK_ENABLED &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(CHECK_DISABLED &disables, 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:
                disables[shader_validation] = true;
                break;
            case VK_VALIDATION_CHECK_ALL_EXT:
                // Set all disabled flags to true
                disables[shader_validation] = true;
                break;
            default:
                break;
        }
    }
}

// Process Validation Features flags specified through the ValidationFeature extension
void SetValidationFeatures(CHECK_DISABLED &disable_data, CHECK_ENABLED &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
void SetLocalEnableSetting(std::string list_of_enables, const std::string &delimiter, CHECK_ENABLED &enables) {
    size_t pos = 0;
    std::string token;
    while (list_of_enables.length() != 0) {
        token = GetNextToken(&list_of_enables, delimiter, &pos);
        if (token.find("VK_VALIDATION_FEATURE_ENABLE_") != std::string::npos) {
            auto result = VkValFeatureEnableLookup().find(token);
            if (result != VkValFeatureEnableLookup().end()) {
                SetValidationFeatureEnable(enables, result->second);
            }
        } else if (token.find("VALIDATION_CHECK_ENABLE_") != std::string::npos) {
            auto result = ValidationEnableLookup().find(token);
            if (result != ValidationEnableLookup().end()) {
                SetValidationEnable(enables, result->second);
            }
        }
    }
}

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

uint32_t TokenToUint(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(std::string raw_id_list, const std::string &delimiter, vvl::unordered_set<uint32_t> &filter_list) {
    size_t pos = 0;
    std::string token;
    while (raw_id_list.length() != 0) {
        token = GetNextToken(&raw_id_list, delimiter, &pos);
        uint32_t int_id = TokenToUint(token);
        if (int_id == 0) {
            const uint32_t id_hash = hash_util::VuidHash(token);
            if (id_hash != 0) {
                int_id = id_hash;
            }
        }
        if ((int_id != 0) && filter_list.find(int_id) == filter_list.end()) {
            filter_list.insert(int_id);
        }
    }
}

// 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::stringstream 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;
            }
        }
    } 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;
}

// TODO - This should be in https://github.com/KhronosGroup/Vulkan-Utility-Libraries/issues/254
// Doing here in VVL until added in VUL
// If it will not be done in VUL for a while, we should at least generate this from the JSON
static void ValidateLayerSettingsProvided(const VkLayerSettingsCreateInfoEXT *layer_setting_create_info,
                                          VkuLayerSettingSet layer_setting_set, std::vector<std::string> &setting_warnings) {
    if (!layer_setting_create_info) return;
    // Found that a set of <const char*> doesn't detect duplicates on all compilers
    vvl::unordered_set<std::string> used_settings;

    for (uint32_t i = 0; i < layer_setting_create_info->settingCount; i++) {
        const VkLayerSettingEXT &setting = layer_setting_create_info->pSettings[i];
        if (strcmp(OBJECT_LAYER_NAME, setting.pLayerName) != 0) continue;

        // used as a backup for settings not listed below
        VkLayerSettingTypeEXT required_type = VK_LAYER_SETTING_TYPE_MAX_ENUM_EXT;

        // Debugging settings are not added here, those are for internal development and not designed for an app to use via
        // VkLayerSettings API
        if (strcmp(VK_LAYER_VALIDATE_BEST_PRACTICES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_VALIDATE_BEST_PRACTICES_ARM, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_VALIDATE_BEST_PRACTICES_AMD, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_VALIDATE_BEST_PRACTICES_IMG, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_VALIDATE_BEST_PRACTICES_NVIDIA, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_VALIDATE_SYNC, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_CHECK_SHADERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_THREAD_SAFETY, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_STATELESS_PARAM, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_OBJECT_LIFETIME, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_VALIDATE_CORE, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_UNIQUE_HANDLES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_CHECK_SHADERS_CACHING, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_CHECK_COMMAND_BUFFER, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_CHECK_OBJECT_IN_USE, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_CHECK_QUERY, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_CHECK_IMAGE_LAYOUT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_FINE_GRAINED_LOCKING, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_MESSAGE_ID_FILTER, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_STRING_EXT;
        } else if (strcmp(VK_LAYER_CUSTOM_STYPE_LIST, setting.pSettingName) == 0) {
            // TODO - Need to decide on a type for this
        } else if (strcmp(VK_LAYER_ENABLE_MESSAGE_LIMIT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_DUPLICATE_MESSAGE_LIMIT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_UINT32_EXT;
        } else if (strcmp(VK_LAYER_PRINTF_ONLY_PRESET, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_PRINTF_ENABLE, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_PRINTF_TO_STDOUT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_PRINTF_VERBOSE, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_PRINTF_BUFFER_SIZE, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_UINT32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_ENABLE, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_VK_LAYER_VALIDATE_GPU_BASED, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_SHADER_INSTRUMENTATION, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_DESCRIPTOR_CHECKS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_WARN_ON_ROBUST_OOB, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_BUFFER_ADDRESS_OOB, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_MAX_BUFFER_DEVICE_ADDRESSES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_UINT32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_VALIDATE_RAY_QUERY, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_POST_PROCESS_DESCRIPTOR_INDEXING, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_SELECT_INSTRUMENTED_SHADERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_BUFFERS_VALIDATION, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_INDIRECT_DRAWS_BUFFERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_INDIRECT_DISPATCHES_BUFFERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_INDIRECT_TRACE_RAYS_BUFFERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_BUFFER_COPIES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_INDEX_BUFFERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_IMAGE_LAYOUT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_RESERVE_BINDING_SLOT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_GPUAV_VMA_LINEAR_OUTPUT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_SYNCVAL_SHADER_ACCESSES_HEURISTIC, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_MESSAGE_FORMAT_DISPLAY_APPLICATION_NAME, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(VK_LAYER_LOG_FILENAME, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_STRING_EXT;
        } else if (strcmp(VK_LAYER_DEBUG_ACTION, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_STRING_EXT;
        } else if (strcmp(VK_LAYER_REPORT_FLAGS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_STRING_EXT;
        } else if (strcmp(VK_LAYER_ENABLES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_STRING_EXT;
        } else if (strcmp(VK_LAYER_DISABLES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_STRING_EXT;
        } else if (strcmp(DEPRECATED_VK_LAYER_GPUAV_VALIDATE_COPIES, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_VK_LAYER_GPUAV_VALIDATE_INDIRECT_BUFFER, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_VK_LAYER_RESERVE_BINDING_SLOT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_GPUAV_VMA_LINEAR_OUTPUT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_GPUAV_WARN_ON_ROBUST_OOB, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_GPUAV_SELECT_INSTRUMENTED_SHADERS, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else if (strcmp(DEPRECATED_VK_LAYER_VALIDATE_SYNC_QUEUE_SUBMIT, setting.pSettingName) == 0) {
            required_type = VK_LAYER_SETTING_TYPE_BOOL32_EXT;
        } else {
            setting_warnings.emplace_back("The setting " + std::string(setting.pSettingName) +
                                          " in VkLayerSettingsCreateInfoEXT was not recognize by the Validation Layers. Please "
                                          "view the VkLayer_khronos_validation.json for a list of all settings.");
        }

        if (required_type != VK_LAYER_SETTING_TYPE_MAX_ENUM_EXT && setting.type != required_type) {
            setting_warnings.emplace_back(
                "The setting " + std::string(setting.pSettingName) + " in VkLayerSettingsCreateInfoEXT was set to type " +
                std::string(string_VkLayerSettingTypeEXT(setting.type)) + " but requires type " +
                std::string(string_VkLayerSettingTypeEXT(required_type)) + " and the value may be parsed incorrectly.");
        }

        if (used_settings.count(setting.pSettingName)) {
            setting_warnings.emplace_back(
                "The setting " + std::string(setting.pSettingName) +
                " in VkLayerSettingsCreateInfoEXT was listed twice and only the first one listed will be recognized.");
        }
        used_settings.insert(setting.pSettingName);
    }
}

static void SetValidationSetting(VkuLayerSettingSet layer_setting_set, CHECK_DISABLED &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, CHECK_ENABLED &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_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);
    }
    const std::string string_message_id_filter = Merge(message_id_filter);
    CreateFilterMessageIdList(string_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_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_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_DEFAULT]");
            }
        }
    }

    std::vector<std::string> report_flags_list = {"error"};  // 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 any non-stdout DebugPrintf is being used, just turn off duplicate_message_limit, it will prevent people thinking
        // DebugPrintf is broken because nothing is printing.
        if (!settings_data->gpuav_settings->debug_printf_to_stdout && debug_report->duplicate_message_limit != 0) {
            debug_report->duplicate_message_limit = 0;
            setting_warnings.emplace_back("DebugPrintf logs can possibly print many times, but duplicate_message_limit is set to " +
                                          std::to_string(debug_report->duplicate_message_limit) +
                                          ", setting enable_message_limit to false so all logs are printed.");
        }
    }

    // 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;
    }

    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.");
    }

    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);
    }

    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);
    }
}

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

// Global list of sType,size identifiers
std::vector<std::pair<uint32_t, uint32_t>> &GetCustomStypeInfo() {
    static std::vector<std::pair<uint32_t, uint32_t>> custom_stype_info{};
    return custom_stype_info;
}

// 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)
    // 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
    GetCustomStypeInfo().clear();

    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());

    ValidateLayerSettingsProvided(layer_setting_create_info, layer_setting_set, setting_warnings);

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

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

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

    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_CUSTOM_STYPE_LIST)) {
        vkuGetLayerSettingValues(layer_setting_set, VK_LAYER_CUSTOM_STYPE_LIST, GetCustomStypeInfo());
    }

    GpuAVSettings &gpuav_settings = *settings_data->gpuav_settings;
    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_index);
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_WARN_ON_ROBUST_OOB)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_WARN_ON_ROBUST_OOB, gpuav_settings.warn_on_robust_oob);
        } else if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_GPUAV_WARN_ON_ROBUST_OOB)) {
            vkuGetLayerSettingValue(layer_setting_set, DEPRECATED_GPUAV_WARN_ON_ROBUST_OOB, gpuav_settings.warn_on_robust_oob);
            setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_GPUAV_WARN_ON_ROBUST_OOB) +
                                          " setting was set, use " + std::string(VK_LAYER_GPUAV_WARN_ON_ROBUST_OOB) + " instead.");
        }

        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_MAX_BUFFER_DEVICE_ADDRESSES)) {
            const uint32_t default_max_bda_in_use = gpuav_settings.max_bda_in_use;
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_MAX_BUFFER_DEVICE_ADDRESSES, gpuav_settings.max_bda_in_use);
            if (gpuav_settings.max_bda_in_use == 0) {
                gpuav_settings.max_bda_in_use = default_max_bda_in_use;
                setting_warnings.emplace_back(std::string(VK_LAYER_GPUAV_MAX_BUFFER_DEVICE_ADDRESSES) +
                                              " was set to zero, which is invalid, setting to the default of " +
                                              std::to_string(default_max_bda_in_use));
            }
        }

        if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_VALIDATE_RAY_QUERY)) {
            vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_VALIDATE_RAY_QUERY,
                                    gpuav_settings.shader_instrumentation.ray_query);
        }

        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);
        } else if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_GPUAV_SELECT_INSTRUMENTED_SHADERS)) {
            vkuGetLayerSettingValue(layer_setting_set, DEPRECATED_GPUAV_SELECT_INSTRUMENTED_SHADERS,
                                    gpuav_settings.select_instrumented_shaders);
            setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_GPUAV_SELECT_INSTRUMENTED_SHADERS) +
                                          " setting was set, use " + std::string(VK_LAYER_GPUAV_SELECT_INSTRUMENTED_SHADERS) +
                                          " instead.");
        }

        // 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);
        } else if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_VK_LAYER_GPUAV_VALIDATE_COPIES)) {
            vkuGetLayerSettingValue(layer_setting_set, DEPRECATED_VK_LAYER_GPUAV_VALIDATE_COPIES,
                                    gpuav_settings.validate_buffer_copies);
            setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_VK_LAYER_GPUAV_VALIDATE_COPIES) +
                                          " setting was set, use " + std::string(VK_LAYER_GPUAV_BUFFER_COPIES) + " instead.");
        }
        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_IMAGE_LAYOUT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_IMAGE_LAYOUT, gpuav_settings.validate_image_layout);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_RESERVE_BINDING_SLOT)) {
        SetValidationSetting(layer_setting_set, settings_data->enables, gpu_validation_reserve_binding_slot,
                             VK_LAYER_GPUAV_RESERVE_BINDING_SLOT);
    } else if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_VK_LAYER_RESERVE_BINDING_SLOT)) {
        SetValidationSetting(layer_setting_set, settings_data->enables, gpu_validation_reserve_binding_slot,
                             DEPRECATED_VK_LAYER_RESERVE_BINDING_SLOT);
        setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_VK_LAYER_RESERVE_BINDING_SLOT) +
                                      " setting was set, use " + std::string(VK_LAYER_GPUAV_RESERVE_BINDING_SLOT) + " instead.");
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_GPUAV_VMA_LINEAR_OUTPUT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_GPUAV_VMA_LINEAR_OUTPUT, gpuav_settings.vma_linear_output);
    } else if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_GPUAV_VMA_LINEAR_OUTPUT)) {
        vkuGetLayerSettingValue(layer_setting_set, DEPRECATED_GPUAV_VMA_LINEAR_OUTPUT, gpuav_settings.vma_linear_output);
        setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_GPUAV_VMA_LINEAR_OUTPUT) + " setting was set, use " +
                                      std::string(VK_LAYER_GPUAV_VMA_LINEAR_OUTPUT) + " instead.");
    }

    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_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);
    } else if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_VK_LAYER_VALIDATE_SYNC_QUEUE_SUBMIT)) {
        vkuGetLayerSettingValue(layer_setting_set, DEPRECATED_VK_LAYER_VALIDATE_SYNC_QUEUE_SUBMIT,
                                syncval_settings.submit_time_validation);
        setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_VK_LAYER_VALIDATE_SYNC_QUEUE_SUBMIT) +
                                      " setting was set, use " + std::string(VK_LAYER_SYNCVAL_SUBMIT_TIME_VALIDATION) +
                                      " instead.");
    }

    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_MESSAGE_EXTRA_PROPERTIES)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES,
                                syncval_settings.message_extra_properties);
    }

    if (vkuHasLayerSetting(layer_setting_set, VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT)) {
        vkuGetLayerSettingValue(layer_setting_set, VK_LAYER_SYNCVAL_MESSAGE_EXTRA_PROPERTIES_PRETTY_PRINT,
                                syncval_settings.message_extra_properties_pretty_print);
    }

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

    const bool use_fine_grained_settings = disables.empty() && enables.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->enables, best_practices, VK_LAYER_VALIDATE_BEST_PRACTICES);
        SetValidationSetting(layer_setting_set, settings_data->enables, vendor_specific_arm, VK_LAYER_VALIDATE_BEST_PRACTICES_ARM);
        SetValidationSetting(layer_setting_set, settings_data->enables, vendor_specific_amd, VK_LAYER_VALIDATE_BEST_PRACTICES_AMD);
        SetValidationSetting(layer_setting_set, settings_data->enables, vendor_specific_img, VK_LAYER_VALIDATE_BEST_PRACTICES_IMG);
        SetValidationSetting(layer_setting_set, settings_data->enables, vendor_specific_nvidia,
                             VK_LAYER_VALIDATE_BEST_PRACTICES_NVIDIA);
        SetValidationSetting(layer_setting_set, settings_data->enables, sync_validation, VK_LAYER_VALIDATE_SYNC);

        // These were deprecated after the 1.3.296 SDK release
        // Before GPU-AV and DebugPrintf were merged, we used this enum to set GPU-AV and DebugPrintf in vkconfig.
        // This code should in theory be dead since removing it from vkconfig, but keep just incase for a bit
        if (vkuHasLayerSetting(layer_setting_set, DEPRECATED_VK_LAYER_VALIDATE_GPU_BASED)) {
            std::string setting_value;
            vkuGetLayerSettingValue(layer_setting_set, DEPRECATED_VK_LAYER_VALIDATE_GPU_BASED, setting_value);
            if (setting_value == "GPU_BASED_GPU_ASSISTED") {
                settings_data->enables[gpu_validation] = true;
                setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_VK_LAYER_VALIDATE_GPU_BASED) +
                                              " setting was set, use " + std::string(VK_LAYER_GPUAV_ENABLE) + " instead.");
            } else if (setting_value == "GPU_BASED_DEBUG_PRINTF") {
                settings_data->enables[debug_printf_validation] = true;
                setting_warnings.emplace_back("Deprecated " + std::string(DEPRECATED_VK_LAYER_VALIDATE_GPU_BASED) +
                                              " setting was set, use " + std::string(VK_LAYER_PRINTF_ENABLE) + " instead.");
            }
        }
    }

    // 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->disables, stateless_checks, VK_LAYER_STATELESS_PARAM);
        SetValidationSetting(layer_setting_set, settings_data->disables, thread_safety, VK_LAYER_THREAD_SAFETY);
        SetValidationSetting(layer_setting_set, settings_data->disables, core_checks, VK_LAYER_VALIDATE_CORE);
        SetValidationSetting(layer_setting_set, settings_data->disables, command_buffer_state, VK_LAYER_CHECK_COMMAND_BUFFER);
        SetValidationSetting(layer_setting_set, settings_data->disables, object_in_use, VK_LAYER_CHECK_OBJECT_IN_USE);
        SetValidationSetting(layer_setting_set, settings_data->disables, query_validation, VK_LAYER_CHECK_QUERY);
        SetValidationSetting(layer_setting_set, settings_data->disables, image_layout_validation, VK_LAYER_CHECK_IMAGE_LAYOUT);
        SetValidationSetting(layer_setting_set, settings_data->disables, handle_wrapping, VK_LAYER_UNIQUE_HANDLES);
        SetValidationSetting(layer_setting_set, settings_data->disables, object_tracking, VK_LAYER_OBJECT_LIFETIME);
        SetValidationSetting(layer_setting_set, settings_data->disables, shader_validation, VK_LAYER_CHECK_SHADERS);
        SetValidationSetting(layer_setting_set, settings_data->disables, 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->enables[debug_printf_validation]) {
        gpuav_settings.debug_printf_enabled = true;
        if (!settings_data->enables[gpu_validation]) {
            gpuav_settings.SetOnlyDebugPrintf();
        }
    } else if (gpuav_settings.debug_printf_enabled) {
        // enabled the new way, but chassis uses this to create Validation Object
        settings_data->enables[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->enables[debug_printf_validation] = true;
            settings_data->enables[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->disables, VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT);
            SetValidationFeatureDisable(settings_data->disables, VK_VALIDATION_FEATURE_DISABLE_THREAD_SAFETY_EXT);
            SetValidationFeatureDisable(settings_data->disables, VK_VALIDATION_FEATURE_DISABLE_API_PARAMETERS_EXT);
            SetValidationFeatureDisable(settings_data->disables, VK_VALIDATION_FEATURE_DISABLE_OBJECT_LIFETIMES_EXT);
            SetValidationFeatureDisable(settings_data->disables, VK_VALIDATION_FEATURE_DISABLE_CORE_CHECKS_EXT);
            SetValidationFeatureDisable(settings_data->disables, 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->enables[gpu_validation] = true;
        }
    }

    // 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->enables[gpu_validation] = false;
        }
    }

    if (settings_data->enables[gpu_validation] && !settings_data->disables[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.");
    }

    // 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) {
        settings_data->debug_report->DebugLogMsg(kWarningBit, {}, warning.c_str(), "VALIDATION-SETTINGS");
    }

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