video_core: Rewrite the texture cache
The current texture cache has several points that hurt maintainability and performance. It's easy to break unrelated parts of the cache when doing minor changes. The cache can easily forget valuable information about the cached textures by CPU writes or simply by its normal usage.The current texture cache has several points that hurt maintainability and performance. It's easy to break unrelated parts of the cache when doing minor changes. The cache can easily forget valuable information about the cached textures by CPU writes or simply by its normal usage. This commit aims to address those issues.
This commit is contained in:
parent
9106ac1e6b
commit
9764c13d6d
152 changed files with 10609 additions and 8351 deletions
70
src/video_core/texture_cache/accelerated_swizzle.cpp
Normal file
70
src/video_core/texture_cache/accelerated_swizzle.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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::AlignBits(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::AlignBits(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
|
45
src/video_core/texture_cache/accelerated_swizzle.h
Normal file
45
src/video_core/texture_cache/accelerated_swizzle.h
Normal file
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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 {
|
||||
std::array<u32, 3> origin;
|
||||
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
|
|
@ -1,36 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
struct CopyParams {
|
||||
constexpr CopyParams(u32 source_x_, u32 source_y_, u32 source_z_, u32 dest_x_, u32 dest_y_,
|
||||
u32 dest_z_, u32 source_level_, u32 dest_level_, u32 width_, u32 height_,
|
||||
u32 depth_)
|
||||
: source_x{source_x_}, source_y{source_y_}, source_z{source_z_}, dest_x{dest_x_},
|
||||
dest_y{dest_y_}, dest_z{dest_z_}, source_level{source_level_},
|
||||
dest_level{dest_level_}, width{width_}, height{height_}, depth{depth_} {}
|
||||
|
||||
constexpr CopyParams(u32 width_, u32 height_, u32 depth_, u32 level_)
|
||||
: source_x{}, source_y{}, source_z{}, dest_x{}, dest_y{}, dest_z{}, source_level{level_},
|
||||
dest_level{level_}, width{width_}, height{height_}, depth{depth_} {}
|
||||
|
||||
u32 source_x;
|
||||
u32 source_y;
|
||||
u32 source_z;
|
||||
u32 dest_x;
|
||||
u32 dest_y;
|
||||
u32 dest_z;
|
||||
u32 source_level;
|
||||
u32 dest_level;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
97
src/video_core/texture_cache/decode_bc4.cpp
Normal file
97
src/video_core/texture_cache/decode_bc4.cpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <span>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/texture_cache/decode_bc4.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_compression_rgtc.txt
|
||||
[[nodiscard]] constexpr u32 DecompressBlock(u64 bits, u32 x, u32 y) {
|
||||
const u32 code_offset = 16 + 3 * (4 * y + x);
|
||||
const u32 code = (bits >> code_offset) & 7;
|
||||
const u32 red0 = (bits >> 0) & 0xff;
|
||||
const u32 red1 = (bits >> 8) & 0xff;
|
||||
if (red0 > red1) {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return red0;
|
||||
case 1:
|
||||
return red1;
|
||||
case 2:
|
||||
return (6 * red0 + 1 * red1) / 7;
|
||||
case 3:
|
||||
return (5 * red0 + 2 * red1) / 7;
|
||||
case 4:
|
||||
return (4 * red0 + 3 * red1) / 7;
|
||||
case 5:
|
||||
return (3 * red0 + 4 * red1) / 7;
|
||||
case 6:
|
||||
return (2 * red0 + 5 * red1) / 7;
|
||||
case 7:
|
||||
return (1 * red0 + 6 * red1) / 7;
|
||||
}
|
||||
} else {
|
||||
switch (code) {
|
||||
case 0:
|
||||
return red0;
|
||||
case 1:
|
||||
return red1;
|
||||
case 2:
|
||||
return (4 * red0 + 1 * red1) / 5;
|
||||
case 3:
|
||||
return (3 * red0 + 2 * red1) / 5;
|
||||
case 4:
|
||||
return (2 * red0 + 3 * red1) / 5;
|
||||
case 5:
|
||||
return (1 * red0 + 4 * red1) / 5;
|
||||
case 6:
|
||||
return 0;
|
||||
case 7:
|
||||
return 0xff;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DecompressBC4(std::span<const u8> input, Extent3D extent, std::span<u8> output) {
|
||||
UNIMPLEMENTED_IF_MSG(extent.width % 4 != 0, "Unaligned width={}", extent.width);
|
||||
UNIMPLEMENTED_IF_MSG(extent.height % 4 != 0, "Unaligned height={}", extent.height);
|
||||
static constexpr u32 BLOCK_SIZE = 4;
|
||||
size_t input_offset = 0;
|
||||
for (u32 slice = 0; slice < extent.depth; ++slice) {
|
||||
for (u32 block_y = 0; block_y < extent.height / 4; ++block_y) {
|
||||
for (u32 block_x = 0; block_x < extent.width / 4; ++block_x) {
|
||||
u64 bits;
|
||||
std::memcpy(&bits, &input[input_offset], sizeof(bits));
|
||||
input_offset += sizeof(bits);
|
||||
|
||||
for (u32 y = 0; y < BLOCK_SIZE; ++y) {
|
||||
for (u32 x = 0; x < BLOCK_SIZE; ++x) {
|
||||
const u32 linear_z = slice;
|
||||
const u32 linear_y = block_y * BLOCK_SIZE + y;
|
||||
const u32 linear_x = block_x * BLOCK_SIZE + x;
|
||||
const u32 offset_z = linear_z * extent.width * extent.height;
|
||||
const u32 offset_y = linear_y * extent.width;
|
||||
const u32 offset_x = linear_x;
|
||||
const u32 output_offset = (offset_z + offset_y + offset_x) * 4ULL;
|
||||
const u32 color = DecompressBlock(bits, x, y);
|
||||
output[output_offset + 0] = static_cast<u8>(color);
|
||||
output[output_offset + 1] = 0;
|
||||
output[output_offset + 2] = 0;
|
||||
output[output_offset + 3] = 0xff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
16
src/video_core/texture_cache/decode_bc4.h
Normal file
16
src/video_core/texture_cache/decode_bc4.h
Normal file
|
@ -0,0 +1,16 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
void DecompressBC4(std::span<const u8> data, Extent3D extent, std::span<u8> output);
|
||||
|
||||
} // namespace VideoCommon
|
82
src/video_core/texture_cache/descriptor_table.h
Normal file
82
src/video_core/texture_cache/descriptor_table.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/div_ceil.h"
|
||||
#include "common/logging/log.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 Synchornize(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
|
|
@ -2,7 +2,6 @@
|
|||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <array>
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/texture_cache/format_lookup_table.h"
|
||||
|
@ -20,198 +19,207 @@ constexpr auto UNORM = ComponentType::UNORM;
|
|||
constexpr auto SINT = ComponentType::SINT;
|
||||
constexpr auto UINT = ComponentType::UINT;
|
||||
constexpr auto FLOAT = ComponentType::FLOAT;
|
||||
constexpr bool C = false; // Normal color
|
||||
constexpr bool S = true; // Srgb
|
||||
constexpr bool LINEAR = false;
|
||||
constexpr bool SRGB = true;
|
||||
|
||||
struct Table {
|
||||
constexpr Table(TextureFormat texture_format_, bool is_srgb_, ComponentType red_component_,
|
||||
ComponentType green_component_, ComponentType blue_component_,
|
||||
ComponentType alpha_component_, PixelFormat pixel_format_)
|
||||
: texture_format{texture_format_}, pixel_format{pixel_format_},
|
||||
red_component{red_component_}, green_component{green_component_},
|
||||
blue_component{blue_component_}, alpha_component{alpha_component_}, is_srgb{is_srgb_} {}
|
||||
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;
|
||||
}
|
||||
|
||||
TextureFormat texture_format;
|
||||
PixelFormat pixel_format;
|
||||
ComponentType red_component;
|
||||
ComponentType green_component;
|
||||
ComponentType blue_component;
|
||||
ComponentType alpha_component;
|
||||
bool is_srgb;
|
||||
};
|
||||
constexpr std::array<Table, 86> DefinitionTable = {{
|
||||
{TextureFormat::A8R8G8B8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::A8B8G8R8_UNORM},
|
||||
{TextureFormat::A8R8G8B8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::A8B8G8R8_SNORM},
|
||||
{TextureFormat::A8R8G8B8, C, UINT, UINT, UINT, UINT, PixelFormat::A8B8G8R8_UINT},
|
||||
{TextureFormat::A8R8G8B8, C, SINT, SINT, SINT, SINT, PixelFormat::A8B8G8R8_SINT},
|
||||
{TextureFormat::A8R8G8B8, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::A8B8G8R8_SRGB},
|
||||
|
||||
{TextureFormat::B5G6R5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::B5G6R5_UNORM},
|
||||
|
||||
{TextureFormat::A2B10G10R10, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::A2B10G10R10_UNORM},
|
||||
{TextureFormat::A2B10G10R10, C, UINT, UINT, UINT, UINT, PixelFormat::A2B10G10R10_UINT},
|
||||
|
||||
{TextureFormat::A1B5G5R5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::A1B5G5R5_UNORM},
|
||||
|
||||
{TextureFormat::A4B4G4R4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::A4B4G4R4_UNORM},
|
||||
|
||||
{TextureFormat::R8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R8_UNORM},
|
||||
{TextureFormat::R8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::R8_SNORM},
|
||||
{TextureFormat::R8, C, UINT, UINT, UINT, UINT, PixelFormat::R8_UINT},
|
||||
{TextureFormat::R8, C, SINT, SINT, SINT, SINT, PixelFormat::R8_SINT},
|
||||
|
||||
{TextureFormat::R8G8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R8G8_UNORM},
|
||||
{TextureFormat::R8G8, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::R8G8_SNORM},
|
||||
{TextureFormat::R8G8, C, UINT, UINT, UINT, UINT, PixelFormat::R8G8_UINT},
|
||||
{TextureFormat::R8G8, C, SINT, SINT, SINT, SINT, PixelFormat::R8G8_SINT},
|
||||
|
||||
{TextureFormat::R16G16B16A16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::R16G16B16A16_SNORM},
|
||||
{TextureFormat::R16G16B16A16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R16G16B16A16_UNORM},
|
||||
{TextureFormat::R16G16B16A16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R16G16B16A16_FLOAT},
|
||||
{TextureFormat::R16G16B16A16, C, UINT, UINT, UINT, UINT, PixelFormat::R16G16B16A16_UINT},
|
||||
{TextureFormat::R16G16B16A16, C, SINT, SINT, SINT, SINT, PixelFormat::R16G16B16A16_SINT},
|
||||
|
||||
{TextureFormat::R16G16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R16G16_FLOAT},
|
||||
{TextureFormat::R16G16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R16G16_UNORM},
|
||||
{TextureFormat::R16G16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::R16G16_SNORM},
|
||||
{TextureFormat::R16G16, C, UINT, UINT, UINT, UINT, PixelFormat::R16G16_UINT},
|
||||
{TextureFormat::R16G16, C, SINT, SINT, SINT, SINT, PixelFormat::R16G16_SINT},
|
||||
|
||||
{TextureFormat::R16, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R16_FLOAT},
|
||||
{TextureFormat::R16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::R16_UNORM},
|
||||
{TextureFormat::R16, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::R16_SNORM},
|
||||
{TextureFormat::R16, C, UINT, UINT, UINT, UINT, PixelFormat::R16_UINT},
|
||||
{TextureFormat::R16, C, SINT, SINT, SINT, SINT, PixelFormat::R16_SINT},
|
||||
|
||||
{TextureFormat::B10G11R11, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::B10G11R11_FLOAT},
|
||||
|
||||
{TextureFormat::R32G32B32A32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32G32B32A32_FLOAT},
|
||||
{TextureFormat::R32G32B32A32, C, UINT, UINT, UINT, UINT, PixelFormat::R32G32B32A32_UINT},
|
||||
{TextureFormat::R32G32B32A32, C, SINT, SINT, SINT, SINT, PixelFormat::R32G32B32A32_SINT},
|
||||
|
||||
{TextureFormat::R32G32B32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32G32B32_FLOAT},
|
||||
|
||||
{TextureFormat::R32G32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32G32_FLOAT},
|
||||
{TextureFormat::R32G32, C, UINT, UINT, UINT, UINT, PixelFormat::R32G32_UINT},
|
||||
{TextureFormat::R32G32, C, SINT, SINT, SINT, SINT, PixelFormat::R32G32_SINT},
|
||||
|
||||
{TextureFormat::R32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::R32_FLOAT},
|
||||
{TextureFormat::R32, C, UINT, UINT, UINT, UINT, PixelFormat::R32_UINT},
|
||||
{TextureFormat::R32, C, SINT, SINT, SINT, SINT, PixelFormat::R32_SINT},
|
||||
|
||||
{TextureFormat::E5B9G9R9, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::E5B9G9R9_FLOAT},
|
||||
|
||||
{TextureFormat::D32, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::D32_FLOAT},
|
||||
{TextureFormat::D16, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::D16_UNORM},
|
||||
{TextureFormat::S8D24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8_UINT_D24_UNORM},
|
||||
{TextureFormat::R8G24, C, UINT, UNORM, UNORM, UNORM, PixelFormat::S8_UINT_D24_UNORM},
|
||||
{TextureFormat::D32S8, C, FLOAT, UINT, UNORM, UNORM, PixelFormat::D32_FLOAT_S8_UINT},
|
||||
|
||||
{TextureFormat::BC1_RGBA, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC1_RGBA_UNORM},
|
||||
{TextureFormat::BC1_RGBA, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC1_RGBA_SRGB},
|
||||
|
||||
{TextureFormat::BC2, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC2_UNORM},
|
||||
{TextureFormat::BC2, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC2_SRGB},
|
||||
|
||||
{TextureFormat::BC3, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC3_UNORM},
|
||||
{TextureFormat::BC3, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC3_SRGB},
|
||||
|
||||
{TextureFormat::BC4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC4_UNORM},
|
||||
{TextureFormat::BC4, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::BC4_SNORM},
|
||||
|
||||
{TextureFormat::BC5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC5_UNORM},
|
||||
{TextureFormat::BC5, C, SNORM, SNORM, SNORM, SNORM, PixelFormat::BC5_SNORM},
|
||||
|
||||
{TextureFormat::BC7, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC7_UNORM},
|
||||
{TextureFormat::BC7, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::BC7_SRGB},
|
||||
|
||||
{TextureFormat::BC6H_SFLOAT, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::BC6H_SFLOAT},
|
||||
{TextureFormat::BC6H_UFLOAT, C, FLOAT, FLOAT, FLOAT, FLOAT, PixelFormat::BC6H_UFLOAT},
|
||||
|
||||
{TextureFormat::ASTC_2D_4X4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_4X4_UNORM},
|
||||
{TextureFormat::ASTC_2D_4X4, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_4X4_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_5X4, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X4_UNORM},
|
||||
{TextureFormat::ASTC_2D_5X4, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X4_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_5X5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X5_UNORM},
|
||||
{TextureFormat::ASTC_2D_5X5, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_5X5_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_8X8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X8_UNORM},
|
||||
{TextureFormat::ASTC_2D_8X8, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X8_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_8X5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X5_UNORM},
|
||||
{TextureFormat::ASTC_2D_8X5, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X5_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_10X8, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X8_UNORM},
|
||||
{TextureFormat::ASTC_2D_10X8, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X8_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_6X6, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X6_UNORM},
|
||||
{TextureFormat::ASTC_2D_6X6, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X6_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_10X10, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X10_UNORM},
|
||||
{TextureFormat::ASTC_2D_10X10, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_10X10_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_12X12, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_12X12_UNORM},
|
||||
{TextureFormat::ASTC_2D_12X12, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_12X12_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_8X6, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X6_UNORM},
|
||||
{TextureFormat::ASTC_2D_8X6, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_8X6_SRGB},
|
||||
|
||||
{TextureFormat::ASTC_2D_6X5, C, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X5_UNORM},
|
||||
{TextureFormat::ASTC_2D_6X5, S, UNORM, UNORM, UNORM, UNORM, PixelFormat::ASTC_2D_6X5_SRGB},
|
||||
}};
|
||||
constexpr u32 Hash(TextureFormat format, ComponentType component, bool is_srgb = LINEAR) {
|
||||
return Hash(format, component, component, component, component, is_srgb);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
FormatLookupTable::FormatLookupTable() {
|
||||
table.fill(static_cast<u8>(PixelFormat::Invalid));
|
||||
|
||||
for (const auto& entry : DefinitionTable) {
|
||||
table[CalculateIndex(entry.texture_format, entry.is_srgb != 0, entry.red_component,
|
||||
entry.green_component, entry.blue_component, entry.alpha_component)] =
|
||||
static_cast<u8>(entry.pixel_format);
|
||||
}
|
||||
}
|
||||
|
||||
PixelFormat FormatLookupTable::GetPixelFormat(TextureFormat format, bool is_srgb,
|
||||
ComponentType red_component,
|
||||
ComponentType green_component,
|
||||
ComponentType blue_component,
|
||||
ComponentType alpha_component) const noexcept {
|
||||
const auto pixel_format = static_cast<PixelFormat>(table[CalculateIndex(
|
||||
format, is_srgb, red_component, green_component, blue_component, alpha_component)]);
|
||||
// [[likely]]
|
||||
if (pixel_format != PixelFormat::Invalid) {
|
||||
return pixel_format;
|
||||
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::A8R8G8B8, UNORM):
|
||||
return PixelFormat::A8B8G8R8_UNORM;
|
||||
case Hash(TextureFormat::A8R8G8B8, SNORM):
|
||||
return PixelFormat::A8B8G8R8_SNORM;
|
||||
case Hash(TextureFormat::A8R8G8B8, UINT):
|
||||
return PixelFormat::A8B8G8R8_UINT;
|
||||
case Hash(TextureFormat::A8R8G8B8, SINT):
|
||||
return PixelFormat::A8B8G8R8_SINT;
|
||||
case Hash(TextureFormat::A8R8G8B8, 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::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::R8G8, UNORM):
|
||||
return PixelFormat::R8G8_UNORM;
|
||||
case Hash(TextureFormat::R8G8, SNORM):
|
||||
return PixelFormat::R8G8_SNORM;
|
||||
case Hash(TextureFormat::R8G8, UINT):
|
||||
return PixelFormat::R8G8_UINT;
|
||||
case Hash(TextureFormat::R8G8, 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::D32, FLOAT):
|
||||
return PixelFormat::D32_FLOAT;
|
||||
case Hash(TextureFormat::D16, UNORM):
|
||||
return PixelFormat::D16_UNORM;
|
||||
case Hash(TextureFormat::S8D24, UINT, UNORM, UNORM, UNORM, LINEAR):
|
||||
return PixelFormat::S8_UINT_D24_UNORM;
|
||||
case Hash(TextureFormat::R8G24, UINT, UNORM, UNORM, UNORM, LINEAR):
|
||||
return PixelFormat::S8_UINT_D24_UNORM;
|
||||
case Hash(TextureFormat::D32S8, FLOAT, UINT, UNORM, UNORM, LINEAR):
|
||||
return PixelFormat::D32_FLOAT_S8_UINT;
|
||||
case Hash(TextureFormat::BC1_RGBA, UNORM, LINEAR):
|
||||
return PixelFormat::BC1_RGBA_UNORM;
|
||||
case Hash(TextureFormat::BC1_RGBA, UNORM, SRGB):
|
||||
return PixelFormat::BC1_RGBA_SRGB;
|
||||
case Hash(TextureFormat::BC2, UNORM, LINEAR):
|
||||
return PixelFormat::BC2_UNORM;
|
||||
case Hash(TextureFormat::BC2, UNORM, SRGB):
|
||||
return PixelFormat::BC2_SRGB;
|
||||
case Hash(TextureFormat::BC3, UNORM, LINEAR):
|
||||
return PixelFormat::BC3_UNORM;
|
||||
case Hash(TextureFormat::BC3, UNORM, SRGB):
|
||||
return PixelFormat::BC3_SRGB;
|
||||
case Hash(TextureFormat::BC4, UNORM):
|
||||
return PixelFormat::BC4_UNORM;
|
||||
case Hash(TextureFormat::BC4, SNORM):
|
||||
return PixelFormat::BC4_SNORM;
|
||||
case Hash(TextureFormat::BC5, UNORM):
|
||||
return PixelFormat::BC5_UNORM;
|
||||
case Hash(TextureFormat::BC5, SNORM):
|
||||
return PixelFormat::BC5_SNORM;
|
||||
case Hash(TextureFormat::BC7, UNORM, LINEAR):
|
||||
return PixelFormat::BC7_UNORM;
|
||||
case Hash(TextureFormat::BC7, UNORM, SRGB):
|
||||
return PixelFormat::BC7_SRGB;
|
||||
case Hash(TextureFormat::BC6H_SFLOAT, FLOAT):
|
||||
return PixelFormat::BC6H_SFLOAT;
|
||||
case Hash(TextureFormat::BC6H_UFLOAT, 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_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_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_component),
|
||||
static_cast<int>(green_component), static_cast<int>(blue_component),
|
||||
static_cast<int>(alpha_component));
|
||||
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;
|
||||
}
|
||||
|
||||
void FormatLookupTable::Set(TextureFormat format, bool is_srgb, ComponentType red_component,
|
||||
ComponentType green_component, ComponentType blue_component,
|
||||
ComponentType alpha_component, PixelFormat pixel_format) {}
|
||||
|
||||
std::size_t FormatLookupTable::CalculateIndex(TextureFormat format, bool is_srgb,
|
||||
ComponentType red_component,
|
||||
ComponentType green_component,
|
||||
ComponentType blue_component,
|
||||
ComponentType alpha_component) noexcept {
|
||||
const auto format_index = static_cast<std::size_t>(format);
|
||||
const auto red_index = static_cast<std::size_t>(red_component);
|
||||
const auto green_index = static_cast<std::size_t>(green_component);
|
||||
const auto blue_index = static_cast<std::size_t>(blue_component);
|
||||
const auto alpha_index = static_cast<std::size_t>(alpha_component);
|
||||
const std::size_t srgb_index = is_srgb ? 1 : 0;
|
||||
|
||||
return format_index * PerFormat +
|
||||
srgb_index * PerComponent * PerComponent * PerComponent * PerComponent +
|
||||
alpha_index * PerComponent * PerComponent * PerComponent +
|
||||
blue_index * PerComponent * PerComponent + green_index * PerComponent + red_index;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
|
|
@ -4,48 +4,14 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
class FormatLookupTable {
|
||||
public:
|
||||
explicit FormatLookupTable();
|
||||
|
||||
VideoCore::Surface::PixelFormat GetPixelFormat(
|
||||
Tegra::Texture::TextureFormat format, bool is_srgb,
|
||||
Tegra::Texture::ComponentType red_component, Tegra::Texture::ComponentType green_component,
|
||||
Tegra::Texture::ComponentType blue_component,
|
||||
Tegra::Texture::ComponentType alpha_component) const noexcept;
|
||||
|
||||
private:
|
||||
static_assert(VideoCore::Surface::MaxPixelFormat <= std::numeric_limits<u8>::max());
|
||||
|
||||
static constexpr std::size_t NumTextureFormats = 128;
|
||||
|
||||
static constexpr std::size_t PerComponent = 8;
|
||||
static constexpr std::size_t PerComponents2 = PerComponent * PerComponent;
|
||||
static constexpr std::size_t PerComponents3 = PerComponents2 * PerComponent;
|
||||
static constexpr std::size_t PerComponents4 = PerComponents3 * PerComponent;
|
||||
static constexpr std::size_t PerFormat = PerComponents4 * 2;
|
||||
|
||||
static std::size_t CalculateIndex(Tegra::Texture::TextureFormat format, bool is_srgb,
|
||||
Tegra::Texture::ComponentType red_component,
|
||||
Tegra::Texture::ComponentType green_component,
|
||||
Tegra::Texture::ComponentType blue_component,
|
||||
Tegra::Texture::ComponentType alpha_component) noexcept;
|
||||
|
||||
void Set(Tegra::Texture::TextureFormat format, bool is_srgb,
|
||||
Tegra::Texture::ComponentType red_component,
|
||||
Tegra::Texture::ComponentType green_component,
|
||||
Tegra::Texture::ComponentType blue_component,
|
||||
Tegra::Texture::ComponentType alpha_component,
|
||||
VideoCore::Surface::PixelFormat pixel_format);
|
||||
|
||||
std::array<u8, NumTextureFormats * PerFormat> table;
|
||||
};
|
||||
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
|
||||
|
|
95
src/video_core/texture_cache/formatter.cpp
Normal file
95
src/video_core/texture_cache/formatter.cpp
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#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"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
std::string Name(const ImageBase& image) {
|
||||
const GPUVAddr gpu_addr = image.gpu_addr;
|
||||
const ImageInfo& info = image.info;
|
||||
const u32 width = info.size.width;
|
||||
const 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 (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, std::optional<ImageViewType> type) {
|
||||
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 (type.value_or(image_view.type)) {
|
||||
case ImageViewType::e1D:
|
||||
return fmt::format("ImageView 1D {}{}", width, level);
|
||||
case ImageViewType::e2D:
|
||||
return fmt::format("ImageView 2D {}x{}{}", width, height, level);
|
||||
case ImageViewType::Cube:
|
||||
return fmt::format("ImageView Cube {}x{}{}", width, height, level);
|
||||
case ImageViewType::e3D:
|
||||
return fmt::format("ImageView 3D {}x{}x{}{}", width, height, depth, level);
|
||||
case ImageViewType::e1DArray:
|
||||
return fmt::format("ImageView 1DArray {}{}|{}", width, level, num_layers);
|
||||
case ImageViewType::e2DArray:
|
||||
return fmt::format("ImageView 2DArray {}x{}{}|{}", width, height, level, num_layers);
|
||||
case ImageViewType::CubeArray:
|
||||
return fmt::format("ImageView CubeArray {}x{}{}|{}", width, height, level, num_layers);
|
||||
case ImageViewType::Rect:
|
||||
return fmt::format("ImageView Rect {}x{}{}", width, height, level);
|
||||
case ImageViewType::Buffer:
|
||||
return fmt::format("BufferView {}", 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
|
263
src/video_core/texture_cache/formatter.h
Normal file
263
src/video_core/texture_cache/formatter.h
Normal file
|
@ -0,0 +1,263 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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::A1B5G5R5_UNORM:
|
||||
return "A1B5G5R5_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::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_10X10_UNORM:
|
||||
return "ASTC_2D_10X10_UNORM";
|
||||
case PixelFormat::ASTC_2D_10X10_SRGB:
|
||||
return "ASTC_2D_10X10_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::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,
|
||||
std::optional<ImageViewType> type = std::nullopt);
|
||||
|
||||
[[nodiscard]] std::string Name(const RenderTargets& render_targets);
|
||||
|
||||
} // namespace VideoCommon
|
216
src/video_core/texture_cache/image_base.cpp
Normal file
216
src/video_core/texture_cache/image_base.cpp
Normal file
|
@ -0,0 +1,216 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.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)}, 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);
|
||||
}
|
||||
}
|
||||
|
||||
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, 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);
|
||||
}
|
||||
|
||||
void 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 {
|
||||
base = FindSubresource(rhs.info, lhs, rhs.gpu_addr, OPTIONS);
|
||||
}
|
||||
if (!base) {
|
||||
LOG_ERROR(HW_GPU, "Image alias should have been flipped");
|
||||
return;
|
||||
}
|
||||
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;
|
||||
if (is_lhs_compressed && is_rhs_compressed) {
|
||||
LOG_ERROR(HW_GPU, "Compressed to compressed image aliasing is not implemented");
|
||||
return;
|
||||
}
|
||||
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 /= lhs_block.width;
|
||||
lhs_size.height /= lhs_block.height;
|
||||
}
|
||||
if (is_rhs_compressed) {
|
||||
rhs_size.width /= rhs_block.width;
|
||||
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;
|
||||
}
|
||||
lhs.aliased_images.push_back(std::move(lhs_alias));
|
||||
rhs.aliased_images.push_back(std::move(rhs_alias));
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
83
src/video_core/texture_cache/image_base.h
Normal file
83
src/video_core/texture_cache/image_base.h
Normal file
|
@ -0,0 +1,83 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(ImageFlagBits)
|
||||
|
||||
struct ImageViewInfo;
|
||||
|
||||
struct AliasedImage {
|
||||
std::vector<ImageCopy> copies;
|
||||
ImageId id;
|
||||
};
|
||||
|
||||
struct ImageBase {
|
||||
explicit ImageBase(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
|
||||
|
||||
[[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 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;
|
||||
}
|
||||
|
||||
ImageInfo info;
|
||||
|
||||
u32 guest_size_bytes = 0;
|
||||
u32 unswizzled_size_bytes = 0;
|
||||
u32 converted_size_bytes = 0;
|
||||
ImageFlagBits flags = ImageFlagBits::CpuModified;
|
||||
|
||||
GPUVAddr gpu_addr = 0;
|
||||
VAddr cpu_addr = 0;
|
||||
VAddr cpu_addr_end = 0;
|
||||
|
||||
u64 modification_tick = 0;
|
||||
u64 frame_tick = 0;
|
||||
|
||||
std::array<u32, MAX_MIP_LEVELS> mip_level_offsets{};
|
||||
|
||||
std::vector<ImageViewInfo> image_view_infos;
|
||||
std::vector<ImageViewId> image_view_ids;
|
||||
|
||||
std::vector<u32> slice_offsets;
|
||||
std::vector<SubresourceBase> slice_subresources;
|
||||
|
||||
std::vector<AliasedImage> aliased_images;
|
||||
};
|
||||
|
||||
struct ImageAllocBase {
|
||||
std::vector<ImageId> images;
|
||||
};
|
||||
|
||||
void AddImageAlias(ImageBase& lhs, ImageBase& rhs, ImageId lhs_id, ImageId rhs_id);
|
||||
|
||||
} // namespace VideoCommon
|
189
src/video_core/texture_cache/image_info.cpp
Normal file
189
src/video_core/texture_cache/image_info.cpp
Normal file
|
@ -0,0 +1,189 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/assert.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::Texture::TextureType;
|
||||
using Tegra::Texture::TICEntry;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
ImageInfo::ImageInfo(const TICEntry& config) noexcept {
|
||||
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,
|
||||
};
|
||||
}
|
||||
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();
|
||||
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;
|
||||
size.width = config.Width();
|
||||
size.height = config.Height();
|
||||
resources.layers = config.BaseLayer() + 1;
|
||||
break;
|
||||
case TextureType::Texture2DArray:
|
||||
type = ImageType::e2D;
|
||||
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();
|
||||
break;
|
||||
case TextureType::Texture1DBuffer:
|
||||
type = ImageType::Buffer;
|
||||
size.width = config.Width();
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE_MSG("Invalid texture_type={}", static_cast<int>(config.texture_type.Value()));
|
||||
break;
|
||||
}
|
||||
if (type != ImageType::Linear) {
|
||||
// FIXME: Call this without passing *this
|
||||
layer_stride = CalculateLayerStride(*this);
|
||||
maybe_unaligned_layer_stride = CalculateLayerSize(*this);
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept {
|
||||
const auto& rt = regs.rt[index];
|
||||
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(rt.format);
|
||||
if (rt.tile_mode.is_pitch_linear) {
|
||||
ASSERT(rt.tile_mode.is_3d == 0);
|
||||
type = ImageType::Linear;
|
||||
pitch = rt.width;
|
||||
size = Extent3D{
|
||||
.width = pitch / BytesPerBlock(format),
|
||||
.height = rt.height,
|
||||
.depth = 1,
|
||||
};
|
||||
return;
|
||||
}
|
||||
size.width = rt.width;
|
||||
size.height = rt.height;
|
||||
layer_stride = rt.layer_stride * 4;
|
||||
maybe_unaligned_layer_stride = layer_stride;
|
||||
num_samples = NumSamples(regs.multisample_mode);
|
||||
block = Extent3D{
|
||||
.width = rt.tile_mode.block_width,
|
||||
.height = rt.tile_mode.block_height,
|
||||
.depth = rt.tile_mode.block_depth,
|
||||
};
|
||||
if (rt.tile_mode.is_3d) {
|
||||
type = ImageType::e3D;
|
||||
size.depth = rt.depth;
|
||||
} else {
|
||||
type = ImageType::e2D;
|
||||
resources.layers = rt.depth;
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept {
|
||||
format = VideoCore::Surface::PixelFormatFromDepthFormat(regs.zeta.format);
|
||||
size.width = regs.zeta_width;
|
||||
size.height = regs.zeta_height;
|
||||
resources.levels = 1;
|
||||
layer_stride = regs.zeta.layer_stride * 4;
|
||||
maybe_unaligned_layer_stride = layer_stride;
|
||||
num_samples = NumSamples(regs.multisample_mode);
|
||||
block = Extent3D{
|
||||
.width = regs.zeta.tile_mode.block_width,
|
||||
.height = regs.zeta.tile_mode.block_height,
|
||||
.depth = regs.zeta.tile_mode.block_depth,
|
||||
};
|
||||
if (regs.zeta.tile_mode.is_pitch_linear) {
|
||||
ASSERT(regs.zeta.tile_mode.is_3d == 0);
|
||||
type = ImageType::Linear;
|
||||
pitch = size.width * BytesPerBlock(format);
|
||||
} else if (regs.zeta.tile_mode.is_3d) {
|
||||
ASSERT(regs.zeta.tile_mode.is_pitch_linear == 0);
|
||||
type = ImageType::e3D;
|
||||
size.depth = regs.zeta_depth;
|
||||
} else {
|
||||
type = ImageType::e2D;
|
||||
resources.layers = regs.zeta_depth;
|
||||
}
|
||||
}
|
||||
|
||||
ImageInfo::ImageInfo(const Tegra::Engines::Fermi2D::Surface& config) noexcept {
|
||||
UNIMPLEMENTED_IF_MSG(config.layer != 0, "Surface layer is not zero");
|
||||
format = VideoCore::Surface::PixelFormatFromRenderTargetFormat(config.format);
|
||||
if (config.linear == Tegra::Engines::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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
38
src/video_core/texture_cache/image_info.h
Normal file
38
src/video_core/texture_cache/image_info.h
Normal file
|
@ -0,0 +1,38 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.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 {
|
||||
explicit ImageInfo() = default;
|
||||
explicit ImageInfo(const TICEntry& config) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs, size_t index) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Maxwell3D::Regs& regs) noexcept;
|
||||
explicit ImageInfo(const Tegra::Engines::Fermi2D::Surface& 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;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
41
src/video_core/texture_cache/image_view_base.cpp
Normal file
41
src/video_core/texture_cache/image_view_base.cpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "core/settings.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_)
|
||||
: image_id{image_id_}, 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),
|
||||
"Image view format {} is incompatible with image format {}", info.format,
|
||||
image_info.format);
|
||||
const bool is_async = Settings::values.use_asynchronous_gpu_emulation.GetValue();
|
||||
if (image_info.type == ImageType::Linear && is_async) {
|
||||
flags |= ImageViewFlagBits::PreemtiveDownload;
|
||||
}
|
||||
if (image_info.type == ImageType::e3D && info.type != ImageViewType::e3D) {
|
||||
flags |= ImageViewFlagBits::Slice;
|
||||
}
|
||||
}
|
||||
|
||||
ImageViewBase::ImageViewBase(const NullImageParams&) {}
|
||||
|
||||
} // namespace VideoCommon
|
47
src/video_core/texture_cache/image_view_base.h
Normal file
47
src/video_core/texture_cache/image_view_base.h
Normal file
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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 NullImageParams {};
|
||||
|
||||
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);
|
||||
explicit ImageViewBase(const NullImageParams&);
|
||||
|
||||
[[nodiscard]] bool IsBuffer() const noexcept {
|
||||
return type == ImageViewType::Buffer;
|
||||
}
|
||||
|
||||
ImageId image_id{};
|
||||
PixelFormat format{};
|
||||
ImageViewType type{};
|
||||
SubresourceRange range;
|
||||
Extent3D size{0, 0, 0};
|
||||
ImageViewFlagBits flags{};
|
||||
|
||||
u64 invalidation_tick = 0;
|
||||
u64 modification_tick = 0;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
88
src/video_core/texture_cache/image_view_info.cpp
Normal file
88
src/video_core/texture_cache/image_view_info.cpp
Normal file
|
@ -0,0 +1,88 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "video_core/texture_cache/image_view_info.h"
|
||||
#include "video_core/texture_cache/texture_cache.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
namespace {
|
||||
|
||||
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:
|
||||
UNREACHABLE_MSG("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
|
50
src/video_core/texture_cache/image_view_info.h
Normal file
50
src/video_core/texture_cache/image_view_info.h
Normal file
|
@ -0,0 +1,50 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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
|
51
src/video_core/texture_cache/render_targets.h
Normal file
51
src/video_core/texture_cache/render_targets.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <algorithm>
|
||||
#include <span>
|
||||
#include <utility>
|
||||
|
||||
#include "common/bit_cast.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
/// Framebuffer properties used to lookup a framebuffer
|
||||
struct RenderTargets {
|
||||
constexpr auto 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;
|
||||
};
|
||||
|
||||
} // 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
|
55
src/video_core/texture_cache/samples_helper.h
Normal file
55
src/video_core/texture_cache/samples_helper.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#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};
|
||||
}
|
||||
UNREACHABLE_MSG("Invalid number of samples={}", num_samples);
|
||||
return {1, 1};
|
||||
}
|
||||
|
||||
[[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;
|
||||
}
|
||||
UNREACHABLE_MSG("Invalid MSAA mode={}", static_cast<int>(msaa_mode));
|
||||
return 1;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
156
src/video_core/texture_cache/slot_vector.h
Normal file
156
src/video_core/texture_cache/slot_vector.h
Normal file
|
@ -0,0 +1,156 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <concepts>
|
||||
#include <numeric>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
struct SlotId {
|
||||
static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
|
||||
|
||||
constexpr auto operator<=>(const SlotId&) const noexcept = default;
|
||||
|
||||
constexpr explicit operator bool() const noexcept {
|
||||
return index != INVALID_INDEX;
|
||||
}
|
||||
|
||||
u32 index = INVALID_INDEX;
|
||||
};
|
||||
|
||||
template <class T>
|
||||
requires std::is_nothrow_move_assignable_v<T>&&
|
||||
std::is_nothrow_move_constructible_v<T> class SlotVector {
|
||||
public:
|
||||
~SlotVector() noexcept {
|
||||
size_t index = 0;
|
||||
for (u64 bits : stored_bitset) {
|
||||
for (size_t bit = 0; bits; ++bit, bits >>= 1) {
|
||||
if ((bits & 1) != 0) {
|
||||
values[index + bit].object.~T();
|
||||
}
|
||||
}
|
||||
index += 64;
|
||||
}
|
||||
delete[] values;
|
||||
}
|
||||
|
||||
[[nodiscard]] T& operator[](SlotId id) noexcept {
|
||||
ValidateIndex(id);
|
||||
return values[id.index].object;
|
||||
}
|
||||
|
||||
[[nodiscard]] const T& operator[](SlotId id) const noexcept {
|
||||
ValidateIndex(id);
|
||||
return values[id.index].object;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
[[nodiscard]] SlotId insert(Args&&... args) noexcept {
|
||||
const u32 index = FreeValueIndex();
|
||||
new (&values[index].object) T(std::forward<Args>(args)...);
|
||||
SetStorageBit(index);
|
||||
|
||||
return SlotId{index};
|
||||
}
|
||||
|
||||
void erase(SlotId id) noexcept {
|
||||
values[id.index].object.~T();
|
||||
free_list.push_back(id.index);
|
||||
ResetStorageBit(id.index);
|
||||
}
|
||||
|
||||
private:
|
||||
struct NonTrivialDummy {
|
||||
NonTrivialDummy() noexcept {}
|
||||
};
|
||||
|
||||
union Entry {
|
||||
Entry() noexcept : dummy{} {}
|
||||
~Entry() noexcept {}
|
||||
|
||||
NonTrivialDummy dummy;
|
||||
T object;
|
||||
};
|
||||
|
||||
void SetStorageBit(u32 index) noexcept {
|
||||
stored_bitset[index / 64] |= u64(1) << (index % 64);
|
||||
}
|
||||
|
||||
void ResetStorageBit(u32 index) noexcept {
|
||||
stored_bitset[index / 64] &= ~(u64(1) << (index % 64));
|
||||
}
|
||||
|
||||
bool ReadStorageBit(u32 index) noexcept {
|
||||
return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0;
|
||||
}
|
||||
|
||||
void ValidateIndex(SlotId id) const noexcept {
|
||||
DEBUG_ASSERT(id);
|
||||
DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
|
||||
DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
|
||||
}
|
||||
|
||||
[[nodiscard]] u32 FreeValueIndex() noexcept {
|
||||
if (free_list.empty()) {
|
||||
Reserve(values_capacity ? (values_capacity << 1) : 1);
|
||||
}
|
||||
const u32 free_index = free_list.back();
|
||||
free_list.pop_back();
|
||||
return free_index;
|
||||
}
|
||||
|
||||
void Reserve(size_t new_capacity) noexcept {
|
||||
Entry* const new_values = new Entry[new_capacity];
|
||||
size_t index = 0;
|
||||
for (u64 bits : stored_bitset) {
|
||||
for (size_t bit = 0; bits; ++bit, bits >>= 1) {
|
||||
const size_t i = index + bit;
|
||||
if ((bits & 1) == 0) {
|
||||
continue;
|
||||
}
|
||||
T& old_value = values[i].object;
|
||||
new (&new_values[i].object) T(std::move(old_value));
|
||||
old_value.~T();
|
||||
}
|
||||
index += 64;
|
||||
}
|
||||
|
||||
stored_bitset.resize((new_capacity + 63) / 64);
|
||||
|
||||
const size_t old_free_size = free_list.size();
|
||||
free_list.resize(old_free_size + (new_capacity - values_capacity));
|
||||
std::iota(free_list.begin() + old_free_size, free_list.end(),
|
||||
static_cast<u32>(values_capacity));
|
||||
|
||||
delete[] values;
|
||||
values = new_values;
|
||||
values_capacity = new_capacity;
|
||||
}
|
||||
|
||||
Entry* values = nullptr;
|
||||
size_t values_capacity = 0;
|
||||
size_t values_size = 0;
|
||||
|
||||
std::vector<u64> stored_bitset;
|
||||
std::vector<u32> free_list;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
template <>
|
||||
struct std::hash<VideoCommon::SlotId> {
|
||||
size_t operator()(const VideoCommon::SlotId& id) const noexcept {
|
||||
return std::hash<u32>{}(id.index);
|
||||
}
|
||||
};
|
|
@ -1,299 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/algorithm.h"
|
||||
#include "common/assert.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/microprofile.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/texture_cache/surface_base.h"
|
||||
#include "video_core/texture_cache/surface_params.h"
|
||||
#include "video_core/textures/convert.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
MICROPROFILE_DEFINE(GPU_Load_Texture, "GPU", "Texture Load", MP_RGB(128, 192, 128));
|
||||
MICROPROFILE_DEFINE(GPU_Flush_Texture, "GPU", "Texture Flush", MP_RGB(128, 192, 128));
|
||||
|
||||
using Tegra::Texture::ConvertFromGuestToHost;
|
||||
using VideoCore::MortonSwizzleMode;
|
||||
using VideoCore::Surface::IsPixelFormatASTC;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
StagingCache::StagingCache() = default;
|
||||
|
||||
StagingCache::~StagingCache() = default;
|
||||
|
||||
SurfaceBaseImpl::SurfaceBaseImpl(GPUVAddr gpu_addr_, const SurfaceParams& params_,
|
||||
bool is_astc_supported_)
|
||||
: params{params_}, gpu_addr{gpu_addr_}, mipmap_sizes(params_.num_levels),
|
||||
mipmap_offsets(params.num_levels) {
|
||||
is_converted = IsPixelFormatASTC(params.pixel_format) && !is_astc_supported_;
|
||||
host_memory_size = params.GetHostSizeInBytes(is_converted);
|
||||
|
||||
std::size_t offset = 0;
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t mipmap_size{params.GetGuestMipmapSize(level)};
|
||||
mipmap_sizes[level] = mipmap_size;
|
||||
mipmap_offsets[level] = offset;
|
||||
offset += mipmap_size;
|
||||
}
|
||||
layer_size = offset;
|
||||
if (params.is_layered) {
|
||||
if (params.is_tiled) {
|
||||
layer_size =
|
||||
SurfaceParams::AlignLayered(layer_size, params.block_height, params.block_depth);
|
||||
}
|
||||
guest_memory_size = layer_size * params.depth;
|
||||
} else {
|
||||
guest_memory_size = layer_size;
|
||||
}
|
||||
}
|
||||
|
||||
MatchTopologyResult SurfaceBaseImpl::MatchesTopology(const SurfaceParams& rhs) const {
|
||||
const u32 src_bpp{params.GetBytesPerPixel()};
|
||||
const u32 dst_bpp{rhs.GetBytesPerPixel()};
|
||||
const bool ib1 = params.IsBuffer();
|
||||
const bool ib2 = rhs.IsBuffer();
|
||||
if (std::tie(src_bpp, params.is_tiled, ib1) == std::tie(dst_bpp, rhs.is_tiled, ib2)) {
|
||||
const bool cb1 = params.IsCompressed();
|
||||
const bool cb2 = rhs.IsCompressed();
|
||||
if (cb1 == cb2) {
|
||||
return MatchTopologyResult::FullMatch;
|
||||
}
|
||||
return MatchTopologyResult::CompressUnmatch;
|
||||
}
|
||||
return MatchTopologyResult::None;
|
||||
}
|
||||
|
||||
MatchStructureResult SurfaceBaseImpl::MatchesStructure(const SurfaceParams& rhs) const {
|
||||
// Buffer surface Check
|
||||
if (params.IsBuffer()) {
|
||||
const std::size_t wd1 = params.width * params.GetBytesPerPixel();
|
||||
const std::size_t wd2 = rhs.width * rhs.GetBytesPerPixel();
|
||||
if (wd1 == wd2) {
|
||||
return MatchStructureResult::FullMatch;
|
||||
}
|
||||
return MatchStructureResult::None;
|
||||
}
|
||||
|
||||
// Linear Surface check
|
||||
if (!params.is_tiled) {
|
||||
if (std::tie(params.height, params.pitch) == std::tie(rhs.height, rhs.pitch)) {
|
||||
if (params.width == rhs.width) {
|
||||
return MatchStructureResult::FullMatch;
|
||||
} else {
|
||||
return MatchStructureResult::SemiMatch;
|
||||
}
|
||||
}
|
||||
return MatchStructureResult::None;
|
||||
}
|
||||
|
||||
// Tiled Surface check
|
||||
if (std::tie(params.depth, params.block_width, params.block_height, params.block_depth,
|
||||
params.tile_width_spacing, params.num_levels) ==
|
||||
std::tie(rhs.depth, rhs.block_width, rhs.block_height, rhs.block_depth,
|
||||
rhs.tile_width_spacing, rhs.num_levels)) {
|
||||
if (std::tie(params.width, params.height) == std::tie(rhs.width, rhs.height)) {
|
||||
return MatchStructureResult::FullMatch;
|
||||
}
|
||||
const u32 ws = SurfaceParams::ConvertWidth(rhs.GetBlockAlignedWidth(), params.pixel_format,
|
||||
rhs.pixel_format);
|
||||
const u32 hs =
|
||||
SurfaceParams::ConvertHeight(rhs.height, params.pixel_format, rhs.pixel_format);
|
||||
const u32 w1 = params.GetBlockAlignedWidth();
|
||||
if (std::tie(w1, params.height) == std::tie(ws, hs)) {
|
||||
return MatchStructureResult::SemiMatch;
|
||||
}
|
||||
}
|
||||
return MatchStructureResult::None;
|
||||
}
|
||||
|
||||
std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap(
|
||||
const GPUVAddr candidate_gpu_addr) const {
|
||||
if (gpu_addr == candidate_gpu_addr) {
|
||||
return {{0, 0}};
|
||||
}
|
||||
|
||||
if (candidate_gpu_addr < gpu_addr) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)};
|
||||
const auto layer{static_cast<u32>(relative_address / layer_size)};
|
||||
if (layer >= params.depth) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const GPUVAddr mipmap_address = relative_address - layer_size * layer;
|
||||
const auto mipmap_it =
|
||||
Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
|
||||
if (mipmap_it == mipmap_offsets.end()) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))};
|
||||
return std::make_pair(layer, level);
|
||||
}
|
||||
|
||||
std::vector<CopyParams> SurfaceBaseImpl::BreakDownLayered(const SurfaceParams& in_params) const {
|
||||
const u32 layers{params.depth};
|
||||
const u32 mipmaps{params.num_levels};
|
||||
std::vector<CopyParams> result;
|
||||
result.reserve(static_cast<std::size_t>(layers) * static_cast<std::size_t>(mipmaps));
|
||||
|
||||
for (u32 layer = 0; layer < layers; layer++) {
|
||||
for (u32 level = 0; level < mipmaps; level++) {
|
||||
const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level);
|
||||
const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level);
|
||||
result.emplace_back(0, 0, layer, 0, 0, layer, level, level, width, height, 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<CopyParams> SurfaceBaseImpl::BreakDownNonLayered(const SurfaceParams& in_params) const {
|
||||
const u32 mipmaps{params.num_levels};
|
||||
std::vector<CopyParams> result;
|
||||
result.reserve(mipmaps);
|
||||
|
||||
for (u32 level = 0; level < mipmaps; level++) {
|
||||
const u32 width = SurfaceParams::IntersectWidth(params, in_params, level, level);
|
||||
const u32 height = SurfaceParams::IntersectHeight(params, in_params, level, level);
|
||||
const u32 depth{std::min(params.GetMipDepth(level), in_params.GetMipDepth(level))};
|
||||
result.emplace_back(width, height, depth, level);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SurfaceBaseImpl::SwizzleFunc(MortonSwizzleMode mode, u8* memory,
|
||||
const SurfaceParams& surface_params, u8* buffer, u32 level) {
|
||||
const u32 width{surface_params.GetMipWidth(level)};
|
||||
const u32 height{surface_params.GetMipHeight(level)};
|
||||
const u32 block_height{surface_params.GetMipBlockHeight(level)};
|
||||
const u32 block_depth{surface_params.GetMipBlockDepth(level)};
|
||||
|
||||
std::size_t guest_offset{mipmap_offsets[level]};
|
||||
if (surface_params.is_layered) {
|
||||
std::size_t host_offset = 0;
|
||||
const std::size_t guest_stride = layer_size;
|
||||
const std::size_t host_stride = surface_params.GetHostLayerSize(level);
|
||||
for (u32 layer = 0; layer < surface_params.depth; ++layer) {
|
||||
MortonSwizzle(mode, surface_params.pixel_format, width, block_height, height,
|
||||
block_depth, 1, surface_params.tile_width_spacing, buffer + host_offset,
|
||||
memory + guest_offset);
|
||||
guest_offset += guest_stride;
|
||||
host_offset += host_stride;
|
||||
}
|
||||
} else {
|
||||
MortonSwizzle(mode, surface_params.pixel_format, width, block_height, height, block_depth,
|
||||
surface_params.GetMipDepth(level), surface_params.tile_width_spacing, buffer,
|
||||
memory + guest_offset);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
|
||||
StagingCache& staging_cache) {
|
||||
MICROPROFILE_SCOPE(GPU_Load_Texture);
|
||||
auto& staging_buffer = staging_cache.GetBuffer(0);
|
||||
u8* host_ptr;
|
||||
// Use an extra temporal buffer
|
||||
auto& tmp_buffer = staging_cache.GetBuffer(1);
|
||||
tmp_buffer.resize(guest_memory_size);
|
||||
host_ptr = tmp_buffer.data();
|
||||
memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
|
||||
|
||||
if (params.is_tiled) {
|
||||
ASSERT_MSG(params.block_width == 0, "Block width is defined as {} on texture target {}",
|
||||
params.block_width, static_cast<u32>(params.target));
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)};
|
||||
SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params,
|
||||
staging_buffer.data() + host_offset, level);
|
||||
}
|
||||
} else {
|
||||
ASSERT_MSG(params.num_levels == 1, "Linear mipmap loading is not implemented");
|
||||
const u32 bpp{params.GetBytesPerPixel()};
|
||||
const u32 block_width{params.GetDefaultBlockWidth()};
|
||||
const u32 block_height{params.GetDefaultBlockHeight()};
|
||||
const u32 width{(params.width + block_width - 1) / block_width};
|
||||
const u32 height{(params.height + block_height - 1) / block_height};
|
||||
const u32 copy_size{width * bpp};
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes(false));
|
||||
} else {
|
||||
const u8* start{host_ptr};
|
||||
u8* write_to{staging_buffer.data()};
|
||||
for (u32 h = height; h > 0; --h) {
|
||||
std::memcpy(write_to, start, copy_size);
|
||||
start += params.pitch;
|
||||
write_to += copy_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_converted && params.pixel_format != PixelFormat::S8_UINT_D24_UNORM) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (u32 level = params.num_levels; level--;) {
|
||||
const std::size_t in_host_offset{params.GetHostMipmapLevelOffset(level, false)};
|
||||
const std::size_t out_host_offset{params.GetHostMipmapLevelOffset(level, is_converted)};
|
||||
u8* const in_buffer = staging_buffer.data() + in_host_offset;
|
||||
u8* const out_buffer = staging_buffer.data() + out_host_offset;
|
||||
ConvertFromGuestToHost(in_buffer, out_buffer, params.pixel_format,
|
||||
params.GetMipWidth(level), params.GetMipHeight(level),
|
||||
params.GetMipDepth(level), true, true);
|
||||
}
|
||||
}
|
||||
|
||||
void SurfaceBaseImpl::FlushBuffer(Tegra::MemoryManager& memory_manager,
|
||||
StagingCache& staging_cache) {
|
||||
MICROPROFILE_SCOPE(GPU_Flush_Texture);
|
||||
auto& staging_buffer = staging_cache.GetBuffer(0);
|
||||
u8* host_ptr;
|
||||
|
||||
// Use an extra temporal buffer
|
||||
auto& tmp_buffer = staging_cache.GetBuffer(1);
|
||||
tmp_buffer.resize(guest_memory_size);
|
||||
host_ptr = tmp_buffer.data();
|
||||
|
||||
if (params.target == SurfaceTarget::Texture3D) {
|
||||
// Special case for 3D texture segments
|
||||
memory_manager.ReadBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
|
||||
}
|
||||
|
||||
if (params.is_tiled) {
|
||||
ASSERT_MSG(params.block_width == 0, "Block width is defined as {}", params.block_width);
|
||||
for (u32 level = 0; level < params.num_levels; ++level) {
|
||||
const std::size_t host_offset{params.GetHostMipmapLevelOffset(level, false)};
|
||||
SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params,
|
||||
staging_buffer.data() + host_offset, level);
|
||||
}
|
||||
} else if (params.IsBuffer()) {
|
||||
// Buffers don't have pitch or any fancy layout property. We can just memcpy them to guest
|
||||
// memory.
|
||||
std::memcpy(host_ptr, staging_buffer.data(), guest_memory_size);
|
||||
} else {
|
||||
ASSERT(params.target == SurfaceTarget::Texture2D);
|
||||
ASSERT(params.num_levels == 1);
|
||||
|
||||
const u32 bpp{params.GetBytesPerPixel()};
|
||||
const u32 copy_size{params.width * bpp};
|
||||
if (params.pitch == copy_size) {
|
||||
std::memcpy(host_ptr, staging_buffer.data(), guest_memory_size);
|
||||
} else {
|
||||
u8* start{host_ptr};
|
||||
const u8* read_to{staging_buffer.data()};
|
||||
for (u32 h = params.height; h > 0; --h) {
|
||||
std::memcpy(start, read_to, copy_size);
|
||||
start += params.pitch;
|
||||
read_to += copy_size;
|
||||
}
|
||||
}
|
||||
}
|
||||
memory_manager.WriteBlockUnsafe(gpu_addr, host_ptr, guest_memory_size);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
|
@ -1,333 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/gpu.h"
|
||||
#include "video_core/morton.h"
|
||||
#include "video_core/texture_cache/copy_params.h"
|
||||
#include "video_core/texture_cache/surface_params.h"
|
||||
#include "video_core/texture_cache/surface_view.h"
|
||||
|
||||
namespace Tegra {
|
||||
class MemoryManager;
|
||||
}
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using VideoCore::MortonSwizzleMode;
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
|
||||
enum class MatchStructureResult : u32 {
|
||||
FullMatch = 0,
|
||||
SemiMatch = 1,
|
||||
None = 2,
|
||||
};
|
||||
|
||||
enum class MatchTopologyResult : u32 {
|
||||
FullMatch = 0,
|
||||
CompressUnmatch = 1,
|
||||
None = 2,
|
||||
};
|
||||
|
||||
class StagingCache {
|
||||
public:
|
||||
explicit StagingCache();
|
||||
~StagingCache();
|
||||
|
||||
std::vector<u8>& GetBuffer(std::size_t index) {
|
||||
return staging_buffer[index];
|
||||
}
|
||||
|
||||
const std::vector<u8>& GetBuffer(std::size_t index) const {
|
||||
return staging_buffer[index];
|
||||
}
|
||||
|
||||
void SetSize(std::size_t size) {
|
||||
staging_buffer.resize(size);
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::vector<u8>> staging_buffer;
|
||||
};
|
||||
|
||||
class SurfaceBaseImpl {
|
||||
public:
|
||||
void LoadBuffer(Tegra::MemoryManager& memory_manager, StagingCache& staging_cache);
|
||||
|
||||
void FlushBuffer(Tegra::MemoryManager& memory_manager, StagingCache& staging_cache);
|
||||
|
||||
GPUVAddr GetGpuAddr() const {
|
||||
return gpu_addr;
|
||||
}
|
||||
|
||||
bool Overlaps(const VAddr start, const VAddr end) const {
|
||||
return (cpu_addr < end) && (cpu_addr_end > start);
|
||||
}
|
||||
|
||||
bool IsInside(const GPUVAddr other_start, const GPUVAddr other_end) const {
|
||||
const GPUVAddr gpu_addr_end = gpu_addr + guest_memory_size;
|
||||
return gpu_addr <= other_start && other_end <= gpu_addr_end;
|
||||
}
|
||||
|
||||
// Use only when recycling a surface
|
||||
void SetGpuAddr(const GPUVAddr new_addr) {
|
||||
gpu_addr = new_addr;
|
||||
}
|
||||
|
||||
VAddr GetCpuAddr() const {
|
||||
return cpu_addr;
|
||||
}
|
||||
|
||||
VAddr GetCpuAddrEnd() const {
|
||||
return cpu_addr_end;
|
||||
}
|
||||
|
||||
void SetCpuAddr(const VAddr new_addr) {
|
||||
cpu_addr = new_addr;
|
||||
cpu_addr_end = new_addr + guest_memory_size;
|
||||
}
|
||||
|
||||
const SurfaceParams& GetSurfaceParams() const {
|
||||
return params;
|
||||
}
|
||||
|
||||
std::size_t GetSizeInBytes() const {
|
||||
return guest_memory_size;
|
||||
}
|
||||
|
||||
std::size_t GetHostSizeInBytes() const {
|
||||
return host_memory_size;
|
||||
}
|
||||
|
||||
std::size_t GetMipmapSize(const u32 level) const {
|
||||
return mipmap_sizes[level];
|
||||
}
|
||||
|
||||
bool IsLinear() const {
|
||||
return !params.is_tiled;
|
||||
}
|
||||
|
||||
bool IsConverted() const {
|
||||
return is_converted;
|
||||
}
|
||||
|
||||
bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const {
|
||||
return params.pixel_format == pixel_format;
|
||||
}
|
||||
|
||||
VideoCore::Surface::PixelFormat GetFormat() const {
|
||||
return params.pixel_format;
|
||||
}
|
||||
|
||||
bool MatchTarget(VideoCore::Surface::SurfaceTarget target) const {
|
||||
return params.target == target;
|
||||
}
|
||||
|
||||
MatchTopologyResult MatchesTopology(const SurfaceParams& rhs) const;
|
||||
|
||||
MatchStructureResult MatchesStructure(const SurfaceParams& rhs) const;
|
||||
|
||||
bool MatchesSubTexture(const SurfaceParams& rhs, const GPUVAddr other_gpu_addr) const {
|
||||
return std::tie(gpu_addr, params.target, params.num_levels) ==
|
||||
std::tie(other_gpu_addr, rhs.target, rhs.num_levels) &&
|
||||
params.target == SurfaceTarget::Texture2D && params.num_levels == 1;
|
||||
}
|
||||
|
||||
std::optional<std::pair<u32, u32>> GetLayerMipmap(const GPUVAddr candidate_gpu_addr) const;
|
||||
|
||||
std::vector<CopyParams> BreakDown(const SurfaceParams& in_params) const {
|
||||
return params.is_layered ? BreakDownLayered(in_params) : BreakDownNonLayered(in_params);
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBaseImpl(GPUVAddr gpu_addr_, const SurfaceParams& params_,
|
||||
bool is_astc_supported_);
|
||||
~SurfaceBaseImpl() = default;
|
||||
|
||||
virtual void DecorateSurfaceName() = 0;
|
||||
|
||||
const SurfaceParams params;
|
||||
std::size_t layer_size;
|
||||
std::size_t guest_memory_size;
|
||||
std::size_t host_memory_size;
|
||||
GPUVAddr gpu_addr{};
|
||||
VAddr cpu_addr{};
|
||||
VAddr cpu_addr_end{};
|
||||
bool is_converted{};
|
||||
|
||||
std::vector<std::size_t> mipmap_sizes;
|
||||
std::vector<std::size_t> mipmap_offsets;
|
||||
|
||||
private:
|
||||
void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& surface_params,
|
||||
u8* buffer, u32 level);
|
||||
|
||||
std::vector<CopyParams> BreakDownLayered(const SurfaceParams& in_params) const;
|
||||
|
||||
std::vector<CopyParams> BreakDownNonLayered(const SurfaceParams& in_params) const;
|
||||
};
|
||||
|
||||
template <typename TView>
|
||||
class SurfaceBase : public SurfaceBaseImpl {
|
||||
public:
|
||||
virtual void UploadTexture(const std::vector<u8>& staging_buffer) = 0;
|
||||
|
||||
virtual void DownloadTexture(std::vector<u8>& staging_buffer) = 0;
|
||||
|
||||
void MarkAsModified(bool is_modified_, u64 tick) {
|
||||
is_modified = is_modified_ || is_target;
|
||||
modification_tick = tick;
|
||||
}
|
||||
|
||||
void MarkAsRenderTarget(bool is_target_, u32 index_) {
|
||||
is_target = is_target_;
|
||||
index = index_;
|
||||
}
|
||||
|
||||
void SetMemoryMarked(bool is_memory_marked_) {
|
||||
is_memory_marked = is_memory_marked_;
|
||||
}
|
||||
|
||||
bool IsMemoryMarked() const {
|
||||
return is_memory_marked;
|
||||
}
|
||||
|
||||
void SetSyncPending(bool is_sync_pending_) {
|
||||
is_sync_pending = is_sync_pending_;
|
||||
}
|
||||
|
||||
bool IsSyncPending() const {
|
||||
return is_sync_pending;
|
||||
}
|
||||
|
||||
void MarkAsPicked(bool is_picked_) {
|
||||
is_picked = is_picked_;
|
||||
}
|
||||
|
||||
bool IsModified() const {
|
||||
return is_modified;
|
||||
}
|
||||
|
||||
bool IsProtected() const {
|
||||
// Only 3D slices are to be protected
|
||||
return is_target && params.target == SurfaceTarget::Texture3D;
|
||||
}
|
||||
|
||||
bool IsRenderTarget() const {
|
||||
return is_target;
|
||||
}
|
||||
|
||||
u32 GetRenderTarget() const {
|
||||
return index;
|
||||
}
|
||||
|
||||
bool IsRegistered() const {
|
||||
return is_registered;
|
||||
}
|
||||
|
||||
bool IsPicked() const {
|
||||
return is_picked;
|
||||
}
|
||||
|
||||
void MarkAsRegistered(bool is_reg) {
|
||||
is_registered = is_reg;
|
||||
}
|
||||
|
||||
u64 GetModificationTick() const {
|
||||
return modification_tick;
|
||||
}
|
||||
|
||||
TView EmplaceOverview(const SurfaceParams& overview_params) {
|
||||
const u32 num_layers{(params.is_layered && !overview_params.is_layered) ? 1 : params.depth};
|
||||
return GetView(ViewParams(overview_params.target, 0, num_layers, 0, params.num_levels));
|
||||
}
|
||||
|
||||
TView Emplace3DView(u32 slice, u32 depth, u32 base_level, u32 num_levels) {
|
||||
return GetView(ViewParams(VideoCore::Surface::SurfaceTarget::Texture3D, slice, depth,
|
||||
base_level, num_levels));
|
||||
}
|
||||
|
||||
std::optional<TView> EmplaceIrregularView(const SurfaceParams& view_params,
|
||||
const GPUVAddr view_addr,
|
||||
const std::size_t candidate_size, const u32 mipmap,
|
||||
const u32 layer) {
|
||||
const auto layer_mipmap{GetLayerMipmap(view_addr + candidate_size)};
|
||||
if (!layer_mipmap) {
|
||||
return {};
|
||||
}
|
||||
const auto [end_layer, end_mipmap] = *layer_mipmap;
|
||||
if (layer != end_layer) {
|
||||
if (mipmap == 0 && end_mipmap == 0) {
|
||||
return GetView(ViewParams(view_params.target, layer, end_layer - layer, 0, 1));
|
||||
}
|
||||
return {};
|
||||
} else {
|
||||
return GetView(ViewParams(view_params.target, layer, 1, mipmap, end_mipmap - mipmap));
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr,
|
||||
const std::size_t candidate_size) {
|
||||
if (params.target == SurfaceTarget::Texture3D ||
|
||||
view_params.target == SurfaceTarget::Texture3D ||
|
||||
(params.num_levels == 1 && !params.is_layered)) {
|
||||
return {};
|
||||
}
|
||||
const auto layer_mipmap{GetLayerMipmap(view_addr)};
|
||||
if (!layer_mipmap) {
|
||||
return {};
|
||||
}
|
||||
const auto [layer, mipmap] = *layer_mipmap;
|
||||
if (GetMipmapSize(mipmap) != candidate_size) {
|
||||
return EmplaceIrregularView(view_params, view_addr, candidate_size, mipmap, layer);
|
||||
}
|
||||
return GetView(ViewParams(view_params.target, layer, 1, mipmap, 1));
|
||||
}
|
||||
|
||||
TView GetMainView() const {
|
||||
return main_view;
|
||||
}
|
||||
|
||||
protected:
|
||||
explicit SurfaceBase(const GPUVAddr gpu_addr_, const SurfaceParams& params_,
|
||||
bool is_astc_supported_)
|
||||
: SurfaceBaseImpl{gpu_addr_, params_, is_astc_supported_} {}
|
||||
|
||||
~SurfaceBase() = default;
|
||||
|
||||
virtual TView CreateView(const ViewParams& view_key) = 0;
|
||||
|
||||
TView main_view;
|
||||
std::unordered_map<ViewParams, TView> views;
|
||||
|
||||
private:
|
||||
TView GetView(const ViewParams& key) {
|
||||
const auto [entry, is_cache_miss] = views.try_emplace(key);
|
||||
auto& view{entry->second};
|
||||
if (is_cache_miss) {
|
||||
view = CreateView(key);
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
static constexpr u32 NO_RT = 0xFFFFFFFF;
|
||||
|
||||
bool is_modified{};
|
||||
bool is_target{};
|
||||
bool is_registered{};
|
||||
bool is_picked{};
|
||||
bool is_memory_marked{};
|
||||
bool is_sync_pending{};
|
||||
u32 index{NO_RT};
|
||||
u64 modification_tick{};
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
|
@ -1,445 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "core/core.h"
|
||||
#include "video_core/engines/shader_bytecode.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/format_lookup_table.h"
|
||||
#include "video_core/texture_cache/surface_params.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::PixelFormatFromDepthFormat;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
using VideoCore::Surface::SurfaceTarget;
|
||||
using VideoCore::Surface::SurfaceTargetFromTextureType;
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
namespace {
|
||||
|
||||
SurfaceTarget TextureTypeToSurfaceTarget(Tegra::Shader::TextureType type, bool is_array) {
|
||||
switch (type) {
|
||||
case Tegra::Shader::TextureType::Texture1D:
|
||||
return is_array ? SurfaceTarget::Texture1DArray : SurfaceTarget::Texture1D;
|
||||
case Tegra::Shader::TextureType::Texture2D:
|
||||
return is_array ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D;
|
||||
case Tegra::Shader::TextureType::Texture3D:
|
||||
ASSERT(!is_array);
|
||||
return SurfaceTarget::Texture3D;
|
||||
case Tegra::Shader::TextureType::TextureCube:
|
||||
return is_array ? SurfaceTarget::TextureCubeArray : SurfaceTarget::TextureCubemap;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return SurfaceTarget::Texture2D;
|
||||
}
|
||||
}
|
||||
|
||||
SurfaceTarget ImageTypeToSurfaceTarget(Tegra::Shader::ImageType type) {
|
||||
switch (type) {
|
||||
case Tegra::Shader::ImageType::Texture1D:
|
||||
return SurfaceTarget::Texture1D;
|
||||
case Tegra::Shader::ImageType::TextureBuffer:
|
||||
return SurfaceTarget::TextureBuffer;
|
||||
case Tegra::Shader::ImageType::Texture1DArray:
|
||||
return SurfaceTarget::Texture1DArray;
|
||||
case Tegra::Shader::ImageType::Texture2D:
|
||||
return SurfaceTarget::Texture2D;
|
||||
case Tegra::Shader::ImageType::Texture2DArray:
|
||||
return SurfaceTarget::Texture2DArray;
|
||||
case Tegra::Shader::ImageType::Texture3D:
|
||||
return SurfaceTarget::Texture3D;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return SurfaceTarget::Texture2D;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
|
||||
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForTexture(const FormatLookupTable& lookup_table,
|
||||
const Tegra::Texture::TICEntry& tic,
|
||||
const VideoCommon::Shader::Sampler& entry) {
|
||||
SurfaceParams params;
|
||||
params.is_tiled = tic.IsTiled();
|
||||
params.srgb_conversion = tic.IsSrgbConversionEnabled();
|
||||
params.block_width = params.is_tiled ? tic.BlockWidth() : 0;
|
||||
params.block_height = params.is_tiled ? tic.BlockHeight() : 0;
|
||||
params.block_depth = params.is_tiled ? tic.BlockDepth() : 0;
|
||||
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
|
||||
params.pixel_format = lookup_table.GetPixelFormat(
|
||||
tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
if (entry.is_shadow && params.type == SurfaceType::ColorTexture) {
|
||||
switch (params.pixel_format) {
|
||||
case PixelFormat::R16_UNORM:
|
||||
case PixelFormat::R16_FLOAT:
|
||||
params.pixel_format = PixelFormat::D16_UNORM;
|
||||
break;
|
||||
case PixelFormat::R32_FLOAT:
|
||||
params.pixel_format = PixelFormat::D32_FLOAT;
|
||||
break;
|
||||
default:
|
||||
UNIMPLEMENTED_MSG("Unimplemented shadow convert format: {}",
|
||||
static_cast<u32>(params.pixel_format));
|
||||
}
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
}
|
||||
// TODO: on 1DBuffer we should use the tic info.
|
||||
if (tic.IsBuffer()) {
|
||||
params.target = SurfaceTarget::TextureBuffer;
|
||||
params.width = tic.Width();
|
||||
params.pitch = params.width * params.GetBytesPerPixel();
|
||||
params.height = 1;
|
||||
params.depth = 1;
|
||||
params.num_levels = 1;
|
||||
params.emulated_levels = 1;
|
||||
params.is_layered = false;
|
||||
} else {
|
||||
params.target = TextureTypeToSurfaceTarget(entry.type, entry.is_array);
|
||||
params.width = tic.Width();
|
||||
params.height = tic.Height();
|
||||
params.depth = tic.Depth();
|
||||
params.pitch = params.is_tiled ? 0 : tic.Pitch();
|
||||
if (params.target == SurfaceTarget::TextureCubemap ||
|
||||
params.target == SurfaceTarget::TextureCubeArray) {
|
||||
params.depth *= 6;
|
||||
}
|
||||
params.num_levels = tic.max_mip_level + 1;
|
||||
params.emulated_levels = std::min(params.num_levels, params.MaxPossibleMipmap());
|
||||
params.is_layered = params.IsLayered();
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_table,
|
||||
const Tegra::Texture::TICEntry& tic,
|
||||
const VideoCommon::Shader::Image& entry) {
|
||||
SurfaceParams params;
|
||||
params.is_tiled = tic.IsTiled();
|
||||
params.srgb_conversion = tic.IsSrgbConversionEnabled();
|
||||
params.block_width = params.is_tiled ? tic.BlockWidth() : 0;
|
||||
params.block_height = params.is_tiled ? tic.BlockHeight() : 0;
|
||||
params.block_depth = params.is_tiled ? tic.BlockDepth() : 0;
|
||||
params.tile_width_spacing = params.is_tiled ? (1 << tic.tile_width_spacing.Value()) : 1;
|
||||
params.pixel_format = lookup_table.GetPixelFormat(
|
||||
tic.format, params.srgb_conversion, tic.r_type, tic.g_type, tic.b_type, tic.a_type);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
params.target = ImageTypeToSurfaceTarget(entry.type);
|
||||
// TODO: on 1DBuffer we should use the tic info.
|
||||
if (tic.IsBuffer()) {
|
||||
params.target = SurfaceTarget::TextureBuffer;
|
||||
params.width = tic.Width();
|
||||
params.pitch = params.width * params.GetBytesPerPixel();
|
||||
params.height = 1;
|
||||
params.depth = 1;
|
||||
params.num_levels = 1;
|
||||
params.emulated_levels = 1;
|
||||
params.is_layered = false;
|
||||
} else {
|
||||
params.width = tic.Width();
|
||||
params.height = tic.Height();
|
||||
params.depth = tic.Depth();
|
||||
params.pitch = params.is_tiled ? 0 : tic.Pitch();
|
||||
if (params.target == SurfaceTarget::TextureCubemap ||
|
||||
params.target == SurfaceTarget::TextureCubeArray) {
|
||||
params.depth *= 6;
|
||||
}
|
||||
params.num_levels = tic.max_mip_level + 1;
|
||||
params.emulated_levels = std::min(params.num_levels, params.MaxPossibleMipmap());
|
||||
params.is_layered = params.IsLayered();
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForDepthBuffer(Tegra::Engines::Maxwell3D& maxwell3d) {
|
||||
const auto& regs = maxwell3d.regs;
|
||||
const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
|
||||
const bool is_layered = regs.zeta_layers > 1 && block_depth == 0;
|
||||
const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
|
||||
return {
|
||||
.is_tiled = regs.zeta.memory_layout.type ==
|
||||
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear,
|
||||
.srgb_conversion = false,
|
||||
.is_layered = is_layered,
|
||||
.block_width = std::min(regs.zeta.memory_layout.block_width.Value(), 5U),
|
||||
.block_height = std::min(regs.zeta.memory_layout.block_height.Value(), 5U),
|
||||
.block_depth = block_depth,
|
||||
.tile_width_spacing = 1,
|
||||
.width = regs.zeta_width,
|
||||
.height = regs.zeta_height,
|
||||
.depth = is_layered ? regs.zeta_layers.Value() : 1U,
|
||||
.pitch = 0,
|
||||
.num_levels = 1,
|
||||
.emulated_levels = 1,
|
||||
.pixel_format = pixel_format,
|
||||
.type = GetFormatType(pixel_format),
|
||||
.target = is_layered ? SurfaceTarget::Texture2DArray : SurfaceTarget::Texture2D,
|
||||
};
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForFramebuffer(Tegra::Engines::Maxwell3D& maxwell3d,
|
||||
std::size_t index) {
|
||||
const auto& config{maxwell3d.regs.rt[index]};
|
||||
SurfaceParams params;
|
||||
params.is_tiled =
|
||||
config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
|
||||
params.srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
|
||||
config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB;
|
||||
params.block_width = config.memory_layout.block_width;
|
||||
params.block_height = config.memory_layout.block_height;
|
||||
params.block_depth = config.memory_layout.block_depth;
|
||||
params.tile_width_spacing = 1;
|
||||
params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
|
||||
params.type = GetFormatType(params.pixel_format);
|
||||
if (params.is_tiled) {
|
||||
params.pitch = 0;
|
||||
params.width = config.width;
|
||||
} else {
|
||||
const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
|
||||
params.pitch = config.width;
|
||||
params.width = params.pitch / bpp;
|
||||
}
|
||||
params.height = config.height;
|
||||
params.num_levels = 1;
|
||||
params.emulated_levels = 1;
|
||||
|
||||
if (config.memory_layout.is_3d != 0) {
|
||||
params.depth = config.layers.Value();
|
||||
params.is_layered = false;
|
||||
params.target = SurfaceTarget::Texture3D;
|
||||
} else if (config.layers > 1) {
|
||||
params.depth = config.layers.Value();
|
||||
params.is_layered = true;
|
||||
params.target = SurfaceTarget::Texture2DArray;
|
||||
} else {
|
||||
params.depth = 1;
|
||||
params.is_layered = false;
|
||||
params.target = SurfaceTarget::Texture2D;
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
SurfaceParams SurfaceParams::CreateForFermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config) {
|
||||
const bool is_tiled = !config.linear;
|
||||
const auto pixel_format = PixelFormatFromRenderTargetFormat(config.format);
|
||||
|
||||
SurfaceParams params{
|
||||
.is_tiled = is_tiled,
|
||||
.srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
|
||||
config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB,
|
||||
.is_layered = false,
|
||||
.block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U,
|
||||
.block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U,
|
||||
.block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U,
|
||||
.tile_width_spacing = 1,
|
||||
.width = config.width,
|
||||
.height = config.height,
|
||||
.depth = 1,
|
||||
.pitch = config.pitch,
|
||||
.num_levels = 1,
|
||||
.emulated_levels = 1,
|
||||
.pixel_format = pixel_format,
|
||||
.type = GetFormatType(pixel_format),
|
||||
// TODO(Rodrigo): Try to guess texture arrays from parameters
|
||||
.target = SurfaceTarget::Texture2D,
|
||||
};
|
||||
|
||||
params.is_layered = params.IsLayered();
|
||||
return params;
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget SurfaceParams::ExpectedTarget(
|
||||
const VideoCommon::Shader::Sampler& entry) {
|
||||
return TextureTypeToSurfaceTarget(entry.type, entry.is_array);
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget SurfaceParams::ExpectedTarget(
|
||||
const VideoCommon::Shader::Image& entry) {
|
||||
return ImageTypeToSurfaceTarget(entry.type);
|
||||
}
|
||||
|
||||
bool SurfaceParams::IsLayered() const {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Auto block resizing algorithm from:
|
||||
// https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
|
||||
u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
|
||||
if (level == 0) {
|
||||
return this->block_height;
|
||||
}
|
||||
|
||||
const u32 height_new{GetMipHeight(level)};
|
||||
const u32 default_block_height{GetDefaultBlockHeight()};
|
||||
const u32 blocks_in_y{(height_new + default_block_height - 1) / default_block_height};
|
||||
const u32 block_height_new = Common::Log2Ceil32(blocks_in_y);
|
||||
return std::clamp(block_height_new, 3U, 7U) - 3U;
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
|
||||
if (level == 0) {
|
||||
return this->block_depth;
|
||||
}
|
||||
if (is_layered) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
const u32 depth_new{GetMipDepth(level)};
|
||||
const u32 block_depth_new = Common::Log2Ceil32(depth_new);
|
||||
if (block_depth_new > 4) {
|
||||
return 5 - (GetMipBlockHeight(level) >= 2);
|
||||
}
|
||||
return block_depth_new;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
|
||||
std::size_t offset = 0;
|
||||
for (u32 i = 0; i < level; i++) {
|
||||
offset += GetInnerMipmapMemorySize(i, false, false);
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level, bool is_converted) const {
|
||||
std::size_t offset = 0;
|
||||
if (is_converted) {
|
||||
for (u32 i = 0; i < level; ++i) {
|
||||
offset += GetConvertedMipmapSize(i) * GetNumLayers();
|
||||
}
|
||||
} else {
|
||||
for (u32 i = 0; i < level; ++i) {
|
||||
offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers();
|
||||
}
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetConvertedMipmapSize(u32 level) const {
|
||||
constexpr std::size_t rgba8_bpp = 4ULL;
|
||||
const std::size_t mip_width = GetMipWidth(level);
|
||||
const std::size_t mip_height = GetMipHeight(level);
|
||||
const std::size_t mip_depth = is_layered ? 1 : GetMipDepth(level);
|
||||
return mip_width * mip_height * mip_depth * rgba8_bpp;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const {
|
||||
std::size_t size = 0;
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
size += GetInnerMipmapMemorySize(level, as_host_size, uncompressed);
|
||||
}
|
||||
if (is_tiled && is_layered) {
|
||||
return Common::AlignBits(size, Tegra::Texture::GOB_SIZE_SHIFT + block_height + block_depth);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size,
|
||||
bool uncompressed) const {
|
||||
const u32 mip_width{GetMipmapSize(uncompressed, GetMipWidth(level), GetDefaultBlockWidth())};
|
||||
const u32 mip_height{GetMipmapSize(uncompressed, GetMipHeight(level), GetDefaultBlockHeight())};
|
||||
const u32 mip_depth{is_layered ? 1U : GetMipDepth(level)};
|
||||
if (is_tiled) {
|
||||
return Tegra::Texture::CalculateSize(!as_host_size, GetBytesPerPixel(), mip_width,
|
||||
mip_height, mip_depth, GetMipBlockHeight(level),
|
||||
GetMipBlockDepth(level));
|
||||
} else if (as_host_size || IsBuffer()) {
|
||||
return GetBytesPerPixel() * mip_width * mip_height * mip_depth;
|
||||
} else {
|
||||
// Linear Texture Case
|
||||
return pitch * mip_height * mip_depth;
|
||||
}
|
||||
}
|
||||
|
||||
bool SurfaceParams::operator==(const SurfaceParams& rhs) const {
|
||||
return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
|
||||
height, depth, pitch, num_levels, pixel_format, type, target) ==
|
||||
std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
|
||||
rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
|
||||
rhs.num_levels, rhs.pixel_format, rhs.type, rhs.target);
|
||||
}
|
||||
|
||||
std::string SurfaceParams::TargetName() const {
|
||||
switch (target) {
|
||||
case SurfaceTarget::Texture1D:
|
||||
return "1D";
|
||||
case SurfaceTarget::TextureBuffer:
|
||||
return "TexBuffer";
|
||||
case SurfaceTarget::Texture2D:
|
||||
return "2D";
|
||||
case SurfaceTarget::Texture3D:
|
||||
return "3D";
|
||||
case SurfaceTarget::Texture1DArray:
|
||||
return "1DArray";
|
||||
case SurfaceTarget::Texture2DArray:
|
||||
return "2DArray";
|
||||
case SurfaceTarget::TextureCubemap:
|
||||
return "Cube";
|
||||
case SurfaceTarget::TextureCubeArray:
|
||||
return "CubeArray";
|
||||
default:
|
||||
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", target);
|
||||
UNREACHABLE();
|
||||
return fmt::format("TUK({})", target);
|
||||
}
|
||||
}
|
||||
|
||||
u32 SurfaceParams::GetBlockSize() const {
|
||||
const u32 x = 64U << block_width;
|
||||
const u32 y = 8U << block_height;
|
||||
const u32 z = 1U << block_depth;
|
||||
return x * y * z;
|
||||
}
|
||||
|
||||
std::pair<u32, u32> SurfaceParams::GetBlockXY() const {
|
||||
const u32 x_pixels = 64U / GetBytesPerPixel();
|
||||
const u32 x = x_pixels << block_width;
|
||||
const u32 y = 8U << block_height;
|
||||
return {x, y};
|
||||
}
|
||||
|
||||
std::tuple<u32, u32, u32> SurfaceParams::GetBlockOffsetXYZ(u32 offset) const {
|
||||
const auto div_ceil = [](const u32 x, const u32 y) { return ((x + y - 1) / y); };
|
||||
const u32 block_size = GetBlockSize();
|
||||
const u32 block_index = offset / block_size;
|
||||
const u32 gob_offset = offset % block_size;
|
||||
const u32 gob_index = gob_offset / static_cast<u32>(Tegra::Texture::GOB_SIZE);
|
||||
const u32 x_gob_pixels = 64U / GetBytesPerPixel();
|
||||
const u32 x_block_pixels = x_gob_pixels << block_width;
|
||||
const u32 y_block_pixels = 8U << block_height;
|
||||
const u32 z_block_pixels = 1U << block_depth;
|
||||
const u32 x_blocks = div_ceil(width, x_block_pixels);
|
||||
const u32 y_blocks = div_ceil(height, y_block_pixels);
|
||||
const u32 z_blocks = div_ceil(depth, z_block_pixels);
|
||||
const u32 base_x = block_index % x_blocks;
|
||||
const u32 base_y = (block_index / x_blocks) % y_blocks;
|
||||
const u32 base_z = (block_index / (x_blocks * y_blocks)) % z_blocks;
|
||||
u32 x = base_x * x_block_pixels;
|
||||
u32 y = base_y * y_block_pixels;
|
||||
u32 z = base_z * z_block_pixels;
|
||||
z += gob_index >> block_height;
|
||||
y += (gob_index * 8U) % y_block_pixels;
|
||||
return {x, y, z};
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
|
@ -1,294 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/bit_util.h"
|
||||
#include "common/cityhash.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/shader/shader_ir.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/textures/decoders.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
class FormatLookupTable;
|
||||
|
||||
class SurfaceParams {
|
||||
public:
|
||||
/// Creates SurfaceCachedParams from a texture configuration.
|
||||
static SurfaceParams CreateForTexture(const FormatLookupTable& lookup_table,
|
||||
const Tegra::Texture::TICEntry& tic,
|
||||
const VideoCommon::Shader::Sampler& entry);
|
||||
|
||||
/// Creates SurfaceCachedParams from an image configuration.
|
||||
static SurfaceParams CreateForImage(const FormatLookupTable& lookup_table,
|
||||
const Tegra::Texture::TICEntry& tic,
|
||||
const VideoCommon::Shader::Image& entry);
|
||||
|
||||
/// Creates SurfaceCachedParams for a depth buffer configuration.
|
||||
static SurfaceParams CreateForDepthBuffer(Tegra::Engines::Maxwell3D& maxwell3d);
|
||||
|
||||
/// Creates SurfaceCachedParams from a framebuffer configuration.
|
||||
static SurfaceParams CreateForFramebuffer(Tegra::Engines::Maxwell3D& maxwell3d,
|
||||
std::size_t index);
|
||||
|
||||
/// Creates SurfaceCachedParams from a Fermi2D surface configuration.
|
||||
static SurfaceParams CreateForFermiCopySurface(
|
||||
const Tegra::Engines::Fermi2D::Regs::Surface& config);
|
||||
|
||||
/// Obtains the texture target from a shader's sampler entry.
|
||||
static VideoCore::Surface::SurfaceTarget ExpectedTarget(
|
||||
const VideoCommon::Shader::Sampler& entry);
|
||||
|
||||
/// Obtains the texture target from a shader's sampler entry.
|
||||
static VideoCore::Surface::SurfaceTarget ExpectedTarget(
|
||||
const VideoCommon::Shader::Image& entry);
|
||||
|
||||
std::size_t Hash() const {
|
||||
return static_cast<std::size_t>(
|
||||
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
|
||||
}
|
||||
|
||||
bool operator==(const SurfaceParams& rhs) const;
|
||||
|
||||
bool operator!=(const SurfaceParams& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
std::size_t GetGuestSizeInBytes() const {
|
||||
return GetInnerMemorySize(false, false, false);
|
||||
}
|
||||
|
||||
std::size_t GetHostSizeInBytes(bool is_converted) const {
|
||||
if (!is_converted) {
|
||||
return GetInnerMemorySize(true, false, false);
|
||||
}
|
||||
// ASTC is uncompressed in software, in emulated as RGBA8
|
||||
std::size_t host_size_in_bytes = 0;
|
||||
for (u32 level = 0; level < num_levels; ++level) {
|
||||
host_size_in_bytes += GetConvertedMipmapSize(level) * GetNumLayers();
|
||||
}
|
||||
return host_size_in_bytes;
|
||||
}
|
||||
|
||||
u32 GetBlockAlignedWidth() const {
|
||||
return Common::AlignUp(width, 64 / GetBytesPerPixel());
|
||||
}
|
||||
|
||||
/// Returns the width of a given mipmap level.
|
||||
u32 GetMipWidth(u32 level) const {
|
||||
return std::max(1U, width >> level);
|
||||
}
|
||||
|
||||
/// Returns the height of a given mipmap level.
|
||||
u32 GetMipHeight(u32 level) const {
|
||||
return std::max(1U, height >> level);
|
||||
}
|
||||
|
||||
/// Returns the depth of a given mipmap level.
|
||||
u32 GetMipDepth(u32 level) const {
|
||||
return is_layered ? depth : std::max(1U, depth >> level);
|
||||
}
|
||||
|
||||
/// Returns the block height of a given mipmap level.
|
||||
u32 GetMipBlockHeight(u32 level) const;
|
||||
|
||||
/// Returns the block depth of a given mipmap level.
|
||||
u32 GetMipBlockDepth(u32 level) const;
|
||||
|
||||
/// Returns the best possible row/pitch alignment for the surface.
|
||||
u32 GetRowAlignment(u32 level, bool is_converted) const {
|
||||
const u32 bpp = is_converted ? 4 : GetBytesPerPixel();
|
||||
return 1U << Common::CountTrailingZeroes32(GetMipWidth(level) * bpp);
|
||||
}
|
||||
|
||||
/// Returns the offset in bytes in guest memory of a given mipmap level.
|
||||
std::size_t GetGuestMipmapLevelOffset(u32 level) const;
|
||||
|
||||
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
|
||||
std::size_t GetHostMipmapLevelOffset(u32 level, bool is_converted) const;
|
||||
|
||||
/// Returns the size in bytes in guest memory of a given mipmap level.
|
||||
std::size_t GetGuestMipmapSize(u32 level) const {
|
||||
return GetInnerMipmapMemorySize(level, false, false);
|
||||
}
|
||||
|
||||
/// Returns the size in bytes in host memory (linear) of a given mipmap level.
|
||||
std::size_t GetHostMipmapSize(u32 level) const {
|
||||
return GetInnerMipmapMemorySize(level, true, false) * GetNumLayers();
|
||||
}
|
||||
|
||||
std::size_t GetConvertedMipmapSize(u32 level) const;
|
||||
|
||||
/// Get this texture Tegra Block size in guest memory layout
|
||||
u32 GetBlockSize() const;
|
||||
|
||||
/// Get X, Y coordinates max sizes of a single block.
|
||||
std::pair<u32, u32> GetBlockXY() const;
|
||||
|
||||
/// Get the offset in x, y, z coordinates from a memory offset
|
||||
std::tuple<u32, u32, u32> GetBlockOffsetXYZ(u32 offset) const;
|
||||
|
||||
/// Returns the size of a layer in bytes in guest memory.
|
||||
std::size_t GetGuestLayerSize() const {
|
||||
return GetLayerSize(false, false);
|
||||
}
|
||||
|
||||
/// Returns the size of a layer in bytes in host memory for a given mipmap level.
|
||||
std::size_t GetHostLayerSize(u32 level) const {
|
||||
ASSERT(target != VideoCore::Surface::SurfaceTarget::Texture3D);
|
||||
return GetInnerMipmapMemorySize(level, true, false);
|
||||
}
|
||||
|
||||
/// Returns the max possible mipmap that the texture can have in host gpu
|
||||
u32 MaxPossibleMipmap() const {
|
||||
const u32 max_mipmap_w = Common::Log2Ceil32(width) + 1U;
|
||||
const u32 max_mipmap_h = Common::Log2Ceil32(height) + 1U;
|
||||
const u32 max_mipmap = std::max(max_mipmap_w, max_mipmap_h);
|
||||
if (target != VideoCore::Surface::SurfaceTarget::Texture3D)
|
||||
return max_mipmap;
|
||||
return std::max(max_mipmap, Common::Log2Ceil32(depth) + 1U);
|
||||
}
|
||||
|
||||
/// Returns if the guest surface is a compressed surface.
|
||||
bool IsCompressed() const {
|
||||
return GetDefaultBlockHeight() > 1 || GetDefaultBlockWidth() > 1;
|
||||
}
|
||||
|
||||
/// Returns the default block width.
|
||||
u32 GetDefaultBlockWidth() const {
|
||||
return VideoCore::Surface::GetDefaultBlockWidth(pixel_format);
|
||||
}
|
||||
|
||||
/// Returns the default block height.
|
||||
u32 GetDefaultBlockHeight() const {
|
||||
return VideoCore::Surface::GetDefaultBlockHeight(pixel_format);
|
||||
}
|
||||
|
||||
/// Returns the bits per pixel.
|
||||
u32 GetBitsPerPixel() const {
|
||||
return VideoCore::Surface::GetFormatBpp(pixel_format);
|
||||
}
|
||||
|
||||
/// Returns the bytes per pixel.
|
||||
u32 GetBytesPerPixel() const {
|
||||
return VideoCore::Surface::GetBytesPerPixel(pixel_format);
|
||||
}
|
||||
|
||||
/// Returns true if the pixel format is a depth and/or stencil format.
|
||||
bool IsPixelFormatZeta() const {
|
||||
return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
|
||||
pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
|
||||
}
|
||||
|
||||
/// Returns is the surface is a TextureBuffer type of surface.
|
||||
bool IsBuffer() const {
|
||||
return target == VideoCore::Surface::SurfaceTarget::TextureBuffer;
|
||||
}
|
||||
|
||||
/// Returns the number of layers in the surface.
|
||||
std::size_t GetNumLayers() const {
|
||||
return is_layered ? depth : 1;
|
||||
}
|
||||
|
||||
/// Returns the debug name of the texture for use in graphic debuggers.
|
||||
std::string TargetName() const;
|
||||
|
||||
// Helper used for out of class size calculations
|
||||
static std::size_t AlignLayered(const std::size_t out_size, const u32 block_height,
|
||||
const u32 block_depth) {
|
||||
return Common::AlignBits(out_size,
|
||||
Tegra::Texture::GOB_SIZE_SHIFT + block_height + block_depth);
|
||||
}
|
||||
|
||||
/// Converts a width from a type of surface into another. This helps represent the
|
||||
/// equivalent value between compressed/non-compressed textures.
|
||||
static u32 ConvertWidth(u32 width, VideoCore::Surface::PixelFormat pixel_format_from,
|
||||
VideoCore::Surface::PixelFormat pixel_format_to) {
|
||||
const u32 bw1 = VideoCore::Surface::GetDefaultBlockWidth(pixel_format_from);
|
||||
const u32 bw2 = VideoCore::Surface::GetDefaultBlockWidth(pixel_format_to);
|
||||
return (width * bw2 + bw1 - 1) / bw1;
|
||||
}
|
||||
|
||||
/// Converts a height from a type of surface into another. This helps represent the
|
||||
/// equivalent value between compressed/non-compressed textures.
|
||||
static u32 ConvertHeight(u32 height, VideoCore::Surface::PixelFormat pixel_format_from,
|
||||
VideoCore::Surface::PixelFormat pixel_format_to) {
|
||||
const u32 bh1 = VideoCore::Surface::GetDefaultBlockHeight(pixel_format_from);
|
||||
const u32 bh2 = VideoCore::Surface::GetDefaultBlockHeight(pixel_format_to);
|
||||
return (height * bh2 + bh1 - 1) / bh1;
|
||||
}
|
||||
|
||||
// Finds the maximun possible width between 2 2D layers of different formats
|
||||
static u32 IntersectWidth(const SurfaceParams& src_params, const SurfaceParams& dst_params,
|
||||
const u32 src_level, const u32 dst_level) {
|
||||
const u32 bw1 = src_params.GetDefaultBlockWidth();
|
||||
const u32 bw2 = dst_params.GetDefaultBlockWidth();
|
||||
const u32 t_src_width = (src_params.GetMipWidth(src_level) * bw2 + bw1 - 1) / bw1;
|
||||
const u32 t_dst_width = (dst_params.GetMipWidth(dst_level) * bw1 + bw2 - 1) / bw2;
|
||||
return std::min(t_src_width, t_dst_width);
|
||||
}
|
||||
|
||||
// Finds the maximun possible height between 2 2D layers of different formats
|
||||
static u32 IntersectHeight(const SurfaceParams& src_params, const SurfaceParams& dst_params,
|
||||
const u32 src_level, const u32 dst_level) {
|
||||
const u32 bh1 = src_params.GetDefaultBlockHeight();
|
||||
const u32 bh2 = dst_params.GetDefaultBlockHeight();
|
||||
const u32 t_src_height = (src_params.GetMipHeight(src_level) * bh2 + bh1 - 1) / bh1;
|
||||
const u32 t_dst_height = (dst_params.GetMipHeight(dst_level) * bh1 + bh2 - 1) / bh2;
|
||||
return std::min(t_src_height, t_dst_height);
|
||||
}
|
||||
|
||||
bool is_tiled;
|
||||
bool srgb_conversion;
|
||||
bool is_layered;
|
||||
u32 block_width;
|
||||
u32 block_height;
|
||||
u32 block_depth;
|
||||
u32 tile_width_spacing;
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
u32 pitch;
|
||||
u32 num_levels;
|
||||
u32 emulated_levels;
|
||||
VideoCore::Surface::PixelFormat pixel_format;
|
||||
VideoCore::Surface::SurfaceType type;
|
||||
VideoCore::Surface::SurfaceTarget target;
|
||||
|
||||
private:
|
||||
/// Returns the size of a given mipmap level inside a layer.
|
||||
std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const;
|
||||
|
||||
/// Returns the size of all mipmap levels and aligns as needed.
|
||||
std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const {
|
||||
return GetLayerSize(as_host_size, uncompressed) *
|
||||
(layer_only ? 1U : (is_layered ? depth : 1U));
|
||||
}
|
||||
|
||||
/// Returns the size of a layer
|
||||
std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const;
|
||||
|
||||
/// Returns true if these parameters are from a layered surface.
|
||||
bool IsLayered() const;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<VideoCommon::SurfaceParams> {
|
||||
std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <tuple>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/texture_cache/surface_view.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
std::size_t ViewParams::Hash() const {
|
||||
return static_cast<std::size_t>(base_layer) ^ (static_cast<std::size_t>(num_layers) << 16) ^
|
||||
(static_cast<std::size_t>(base_level) << 24) ^
|
||||
(static_cast<std::size_t>(num_levels) << 32) ^ (static_cast<std::size_t>(target) << 36);
|
||||
}
|
||||
|
||||
bool ViewParams::operator==(const ViewParams& rhs) const {
|
||||
return std::tie(base_layer, num_layers, base_level, num_levels, target) ==
|
||||
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels, rhs.target);
|
||||
}
|
||||
|
||||
bool ViewParams::operator!=(const ViewParams& rhs) const {
|
||||
return !operator==(rhs);
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
|
@ -1,68 +0,0 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/surface_params.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
struct ViewParams {
|
||||
constexpr explicit ViewParams(VideoCore::Surface::SurfaceTarget target_, u32 base_layer_,
|
||||
u32 num_layers_, u32 base_level_, u32 num_levels_)
|
||||
: target{target_}, base_layer{base_layer_}, num_layers{num_layers_},
|
||||
base_level{base_level_}, num_levels{num_levels_} {}
|
||||
|
||||
std::size_t Hash() const;
|
||||
|
||||
bool operator==(const ViewParams& rhs) const;
|
||||
bool operator!=(const ViewParams& rhs) const;
|
||||
|
||||
bool IsLayered() const {
|
||||
switch (target) {
|
||||
case VideoCore::Surface::SurfaceTarget::Texture1DArray:
|
||||
case VideoCore::Surface::SurfaceTarget::Texture2DArray:
|
||||
case VideoCore::Surface::SurfaceTarget::TextureCubemap:
|
||||
case VideoCore::Surface::SurfaceTarget::TextureCubeArray:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
VideoCore::Surface::SurfaceTarget target{};
|
||||
u32 base_layer{};
|
||||
u32 num_layers{};
|
||||
u32 base_level{};
|
||||
u32 num_levels{};
|
||||
};
|
||||
|
||||
class ViewBase {
|
||||
public:
|
||||
constexpr explicit ViewBase(const ViewParams& view_params) : params{view_params} {}
|
||||
|
||||
constexpr const ViewParams& GetViewParams() const {
|
||||
return params;
|
||||
}
|
||||
|
||||
protected:
|
||||
ViewParams params;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
namespace std {
|
||||
|
||||
template <>
|
||||
struct hash<VideoCommon::ViewParams> {
|
||||
std::size_t operator()(const VideoCommon::ViewParams& k) const noexcept {
|
||||
return k.Hash();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
File diff suppressed because it is too large
Load diff
140
src/video_core/texture_cache/types.h
Normal file
140
src/video_core/texture_cache/types.h
Normal file
|
@ -0,0 +1,140 @@
|
|||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "video_core/texture_cache/slot_vector.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
constexpr size_t NUM_RT = 8;
|
||||
constexpr size_t MAX_MIP_LEVELS = 14;
|
||||
|
||||
constexpr SlotId CORRUPT_ID{0xfffffffe};
|
||||
|
||||
using ImageId = SlotId;
|
||||
using ImageViewId = SlotId;
|
||||
using ImageAllocId = SlotId;
|
||||
using SamplerId = SlotId;
|
||||
using FramebufferId = SlotId;
|
||||
|
||||
enum class ImageType : u32 {
|
||||
e1D,
|
||||
e2D,
|
||||
e3D,
|
||||
Linear,
|
||||
Buffer,
|
||||
};
|
||||
|
||||
enum class ImageViewType : u32 {
|
||||
e1D,
|
||||
e2D,
|
||||
Cube,
|
||||
e3D,
|
||||
e1DArray,
|
||||
e2DArray,
|
||||
CubeArray,
|
||||
Rect,
|
||||
Buffer,
|
||||
};
|
||||
constexpr size_t NUM_IMAGE_VIEW_TYPES = 9;
|
||||
|
||||
enum class RelaxedOptions : u32 {
|
||||
Size = 1 << 0,
|
||||
Format = 1 << 1,
|
||||
Samples = 1 << 2,
|
||||
};
|
||||
DECLARE_ENUM_FLAG_OPERATORS(RelaxedOptions)
|
||||
|
||||
struct Offset2D {
|
||||
constexpr auto operator<=>(const Offset2D&) const noexcept = default;
|
||||
|
||||
s32 x;
|
||||
s32 y;
|
||||
};
|
||||
|
||||
struct Offset3D {
|
||||
constexpr auto operator<=>(const Offset3D&) const noexcept = default;
|
||||
|
||||
s32 x;
|
||||
s32 y;
|
||||
s32 z;
|
||||
};
|
||||
|
||||
struct Extent2D {
|
||||
constexpr auto operator<=>(const Extent2D&) const noexcept = default;
|
||||
|
||||
u32 width;
|
||||
u32 height;
|
||||
};
|
||||
|
||||
struct Extent3D {
|
||||
constexpr auto operator<=>(const Extent3D&) const noexcept = default;
|
||||
|
||||
u32 width;
|
||||
u32 height;
|
||||
u32 depth;
|
||||
};
|
||||
|
||||
struct SubresourceLayers {
|
||||
s32 base_level = 0;
|
||||
s32 base_layer = 0;
|
||||
s32 num_layers = 1;
|
||||
};
|
||||
|
||||
struct SubresourceBase {
|
||||
constexpr auto operator<=>(const SubresourceBase&) const noexcept = default;
|
||||
|
||||
s32 level = 0;
|
||||
s32 layer = 0;
|
||||
};
|
||||
|
||||
struct SubresourceExtent {
|
||||
constexpr auto operator<=>(const SubresourceExtent&) const noexcept = default;
|
||||
|
||||
s32 levels = 1;
|
||||
s32 layers = 1;
|
||||
};
|
||||
|
||||
struct SubresourceRange {
|
||||
constexpr auto operator<=>(const SubresourceRange&) const noexcept = default;
|
||||
|
||||
SubresourceBase base;
|
||||
SubresourceExtent extent;
|
||||
};
|
||||
|
||||
struct ImageCopy {
|
||||
SubresourceLayers src_subresource;
|
||||
SubresourceLayers dst_subresource;
|
||||
Offset3D src_offset;
|
||||
Offset3D dst_offset;
|
||||
Extent3D extent;
|
||||
};
|
||||
|
||||
struct BufferImageCopy {
|
||||
size_t buffer_offset;
|
||||
size_t buffer_size;
|
||||
u32 buffer_row_length;
|
||||
u32 buffer_image_height;
|
||||
SubresourceLayers image_subresource;
|
||||
Offset3D image_offset;
|
||||
Extent3D image_extent;
|
||||
};
|
||||
|
||||
struct BufferCopy {
|
||||
size_t src_offset;
|
||||
size_t dst_offset;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct SwizzleParameters {
|
||||
Extent3D num_tiles;
|
||||
Extent3D block;
|
||||
size_t buffer_offset;
|
||||
s32 level;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
1232
src/video_core/texture_cache/util.cpp
Normal file
1232
src/video_core/texture_cache/util.cpp
Normal file
File diff suppressed because it is too large
Load diff
107
src/video_core/texture_cache/util.h
Normal file
107
src/video_core/texture_cache/util.h
Normal file
|
@ -0,0 +1,107 @@
|
|||
// Copyright 2020 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <span>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/image_base.h"
|
||||
#include "video_core/texture_cache/image_view_base.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using Tegra::Texture::TICEntry;
|
||||
|
||||
struct OverlapResult {
|
||||
GPUVAddr gpu_addr;
|
||||
VAddr cpu_addr;
|
||||
SubresourceExtent resources;
|
||||
};
|
||||
|
||||
[[nodiscard]] u32 CalculateGuestSizeInBytes(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] u32 CalculateUnswizzledSizeBytes(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] u32 CalculateConvertedSizeBytes(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] u32 CalculateLayerStride(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] u32 CalculateLayerSize(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] std::array<u32, MAX_MIP_LEVELS> CalculateMipLevelOffsets(
|
||||
const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] std::vector<u32> CalculateSliceOffsets(const ImageInfo& info);
|
||||
|
||||
[[nodiscard]] std::vector<SubresourceBase> CalculateSliceSubresources(const ImageInfo& info);
|
||||
|
||||
[[nodiscard]] u32 CalculateLevelStrideAlignment(const ImageInfo& info, u32 level);
|
||||
|
||||
[[nodiscard]] VideoCore::Surface::PixelFormat PixelFormatFromTIC(
|
||||
const Tegra::Texture::TICEntry& config) noexcept;
|
||||
|
||||
[[nodiscard]] ImageViewType RenderTargetImageViewType(const ImageInfo& info) noexcept;
|
||||
|
||||
[[nodiscard]] std::vector<ImageCopy> MakeShrinkImageCopies(const ImageInfo& dst,
|
||||
const ImageInfo& src,
|
||||
SubresourceBase base);
|
||||
|
||||
[[nodiscard]] bool IsValidAddress(const Tegra::MemoryManager& gpu_memory, const TICEntry& config);
|
||||
|
||||
[[nodiscard]] std::vector<BufferImageCopy> UnswizzleImage(Tegra::MemoryManager& gpu_memory,
|
||||
GPUVAddr gpu_addr, const ImageInfo& info,
|
||||
std::span<u8> output);
|
||||
|
||||
[[nodiscard]] BufferCopy UploadBufferCopy(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr,
|
||||
const ImageBase& image, std::span<u8> output);
|
||||
|
||||
void ConvertImage(std::span<const u8> input, const ImageInfo& info, std::span<u8> output,
|
||||
std::span<BufferImageCopy> copies);
|
||||
|
||||
[[nodiscard]] std::vector<BufferImageCopy> FullDownloadCopies(const ImageInfo& info);
|
||||
|
||||
[[nodiscard]] Extent3D MipSize(Extent3D size, u32 level);
|
||||
|
||||
[[nodiscard]] Extent3D MipBlockSize(const ImageInfo& info, u32 level);
|
||||
|
||||
[[nodiscard]] std::vector<SwizzleParameters> FullUploadSwizzles(const ImageInfo& info);
|
||||
|
||||
void SwizzleImage(Tegra::MemoryManager& gpu_memory, GPUVAddr gpu_addr, const ImageInfo& info,
|
||||
std::span<const BufferImageCopy> copies, std::span<const u8> memory);
|
||||
|
||||
[[nodiscard]] bool IsBlockLinearSizeCompatible(const ImageInfo& new_info,
|
||||
const ImageInfo& overlap_info, u32 new_level,
|
||||
u32 overlap_level, bool strict_size) noexcept;
|
||||
|
||||
[[nodiscard]] bool IsPitchLinearSameSize(const ImageInfo& lhs, const ImageInfo& rhs,
|
||||
bool strict_size) noexcept;
|
||||
|
||||
[[nodiscard]] std::optional<OverlapResult> ResolveOverlap(const ImageInfo& new_info,
|
||||
GPUVAddr gpu_addr, VAddr cpu_addr,
|
||||
const ImageBase& overlap,
|
||||
bool strict_size);
|
||||
|
||||
[[nodiscard]] bool IsLayerStrideCompatible(const ImageInfo& lhs, const ImageInfo& rhs);
|
||||
|
||||
[[nodiscard]] std::optional<SubresourceBase> FindSubresource(const ImageInfo& candidate,
|
||||
const ImageBase& image,
|
||||
GPUVAddr candidate_addr,
|
||||
RelaxedOptions options);
|
||||
|
||||
[[nodiscard]] bool IsSubresource(const ImageInfo& candidate, const ImageBase& image,
|
||||
GPUVAddr candidate_addr, RelaxedOptions options);
|
||||
|
||||
void DeduceBlitImages(ImageInfo& dst_info, ImageInfo& src_info, const ImageBase* dst,
|
||||
const ImageBase* src);
|
||||
|
||||
[[nodiscard]] u32 MapSizeBytes(const ImageBase& image);
|
||||
|
||||
} // namespace VideoCommon
|
Loading…
Add table
Add a link
Reference in a new issue