code:
// SPDX-FileCopyrightText: 2014 Citra Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <climits>
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/gpu.h"
#include "video_core/textures/texture.h"

namespace VideoCore::Surface {

enum class PixelFormat {
    A8B8G8R8_UNORM,
    A8B8G8R8_SNORM,
    A8B8G8R8_SINT,
    A8B8G8R8_UINT,
    R5G6B5_UNORM,
    B5G6R5_UNORM,
    A1R5G5B5_UNORM,
    A2B10G10R10_UNORM,
    A2B10G10R10_UINT,
    A2R10G10B10_UNORM,
    A1B5G5R5_UNORM,
    A5B5G5R1_UNORM,
    R8_UNORM,
    R8_SNORM,
    R8_SINT,
    R8_UINT,
    R16G16B16A16_FLOAT,
    R16G16B16A16_UNORM,
    R16G16B16A16_SNORM,
    R16G16B16A16_SINT,
    R16G16B16A16_UINT,
    B10G11R11_FLOAT,
    R32G32B32A32_UINT,
    BC1_RGBA_UNORM,
    BC2_UNORM,
    BC3_UNORM,
    BC4_UNORM,
    BC4_SNORM,
    BC5_UNORM,
    BC5_SNORM,
    BC7_UNORM,
    BC6H_UFLOAT,
    BC6H_SFLOAT,
    ASTC_2D_4X4_UNORM,
    B8G8R8A8_UNORM,
    R32G32B32A32_FLOAT,
    R32G32B32A32_SINT,
    R32G32_FLOAT,
    R32G32_SINT,
    R32_FLOAT,
    R16_FLOAT,
    R16_UNORM,
    R16_SNORM,
    R16_UINT,
    R16_SINT,
    R16G16_UNORM,
    R16G16_FLOAT,
    R16G16_UINT,
    R16G16_SINT,
    R16G16_SNORM,
    R32G32B32_FLOAT,
    A8B8G8R8_SRGB,
    R8G8_UNORM,
    R8G8_SNORM,
    R8G8_SINT,
    R8G8_UINT,
    R32G32_UINT,
    R16G16B16X16_FLOAT,
    R32_UINT,
    R32_SINT,
    ASTC_2D_8X8_UNORM,
    ASTC_2D_8X5_UNORM,
    ASTC_2D_5X4_UNORM,
    B8G8R8A8_SRGB,
    BC1_RGBA_SRGB,
    BC2_SRGB,
    BC3_SRGB,
    BC7_SRGB,
    A4B4G4R4_UNORM,
    G4R4_UNORM,
    ASTC_2D_4X4_SRGB,
    ASTC_2D_8X8_SRGB,
    ASTC_2D_8X5_SRGB,
    ASTC_2D_5X4_SRGB,
    ASTC_2D_5X5_UNORM,
    ASTC_2D_5X5_SRGB,
    ASTC_2D_10X8_UNORM,
    ASTC_2D_10X8_SRGB,
    ASTC_2D_6X6_UNORM,
    ASTC_2D_6X6_SRGB,
    ASTC_2D_10X6_UNORM,
    ASTC_2D_10X6_SRGB,
    ASTC_2D_10X5_UNORM,
    ASTC_2D_10X5_SRGB,
    ASTC_2D_10X10_UNORM,
    ASTC_2D_10X10_SRGB,
    ASTC_2D_12X10_UNORM,
    ASTC_2D_12X10_SRGB,
    ASTC_2D_12X12_UNORM,
    ASTC_2D_12X12_SRGB,
    ASTC_2D_8X6_UNORM,
    ASTC_2D_8X6_SRGB,
    ASTC_2D_6X5_UNORM,
    ASTC_2D_6X5_SRGB,
    E5B9G9R9_FLOAT,

    MaxColorFormat,

    // Depth formats
    D32_FLOAT = MaxColorFormat,
    D16_UNORM,
    X8_D24_UNORM,

    MaxDepthFormat,

    // Stencil formats
    S8_UINT = MaxDepthFormat,
    MaxStencilFormat,

    // DepthStencil formats
    D24_UNORM_S8_UINT = MaxStencilFormat,
    S8_UINT_D24_UNORM,
    D32_FLOAT_S8_UINT,

    MaxDepthStencilFormat,

    Max = MaxDepthStencilFormat,
    Invalid = 255,
};
constexpr std::size_t MaxPixelFormat = static_cast<std::size_t>(PixelFormat::Max);

enum class SurfaceType {
    ColorTexture = 0,
    Depth = 1,
    Stencil = 2,
    DepthStencil = 3,
    Invalid = 4,
};

enum class SurfaceTarget {
    Texture1D,
    TextureBuffer,
    Texture2D,
    Texture3D,
    Texture1DArray,
    Texture2DArray,
    TextureCubemap,
    TextureCubeArray,
};

constexpr std::array<u8, MaxPixelFormat> BLOCK_WIDTH_TABLE = {{
    1,  // A8B8G8R8_UNORM
    1,  // A8B8G8R8_SNORM
    1,  // A8B8G8R8_SINT
    1,  // A8B8G8R8_UINT
    1,  // R5G6B5_UNORM
    1,  // B5G6R5_UNORM
    1,  // A1R5G5B5_UNORM
    1,  // A2B10G10R10_UNORM
    1,  // A2B10G10R10_UINT
    1,  // A2R10G10B10_UNORM
    1,  // A1B5G5R5_UNORM
    1,  // A5B5G5R1_UNORM
    1,  // R8_UNORM
    1,  // R8_SNORM
    1,  // R8_SINT
    1,  // R8_UINT
    1,  // R16G16B16A16_FLOAT
    1,  // R16G16B16A16_UNORM
    1,  // R16G16B16A16_SNORM
    1,  // R16G16B16A16_SINT
    1,  // R16G16B16A16_UINT
    1,  // B10G11R11_FLOAT
    1,  // R32G32B32A32_UINT
    4,  // BC1_RGBA_UNORM
    4,  // BC2_UNORM
    4,  // BC3_UNORM
    4,  // BC4_UNORM
    4,  // BC4_SNORM
    4,  // BC5_UNORM
    4,  // BC5_SNORM
    4,  // BC7_UNORM
    4,  // BC6H_UFLOAT
    4,  // BC6H_SFLOAT
    4,  // ASTC_2D_4X4_UNORM
    1,  // B8G8R8A8_UNORM
    1,  // R32G32B32A32_FLOAT
    1,  // R32G32B32A32_SINT
    1,  // R32G32_FLOAT
    1,  // R32G32_SINT
    1,  // R32_FLOAT
    1,  // R16_FLOAT
    1,  // R16_UNORM
    1,  // R16_SNORM
    1,  // R16_UINT
    1,  // R16_SINT
    1,  // R16G16_UNORM
    1,  // R16G16_FLOAT
    1,  // R16G16_UINT
    1,  // R16G16_SINT
    1,  // R16G16_SNORM
    1,  // R32G32B32_FLOAT
    1,  // A8B8G8R8_SRGB
    1,  // R8G8_UNORM
    1,  // R8G8_SNORM
    1,  // R8G8_SINT
    1,  // R8G8_UINT
    1,  // R32G32_UINT
    1,  // R16G16B16X16_FLOAT
    1,  // R32_UINT
    1,  // R32_SINT
    8,  // ASTC_2D_8X8_UNORM
    8,  // ASTC_2D_8X5_UNORM
    5,  // ASTC_2D_5X4_UNORM
    1,  // B8G8R8A8_SRGB
    4,  // BC1_RGBA_SRGB
    4,  // BC2_SRGB
    4,  // BC3_SRGB
    4,  // BC7_SRGB
    1,  // A4B4G4R4_UNORM
    1,  // G4R4_UNORM
    4,  // ASTC_2D_4X4_SRGB
    8,  // ASTC_2D_8X8_SRGB
    8,  // ASTC_2D_8X5_SRGB
    5,  // ASTC_2D_5X4_SRGB
    5,  // ASTC_2D_5X5_UNORM
    5,  // ASTC_2D_5X5_SRGB
    10, // ASTC_2D_10X8_UNORM
    10, // ASTC_2D_10X8_SRGB
    6,  // ASTC_2D_6X6_UNORM
    6,  // ASTC_2D_6X6_SRGB
    10, // ASTC_2D_10X6_UNORM
    10, // ASTC_2D_10X6_SRGB
    10, // ASTC_2D_10X5_UNORM
    10, // ASTC_2D_10X5_SRGB
    10, // ASTC_2D_10X10_UNORM
    10, // ASTC_2D_10X10_SRGB
    12, // ASTC_2D_12X10_UNORM
    12, // ASTC_2D_12X10_SRGB
    12, // ASTC_2D_12X12_UNORM
    12, // ASTC_2D_12X12_SRGB
    8,  // ASTC_2D_8X6_UNORM
    8,  // ASTC_2D_8X6_SRGB
    6,  // ASTC_2D_6X5_UNORM
    6,  // ASTC_2D_6X5_SRGB
    1,  // E5B9G9R9_FLOAT
    1,  // D32_FLOAT
    1,  // D16_UNORM
    1,  // X8_D24_UNORM
    1,  // S8_UINT
    1,  // D24_UNORM_S8_UINT
    1,  // S8_UINT_D24_UNORM
    1,  // D32_FLOAT_S8_UINT
}};

constexpr u32 DefaultBlockWidth(PixelFormat format) {
    ASSERT(static_cast<std::size_t>(format) < BLOCK_WIDTH_TABLE.size());
    return BLOCK_WIDTH_TABLE[static_cast<std::size_t>(format)];
}

constexpr std::array<u8, MaxPixelFormat> BLOCK_HEIGHT_TABLE = {{
    1,  // A8B8G8R8_UNORM
    1,  // A8B8G8R8_SNORM
    1,  // A8B8G8R8_SINT
    1,  // A8B8G8R8_UINT
    1,  // R5G6B5_UNORM
    1,  // B5G6R5_UNORM
    1,  // A1R5G5B5_UNORM
    1,  // A2B10G10R10_UNORM
    1,  // A2B10G10R10_UINT
    1,  // A2R10G10B10_UNORM
    1,  // A1B5G5R5_UNORM
    1,  // A5B5G5R1_UNORM
    1,  // R8_UNORM
    1,  // R8_SNORM
    1,  // R8_SINT
    1,  // R8_UINT
    1,  // R16G16B16A16_FLOAT
    1,  // R16G16B16A16_UNORM
    1,  // R16G16B16A16_SNORM
    1,  // R16G16B16A16_SINT
    1,  // R16G16B16A16_UINT
    1,  // B10G11R11_FLOAT
    1,  // R32G32B32A32_UINT
    4,  // BC1_RGBA_UNORM
    4,  // BC2_UNORM
    4,  // BC3_UNORM
    4,  // BC4_UNORM
    4,  // BC4_SNORM
    4,  // BC5_UNORM
    4,  // BC5_SNORM
    4,  // BC7_UNORM
    4,  // BC6H_UFLOAT
    4,  // BC6H_SFLOAT
    4,  // ASTC_2D_4X4_UNORM
    1,  // B8G8R8A8_UNORM
    1,  // R32G32B32A32_FLOAT
    1,  // R32G32B32A32_SINT
    1,  // R32G32_FLOAT
    1,  // R32G32_SINT
    1,  // R32_FLOAT
    1,  // R16_FLOAT
    1,  // R16_UNORM
    1,  // R16_SNORM
    1,  // R16_UINT
    1,  // R16_SINT
    1,  // R16G16_UNORM
    1,  // R16G16_FLOAT
    1,  // R16G16_UINT
    1,  // R16G16_SINT
    1,  // R16G16_SNORM
    1,  // R32G32B32_FLOAT
    1,  // A8B8G8R8_SRGB
    1,  // R8G8_UNORM
    1,  // R8G8_SNORM
    1,  // R8G8_SINT
    1,  // R8G8_UINT
    1,  // R32G32_UINT
    1,  // R16G16B16X16_FLOAT
    1,  // R32_UINT
    1,  // R32_SINT
    8,  // ASTC_2D_8X8_UNORM
    5,  // ASTC_2D_8X5_UNORM
    4,  // ASTC_2D_5X4_UNORM
    1,  // B8G8R8A8_SRGB
    4,  // BC1_RGBA_SRGB
    4,  // BC2_SRGB
    4,  // BC3_SRGB
    4,  // BC7_SRGB
    1,  // A4B4G4R4_UNORM
    1,  // G4R4_UNORM
    4,  // ASTC_2D_4X4_SRGB
    8,  // ASTC_2D_8X8_SRGB
    5,  // ASTC_2D_8X5_SRGB
    4,  // ASTC_2D_5X4_SRGB
    5,  // ASTC_2D_5X5_UNORM
    5,  // ASTC_2D_5X5_SRGB
    8,  // ASTC_2D_10X8_UNORM
    8,  // ASTC_2D_10X8_SRGB
    6,  // ASTC_2D_6X6_UNORM
    6,  // ASTC_2D_6X6_SRGB
    6,  // ASTC_2D_10X6_UNORM
    6,  // ASTC_2D_10X6_SRGB
    5,  // ASTC_2D_10X5_UNORM
    5,  // ASTC_2D_10X5_SRGB
    10, // ASTC_2D_10X10_UNORM
    10, // ASTC_2D_10X10_SRGB
    10, // ASTC_2D_12X10_UNORM
    10, // ASTC_2D_12X10_SRGB
    12, // ASTC_2D_12X12_UNORM
    12, // ASTC_2D_12X12_SRGB
    6,  // ASTC_2D_8X6_UNORM
    6,  // ASTC_2D_8X6_SRGB
    5,  // ASTC_2D_6X5_UNORM
    5,  // ASTC_2D_6X5_SRGB
    1,  // E5B9G9R9_FLOAT
    1,  // D32_FLOAT
    1,  // D16_UNORM
    1,  // X8_D24_UNORM
    1,  // S8_UINT
    1,  // D24_UNORM_S8_UINT
    1,  // S8_UINT_D24_UNORM
    1,  // D32_FLOAT_S8_UINT
}};

constexpr u32 DefaultBlockHeight(PixelFormat format) {
    ASSERT(static_cast<std::size_t>(format) < BLOCK_HEIGHT_TABLE.size());
    return BLOCK_HEIGHT_TABLE[static_cast<std::size_t>(format)];
}

constexpr std::array<u8, MaxPixelFormat> BITS_PER_BLOCK_TABLE = {{
    32,  // A8B8G8R8_UNORM
    32,  // A8B8G8R8_SNORM
    32,  // A8B8G8R8_SINT
    32,  // A8B8G8R8_UINT
    16,  // R5G6B5_UNORM
    16,  // B5G6R5_UNORM
    16,  // A1R5G5B5_UNORM
    32,  // A2B10G10R10_UNORM
    32,  // A2B10G10R10_UINT
    32,  // A2R10G10B10_UNORM
    16,  // A1B5G5R5_UNORM
    16,  // A5B5G5R1_UNORM
    8,   // R8_UNORM
    8,   // R8_SNORM
    8,   // R8_SINT
    8,   // R8_UINT
    64,  // R16G16B16A16_FLOAT
    64,  // R16G16B16A16_UNORM
    64,  // R16G16B16A16_SNORM
    64,  // R16G16B16A16_SINT
    64,  // R16G16B16A16_UINT
    32,  // B10G11R11_FLOAT
    128, // R32G32B32A32_UINT
    64,  // BC1_RGBA_UNORM
    128, // BC2_UNORM
    128, // BC3_UNORM
    64,  // BC4_UNORM
    64,  // BC4_SNORM
    128, // BC5_UNORM
    128, // BC5_SNORM
    128, // BC7_UNORM
    128, // BC6H_UFLOAT
    128, // BC6H_SFLOAT
    128, // ASTC_2D_4X4_UNORM
    32,  // B8G8R8A8_UNORM
    128, // R32G32B32A32_FLOAT
    128, // R32G32B32A32_SINT
    64,  // R32G32_FLOAT
    64,  // R32G32_SINT
    32,  // R32_FLOAT
    16,  // R16_FLOAT
    16,  // R16_UNORM
    16,  // R16_SNORM
    16,  // R16_UINT
    16,  // R16_SINT
    32,  // R16G16_UNORM
    32,  // R16G16_FLOAT
    32,  // R16G16_UINT
    32,  // R16G16_SINT
    32,  // R16G16_SNORM
    96,  // R32G32B32_FLOAT
    32,  // A8B8G8R8_SRGB
    16,  // R8G8_UNORM
    16,  // R8G8_SNORM
    16,  // R8G8_SINT
    16,  // R8G8_UINT
    64,  // R32G32_UINT
    64,  // R16G16B16X16_FLOAT
    32,  // R32_UINT
    32,  // R32_SINT
    128, // ASTC_2D_8X8_UNORM
    128, // ASTC_2D_8X5_UNORM
    128, // ASTC_2D_5X4_UNORM
    32,  // B8G8R8A8_SRGB
    64,  // BC1_RGBA_SRGB
    128, // BC2_SRGB
    128, // BC3_SRGB
    128, // BC7_UNORM
    16,  // A4B4G4R4_UNORM
    8,   // G4R4_UNORM
    128, // ASTC_2D_4X4_SRGB
    128, // ASTC_2D_8X8_SRGB
    128, // ASTC_2D_8X5_SRGB
    128, // ASTC_2D_5X4_SRGB
    128, // ASTC_2D_5X5_UNORM
    128, // ASTC_2D_5X5_SRGB
    128, // ASTC_2D_10X8_UNORM
    128, // ASTC_2D_10X8_SRGB
    128, // ASTC_2D_6X6_UNORM
    128, // ASTC_2D_6X6_SRGB
    128, // ASTC_2D_10X6_UNORM
    128, // ASTC_2D_10X6_SRGB
    128, // ASTC_2D_10X5_UNORM
    128, // ASTC_2D_10X5_SRGB
    128, // ASTC_2D_10X10_UNORM
    128, // ASTC_2D_10X10_SRGB
    128, // ASTC_2D_12X10_UNORM
    128, // ASTC_2D_12X10_SRGB
    128, // ASTC_2D_12X12_UNORM
    128, // ASTC_2D_12X12_SRGB
    128, // ASTC_2D_8X6_UNORM
    128, // ASTC_2D_8X6_SRGB
    128, // ASTC_2D_6X5_UNORM
    128, // ASTC_2D_6X5_SRGB
    32,  // E5B9G9R9_FLOAT
    32,  // D32_FLOAT
    16,  // D16_UNORM
    32,  // X8_D24_UNORM
    8,   // S8_UINT
    32,  // D24_UNORM_S8_UINT
    32,  // S8_UINT_D24_UNORM
    64,  // D32_FLOAT_S8_UINT
}};

constexpr u32 BitsPerBlock(PixelFormat format) {
    ASSERT(static_cast<std::size_t>(format) < BITS_PER_BLOCK_TABLE.size());
    return BITS_PER_BLOCK_TABLE[static_cast<std::size_t>(format)];
}

/// Returns the sizer in bytes of the specified pixel format
constexpr u32 BytesPerBlock(PixelFormat pixel_format) {
    return BitsPerBlock(pixel_format) / CHAR_BIT;
}

SurfaceTarget SurfaceTargetFromTextureType(Tegra::Texture::TextureType texture_type);

bool SurfaceTargetIsLayered(SurfaceTarget target);

bool SurfaceTargetIsArray(SurfaceTarget target);

PixelFormat PixelFormatFromDepthFormat(Tegra::DepthFormat format);

PixelFormat PixelFormatFromRenderTargetFormat(Tegra::RenderTargetFormat format);

PixelFormat PixelFormatFromGPUPixelFormat(Service::android::PixelFormat format);

SurfaceType GetFormatType(PixelFormat pixel_format);

bool IsPixelFormatASTC(PixelFormat format);

bool IsPixelFormatBCn(PixelFormat format);

bool IsPixelFormatSRGB(PixelFormat format);

bool IsPixelFormatInteger(PixelFormat format);

bool IsPixelFormatSignedInteger(PixelFormat format);

size_t PixelComponentSizeBitsInteger(PixelFormat format);

std::pair<u32, u32> GetASTCBlockSize(PixelFormat format);

u64 TranscodedAstcSize(u64 base_size, PixelFormat format);

} // namespace VideoCore::Surface


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <array>
#include <bit>

