// SPDX-License-Identifier: GPL-2.0-or-later
// Copyright © 2023 Skyline Team and Contributors (https://github.com/skyline-emu/)
// Copyright © 2015-2023 The Khronos Group Inc.
// Copyright © 2015-2023 Valve Corporation
// Copyright © 2015-2023 LunarG, Inc.

#include <unordered_map>
#include "common/enum.h"
#include "video_core/texture_cache/host_compatibility.h"

namespace VideoCore {

/**
 * @brief All classes of format compatibility according to the Vulkan specification
 * @url
 * https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f/layers/generated/vk_format_utils.h#L47-L131
 */
enum class CompatibilityClass {
    NONE = 0,
    _128BIT = 1 << 0,
    _16BIT = 1 << 1,
    _192BIT = 1 << 2,
    _24BIT = 1 << 3,
    _256BIT = 1 << 4,
    _32BIT = 1 << 5,
    _48BIT = 1 << 6,
    _64BIT = 1 << 7,
    _8BIT = 1 << 8,
    _96BIT = 1 << 9,
    BC1_RGB = 1 << 10,
    BC1_RGBA = 1 << 11,
    BC2 = 1 << 12,
    BC3 = 1 << 13,
    BC4 = 1 << 14,
    BC5 = 1 << 15,
    BC6H = 1 << 16,
    BC7 = 1 << 17,
    D16 = 1 << 18,
    D16S8 = 1 << 19,
    D24 = 1 << 20,
    D24S8 = 1 << 21,
    D32 = 1 << 22,
    D32S8 = 1 << 23,
    S8 = 1 << 24,
};
DECLARE_ENUM_FLAG_OPERATORS(CompatibilityClass)

/**
 * @brief The format compatibility class according to the Vulkan specification
 * @url
 * https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#formats-compatibility-classes
 * @url
 * https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/d37c676f/layers/generated/vk_format_utils.cpp#L70-L812
 */
static const std::unordered_map<vk::Format, CompatibilityClass> FORMAT_TABLE = {
    {vk::Format::eA1R5G5B5UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eA2B10G10R10SintPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2B10G10R10SnormPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2B10G10R10SscaledPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2B10G10R10UintPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2B10G10R10UnormPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2B10G10R10UscaledPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2R10G10B10SintPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2R10G10B10SnormPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2R10G10B10SscaledPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2R10G10B10UintPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2R10G10B10UnormPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA2R10G10B10UscaledPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA4B4G4R4UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eA4R4G4B4UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eA8B8G8R8SintPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA8B8G8R8SnormPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA8B8G8R8SrgbPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA8B8G8R8SscaledPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA8B8G8R8UintPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA8B8G8R8UnormPack32, CompatibilityClass::_32BIT},
    {vk::Format::eA8B8G8R8UscaledPack32, CompatibilityClass::_32BIT},
    {vk::Format::eB10G11R11UfloatPack32, CompatibilityClass::_32BIT},
    {vk::Format::eB4G4R4A4UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eB5G5R5A1UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eB5G6R5UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eB8G8R8A8Sint, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8A8Snorm, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8A8Srgb, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8A8Sscaled, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8A8Uint, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8A8Unorm, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8A8Uscaled, CompatibilityClass::_32BIT},
    {vk::Format::eB8G8R8Sint, CompatibilityClass::_24BIT},
    {vk::Format::eB8G8R8Snorm, CompatibilityClass::_24BIT},
    {vk::Format::eB8G8R8Srgb, CompatibilityClass::_24BIT},
    {vk::Format::eB8G8R8Sscaled, CompatibilityClass::_24BIT},
    {vk::Format::eB8G8R8Uint, CompatibilityClass::_24BIT},
    {vk::Format::eB8G8R8Unorm, CompatibilityClass::_24BIT},
    {vk::Format::eB8G8R8Uscaled, CompatibilityClass::_24BIT},
    {vk::Format::eBc1RgbaSrgbBlock, CompatibilityClass::BC1_RGBA | CompatibilityClass::_64BIT},
    {vk::Format::eBc1RgbaUnormBlock, CompatibilityClass::BC1_RGBA | CompatibilityClass::_64BIT},
    {vk::Format::eBc1RgbSrgbBlock, CompatibilityClass::BC1_RGB | CompatibilityClass::_64BIT},
    {vk::Format::eBc1RgbUnormBlock, CompatibilityClass::BC1_RGB | CompatibilityClass::_64BIT},
    {vk::Format::eBc2SrgbBlock, CompatibilityClass::BC2 | CompatibilityClass::_128BIT},
    {vk::Format::eBc2UnormBlock, CompatibilityClass::BC2 | CompatibilityClass::_128BIT},
    {vk::Format::eBc3SrgbBlock, CompatibilityClass::BC3 | CompatibilityClass::_128BIT},
    {vk::Format::eBc3UnormBlock, CompatibilityClass::BC3 | CompatibilityClass::_128BIT},
    {vk::Format::eBc4SnormBlock, CompatibilityClass::BC4 | CompatibilityClass::_64BIT},
    {vk::Format::eBc4UnormBlock, CompatibilityClass::BC4 | CompatibilityClass::_64BIT},
    {vk::Format::eBc5SnormBlock, CompatibilityClass::BC5 | CompatibilityClass::_128BIT},
    {vk::Format::eBc5UnormBlock, CompatibilityClass::BC5 | CompatibilityClass::_128BIT},
    {vk::Format::eBc6HSfloatBlock, CompatibilityClass::BC6H | CompatibilityClass::_128BIT},
    {vk::Format::eBc6HUfloatBlock, CompatibilityClass::BC6H | CompatibilityClass::_128BIT},
    {vk::Format::eBc7SrgbBlock, CompatibilityClass::BC7 | CompatibilityClass::_128BIT},
    {vk::Format::eBc7UnormBlock, CompatibilityClass::BC7 | CompatibilityClass::_128BIT},
    {vk::Format::eD16Unorm, CompatibilityClass::D16},
    {vk::Format::eD16UnormS8Uint, CompatibilityClass::D16S8},
    {vk::Format::eD24UnormS8Uint, CompatibilityClass::D24S8},
    {vk::Format::eD32Sfloat, CompatibilityClass::D32},
    {vk::Format::eD32SfloatS8Uint, CompatibilityClass::D32S8},
    {vk::Format::eE5B9G9R9UfloatPack32, CompatibilityClass::_32BIT},
    {vk::Format::eR10X6G10X6Unorm2Pack16, CompatibilityClass::_32BIT},
    {vk::Format::eR10X6UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eR12X4G12X4Unorm2Pack16, CompatibilityClass::_32BIT},
    {vk::Format::eR12X4UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eR16G16B16A16Sfloat, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16A16Sint, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16A16Snorm, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16A16Sscaled, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16A16Uint, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16A16Unorm, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16A16Uscaled, CompatibilityClass::_64BIT},
    {vk::Format::eR16G16B16Sfloat, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16B16Sint, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16B16Snorm, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16B16Sscaled, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16B16Uint, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16B16Unorm, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16B16Uscaled, CompatibilityClass::_48BIT},
    {vk::Format::eR16G16Sfloat, CompatibilityClass::_32BIT},
    {vk::Format::eR16G16Sint, CompatibilityClass::_32BIT},
    {vk::Format::eR16G16Snorm, CompatibilityClass::_32BIT},
    {vk::Format::eR16G16Sscaled, CompatibilityClass::_32BIT},
    {vk::Format::eR16G16Uint, CompatibilityClass::_32BIT},
    {vk::Format::eR16G16Unorm, CompatibilityClass::_32BIT},
    {vk::Format::eR16G16Uscaled, CompatibilityClass::_32BIT},
    {vk::Format::eR16Sfloat, CompatibilityClass::_16BIT},
    {vk::Format::eR16Sint, CompatibilityClass::_16BIT},
    {vk::Format::eR16Snorm, CompatibilityClass::_16BIT},
    {vk::Format::eR16Sscaled, CompatibilityClass::_16BIT},
    {vk::Format::eR16Uint, CompatibilityClass::_16BIT},
    {vk::Format::eR16Unorm, CompatibilityClass::_16BIT},
    {vk::Format::eR16Uscaled, CompatibilityClass::_16BIT},
    {vk::Format::eR32G32B32A32Sfloat, CompatibilityClass::_128BIT},
    {vk::Format::eR32G32B32A32Sint, CompatibilityClass::_128BIT},
    {vk::Format::eR32G32B32A32Uint, CompatibilityClass::_128BIT},
    {vk::Format::eR32G32B32Sfloat, CompatibilityClass::_96BIT},
    {vk::Format::eR32G32B32Sint, CompatibilityClass::_96BIT},
    {vk::Format::eR32G32B32Uint, CompatibilityClass::_96BIT},
    {vk::Format::eR32G32Sfloat, CompatibilityClass::_64BIT},
    {vk::Format::eR32G32Sint, CompatibilityClass::_64BIT},
    {vk::Format::eR32G32Uint, CompatibilityClass::_64BIT},
    {vk::Format::eR32Sfloat, CompatibilityClass::_32BIT},
    {vk::Format::eR32Sint, CompatibilityClass::_32BIT},
    {vk::Format::eR32Uint, CompatibilityClass::_32BIT},
    {vk::Format::eR4G4B4A4UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eR4G4UnormPack8, CompatibilityClass::_8BIT},
    {vk::Format::eR5G5B5A1UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eR5G6B5UnormPack16, CompatibilityClass::_16BIT},
    {vk::Format::eR64G64B64A64Sfloat, CompatibilityClass::_256BIT},
    {vk::Format::eR64G64B64A64Sint, CompatibilityClass::_256BIT},
    {vk::Format::eR64G64B64A64Uint, CompatibilityClass::_256BIT},
    {vk::Format::eR64G64B64Sfloat, CompatibilityClass::_192BIT},
    {vk::Format::eR64G64B64Sint, CompatibilityClass::_192BIT},
    {vk::Format::eR64G64B64Uint, CompatibilityClass::_192BIT},
    {vk::Format::eR64G64Sfloat, CompatibilityClass::_128BIT},
    {vk::Format::eR64G64Sint, CompatibilityClass::_128BIT},
    {vk::Format::eR64G64Uint, CompatibilityClass::_128BIT},
    {vk::Format::eR64Sfloat, CompatibilityClass::_64BIT},
    {vk::Format::eR64Sint, CompatibilityClass::_64BIT},
    {vk::Format::eR64Uint, CompatibilityClass::_64BIT},
    {vk::Format::eR8G8B8A8Sint, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8A8Snorm, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8A8Srgb, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8A8Sscaled, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8A8Uint, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8A8Unorm, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8A8Uscaled, CompatibilityClass::_32BIT},
    {vk::Format::eR8G8B8Sint, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8B8Snorm, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8B8Srgb, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8B8Sscaled, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8B8Uint, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8B8Unorm, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8B8Uscaled, CompatibilityClass::_24BIT},
    {vk::Format::eR8G8Sint, CompatibilityClass::_16BIT},
    {vk::Format::eR8G8Snorm, CompatibilityClass::_16BIT},
    {vk::Format::eR8G8Srgb, CompatibilityClass::_16BIT},
    {vk::Format::eR8G8Sscaled, CompatibilityClass::_16BIT},
    {vk::Format::eR8G8Uint, CompatibilityClass::_16BIT},
    {vk::Format::eR8G8Unorm, CompatibilityClass::_16BIT},
    {vk::Format::eR8G8Uscaled, CompatibilityClass::_16BIT},
    {vk::Format::eR8Sint, CompatibilityClass::_8BIT},
    {vk::Format::eR8Snorm, CompatibilityClass::_8BIT},
    {vk::Format::eR8Srgb, CompatibilityClass::_8BIT},
    {vk::Format::eR8Sscaled, CompatibilityClass::_8BIT},
    {vk::Format::eR8Uint, CompatibilityClass::_8BIT},
    {vk::Format::eR8Unorm, CompatibilityClass::_8BIT},
    {vk::Format::eR8Uscaled, CompatibilityClass::_8BIT},
    {vk::Format::eS8Uint, CompatibilityClass::S8},
    {vk::Format::eX8D24UnormPack32, CompatibilityClass::D24},
    {vk::Format::eUndefined, CompatibilityClass::NONE},
};

bool IsVulkanFormatCompatible(vk::Format base, vk::Format view) {
    if (base == view) {
        return true;
    }
    const auto base_comp = FORMAT_TABLE.at(base);
    const auto view_comp = FORMAT_TABLE.at(view);
    return (base_comp & view_comp) == view_comp;
}

} // namespace VideoCore
