early-access version 1255

This commit is contained in:
pineappleEA
2020-12-28 15:15:37 +00:00
parent 84b39492d1
commit 78b48028e1
6254 changed files with 1868140 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

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

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

File diff suppressed because it is too large Load Diff

View 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

File diff suppressed because it is too large Load Diff

View 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