summaryrefslogtreecommitdiffstats
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
authorFernando Sahmkow <fsahmkow27@gmail.com>2019-05-07 16:56:45 +0200
committerReinUsesLisp <reinuseslisp@airmail.cc>2019-06-21 02:36:12 +0200
commit3d471e732d688c20aef73a506bdb6126002d3193 (patch)
treefc10f7750306fd8b17707473b8bc1cb1624d0cda /src/video_core/texture_cache
parentAdd OGLTextureView (diff)
downloadyuzu-3d471e732d688c20aef73a506bdb6126002d3193.tar
yuzu-3d471e732d688c20aef73a506bdb6126002d3193.tar.gz
yuzu-3d471e732d688c20aef73a506bdb6126002d3193.tar.bz2
yuzu-3d471e732d688c20aef73a506bdb6126002d3193.tar.lz
yuzu-3d471e732d688c20aef73a506bdb6126002d3193.tar.xz
yuzu-3d471e732d688c20aef73a506bdb6126002d3193.tar.zst
yuzu-3d471e732d688c20aef73a506bdb6126002d3193.zip
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/copy_params.h25
-rw-r--r--src/video_core/texture_cache/surface_base.cpp108
-rw-r--r--src/video_core/texture_cache/surface_base.h297
-rw-r--r--src/video_core/texture_cache/surface_params.cpp210
-rw-r--r--src/video_core/texture_cache/surface_params.h159
-rw-r--r--src/video_core/texture_cache/surface_view.cpp12
-rw-r--r--src/video_core/texture_cache/surface_view.h35
7 files changed, 466 insertions, 380 deletions
diff --git a/src/video_core/texture_cache/copy_params.h b/src/video_core/texture_cache/copy_params.h
new file mode 100644
index 000000000..75c2b1f05
--- /dev/null
+++ b/src/video_core/texture_cache/copy_params.h
@@ -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
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index d0779b502..5273fcb44 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -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()};
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
index eed8dc59d..5fd7add0a 100644
--- a/src/video_core/texture_cache/surface_base.h
+++ b/src/video_core/texture_cache/surface_base.h
@@ -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();
+ std::size_t GetSizeInBytes() const {
+ return memory_size;
}
- void Unregister() {
- ASSERT(is_registered);
- is_registered = false;
+ std::size_t GetHostSizeInBytes() const {
+ return host_memory_size;
}
- bool IsRegistered() const {
- return is_registered;
+ std::size_t GetMipmapSize(const u32 level) const {
+ return mipmap_sizes[level];
}
- std::size_t GetSizeInBytes() const {
- return params.GetGuestSizeInBytes();
+ 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 {};
}
- u8* GetStagingBufferLevelData(u32 level) {
- return staging_buffer.data() + params.GetHostMipmapLevelOffset(level);
+ 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;
-
-private:
GPUVAddr gpu_addr{};
- VAddr cpu_addr{};
- u8* host_ptr{};
- CacheAddr cache_addr{};
- bool is_registered{};
+ 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;
- std::vector<u8> staging_buffer;
+private:
+ 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;
+ void MarkAsProtected(const bool is_protected) {
+ this->is_protected = is_protected;
+ }
- if (!params.IsViewValid(view_params, layer, level)) {
- return {};
- }
+ void MarkAsPicked(const bool is_picked) {
+ this->is_picked = is_picked;
+ }
- return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
+ bool IsModified() const {
+ return is_modified;
}
- void MarkAsModified(bool is_modified_) {
- is_modified = is_modified_;
- if (is_modified_) {
- modification_tick = texture_cache.Tick();
- }
+ bool IsProtected() const {
+ return is_protected;
}
- TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
- TView* view{TryGetView(view_addr, view_params)};
- ASSERT(view != nullptr);
- return view;
+ bool IsRegistered() const {
+ return is_registered;
}
- bool IsModified() const {
- return is_modified;
+ 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{};
};
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index d1f8c53d5..d9052152c 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -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);
-}
-
-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);
+ return GetLayerSize(as_host_size, uncompressed) * (layer_only ? 1U : depth);
}
-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
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
index 77dc0ba66..ec8efa210 100644
--- a/src/video_core/texture_cache/surface_params.h
+++ b/src/video_core/texture_cache/surface_params.h
@@ -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;
- }
-
- bool GetSrgbConversion() const {
- return srgb_conversion;
- }
-
- 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;
- }
+ std::size_t Hash() const;
- VideoCore::Surface::SurfaceTarget GetTarget() const {
- return target;
- }
+ bool operator==(const SurfaceParams& rhs) const;
- 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;
-
- /// 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 GetNumLayers() const {
+ return is_layered ? depth : 1;
+ }
- 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
diff --git a/src/video_core/texture_cache/surface_view.cpp b/src/video_core/texture_cache/surface_view.cpp
index 5f4cdbb1c..467696a4c 100644
--- a/src/video_core/texture_cache/surface_view.cpp
+++ b/src/video_core/texture_cache/surface_view.cpp
@@ -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
diff --git a/src/video_core/texture_cache/surface_view.h b/src/video_core/texture_cache/surface_view.h
index e73d8f6ae..c122800a6 100644
--- a/src/video_core/texture_cache/surface_view.h
+++ b/src/video_core/texture_cache/surface_view.h
@@ -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();
}
};