Correct Surface Base and Views for new Texture Cache

This commit is contained in:
Fernando Sahmkow 2019-05-07 10:56:45 -04:00 committed by ReinUsesLisp
parent 3b26206dbd
commit 3d471e732d
7 changed files with 472 additions and 386 deletions

View File

@ -0,0 +1,25 @@
// Copyright 2019 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include "common/common_types.h"
namespace VideoCommon {
struct CopyParams {
u32 source_x;
u32 source_y;
u32 source_z;
u32 dest_x;
u32 dest_y;
u32 dest_z;
u32 source_level;
u32 dest_level;
u32 width;
u32 height;
u32 depth;
};
} // namespace VideoCommon

View File

@ -4,104 +4,120 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/morton.h"
#include "common/microprofile.h"
#include "video_core/memory_manager.h"
#include "video_core/texture_cache/surface_base.h"
#include "video_core/texture_cache/surface_params.h"
#include "video_core/textures/convert.h"
namespace VideoCommon {
MICROPROFILE_DEFINE(GPU_Load_Texture, "GPU", "Texture Load", MP_RGB(128, 192, 128));
MICROPROFILE_DEFINE(GPU_Flush_Texture, "GPU", "Texture Flush", MP_RGB(128, 192, 128));
using Tegra::Texture::ConvertFromGuestToHost;
using VideoCore::MortonSwizzleMode;
namespace {
void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer,
u32 level) {
SurfaceBaseImpl::SurfaceBaseImpl(const GPUVAddr gpu_vaddr, const SurfaceParams& params)
: gpu_addr{gpu_vaddr}, params{params}, mipmap_sizes{params.num_levels},
mipmap_offsets{params.num_levels}, layer_size{params.GetGuestLayerSize()},
memory_size{params.GetGuestSizeInBytes()}, host_memory_size{params.GetHostSizeInBytes()} {
u32 offset = 0;
mipmap_offsets.resize(params.num_levels);
mipmap_sizes.resize(params.num_levels);
gpu_addr_end = gpu_addr + memory_size;
for (u32 i = 0; i < params.num_levels; i++) {
mipmap_offsets[i] = offset;
mipmap_sizes[i] = params.GetGuestMipmapSize(i);
offset += mipmap_sizes[i];
}
}
void SurfaceBaseImpl::SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params,
u8* buffer, u32 level) {
const u32 width{params.GetMipWidth(level)};
const u32 height{params.GetMipHeight(level)};
const u32 block_height{params.GetMipBlockHeight(level)};
const u32 block_depth{params.GetMipBlockDepth(level)};
std::size_t guest_offset{params.GetGuestMipmapLevelOffset(level)};
if (params.IsLayered()) {
std::size_t guest_offset{mipmap_offsets[level]};
if (params.is_layered) {
std::size_t host_offset{0};
const std::size_t guest_stride = params.GetGuestLayerSize();
const std::size_t guest_stride = layer_size;
const std::size_t host_stride = params.GetHostLayerSize(level);
for (u32 layer = 0; layer < params.GetNumLayers(); layer++) {
MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
1, params.GetTileWidthSpacing(), buffer + host_offset,
memory + guest_offset);
for (u32 layer = 0; layer < params.depth; layer++) {
MortonSwizzle(mode, params.pixel_format, width, block_height, height, block_depth, 1,
params.tile_width_spacing, buffer + host_offset, memory + guest_offset);
guest_offset += guest_stride;
host_offset += host_stride;
}
} else {
MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
params.GetMipDepth(level), params.GetTileWidthSpacing(), buffer,
MortonSwizzle(mode, params.pixel_format, width, block_height, height, block_depth,
params.GetMipDepth(level), params.tile_width_spacing, buffer,
memory + guest_offset);
}
}
} // Anonymous namespace
SurfaceBaseImpl::SurfaceBaseImpl(const SurfaceParams& params) : params{params} {
staging_buffer.resize(params.GetHostSizeInBytes());
}
SurfaceBaseImpl::~SurfaceBaseImpl() = default;
void SurfaceBaseImpl::LoadBuffer() {
if (params.IsTiled()) {
ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {} on texture target {}",
params.GetBlockWidth(), static_cast<u32>(params.GetTarget()));
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
void SurfaceBaseImpl::LoadBuffer(Tegra::MemoryManager& memory_manager,
std::vector<u8>& staging_buffer) {
MICROPROFILE_SCOPE(GPU_Load_Texture);
auto host_ptr = memory_manager.GetPointer(gpu_addr);
if (params.is_tiled) {
ASSERT_MSG(params.block_width == 1, "Block width is defined as {} on texture target {}",
params.block_width, static_cast<u32>(params.target));
for (u32 level = 0; level < params.num_levels; ++level) {
const u32 host_offset = params.GetHostMipmapLevelOffset(level);
SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params,
GetStagingBufferLevelData(level), level);
staging_buffer.data() + host_offset, level);
}
} else {
ASSERT_MSG(params.GetNumLevels() == 1, "Linear mipmap loading is not implemented");
const u32 bpp{GetFormatBpp(params.GetPixelFormat()) / CHAR_BIT};
ASSERT_MSG(params.num_levels == 1, "Linear mipmap loading is not implemented");
const u32 bpp{params.GetBytesPerPixel()};
const u32 block_width{params.GetDefaultBlockWidth()};
const u32 block_height{params.GetDefaultBlockHeight()};
const u32 width{(params.GetWidth() + block_width - 1) / block_width};
const u32 height{(params.GetHeight() + block_height - 1) / block_height};
const u32 width{(params.width + block_width - 1) / block_width};
const u32 height{(params.height + block_height - 1) / block_height};
const u32 copy_size{width * bpp};
if (params.GetPitch() == copy_size) {
if (params.pitch == copy_size) {
std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes());
} else {
const u8* start{host_ptr};
u8* write_to{staging_buffer.data()};
for (u32 h = height; h > 0; --h) {
std::memcpy(write_to, start, copy_size);
start += params.GetPitch();
start += params.pitch;
write_to += copy_size;
}
}
}
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
ConvertFromGuestToHost(GetStagingBufferLevelData(level), params.GetPixelFormat(),
for (u32 level = 0; level < params.num_levels; ++level) {
const u32 host_offset = params.GetHostMipmapLevelOffset(level);
ConvertFromGuestToHost(staging_buffer.data() + host_offset, params.pixel_format,
params.GetMipWidth(level), params.GetMipHeight(level),
params.GetMipDepth(level), true, true);
}
}
void SurfaceBaseImpl::FlushBuffer() {
if (params.IsTiled()) {
ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {}",
params.GetBlockWidth());
for (u32 level = 0; level < params.GetNumLevels(); ++level) {
SwizzleFunc(MortonSwizzleMode::LinearToMorton, GetHostPtr(), params,
GetStagingBufferLevelData(level), level);
void SurfaceBaseImpl::FlushBuffer(std::vector<u8>& staging_buffer) {
MICROPROFILE_SCOPE(GPU_Flush_Texture);
if (params.is_tiled) {
ASSERT_MSG(params.block_width == 1, "Block width is defined as {}", params.block_width);
for (u32 level = 0; level < params.num_levels; ++level) {
const u32 host_offset = params.GetHostMipmapLevelOffset(level);
SwizzleFunc(MortonSwizzleMode::LinearToMorton, host_ptr, params,
staging_buffer.data() + host_offset, level);
}
} else {
UNIMPLEMENTED();
/*
ASSERT(params.GetTarget() == SurfaceTarget::Texture2D);
ASSERT(params.GetNumLevels() == 1);
ASSERT(params.target == SurfaceTarget::Texture2D);
ASSERT(params.num_levels == 1);
const u32 bpp{params.GetFormatBpp() / 8};
const u32 copy_size{params.GetWidth() * bpp};
if (params.GetPitch() == copy_size) {
std::memcpy(host_ptr, staging_buffer.data(), GetSizeInBytes());
const u32 copy_size{params.width * bpp};
if (params.pitch == copy_size) {
std::memcpy(host_ptr, staging_buffer.data(), memory_size);
} else {
u8* start{host_ptr};
const u8* read_to{staging_buffer.data()};

View File

@ -4,166 +4,309 @@
#pragma once
#include <algorithm>
#include <unordered_map>
#include <vector>
#include "common/assert.h"
#include "common/common_types.h"
#include "video_core/gpu.h"
#include "video_core/morton.h"
#include "video_core/texture_cache/copy_params.h"
#include "video_core/texture_cache/surface_params.h"
#include "video_core/texture_cache/surface_view.h"
template<class ForwardIt, class T, class Compare=std::less<>>
ForwardIt binary_find(ForwardIt first, ForwardIt last, const T& value, Compare comp={})
{
// Note: BOTH type T and the type after ForwardIt is dereferenced
// must be implicitly convertible to BOTH Type1 and Type2, used in Compare.
// This is stricter than lower_bound requirement (see above)
first = std::lower_bound(first, last, value, comp);
return first != last && !comp(value, *first) ? first : last;
}
namespace Tegra {
class MemoryManager;
}
namespace VideoCommon {
using VideoCore::Surface::SurfaceTarget;
using VideoCore::MortonSwizzleMode;
class SurfaceBaseImpl {
public:
void LoadBuffer();
void LoadBuffer(Tegra::MemoryManager& memory_manager, std::vector<u8>& staging_buffer);
void FlushBuffer();
void FlushBuffer(std::vector<u8>& staging_buffer);
GPUVAddr GetGpuAddr() const {
ASSERT(is_registered);
return gpu_addr;
}
GPUVAddr GetGpuAddrEnd() const {
return gpu_addr_end;
}
bool Overlaps(const GPUVAddr start, const GPUVAddr end) const {
return (gpu_addr < end) && (gpu_addr_end > start);
}
// Use only when recycling a surface
void SetGpuAddr(const GPUVAddr new_addr) {
gpu_addr = new_addr;
gpu_addr_end = new_addr + memory_size;
}
VAddr GetCpuAddr() const {
ASSERT(is_registered);
return cpu_addr;
return gpu_addr;
}
void SetCpuAddr(const VAddr new_addr) {
cpu_addr = new_addr;
}
u8* GetHostPtr() const {
ASSERT(is_registered);
return host_ptr;
}
CacheAddr GetCacheAddr() const {
ASSERT(is_registered);
return cache_addr;
void SetHostPtr(u8* new_addr) {
host_ptr = new_addr;
}
const SurfaceParams& GetSurfaceParams() const {
return params;
}
void Register(GPUVAddr gpu_addr_, VAddr cpu_addr_, u8* host_ptr_) {
ASSERT(!is_registered);
is_registered = true;
gpu_addr = gpu_addr_;
cpu_addr = cpu_addr_;
host_ptr = host_ptr_;
cache_addr = ToCacheAddr(host_ptr_);
DecorateSurfaceName();
}
void Unregister() {
ASSERT(is_registered);
is_registered = false;
}
bool IsRegistered() const {
return is_registered;
}
std::size_t GetSizeInBytes() const {
return params.GetGuestSizeInBytes();
return memory_size;
}
u8* GetStagingBufferLevelData(u32 level) {
return staging_buffer.data() + params.GetHostMipmapLevelOffset(level);
std::size_t GetHostSizeInBytes() const {
return host_memory_size;
}
std::size_t GetMipmapSize(const u32 level) const {
return mipmap_sizes[level];
}
bool MatchFormat(VideoCore::Surface::PixelFormat pixel_format) const {
return params.pixel_format == pixel_format;
}
bool MatchTarget(VideoCore::Surface::SurfaceTarget target) const {
return params.target == target;
}
bool MatchesTopology(const SurfaceParams& rhs) const {
const u32 src_bpp = params.GetBytesPerPixel();
const u32 dst_bpp = rhs.GetBytesPerPixel();
return std::tie(src_bpp, params.is_tiled) == std::tie(dst_bpp, rhs.is_tiled);
}
bool MatchesStructure(const SurfaceParams& rhs) const {
if (params.is_tiled) {
const u32 a_width1 = params.GetBlockAlignedWidth();
const u32 a_width2 = rhs.GetBlockAlignedWidth();
return std::tie(a_width1, params.height, params.depth, params.block_width,
params.block_height, params.block_depth, params.tile_width_spacing) ==
std::tie(a_width2, rhs.height, rhs.depth, rhs.block_width, rhs.block_height,
rhs.block_depth, rhs.tile_width_spacing);
} else {
return std::tie(params.width, params.height, params.pitch) ==
std::tie(rhs.width, rhs.height, rhs.pitch);
}
}
std::optional<std::pair<u32, u32>> GetLayerMipmap(const GPUVAddr candidate_gpu_addr) const {
if (candidate_gpu_addr < gpu_addr)
return {};
const GPUVAddr relative_address = candidate_gpu_addr - gpu_addr;
const u32 layer = relative_address / layer_size;
const GPUVAddr mipmap_address = relative_address - layer_size * layer;
const auto mipmap_it = binary_find(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
if (mipmap_it != mipmap_offsets.end()) {
return {{layer, std::distance(mipmap_offsets.begin(), mipmap_it)}};
}
return {};
}
std::vector<CopyParams> BreakDown() const {
auto set_up_copy = [](CopyParams& cp, const SurfaceParams& params, const u32 depth,
const u32 level) {
cp.source_x = 0;
cp.source_y = 0;
cp.source_z = 0;
cp.dest_x = 0;
cp.dest_y = 0;
cp.dest_z = 0;
cp.source_level = level;
cp.dest_level = level;
cp.width = params.GetMipWidth(level);
cp.height = params.GetMipHeight(level);
cp.depth = depth;
};
const u32 layers = params.depth;
const u32 mipmaps = params.num_levels;
if (params.is_layered) {
std::vector<CopyParams> result{layers * mipmaps};
for (std::size_t layer = 0; layer < layers; layer++) {
const u32 layer_offset = layer * mipmaps;
for (std::size_t level = 0; level < mipmaps; level++) {
CopyParams& cp = result[layer_offset + level];
set_up_copy(cp, params, layer, level);
}
}
return result;
} else {
std::vector<CopyParams> result{mipmaps};
for (std::size_t level = 0; level < mipmaps; level++) {
CopyParams& cp = result[level];
set_up_copy(cp, params, params.GetMipDepth(level), level);
}
return result;
}
}
protected:
explicit SurfaceBaseImpl(const SurfaceParams& params);
~SurfaceBaseImpl(); // non-virtual is intended
explicit SurfaceBaseImpl(const GPUVAddr gpu_vaddr, const SurfaceParams& params);
~SurfaceBaseImpl() = default;
virtual void DecorateSurfaceName() = 0;
const SurfaceParams params;
GPUVAddr gpu_addr{};
GPUVAddr gpu_addr_end{};
std::vector<u32> mipmap_sizes;
std::vector<u32> mipmap_offsets;
const std::size_t layer_size;
const std::size_t memory_size;
const std::size_t host_memory_size;
u8* host_ptr;
VAddr cpu_addr;
private:
GPUVAddr gpu_addr{};
VAddr cpu_addr{};
u8* host_ptr{};
CacheAddr cache_addr{};
bool is_registered{};
std::vector<u8> staging_buffer;
void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer,
u32 level);
};
template <typename TTextureCache, typename TView>
template <typename TView>
class SurfaceBase : public SurfaceBaseImpl {
public:
virtual void UploadTexture() = 0;
virtual void UploadTexture(std::vector<u8>& staging_buffer) = 0;
virtual void DownloadTexture() = 0;
virtual void DownloadTexture(std::vector<u8>& staging_buffer) = 0;
TView* TryGetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
if (view_addr < GetGpuAddr() || !params.IsFamiliar(view_params)) {
// It can't be a view if it's in a prior address.
return {};
void MarkAsModified(const bool is_modified_, const u64 tick) {
is_modified = is_modified_ || is_protected;
modification_tick = tick;
}
const auto relative_offset{static_cast<u64>(view_addr - GetGpuAddr())};
const auto it{view_offset_map.find(relative_offset)};
if (it == view_offset_map.end()) {
// Couldn't find an aligned view.
return {};
}
const auto [layer, level] = it->second;
if (!params.IsViewValid(view_params, layer, level)) {
return {};
void MarkAsProtected(const bool is_protected) {
this->is_protected = is_protected;
}
return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
}
void MarkAsModified(bool is_modified_) {
is_modified = is_modified_;
if (is_modified_) {
modification_tick = texture_cache.Tick();
}
}
TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
TView* view{TryGetView(view_addr, view_params)};
ASSERT(view != nullptr);
return view;
void MarkAsPicked(const bool is_picked) {
this->is_picked = is_picked;
}
bool IsModified() const {
return is_modified;
}
bool IsProtected() const {
return is_protected;
}
bool IsRegistered() const {
return is_registered;
}
bool IsPicked() const {
return is_picked;
}
void MarkAsRegistered(bool is_reg) {
is_registered = is_reg;
}
u64 GetModificationTick() const {
return modification_tick;
}
TView EmplaceOverview(const SurfaceParams& overview_params) {
ViewParams vp{};
vp.base_level = 0;
vp.num_levels = params.num_levels;
vp.target = overview_params.target;
if (params.is_layered && !overview_params.is_layered) {
vp.base_layer = 0;
vp.num_layers = 1;
} else {
vp.base_layer = 0;
vp.num_layers = params.depth;
}
return GetView(vp);
}
std::optional<TView> EmplaceView(const SurfaceParams& view_params, const GPUVAddr view_addr) {
if (view_addr < gpu_addr)
return {};
if (params.target == SurfaceTarget::Texture3D || view_params.target == SurfaceTarget::Texture3D) {
return {};
}
const std::size_t size = view_params.GetGuestSizeInBytes();
const GPUVAddr relative_address = view_addr - gpu_addr;
auto layer_mipmap = GetLayerMipmap(relative_address);
if (!layer_mipmap) {
return {};
}
const u32 layer = (*layer_mipmap).first;
const u32 mipmap = (*layer_mipmap).second;
if (GetMipmapSize(mipmap) != size) {
// TODO: the view may cover many mimaps, this case can still go on
return {};
}
ViewParams vp{};
vp.base_layer = layer;
vp.num_layers = 1;
vp.base_level = mipmap;
vp.num_levels = 1;
vp.target = params.target;
return {GetView(vp)};
}
TView GetMainView() const {
return main_view;
}
protected:
explicit SurfaceBase(TTextureCache& texture_cache, const SurfaceParams& params)
: SurfaceBaseImpl{params}, texture_cache{texture_cache},
view_offset_map{params.CreateViewOffsetMap()} {}
explicit SurfaceBase(const GPUVAddr gpu_addr, const SurfaceParams& params)
: SurfaceBaseImpl(gpu_addr, params) {}
~SurfaceBase() = default;
virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
virtual TView CreateView(const ViewParams& view_key) = 0;
std::unordered_map<ViewParams, TView> views;
TView main_view;
private:
TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
const ViewKey key{base_layer, num_layers, base_level, num_levels};
TView GetView(const ViewParams& key) {
const auto [entry, is_cache_miss] = views.try_emplace(key);
auto& view{entry->second};
if (is_cache_miss) {
view = CreateView(key);
}
return view.get();
return view;
}
TTextureCache& texture_cache;
const std::map<u64, std::pair<u32, u32>> view_offset_map;
std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
bool is_modified{};
bool is_protected{};
bool is_registered{};
bool is_picked{};
u64 modification_tick{};
};

View File

@ -7,6 +7,7 @@
#include "common/cityhash.h"
#include "common/alignment.h"
#include "core/core.h"
#include "video_core/engines/shader_bytecode.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/surface_params.h"
#include "video_core/textures/decoders.h"
@ -22,6 +23,37 @@ using VideoCore::Surface::PixelFormatFromTextureFormat;
using VideoCore::Surface::SurfaceTarget;
using VideoCore::Surface::SurfaceTargetFromTextureType;
SurfaceTarget TextureType2SurfaceTarget(Tegra::Shader::TextureType type, bool is_array) {
switch (type) {
case Tegra::Shader::TextureType::Texture1D: {
if (is_array)
return SurfaceTarget::Texture1DArray;
else
return SurfaceTarget::Texture1D;
}
case Tegra::Shader::TextureType::Texture2D: {
if (is_array)
return SurfaceTarget::Texture2DArray;
else
return SurfaceTarget::Texture2D;
}
case Tegra::Shader::TextureType::Texture3D: {
ASSERT(!is_array);
return SurfaceTarget::Texture3D;
}
case Tegra::Shader::TextureType::TextureCube: {
if (is_array)
return SurfaceTarget::TextureCubeArray;
else
return SurfaceTarget::TextureCubemap;
}
default: {
UNREACHABLE();
return SurfaceTarget::Texture2D;
}
}
}
namespace {
constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
@ -29,7 +61,8 @@ constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
} // Anonymous namespace
SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
const Tegra::Texture::FullTextureInfo& config) {
const Tegra::Texture::FullTextureInfo& config,
const VideoCommon::Shader::Sampler& entry) {
SurfaceParams params;
params.is_tiled = config.tic.IsTiled();
params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
@ -41,7 +74,8 @@ SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
params.srgb_conversion);
params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
params.type = GetFormatType(params.pixel_format);
params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
// TODO: on 1DBuffer we should use the tic info.
params.target = TextureType2SurfaceTarget(entry.GetType(), entry.IsArray());
params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
params.depth = config.tic.Depth();
@ -52,8 +86,7 @@ SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
params.unaligned_height = config.tic.Height();
params.num_levels = config.tic.max_mip_level + 1;
params.CalculateCachedValues();
params.is_layered = params.IsLayered();
return params;
}
@ -77,8 +110,7 @@ SurfaceParams SurfaceParams::CreateForDepthBuffer(
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
params.num_levels = 1;
params.CalculateCachedValues();
params.is_layered = false;
return params;
}
@ -108,8 +140,7 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz
params.unaligned_height = config.height;
params.target = SurfaceTarget::Texture2D;
params.num_levels = 1;
params.CalculateCachedValues();
params.is_layered = false;
return params;
}
@ -128,13 +159,13 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
params.type = GetFormatType(params.pixel_format);
params.width = config.width;
params.height = config.height;
params.pitch = config.pitch;
params.unaligned_height = config.height;
// TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
params.target = SurfaceTarget::Texture2D;
params.depth = 1;
params.num_levels = 1;
params.CalculateCachedValues();
params.is_layered = params.IsLayered();
return params;
}
@ -147,7 +178,7 @@ u32 SurfaceParams::GetMipHeight(u32 level) const {
}
u32 SurfaceParams::GetMipDepth(u32 level) const {
return IsLayered() ? depth : std::max(1U, depth >> level);
return is_layered ? depth : std::max(1U, depth >> level);
}
bool SurfaceParams::IsLayered() const {
@ -183,7 +214,7 @@ u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
if (level == 0) {
return this->block_depth;
}
if (IsLayered()) {
if (is_layered) {
return 1;
}
@ -216,6 +247,10 @@ std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
return offset;
}
std::size_t SurfaceParams::GetGuestMipmapSize(u32 level) const {
return GetInnerMipmapMemorySize(level, false, false);
}
std::size_t SurfaceParams::GetHostMipmapSize(u32 level) const {
return GetInnerMipmapMemorySize(level, true, false) * GetNumLayers();
}
@ -229,7 +264,7 @@ std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) co
for (u32 level = 0; level < num_levels; ++level) {
size += GetInnerMipmapMemorySize(level, as_host_size, uncompressed);
}
if (is_tiled && (IsLayered() || target == SurfaceTarget::Texture3D)) {
if (is_tiled && is_layered) {
return Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
}
return size;
@ -256,150 +291,32 @@ u32 SurfaceParams::GetBytesPerPixel() const {
return VideoCore::Surface::GetBytesPerPixel(pixel_format);
}
bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
view_params.component_type, view_params.type)) {
return false;
}
const SurfaceTarget view_target{view_params.target};
if (view_target == target) {
return true;
}
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D:
return false;
case SurfaceTarget::Texture1DArray:
return view_target == SurfaceTarget::Texture1D;
case SurfaceTarget::Texture2DArray:
return view_target == SurfaceTarget::Texture2D;
case SurfaceTarget::TextureCubemap:
return view_target == SurfaceTarget::Texture2D ||
view_target == SurfaceTarget::Texture2DArray;
case SurfaceTarget::TextureCubeArray:
return view_target == SurfaceTarget::Texture2D ||
view_target == SurfaceTarget::Texture2DArray ||
view_target == SurfaceTarget::TextureCubemap;
default:
UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
return false;
}
}
bool SurfaceParams::IsPixelFormatZeta() const {
return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
}
void SurfaceParams::CalculateCachedValues() {
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D:
num_layers = 1;
break;
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::TextureCubeArray:
num_layers = depth;
break;
default:
UNREACHABLE();
}
guest_size_in_bytes = GetInnerMemorySize(false, false, false);
if (IsPixelFormatASTC(pixel_format)) {
// ASTC is uncompressed in software, in emulated as RGBA8
host_size_in_bytes = static_cast<std::size_t>(width) * static_cast<std::size_t>(height) *
static_cast<std::size_t>(depth) * 4ULL;
} else {
host_size_in_bytes = GetInnerMemorySize(true, false, false);
}
}
std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size,
bool uncompressed) const {
const bool tiled{as_host_size ? false : is_tiled};
const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), GetDefaultBlockWidth())};
const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), GetDefaultBlockHeight())};
const u32 depth{target == SurfaceTarget::Texture3D ? GetMipDepth(level) : 1U};
const u32 depth{is_layered ? 1U : GetMipDepth(level)};
return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(), width, height, depth,
GetMipBlockHeight(level), GetMipBlockDepth(level));
}
std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
bool uncompressed) const {
return GetLayerSize(as_host_size, uncompressed) * (layer_only ? 1U : num_layers);
return GetLayerSize(as_host_size, uncompressed) * (layer_only ? 1U : depth);
}
std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
std::map<u64, std::pair<u32, u32>> view_offset_map;
switch (target) {
case SurfaceTarget::Texture1D:
case SurfaceTarget::Texture2D:
case SurfaceTarget::Texture3D: {
// TODO(Rodrigo): Add layer iterations for 3D textures
constexpr u32 layer = 0;
for (u32 level = 0; level < num_levels; ++level) {
const std::size_t offset{GetGuestMipmapLevelOffset(level)};
view_offset_map.insert({offset, {layer, level}});
}
break;
}
case SurfaceTarget::Texture1DArray:
case SurfaceTarget::Texture2DArray:
case SurfaceTarget::TextureCubemap:
case SurfaceTarget::TextureCubeArray: {
const std::size_t layer_size{GetGuestLayerSize()};
for (u32 level = 0; level < num_levels; ++level) {
const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
for (u32 layer = 0; layer < num_layers; ++layer) {
const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
const std::size_t offset{level_offset + layer_offset};
view_offset_map.insert({offset, {layer, level}});
}
}
break;
}
default:
UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
}
return view_offset_map;
}
bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
IsInBounds(view_params, layer, level);
}
bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
}
bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
if (view_params.target != SurfaceTarget::Texture3D) {
return true;
}
return view_params.depth == GetMipDepth(level);
}
bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
return layer + view_params.num_layers <= num_layers &&
level + view_params.num_levels <= num_levels;
}
std::size_t HasheableSurfaceParams::Hash() const {
std::size_t SurfaceParams::Hash() const {
return static_cast<std::size_t>(
Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
}
bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
bool SurfaceParams::operator==(const SurfaceParams& rhs) const {
return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
height, depth, pitch, unaligned_height, num_levels, pixel_format,
component_type, type, target) ==
@ -409,4 +326,27 @@ bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const
rhs.type, rhs.target);
}
std::string SurfaceParams::TargetName() const {
switch (target) {
case SurfaceTarget::Texture1D:
return "1D";
case SurfaceTarget::Texture2D:
return "2D";
case SurfaceTarget::Texture3D:
return "3D";
case SurfaceTarget::Texture1DArray:
return "1DArray";
case SurfaceTarget::Texture2DArray:
return "2DArray";
case SurfaceTarget::TextureCubemap:
return "Cube";
case SurfaceTarget::TextureCubeArray:
return "CubeArray";
default:
LOG_CRITICAL(HW_GPU, "Unimplemented surface_target={}", static_cast<u32>(target));
UNREACHABLE();
return fmt::format("TUK({})", static_cast<u32>(target));
}
}
} // namespace VideoCommon

View File

@ -6,50 +6,21 @@
#include <map>
#include "common/alignment.h"
#include "common/common_types.h"
#include "video_core/engines/fermi_2d.h"
#include "video_core/engines/maxwell_3d.h"
#include "video_core/surface.h"
#include "video_core/shader/shader_ir.h"
namespace VideoCommon {
class HasheableSurfaceParams {
public:
std::size_t Hash() const;
bool operator==(const HasheableSurfaceParams& rhs) const;
bool operator!=(const HasheableSurfaceParams& rhs) const {
return !operator==(rhs);
}
protected:
// Avoid creation outside of a managed environment.
HasheableSurfaceParams() = default;
bool is_tiled;
bool srgb_conversion;
u32 block_width;
u32 block_height;
u32 block_depth;
u32 tile_width_spacing;
u32 width;
u32 height;
u32 depth;
u32 pitch;
u32 unaligned_height;
u32 num_levels;
VideoCore::Surface::PixelFormat pixel_format;
VideoCore::Surface::ComponentType component_type;
VideoCore::Surface::SurfaceType type;
VideoCore::Surface::SurfaceTarget target;
};
class SurfaceParams final : public HasheableSurfaceParams {
class SurfaceParams {
public:
/// Creates SurfaceCachedParams from a texture configuration.
static SurfaceParams CreateForTexture(Core::System& system,
const Tegra::Texture::FullTextureInfo& config);
const Tegra::Texture::FullTextureInfo& config,
const VideoCommon::Shader::Sampler& entry);
/// Creates SurfaceCachedParams for a depth buffer configuration.
static SurfaceParams CreateForDepthBuffer(
@ -64,68 +35,33 @@ public:
static SurfaceParams CreateForFermiCopySurface(
const Tegra::Engines::Fermi2D::Regs::Surface& config);
bool IsTiled() const {
return is_tiled;
}
std::size_t Hash() const;
bool GetSrgbConversion() const {
return srgb_conversion;
}
bool operator==(const SurfaceParams& rhs) const;
u32 GetBlockWidth() const {
return block_width;
}
u32 GetTileWidthSpacing() const {
return tile_width_spacing;
}
u32 GetWidth() const {
return width;
}
u32 GetHeight() const {
return height;
}
u32 GetDepth() const {
return depth;
}
u32 GetPitch() const {
return pitch;
}
u32 GetNumLevels() const {
return num_levels;
}
VideoCore::Surface::PixelFormat GetPixelFormat() const {
return pixel_format;
}
VideoCore::Surface::ComponentType GetComponentType() const {
return component_type;
}
VideoCore::Surface::SurfaceTarget GetTarget() const {
return target;
}
VideoCore::Surface::SurfaceType GetType() const {
return type;
bool operator!=(const SurfaceParams& rhs) const {
return !operator==(rhs);
}
std::size_t GetGuestSizeInBytes() const {
return guest_size_in_bytes;
return GetInnerMemorySize(false, false, false);
}
std::size_t GetHostSizeInBytes() const {
std::size_t host_size_in_bytes;
if (IsPixelFormatASTC(pixel_format)) {
// ASTC is uncompressed in software, in emulated as RGBA8
host_size_in_bytes = static_cast<std::size_t>(Common::AlignUp(width, GetDefaultBlockWidth())) *
static_cast<std::size_t>(Common::AlignUp(height, GetDefaultBlockHeight())) *
static_cast<std::size_t>(depth) * 4ULL;
} else {
host_size_in_bytes = GetInnerMemorySize(true, false, false);
}
return host_size_in_bytes;
}
u32 GetNumLayers() const {
return num_layers;
u32 GetBlockAlignedWidth() const {
return Common::AlignUp(width, 64 / GetBytesPerPixel());
}
/// Returns the width of a given mipmap level.
@ -137,9 +73,6 @@ public:
/// Returns the depth of a given mipmap level.
u32 GetMipDepth(u32 level) const;
/// Returns true if these parameters are from a layered surface.
bool IsLayered() const;
/// Returns the block height of a given mipmap level.
u32 GetMipBlockHeight(u32 level) const;
@ -152,6 +85,9 @@ public:
/// Returns the offset in bytes in host memory (linear) of a given mipmap level.
std::size_t GetHostMipmapLevelOffset(u32 level) const;
/// Returns the size in bytes in guest memory of a given mipmap level.
std::size_t GetGuestMipmapSize(u32 level) const;
/// Returns the size in bytes in host memory (linear) of a given mipmap level.
std::size_t GetHostMipmapSize(u32 level) const;
@ -173,24 +109,30 @@ public:
/// Returns the bytes per pixel.
u32 GetBytesPerPixel() const;
/// Returns true if another surface can be familiar with this. This is a loosely defined term
/// that reflects the possibility of these two surface parameters potentially being part of a
/// bigger superset.
bool IsFamiliar(const SurfaceParams& view_params) const;
/// Returns true if the pixel format is a depth and/or stencil format.
bool IsPixelFormatZeta() const;
/// Creates a map that redirects an address difference to a layer and mipmap level.
std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
std::string TargetName() const;
/// Returns true if the passed surface view parameters is equal or a valid subset of this.
bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
bool is_tiled;
bool srgb_conversion;
bool is_layered;
u32 block_width;
u32 block_height;
u32 block_depth;
u32 tile_width_spacing;
u32 width;
u32 height;
u32 depth;
u32 pitch;
u32 unaligned_height;
u32 num_levels;
VideoCore::Surface::PixelFormat pixel_format;
VideoCore::Surface::ComponentType component_type;
VideoCore::Surface::SurfaceType type;
VideoCore::Surface::SurfaceTarget target;
private:
/// Calculates values that can be deduced from HasheableSurfaceParams.
void CalculateCachedValues();
/// Returns the size of a given mipmap level inside a layer.
std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const;
@ -200,19 +142,12 @@ private:
/// Returns the size of a layer
std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const;
/// Returns true if the passed view width and height match the size of this params in a given
/// mipmap level.
bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
std::size_t GetNumLayers() const {
return is_layered ? depth : 1;
}
/// Returns true if the passed view depth match the size of this params in a given mipmap level.
bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
/// Returns true if the passed view layers and mipmap levels are in bounds.
bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
std::size_t guest_size_in_bytes;
std::size_t host_size_in_bytes;
u32 num_layers;
/// Returns true if these parameters are from a layered surface.
bool IsLayered() const;
};
} // namespace VideoCommon

View File

@ -9,15 +9,15 @@
namespace VideoCommon {
std::size_t ViewKey::Hash() const {
std::size_t ViewParams::Hash() const {
return static_cast<std::size_t>(base_layer) ^ static_cast<std::size_t>(num_layers << 16) ^
(static_cast<std::size_t>(base_level) << 32) ^
(static_cast<std::size_t>(num_levels) << 48);
(static_cast<std::size_t>(base_level) << 24) ^
(static_cast<std::size_t>(num_levels) << 32) ^ (static_cast<std::size_t>(target) << 36);
}
bool ViewKey::operator==(const ViewKey& rhs) const {
return std::tie(base_layer, num_layers, base_level, num_levels) ==
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
bool ViewParams::operator==(const ViewParams& rhs) const {
return std::tie(base_layer, num_layers, base_level, num_levels, target) ==
std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels, rhs.target);
}
} // namespace VideoCommon

View File

@ -7,18 +7,45 @@
#include <functional>
#include "common/common_types.h"
#include "video_core/surface.h"
#include "video_core/texture_cache/surface_params.h"
namespace VideoCommon {
struct ViewKey {
struct ViewParams {
std::size_t Hash() const;
bool operator==(const ViewKey& rhs) const;
bool operator==(const ViewParams& rhs) const;
u32 base_layer{};
u32 num_layers{};
u32 base_level{};
u32 num_levels{};
VideoCore::Surface::SurfaceTarget target;
bool IsLayered() const {
switch (target) {
case VideoCore::Surface::SurfaceTarget::Texture1DArray:
case VideoCore::Surface::SurfaceTarget::Texture2DArray:
case VideoCore::Surface::SurfaceTarget::TextureCubemap:
case VideoCore::Surface::SurfaceTarget::TextureCubeArray:
return true;
default:
return false;
}
}
};
class ViewBase {
public:
ViewBase(const ViewParams& params) : params{params} {}
~ViewBase() = default;
const ViewParams& GetViewParams() const {
return params;
}
protected:
ViewParams params;
};
} // namespace VideoCommon
@ -26,8 +53,8 @@ struct ViewKey {
namespace std {
template <>
struct hash<VideoCommon::ViewKey> {
std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
struct hash<VideoCommon::ViewParams> {
std::size_t operator()(const VideoCommon::ViewParams& k) const noexcept {
return k.Hash();
}
};