early-access version 1255
This commit is contained in:
97
src/video_core/texture_cache/decode_bc4.cpp
Executable file
97
src/video_core/texture_cache/decode_bc4.cpp
Executable 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
Executable file
16
src/video_core/texture_cache/decode_bc4.h
Executable 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
Executable file
82
src/video_core/texture_cache/descriptor_table.h
Executable 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
|
225
src/video_core/texture_cache/format_lookup_table.cpp
Executable file
225
src/video_core/texture_cache/format_lookup_table.cpp
Executable file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/texture_cache/format_lookup_table.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using Tegra::Texture::ComponentType;
|
||||
using Tegra::Texture::TextureFormat;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr auto SNORM = ComponentType::SNORM;
|
||||
constexpr auto UNORM = ComponentType::UNORM;
|
||||
constexpr auto SINT = ComponentType::SINT;
|
||||
constexpr auto UINT = ComponentType::UINT;
|
||||
constexpr auto FLOAT = ComponentType::FLOAT;
|
||||
constexpr bool LINEAR = false;
|
||||
constexpr bool SRGB = true;
|
||||
|
||||
constexpr u32 Hash(TextureFormat format, ComponentType red_component, ComponentType green_component,
|
||||
ComponentType blue_component, ComponentType alpha_component, bool is_srgb) {
|
||||
u32 hash = is_srgb ? 1 : 0;
|
||||
hash |= static_cast<u32>(red_component) << 1;
|
||||
hash |= static_cast<u32>(green_component) << 4;
|
||||
hash |= static_cast<u32>(blue_component) << 7;
|
||||
hash |= static_cast<u32>(alpha_component) << 10;
|
||||
hash |= static_cast<u32>(format) << 13;
|
||||
return hash;
|
||||
}
|
||||
|
||||
constexpr u32 Hash(TextureFormat format, ComponentType component, bool is_srgb = LINEAR) {
|
||||
return Hash(format, component, component, component, component, is_srgb);
|
||||
}
|
||||
|
||||
} // Anonymous namespace
|
||||
|
||||
PixelFormat PixelFormatFromTextureInfo(TextureFormat format, ComponentType red, ComponentType green,
|
||||
ComponentType blue, ComponentType alpha,
|
||||
bool is_srgb) noexcept {
|
||||
switch (Hash(format, red, green, blue, alpha, is_srgb)) {
|
||||
case Hash(TextureFormat::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),
|
||||
static_cast<int>(green), static_cast<int>(blue), static_cast<int>(alpha));
|
||||
return PixelFormat::A8B8G8R8_UNORM;
|
||||
}
|
||||
|
||||
} // namespace VideoCommon
|
17
src/video_core/texture_cache/format_lookup_table.h
Executable file
17
src/video_core/texture_cache/format_lookup_table.h
Executable file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
VideoCore::Surface::PixelFormat PixelFormatFromTextureInfo(
|
||||
Tegra::Texture::TextureFormat format, Tegra::Texture::ComponentType red_component,
|
||||
Tegra::Texture::ComponentType green_component, Tegra::Texture::ComponentType blue_component,
|
||||
Tegra::Texture::ComponentType alpha_component, bool is_srgb) noexcept;
|
||||
|
||||
} // namespace VideoCommon
|
91
src/video_core/texture_cache/formatter.cpp
Executable file
91
src/video_core/texture_cache/formatter.cpp
Executable file
@@ -0,0 +1,91 @@
|
||||
// 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"
|
||||
|
||||
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
|
262
src/video_core/texture_cache/formatter.h
Executable file
262
src/video_core/texture_cache/formatter.h
Executable file
@@ -0,0 +1,262 @@
|
||||
// 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/texture_cache/texture_cache.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
Executable file
216
src/video_core/texture_cache/image_base.cpp
Executable 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
Executable file
83
src/video_core/texture_cache/image_base.h
Executable 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
Executable file
189
src/video_core/texture_cache/image_info.cpp
Executable 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
Executable file
38
src/video_core/texture_cache/image_info.h
Executable 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
Executable file
41
src/video_core/texture_cache/image_view_base.cpp
Executable 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
Executable file
47
src/video_core/texture_cache/image_view_base.h
Executable 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
Executable file
88
src/video_core/texture_cache/image_view_info.cpp
Executable 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
Executable file
50
src/video_core/texture_cache/image_view_info.h
Executable 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
Executable file
51
src/video_core/texture_cache/render_targets.h
Executable 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
Executable file
55
src/video_core/texture_cache/samples_helper.h
Executable 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
Executable file
156
src/video_core/texture_cache/slot_vector.h
Executable 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);
|
||||
}
|
||||
};
|
354
src/video_core/texture_cache/texture_cache.h
Executable file
354
src/video_core/texture_cache/texture_cache.h
Executable file
@@ -0,0 +1,354 @@
|
||||
// Copyright 2019 yuzu Emulator Project
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <bit>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include <span>
|
||||
#include <type_traits>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "common/alignment.h"
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/common_types.h"
|
||||
#include "common/logging/log.h"
|
||||
#include "video_core/compatible_formats.h"
|
||||
#include "video_core/delayed_destruction_ring.h"
|
||||
#include "video_core/dirty_flags.h"
|
||||
#include "video_core/engines/fermi_2d.h"
|
||||
#include "video_core/engines/kepler_compute.h"
|
||||
#include "video_core/engines/maxwell_3d.h"
|
||||
#include "video_core/memory_manager.h"
|
||||
#include "video_core/rasterizer_interface.h"
|
||||
#include "video_core/surface.h"
|
||||
#include "video_core/texture_cache/descriptor_table.h"
|
||||
#include "video_core/texture_cache/format_lookup_table.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/image_view_info.h"
|
||||
#include "video_core/texture_cache/render_targets.h"
|
||||
#include "video_core/texture_cache/slot_vector.h"
|
||||
#include "video_core/texture_cache/types.h"
|
||||
#include "video_core/textures/texture.h"
|
||||
|
||||
namespace VideoCommon {
|
||||
|
||||
using Tegra::Texture::SwizzleSource;
|
||||
using Tegra::Texture::TextureType;
|
||||
using Tegra::Texture::TICEntry;
|
||||
using Tegra::Texture::TSCEntry;
|
||||
using VideoCore::Surface::GetFormatType;
|
||||
using VideoCore::Surface::IsCopyCompatible;
|
||||
using VideoCore::Surface::PixelFormat;
|
||||
using VideoCore::Surface::PixelFormatFromDepthFormat;
|
||||
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
||||
using VideoCore::Surface::SurfaceType;
|
||||
|
||||
template <class P>
|
||||
class TextureCache {
|
||||
/// Address shift for caching images into a hash table
|
||||
static constexpr u64 PAGE_SHIFT = 20;
|
||||
|
||||
/// Enables debugging features to the texture cache
|
||||
static constexpr bool ENABLE_VALIDATION = P::ENABLE_VALIDATION;
|
||||
/// Implement blits as copies between framebuffers
|
||||
static constexpr bool FRAMEBUFFER_BLITS = P::FRAMEBUFFER_BLITS;
|
||||
/// True when some copies have to be emulated
|
||||
static constexpr bool HAS_EMULATED_COPIES = P::HAS_EMULATED_COPIES;
|
||||
|
||||
/// Image view ID for null descriptors
|
||||
static constexpr ImageViewId NULL_IMAGE_VIEW_ID{0};
|
||||
/// Sampler ID for bugged sampler ids
|
||||
static constexpr SamplerId NULL_SAMPLER_ID{0};
|
||||
|
||||
using Runtime = typename P::Runtime;
|
||||
using Image = typename P::Image;
|
||||
using ImageAlloc = typename P::ImageAlloc;
|
||||
using ImageView = typename P::ImageView;
|
||||
using Sampler = typename P::Sampler;
|
||||
using Framebuffer = typename P::Framebuffer;
|
||||
|
||||
struct BlitImages {
|
||||
ImageId dst_id;
|
||||
ImageId src_id;
|
||||
PixelFormat dst_format;
|
||||
PixelFormat src_format;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct IdentityHash {
|
||||
[[nodiscard]] size_t operator()(T value) const noexcept {
|
||||
return static_cast<size_t>(value);
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
explicit TextureCache(Runtime&, VideoCore::RasterizerInterface&, Tegra::Engines::Maxwell3D&,
|
||||
Tegra::Engines::KeplerCompute&, Tegra::MemoryManager&);
|
||||
|
||||
/// Notify the cache that a new frame has been queued
|
||||
void TickFrame();
|
||||
|
||||
/// Return an unique mutually exclusive lock for the cache
|
||||
[[nodiscard]] std::unique_lock<std::mutex> AcquireLock();
|
||||
|
||||
/// Return a constant reference to the given image view id
|
||||
[[nodiscard]] const ImageView& GetImageView(ImageViewId id) const noexcept;
|
||||
|
||||
/// Return a reference to the given image view id
|
||||
[[nodiscard]] ImageView& GetImageView(ImageViewId id) noexcept;
|
||||
|
||||
/// Fill image_view_ids with the graphics images in indices
|
||||
void FillGraphicsImageViews(std::span<const u32> indices,
|
||||
std::span<ImageViewId> image_view_ids);
|
||||
|
||||
/// Fill image_view_ids with the compute images in indices
|
||||
void FillComputeImageViews(std::span<const u32> indices, std::span<ImageViewId> image_view_ids);
|
||||
|
||||
/// Get the sampler from the graphics descriptor table in the specified index
|
||||
Sampler* GetGraphicsSampler(u32 index);
|
||||
|
||||
/// Get the sampler from the compute descriptor table in the specified index
|
||||
Sampler* GetComputeSampler(u32 index);
|
||||
|
||||
/// Refresh the state for graphics image view and sampler descriptors
|
||||
void SynchronizeGraphicsDescriptors();
|
||||
|
||||
/// Refresh the state for compute image view and sampler descriptors
|
||||
void SynchronizeComputeDescriptors();
|
||||
|
||||
/// Update bound render targets and upload memory if necessary
|
||||
/// @param is_clear True when the render targets are being used for clears
|
||||
void UpdateRenderTargets(bool is_clear);
|
||||
|
||||
/// Find a framebuffer with the currently bound render targets
|
||||
/// UpdateRenderTargets should be called before this
|
||||
Framebuffer* GetFramebuffer();
|
||||
|
||||
/// Mark images in a range as modified from the CPU
|
||||
void WriteMemory(VAddr cpu_addr, size_t size);
|
||||
|
||||
/// Download contents of host images to guest memory in a region
|
||||
void DownloadMemory(VAddr cpu_addr, size_t size);
|
||||
|
||||
/// Remove images in a region
|
||||
void UnmapMemory(VAddr cpu_addr, size_t size);
|
||||
|
||||
/// Blit an image with the given parameters
|
||||
void BlitImage(const Tegra::Engines::Fermi2D::Surface& dst,
|
||||
const Tegra::Engines::Fermi2D::Surface& src,
|
||||
const Tegra::Engines::Fermi2D::Config& copy);
|
||||
|
||||
/// Invalidate the contents of the color buffer index
|
||||
/// These contents become unspecified, the cache can assume aggressive optimizations.
|
||||
void InvalidateColorBuffer(size_t index);
|
||||
|
||||
/// Invalidate the contents of the depth buffer
|
||||
/// These contents become unspecified, the cache can assume aggressive optimizations.
|
||||
void InvalidateDepthBuffer();
|
||||
|
||||
/// Try to find a cached image view in the given CPU address
|
||||
[[nodiscard]] ImageView* TryFindFramebufferImageView(VAddr cpu_addr);
|
||||
|
||||
/// Return true when there are uncommitted images to be downloaded
|
||||
[[nodiscard]] bool HasUncommittedFlushes() const noexcept;
|
||||
|
||||
/// Return true when the caller should wait for async downloads
|
||||
[[nodiscard]] bool ShouldWaitAsyncFlushes() const noexcept;
|
||||
|
||||
/// Commit asynchronous downloads
|
||||
void CommitAsyncFlushes();
|
||||
|
||||
/// Pop asynchronous downloads
|
||||
void PopAsyncFlushes();
|
||||
|
||||
/// Return true when a CPU region is modified from the GPU
|
||||
[[nodiscard]] bool IsRegionGpuModified(VAddr addr, size_t size);
|
||||
|
||||
private:
|
||||
/// Iterate over all page indices in a range
|
||||
template <typename Func>
|
||||
static void ForEachPage(VAddr addr, size_t size, Func&& func) {
|
||||
static constexpr bool RETURNS_BOOL = std::is_same_v<std::invoke_result<Func, u64>, bool>;
|
||||
const u64 page_end = (addr + size - 1) >> PAGE_SHIFT;
|
||||
for (u64 page = addr >> PAGE_SHIFT; page <= page_end; ++page) {
|
||||
if constexpr (RETURNS_BOOL) {
|
||||
if (func(page)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
func(page);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills image_view_ids in the image views in indices
|
||||
void FillImageViews(DescriptorTable<TICEntry>& table,
|
||||
std::span<ImageViewId> cached_image_view_ids, std::span<const u32> indices,
|
||||
std::span<ImageViewId> image_view_ids);
|
||||
|
||||
/// Find or create an image view in the guest descriptor table
|
||||
ImageViewId VisitImageView(DescriptorTable<TICEntry>& table,
|
||||
std::span<ImageViewId> cached_image_view_ids, u32 index);
|
||||
|
||||
/// Find or create a framebuffer with the given render target parameters
|
||||
FramebufferId GetFramebufferId(const RenderTargets& key);
|
||||
|
||||
/// Refresh the contents (pixel data) of an image
|
||||
void RefreshContents(Image& image);
|
||||
|
||||
/// Upload data from guest to an image
|
||||
template <typename MapBuffer>
|
||||
void UploadImageContents(Image& image, MapBuffer& map, size_t buffer_offset);
|
||||
|
||||
/// Find or create an image view from a guest descriptor
|
||||
[[nodiscard]] ImageViewId FindImageView(const TICEntry& config);
|
||||
|
||||
/// Create a new image view from a guest descriptor
|
||||
[[nodiscard]] ImageViewId CreateImageView(const TICEntry& config);
|
||||
|
||||
/// Find or create an image from the given parameters
|
||||
[[nodiscard]] ImageId FindOrInsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
RelaxedOptions options = RelaxedOptions{});
|
||||
|
||||
/// Find an image from the given parameters
|
||||
[[nodiscard]] ImageId FindImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
RelaxedOptions options);
|
||||
|
||||
/// Create an image from the given parameters
|
||||
[[nodiscard]] ImageId InsertImage(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
RelaxedOptions options);
|
||||
|
||||
/// Create a new image and join perfectly matching existing images
|
||||
/// Remove joined images from the cache
|
||||
[[nodiscard]] ImageId JoinImages(const ImageInfo& info, GPUVAddr gpu_addr, VAddr cpu_addr);
|
||||
|
||||
/// Return a blit image pair from the given guest blit parameters
|
||||
[[nodiscard]] BlitImages GetBlitImages(const Tegra::Engines::Fermi2D::Surface& dst,
|
||||
const Tegra::Engines::Fermi2D::Surface& src);
|
||||
|
||||
/// Find or create a sampler from a guest descriptor sampler
|
||||
[[nodiscard]] SamplerId FindSampler(const TSCEntry& config);
|
||||
|
||||
/// Find or create an image view for the given color buffer index
|
||||
[[nodiscard]] ImageViewId FindColorBuffer(size_t index, bool is_clear);
|
||||
|
||||
/// Find or create an image view for the depth buffer
|
||||
[[nodiscard]] ImageViewId FindDepthBuffer(bool is_clear);
|
||||
|
||||
/// Find or create a view for a render target with the given image parameters
|
||||
[[nodiscard]] ImageViewId FindRenderTargetView(const ImageInfo& info, GPUVAddr gpu_addr,
|
||||
bool is_clear);
|
||||
|
||||
/// Iterates over all the images in a region calling func
|
||||
template <typename Func>
|
||||
void ForEachImageInRegion(VAddr cpu_addr, size_t size, Func&& func);
|
||||
|
||||
/// Find or create an image view in the given image with the passed parameters
|
||||
[[nodiscard]] ImageViewId FindOrEmplaceImageView(ImageId image_id, const ImageViewInfo& info);
|
||||
|
||||
/// Register image in the page table
|
||||
void RegisterImage(ImageId image);
|
||||
|
||||
/// Unregister image from the page table
|
||||
void UnregisterImage(ImageId image);
|
||||
|
||||
/// Track CPU reads and writes for image
|
||||
void TrackImage(ImageBase& image);
|
||||
|
||||
/// Stop tracking CPU reads and writes for image
|
||||
void UntrackImage(ImageBase& image);
|
||||
|
||||
/// Delete image from the cache
|
||||
void DeleteImage(ImageId image);
|
||||
|
||||
/// Remove image views references from the cache
|
||||
void RemoveImageViewReferences(std::span<const ImageViewId> removed_views);
|
||||
|
||||
/// Remove framebuffers using the given image views from the cache
|
||||
void RemoveFramebuffers(std::span<const ImageViewId> removed_views);
|
||||
|
||||
/// Mark an image as modified from the GPU
|
||||
void MarkModification(ImageBase& image) noexcept;
|
||||
|
||||
/// Synchronize image aliases, copying data if needed
|
||||
void SynchronizeAliases(ImageId image_id);
|
||||
|
||||
/// Prepare an image to be used
|
||||
void PrepareImage(ImageId image_id, bool is_modification, bool invalidate);
|
||||
|
||||
/// Prepare an image view to be used
|
||||
void PrepareImageView(ImageViewId image_view_id, bool is_modification, bool invalidate);
|
||||
|
||||
/// Execute copies from one image to the other, even if they are incompatible
|
||||
void CopyImage(ImageId dst_id, ImageId src_id, std::span<const ImageCopy> copies);
|
||||
|
||||
/// Bind an image view as render target, downloading resources preemtively if needed
|
||||
void BindRenderTarget(ImageViewId* old_id, ImageViewId new_id);
|
||||
|
||||
/// Create a render target from a given image and image view parameters
|
||||
[[nodiscard]] std::pair<FramebufferId, ImageViewId> RenderTargetFromImage(
|
||||
ImageId, const ImageViewInfo& view_info);
|
||||
|
||||
/// Returns true if the current clear parameters clear the whole image of a given image view
|
||||
[[nodiscard]] bool IsFullClear(ImageViewId id);
|
||||
|
||||
Runtime& runtime;
|
||||
VideoCore::RasterizerInterface& rasterizer;
|
||||
Tegra::Engines::Maxwell3D& maxwell3d;
|
||||
Tegra::Engines::KeplerCompute& kepler_compute;
|
||||
Tegra::MemoryManager& gpu_memory;
|
||||
|
||||
DescriptorTable<TICEntry> graphics_image_table{gpu_memory};
|
||||
DescriptorTable<TSCEntry> graphics_sampler_table{gpu_memory};
|
||||
std::vector<SamplerId> graphics_sampler_ids;
|
||||
std::vector<ImageViewId> graphics_image_view_ids;
|
||||
|
||||
DescriptorTable<TICEntry> compute_image_table{gpu_memory};
|
||||
DescriptorTable<TSCEntry> compute_sampler_table{gpu_memory};
|
||||
std::vector<SamplerId> compute_sampler_ids;
|
||||
std::vector<ImageViewId> compute_image_view_ids;
|
||||
|
||||
RenderTargets render_targets;
|
||||
|
||||
std::mutex mutex;
|
||||
|
||||
std::unordered_map<TICEntry, ImageViewId> image_views;
|
||||
std::unordered_map<TSCEntry, SamplerId> samplers;
|
||||
std::unordered_map<RenderTargets, FramebufferId> framebuffers;
|
||||
|
||||
std::unordered_map<u64, std::vector<ImageId>, IdentityHash<u64>> page_table;
|
||||
|
||||
bool has_deleted_images = false;
|
||||
|
||||
SlotVector<Image> slot_images;
|
||||
SlotVector<ImageView> slot_image_views;
|
||||
SlotVector<ImageAlloc> slot_image_allocs;
|
||||
SlotVector<Sampler> slot_samplers;
|
||||
SlotVector<Framebuffer> slot_framebuffers;
|
||||
|
||||
// TODO: This data structure is not optimal and it should be reworked
|
||||
std::vector<ImageId> uncommitted_downloads;
|
||||
std::queue<std::vector<ImageId>> committed_downloads;
|
||||
|
||||
static constexpr size_t TICKS_TO_DESTROY = 6;
|
||||
DelayedDestructionRing<Image, TICKS_TO_DESTROY> sentenced_images;
|
||||
DelayedDestructionRing<ImageView, TICKS_TO_DESTROY> sentenced_image_view;
|
||||
DelayedDestructionRing<Framebuffer, TICKS_TO_DESTROY> sentenced_framebuffers;
|
||||
|
||||
std::unordered_map<GPUVAddr, ImageAllocId> image_allocs_table;
|
||||
|
||||
u64 modification_tick = 0;
|
||||
u64 frame_tick = 0;
|
||||
};
|
||||
|
||||
} // namespace VideoCommon
|
||||
|
||||
#include "texture_cache.inl"
|
1118
src/video_core/texture_cache/texture_cache.inl
Executable file
1118
src/video_core/texture_cache/texture_cache.inl
Executable file
File diff suppressed because it is too large
Load Diff
140
src/video_core/texture_cache/types.h
Executable file
140
src/video_core/texture_cache/types.h
Executable 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
|
1230
src/video_core/texture_cache/util.cpp
Executable file
1230
src/video_core/texture_cache/util.cpp
Executable file
File diff suppressed because it is too large
Load Diff
107
src/video_core/texture_cache/util.h
Executable file
107
src/video_core/texture_cache/util.h
Executable 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
|
Reference in New Issue
Block a user