#include "common/alignment.h"
#include "common/common_types.h"
#include "common/div_ceil.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/accelerated_swizzle.h"
#include "video_core/texture_cache/util.h"
#include "video_core/textures/decoders.h"

namespace VideoCommon::Accelerated {

using Tegra::Texture::GOB_SIZE_SHIFT;
using Tegra::Texture::GOB_SIZE_X;
using Tegra::Texture::GOB_SIZE_X_SHIFT;
using Tegra::Texture::GOB_SIZE_Y_SHIFT;
using VideoCore::Surface::BytesPerBlock;

BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(const SwizzleParameters& swizzle,
                                                          const ImageInfo& info) {
    const Extent3D block = swizzle.block;
    const Extent3D num_tiles = swizzle.num_tiles;
    const u32 bytes_per_block = BytesPerBlock(info.format);
    const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level);
    const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block;
    const u32 gobs_in_x = Common::DivCeilLog2(stride, GOB_SIZE_X_SHIFT);
    return BlockLinearSwizzle2DParams{
        .origin{0, 0, 0},
        .destination{0, 0, 0},
        .bytes_per_block_log2 = static_cast<u32>(std::countr_zero(bytes_per_block)),
        .layer_stride = info.layer_stride,
        .block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth),
        .x_shift = GOB_SIZE_SHIFT + block.height + block.depth,
        .block_height = block.height,
        .block_height_mask = (1U << block.height) - 1,
    };
}

BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(const SwizzleParameters& swizzle,
                                                          const ImageInfo& info) {
    const Extent3D block = swizzle.block;
    const Extent3D num_tiles = swizzle.num_tiles;
    const u32 bytes_per_block = BytesPerBlock(info.format);
    const u32 stride_alignment = CalculateLevelStrideAlignment(info, swizzle.level);
    const u32 stride = Common::AlignUpLog2(num_tiles.width, stride_alignment) * bytes_per_block;

    const u32 gobs_in_x = (stride + GOB_SIZE_X - 1) >> GOB_SIZE_X_SHIFT;
    const u32 block_size = gobs_in_x << (GOB_SIZE_SHIFT + block.height + block.depth);
    const u32 slice_size =
        Common::DivCeilLog2(num_tiles.height, block.height + GOB_SIZE_Y_SHIFT) * block_size;
    return BlockLinearSwizzle3DParams{
        .origin{0, 0, 0},
        .destination{0, 0, 0},
        .bytes_per_block_log2 = static_cast<u32>(std::countr_zero(bytes_per_block)),
        .slice_size = slice_size,
        .block_size = block_size,
        .x_shift = GOB_SIZE_SHIFT + block.height + block.depth,
        .block_height = block.height,
        .block_height_mask = (1U << block.height) - 1,
        .block_depth = block.depth,
        .block_depth_mask = (1U << block.depth) - 1,
    };
}

} // namespace VideoCommon::Accelerated

repo_name: litucks/torzu

license: mit


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>

#include "common/common_types.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon::Accelerated {

struct BlockLinearSwizzle2DParams {
    alignas(16) std::array<u32, 3> origin;
    alignas(16) std::array<s32, 3> destination;
    u32 bytes_per_block_log2;
    u32 layer_stride;
    u32 block_size;
    u32 x_shift;
    u32 block_height;
    u32 block_height_mask;
};

struct BlockLinearSwizzle3DParams {
    std::array<u32, 3> origin;
    std::array<s32, 3> destination;
    u32 bytes_per_block_log2;
    u32 slice_size;
    u32 block_size;
    u32 x_shift;
    u32 block_height;
    u32 block_height_mask;
    u32 block_depth;
    u32 block_depth_mask;
};

[[nodiscard]] BlockLinearSwizzle2DParams MakeBlockLinearSwizzle2DParams(
    const SwizzleParameters& swizzle, const ImageInfo& info);

[[nodiscard]] BlockLinearSwizzle3DParams MakeBlockLinearSwizzle3DParams(
    const SwizzleParameters& swizzle, const ImageInfo& info);

} // namespace VideoCommon::Accelerated


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <array>
#include <span>
#include <bc_decoder.h>

#include "common/common_types.h"
#include "video_core/texture_cache/decode_bc.h"

namespace VideoCommon {

namespace {
constexpr u32 BLOCK_SIZE = 4;

using VideoCore::Surface::PixelFormat;

constexpr bool IsSigned(PixelFormat pixel_format) {
    switch (pixel_format) {
    case PixelFormat::BC4_SNORM:
    case PixelFormat::BC4_UNORM:
    case PixelFormat::BC5_SNORM:
    case PixelFormat::BC5_UNORM:
    case PixelFormat::BC6H_SFLOAT:
    case PixelFormat::BC6H_UFLOAT:
        return true;
    default:
        return false;
    }
}

constexpr u32 BlockSize(PixelFormat pixel_format) {
    switch (pixel_format) {
    case PixelFormat::BC1_RGBA_SRGB:
    case PixelFormat::BC1_RGBA_UNORM:
    case PixelFormat::BC4_SNORM:
    case PixelFormat::BC4_UNORM:
        return 8;
    default:
        return 16;
    }
}
} // Anonymous namespace

u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format) {
    switch (pixel_format) {
    case PixelFormat::BC4_SNORM:
    case PixelFormat::BC4_UNORM:
        return 1;
    case PixelFormat::BC5_SNORM:
    case PixelFormat::BC5_UNORM:
        return 2;
    case PixelFormat::BC6H_SFLOAT:
    case PixelFormat::BC6H_UFLOAT:
        return 8;
    default:
        return 4;
    }
}

