diff options
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/texture_cache.cpp | 386 | ||||
| -rw-r--r-- | src/video_core/texture_cache.h | 586 |
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 | |||
| 15 | namespace VideoCommon { | ||
| 16 | |||
| 17 | using VideoCore::Surface::SurfaceTarget; | ||
| 18 | |||
| 19 | using VideoCore::Surface::ComponentTypeFromDepthFormat; | ||
| 20 | using VideoCore::Surface::ComponentTypeFromRenderTarget; | ||
| 21 | using VideoCore::Surface::ComponentTypeFromTexture; | ||
| 22 | using VideoCore::Surface::PixelFormatFromDepthFormat; | ||
| 23 | using VideoCore::Surface::PixelFormatFromRenderTargetFormat; | ||
| 24 | using VideoCore::Surface::PixelFormatFromTextureFormat; | ||
| 25 | using VideoCore::Surface::SurfaceTargetFromTextureType; | ||
| 26 | |||
| 27 | constexpr u32 GetMipmapSize(bool uncompressed, u32 mip_size, u32 tile) { | ||
| 28 | return uncompressed ? mip_size : std::max(1U, (mip_size + tile - 1) / tile); | ||
| 29 | } | ||
| 30 | |||
| 31 | SurfaceParams 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 | |||
| 59 | SurfaceParams 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 | |||
| 83 | SurfaceParams 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 | |||
| 112 | SurfaceParams 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 | |||
| 135 | u32 SurfaceParams::GetMipWidth(u32 level) const { | ||
| 136 | return std::max(1U, width >> level); | ||
| 137 | } | ||
| 138 | |||
| 139 | u32 SurfaceParams::GetMipHeight(u32 level) const { | ||
| 140 | return std::max(1U, height >> level); | ||
| 141 | } | ||
| 142 | |||
| 143 | u32 SurfaceParams::GetMipDepth(u32 level) const { | ||
| 144 | return IsLayered() ? depth : std::max(1U, depth >> level); | ||
| 145 | } | ||
| 146 | |||
| 147 | bool 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 | |||
| 159 | u32 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 | |||
| 175 | u32 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 | |||
| 192 | std::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 | |||
| 200 | std::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 | |||
| 208 | std::size_t SurfaceParams::GetGuestLayerSize() const { | ||
| 209 | return GetInnerMemorySize(false, true, false); | ||
| 210 | } | ||
| 211 | |||
| 212 | std::size_t SurfaceParams::GetHostLayerSize(u32 level) const { | ||
| 213 | return GetInnerMipmapMemorySize(level, true, IsLayered(), false); | ||
| 214 | } | ||
| 215 | |||
| 216 | bool 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 | |||
| 250 | bool SurfaceParams::IsPixelFormatZeta() const { | ||
| 251 | return pixel_format >= VideoCore::Surface::PixelFormat::MaxColorFormat && | ||
| 252 | pixel_format < VideoCore::Surface::PixelFormat::MaxDepthStencilFormat; | ||
| 253 | } | ||
| 254 | |||
| 255 | void 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 | |||
| 282 | std::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 | |||
| 294 | std::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 | |||
| 306 | std::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 | |||
| 340 | bool 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 | |||
| 345 | bool SurfaceParams::IsDimensionValid(const SurfaceParams& view_params, u32 level) const { | ||
| 346 | return view_params.width == GetMipWidth(level) && view_params.height == GetMipHeight(level); | ||
| 347 | } | ||
| 348 | |||
| 349 | bool 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 | |||
| 356 | bool 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 | |||
| 361 | std::size_t HasheableSurfaceParams::Hash() const { | ||
| 362 | return static_cast<std::size_t>( | ||
| 363 | Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||
| 364 | } | ||
| 365 | |||
| 366 | bool 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 | |||
| 376 | std::size_t ViewKey::Hash() const { | ||
| 377 | return static_cast<std::size_t>( | ||
| 378 | Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this))); | ||
| 379 | } | ||
| 380 | |||
| 381 | bool 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 | |||
| 26 | namespace Core { | ||
| 27 | class System; | ||
| 28 | } | ||
| 29 | |||
| 30 | namespace Tegra::Texture { | ||
| 31 | struct FullTextureInfo; | ||
| 32 | } | ||
| 33 | |||
| 34 | namespace VideoCore { | ||
| 35 | class RasterizerInterface; | ||
| 36 | } | ||
| 37 | |||
| 38 | namespace VideoCommon { | ||
| 39 | |||
| 40 | class HasheableSurfaceParams { | ||
| 41 | public: | ||
| 42 | std::size_t Hash() const; | ||
| 43 | |||
| 44 | bool operator==(const HasheableSurfaceParams& rhs) const; | ||
| 45 | |||
| 46 | protected: | ||
| 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 | |||
| 67 | class SurfaceParams final : public HasheableSurfaceParams { | ||
| 68 | public: | ||
| 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 | |||
| 190 | private: | ||
| 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 | |||
| 216 | struct 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 | |||
| 229 | namespace std { | ||
| 230 | |||
| 231 | template <> | ||
| 232 | struct hash<VideoCommon::SurfaceParams> { | ||
| 233 | std::size_t operator()(const VideoCommon::SurfaceParams& k) const noexcept { | ||
| 234 | return k.Hash(); | ||
| 235 | } | ||
| 236 | }; | ||
| 237 | |||
| 238 | template <> | ||
| 239 | struct 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 | |||
| 247 | namespace VideoCommon { | ||
| 248 | |||
| 249 | template <typename TView, typename TExecutionContext> | ||
| 250 | class SurfaceBase { | ||
| 251 | static_assert(std::is_trivially_copyable_v<TExecutionContext>); | ||
| 252 | |||
| 253 | public: | ||
| 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 | |||
| 335 | protected: | ||
| 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 | |||
| 349 | private: | ||
| 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 | |||
| 370 | template <typename TSurface, typename TView, typename TExecutionContext> | ||
| 371 | class 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 | |||
| 377 | public: | ||
| 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 | |||
| 453 | protected: | ||
| 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 | |||
| 489 | private: | ||
| 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 | ||