summaryrefslogtreecommitdiff
path: root/src/video_core/texture_cache
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/texture_cache')
-rw-r--r--src/video_core/texture_cache/surface_base.cpp118
-rw-r--r--src/video_core/texture_cache/surface_base.h172
-rw-r--r--src/video_core/texture_cache/surface_params.cpp412
-rw-r--r--src/video_core/texture_cache/surface_params.h229
-rw-r--r--src/video_core/texture_cache/surface_view.cpp23
-rw-r--r--src/video_core/texture_cache/surface_view.h35
-rw-r--r--src/video_core/texture_cache/texture_cache.h282
-rw-r--r--src/video_core/texture_cache/texture_cache_contextless.h93
8 files changed, 1364 insertions, 0 deletions
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
new file mode 100644
index 000000000..8680485b4
--- /dev/null
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -0,0 +1,118 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include "common/assert.h"
6#include "common/common_types.h"
7#include "video_core/morton.h"
8#include "video_core/texture_cache/surface_base.h"
9#include "video_core/texture_cache/surface_params.h"
10#include "video_core/textures/convert.h"
11
12namespace VideoCommon {
13
14using Tegra::Texture::ConvertFromGuestToHost;
15using VideoCore::MortonSwizzleMode;
16
17namespace {
18void SwizzleFunc(MortonSwizzleMode mode, u8* memory, const SurfaceParams& params, u8* buffer,
19 u32 level) {
20 const u32 width{params.GetMipWidth(level)};
21 const u32 height{params.GetMipHeight(level)};
22 const u32 block_height{params.GetMipBlockHeight(level)};
23 const u32 block_depth{params.GetMipBlockDepth(level)};
24
25 std::size_t guest_offset{params.GetGuestMipmapLevelOffset(level)};
26 if (params.IsLayered()) {
27 std::size_t host_offset{0};
28 const std::size_t guest_stride = params.GetGuestLayerSize();
29 const std::size_t host_stride = params.GetHostLayerSize(level);
30 for (u32 layer = 0; layer < params.GetNumLayers(); layer++) {
31 MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
32 1, params.GetTileWidthSpacing(), buffer + host_offset,
33 memory + guest_offset);
34 guest_offset += guest_stride;
35 host_offset += host_stride;
36 }
37 } else {
38 MortonSwizzle(mode, params.GetPixelFormat(), width, block_height, height, block_depth,
39 params.GetMipDepth(level), params.GetTileWidthSpacing(), buffer,
40 memory + guest_offset);
41 }
42}
43} // Anonymous namespace
44
45SurfaceBaseImpl::SurfaceBaseImpl(const SurfaceParams& params) : params{params} {
46 staging_buffer.resize(params.GetHostSizeInBytes());
47}
48
49SurfaceBaseImpl::~SurfaceBaseImpl() = default;
50
51void SurfaceBaseImpl::LoadBuffer() {
52 if (params.IsTiled()) {
53 ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {} on texture target {}",
54 params.GetBlockWidth(), static_cast<u32>(params.GetTarget()));
55 for (u32 level = 0; level < params.GetNumLevels(); ++level) {
56 u8* const buffer{GetStagingBufferLevelData(level)};
57 SwizzleFunc(MortonSwizzleMode::MortonToLinear, host_ptr, params, buffer, level);
58 }
59 } else {
60 ASSERT_MSG(params.GetNumLevels() == 1, "Linear mipmap loading is not implemented");
61 const u32 bpp{GetFormatBpp(params.GetPixelFormat()) / CHAR_BIT};
62 const u32 block_width{params.GetDefaultBlockWidth()};
63 const u32 block_height{params.GetDefaultBlockHeight()};
64 const u32 width{(params.GetWidth() + block_width - 1) / block_width};
65 const u32 height{(params.GetHeight() + block_height - 1) / block_height};
66 const u32 copy_size{width * bpp};
67 if (params.GetPitch() == copy_size) {
68 std::memcpy(staging_buffer.data(), host_ptr, params.GetHostSizeInBytes());
69 } else {
70 const u8* start{host_ptr};
71 u8* write_to{staging_buffer.data()};
72 for (u32 h = height; h > 0; --h) {
73 std::memcpy(write_to, start, copy_size);
74 start += params.GetPitch();
75 write_to += copy_size;
76 }
77 }
78 }
79
80 for (u32 level = 0; level < params.GetNumLevels(); ++level) {
81 ConvertFromGuestToHost(GetStagingBufferLevelData(level), params.GetPixelFormat(),
82 params.GetMipWidth(level), params.GetMipHeight(level),
83 params.GetMipDepth(level), true, true);
84 }
85}
86
87void SurfaceBaseImpl::FlushBuffer() {
88 if (params.IsTiled()) {
89 ASSERT_MSG(params.GetBlockWidth() == 1, "Block width is defined as {}",
90 params.GetBlockWidth());
91 for (u32 level = 0; level < params.GetNumLevels(); ++level) {
92 u8* const buffer = GetStagingBufferLevelData(level);
93 SwizzleFunc(MortonSwizzleMode::LinearToMorton, GetHostPtr(), params, buffer, level);
94 }
95 } else {
96 UNIMPLEMENTED();
97 /*
98 ASSERT(params.GetTarget() == SurfaceTarget::Texture2D);
99 ASSERT(params.GetNumLevels() == 1);
100
101 const u32 bpp{params.GetFormatBpp() / 8};
102 const u32 copy_size{params.GetWidth() * bpp};
103 if (params.GetPitch() == copy_size) {
104 std::memcpy(host_ptr, staging_buffer.data(), GetSizeInBytes());
105 } else {
106 u8* start{host_ptr};
107 const u8* read_to{staging_buffer.data()};
108 for (u32 h = params.GetHeight(); h > 0; --h) {
109 std::memcpy(start, read_to, copy_size);
110 start += params.GetPitch();
111 read_to += copy_size;
112 }
113 }
114 */
115 }
116}
117
118} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h
new file mode 100644
index 000000000..d0142a9e6
--- /dev/null
+++ b/src/video_core/texture_cache/surface_base.h
@@ -0,0 +1,172 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <unordered_map>
8
9#include "common/assert.h"
10#include "common/common_types.h"
11#include "video_core/gpu.h"
12#include "video_core/texture_cache/surface_params.h"
13#include "video_core/texture_cache/surface_view.h"
14
15namespace VideoCommon {
16
17class SurfaceBaseImpl {
18public:
19 void LoadBuffer();
20
21 void FlushBuffer();
22
23 GPUVAddr GetGpuAddr() const {
24 ASSERT(is_registered);
25 return gpu_addr;
26 }
27
28 VAddr GetCpuAddr() const {
29 ASSERT(is_registered);
30 return cpu_addr;
31 }
32
33 u8* GetHostPtr() const {
34 ASSERT(is_registered);
35 return host_ptr;
36 }
37
38 CacheAddr GetCacheAddr() const {
39 ASSERT(is_registered);
40 return cache_addr;
41 }
42
43 const SurfaceParams& GetSurfaceParams() const {
44 return params;
45 }
46
47 void Register(GPUVAddr gpu_addr_, VAddr cpu_addr_, u8* host_ptr_) {
48 ASSERT(!is_registered);
49 is_registered = true;
50 gpu_addr = gpu_addr_;
51 cpu_addr = cpu_addr_;
52 host_ptr = host_ptr_;
53 cache_addr = ToCacheAddr(host_ptr_);
54 DecorateSurfaceName();
55 }
56
57 void Unregister() {
58 ASSERT(is_registered);
59 is_registered = false;
60 }
61
62 bool IsRegistered() const {
63 return is_registered;
64 }
65
66 std::size_t GetSizeInBytes() const {
67 return params.GetGuestSizeInBytes();
68 }
69
70 u8* GetStagingBufferLevelData(u32 level) {
71 return staging_buffer.data() + params.GetHostMipmapLevelOffset(level);
72 }
73
74protected:
75 explicit SurfaceBaseImpl(const SurfaceParams& params);
76 ~SurfaceBaseImpl(); // non-virtual is intended
77
78 virtual void DecorateSurfaceName() = 0;
79
80 const SurfaceParams params;
81
82private:
83 GPUVAddr gpu_addr{};
84 VAddr cpu_addr{};
85 u8* host_ptr{};
86 CacheAddr cache_addr{};
87 bool is_registered{};
88
89 std::vector<u8> staging_buffer;
90};
91
92template <typename TTextureCache, typename TView, typename TExecutionContext>
93class SurfaceBase : public SurfaceBaseImpl {
94 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
95
96public:
97 virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
98
99 virtual TExecutionContext DownloadTexture(TExecutionContext exctx) = 0;
100
101 TView* TryGetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
102 if (view_addr < GetGpuAddr() || !params.IsFamiliar(view_params)) {
103 // It can't be a view if it's in a prior address.
104 return {};
105 }
106
107 const auto relative_offset{static_cast<u64>(view_addr - GetGpuAddr())};
108 const auto it{view_offset_map.find(relative_offset)};
109 if (it == view_offset_map.end()) {
110 // Couldn't find an aligned view.
111 return {};
112 }
113 const auto [layer, level] = it->second;
114
115 if (!params.IsViewValid(view_params, layer, level)) {
116 return {};
117 }
118
119 return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
120 }
121
122 void MarkAsModified(bool is_modified_) {
123 is_modified = is_modified_;
124 if (is_modified_) {
125 modification_tick = texture_cache.Tick();
126 }
127 }
128
129 TView* GetView(GPUVAddr view_addr, const SurfaceParams& view_params) {
130 TView* view{TryGetView(view_addr, view_params)};
131 ASSERT(view != nullptr);
132 return view;
133 }
134
135 bool IsModified() const {
136 return is_modified;
137 }
138
139 u64 GetModificationTick() const {
140 return modification_tick;
141 }
142
143protected:
144 explicit SurfaceBase(TTextureCache& texture_cache, const SurfaceParams& params)
145 : SurfaceBaseImpl{params}, texture_cache{texture_cache},
146 view_offset_map{params.CreateViewOffsetMap()} {}
147
148 ~SurfaceBase() = default;
149
150 virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
151
152private:
153 TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
154 const ViewKey key{base_layer, num_layers, base_level, num_levels};
155 const auto [entry, is_cache_miss] = views.try_emplace(key);
156 auto& view{entry->second};
157 if (is_cache_miss) {
158 view = CreateView(key);
159 }
160 return view.get();
161 }
162
163 TTextureCache& texture_cache;
164 const std::map<u64, std::pair<u32, u32>> view_offset_map;
165
166 std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
167
168 bool is_modified{};
169 u64 modification_tick{};
170};
171
172} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
new file mode 100644
index 000000000..d1f8c53d5
--- /dev/null
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -0,0 +1,412 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <map>
6
7#include "common/cityhash.h"
8#include "common/alignment.h"
9#include "core/core.h"
10#include "video_core/surface.h"
11#include "video_core/texture_cache/surface_params.h"
12#include "video_core/textures/decoders.h"
13
14namespace VideoCommon {
15
16using VideoCore::Surface::ComponentTypeFromDepthFormat;
17using VideoCore::Surface::ComponentTypeFromRenderTarget;
18using VideoCore::Surface::ComponentTypeFromTexture;
19using VideoCore::Surface::PixelFormatFromDepthFormat;
20using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
21using VideoCore::Surface::PixelFormatFromTextureFormat;
22using VideoCore::Surface::SurfaceTarget;
23using VideoCore::Surface::SurfaceTargetFromTextureType;
24
25namespace {
26constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
27 return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
28}
29} // Anonymous namespace
30
31SurfaceParams SurfaceParams::CreateForTexture(Core::System& system,
32 const Tegra::Texture::FullTextureInfo& config) {
33 SurfaceParams params;
34 params.is_tiled = config.tic.IsTiled();
35 params.srgb_conversion = config.tic.IsSrgbConversionEnabled();
36 params.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
37 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
38 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
39 params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1;
40 params.pixel_format = PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(),
41 params.srgb_conversion);
42 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
43 params.type = GetFormatType(params.pixel_format);
44 params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
45 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
46 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
47 params.depth = config.tic.Depth();
48 if (params.target == SurfaceTarget::TextureCubemap ||
49 params.target == SurfaceTarget::TextureCubeArray) {
50 params.depth *= 6;
51 }
52 params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
53 params.unaligned_height = config.tic.Height();
54 params.num_levels = config.tic.max_mip_level + 1;
55
56 params.CalculateCachedValues();
57 return params;
58}
59
60SurfaceParams SurfaceParams::CreateForDepthBuffer(
61 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
62 u32 block_width, u32 block_height, u32 block_depth,
63 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
64 SurfaceParams params;
65 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
66 params.srgb_conversion = false;
67 params.block_width = 1 << std::min(block_width, 5U);
68 params.block_height = 1 << std::min(block_height, 5U);
69 params.block_depth = 1 << std::min(block_depth, 5U);
70 params.tile_width_spacing = 1;
71 params.pixel_format = PixelFormatFromDepthFormat(format);
72 params.component_type = ComponentTypeFromDepthFormat(format);
73 params.type = GetFormatType(params.pixel_format);
74 params.width = zeta_width;
75 params.height = zeta_height;
76 params.unaligned_height = zeta_height;
77 params.target = SurfaceTarget::Texture2D;
78 params.depth = 1;
79 params.num_levels = 1;
80
81 params.CalculateCachedValues();
82 return params;
83}
84
85SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
86 const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
87 SurfaceParams params;
88 params.is_tiled =
89 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
90 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
91 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
92 params.block_width = 1 << config.memory_layout.block_width;
93 params.block_height = 1 << config.memory_layout.block_height;
94 params.block_depth = 1 << config.memory_layout.block_depth;
95 params.tile_width_spacing = 1;
96 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
97 params.component_type = ComponentTypeFromRenderTarget(config.format);
98 params.type = GetFormatType(params.pixel_format);
99 if (params.is_tiled) {
100 params.width = config.width;
101 } else {
102 const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
103 params.pitch = config.width;
104 params.width = params.pitch / bpp;
105 }
106 params.height = config.height;
107 params.depth = 1;
108 params.unaligned_height = config.height;
109 params.target = SurfaceTarget::Texture2D;
110 params.num_levels = 1;
111
112 params.CalculateCachedValues();
113 return params;
114}
115
116SurfaceParams SurfaceParams::CreateForFermiCopySurface(
117 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
118 SurfaceParams params{};
119 params.is_tiled = !config.linear;
120 params.srgb_conversion = config.format == Tegra::RenderTargetFormat::BGRA8_SRGB ||
121 config.format == Tegra::RenderTargetFormat::RGBA8_SRGB;
122 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
123 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
124 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
125 params.tile_width_spacing = 1;
126 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
127 params.component_type = ComponentTypeFromRenderTarget(config.format);
128 params.type = GetFormatType(params.pixel_format);
129 params.width = config.width;
130 params.height = config.height;
131 params.unaligned_height = config.height;
132 // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
133 params.target = SurfaceTarget::Texture2D;
134 params.depth = 1;
135 params.num_levels = 1;
136
137 params.CalculateCachedValues();
138 return params;
139}
140
141u32 SurfaceParams::GetMipWidth(u32 level) const {
142 return std::max(1U, width >> level);
143}
144
145u32 SurfaceParams::GetMipHeight(u32 level) const {
146 return std::max(1U, height >> level);
147}
148
149u32 SurfaceParams::GetMipDepth(u32 level) const {
150 return IsLayered() ? depth : std::max(1U, depth >> level);
151}
152
153bool SurfaceParams::IsLayered() const {
154 switch (target) {
155 case SurfaceTarget::Texture1DArray:
156 case SurfaceTarget::Texture2DArray:
157 case SurfaceTarget::TextureCubemap:
158 case SurfaceTarget::TextureCubeArray:
159 return true;
160 default:
161 return false;
162 }
163}
164
165u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
166 // Auto block resizing algorithm from:
167 // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
168 if (level == 0) {
169 return this->block_height;
170 }
171
172 const u32 height{GetMipHeight(level)};
173 const u32 default_block_height{GetDefaultBlockHeight()};
174 const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height};
175 u32 block_height = 16;
176 while (block_height > 1 && blocks_in_y <= block_height * 4) {
177 block_height >>= 1;
178 }
179 return block_height;
180}
181
182u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
183 if (level == 0) {
184 return this->block_depth;
185 }
186 if (IsLayered()) {
187 return 1;
188 }
189
190 const u32 depth{GetMipDepth(level)};
191 u32 block_depth = 32;
192 while (block_depth > 1 && depth * 2 <= block_depth) {
193 block_depth >>= 1;
194 }
195
196 if (block_depth == 32 && GetMipBlockHeight(level) >= 4) {
197 return 16;
198 }
199
200 return block_depth;
201}
202
203std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
204 std::size_t offset = 0;
205 for (u32 i = 0; i < level; i++) {
206 offset += GetInnerMipmapMemorySize(i, false, false);
207 }
208 return offset;
209}
210
211std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
212 std::size_t offset = 0;
213 for (u32 i = 0; i < level; i++) {
214 offset += GetInnerMipmapMemorySize(i, true, false) * GetNumLayers();
215 }
216 return offset;
217}
218
219std::size_t SurfaceParams::GetHostMipmapSize(u32 level) const {
220 return GetInnerMipmapMemorySize(level, true, false) * GetNumLayers();
221}
222
223std::size_t SurfaceParams::GetGuestLayerSize() const {
224 return GetLayerSize(false, false);
225}
226
227std::size_t SurfaceParams::GetLayerSize(bool as_host_size, bool uncompressed) const {
228 std::size_t size = 0;
229 for (u32 level = 0; level < num_levels; ++level) {
230 size += GetInnerMipmapMemorySize(level, as_host_size, uncompressed);
231 }
232 if (is_tiled && (IsLayered() || target == SurfaceTarget::Texture3D)) {
233 return Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
234 }
235 return size;
236}
237
238std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
239 ASSERT(target != SurfaceTarget::Texture3D);
240 return GetInnerMipmapMemorySize(level, true, false);
241}
242
243u32 SurfaceParams::GetDefaultBlockWidth() const {
244 return VideoCore::Surface::GetDefaultBlockWidth(pixel_format);
245}
246
247u32 SurfaceParams::GetDefaultBlockHeight() const {
248 return VideoCore::Surface::GetDefaultBlockHeight(pixel_format);
249}
250
251u32 SurfaceParams::GetBitsPerPixel() const {
252 return VideoCore::Surface::GetFormatBpp(pixel_format);
253}
254
255u32 SurfaceParams::GetBytesPerPixel() const {
256 return VideoCore::Surface::GetBytesPerPixel(pixel_format);
257}
258
259bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
260 if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
261 std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
262 view_params.component_type, view_params.type)) {
263 return false;
264 }
265
266 const SurfaceTarget view_target{view_params.target};
267 if (view_target == target) {
268 return true;
269 }
270
271 switch (target) {
272 case SurfaceTarget::Texture1D:
273 case SurfaceTarget::Texture2D:
274 case SurfaceTarget::Texture3D:
275 return false;
276 case SurfaceTarget::Texture1DArray:
277 return view_target == SurfaceTarget::Texture1D;
278 case SurfaceTarget::Texture2DArray:
279 return view_target == SurfaceTarget::Texture2D;
280 case SurfaceTarget::TextureCubemap:
281 return view_target == SurfaceTarget::Texture2D ||
282 view_target == SurfaceTarget::Texture2DArray;
283 case SurfaceTarget::TextureCubeArray:
284 return view_target == SurfaceTarget::Texture2D ||
285 view_target == SurfaceTarget::Texture2DArray ||
286 view_target == SurfaceTarget::TextureCubemap;
287 default:
288 UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
289 return false;
290 }
291}
292
293bool SurfaceParams::IsPixelFormatZeta() const {
294 return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
295 pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
296}
297
298void SurfaceParams::CalculateCachedValues() {
299 switch (target) {
300 case SurfaceTarget::Texture1D:
301 case SurfaceTarget::Texture2D:
302 case SurfaceTarget::Texture3D:
303 num_layers = 1;
304 break;
305 case SurfaceTarget::Texture1DArray:
306 case SurfaceTarget::Texture2DArray:
307 case SurfaceTarget::TextureCubemap:
308 case SurfaceTarget::TextureCubeArray:
309 num_layers = depth;
310 break;
311 default:
312 UNREACHABLE();
313 }
314
315 guest_size_in_bytes = GetInnerMemorySize(false, false, false);
316
317 if (IsPixelFormatASTC(pixel_format)) {
318 // ASTC is uncompressed in software, in emulated as RGBA8
319 host_size_in_bytes = static_cast<std::size_t>(width) * static_cast<std::size_t>(height) *
320 static_cast<std::size_t>(depth) * 4ULL;
321 } else {
322 host_size_in_bytes = GetInnerMemorySize(true, false, false);
323 }
324}
325
326std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size,
327 bool uncompressed) const {
328 const bool tiled{as_host_size ? false : is_tiled};
329 const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), GetDefaultBlockWidth())};
330 const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), GetDefaultBlockHeight())};
331 const u32 depth{target == SurfaceTarget::Texture3D ? GetMipDepth(level) : 1U};
332 return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(), width, height, depth,
333 GetMipBlockHeight(level), GetMipBlockDepth(level));
334}
335
336std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
337 bool uncompressed) const {
338 return GetLayerSize(as_host_size, uncompressed) * (layer_only ? 1U : num_layers);
339}
340
341std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
342 std::map<u64, std::pair<u32, u32>> view_offset_map;
343 switch (target) {
344 case SurfaceTarget::Texture1D:
345 case SurfaceTarget::Texture2D:
346 case SurfaceTarget::Texture3D: {
347 // TODO(Rodrigo): Add layer iterations for 3D textures
348 constexpr u32 layer = 0;
349 for (u32 level = 0; level < num_levels; ++level) {
350 const std::size_t offset{GetGuestMipmapLevelOffset(level)};
351 view_offset_map.insert({offset, {layer, level}});
352 }
353 break;
354 }
355 case SurfaceTarget::Texture1DArray:
356 case SurfaceTarget::Texture2DArray:
357 case SurfaceTarget::TextureCubemap:
358 case SurfaceTarget::TextureCubeArray: {
359 const std::size_t layer_size{GetGuestLayerSize()};
360 for (u32 level = 0; level < num_levels; ++level) {
361 const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
362 for (u32 layer = 0; layer < num_layers; ++layer) {
363 const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
364 const std::size_t offset{level_offset + layer_offset};
365 view_offset_map.insert({offset, {layer, level}});
366 }
367 }
368 break;
369 }
370 default:
371 UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
372 }
373 return view_offset_map;
374}
375
376bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
377 return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
378 IsInBounds(view_params, layer, level);
379}
380
381bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
382 return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
383}
384
385bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
386 if (view_params.target != SurfaceTarget::Texture3D) {
387 return true;
388 }
389 return view_params.depth == GetMipDepth(level);
390}
391
392bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
393 return layer + view_params.num_layers <= num_layers &&
394 level + view_params.num_levels <= num_levels;
395}
396
397std::size_t HasheableSurfaceParams::Hash() const {
398 return static_cast<std::size_t>(
399 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
400}
401
402bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
403 return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
404 height, depth, pitch, unaligned_height, num_levels, pixel_format,
405 component_type, type, target) ==
406 std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
407 rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
408 rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type,
409 rhs.type, rhs.target);
410}
411
412} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
new file mode 100644
index 000000000..77dc0ba66
--- /dev/null
+++ b/src/video_core/texture_cache/surface_params.h
@@ -0,0 +1,229 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <map>
8
9#include "common/common_types.h"
10#include "video_core/engines/fermi_2d.h"
11#include "video_core/engines/maxwell_3d.h"
12#include "video_core/surface.h"
13
14namespace VideoCommon {
15
16class HasheableSurfaceParams {
17public:
18 std::size_t Hash() const;
19
20 bool operator==(const HasheableSurfaceParams& rhs) const;
21
22 bool operator!=(const HasheableSurfaceParams& rhs) const {
23 return !operator==(rhs);
24 }
25
26protected:
27 // Avoid creation outside of a managed environment.
28 HasheableSurfaceParams() = default;
29
30 bool is_tiled;
31 bool srgb_conversion;
32 u32 block_width;
33 u32 block_height;
34 u32 block_depth;
35 u32 tile_width_spacing;
36 u32 width;
37 u32 height;
38 u32 depth;
39 u32 pitch;
40 u32 unaligned_height;
41 u32 num_levels;
42 VideoCore::Surface::PixelFormat pixel_format;
43 VideoCore::Surface::ComponentType component_type;
44 VideoCore::Surface::SurfaceType type;
45 VideoCore::Surface::SurfaceTarget target;
46};
47
48class SurfaceParams final : public HasheableSurfaceParams {
49public:
50 /// Creates SurfaceCachedParams from a texture configuration.
51 static SurfaceParams CreateForTexture(Core::System& system,
52 const Tegra::Texture::FullTextureInfo& config);
53
54 /// Creates SurfaceCachedParams for a depth buffer configuration.
55 static SurfaceParams CreateForDepthBuffer(
56 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
57 u32 block_width, u32 block_height, u32 block_depth,
58 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
59
60 /// Creates SurfaceCachedParams from a framebuffer configuration.
61 static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
62
63 /// Creates SurfaceCachedParams from a Fermi2D surface configuration.
64 static SurfaceParams CreateForFermiCopySurface(
65 const Tegra::Engines::Fermi2D::Regs::Surface& config);
66
67 bool IsTiled() const {
68 return is_tiled;
69 }
70
71 bool GetSrgbConversion() const {
72 return srgb_conversion;
73 }
74
75 u32 GetBlockWidth() const {
76 return block_width;
77 }
78
79 u32 GetTileWidthSpacing() const {
80 return tile_width_spacing;
81 }
82
83 u32 GetWidth() const {
84 return width;
85 }
86
87 u32 GetHeight() const {
88 return height;
89 }
90
91 u32 GetDepth() const {
92 return depth;
93 }
94
95 u32 GetPitch() const {
96 return pitch;
97 }
98
99 u32 GetNumLevels() const {
100 return num_levels;
101 }
102
103 VideoCore::Surface::PixelFormat GetPixelFormat() const {
104 return pixel_format;
105 }
106
107 VideoCore::Surface::ComponentType GetComponentType() const {
108 return component_type;
109 }
110
111 VideoCore::Surface::SurfaceTarget GetTarget() const {
112 return target;
113 }
114
115 VideoCore::Surface::SurfaceType GetType() const {
116 return type;
117 }
118
119 std::size_t GetGuestSizeInBytes() const {
120 return guest_size_in_bytes;
121 }
122
123 std::size_t GetHostSizeInBytes() const {
124 return host_size_in_bytes;
125 }
126
127 u32 GetNumLayers() const {
128 return num_layers;
129 }
130
131 /// Returns the width of a given mipmap level.
132 u32 GetMipWidth(u32 level) const;
133
134 /// Returns the height of a given mipmap level.
135 u32 GetMipHeight(u32 level) const;
136
137 /// Returns the depth of a given mipmap level.
138 u32 GetMipDepth(u32 level) const;
139
140 /// Returns true if these parameters are from a layered surface.
141 bool IsLayered() const;
142
143 /// Returns the block height of a given mipmap level.
144 u32 GetMipBlockHeight(u32 level) const;
145
146 /// Returns the block depth of a given mipmap level.
147 u32 GetMipBlockDepth(u32 level) const;
148
149 /// Returns the offset in bytes in guest memory of a given mipmap level.
150 std::size_t GetGuestMipmapLevelOffset(u32 level) const;
151
152 /// Returns the offset in bytes in host memory (linear) of a given mipmap level.
153 std::size_t GetHostMipmapLevelOffset(u32 level) const;
154
155 /// Returns the size in bytes in host memory (linear) of a given mipmap level.
156 std::size_t GetHostMipmapSize(u32 level) const;
157
158 /// Returns the size of a layer in bytes in guest memory.
159 std::size_t GetGuestLayerSize() const;
160
161 /// Returns the size of a layer in bytes in host memory for a given mipmap level.
162 std::size_t GetHostLayerSize(u32 level) const;
163
164 /// Returns the default block width.
165 u32 GetDefaultBlockWidth() const;
166
167 /// Returns the default block height.
168 u32 GetDefaultBlockHeight() const;
169
170 /// Returns the bits per pixel.
171 u32 GetBitsPerPixel() const;
172
173 /// Returns the bytes per pixel.
174 u32 GetBytesPerPixel() const;
175
176 /// Returns true if another surface can be familiar with this. This is a loosely defined term
177 /// that reflects the possibility of these two surface parameters potentially being part of a
178 /// bigger superset.
179 bool IsFamiliar(const SurfaceParams& view_params) const;
180
181 /// Returns true if the pixel format is a depth and/or stencil format.
182 bool IsPixelFormatZeta() const;
183
184 /// Creates a map that redirects an address difference to a layer and mipmap level.
185 std::map<u64, std::pair<u32, u32>> CreateViewOffsetMap() const;
186
187 /// Returns true if the passed surface view parameters is equal or a valid subset of this.
188 bool IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const;
189
190private:
191 /// Calculates values that can be deduced from HasheableSurfaceParams.
192 void CalculateCachedValues();
193
194 /// Returns the size of a given mipmap level inside a layer.
195 std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool uncompressed) const;
196
197 /// Returns the size of all mipmap levels and aligns as needed.
198 std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
199
200 /// Returns the size of a layer
201 std::size_t GetLayerSize(bool as_host_size, bool uncompressed) const;
202
203 /// Returns true if the passed view width and height match the size of this params in a given
204 /// mipmap level.
205 bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
206
207 /// Returns true if the passed view depth match the size of this params in a given mipmap level.
208 bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
209
210 /// Returns true if the passed view layers and mipmap levels are in bounds.
211 bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
212
213 std::size_t guest_size_in_bytes;
214 std::size_t host_size_in_bytes;
215 u32 num_layers;
216};
217
218} // namespace VideoCommon
219
220namespace std {
221
222template <>
223struct hash<VideoCommon::SurfaceParams> {
224 std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
225 return k.Hash();
226 }
227};
228
229} // namespace std
diff --git a/src/video_core/texture_cache/surface_view.cpp b/src/video_core/texture_cache/surface_view.cpp
new file mode 100644
index 000000000..5f4cdbb1c
--- /dev/null
+++ b/src/video_core/texture_cache/surface_view.cpp
@@ -0,0 +1,23 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <tuple>
6
7#include "common/common_types.h"
8#include "video_core/texture_cache/surface_view.h"
9
10namespace VideoCommon {
11
12std::size_t ViewKey::Hash() const {
13 return static_cast<std::size_t>(base_layer) ^ static_cast<std::size_t>(num_layers << 16) ^
14 (static_cast<std::size_t>(base_level) << 32) ^
15 (static_cast<std::size_t>(num_levels) << 48);
16}
17
18bool ViewKey::operator==(const ViewKey& rhs) const {
19 return std::tie(base_layer, num_layers, base_level, num_levels) ==
20 std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
21}
22
23} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/surface_view.h b/src/video_core/texture_cache/surface_view.h
new file mode 100644
index 000000000..e73d8f6ae
--- /dev/null
+++ b/src/video_core/texture_cache/surface_view.h
@@ -0,0 +1,35 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <functional>
8
9#include "common/common_types.h"
10
11namespace VideoCommon {
12
13struct ViewKey {
14 std::size_t Hash() const;
15
16 bool operator==(const ViewKey& rhs) const;
17
18 u32 base_layer{};
19 u32 num_layers{};
20 u32 base_level{};
21 u32 num_levels{};
22};
23
24} // namespace VideoCommon
25
26namespace std {
27
28template <>
29struct hash<VideoCommon::ViewKey> {
30 std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
31 return k.Hash();
32 }
33};
34
35} // namespace std
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
new file mode 100644
index 000000000..fb43fa65e
--- /dev/null
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -0,0 +1,282 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <list>
8#include <memory>
9#include <set>
10#include <tuple>
11#include <type_traits>
12#include <unordered_map>
13
14#include <boost/icl/interval_map.hpp>
15#include <boost/range/iterator_range.hpp>
16
17#include "common/assert.h"
18#include "common/common_types.h"
19#include "core/memory.h"
20#include "video_core/engines/fermi_2d.h"
21#include "video_core/engines/maxwell_3d.h"
22#include "video_core/gpu.h"
23#include "video_core/memory_manager.h"
24#include "video_core/rasterizer_interface.h"
25#include "video_core/surface.h"
26#include "video_core/texture_cache/surface_base.h"
27#include "video_core/texture_cache/surface_params.h"
28#include "video_core/texture_cache/surface_view.h"
29
30namespace Core {
31class System;
32}
33
34namespace Tegra::Texture {
35struct FullTextureInfo;
36}
37
38namespace VideoCore {
39class RasterizerInterface;
40}
41
42namespace VideoCommon {
43
44template <typename TSurface, typename TView, typename TExecutionContext>
45class TextureCache {
46 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
47
48 using ResultType = std::tuple<TView*, TExecutionContext>;
49 using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>;
50 using IntervalType = typename IntervalMap::interval_type;
51
52public:
53 void InvalidateRegion(CacheAddr addr, std::size_t size) {
54 for (const auto& surface : GetSurfacesInRegion(addr, size)) {
55 if (!surface->IsRegistered()) {
56 // Skip duplicates
57 continue;
58 }
59 Unregister(surface);
60 }
61 }
62
63 ResultType GetTextureSurface(TExecutionContext exctx,
64 const Tegra::Texture::FullTextureInfo& config) {
65 const auto gpu_addr{config.tic.Address()};
66 if (!gpu_addr) {
67 return {{}, exctx};
68 }
69 const auto params{SurfaceParams::CreateForTexture(system, config)};
70 return GetSurfaceView(exctx, gpu_addr, params, true);
71 }
72
73 ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
74 const auto& regs{system.GPU().Maxwell3D().regs};
75 const auto gpu_addr{regs.zeta.Address()};
76 if (!gpu_addr || !regs.zeta_enable) {
77 return {{}, exctx};
78 }
79 const auto depth_params{SurfaceParams::CreateForDepthBuffer(
80 system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
81 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
82 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
83 return GetSurfaceView(exctx, gpu_addr, depth_params, preserve_contents);
84 }
85
86 ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
87 bool preserve_contents) {
88 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
89
90 const auto& regs{system.GPU().Maxwell3D().regs};
91 if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
92 regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
93 return {{}, exctx};
94 }
95
96 auto& memory_manager{system.GPU().MemoryManager()};
97 const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
98 const auto gpu_addr{config.Address() +
99 config.base_layer * config.layer_stride * sizeof(u32)};
100 if (!gpu_addr) {
101 return {{}, exctx};
102 }
103
104 return GetSurfaceView(exctx, gpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
105 preserve_contents);
106 }
107
108 ResultType GetFermiSurface(TExecutionContext exctx,
109 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
110 return GetSurfaceView(exctx, config.Address(),
111 SurfaceParams::CreateForFermiCopySurface(config), true);
112 }
113
114 std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const {
115 const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
116 return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
117 }
118
119 u64 Tick() {
120 return ++ticks;
121 }
122
123protected:
124 TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
125 : system{system}, rasterizer{rasterizer} {}
126
127 ~TextureCache() = default;
128
129 virtual ResultType TryFastGetSurfaceView(
130 TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
131 const SurfaceParams& params, bool preserve_contents,
132 const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
133
134 virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
135
136 void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr,
137 u8* host_ptr) {
138 surface->Register(gpu_addr, cpu_addr, host_ptr);
139 registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
140 rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
141 }
142
143 void Unregister(std::shared_ptr<TSurface> surface) {
144 registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
145 rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
146 surface->Unregister();
147 }
148
149 std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) {
150 if (const auto surface = TryGetReservedSurface(params); surface)
151 return surface;
152 // No reserved surface available, create a new one and reserve it
153 auto new_surface{CreateSurface(params)};
154 ReserveSurface(params, new_surface);
155 return new_surface;
156 }
157
158 Core::System& system;
159
160private:
161 ResultType GetSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr,
162 const SurfaceParams& params, bool preserve_contents) {
163 auto& memory_manager{system.GPU().MemoryManager()};
164 const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)};
165 DEBUG_ASSERT(cpu_addr);
166
167 const auto host_ptr{memory_manager.GetPointer(gpu_addr)};
168 const auto cache_addr{ToCacheAddr(host_ptr)};
169 auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
170 if (overlaps.empty()) {
171 return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
172 }
173
174 if (overlaps.size() == 1) {
175 if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) {
176 return {view, exctx};
177 }
178 }
179
180 TView* fast_view;
181 std::tie(fast_view, exctx) = TryFastGetSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr,
182 params, preserve_contents, overlaps);
183
184 if (!fast_view) {
185 std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) {
186 return lhs->GetModificationTick() < rhs->GetModificationTick();
187 });
188 }
189
190 for (const auto& surface : overlaps) {
191 if (!fast_view) {
192 // Flush even when we don't care about the contents, to preserve memory not
193 // written by the new surface.
194 exctx = FlushSurface(exctx, surface);
195 }
196 Unregister(surface);
197 }
198
199 if (fast_view) {
200 return {fast_view, exctx};
201 }
202
203 return LoadSurfaceView(exctx, gpu_addr, *cpu_addr, host_ptr, params, preserve_contents);
204 }
205
206 ResultType LoadSurfaceView(TExecutionContext exctx, GPUVAddr gpu_addr, VAddr cpu_addr,
207 u8* host_ptr, const SurfaceParams& params, bool preserve_contents) {
208 const auto new_surface{GetUncachedSurface(params)};
209 Register(new_surface, gpu_addr, cpu_addr, host_ptr);
210 if (preserve_contents) {
211 exctx = LoadSurface(exctx, new_surface);
212 }
213 return {new_surface->GetView(gpu_addr, params), exctx};
214 }
215
216 TExecutionContext LoadSurface(TExecutionContext exctx,
217 const std::shared_ptr<TSurface>& surface) {
218 surface->LoadBuffer();
219 exctx = surface->UploadTexture(exctx);
220 surface->MarkAsModified(false);
221 return exctx;
222 }
223
224 TExecutionContext FlushSurface(TExecutionContext exctx,
225 const std::shared_ptr<TSurface>& surface) {
226 if (!surface->IsModified()) {
227 return exctx;
228 }
229 exctx = surface->DownloadTexture(exctx);
230 surface->FlushBuffer();
231 return exctx;
232 }
233
234 std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr,
235 std::size_t size) const {
236 if (size == 0) {
237 return {};
238 }
239 const IntervalType interval{cache_addr, cache_addr + size};
240
241 std::vector<std::shared_ptr<TSurface>> surfaces;
242 for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
243 surfaces.push_back(*pair.second.begin());
244 }
245 return surfaces;
246 }
247
248 void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) {
249 surface_reserve[params].push_back(std::move(surface));
250 }
251
252 std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) {
253 auto search{surface_reserve.find(params)};
254 if (search == surface_reserve.end()) {
255 return {};
256 }
257 for (auto& surface : search->second) {
258 if (!surface->IsRegistered()) {
259 return surface;
260 }
261 }
262 return {};
263 }
264
265 IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const {
266 return IntervalType::right_open(surface->GetCacheAddr(),
267 surface->GetCacheAddr() + surface->GetSizeInBytes());
268 }
269
270 VideoCore::RasterizerInterface& rasterizer;
271
272 u64 ticks{};
273
274 IntervalMap registered_surfaces;
275
276 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
277 /// previously been used. This is to prevent surfaces from being constantly created and
278 /// destroyed when used with different surface parameters.
279 std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve;
280};
281
282} // namespace VideoCommon
diff --git a/src/video_core/texture_cache/texture_cache_contextless.h b/src/video_core/texture_cache/texture_cache_contextless.h
new file mode 100644
index 000000000..cd35a9fd4
--- /dev/null
+++ b/src/video_core/texture_cache/texture_cache_contextless.h
@@ -0,0 +1,93 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "video_core/texture_cache/texture_cache.h"
8
9namespace VideoCommon {
10
11struct DummyExecutionContext {};
12
13template <typename TSurface, typename TView>
14class TextureCacheContextless : protected TextureCache<TSurface, TView, DummyExecutionContext> {
15 using Base = TextureCache<TSurface, TView, DummyExecutionContext>;
16
17public:
18 void InvalidateRegion(CacheAddr addr, std::size_t size) {
19 Base::InvalidateRegion(addr, size);
20 }
21
22 TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) {
23 return RemoveContext(Base::GetTextureSurface({}, config));
24 }
25
26 TView* GetDepthBufferSurface(bool preserve_contents) {
27 return RemoveContext(Base::GetDepthBufferSurface({}, preserve_contents));
28 }
29
30 TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) {
31 return RemoveContext(Base::GetColorBufferSurface({}, index, preserve_contents));
32 }
33
34 TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) {
35 return RemoveContext(Base::GetFermiSurface({}, config));
36 }
37
38 std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const {
39 return Base::TryFindFramebufferSurface(host_ptr);
40 }
41
42 u64 Tick() {
43 return Base::Tick();
44 }
45
46protected:
47 explicit TextureCacheContextless(Core::System& system,
48 VideoCore::RasterizerInterface& rasterizer)
49 : TextureCache<TSurface, TView, DummyExecutionContext>{system, rasterizer} {}
50
51 virtual TView* TryFastGetSurfaceView(
52 GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params,
53 bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0;
54
55private:
56 std::tuple<TView*, DummyExecutionContext> TryFastGetSurfaceView(
57 DummyExecutionContext, GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr,
58 const SurfaceParams& params, bool preserve_contents,
59 const std::vector<std::shared_ptr<TSurface>>& overlaps) {
60 return {TryFastGetSurfaceView(gpu_addr, cpu_addr, host_ptr, params, preserve_contents,
61 overlaps),
62 {}};
63 }
64
65 TView* RemoveContext(std::tuple<TView*, DummyExecutionContext> return_value) {
66 const auto [view, exctx] = return_value;
67 return view;
68 }
69};
70
71template <typename TTextureCache, typename TView>
72class SurfaceBaseContextless : public SurfaceBase<TTextureCache, TView, DummyExecutionContext> {
73public:
74 DummyExecutionContext DownloadTexture(DummyExecutionContext) {
75 DownloadTextureImpl();
76 return {};
77 }
78
79 DummyExecutionContext UploadTexture(DummyExecutionContext) {
80 UploadTextureImpl();
81 return {};
82 }
83
84protected:
85 explicit SurfaceBaseContextless(TTextureCache& texture_cache, const SurfaceParams& params)
86 : SurfaceBase<TTextureCache, TView, DummyExecutionContext>{texture_cache, params} {}
87
88 virtual void DownloadTextureImpl() = 0;
89
90 virtual void UploadTextureImpl() = 0;
91};
92
93} // namespace VideoCommon