template <auto decompress, PixelFormat pixel_format>
void DecompressBlocks(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
                      bool is_signed = false) {
    const u32 out_bpp = ConvertedBytesPerBlock(pixel_format);
    const u32 block_size = BlockSize(pixel_format);
    const u32 width = copy.image_extent.width;
    const u32 height = copy.image_extent.height * copy.image_subresource.num_layers;
    const u32 depth = copy.image_extent.depth;
    const u32 block_width = std::min(width, BLOCK_SIZE);
    const u32 block_height = std::min(height, BLOCK_SIZE);
    const u32 pitch = width * out_bpp;
    size_t input_offset = 0;
    size_t output_offset = 0;
    for (u32 slice = 0; slice < depth; ++slice) {
        for (u32 y = 0; y < height; y += block_height) {
            size_t src_offset = input_offset;
            size_t dst_offset = output_offset;
            for (u32 x = 0; x < width; x += block_width) {
                const u8* src = input.data() + src_offset;
                u8* const dst = output.data() + dst_offset;
                if constexpr (IsSigned(pixel_format)) {
                    decompress(src, dst, x, y, width, height, is_signed);
                } else {
                    decompress(src, dst, x, y, width, height);
                }
                src_offset += block_size;
                dst_offset += block_width * out_bpp;
            }
            input_offset += copy.buffer_row_length * block_size / block_width;
            output_offset += block_height * pitch;
        }
    }
}

