summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/video_core/CMakeLists.txt2
-rw-r--r--src/video_core/texture_cache.cpp386
-rw-r--r--src/video_core/texture_cache.h586
3 files changed, 974 insertions, 0 deletions
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 242a0d1cd..5c8ca429e 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -106,6 +106,8 @@ add_library(video_core STATIC
106 textures/decoders.cpp 106 textures/decoders.cpp
107 textures/decoders.h 107 textures/decoders.h
108 textures/texture.h 108 textures/texture.h
109 texture_cache.cpp
110 texture_cache.h
109 video_core.cpp 111 video_core.cpp
110 video_core.h 112 video_core.h
111) 113)
diff --git a/src/video_core/texture_cache.cpp b/src/video_core/texture_cache.cpp
new file mode 100644
index 000000000..e96eba7cc
--- /dev/null
+++ b/src/video_core/texture_cache.cpp
@@ -0,0 +1,386 @@
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/alignment.h"
6#include "common/assert.h"
7#include "common/cityhash.h"
8#include "common/common_types.h"
9#include "core/core.h"
10#include "video_core/surface.h"
11#include "video_core/texture_cache.h"
12#include "video_core/textures/decoders.h"
13#include "video_core/textures/texture.h"
14
15namespace VideoCommon {
16
17using VideoCore::Surface::SurfaceTarget;
18
19using VideoCore::Surface::ComponentTypeFromDepthFormat;
20using VideoCore::Surface::ComponentTypeFromRenderTarget;
21using VideoCore::Surface::ComponentTypeFromTexture;
22using VideoCore::Surface::PixelFormatFromDepthFormat;
23using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
24using VideoCore::Surface::PixelFormatFromTextureFormat;
25using VideoCore::Surface::SurfaceTargetFromTextureType;
26
27constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) {
28 return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile);
29}
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.block_width = params.is_tiled ? config.tic.BlockWidth() : 0,
36 params.block_height = params.is_tiled ? config.tic.BlockHeight() : 0,
37 params.block_depth = params.is_tiled ? config.tic.BlockDepth() : 0,
38 params.tile_width_spacing = params.is_tiled ? (1 << config.tic.tile_width_spacing.Value()) : 1;
39 params.pixel_format =
40 PixelFormatFromTextureFormat(config.tic.format, config.tic.r_type.Value(), false);
41 params.component_type = ComponentTypeFromTexture(config.tic.r_type.Value());
42 params.type = GetFormatType(params.pixel_format);
43 params.target = SurfaceTargetFromTextureType(config.tic.texture_type);
44 params.width = Common::AlignUp(config.tic.Width(), GetCompressionFactor(params.pixel_format));
45 params.height = Common::AlignUp(config.tic.Height(), GetCompressionFactor(params.pixel_format));
46 params.depth = config.tic.Depth();
47 if (params.target == SurfaceTarget::TextureCubemap ||
48 params.target == SurfaceTarget::TextureCubeArray) {
49 params.depth *= 6;
50 }
51 params.pitch = params.is_tiled ? 0 : config.tic.Pitch();
52 params.unaligned_height = config.tic.Height();
53 params.num_levels = config.tic.max_mip_level + 1;
54
55 params.CalculateCachedValues();
56 return params;
57}
58
59SurfaceParams SurfaceParams::CreateForDepthBuffer(
60 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
61 u32 block_width, u32 block_height, u32 block_depth,
62 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type) {
63 SurfaceParams params;
64 params.is_tiled = type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
65 params.block_width = 1 << std::min(block_width, 5U);
66 params.block_height = 1 << std::min(block_height, 5U);
67 params.block_depth = 1 << std::min(block_depth, 5U);
68 params.tile_width_spacing = 1;
69 params.pixel_format = PixelFormatFromDepthFormat(format);
70 params.component_type = ComponentTypeFromDepthFormat(format);
71 params.type = GetFormatType(params.pixel_format);
72 params.width = zeta_width;
73 params.height = zeta_height;
74 params.unaligned_height = zeta_height;
75 params.target = SurfaceTarget::Texture2D;
76 params.depth = 1;
77 params.num_levels = 1;
78
79 params.CalculateCachedValues();
80 return params;
81}
82
83SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
84 const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
85 SurfaceParams params;
86 params.is_tiled =
87 config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
88 params.block_width = 1 << config.memory_layout.block_width;
89 params.block_height = 1 << config.memory_layout.block_height;
90 params.block_depth = 1 << config.memory_layout.block_depth;
91 params.tile_width_spacing = 1;
92 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
93 params.component_type = ComponentTypeFromRenderTarget(config.format);
94 params.type = GetFormatType(params.pixel_format);
95 if (params.is_tiled) {
96 params.width = config.width;
97 } else {
98 const u32 bpp = GetFormatBpp(params.pixel_format) / CHAR_BIT;
99 params.pitch = config.width;
100 params.width = params.pitch / bpp;
101 }
102 params.height = config.height;
103 params.depth = 1;
104 params.unaligned_height = config.height;
105 params.target = SurfaceTarget::Texture2D;
106 params.num_levels = 1;
107
108 params.CalculateCachedValues();
109 return params;
110}
111
112SurfaceParams SurfaceParams::CreateForFermiCopySurface(
113 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
114 SurfaceParams params{};
115 params.is_tiled = !config.linear;
116 params.block_width = params.is_tiled ? std::min(config.BlockWidth(), 32U) : 0,
117 params.block_height = params.is_tiled ? std::min(config.BlockHeight(), 32U) : 0,
118 params.block_depth = params.is_tiled ? std::min(config.BlockDepth(), 32U) : 0,
119 params.tile_width_spacing = 1;
120 params.pixel_format = PixelFormatFromRenderTargetFormat(config.format);
121 params.component_type = ComponentTypeFromRenderTarget(config.format);
122 params.type = GetFormatType(params.pixel_format);
123 params.width = config.width;
124 params.height = config.height;
125 params.unaligned_height = config.height;
126 // TODO(Rodrigo): Try to guess the surface target from depth and layer parameters
127 params.target = SurfaceTarget::Texture2D;
128 params.depth = 1;
129 params.num_levels = 1;
130
131 params.CalculateCachedValues();
132 return params;
133}
134
135u32 SurfaceParams::GetMipWidth(u32 level) const {
136 return std::max(1U, width >> level);
137}
138
139u32 SurfaceParams::GetMipHeight(u32 level) const {
140 return std::max(1U, height >> level);
141}
142
143u32 SurfaceParams::GetMipDepth(u32 level) const {
144 return IsLayered() ? depth : std::max(1U, depth >> level);
145}
146
147bool SurfaceParams::IsLayered() const {
148 switch (target) {
149 case SurfaceTarget::Texture1DArray:
150 case SurfaceTarget::Texture2DArray:
151 case SurfaceTarget::TextureCubeArray:
152 case SurfaceTarget::TextureCubemap:
153 return true;
154 default:
155 return false;
156 }
157}
158
159u32 SurfaceParams::GetMipBlockHeight(u32 level) const {
160 // Auto block resizing algorithm from:
161 // https://cgit.freedesktop.org/mesa/mesa/tree/src/gallium/drivers/nouveau/nv50/nv50_miptree.c
162 if (level == 0) {
163 return block_height;
164 }
165 const u32 height{GetMipHeight(level)};
166 const u32 default_block_height{GetDefaultBlockHeight(pixel_format)};
167 const u32 blocks_in_y{(height + default_block_height - 1) / default_block_height};
168 u32 block_height = 16;
169 while (block_height > 1 && blocks_in_y <= block_height * 4) {
170 block_height >>= 1;
171 }
172 return block_height;
173}
174
175u32 SurfaceParams::GetMipBlockDepth(u32 level) const {
176 if (level == 0)
177 return block_depth;
178 if (target != SurfaceTarget::Texture3D)
179 return 1;
180
181 const u32 depth{GetMipDepth(level)};
182 u32 block_depth = 32;
183 while (block_depth > 1 && depth * 2 <= block_depth) {
184 block_depth >>= 1;
185 }
186 if (block_depth == 32 && GetMipBlockHeight(level) >= 4) {
187 return 16;
188 }
189 return block_depth;
190}
191
192std::size_t SurfaceParams::GetGuestMipmapLevelOffset(u32 level) const {
193 std::size_t offset = 0;
194 for (u32 i = 0; i < level; i++) {
195 offset += GetInnerMipmapMemorySize(i, false, IsLayered(), false);
196 }
197 return offset;
198}
199
200std::size_t SurfaceParams::GetHostMipmapLevelOffset(u32 level) const {
201 std::size_t offset = 0;
202 for (u32 i = 0; i < level; i++) {
203 offset += GetInnerMipmapMemorySize(i, true, false, false);
204 }
205 return offset;
206}
207
208std::size_t SurfaceParams::GetGuestLayerSize() const {
209 return GetInnerMemorySize(false, true, false);
210}
211
212std::size_t SurfaceParams::GetHostLayerSize(u32 level) const {
213 return GetInnerMipmapMemorySize(level, true, IsLayered(), false);
214}
215
216bool SurfaceParams::IsFamiliar(const SurfaceParams& view_params) const {
217 if (std::tie(is_tiled, tile_width_spacing, pixel_format, component_type, type) !=
218 std::tie(view_params.is_tiled, view_params.tile_width_spacing, view_params.pixel_format,
219 view_params.component_type, view_params.type)) {
220 return false;
221 }
222
223 const SurfaceTarget view_target{view_params.target};
224 if (view_target == target) {
225 return true;
226 }
227
228 switch (target) {
229 case SurfaceTarget::Texture1D:
230 case SurfaceTarget::Texture2D:
231 case SurfaceTarget::Texture3D:
232 return false;
233 case SurfaceTarget::Texture1DArray:
234 return view_target == SurfaceTarget::Texture1D;
235 case SurfaceTarget::Texture2DArray:
236 return view_target == SurfaceTarget::Texture2D;
237 case SurfaceTarget::TextureCubemap:
238 return view_target == SurfaceTarget::Texture2D ||
239 view_target == SurfaceTarget::Texture2DArray;
240 case SurfaceTarget::TextureCubeArray:
241 return view_target == SurfaceTarget::Texture2D ||
242 view_target == SurfaceTarget::Texture2DArray ||
243 view_target == SurfaceTarget::TextureCubemap;
244 default:
245 UNIMPLEMENTED_MSG("Unimplemented texture family={}", static_cast<u32>(target));
246 return false;
247 }
248}
249
250bool SurfaceParams::IsPixelFormatZeta() const {
251 return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat &&
252 pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat;
253}
254
255void SurfaceParams::CalculateCachedValues() {
256 guest_size_in_bytes = GetInnerMemorySize(false, false, false);
257
258 // ASTC is uncompressed in software, in emulated as RGBA8
259 if (IsPixelFormatASTC(pixel_format)) {
260 host_size_in_bytes = width * height * depth * 4;
261 } else {
262 host_size_in_bytes = GetInnerMemorySize(true, false, false);
263 }
264
265 switch (target) {
266 case SurfaceTarget::Texture1D:
267 case SurfaceTarget::Texture2D:
268 case SurfaceTarget::Texture3D:
269 num_layers = 1;
270 break;
271 case SurfaceTarget::Texture1DArray:
272 case SurfaceTarget::Texture2DArray:
273 case SurfaceTarget::TextureCubemap:
274 case SurfaceTarget::TextureCubeArray:
275 num_layers = depth;
276 break;
277 default:
278 UNREACHABLE();
279 }
280}
281
282std::size_t SurfaceParams::GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
283 bool uncompressed) const {
284 const bool tiled{as_host_size ? false : is_tiled};
285 const u32 tile_x{GetDefaultBlockWidth(pixel_format)};
286 const u32 tile_y{GetDefaultBlockHeight(pixel_format)};
287 const u32 width{GetMipmapSize(uncompressed, GetMipWidth(level), tile_x)};
288 const u32 height{GetMipmapSize(uncompressed, GetMipHeight(level), tile_y)};
289 const u32 depth{layer_only ? 1U : GetMipDepth(level)};
290 return Tegra::Texture::CalculateSize(tiled, GetBytesPerPixel(pixel_format), width, height,
291 depth, GetMipBlockHeight(level), GetMipBlockDepth(level));
292}
293
294std::size_t SurfaceParams::GetInnerMemorySize(bool as_host_size, bool layer_only,
295 bool uncompressed) const {
296 std::size_t size = 0;
297 for (u32 level = 0; level < num_levels; ++level) {
298 size += GetInnerMipmapMemorySize(level, as_host_size, layer_only, uncompressed);
299 }
300 if (!as_host_size && is_tiled) {
301 size = Common::AlignUp(size, Tegra::Texture::GetGOBSize() * block_height * block_depth);
302 }
303 return size;
304}
305
306std::map<u64, std::pair<u32, u32>> SurfaceParams::CreateViewOffsetMap() const {
307 std::map<u64, std::pair<u32, u32>> view_offset_map;
308 switch (target) {
309 case SurfaceTarget::Texture1D:
310 case SurfaceTarget::Texture2D:
311 case SurfaceTarget::Texture3D: {
312 constexpr u32 layer = 0;
313 for (u32 level = 0; level < num_levels; ++level) {
314 const std::size_t offset{GetGuestMipmapLevelOffset(level)};
315 view_offset_map.insert({offset, {layer, level}});
316 }
317 break;
318 }
319 case SurfaceTarget::Texture1DArray:
320 case SurfaceTarget::Texture2DArray:
321 case SurfaceTarget::TextureCubemap:
322 case SurfaceTarget::TextureCubeArray: {
323 const std::size_t layer_size{GetGuestLayerSize()};
324 for (u32 level = 0; level < num_levels; ++level) {
325 const std::size_t level_offset{GetGuestMipmapLevelOffset(level)};
326 for (u32 layer = 0; layer < num_layers; ++layer) {
327 const auto layer_offset{static_cast<std::size_t>(layer_size * layer)};
328 const std::size_t offset{level_offset + layer_offset};
329 view_offset_map.insert({offset, {layer, level}});
330 }
331 }
332 break;
333 }
334 default:
335 UNIMPLEMENTED_MSG("Unimplemented surface target {}", static_cast<u32>(target));
336 }
337 return view_offset_map;
338}
339
340bool SurfaceParams::IsViewValid(const SurfaceParams& view_params, u32 layer, u32 level) const {
341 return IsDimensionValid(view_params, level) && IsDepthValid(view_params, level) &&
342 IsInBounds(view_params, layer, level);
343}
344
345bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const {
346 return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level);
347}
348
349bool SurfaceParams::IsDepthValid(const SurfaceParams& view_params, u32 level) const {
350 if (view_params.target != SurfaceTarget::Texture3D) {
351 return true;
352 }
353 return view_params.depth == GetMipDepth(level);
354}
355
356bool SurfaceParams::IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const {
357 return layer + view_params.num_layers <= num_layers &&
358 level + view_params.num_levels <= num_levels;
359}
360
361std::size_t HasheableSurfaceParams::Hash() const {
362 return static_cast<std::size_t>(
363 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
364}
365
366bool HasheableSurfaceParams::operator==(const HasheableSurfaceParams& rhs) const {
367 return std::tie(is_tiled, block_width, block_height, block_depth, tile_width_spacing, width,
368 height, depth, pitch, unaligned_height, num_levels, pixel_format,
369 component_type, type, target) ==
370 std::tie(rhs.is_tiled, rhs.block_width, rhs.block_height, rhs.block_depth,
371 rhs.tile_width_spacing, rhs.width, rhs.height, rhs.depth, rhs.pitch,
372 rhs.unaligned_height, rhs.num_levels, rhs.pixel_format, rhs.component_type,
373 rhs.type, rhs.target);
374}
375
376std::size_t ViewKey::Hash() const {
377 return static_cast<std::size_t>(
378 Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
379}
380
381bool ViewKey::operator==(const ViewKey& rhs) const {
382 return std::tie(base_layer, num_layers, base_level, num_levels) ==
383 std::tie(rhs.base_layer, rhs.num_layers, rhs.base_level, rhs.num_levels);
384}
385
386} // namespace VideoCommon
diff --git a/src/video_core/texture_cache.h b/src/video_core/texture_cache.h
new file mode 100644
index 000000000..041551691
--- /dev/null
+++ b/src/video_core/texture_cache.h
@@ -0,0 +1,586 @@
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/rasterizer_interface.h"
24#include "video_core/surface.h"
25
26namespace Core {
27class System;
28}
29
30namespace Tegra::Texture {
31struct FullTextureInfo;
32}
33
34namespace VideoCore {
35class RasterizerInterface;
36}
37
38namespace VideoCommon {
39
40class HasheableSurfaceParams {
41public:
42 std::size_t Hash() const;
43
44 bool operator==(const HasheableSurfaceParams& rhs) const;
45
46protected:
47 // Avoid creation outside of a managed environment.
48 HasheableSurfaceParams() = default;
49
50 bool is_tiled;
51 u32 block_width;
52 u32 block_height;
53 u32 block_depth;
54 u32 tile_width_spacing;
55 u32 width;
56 u32 height;
57 u32 depth;
58 u32 pitch;
59 u32 unaligned_height;
60 u32 num_levels;
61 VideoCore::Surface::PixelFormat pixel_format;
62 VideoCore::Surface::ComponentType component_type;
63 VideoCore::Surface::SurfaceType type;
64 VideoCore::Surface::SurfaceTarget target;
65};
66
67class SurfaceParams final : public HasheableSurfaceParams {
68public:
69 /// Creates SurfaceCachedParams from a texture configuration.
70 static SurfaceParams CreateForTexture(Core::System& system,
71 const Tegra::Texture::FullTextureInfo& config);
72
73 /// Creates SurfaceCachedParams for a depth buffer configuration.
74 static SurfaceParams CreateForDepthBuffer(
75 Core::System& system, u32 zeta_width, u32 zeta_height, Tegra::DepthFormat format,
76 u32 block_width, u32 block_height, u32 block_depth,
77 Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout type);
78
79 /// Creates SurfaceCachedParams from a framebuffer configuration.
80 static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
81
82 /// Creates SurfaceCachedParams from a Fermi2D surface configuration.
83 static SurfaceParams CreateForFermiCopySurface(
84 const Tegra::Engines::Fermi2D::Regs::Surface& config);
85
86 bool IsTiled() const {
87 return is_tiled;
88 }
89
90 u32 GetBlockWidth() const {
91 return block_width;
92 }
93
94 u32 GetTileWidthSpacing() const {
95 return tile_width_spacing;
96 }
97
98 u32 GetWidth() const {
99 return width;
100 }
101
102 u32 GetHeight() const {
103 return height;
104 }
105
106 u32 GetDepth() const {
107 return depth;
108 }
109
110 u32 GetPitch() const {
111 return pitch;
112 }
113
114 u32 GetNumLevels() const {
115 return num_levels;
116 }
117
118 VideoCore::Surface::PixelFormat GetPixelFormat() const {
119 return pixel_format;
120 }
121
122 VideoCore::Surface::ComponentType GetComponentType() const {
123 return component_type;
124 }
125
126 VideoCore::Surface::SurfaceTarget GetTarget() const {
127 return target;
128 }
129
130 VideoCore::Surface::SurfaceType GetType() const {
131 return type;
132 }
133
134 std::size_t GetGuestSizeInBytes() const {
135 return guest_size_in_bytes;
136 }
137
138 std::size_t GetHostSizeInBytes() const {
139 return host_size_in_bytes;
140 }
141
142 u32 GetNumLayers() const {
143 return num_layers;
144 }
145
146 /// Returns the width of a given mipmap level.
147 u32 GetMipWidth(u32 level) const;
148
149 /// Returns the height of a given mipmap level.
150 u32 GetMipHeight(u32 level) const;
151
152 /// Returns the depth of a given mipmap level.
153 u32 GetMipDepth(u32 level) const;
154
155 /// Returns true if these parameters are from a layered surface.
156 bool IsLayered() const;
157
158 /// Returns the block height of a given mipmap level.
159 u32 GetMipBlockHeight(u32 level) const;
160
161 /// Returns the block depth of a given mipmap level.
162 u32 GetMipBlockDepth(u32 level) const;
163
164 /// Returns the offset in bytes in guest memory of a given mipmap level.
165 std::size_t GetGuestMipmapLevelOffset(u32 level) const;
166
167 /// Returns the offset in bytes in host memory (linear) of a given mipmap level.
168 std::size_t GetHostMipmapLevelOffset(u32 level) const;
169
170 /// Returns the size of a layer in bytes in guest memory.
171 std::size_t GetGuestLayerSize() const;
172
173 /// Returns the size of a layer in bytes in host memory for a given mipmap level.
174 std::size_t GetHostLayerSize(u32 level) 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.
195 std::size_t GetInnerMipmapMemorySize(u32 level, bool as_host_size, bool layer_only,
196 bool uncompressed) const;
197
198 /// Returns the size of all mipmap levels and aligns as needed.
199 std::size_t GetInnerMemorySize(bool as_host_size, bool layer_only, bool uncompressed) const;
200
201 /// Returns true if the passed view width and height match the size of this params in a given
202 /// mipmap level.
203 bool IsDimensionValid(const SurfaceParams& view_params, u32 level) const;
204
205 /// Returns true if the passed view depth match the size of this params in a given mipmap level.
206 bool IsDepthValid(const SurfaceParams& view_params, u32 level) const;
207
208 /// Returns true if the passed view layers and mipmap levels are in bounds.
209 bool IsInBounds(const SurfaceParams& view_params, u32 layer, u32 level) const;
210
211 std::size_t guest_size_in_bytes;
212 std::size_t host_size_in_bytes;
213 u32 num_layers;
214};
215
216struct ViewKey {
217 std::size_t Hash() const;
218
219 bool operator==(const ViewKey& rhs) const;
220
221 u32 base_layer{};
222 u32 num_layers{};
223 u32 base_level{};
224 u32 num_levels{};
225};
226
227} // namespace VideoCommon
228
229namespace std {
230
231template <>
232struct hash<VideoCommon::SurfaceParams> {
233 std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept {
234 return k.Hash();
235 }
236};
237
238template <>
239struct hash<VideoCommon::ViewKey> {
240 std::size_t operator()(const VideoCommon::ViewKey& k) const noexcept {
241 return k.Hash();
242 }
243};
244
245} // namespace std
246
247namespace VideoCommon {
248
249template <typename TView, typename TExecutionContext>
250class SurfaceBase {
251 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
252
253public:
254 virtual void LoadBuffer() = 0;
255
256 virtual TExecutionContext FlushBuffer(TExecutionContext exctx) = 0;
257
258 virtual TExecutionContext UploadTexture(TExecutionContext exctx) = 0;
259
260 TView* TryGetView(VAddr view_addr, const SurfaceParams& view_params) {
261 if (view_addr < cpu_addr || !params.IsFamiliar(view_params)) {
262 // It can't be a view if it's in a prior address.
263 return {};
264 }
265
266 const auto relative_offset{static_cast<u64>(view_addr - cpu_addr)};
267 const auto it{view_offset_map.find(relative_offset)};
268 if (it == view_offset_map.end()) {
269 // Couldn't find an aligned view.
270 return {};
271 }
272 const auto [layer, level] = it->second;
273
274 if (!params.IsViewValid(view_params, layer, level)) {
275 return {};
276 }
277
278 return GetView(layer, view_params.GetNumLayers(), level, view_params.GetNumLevels());
279 }
280
281 VAddr GetCpuAddr() const {
282 ASSERT(is_registered);
283 return cpu_addr;
284 }
285
286 u8* GetHostPtr() const {
287 ASSERT(is_registered);
288 return host_ptr;
289 }
290
291 CacheAddr GetCacheAddr() const {
292 ASSERT(is_registered);
293 return cache_addr;
294 }
295
296 std::size_t GetSizeInBytes() const {
297 return params.GetGuestSizeInBytes();
298 }
299
300 void MarkAsModified(bool is_modified_) {
301 is_modified = is_modified_;
302 }
303
304 const SurfaceParams& GetSurfaceParams() const {
305 return params;
306 }
307
308 TView* GetView(VAddr view_addr, const SurfaceParams& view_params) {
309 TView* view{TryGetView(view_addr, view_params)};
310 ASSERT(view != nullptr);
311 return view;
312 }
313
314 void Register(VAddr cpu_addr_, u8* host_ptr_) {
315 ASSERT(!is_registered);
316 is_registered = true;
317 cpu_addr = cpu_addr_;
318 host_ptr = host_ptr_;
319 cache_addr = ToCacheAddr(host_ptr_);
320 }
321
322 void Register(VAddr cpu_addr_) {
323 Register(cpu_addr_, Memory::GetPointer(cpu_addr_));
324 }
325
326 void Unregister() {
327 ASSERT(is_registered);
328 is_registered = false;
329 }
330
331 bool IsRegistered() const {
332 return is_registered;
333 }
334
335protected:
336 explicit SurfaceBase(const SurfaceParams& params)
337 : params{params}, view_offset_map{params.CreateViewOffsetMap()} {}
338
339 ~SurfaceBase() = default;
340
341 virtual std::unique_ptr<TView> CreateView(const ViewKey& view_key) = 0;
342
343 bool IsModified() const {
344 return is_modified;
345 }
346
347 const SurfaceParams params;
348
349private:
350 TView* GetView(u32 base_layer, u32 num_layers, u32 base_level, u32 num_levels) {
351 const ViewKey key{base_layer, num_layers, base_level, num_levels};
352 const auto [entry, is_cache_miss] = views.try_emplace(key);
353 auto& view{entry->second};
354 if (is_cache_miss) {
355 view = CreateView(key);
356 }
357 return view.get();
358 }
359
360 const std::map<u64, std::pair<u32, u32>> view_offset_map;
361
362 VAddr cpu_addr{};
363 u8* host_ptr{};
364 CacheAddr cache_addr{};
365 bool is_modified{};
366 bool is_registered{};
367 std::unordered_map<ViewKey, std::unique_ptr<TView>> views;
368};
369
370template <typename TSurface, typename TView, typename TExecutionContext>
371class TextureCache {
372 static_assert(std::is_trivially_copyable_v<TExecutionContext>);
373 using ResultType = std::tuple<TView*, TExecutionContext>;
374 using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface*>>;
375 using IntervalType = typename IntervalMap::interval_type;
376
377public:
378 void InvalidateRegion(CacheAddr addr, std::size_t size) {
379 for (TSurface* surface : GetSurfacesInRegion(addr, size)) {
380 if (!surface->IsRegistered()) {
381 // Skip duplicates
382 continue;
383 }
384 Unregister(surface);
385 }
386 }
387
388 ResultType GetTextureSurface(TExecutionContext exctx,
389 const Tegra::Texture::FullTextureInfo& config) {
390 auto& memory_manager{system.GPU().MemoryManager()};
391 const auto cpu_addr{memory_manager.GpuToCpuAddress(config.tic.Address())};
392 if (!cpu_addr) {
393 return {{}, exctx};
394 }
395 const auto params{SurfaceParams::CreateForTexture(system, config)};
396 return GetSurfaceView(exctx, *cpu_addr, params, true);
397 }
398
399 ResultType GetDepthBufferSurface(TExecutionContext exctx, bool preserve_contents) {
400 const auto& regs{system.GPU().Maxwell3D().regs};
401 if (!regs.zeta.Address() || !regs.zeta_enable) {
402 return {{}, exctx};
403 }
404
405 auto& memory_manager{system.GPU().MemoryManager()};
406 const auto cpu_addr{memory_manager.GpuToCpuAddress(regs.zeta.Address())};
407 if (!cpu_addr) {
408 return {{}, exctx};
409 }
410
411 const auto depth_params{SurfaceParams::CreateForDepthBuffer(
412 system, regs.zeta_width, regs.zeta_height, regs.zeta.format,
413 regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height,
414 regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)};
415 return GetSurfaceView(exctx, *cpu_addr, depth_params, preserve_contents);
416 }
417
418 ResultType GetColorBufferSurface(TExecutionContext exctx, std::size_t index,
419 bool preserve_contents) {
420 ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
421
422 const auto& regs{system.GPU().Maxwell3D().regs};
423 if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 ||
424 regs.rt[index].format == Tegra::RenderTargetFormat::NONE) {
425 return {{}, exctx};
426 }
427
428 auto& memory_manager{system.GPU().MemoryManager()};
429 const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
430 const auto cpu_addr{memory_manager.GpuToCpuAddress(
431 config.Address() + config.base_layer * config.layer_stride * sizeof(u32))};
432 if (!cpu_addr) {
433 return {{}, exctx};
434 }
435
436 return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
437 preserve_contents);
438 }
439
440 ResultType GetFermiSurface(TExecutionContext exctx,
441 const Tegra::Engines::Fermi2D::Regs::Surface& config) {
442 const auto cpu_addr{system.GPU().MemoryManager().GpuToCpuAddress(config.Address())};
443 ASSERT(cpu_addr);
444 return GetSurfaceView(exctx, *cpu_addr, SurfaceParams::CreateForFermiCopySurface(config),
445 true);
446 }
447
448 TSurface* TryFindFramebufferSurface(const u8* host_ptr) const {
449 const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))};
450 return it != registered_surfaces.end() ? *it->second.begin() : nullptr;
451 }
452
453protected:
454 TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
455 : system{system}, rasterizer{rasterizer} {}
456
457 ~TextureCache() = default;
458
459 virtual ResultType TryFastGetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
460 const SurfaceParams& params, bool preserve_contents,
461 const std::vector<TSurface*>& overlaps) = 0;
462
463 virtual std::unique_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0;
464
465 void Register(TSurface* surface, VAddr cpu_addr, u8* host_ptr) {
466 surface->Register(cpu_addr, host_ptr);
467 registered_surfaces.add({GetSurfaceInterval(surface), {surface}});
468 rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1);
469 }
470
471 void Unregister(TSurface* surface) {
472 registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}});
473 rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1);
474 surface->Unregister();
475 }
476
477 TSurface* GetUncachedSurface(const SurfaceParams& params) {
478 if (TSurface* surface = TryGetReservedSurface(params); surface)
479 return surface;
480 // No reserved surface available, create a new one and reserve it
481 auto new_surface{CreateSurface(params)};
482 TSurface* surface{new_surface.get()};
483 ReserveSurface(params, std::move(new_surface));
484 return surface;
485 }
486
487 Core::System& system;
488
489private:
490 ResultType GetSurfaceView(TExecutionContext exctx, VAddr cpu_addr, const SurfaceParams& params,
491 bool preserve_contents) {
492 const auto host_ptr{Memory::GetPointer(cpu_addr)};
493 const auto cache_addr{ToCacheAddr(host_ptr)};
494 const auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())};
495 if (overlaps.empty()) {
496 return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
497 }
498
499 if (overlaps.size() == 1) {
500 if (TView* view = overlaps[0]->TryGetView(cpu_addr, params); view)
501 return {view, exctx};
502 }
503
504 TView* fast_view;
505 std::tie(fast_view, exctx) =
506 TryFastGetSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents, overlaps);
507
508 for (TSurface* surface : overlaps) {
509 if (!fast_view) {
510 // Flush even when we don't care about the contents, to preserve memory not written
511 // by the new surface.
512 exctx = surface->FlushBuffer(exctx);
513 }
514 Unregister(surface);
515 }
516
517 if (fast_view) {
518 return {fast_view, exctx};
519 }
520
521 return LoadSurfaceView(exctx, cpu_addr, host_ptr, params, preserve_contents);
522 }
523
524 ResultType LoadSurfaceView(TExecutionContext exctx, VAddr cpu_addr, u8* host_ptr,
525 const SurfaceParams& params, bool preserve_contents) {
526 TSurface* new_surface{GetUncachedSurface(params)};
527 Register(new_surface, cpu_addr, host_ptr);
528 if (preserve_contents) {
529 exctx = LoadSurface(exctx, new_surface);
530 }
531 return {new_surface->GetView(cpu_addr, params), exctx};
532 }
533
534 TExecutionContext LoadSurface(TExecutionContext exctx, TSurface* surface) {
535 surface->LoadBuffer();
536 exctx = surface->UploadTexture(exctx);
537 surface->MarkAsModified(false);
538 return exctx;
539 }
540
541 std::vector<TSurface*> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const {
542 if (size == 0) {
543 return {};
544 }
545 const IntervalType interval{cache_addr, cache_addr + size};
546
547 std::vector<TSurface*> surfaces;
548 for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) {
549 surfaces.push_back(*pair.second.begin());
550 }
551 return surfaces;
552 }
553
554 void ReserveSurface(const SurfaceParams& params, std::unique_ptr<TSurface> surface) {
555 surface_reserve[params].push_back(std::move(surface));
556 }
557
558 TSurface* TryGetReservedSurface(const SurfaceParams& params) {
559 auto search{surface_reserve.find(params)};
560 if (search == surface_reserve.end()) {
561 return {};
562 }
563 for (auto& surface : search->second) {
564 if (!surface->IsRegistered()) {
565 return surface.get();
566 }
567 }
568 return {};
569 }
570
571 IntervalType GetSurfaceInterval(TSurface* surface) const {
572 return IntervalType::right_open(surface->GetCacheAddr(),
573 surface->GetCacheAddr() + surface->GetSizeInBytes());
574 }
575
576 VideoCore::RasterizerInterface& rasterizer;
577
578 IntervalMap registered_surfaces;
579
580 /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
581 /// previously been used. This is to prevent surfaces from being constantly created and
582 /// destroyed when used with different surface parameters.
583 std::unordered_map<SurfaceParams, std::list<std::unique_ptr<TSurface>>> surface_reserve;
584};
585
586} // namespace VideoCommon