void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
                   VideoCore::Surface::PixelFormat pixel_format) {
    switch (pixel_format) {
    case PixelFormat::BC1_RGBA_UNORM:
    case PixelFormat::BC1_RGBA_SRGB:
        DecompressBlocks<bcn::DecodeBc1, PixelFormat::BC1_RGBA_UNORM>(input, output, copy);
        break;
    case PixelFormat::BC2_UNORM:
    case PixelFormat::BC2_SRGB:
        DecompressBlocks<bcn::DecodeBc2, PixelFormat::BC2_UNORM>(input, output, copy);
        break;
    case PixelFormat::BC3_UNORM:
    case PixelFormat::BC3_SRGB:
        DecompressBlocks<bcn::DecodeBc3, PixelFormat::BC3_UNORM>(input, output, copy);
        break;
    case PixelFormat::BC4_SNORM:
    case PixelFormat::BC4_UNORM:
        DecompressBlocks<bcn::DecodeBc4, PixelFormat::BC4_UNORM>(
            input, output, copy, pixel_format == PixelFormat::BC4_SNORM);
        break;
    case PixelFormat::BC5_SNORM:
    case PixelFormat::BC5_UNORM:
        DecompressBlocks<bcn::DecodeBc5, PixelFormat::BC5_UNORM>(
            input, output, copy, pixel_format == PixelFormat::BC5_SNORM);
        break;
    case PixelFormat::BC6H_SFLOAT:
    case PixelFormat::BC6H_UFLOAT:
        DecompressBlocks<bcn::DecodeBc6, PixelFormat::BC6H_UFLOAT>(
            input, output, copy, pixel_format == PixelFormat::BC6H_SFLOAT);
        break;
    case PixelFormat::BC7_SRGB:
    case PixelFormat::BC7_UNORM:
        DecompressBlocks<bcn::DecodeBc7, PixelFormat::BC7_UNORM>(input, output, copy);
        break;
    default:
        LOG_WARNING(HW_GPU, "Unimplemented BCn decompression {}", pixel_format);
    }
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <span>

#include "common/common_types.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon {

[[nodiscard]] u32 ConvertedBytesPerBlock(VideoCore::Surface::PixelFormat pixel_format);

void DecompressBCn(std::span<const u8> input, std::span<u8> output, BufferImageCopy& copy,
                   VideoCore::Surface::PixelFormat pixel_format);

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <algorithm>
#include <vector>

#include "common/common_types.h"
#include "common/div_ceil.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"

namespace VideoCommon {

template <typename Descriptor>
class DescriptorTable {
public:
    explicit DescriptorTable(Tegra::MemoryManager& gpu_memory_) : gpu_memory{gpu_memory_} {}

    [[nodiscard]] bool Synchronize(GPUVAddr gpu_addr, u32 limit) {
        [[likely]] if (current_gpu_addr == gpu_addr && current_limit == limit) { return false; }
        Refresh(gpu_addr, limit);
        return true;
    }

    void Invalidate() noexcept {
        std::ranges::fill(read_descriptors, 0);
    }

    [[nodiscard]] std::pair<Descriptor, bool> Read(u32 index) {
        DEBUG_ASSERT(index <= current_limit);
        const GPUVAddr gpu_addr = current_gpu_addr + index * sizeof(Descriptor);
        std::pair<Descriptor, bool> result;
        gpu_memory.ReadBlockUnsafe(gpu_addr, &result.first, sizeof(Descriptor));
        if (IsDescriptorRead(index)) {
            result.second = result.first != descriptors[index];
        } else {
            MarkDescriptorAsRead(index);
            result.second = true;
        }
        if (result.second) {
            descriptors[index] = result.first;
        }
        return result;
    }

    [[nodiscard]] u32 Limit() const noexcept {
        return current_limit;
    }

private:
    void Refresh(GPUVAddr gpu_addr, u32 limit) {
        current_gpu_addr = gpu_addr;
        current_limit = limit;

        const size_t num_descriptors = static_cast<size_t>(limit) + 1;
        read_descriptors.clear();
        read_descriptors.resize(Common::DivCeil(num_descriptors, 64U), 0);
        descriptors.resize(num_descriptors);
    }

    void MarkDescriptorAsRead(u32 index) noexcept {
        read_descriptors[index / 64] |= 1ULL << (index % 64);
    }

    [[nodiscard]] bool IsDescriptorRead(u32 index) const noexcept {
        return (read_descriptors[index / 64] & (1ULL << (index % 64))) != 0;
    }

    Tegra::MemoryManager& gpu_memory;
    GPUVAddr current_gpu_addr{};
    u32 current_limit{};
    std::vector<u64> read_descriptors;
    std::vector<Descriptor> descriptors;
};

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include "common/common_types.h"
#include "common/logging/log.h"
#include "video_core/texture_cache/format_lookup_table.h"

namespace VideoCommon {

using Tegra::Texture::ComponentType;
using Tegra::Texture::TextureFormat;
using VideoCore::Surface::PixelFormat;

namespace {

constexpr auto SNORM = ComponentType::SNORM;
constexpr auto UNORM = ComponentType::UNORM;
constexpr auto SINT = ComponentType::SINT;
constexpr auto UINT = ComponentType::UINT;
constexpr auto FLOAT = ComponentType::FLOAT;
constexpr bool LINEAR = false;
constexpr bool SRGB = true;

constexpr u32 Hash(TextureFormat format, ComponentType red_component, ComponentType green_component,
                   ComponentType blue_component, ComponentType alpha_component, bool is_srgb) {
    u32 hash = is_srgb ? 1 : 0;
    hash |= static_cast<u32>(red_component) << 1;
    hash |= static_cast<u32>(green_component) << 4;
    hash |= static_cast<u32>(blue_component) << 7;
    hash |= static_cast<u32>(alpha_component) << 10;
    hash |= static_cast<u32>(format) << 13;
    return hash;
}

constexpr u32 Hash(TextureFormat format, ComponentType component, bool is_srgb = LINEAR) {
    return Hash(format, component, component, component, component, is_srgb);
}

} // Anonymous namespace

PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, ComponentType green,
                                       ComponentType blue, ComponentType alpha,
                                       bool is_srgb) noexcept {
    switch (Hash(format, red, green, blue, alpha, is_srgb)) {
    case Hash(TextureFormat::A8B8G8R8, UNORM):
        return PixelFormat::A8B8G8R8_UNORM;
    case Hash(TextureFormat::A8B8G8R8, SNORM):
        return PixelFormat::A8B8G8R8_SNORM;
    case Hash(TextureFormat::A8B8G8R8, UINT):
        return PixelFormat::A8B8G8R8_UINT;
    case Hash(TextureFormat::A8B8G8R8, SINT):
        return PixelFormat::A8B8G8R8_SINT;
    case Hash(TextureFormat::A8B8G8R8, UNORM, SRGB):
        return PixelFormat::A8B8G8R8_SRGB;
    case Hash(TextureFormat::B5G6R5, UNORM):
        return PixelFormat::B5G6R5_UNORM;
    case Hash(TextureFormat::A2B10G10R10, UNORM):
        return PixelFormat::A2B10G10R10_UNORM;
    case Hash(TextureFormat::A2B10G10R10, UINT):
        return PixelFormat::A2B10G10R10_UINT;
    case Hash(TextureFormat::A1B5G5R5, UNORM):
        return PixelFormat::A1B5G5R5_UNORM;
    case Hash(TextureFormat::A4B4G4R4, UNORM):
        return PixelFormat::A4B4G4R4_UNORM;
    case Hash(TextureFormat::G4R4, UNORM):
        return PixelFormat::G4R4_UNORM;
    case Hash(TextureFormat::A5B5G5R1, UNORM):
        return PixelFormat::A5B5G5R1_UNORM;
    case Hash(TextureFormat::R8, UNORM):
        return PixelFormat::R8_UNORM;
    case Hash(TextureFormat::R8, SNORM):
        return PixelFormat::R8_SNORM;
    case Hash(TextureFormat::R8, UINT):
        return PixelFormat::R8_UINT;
    case Hash(TextureFormat::R8, SINT):
        return PixelFormat::R8_SINT;
    case Hash(TextureFormat::G8R8, UNORM):
        return PixelFormat::R8G8_UNORM;
    case Hash(TextureFormat::G8R8, SNORM):
        return PixelFormat::R8G8_SNORM;
    case Hash(TextureFormat::G8R8, UINT):
        return PixelFormat::R8G8_UINT;
    case Hash(TextureFormat::G8R8, SINT):
        return PixelFormat::R8G8_SINT;
    case Hash(TextureFormat::R16G16B16A16, FLOAT):
        return PixelFormat::R16G16B16A16_FLOAT;
    case Hash(TextureFormat::R16G16B16A16, UNORM):
        return PixelFormat::R16G16B16A16_UNORM;
    case Hash(TextureFormat::R16G16B16A16, SNORM):
        return PixelFormat::R16G16B16A16_SNORM;
    case Hash(TextureFormat::R16G16B16A16, UINT):
        return PixelFormat::R16G16B16A16_UINT;
    case Hash(TextureFormat::R16G16B16A16, SINT):
        return PixelFormat::R16G16B16A16_SINT;
    case Hash(TextureFormat::R16G16, FLOAT):
        return PixelFormat::R16G16_FLOAT;
    case Hash(TextureFormat::R16G16, UNORM):
        return PixelFormat::R16G16_UNORM;
    case Hash(TextureFormat::R16G16, SNORM):
        return PixelFormat::R16G16_SNORM;
    case Hash(TextureFormat::R16G16, UINT):
        return PixelFormat::R16G16_UINT;
    case Hash(TextureFormat::R16G16, SINT):
        return PixelFormat::R16G16_SINT;
    case Hash(TextureFormat::R16, FLOAT):
        return PixelFormat::R16_FLOAT;
    case Hash(TextureFormat::R16, UNORM):
        return PixelFormat::R16_UNORM;
    case Hash(TextureFormat::R16, SNORM):
        return PixelFormat::R16_SNORM;
    case Hash(TextureFormat::R16, UINT):
        return PixelFormat::R16_UINT;
    case Hash(TextureFormat::R16, SINT):
        return PixelFormat::R16_SINT;
    case Hash(TextureFormat::B10G11R11, FLOAT):
        return PixelFormat::B10G11R11_FLOAT;
    case Hash(TextureFormat::R32G32B32A32, FLOAT):
        return PixelFormat::R32G32B32A32_FLOAT;
    case Hash(TextureFormat::R32G32B32A32, UINT):
        return PixelFormat::R32G32B32A32_UINT;
    case Hash(TextureFormat::R32G32B32A32, SINT):
        return PixelFormat::R32G32B32A32_SINT;
    case Hash(TextureFormat::R32G32B32, FLOAT):
        return PixelFormat::R32G32B32_FLOAT;
    case Hash(TextureFormat::R32G32, FLOAT):
        return PixelFormat::R32G32_FLOAT;
    case Hash(TextureFormat::R32G32, UINT):
        return PixelFormat::R32G32_UINT;
    case Hash(TextureFormat::R32G32, SINT):
        return PixelFormat::R32G32_SINT;
    case Hash(TextureFormat::R32, FLOAT):
        return PixelFormat::R32_FLOAT;
    case Hash(TextureFormat::R32, UINT):
        return PixelFormat::R32_UINT;
    case Hash(TextureFormat::R32, SINT):
        return PixelFormat::R32_SINT;
    case Hash(TextureFormat::E5B9G9R9, FLOAT):
        return PixelFormat::E5B9G9R9_FLOAT;
    case Hash(TextureFormat::Z32, FLOAT):
        return PixelFormat::D32_FLOAT;
    case Hash(TextureFormat::Z32, FLOAT, UINT, UINT, UINT, LINEAR):
        return PixelFormat::D32_FLOAT;
    case Hash(TextureFormat::Z16, UNORM):
        return PixelFormat::D16_UNORM;
    case Hash(TextureFormat::Z16, UNORM, UINT, UINT, UINT, LINEAR):
        return PixelFormat::D16_UNORM;
    case Hash(TextureFormat::X8Z24, UNORM):
        return PixelFormat::X8_D24_UNORM;
    case Hash(TextureFormat::X8Z24, UNORM, UINT, UINT, UINT, LINEAR):
        return PixelFormat::X8_D24_UNORM;
    case Hash(TextureFormat::Z24S8, UINT, UNORM, UNORM, UNORM, LINEAR):
        return PixelFormat::S8_UINT_D24_UNORM;
    case Hash(TextureFormat::Z24S8, UINT, UNORM, UINT, UINT, LINEAR):
        return PixelFormat::S8_UINT_D24_UNORM;
    case Hash(TextureFormat::G24R8, UINT, UNORM, UNORM, UNORM, LINEAR):
        return PixelFormat::S8_UINT_D24_UNORM;
    case Hash(TextureFormat::S8Z24, UNORM, UINT, UINT, UINT, LINEAR):
        return PixelFormat::D24_UNORM_S8_UINT;
    case Hash(TextureFormat::Z32_X24S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
        return PixelFormat::D32_FLOAT_S8_UINT;
    case Hash(TextureFormat::R32B24G8, FLOAT, UINT, UNORM, UNORM, LINEAR):
        return PixelFormat::D32_FLOAT_S8_UINT;
    case Hash(TextureFormat::DXT1, UNORM, LINEAR):
        return PixelFormat::BC1_RGBA_UNORM;
    case Hash(TextureFormat::DXT1, UNORM, SRGB):
        return PixelFormat::BC1_RGBA_SRGB;
    case Hash(TextureFormat::DXT23, UNORM, LINEAR):
        return PixelFormat::BC2_UNORM;
    case Hash(TextureFormat::DXT23, UNORM, SRGB):
        return PixelFormat::BC2_SRGB;
    case Hash(TextureFormat::DXT45, UNORM, LINEAR):
        return PixelFormat::BC3_UNORM;
    case Hash(TextureFormat::DXT45, UNORM, SRGB):
        return PixelFormat::BC3_SRGB;
    case Hash(TextureFormat::DXN1, UNORM):
        return PixelFormat::BC4_UNORM;
    case Hash(TextureFormat::DXN1, SNORM):
        return PixelFormat::BC4_SNORM;
    case Hash(TextureFormat::DXN2, UNORM):
        return PixelFormat::BC5_UNORM;
    case Hash(TextureFormat::DXN2, SNORM):
        return PixelFormat::BC5_SNORM;
    case Hash(TextureFormat::BC7U, UNORM, LINEAR):
        return PixelFormat::BC7_UNORM;
    case Hash(TextureFormat::BC7U, UNORM, SRGB):
        return PixelFormat::BC7_SRGB;
    case Hash(TextureFormat::BC6H_S16, FLOAT):
        return PixelFormat::BC6H_SFLOAT;
    case Hash(TextureFormat::BC6H_U16, FLOAT):
        return PixelFormat::BC6H_UFLOAT;
    case Hash(TextureFormat::ASTC_2D_4X4, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_4X4_UNORM;
    case Hash(TextureFormat::ASTC_2D_4X4, UNORM, SRGB):
        return PixelFormat::ASTC_2D_4X4_SRGB;
    case Hash(TextureFormat::ASTC_2D_5X4, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_5X4_UNORM;
    case Hash(TextureFormat::ASTC_2D_5X4, UNORM, SRGB):
        return PixelFormat::ASTC_2D_5X4_SRGB;
    case Hash(TextureFormat::ASTC_2D_5X5, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_5X5_UNORM;
    case Hash(TextureFormat::ASTC_2D_5X5, UNORM, SRGB):
        return PixelFormat::ASTC_2D_5X5_SRGB;
    case Hash(TextureFormat::ASTC_2D_8X8, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_8X8_UNORM;
    case Hash(TextureFormat::ASTC_2D_8X8, UNORM, SRGB):
        return PixelFormat::ASTC_2D_8X8_SRGB;
    case Hash(TextureFormat::ASTC_2D_8X5, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_8X5_UNORM;
    case Hash(TextureFormat::ASTC_2D_8X5, UNORM, SRGB):
        return PixelFormat::ASTC_2D_8X5_SRGB;
    case Hash(TextureFormat::ASTC_2D_10X8, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_10X8_UNORM;
    case Hash(TextureFormat::ASTC_2D_10X8, UNORM, SRGB):
        return PixelFormat::ASTC_2D_10X8_SRGB;
    case Hash(TextureFormat::ASTC_2D_6X6, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_6X6_UNORM;
    case Hash(TextureFormat::ASTC_2D_6X6, UNORM, SRGB):
        return PixelFormat::ASTC_2D_6X6_SRGB;
    case Hash(TextureFormat::ASTC_2D_10X6, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_10X6_UNORM;
    case Hash(TextureFormat::ASTC_2D_10X6, UNORM, SRGB):
        return PixelFormat::ASTC_2D_10X6_SRGB;
    case Hash(TextureFormat::ASTC_2D_10X5, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_10X5_UNORM;
    case Hash(TextureFormat::ASTC_2D_10X5, UNORM, SRGB):
        return PixelFormat::ASTC_2D_10X5_SRGB;
    case Hash(TextureFormat::ASTC_2D_10X10, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_10X10_UNORM;
    case Hash(TextureFormat::ASTC_2D_10X10, UNORM, SRGB):
        return PixelFormat::ASTC_2D_10X10_SRGB;
    case Hash(TextureFormat::ASTC_2D_12X10, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_12X10_UNORM;
    case Hash(TextureFormat::ASTC_2D_12X10, UNORM, SRGB):
        return PixelFormat::ASTC_2D_12X10_SRGB;
    case Hash(TextureFormat::ASTC_2D_12X12, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_12X12_UNORM;
    case Hash(TextureFormat::ASTC_2D_12X12, UNORM, SRGB):
        return PixelFormat::ASTC_2D_12X12_SRGB;
    case Hash(TextureFormat::ASTC_2D_8X6, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_8X6_UNORM;
    case Hash(TextureFormat::ASTC_2D_8X6, UNORM, SRGB):
        return PixelFormat::ASTC_2D_8X6_SRGB;
    case Hash(TextureFormat::ASTC_2D_6X5, UNORM, LINEAR):
        return PixelFormat::ASTC_2D_6X5_UNORM;
    case Hash(TextureFormat::ASTC_2D_6X5, UNORM, SRGB):
        return PixelFormat::ASTC_2D_6X5_SRGB;
    }
    UNIMPLEMENTED_MSG("texture format={} srgb={} components={{{} {} {} {}}}",
                      static_cast<int>(format), is_srgb, static_cast<int>(red),
                      static_cast<int>(green), static_cast<int>(blue), static_cast<int>(alpha));
    return PixelFormat::A8B8G8R8_UNORM;
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "video_core/surface.h"
#include "video_core/textures/texture.h"

namespace VideoCommon {

VideoCore::Surface::PixelFormat PixelFormatFromTextureInfo(
    Tegra::Texture::TextureFormat format, Tegra::Texture::ComponentType red_component,
    Tegra::Texture::ComponentType green_component, Tegra::Texture::ComponentType blue_component,
    Tegra::Texture::ComponentType alpha_component, bool is_srgb) noexcept;

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <string>

#include "common/polyfill_ranges.h"
#include "video_core/texture_cache/formatter.h"
#include "video_core/texture_cache/image_base.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_base.h"
#include "video_core/texture_cache/render_targets.h"
#include "video_core/texture_cache/samples_helper.h"

namespace VideoCommon {

std::string Name(const ImageBase& image) {
    const GPUVAddr gpu_addr = image.gpu_addr;
    const ImageInfo& info = image.info;
    u32 width = info.size.width;
    u32 height = info.size.height;
    const u32 depth = info.size.depth;
    const u32 num_layers = image.info.resources.layers;
    const u32 num_levels = image.info.resources.levels;
    std::string resource;
    if (image.info.num_samples > 1) {
        const auto [samples_x, samples_y] = VideoCommon::SamplesLog2(image.info.num_samples);
        width >>= samples_x;
        height >>= samples_y;
        resource += fmt::format(":{}xMSAA", image.info.num_samples);
    }
    if (num_layers > 1) {
        resource += fmt::format(":L{}", num_layers);
    }
    if (num_levels > 1) {
        resource += fmt::format(":M{}", num_levels);
    }
    switch (image.info.type) {
    case ImageType::e1D:
        return fmt::format("Image 1D 0x{:x} {}{}", gpu_addr, width, resource);
    case ImageType::e2D:
        return fmt::format("Image 2D 0x{:x} {}x{}{}", gpu_addr, width, height, resource);
    case ImageType::e3D:
        return fmt::format("Image 2D 0x{:x} {}x{}x{}{}", gpu_addr, width, height, depth, resource);
    case ImageType::Linear:
        return fmt::format("Image Linear 0x{:x} {}x{}", gpu_addr, width, height);
    case ImageType::Buffer:
        return fmt::format("Buffer 0x{:x} {}", image.gpu_addr, image.info.size.width);
    }
    return "Invalid";
}

std::string Name(const ImageViewBase& image_view, GPUVAddr addr) {
    const u32 width = image_view.size.width;
    const u32 height = image_view.size.height;
    const u32 depth = image_view.size.depth;
    const u32 num_levels = image_view.range.extent.levels;
    const u32 num_layers = image_view.range.extent.layers;

    const std::string level = num_levels > 1 ? fmt::format(":{}", num_levels) : "";
    switch (image_view.type) {
    case ImageViewType::e1D:
        return fmt::format("ImageView 1D 0x{:X} {}{}", addr, width, level);
    case ImageViewType::e2D:
        return fmt::format("ImageView 2D 0x{:X} {}x{}{}", addr, width, height, level);
    case ImageViewType::Cube:
        return fmt::format("ImageView Cube 0x{:X} {}x{}{}", addr, width, height, level);
    case ImageViewType::e3D:
        return fmt::format("ImageView 3D 0x{:X} {}x{}x{}{}", addr, width, height, depth, level);
    case ImageViewType::e1DArray:
        return fmt::format("ImageView 1DArray 0x{:X} {}{}|{}", addr, width, level, num_layers);
    case ImageViewType::e2DArray:
        return fmt::format("ImageView 2DArray 0x{:X} {}x{}{}|{}", addr, width, height, level,
                           num_layers);
    case ImageViewType::CubeArray:
        return fmt::format("ImageView CubeArray 0x{:X} {}x{}{}|{}", addr, width, height, level,
                           num_layers);
    case ImageViewType::Rect:
        return fmt::format("ImageView Rect 0x{:X} {}x{}{}", addr, width, height, level);
    case ImageViewType::Buffer:
        return fmt::format("BufferView 0x{:X} {}", addr, width);
    }
    return "Invalid";
}

std::string Name(const RenderTargets& render_targets) {
    std::string_view debug_prefix;
    const auto num_color = std::ranges::count_if(
        render_targets.color_buffer_ids, [](ImageViewId id) { return static_cast<bool>(id); });
    if (render_targets.depth_buffer_id) {
        debug_prefix = num_color > 0 ? "R" : "Z";
    } else {
        debug_prefix = num_color > 0 ? "C" : "X";
    }
    const Extent2D size = render_targets.size;
    if (num_color > 0) {
        return fmt::format("Framebuffer {}{} {}x{}", debug_prefix, num_color, size.width,
                           size.height);
    } else {
        return fmt::format("Framebuffer {} {}x{}", debug_prefix, size.width, size.height);
    }
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <string>

#include <fmt/format.h>

#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"

template <>
struct fmt::formatter<VideoCore::Surface::PixelFormat> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(VideoCore::Surface::PixelFormat format, FormatContext& ctx) {
        using VideoCore::Surface::PixelFormat;
        const string_view name = [format] {
            switch (format) {
            case PixelFormat::A8B8G8R8_UNORM:
                return "A8B8G8R8_UNORM";
            case PixelFormat::A8B8G8R8_SNORM:
                return "A8B8G8R8_SNORM";
            case PixelFormat::A8B8G8R8_SINT:
                return "A8B8G8R8_SINT";
            case PixelFormat::A8B8G8R8_UINT:
                return "A8B8G8R8_UINT";
            case PixelFormat::R5G6B5_UNORM:
                return "R5G6B5_UNORM";
            case PixelFormat::B5G6R5_UNORM:
                return "B5G6R5_UNORM";
            case PixelFormat::A1R5G5B5_UNORM:
                return "A1R5G5B5_UNORM";
            case PixelFormat::A2B10G10R10_UNORM:
                return "A2B10G10R10_UNORM";
            case PixelFormat::A2B10G10R10_UINT:
                return "A2B10G10R10_UINT";
            case PixelFormat::A2R10G10B10_UNORM:
                return "A2R10G10B10_UNORM";
            case PixelFormat::A1B5G5R5_UNORM:
                return "A1B5G5R5_UNORM";
            case PixelFormat::A5B5G5R1_UNORM:
                return "A5B5G5R1_UNORM";
            case PixelFormat::R8_UNORM:
                return "R8_UNORM";
            case PixelFormat::R8_SNORM:
                return "R8_SNORM";
            case PixelFormat::R8_SINT:
                return "R8_SINT";
            case PixelFormat::R8_UINT:
                return "R8_UINT";
            case PixelFormat::R16G16B16A16_FLOAT:
                return "R16G16B16A16_FLOAT";
            case PixelFormat::R16G16B16A16_UNORM:
                return "R16G16B16A16_UNORM";
            case PixelFormat::R16G16B16A16_SNORM:
                return "R16G16B16A16_SNORM";
            case PixelFormat::R16G16B16A16_SINT:
                return "R16G16B16A16_SINT";
            case PixelFormat::R16G16B16A16_UINT:
                return "R16G16B16A16_UINT";
            case PixelFormat::B10G11R11_FLOAT:
                return "B10G11R11_FLOAT";
            case PixelFormat::R32G32B32A32_UINT:
                return "R32G32B32A32_UINT";
            case PixelFormat::BC1_RGBA_UNORM:
                return "BC1_RGBA_UNORM";
            case PixelFormat::BC2_UNORM:
                return "BC2_UNORM";
            case PixelFormat::BC3_UNORM:
                return "BC3_UNORM";
            case PixelFormat::BC4_UNORM:
                return "BC4_UNORM";
            case PixelFormat::BC4_SNORM:
                return "BC4_SNORM";
            case PixelFormat::BC5_UNORM:
                return "BC5_UNORM";
            case PixelFormat::BC5_SNORM:
                return "BC5_SNORM";
            case PixelFormat::BC7_UNORM:
                return "BC7_UNORM";
            case PixelFormat::BC6H_UFLOAT:
                return "BC6H_UFLOAT";
            case PixelFormat::BC6H_SFLOAT:
                return "BC6H_SFLOAT";
            case PixelFormat::ASTC_2D_4X4_UNORM:
                return "ASTC_2D_4X4_UNORM";
            case PixelFormat::B8G8R8A8_UNORM:
                return "B8G8R8A8_UNORM";
            case PixelFormat::R32G32B32A32_FLOAT:
                return "R32G32B32A32_FLOAT";
            case PixelFormat::R32G32B32A32_SINT:
                return "R32G32B32A32_SINT";
            case PixelFormat::R32G32_FLOAT:
                return "R32G32_FLOAT";
            case PixelFormat::R32G32_SINT:
                return "R32G32_SINT";
            case PixelFormat::R32_FLOAT:
                return "R32_FLOAT";
            case PixelFormat::R16_FLOAT:
                return "R16_FLOAT";
            case PixelFormat::R16_UNORM:
                return "R16_UNORM";
            case PixelFormat::R16_SNORM:
                return "R16_SNORM";
            case PixelFormat::R16_UINT:
                return "R16_UINT";
            case PixelFormat::R16_SINT:
                return "R16_SINT";
            case PixelFormat::R16G16_UNORM:
                return "R16G16_UNORM";
            case PixelFormat::R16G16_FLOAT:
                return "R16G16_FLOAT";
            case PixelFormat::R16G16_UINT:
                return "R16G16_UINT";
            case PixelFormat::R16G16_SINT:
                return "R16G16_SINT";
            case PixelFormat::R16G16_SNORM:
                return "R16G16_SNORM";
            case PixelFormat::R32G32B32_FLOAT:
                return "R32G32B32_FLOAT";
            case PixelFormat::A8B8G8R8_SRGB:
                return "A8B8G8R8_SRGB";
            case PixelFormat::R8G8_UNORM:
                return "R8G8_UNORM";
            case PixelFormat::R8G8_SNORM:
                return "R8G8_SNORM";
            case PixelFormat::R8G8_SINT:
                return "R8G8_SINT";
            case PixelFormat::R8G8_UINT:
                return "R8G8_UINT";
            case PixelFormat::R32G32_UINT:
                return "R32G32_UINT";
            case PixelFormat::R16G16B16X16_FLOAT:
                return "R16G16B16X16_FLOAT";
            case PixelFormat::R32_UINT:
                return "R32_UINT";
            case PixelFormat::R32_SINT:
                return "R32_SINT";
            case PixelFormat::ASTC_2D_8X8_UNORM:
                return "ASTC_2D_8X8_UNORM";
            case PixelFormat::ASTC_2D_8X5_UNORM:
                return "ASTC_2D_8X5_UNORM";
            case PixelFormat::ASTC_2D_5X4_UNORM:
                return "ASTC_2D_5X4_UNORM";
            case PixelFormat::B8G8R8A8_SRGB:
                return "B8G8R8A8_SRGB";
            case PixelFormat::BC1_RGBA_SRGB:
                return "BC1_RGBA_SRGB";
            case PixelFormat::BC2_SRGB:
                return "BC2_SRGB";
            case PixelFormat::BC3_SRGB:
                return "BC3_SRGB";
            case PixelFormat::BC7_SRGB:
                return "BC7_SRGB";
            case PixelFormat::A4B4G4R4_UNORM:
                return "A4B4G4R4_UNORM";
            case PixelFormat::G4R4_UNORM:
                return "G4R4_UNORM";
            case PixelFormat::ASTC_2D_4X4_SRGB:
                return "ASTC_2D_4X4_SRGB";
            case PixelFormat::ASTC_2D_8X8_SRGB:
                return "ASTC_2D_8X8_SRGB";
            case PixelFormat::ASTC_2D_8X5_SRGB:
                return "ASTC_2D_8X5_SRGB";
            case PixelFormat::ASTC_2D_5X4_SRGB:
                return "ASTC_2D_5X4_SRGB";
            case PixelFormat::ASTC_2D_5X5_UNORM:
                return "ASTC_2D_5X5_UNORM";
            case PixelFormat::ASTC_2D_5X5_SRGB:
                return "ASTC_2D_5X5_SRGB";
            case PixelFormat::ASTC_2D_10X8_UNORM:
                return "ASTC_2D_10X8_UNORM";
            case PixelFormat::ASTC_2D_10X8_SRGB:
                return "ASTC_2D_10X8_SRGB";
            case PixelFormat::ASTC_2D_6X6_UNORM:
                return "ASTC_2D_6X6_UNORM";
            case PixelFormat::ASTC_2D_6X6_SRGB:
                return "ASTC_2D_6X6_SRGB";
            case PixelFormat::ASTC_2D_10X6_UNORM:
                return "ASTC_2D_10X6_UNORM";
            case PixelFormat::ASTC_2D_10X6_SRGB:
                return "ASTC_2D_10X6_SRGB";
            case PixelFormat::ASTC_2D_10X5_UNORM:
                return "ASTC_2D_10X5_UNORM";
            case PixelFormat::ASTC_2D_10X5_SRGB:
                return "ASTC_2D_10X5_SRGB";
            case PixelFormat::ASTC_2D_10X10_UNORM:
                return "ASTC_2D_10X10_UNORM";
            case PixelFormat::ASTC_2D_10X10_SRGB:
                return "ASTC_2D_10X10_SRGB";
            case PixelFormat::ASTC_2D_12X10_UNORM:
                return "ASTC_2D_12X10_UNORM";
            case PixelFormat::ASTC_2D_12X10_SRGB:
                return "ASTC_2D_12X10_SRGB";
            case PixelFormat::ASTC_2D_12X12_UNORM:
                return "ASTC_2D_12X12_UNORM";
            case PixelFormat::ASTC_2D_12X12_SRGB:
                return "ASTC_2D_12X12_SRGB";
            case PixelFormat::ASTC_2D_8X6_UNORM:
                return "ASTC_2D_8X6_UNORM";
            case PixelFormat::ASTC_2D_8X6_SRGB:
                return "ASTC_2D_8X6_SRGB";
            case PixelFormat::ASTC_2D_6X5_UNORM:
                return "ASTC_2D_6X5_UNORM";
            case PixelFormat::ASTC_2D_6X5_SRGB:
                return "ASTC_2D_6X5_SRGB";
            case PixelFormat::E5B9G9R9_FLOAT:
                return "E5B9G9R9_FLOAT";
            case PixelFormat::D32_FLOAT:
                return "D32_FLOAT";
            case PixelFormat::D16_UNORM:
                return "D16_UNORM";
            case PixelFormat::X8_D24_UNORM:
                return "X8_D24_UNORM";
            case PixelFormat::S8_UINT:
                return "S8_UINT";
            case PixelFormat::D24_UNORM_S8_UINT:
                return "D24_UNORM_S8_UINT";
            case PixelFormat::S8_UINT_D24_UNORM:
                return "S8_UINT_D24_UNORM";
            case PixelFormat::D32_FLOAT_S8_UINT:
                return "D32_FLOAT_S8_UINT";
            case PixelFormat::MaxDepthStencilFormat:
            case PixelFormat::Invalid:
                return "Invalid";
            }
            return "Invalid";
        }();
        return formatter<string_view>::format(name, ctx);
    }
};

template <>
struct fmt::formatter<VideoCommon::ImageType> : fmt::formatter<fmt::string_view> {
    template <typename FormatContext>
    auto format(VideoCommon::ImageType type, FormatContext& ctx) {
        const string_view name = [type] {
            using VideoCommon::ImageType;
            switch (type) {
            case ImageType::e1D:
                return "1D";
            case ImageType::e2D:
                return "2D";
            case ImageType::e3D:
                return "3D";
            case ImageType::Linear:
                return "Linear";
            case ImageType::Buffer:
                return "Buffer";
            }
            return "Invalid";
        }();
        return formatter<string_view>::format(name, ctx);
    }
};

template <>
struct fmt::formatter<VideoCommon::Extent3D> {
    constexpr auto parse(fmt::format_parse_context& ctx) {
        return ctx.begin();
    }

    template <typename FormatContext>
    auto format(const VideoCommon::Extent3D& extent, FormatContext& ctx) {
        return fmt::format_to(ctx.out(), "{{{}, {}, {}}}", extent.width, extent.height,
                              extent.depth);
    }
};

namespace VideoCommon {

struct ImageBase;
struct ImageViewBase;
struct RenderTargets;

[[nodiscard]] std::string Name(const ImageBase& image);

[[nodiscard]] std::string Name(const ImageViewBase& image_view, GPUVAddr addr);

[[nodiscard]] std::string Name(const RenderTargets& render_targets);

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>
#include <optional>
#include <utility>
#include <vector>

#include "common/common_types.h"
#include "common/div_ceil.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/formatter.h"
#include "video_core/texture_cache/image_base.h"
#include "video_core/texture_cache/image_view_info.h"
#include "video_core/texture_cache/util.h"

namespace VideoCommon {

using VideoCore::Surface::DefaultBlockHeight;
using VideoCore::Surface::DefaultBlockWidth;

namespace {
/// Returns the base layer and mip level offset
[[nodiscard]] std::pair<s32, s32> LayerMipOffset(s32 diff, u32 layer_stride) {
    if (layer_stride == 0) {
        return {0, diff};
    } else {
        return {diff / layer_stride, diff % layer_stride};
    }
}

[[nodiscard]] bool ValidateLayers(const SubresourceLayers& layers, const ImageInfo& info) {
    return layers.base_level < info.resources.levels &&
           layers.base_layer + layers.num_layers <= info.resources.layers;
}

[[nodiscard]] bool ValidateCopy(const ImageCopy& copy, const ImageInfo& dst, const ImageInfo& src) {
    const Extent3D src_size = MipSize(src.size, copy.src_subresource.base_level);
    const Extent3D dst_size = MipSize(dst.size, copy.dst_subresource.base_level);
    if (!ValidateLayers(copy.src_subresource, src)) {
        return false;
    }
    if (!ValidateLayers(copy.dst_subresource, dst)) {
        return false;
    }
    if (copy.src_offset.x + copy.extent.width > src_size.width ||
        copy.src_offset.y + copy.extent.height > src_size.height ||
        copy.src_offset.z + copy.extent.depth > src_size.depth) {
        return false;
    }
    if (copy.dst_offset.x + copy.extent.width > dst_size.width ||
        copy.dst_offset.y + copy.extent.height > dst_size.height ||
        copy.dst_offset.z + copy.extent.depth > dst_size.depth) {
        return false;
    }
    return true;
}
} // Anonymous namespace

ImageBase::ImageBase(const ImageInfo& info_, GPUVAddr gpu_addr_, VAddr cpu_addr_)
    : info{info_}, guest_size_bytes{CalculateGuestSizeInBytes(info)},
      unswizzled_size_bytes{CalculateUnswizzledSizeBytes(info)},
      converted_size_bytes{CalculateConvertedSizeBytes(info)}, scale_rating{}, scale_tick{},
      has_scaled{}, gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_},
      cpu_addr_end{cpu_addr + guest_size_bytes}, mip_level_offsets{CalculateMipLevelOffsets(info)} {
    if (info.type == ImageType::e3D) {
        slice_offsets = CalculateSliceOffsets(info);
        slice_subresources = CalculateSliceSubresources(info);
    }
}

ImageBase::ImageBase(const NullImageParams&) {}

ImageMapView::ImageMapView(GPUVAddr gpu_addr_, VAddr cpu_addr_, size_t size_, ImageId image_id_)
    : gpu_addr{gpu_addr_}, cpu_addr{cpu_addr_}, size{size_}, image_id{image_id_} {}

std::optional<SubresourceBase> ImageBase::TryFindBase(GPUVAddr other_addr) const noexcept {
    if (other_addr < gpu_addr) {
        // Subresource address can't be lower than the base
        return std::nullopt;
    }
    const u32 diff = static_cast<u32>(other_addr - gpu_addr);
    if (diff > guest_size_bytes) {
        // This can happen when two CPU addresses are used for different GPU addresses
        return std::nullopt;
    }
    if (info.type != ImageType::e3D) {
        const auto [layer, mip_offset] = LayerMipOffset(diff, info.layer_stride);
        const auto end = mip_level_offsets.begin() + info.resources.levels;
        const auto it = std::find(mip_level_offsets.begin(), end, static_cast<u32>(mip_offset));
        if (layer > info.resources.layers || it == end) {
            return std::nullopt;
        }
        return SubresourceBase{
            .level = static_cast<s32>(std::distance(mip_level_offsets.begin(), it)),
            .layer = layer,
        };
    } else {
        // TODO: Consider using binary_search after a threshold
        const auto it = std::ranges::find(slice_offsets, diff);
        if (it == slice_offsets.cend()) {
            return std::nullopt;
        }
        return slice_subresources[std::distance(slice_offsets.begin(), it)];
    }
}

ImageViewId ImageBase::FindView(const ImageViewInfo& view_info) const noexcept {
    const auto it = std::ranges::find(image_view_infos, view_info);
    if (it == image_view_infos.end()) {
        return ImageViewId{};
    }
    return image_view_ids[std::distance(image_view_infos.begin(), it)];
}

void ImageBase::InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id) {
    image_view_infos.push_back(view_info);
    image_view_ids.push_back(image_view_id);
}

bool ImageBase::IsSafeDownload() const noexcept {
    // Skip images that were not modified from the GPU
    if (False(flags & ImageFlagBits::GpuModified)) {
        return false;
    }
    // Skip images that .are. modified from the CPU
    // We don't want to write sensitive data from the guest
    if (True(flags & ImageFlagBits::CpuModified)) {
        return false;
    }
    if (info.num_samples > 1) {
        LOG_WARNING(HW_GPU, "MSAA image downloads are not implemented");
        return false;
    }
    return true;
}

void ImageBase::CheckBadOverlapState() {
    if (False(flags & ImageFlagBits::BadOverlap)) {
        return;
    }
    if (!overlapping_images.empty()) {
        return;
    }
    flags &= ~ImageFlagBits::BadOverlap;
}

void ImageBase::CheckAliasState() {
    if (False(flags & ImageFlagBits::Alias)) {
        return;
    }
    if (!aliased_images.empty()) {
        return;
    }
    flags &= ~ImageFlagBits::Alias;
}

bool AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_id) {
    static constexpr auto OPTIONS = RelaxedOptions::Size | RelaxedOptions::Format;
    ASSERT(lhs.info.type == rhs.info.type);
    std::optional<SubresourceBase> base;
    if (lhs.info.type == ImageType::Linear) {
        base = SubresourceBase{.level = 0, .layer = 0};
    } else {
        // We are passing relaxed formats as an option, having broken views/bgr or not won't matter
        static constexpr bool broken_views = false;
        static constexpr bool native_bgr = true;
        base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS, broken_views, native_bgr);
    }
    if (!base) {
        LOG_ERROR(HW_GPU, "Image alias should have been flipped");
        return false;
    }
    const PixelFormat lhs_format = lhs.info.format;
    const PixelFormat rhs_format = rhs.info.format;
    const Extent2D lhs_block{
        .width = DefaultBlockWidth(lhs_format),
        .height = DefaultBlockHeight(lhs_format),
    };
    const Extent2D rhs_block{
        .width = DefaultBlockWidth(rhs_format),
        .height = DefaultBlockHeight(rhs_format),
    };
    const bool is_lhs_compressed = lhs_block.width > 1 || lhs_block.height > 1;
    const bool is_rhs_compressed = rhs_block.width > 1 || rhs_block.height > 1;
    const s32 lhs_mips = lhs.info.resources.levels;
    const s32 rhs_mips = rhs.info.resources.levels;
    const s32 num_mips = std::min(lhs_mips - base->level, rhs_mips);
    AliasedImage lhs_alias;
    AliasedImage rhs_alias;
    lhs_alias.id = rhs_id;
    rhs_alias.id = lhs_id;
    lhs_alias.copies.reserve(num_mips);
    rhs_alias.copies.reserve(num_mips);
    for (s32 mip_level = 0; mip_level < num_mips; ++mip_level) {
        Extent3D lhs_size = MipSize(lhs.info.size, base->level + mip_level);
        Extent3D rhs_size = MipSize(rhs.info.size, mip_level);
        if (is_lhs_compressed) {
            lhs_size.width = Common::DivCeil(lhs_size.width, lhs_block.width);
            lhs_size.height = Common::DivCeil(lhs_size.height, lhs_block.height);
        }
        if (is_rhs_compressed) {
            rhs_size.width = Common::DivCeil(rhs_size.width, rhs_block.width);
            rhs_size.height = Common::DivCeil(rhs_size.height, rhs_block.height);
        }
        const Extent3D copy_size{
            .width = std::min(lhs_size.width, rhs_size.width),
            .height = std::min(lhs_size.height, rhs_size.height),
            .depth = std::min(lhs_size.depth, rhs_size.depth),
        };
        if (copy_size.width == 0 || copy_size.height == 0) {
            LOG_WARNING(HW_GPU, "Copy size is smaller than block size. Mip cannot be aliased.");
            continue;
        }
        const bool is_lhs_3d = lhs.info.type == ImageType::e3D;
        const bool is_rhs_3d = rhs.info.type == ImageType::e3D;
        const Offset3D lhs_offset{0, 0, 0};
        const Offset3D rhs_offset{0, 0, is_rhs_3d ? base->layer : 0};
        const s32 lhs_layers = is_lhs_3d ? 1 : lhs.info.resources.layers - base->layer;
        const s32 rhs_layers = is_rhs_3d ? 1 : rhs.info.resources.layers;
        const s32 num_layers = std::min(lhs_layers, rhs_layers);
        const SubresourceLayers lhs_subresource{
            .base_level = mip_level,
            .base_layer = 0,
            .num_layers = num_layers,
        };
        const SubresourceLayers rhs_subresource{
            .base_level = base->level + mip_level,
            .base_layer = is_rhs_3d ? 0 : base->layer,
            .num_layers = num_layers,
        };
        [[maybe_unused]] const ImageCopy& to_lhs_copy = lhs_alias.copies.emplace_back(ImageCopy{
            .src_subresource = lhs_subresource,
            .dst_subresource = rhs_subresource,
            .src_offset = lhs_offset,
            .dst_offset = rhs_offset,
            .extent = copy_size,
        });
        [[maybe_unused]] const ImageCopy& to_rhs_copy = rhs_alias.copies.emplace_back(ImageCopy{
            .src_subresource = rhs_subresource,
            .dst_subresource = lhs_subresource,
            .src_offset = rhs_offset,
            .dst_offset = lhs_offset,
            .extent = copy_size,
        });
        ASSERT_MSG(ValidateCopy(to_lhs_copy, lhs.info, rhs.info), "Invalid RHS to LHS copy");
        ASSERT_MSG(ValidateCopy(to_rhs_copy, rhs.info, lhs.info), "Invalid LHS to RHS copy");
    }
    ASSERT(lhs_alias.copies.empty() == rhs_alias.copies.empty());
    if (lhs_alias.copies.empty()) {
        return false;
    }
    lhs.aliased_images.push_back(std::move(lhs_alias));
    rhs.aliased_images.push_back(std::move(rhs_alias));
    lhs.flags &= ~ImageFlagBits::IsRescalable;
    rhs.flags &= ~ImageFlagBits::IsRescalable;
    return true;
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <optional>
#include <vector>
#include <boost/container/small_vector.hpp>

#include "common/common_funcs.h"
#include "common/common_types.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_info.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon {

enum class ImageFlagBits : u32 {
    AcceleratedUpload = 1 << 0, ///< Upload can be accelerated in the GPU
    Converted = 1 << 1,   ///< Guest format is not supported natively and it has to be converted
    CpuModified = 1 << 2, ///< Contents have been modified from the CPU
    GpuModified = 1 << 3, ///< Contents have been modified from the GPU
    Tracked = 1 << 4,     ///< Writes and reads are being hooked from the CPU JIT
    Strong = 1 << 5,      ///< Exists in the image table, the dimensions are can be trusted
    Registered = 1 << 6,  ///< True when the image is registered
    Picked = 1 << 7,      ///< Temporary flag to mark the image as picked
    Remapped = 1 << 8,    ///< Image has been remapped.
    Sparse = 1 << 9,      ///< Image has non continuous submemory.

    // Garbage Collection Flags
    BadOverlap = 1 << 10, ///< This image overlaps other but doesn't fit, has higher
                          ///< garbage collection priority
    Alias = 1 << 11,      ///< This image has aliases and has priority on garbage
                          ///< collection
    CostlyLoad = 1 << 12, ///< Protected from low-tier GC as it is costly to load back.

    // Rescaler
    Rescaled = 1 << 13,
    CheckingRescalable = 1 << 14,
    IsRescalable = 1 << 15,

    AsynchronousDecode = 1 << 16,
    IsDecoding = 1 << 17, ///< Is currently being decoded asynchronously.
};
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)

struct ImageViewInfo;

struct AliasedImage {
    std::vector<ImageCopy> copies;
    ImageId id;
};

struct NullImageParams {};

struct ImageBase {
    explicit ImageBase(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
    explicit ImageBase(const NullImageParams&);

    [[nodiscard]] std::optional<SubresourceBase> TryFindBase(GPUVAddr other_addr) const noexcept;

    [[nodiscard]] ImageViewId FindView(const ImageViewInfo& view_info) const noexcept;

    void InsertView(const ImageViewInfo& view_info, ImageViewId image_view_id);

    [[nodiscard]] bool IsSafeDownload() const noexcept;

    [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept {
        const VAddr overlap_end = overlap_cpu_addr + overlap_size;
        return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end;
    }

    [[nodiscard]] bool OverlapsGPU(GPUVAddr overlap_gpu_addr, size_t overlap_size) const noexcept {
        const VAddr overlap_end = overlap_gpu_addr + overlap_size;
        const GPUVAddr gpu_addr_end = gpu_addr + guest_size_bytes;
        return gpu_addr < overlap_end && overlap_gpu_addr < gpu_addr_end;
    }

    void CheckBadOverlapState();
    void CheckAliasState();

    bool HasScaled() const {
        return has_scaled;
    }

    ImageInfo info;

    u32 guest_size_bytes = 0;
    u32 unswizzled_size_bytes = 0;
    u32 converted_size_bytes = 0;
    u32 scale_rating = 0;
    u64 scale_tick = 0;
    bool has_scaled = false;

    size_t channel = 0;

    ImageFlagBits flags = ImageFlagBits::CpuModified;

    GPUVAddr gpu_addr = 0;
    VAddr cpu_addr = 0;
    VAddr cpu_addr_end = 0;

    u64 modification_tick = 0;
    size_t lru_index = SIZE_MAX;

    std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{};

    std::vector<ImageViewInfo> image_view_infos;
    std::vector<ImageViewId> image_view_ids;

    boost::container::small_vector<u32, 16> slice_offsets;
    boost::container::small_vector<SubresourceBase, 16> slice_subresources;

    std::vector<AliasedImage> aliased_images;
    std::vector<ImageId> overlapping_images;
    ImageMapId map_view_id{};
};

struct ImageMapView {
    explicit ImageMapView(GPUVAddr gpu_addr, VAddr cpu_addr, size_t size, ImageId image_id);

    [[nodiscard]] bool Overlaps(VAddr overlap_cpu_addr, size_t overlap_size) const noexcept {
        const VAddr overlap_end = overlap_cpu_addr + overlap_size;
        const VAddr cpu_addr_end = cpu_addr + size;
        return cpu_addr < overlap_end && overlap_cpu_addr < cpu_addr_end;
    }

    [[nodiscard]] bool OverlapsGPU(GPUVAddr overlap_gpu_addr, size_t overlap_size) const noexcept {
        const GPUVAddr overlap_end = overlap_gpu_addr + overlap_size;
        const GPUVAddr gpu_addr_end = gpu_addr + size;
        return gpu_addr < overlap_end && overlap_gpu_addr < gpu_addr_end;
    }

    GPUVAddr gpu_addr;
    VAddr cpu_addr;
    size_t size;
    ImageId image_id;
    bool picked{};
};

struct ImageAllocBase {
    std::vector<ImageId> images;
};

bool AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_id);

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <fmt/format.h>

#include "common/assert.h"
#include "common/settings.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/format_lookup_table.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/samples_helper.h"
#include "video_core/texture_cache/types.h"
#include "video_core/texture_cache/util.h"
#include "video_core/textures/texture.h"

namespace VideoCommon {

using Tegra::Engines::Fermi2D;
using Tegra::Engines::Maxwell3D;
using Tegra::Texture::TextureType;
using Tegra::Texture::TICEntry;
using VideoCore::Surface::PixelFormat;
using VideoCore::Surface::SurfaceType;

constexpr u32 RescaleHeightThreshold = 288;
constexpr u32 DownscaleHeightThreshold = 512;

ImageInfo::ImageInfo(const TICEntry& config) noexcept {
    forced_flushed = config.IsPitchLinear() && !Settings::values.use_reactive_flushing.GetValue();
    dma_downloaded = forced_flushed;
    format = PixelFormatFromTextureInfo(config.format, config.r_type, config.g_type, config.b_type,
                                        config.a_type, config.srgb_conversion);
    num_samples = NumSamples(config.msaa_mode);
    resources.levels = config.max_mip_level + 1;
    if (config.IsPitchLinear()) {
        pitch = config.Pitch();
    } else if (config.IsBlockLinear()) {
        block = Extent3D{
            .width = config.block_width,
            .height = config.block_height,
            .depth = config.block_depth,
        };
    }
    rescaleable = false;
    is_sparse = config.is_sparse != 0;
    tile_width_spacing = config.tile_width_spacing;
    if (config.texture_type != TextureType::Texture2D &&
        config.texture_type != TextureType::Texture2DNoMipmap) {
        ASSERT(!config.IsPitchLinear());
    }
    switch (config.texture_type) {
    case TextureType::Texture1D:
        ASSERT(config.BaseLayer() == 0);
        type = ImageType::e1D;
        size.width = config.Width();
        resources.layers = 1;
        break;
    case TextureType::Texture1DArray:
        UNIMPLEMENTED_IF(config.BaseLayer() != 0);
        type = ImageType::e1D;
        size.width = config.Width();
        resources.layers = config.Depth();
        break;
    case TextureType::Texture2D:
    case TextureType::Texture2DNoMipmap:
        ASSERT(config.Depth() == 1);
        type = config.IsPitchLinear() ? ImageType::Linear : ImageType::e2D;
        rescaleable = !config.IsPitchLinear();
        size.width = config.Width();
        size.height = config.Height();
        resources.layers = config.BaseLayer() + 1;
        break;
    case TextureType::Texture2DArray:
        type = ImageType::e2D;
        rescaleable = true;
        size.width = config.Width();
        size.height = config.Height();
        resources.layers = config.BaseLayer() + config.Depth();
        break;
    case TextureType::TextureCubemap:
        ASSERT(config.Depth() == 1);
        type = ImageType::e2D;
        size.width = config.Width();
        size.height = config.Height();
        resources.layers = config.BaseLayer() + 6;
        break;
    case TextureType::TextureCubeArray:
        UNIMPLEMENTED_IF(config.load_store_hint != 0);
        type = ImageType::e2D;
        size.width = config.Width();
        size.height = config.Height();
        resources.layers = config.BaseLayer() + config.Depth() * 6;
        break;
    case TextureType::Texture3D:
        ASSERT(config.BaseLayer() == 0);
        type = ImageType::e3D;
        size.width = config.Width();
        size.height = config.Height();
        size.depth = config.Depth();
        resources.layers = 1;
        break;
    case TextureType::Texture1DBuffer:
        type = ImageType::Buffer;
        size.width = config.Width();
        resources.layers = 1;
        break;
    default:
        ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
        break;
    }
    if (num_samples > 1) {
        size.width *= NumSamplesX(config.msaa_mode);
        size.height *= NumSamplesY(config.msaa_mode);
    }
    if (type != ImageType::Linear) {
        // FIXME: Call this without passing *this
        layer_stride = CalculateLayerStride(*this);
        maybe_unaligned_layer_stride = CalculateLayerSize(*this);
        rescaleable &= (block.depth == 0) && resources.levels == 1;
        rescaleable &= size.height > RescaleHeightThreshold ||
                       GetFormatType(format) != SurfaceType::ColorTexture;
        downscaleable = size.height > DownscaleHeightThreshold;
    }
}

ImageInfo::ImageInfo(const Maxwell3D::Regs::RenderTargetConfig& ct,
                     Tegra::Texture::MsaaMode msaa_mode) noexcept {
    forced_flushed =
        ct.tile_mode.is_pitch_linear && !Settings::values.use_reactive_flushing.GetValue();
    dma_downloaded = forced_flushed;
    format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(ct.format);
    rescaleable = false;
    if (ct.tile_mode.is_pitch_linear) {
        ASSERT(ct.tile_mode.dim_control ==
               Maxwell3D::Regs::TileMode::DimensionControl::DefineArraySize);
        type = ImageType::Linear;
        pitch = ct.width;
        size = Extent3D{
            .width = pitch / BytesPerBlock(format),
            .height = ct.height,
            .depth = 1,
        };
        return;
    }
    size.width = ct.width;
    size.height = ct.height;
    layer_stride = ct.array_pitch * 4;
    maybe_unaligned_layer_stride = layer_stride;
    num_samples = NumSamples(msaa_mode);
    block = Extent3D{
        .width = ct.tile_mode.block_width,
        .height = ct.tile_mode.block_height,
        .depth = ct.tile_mode.block_depth,
    };
    if (ct.tile_mode.dim_control == Maxwell3D::Regs::TileMode::DimensionControl::DefineDepthSize) {
        type = ImageType::e3D;
        size.depth = ct.depth;
    } else {
        rescaleable = block.depth == 0;
        rescaleable &= size.height > RescaleHeightThreshold;
        downscaleable = size.height > DownscaleHeightThreshold;
        type = ImageType::e2D;
        resources.layers = ct.depth;
    }
}

ImageInfo::ImageInfo(const Maxwell3D::Regs::Zeta& zt, const Maxwell3D::Regs::ZetaSize& zt_size,
                     Tegra::Texture::MsaaMode msaa_mode) noexcept {
    forced_flushed =
        zt.tile_mode.is_pitch_linear && !Settings::values.use_reactive_flushing.GetValue();
    dma_downloaded = forced_flushed;
    format = VideoCore::Surface::PixelFormatFromDepthFormat(zt.format);
    size.width = zt_size.width;
    size.height = zt_size.height;
    rescaleable = false;
    resources.levels = 1;
    layer_stride = zt.array_pitch * 4;
    maybe_unaligned_layer_stride = layer_stride;
    num_samples = NumSamples(msaa_mode);
    block = Extent3D{
        .width = zt.tile_mode.block_width,
        .height = zt.tile_mode.block_height,
        .depth = zt.tile_mode.block_depth,
    };
    if (zt.tile_mode.is_pitch_linear) {
        ASSERT(zt.tile_mode.dim_control ==
               Maxwell3D::Regs::TileMode::DimensionControl::DefineArraySize);
        type = ImageType::Linear;
        pitch = size.width * BytesPerBlock(format);
    } else if (zt.tile_mode.dim_control ==
               Maxwell3D::Regs::TileMode::DimensionControl::DefineDepthSize) {
        ASSERT(zt_size.dim_control == Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeIsOne);
        type = ImageType::e3D;
        size.depth = zt_size.depth;
    } else {
        rescaleable = block.depth == 0;
        downscaleable = size.height > 512;
        type = ImageType::e2D;
        switch (zt_size.dim_control) {
        case Maxwell3D::Regs::ZetaSize::DimensionControl::DefineArraySize:
            resources.layers = zt_size.depth;
            break;
        case Maxwell3D::Regs::ZetaSize::DimensionControl::ArraySizeIsOne:
            resources.layers = 1;
            break;
        }
    }
}

ImageInfo::ImageInfo(const Fermi2D::Surface& config) noexcept {
    UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero");
    forced_flushed = config.linear == Fermi2D::MemoryLayout::Pitch &&
                     !Settings::values.use_reactive_flushing.GetValue();
    dma_downloaded = forced_flushed;
    format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format);
    rescaleable = false;
    if (config.linear == Fermi2D::MemoryLayout::Pitch) {
        type = ImageType::Linear;
        size = Extent3D{
            .width = config.pitch / VideoCore::Surface::BytesPerBlock(format),
            .height = config.height,
            .depth = 1,
        };
        pitch = config.pitch;
    } else {
        type = config.block_depth > 0 ? ImageType::e3D : ImageType::e2D;

        block = Extent3D{
            .width = config.block_width,
            .height = config.block_height,
            .depth = config.block_depth,
        };
        // 3D blits with more than once slice are not implemented for now
        // Render to individual slices
        size = Extent3D{
            .width = config.width,
            .height = config.height,
            .depth = 1,
        };
        rescaleable = block.depth == 0 && size.height > RescaleHeightThreshold;
        downscaleable = size.height > DownscaleHeightThreshold;
    }
}

static PixelFormat ByteSizeToFormat(u32 bytes_per_pixel) {
    switch (bytes_per_pixel) {
    case 1:
        return PixelFormat::R8_UINT;
    case 2:
        return PixelFormat::R8G8_UINT;
    case 4:
        return PixelFormat::A8B8G8R8_UINT;
    case 8:
        return PixelFormat::R16G16B16A16_UINT;
    case 16:
        return PixelFormat::R32G32B32A32_UINT;
    default:
        UNIMPLEMENTED();
        return PixelFormat::Invalid;
    }
}

ImageInfo::ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept {
    const u32 bytes_per_pixel = config.bytes_per_pixel;
    format = ByteSizeToFormat(bytes_per_pixel);
    type = config.params.block_size.depth > 0 ? ImageType::e3D : ImageType::e2D;
    num_samples = 1;
    block = Extent3D{
        .width = config.params.block_size.width,
        .height = config.params.block_size.height,
        .depth = config.params.block_size.depth,
    };
    size = Extent3D{
        .width = config.params.width,
        .height = config.params.height,
        .depth = config.params.depth,
    };
    tile_width_spacing = 0;
    resources.levels = 1;
    resources.layers = 1;
    layer_stride = CalculateLayerStride(*this);
    maybe_unaligned_layer_stride = CalculateLayerSize(*this);
    rescaleable = block.depth == 0 && size.height > RescaleHeightThreshold;
    downscaleable = size.height > DownscaleHeightThreshold;
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/maxwell_dma.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon {

using Tegra::Texture::TICEntry;
using VideoCore::Surface::PixelFormat;

struct ImageInfo {
    ImageInfo() = default;
    explicit ImageInfo(const TICEntry& config) noexcept;
    explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig& ct,
                       Tegra::Texture::MsaaMode msaa_mode) noexcept;
    explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs::Zeta& zt,
                       const Tegra::Engines::Maxwell3D::Regs::ZetaSize& zt_size,
                       Tegra::Texture::MsaaMode msaa_mode) noexcept;
    explicit ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept;
    explicit ImageInfo(const Tegra::DMA::ImageOperand& config) noexcept;

    PixelFormat format = PixelFormat::Invalid;
    ImageType type = ImageType::e1D;
    SubresourceExtent resources;
    Extent3D size{1, 1, 1};
    union {
        Extent3D block{0, 0, 0};
        u32 pitch;
    };
    u32 layer_stride = 0;
    u32 maybe_unaligned_layer_stride = 0;
    u32 num_samples = 1;
    u32 tile_width_spacing = 0;
    bool rescaleable = false;
    bool downscaleable = false;
    bool forced_flushed = false;
    bool dma_downloaded = false;
    bool is_sparse = false;
};

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <algorithm>

#include "common/assert.h"
#include "video_core/compatible_formats.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/formatter.h"
#include "video_core/texture_cache/image_info.h"
#include "video_core/texture_cache/image_view_base.h"
#include "video_core/texture_cache/image_view_info.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon {

ImageViewBase::ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info,
                             ImageId image_id_, GPUVAddr addr)
    : image_id{image_id_}, gpu_addr{addr}, format{info.format}, type{info.type}, range{info.range},
      size{
          .width = std::max(image_info.size.width >> range.base.level, 1u),
          .height = std::max(image_info.size.height >> range.base.level, 1u),
          .depth = std::max(image_info.size.depth >> range.base.level, 1u),
      } {
    ASSERT_MSG(VideoCore::Surface::IsViewCompatible(image_info.format, info.format, false, true),
               "Image view format {} is incompatible with image format {}", info.format,
               image_info.format);
    if (image_info.forced_flushed) {
        flags |= ImageViewFlagBits::PreemtiveDownload;
    }
    if (image_info.type == ImageType::e3D && info.type != ImageViewType::e3D) {
        flags |= ImageViewFlagBits::Slice;
    }
}

ImageViewBase::ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info, GPUVAddr addr)
    : image_id{NULL_IMAGE_ID}, gpu_addr{addr}, format{info.format}, type{ImageViewType::Buffer},
      size{
          .width = info.size.width,
          .height = 1,
          .depth = 1,
      } {
    ASSERT_MSG(view_info.type == ImageViewType::Buffer, "Expected texture buffer");
}

ImageViewBase::ImageViewBase(const NullImageViewParams&) : image_id{NULL_IMAGE_ID} {}

bool ImageViewBase::SupportsAnisotropy() const noexcept {
    const bool has_mips = range.extent.levels > 1;
    const bool is_2d = type == ImageViewType::e2D || type == ImageViewType::e2DArray;
    if (!has_mips || !is_2d) {
        return false;
    }

    switch (format) {
    case PixelFormat::R8_UNORM:
    case PixelFormat::R8_SNORM:
    case PixelFormat::R8_SINT:
    case PixelFormat::R8_UINT:
    case PixelFormat::BC4_UNORM:
    case PixelFormat::BC4_SNORM:
    case PixelFormat::BC5_UNORM:
    case PixelFormat::BC5_SNORM:
    case PixelFormat::R32G32_FLOAT:
    case PixelFormat::R32G32_SINT:
    case PixelFormat::R32_FLOAT:
    case PixelFormat::R16_FLOAT:
    case PixelFormat::R16_UNORM:
    case PixelFormat::R16_SNORM:
    case PixelFormat::R16_UINT:
    case PixelFormat::R16_SINT:
    case PixelFormat::R16G16_UNORM:
    case PixelFormat::R16G16_FLOAT:
    case PixelFormat::R16G16_UINT:
    case PixelFormat::R16G16_SINT:
    case PixelFormat::R16G16_SNORM:
    case PixelFormat::R8G8_UNORM:
    case PixelFormat::R8G8_SNORM:
    case PixelFormat::R8G8_SINT:
    case PixelFormat::R8G8_UINT:
    case PixelFormat::R32G32_UINT:
    case PixelFormat::R32_UINT:
    case PixelFormat::R32_SINT:
    case PixelFormat::G4R4_UNORM:
    // Depth formats
    case PixelFormat::D32_FLOAT:
    case PixelFormat::D16_UNORM:
    case PixelFormat::X8_D24_UNORM:
    // Stencil formats
    case PixelFormat::S8_UINT:
    // DepthStencil formats
    case PixelFormat::D24_UNORM_S8_UINT:
    case PixelFormat::S8_UINT_D24_UNORM:
    case PixelFormat::D32_FLOAT_S8_UINT:
        return false;
    default:
        return true;
    }
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include "common/common_funcs.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon {

using VideoCore::Surface::PixelFormat;

struct ImageViewInfo;
struct ImageInfo;

struct NullImageViewParams {};

enum class ImageViewFlagBits : u16 {
    PreemtiveDownload = 1 << 0,
    Strong = 1 << 1,
    Slice = 1 << 2,
};
DECLARE_ENUM_FLAG_OPERATORS(ImageViewFlagBits)

struct ImageViewBase {
    explicit ImageViewBase(const ImageViewInfo& info, const ImageInfo& image_info, ImageId image_id,
                           GPUVAddr addr);
    explicit ImageViewBase(const ImageInfo& info, const ImageViewInfo& view_info, GPUVAddr addr);
    explicit ImageViewBase(const NullImageViewParams&);

    [[nodiscard]] bool IsBuffer() const noexcept {
        return type == ImageViewType::Buffer;
    }

    [[nodiscard]] bool SupportsAnisotropy() const noexcept;

    ImageId image_id{};
    GPUVAddr gpu_addr = 0;
    PixelFormat format{};
    ImageViewType type{};
    SubresourceRange range;
    Extent3D size{0, 0, 0};
    ImageViewFlagBits flags{};

    u64 invalidation_tick = 0;
    u64 modification_tick = 0;
};

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#include <limits>

#include "common/assert.h"
#include "video_core/texture_cache/image_view_info.h"
#include "video_core/texture_cache/texture_cache_base.h"
#include "video_core/texture_cache/types.h"
#include "video_core/texture_cache/util.h"
#include "video_core/textures/texture.h"

namespace VideoCommon {

namespace {

using Tegra::Texture::TextureType;

constexpr u8 RENDER_TARGET_SWIZZLE = std::numeric_limits<u8>::max();

[[nodiscard]] u8 CastSwizzle(SwizzleSource source) {
    const u8 casted = static_cast<u8>(source);
    ASSERT(static_cast<SwizzleSource>(casted) == source);
    return casted;
}

} // Anonymous namespace

ImageViewInfo::ImageViewInfo(const TICEntry& config, s32 base_layer) noexcept
    : format{PixelFormatFromTIC(config)}, x_source{CastSwizzle(config.x_source)},
      y_source{CastSwizzle(config.y_source)}, z_source{CastSwizzle(config.z_source)},
      w_source{CastSwizzle(config.w_source)} {
    range.base = SubresourceBase{
        .level = static_cast<s32>(config.res_min_mip_level),
        .layer = base_layer,
    };
    range.extent.levels = config.res_max_mip_level - config.res_min_mip_level + 1;

    switch (config.texture_type) {
    case TextureType::Texture1D:
        ASSERT(config.Height() == 1);
        ASSERT(config.Depth() == 1);
        type = ImageViewType::e1D;
        break;
    case TextureType::Texture2D:
    case TextureType::Texture2DNoMipmap:
        ASSERT(config.Depth() == 1);
        type = config.normalized_coords ? ImageViewType::e2D : ImageViewType::Rect;
        break;
    case TextureType::Texture3D:
        type = ImageViewType::e3D;
        break;
    case TextureType::TextureCubemap:
        ASSERT(config.Depth() == 1);
        type = ImageViewType::Cube;
        range.extent.layers = 6;
        break;
    case TextureType::Texture1DArray:
        type = ImageViewType::e1DArray;
        range.extent.layers = config.Depth();
        break;
    case TextureType::Texture2DArray:
        type = ImageViewType::e2DArray;
        range.extent.layers = config.Depth();
        break;
    case TextureType::Texture1DBuffer:
        type = ImageViewType::Buffer;
        break;
    case TextureType::TextureCubeArray:
        type = ImageViewType::CubeArray;
        range.extent.layers = config.Depth() * 6;
        break;
    default:
        ASSERT_MSG(false, "Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
        break;
    }
}

ImageViewInfo::ImageViewInfo(ImageViewType type_, PixelFormat format_,
                             SubresourceRange range_) noexcept
    : type{type_}, format{format_}, range{range_}, x_source{RENDER_TARGET_SWIZZLE},
      y_source{RENDER_TARGET_SWIZZLE}, z_source{RENDER_TARGET_SWIZZLE},
      w_source{RENDER_TARGET_SWIZZLE} {}

bool ImageViewInfo::IsRenderTarget() const noexcept {
    return x_source == RENDER_TARGET_SWIZZLE && y_source == RENDER_TARGET_SWIZZLE &&
           z_source == RENDER_TARGET_SWIZZLE && w_source == RENDER_TARGET_SWIZZLE;
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <array>
#include <type_traits>

#include "video_core/surface.h"
#include "video_core/texture_cache/types.h"
#include "video_core/textures/texture.h"

namespace VideoCommon {

using Tegra::Texture::SwizzleSource;
using Tegra::Texture::TICEntry;
using VideoCore::Surface::PixelFormat;

/// Properties used to determine a image view
struct ImageViewInfo {
    explicit ImageViewInfo() noexcept = default;
    explicit ImageViewInfo(const TICEntry& config, s32 base_layer) noexcept;
    explicit ImageViewInfo(ImageViewType type, PixelFormat format,
                           SubresourceRange range = {}) noexcept;

    auto operator<=>(const ImageViewInfo&) const noexcept = default;

    [[nodiscard]] bool IsRenderTarget() const noexcept;

    [[nodiscard]] std::array<SwizzleSource, 4> Swizzle() const noexcept {
        return std::array{
            static_cast<SwizzleSource>(x_source),
            static_cast<SwizzleSource>(y_source),
            static_cast<SwizzleSource>(z_source),
            static_cast<SwizzleSource>(w_source),
        };
    }

    ImageViewType type{};
    PixelFormat format{};
    SubresourceRange range;
    u8 x_source = static_cast<u8>(SwizzleSource::R);
    u8 y_source = static_cast<u8>(SwizzleSource::G);
    u8 z_source = static_cast<u8>(SwizzleSource::B);
    u8 w_source = static_cast<u8>(SwizzleSource::A);
};
static_assert(std::has_unique_object_representations_v<ImageViewInfo>);

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <algorithm>
#include <span>

#include "common/bit_cast.h"
#include "video_core/texture_cache/types.h"

namespace VideoCommon {

/// Framebuffer properties used to lookup a framebuffer
struct RenderTargets {
    constexpr bool operator==(const RenderTargets&) const noexcept = default;

    constexpr bool Contains(std::span<const ImageViewId> elements) const noexcept {
        const auto contains = [elements](ImageViewId item) {
            return std::ranges::find(elements, item) != elements.end();
        };
        return std::ranges::any_of(color_buffer_ids, contains) || contains(depth_buffer_id);
    }

    std::array<ImageViewId, NUM_RT> color_buffer_ids{};
    ImageViewId depth_buffer_id{};
    std::array<u8, NUM_RT> draw_buffers{};
    Extent2D size{};
    bool is_rescaled{};
};

} // namespace VideoCommon

namespace std {

template <>
struct hash<VideoCommon::RenderTargets> {
    size_t operator()(const VideoCommon::RenderTargets& rt) const noexcept {
        using VideoCommon::ImageViewId;
        size_t value = std::hash<ImageViewId>{}(rt.depth_buffer_id);
        for (const ImageViewId color_buffer_id : rt.color_buffer_ids) {
            value ^= std::hash<ImageViewId>{}(color_buffer_id);
        }
        value ^= Common::BitCast<u64>(rt.draw_buffers);
        value ^= Common::BitCast<u64>(rt.size);
        return value;
    }
};

} // namespace std


code:
// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later

#pragma once

#include <utility>

#include "common/assert.h"
#include "video_core/textures/texture.h"

namespace VideoCommon {

[[nodiscard]] inline std::pair<int, int> SamplesLog2(int num_samples) {
    switch (num_samples) {
    case 1:
        return {0, 0};
    case 2:
        return {1, 0};
    case 4:
        return {1, 1};
    case 8:
        return {2, 1};
    case 16:
        return {2, 2};
    }
    ASSERT_MSG(false, "Invalid number of samples={}", num_samples);
    return {0, 0};
}

[[nodiscard]] inline int NumSamples(Tegra::Texture::MsaaMode msaa_mode) {
    using Tegra::Texture::MsaaMode;
    switch (msaa_mode) {
    case MsaaMode::Msaa1x1:
        return 1;
    case MsaaMode::Msaa2x1:
    case MsaaMode::Msaa2x1_D3D:
        return 2;
    case MsaaMode::Msaa2x2:
    case MsaaMode::Msaa2x2_VC4:
    case MsaaMode::Msaa2x2_VC12:
        return 4;
    case MsaaMode::Msaa4x2:
    case MsaaMode::Msaa4x2_D3D:
    case MsaaMode::Msaa4x2_VC8:
    case MsaaMode::Msaa4x2_VC24:
        return 8;
    case MsaaMode::Msaa4x4:
        return 16;
    }
    ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
    return 1;
}

[[nodiscard]] inline int NumSamplesX(Tegra::Texture::MsaaMode msaa_mode) {
    using Tegra::Texture::MsaaMode;
    switch (msaa_mode) {
    case MsaaMode::Msaa1x1:
        return 1;
    case MsaaMode::Msaa2x1:
    case MsaaMode::Msaa2x1_D3D:
    case MsaaMode::Msaa2x2:
    case MsaaMode::Msaa2x2_VC4:
    case MsaaMode::Msaa2x2_VC12:
        return 2;
    case MsaaMode::Msaa4x2:
    case MsaaMode::Msaa4x2_D3D:
    case MsaaMode::Msaa4x2_VC8:
    case MsaaMode::Msaa4x2_VC24:
    case MsaaMode::Msaa4x4:
        return 4;
    }
    ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
    return 1;
}

[[nodiscard]] inline int NumSamplesY(Tegra::Texture::MsaaMode msaa_mode) {
    using Tegra::Texture::MsaaMode;
    switch (msaa_mode) {
    case MsaaMode::Msaa1x1:
    case MsaaMode::Msaa2x1:
    case MsaaMode::Msaa2x1_D3D:
        return 1;
    case MsaaMode::Msaa2x2:
    case MsaaMode::Msaa2x2_VC4:
    case MsaaMode::Msaa2x2_VC12:
    case MsaaMode::Msaa4x2:
    case MsaaMode::Msaa4x2_D3D:
    case MsaaMode::Msaa4x2_VC8:
    case MsaaMode::Msaa4x2_VC24:
        return 2;
    case MsaaMode::Msaa4x4:
        return 4;
    }
    ASSERT_MSG(false, "Invalid MSAA mode={}", static_cast<int>(msaa_mode));
    return 1;
}

} // namespace VideoCommon


code:
// SPDX-FileCopyrightText: 2021 yuzu Emulator Project
// SPDX-License-Identifier: GPL-3.0-or-later

#include "video_core/control/channel_state_cache.inc"
#include "video_core/texture_cache/texture_cache_base.h"

namespace VideoCommon {

TextureCacheChannelInfo::TextureCacheChannelInfo(Tegra::Control::ChannelState& state) noexcept
    : ChannelInfo(state), graphics_image_table{gpu_memory}, graphics_sampler_table{gpu_memory},
      compute_image_table{gpu_memory}, compute_sampler_table{gpu_memory} {}

template class VideoCommon::ChannelSetupCaches<VideoCommon::TextureCacheChannelInfo>;

} // namespace VideoCommon