diff options
Diffstat (limited to 'src/video_core/texture_cache')
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 454 |
1 files changed, 366 insertions, 88 deletions
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index c5c01957a..eb0d9bc10 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -22,6 +22,7 @@ | |||
| 22 | #include "video_core/memory_manager.h" | 22 | #include "video_core/memory_manager.h" |
| 23 | #include "video_core/rasterizer_interface.h" | 23 | #include "video_core/rasterizer_interface.h" |
| 24 | #include "video_core/surface.h" | 24 | #include "video_core/surface.h" |
| 25 | #include "video_core/texture_cache/copy_params.h" | ||
| 25 | #include "video_core/texture_cache/surface_base.h" | 26 | #include "video_core/texture_cache/surface_base.h" |
| 26 | #include "video_core/texture_cache/surface_params.h" | 27 | #include "video_core/texture_cache/surface_params.h" |
| 27 | #include "video_core/texture_cache/surface_view.h" | 28 | #include "video_core/texture_cache/surface_view.h" |
| @@ -40,32 +41,42 @@ class RasterizerInterface; | |||
| 40 | 41 | ||
| 41 | namespace VideoCommon { | 42 | namespace VideoCommon { |
| 42 | 43 | ||
| 44 | using VideoCore::Surface::SurfaceTarget; | ||
| 45 | using RenderTargetConfig = Tegra::Engines::Maxwell3D::Regs::RenderTargetConfig; | ||
| 46 | |||
| 43 | template <typename TSurface, typename TView> | 47 | template <typename TSurface, typename TView> |
| 44 | class TextureCache { | 48 | class TextureCache { |
| 45 | using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<std::shared_ptr<TSurface>>>; | 49 | using IntervalMap = boost::icl::interval_map<CacheAddr, std::set<TSurface>>; |
| 46 | using IntervalType = typename IntervalMap::interval_type; | 50 | using IntervalType = typename IntervalMap::interval_type; |
| 47 | 51 | ||
| 48 | public: | 52 | public: |
| 53 | void InitMemoryMananger(Tegra::MemoryManager& memory_manager) { | ||
| 54 | this->memory_manager = &memory_manager; | ||
| 55 | } | ||
| 56 | |||
| 49 | void InvalidateRegion(CacheAddr addr, std::size_t size) { | 57 | void InvalidateRegion(CacheAddr addr, std::size_t size) { |
| 50 | for (const auto& surface : GetSurfacesInRegion(addr, size)) { | 58 | for (const auto& surface : GetSurfacesInRegion(addr, size)) { |
| 51 | if (!surface->IsRegistered()) { | ||
| 52 | // Skip duplicates | ||
| 53 | continue; | ||
| 54 | } | ||
| 55 | Unregister(surface); | 59 | Unregister(surface); |
| 56 | } | 60 | } |
| 57 | } | 61 | } |
| 58 | 62 | ||
| 59 | TView* GetTextureSurface(const Tegra::Texture::FullTextureInfo& config) { | 63 | void InvalidateRegionEx(GPUVAddr addr, std::size_t size) { |
| 64 | for (const auto& surface : GetSurfacesInRegionInner(addr, size)) { | ||
| 65 | Unregister(surface); | ||
| 66 | } | ||
| 67 | } | ||
| 68 | |||
| 69 | TView GetTextureSurface(const Tegra::Texture::FullTextureInfo& config, | ||
| 70 | const VideoCommon::Shader::Sampler& entry) { | ||
| 60 | const auto gpu_addr{config.tic.Address()}; | 71 | const auto gpu_addr{config.tic.Address()}; |
| 61 | if (!gpu_addr) { | 72 | if (!gpu_addr) { |
| 62 | return {}; | 73 | return {}; |
| 63 | } | 74 | } |
| 64 | const auto params{SurfaceParams::CreateForTexture(system, config)}; | 75 | const auto params{SurfaceParams::CreateForTexture(system, config, entry)}; |
| 65 | return GetSurfaceView(gpu_addr, params, true); | 76 | return GetSurface(gpu_addr, params, true).second; |
| 66 | } | 77 | } |
| 67 | 78 | ||
| 68 | TView* GetDepthBufferSurface(bool preserve_contents) { | 79 | TView GetDepthBufferSurface(bool preserve_contents) { |
| 69 | const auto& regs{system.GPU().Maxwell3D().regs}; | 80 | const auto& regs{system.GPU().Maxwell3D().regs}; |
| 70 | const auto gpu_addr{regs.zeta.Address()}; | 81 | const auto gpu_addr{regs.zeta.Address()}; |
| 71 | if (!gpu_addr || !regs.zeta_enable) { | 82 | if (!gpu_addr || !regs.zeta_enable) { |
| @@ -75,36 +86,75 @@ public: | |||
| 75 | system, regs.zeta_width, regs.zeta_height, regs.zeta.format, | 86 | system, regs.zeta_width, regs.zeta_height, regs.zeta.format, |
| 76 | regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, | 87 | regs.zeta.memory_layout.block_width, regs.zeta.memory_layout.block_height, |
| 77 | regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; | 88 | regs.zeta.memory_layout.block_depth, regs.zeta.memory_layout.type)}; |
| 78 | return GetSurfaceView(gpu_addr, depth_params, preserve_contents); | 89 | auto surface_view = GetSurface(gpu_addr, depth_params, preserve_contents); |
| 90 | if (depth_buffer.target) | ||
| 91 | depth_buffer.target->MarkAsProtected(false); | ||
| 92 | if (depth_buffer.target) | ||
| 93 | depth_buffer.target->MarkAsProtected(true); | ||
| 94 | return surface_view.second; | ||
| 79 | } | 95 | } |
| 80 | 96 | ||
| 81 | TView* GetColorBufferSurface(std::size_t index, bool preserve_contents) { | 97 | TView GetColorBufferSurface(std::size_t index, bool preserve_contents) { |
| 82 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); | 98 | ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets); |
| 83 | 99 | ||
| 84 | const auto& regs{system.GPU().Maxwell3D().regs}; | 100 | const auto& regs{system.GPU().Maxwell3D().regs}; |
| 85 | if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || | 101 | if (index >= regs.rt_control.count || regs.rt[index].Address() == 0 || |
| 86 | regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { | 102 | regs.rt[index].format == Tegra::RenderTargetFormat::NONE) { |
| 103 | SetEmptyColorBuffer(index); | ||
| 87 | return {}; | 104 | return {}; |
| 88 | } | 105 | } |
| 89 | 106 | ||
| 90 | auto& memory_manager{system.GPU().MemoryManager()}; | 107 | const auto& config{regs.rt[index]}; |
| 91 | const auto& config{system.GPU().Maxwell3D().regs.rt[index]}; | 108 | const auto gpu_addr{config.Address()}; |
| 92 | const auto gpu_addr{config.Address() + | ||
| 93 | config.base_layer * config.layer_stride * sizeof(u32)}; | ||
| 94 | if (!gpu_addr) { | 109 | if (!gpu_addr) { |
| 110 | SetEmptyColorBuffer(index); | ||
| 95 | return {}; | 111 | return {}; |
| 96 | } | 112 | } |
| 97 | 113 | ||
| 98 | return GetSurfaceView(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index), | 114 | auto surface_view = GetSurface(gpu_addr, SurfaceParams::CreateForFramebuffer(system, index), |
| 99 | preserve_contents); | 115 | preserve_contents); |
| 116 | if (render_targets[index].target) | ||
| 117 | render_targets[index].target->MarkAsProtected(false); | ||
| 118 | render_targets[index].target = surface_view.first; | ||
| 119 | if (render_targets[index].target) | ||
| 120 | render_targets[index].target->MarkAsProtected(true); | ||
| 121 | return surface_view.second; | ||
| 122 | } | ||
| 123 | |||
| 124 | void MarkColorBufferInUse(std::size_t index) { | ||
| 125 | if (render_targets[index].target) | ||
| 126 | render_targets[index].target->MarkAsModified(true, Tick()); | ||
| 100 | } | 127 | } |
| 101 | 128 | ||
| 102 | TView* GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) { | 129 | void MarkDepthBufferInUse() { |
| 103 | return GetSurfaceView(config.Address(), SurfaceParams::CreateForFermiCopySurface(config), | 130 | if (depth_buffer.target) |
| 104 | true); | 131 | depth_buffer.target->MarkAsModified(true, Tick()); |
| 105 | } | 132 | } |
| 106 | 133 | ||
| 107 | std::shared_ptr<TSurface> TryFindFramebufferSurface(const u8* host_ptr) const { | 134 | void SetEmptyDepthBuffer() { |
| 135 | if (depth_buffer.target != nullptr) { | ||
| 136 | depth_buffer.target->MarkAsProtected(false); | ||
| 137 | depth_buffer.target = nullptr; | ||
| 138 | depth_buffer.view = nullptr; | ||
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 | void SetEmptyColorBuffer(std::size_t index) { | ||
| 143 | if (render_targets[index].target != nullptr) { | ||
| 144 | render_targets[index].target->MarkAsProtected(false); | ||
| 145 | std::memset(&render_targets[index].config, sizeof(RenderTargetConfig), 0); | ||
| 146 | render_targets[index].target = nullptr; | ||
| 147 | render_targets[index].view = nullptr; | ||
| 148 | } | ||
| 149 | } | ||
| 150 | |||
| 151 | TView GetFermiSurface(const Tegra::Engines::Fermi2D::Regs::Surface& config) { | ||
| 152 | SurfaceParams params = SurfaceParams::CreateForFermiCopySurface(config); | ||
| 153 | const GPUVAddr gpu_addr = config.Address(); | ||
| 154 | return GetSurface(gpu_addr, params, true).second; | ||
| 155 | } | ||
| 156 | |||
| 157 | TSurface TryFindFramebufferSurface(const u8* host_ptr) const { | ||
| 108 | const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; | 158 | const auto it{registered_surfaces.find(ToCacheAddr(host_ptr))}; |
| 109 | return it != registered_surfaces.end() ? *it->second.begin() : nullptr; | 159 | return it != registered_surfaces.end() ? *it->second.begin() : nullptr; |
| 110 | } | 160 | } |
| @@ -115,126 +165,334 @@ public: | |||
| 115 | 165 | ||
| 116 | protected: | 166 | protected: |
| 117 | TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) | 167 | TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer) |
| 118 | : system{system}, rasterizer{rasterizer} {} | 168 | : system{system}, rasterizer{rasterizer} { |
| 169 | for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) { | ||
| 170 | SetEmptyColorBuffer(i); | ||
| 171 | } | ||
| 172 | SetEmptyDepthBuffer(); | ||
| 173 | } | ||
| 119 | 174 | ||
| 120 | ~TextureCache() = default; | 175 | ~TextureCache() = default; |
| 121 | 176 | ||
| 122 | virtual TView* TryFastGetSurfaceView( | 177 | virtual TSurface CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) = 0; |
| 123 | GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, const SurfaceParams& params, | ||
| 124 | bool preserve_contents, const std::vector<std::shared_ptr<TSurface>>& overlaps) = 0; | ||
| 125 | 178 | ||
| 126 | virtual std::shared_ptr<TSurface> CreateSurface(const SurfaceParams& params) = 0; | 179 | virtual void ImageCopy(TSurface src_surface, TSurface dst_surface, |
| 180 | const CopyParams& copy_params) = 0; | ||
| 127 | 181 | ||
| 128 | void Register(std::shared_ptr<TSurface> surface, GPUVAddr gpu_addr, VAddr cpu_addr, | 182 | void Register(TSurface surface) { |
| 129 | u8* host_ptr) { | 183 | const GPUVAddr gpu_addr = surface->GetGpuAddr(); |
| 130 | surface->Register(gpu_addr, cpu_addr, host_ptr); | 184 | u8* host_ptr = memory_manager->GetPointer(gpu_addr); |
| 131 | registered_surfaces.add({GetSurfaceInterval(surface), {surface}}); | 185 | const std::size_t size = surface->GetSizeInBytes(); |
| 132 | rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), 1); | 186 | const std::optional<VAddr> cpu_addr = memory_manager->GpuToCpuAddress(gpu_addr); |
| 187 | if (!host_ptr || !cpu_addr) { | ||
| 188 | LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}", | ||
| 189 | gpu_addr); | ||
| 190 | return; | ||
| 191 | } | ||
| 192 | surface->SetHostPtr(host_ptr); | ||
| 193 | surface->SetCpuAddr(*cpu_addr); | ||
| 194 | registered_surfaces.add({GetInterval(host_ptr, size), {surface}}); | ||
| 195 | rasterizer.UpdatePagesCachedCount(*cpu_addr, size, 1); | ||
| 196 | RegisterInnerCache(surface); | ||
| 197 | surface->MarkAsRegistered(true); | ||
| 133 | } | 198 | } |
| 134 | 199 | ||
| 135 | void Unregister(std::shared_ptr<TSurface> surface) { | 200 | void Unregister(TSurface surface) { |
| 136 | registered_surfaces.subtract({GetSurfaceInterval(surface), {surface}}); | 201 | if (surface->IsProtected()) |
| 137 | rasterizer.UpdatePagesCachedCount(surface->GetCpuAddr(), surface->GetSizeInBytes(), -1); | 202 | return; |
| 138 | surface->Unregister(); | 203 | const GPUVAddr gpu_addr = surface->GetGpuAddr(); |
| 204 | const void* host_ptr = surface->GetHostPtr(); | ||
| 205 | const std::size_t size = surface->GetSizeInBytes(); | ||
| 206 | const VAddr cpu_addr = surface->GetCpuAddr(); | ||
| 207 | registered_surfaces.erase(GetInterval(host_ptr, size)); | ||
| 208 | rasterizer.UpdatePagesCachedCount(cpu_addr, size, -1); | ||
| 209 | UnregisterInnerCache(surface); | ||
| 210 | surface->MarkAsRegistered(false); | ||
| 211 | ReserveSurface(surface->GetSurfaceParams(), surface); | ||
| 139 | } | 212 | } |
| 140 | 213 | ||
| 141 | std::shared_ptr<TSurface> GetUncachedSurface(const SurfaceParams& params) { | 214 | TSurface GetUncachedSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) { |
| 142 | if (const auto surface = TryGetReservedSurface(params); surface) | 215 | if (const auto surface = TryGetReservedSurface(params); surface) { |
| 216 | surface->SetGpuAddr(gpu_addr); | ||
| 143 | return surface; | 217 | return surface; |
| 218 | } | ||
| 144 | // No reserved surface available, create a new one and reserve it | 219 | // No reserved surface available, create a new one and reserve it |
| 145 | auto new_surface{CreateSurface(params)}; | 220 | auto new_surface{CreateSurface(gpu_addr, params)}; |
| 146 | ReserveSurface(params, new_surface); | ||
| 147 | return new_surface; | 221 | return new_surface; |
| 148 | } | 222 | } |
| 149 | 223 | ||
| 150 | Core::System& system; | 224 | Core::System& system; |
| 151 | 225 | ||
| 152 | private: | 226 | private: |
| 153 | TView* GetSurfaceView(GPUVAddr gpu_addr, const SurfaceParams& params, bool preserve_contents) { | 227 | enum class RecycleStrategy : u32 { |
| 154 | auto& memory_manager{system.GPU().MemoryManager()}; | 228 | Ignore = 0, |
| 155 | const auto cpu_addr{memory_manager.GpuToCpuAddress(gpu_addr)}; | 229 | Flush = 1, |
| 156 | DEBUG_ASSERT(cpu_addr); | 230 | BufferCopy = 3, |
| 157 | 231 | }; | |
| 158 | const auto host_ptr{memory_manager.GetPointer(gpu_addr)}; | 232 | |
| 159 | const auto cache_addr{ToCacheAddr(host_ptr)}; | 233 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, |
| 160 | auto overlaps{GetSurfacesInRegion(cache_addr, params.GetGuestSizeInBytes())}; | 234 | const GPUVAddr gpu_addr, const bool untopological) { |
| 161 | if (overlaps.empty()) { | 235 | // Untopological decision |
| 162 | return LoadSurfaceView(gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); | 236 | if (untopological) { |
| 237 | return RecycleStrategy::Ignore; | ||
| 238 | } | ||
| 239 | // 3D Textures decision | ||
| 240 | if (params.block_depth > 1 || params.target == SurfaceTarget::Texture3D) { | ||
| 241 | return RecycleStrategy::Flush; | ||
| 163 | } | 242 | } |
| 243 | for (auto s : overlaps) { | ||
| 244 | const auto& s_params = s->GetSurfaceParams(); | ||
| 245 | if (s_params.block_depth > 1 || s_params.target == SurfaceTarget::Texture3D) { | ||
| 246 | return RecycleStrategy::Flush; | ||
| 247 | } | ||
| 248 | } | ||
| 249 | return RecycleStrategy::Ignore; | ||
| 250 | } | ||
| 164 | 251 | ||
| 165 | if (overlaps.size() == 1) { | 252 | std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, |
| 166 | if (TView* view = overlaps[0]->TryGetView(gpu_addr, params); view) { | 253 | const SurfaceParams& params, const GPUVAddr gpu_addr, |
| 167 | return view; | 254 | const u8* host_ptr, const bool preserve_contents, |
| 255 | const bool untopological) { | ||
| 256 | for (auto surface : overlaps) { | ||
| 257 | Unregister(surface); | ||
| 258 | } | ||
| 259 | RecycleStrategy strategy = !Settings::values.use_accurate_gpu_emulation | ||
| 260 | ? PickStrategy(overlaps, params, gpu_addr, untopological) | ||
| 261 | : RecycleStrategy::Flush; | ||
| 262 | switch (strategy) { | ||
| 263 | case RecycleStrategy::Ignore: { | ||
| 264 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 265 | } | ||
| 266 | case RecycleStrategy::Flush: { | ||
| 267 | std::sort(overlaps.begin(), overlaps.end(), | ||
| 268 | [](const TSurface& a, const TSurface& b) -> bool { | ||
| 269 | return a->GetModificationTick() < b->GetModificationTick(); | ||
| 270 | }); | ||
| 271 | for (auto surface : overlaps) { | ||
| 272 | FlushSurface(surface); | ||
| 168 | } | 273 | } |
| 274 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 169 | } | 275 | } |
| 276 | default: { | ||
| 277 | UNIMPLEMENTED_MSG("Unimplemented Texture Cache Recycling Strategy!"); | ||
| 278 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 279 | } | ||
| 280 | } | ||
| 281 | } | ||
| 170 | 282 | ||
| 171 | const auto fast_view{TryFastGetSurfaceView(gpu_addr, *cpu_addr, host_ptr, params, | 283 | std::pair<TSurface, TView> RebuildMirage(TSurface current_surface, |
| 172 | preserve_contents, overlaps)}; | 284 | const SurfaceParams& params) { |
| 285 | const auto gpu_addr = current_surface->GetGpuAddr(); | ||
| 286 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | ||
| 287 | std::vector<CopyParams> bricks = current_surface->BreakDown(); | ||
| 288 | for (auto& brick : bricks) { | ||
| 289 | ImageCopy(current_surface, new_surface, brick); | ||
| 290 | } | ||
| 291 | Unregister(current_surface); | ||
| 292 | Register(new_surface); | ||
| 293 | return {new_surface, new_surface->GetMainView()}; | ||
| 294 | } | ||
| 173 | 295 | ||
| 174 | if (!fast_view) { | 296 | std::pair<TSurface, TView> ManageStructuralMatch(TSurface current_surface, |
| 175 | std::sort(overlaps.begin(), overlaps.end(), [](const auto& lhs, const auto& rhs) { | 297 | const SurfaceParams& params) { |
| 176 | return lhs->GetModificationTick() < rhs->GetModificationTick(); | 298 | const bool is_mirage = !current_surface->MatchFormat(params.pixel_format); |
| 177 | }); | 299 | if (is_mirage) { |
| 300 | return RebuildMirage(current_surface, params); | ||
| 178 | } | 301 | } |
| 302 | const bool matches_target = current_surface->MatchTarget(params.target); | ||
| 303 | if (matches_target) { | ||
| 304 | return {current_surface, current_surface->GetMainView()}; | ||
| 305 | } | ||
| 306 | return {current_surface, current_surface->EmplaceOverview(params)}; | ||
| 307 | } | ||
| 179 | 308 | ||
| 180 | for (const auto& surface : overlaps) { | 309 | std::optional<std::pair<TSurface, TView>> ReconstructSurface(std::vector<TSurface>& overlaps, |
| 181 | if (!fast_view) { | 310 | const SurfaceParams& params, |
| 182 | // Flush even when we don't care about the contents, to preserve memory not | 311 | const GPUVAddr gpu_addr, |
| 183 | // written by the new surface. | 312 | const u8* host_ptr) { |
| 184 | FlushSurface(surface); | 313 | if (!params.is_layered || params.target == SurfaceTarget::Texture3D) { |
| 314 | return {}; | ||
| 315 | } | ||
| 316 | TSurface new_surface = GetUncachedSurface(gpu_addr, params); | ||
| 317 | for (auto surface : overlaps) { | ||
| 318 | const SurfaceParams& src_params = surface->GetSurfaceParams(); | ||
| 319 | if (src_params.is_layered || src_params.num_levels > 1) { | ||
| 320 | // We send this cases to recycle as they are more complex to handle | ||
| 321 | return {}; | ||
| 322 | } | ||
| 323 | const std::size_t candidate_size = src_params.GetGuestSizeInBytes(); | ||
| 324 | auto mipmap_layer = new_surface->GetLayerMipmap(surface->GetGpuAddr()); | ||
| 325 | if (!mipmap_layer) { | ||
| 326 | return {}; | ||
| 185 | } | 327 | } |
| 328 | const u32 layer = (*mipmap_layer).first; | ||
| 329 | const u32 mipmap = (*mipmap_layer).second; | ||
| 330 | if (new_surface->GetMipmapSize(mipmap) != candidate_size) { | ||
| 331 | return {}; | ||
| 332 | } | ||
| 333 | // Now we got all the data set up | ||
| 334 | CopyParams copy_params{}; | ||
| 335 | const u32 dst_width = params.GetMipWidth(mipmap); | ||
| 336 | const u32 dst_height = params.GetMipHeight(mipmap); | ||
| 337 | copy_params.width = std::min(src_params.width, dst_width); | ||
| 338 | copy_params.height = std::min(src_params.height, dst_height); | ||
| 339 | copy_params.depth = 1; | ||
| 340 | copy_params.source_level = 0; | ||
| 341 | copy_params.dest_level = mipmap; | ||
| 342 | copy_params.source_z = 0; | ||
| 343 | copy_params.dest_z = layer; | ||
| 344 | ImageCopy(surface, new_surface, copy_params); | ||
| 345 | } | ||
| 346 | for (auto surface : overlaps) { | ||
| 186 | Unregister(surface); | 347 | Unregister(surface); |
| 187 | } | 348 | } |
| 188 | if (fast_view) { | 349 | Register(new_surface); |
| 189 | return fast_view; | 350 | return {{new_surface, new_surface->GetMainView()}}; |
| 351 | } | ||
| 352 | |||
| 353 | std::pair<TSurface, TView> GetSurface(const GPUVAddr gpu_addr, const SurfaceParams& params, | ||
| 354 | bool preserve_contents) { | ||
| 355 | |||
| 356 | const auto host_ptr{memory_manager->GetPointer(gpu_addr)}; | ||
| 357 | const auto cache_addr{ToCacheAddr(host_ptr)}; | ||
| 358 | const std::size_t candidate_size = params.GetGuestSizeInBytes(); | ||
| 359 | auto overlaps{GetSurfacesInRegionInner(gpu_addr, candidate_size)}; | ||
| 360 | if (overlaps.empty()) { | ||
| 361 | return InitializeSurface(gpu_addr, params, preserve_contents); | ||
| 362 | } | ||
| 363 | |||
| 364 | for (auto surface : overlaps) { | ||
| 365 | if (!surface->MatchesTopology(params)) { | ||
| 366 | return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, | ||
| 367 | true); | ||
| 368 | } | ||
| 190 | } | 369 | } |
| 191 | 370 | ||
| 192 | return LoadSurfaceView(gpu_addr, *cpu_addr, host_ptr, params, preserve_contents); | 371 | if (overlaps.size() == 1) { |
| 372 | TSurface current_surface = overlaps[0]; | ||
| 373 | if (current_surface->MatchesStructure(params) && | ||
| 374 | current_surface->GetGpuAddr() == gpu_addr && | ||
| 375 | (params.target != SurfaceTarget::Texture3D || | ||
| 376 | current_surface->MatchTarget(params.target))) { | ||
| 377 | return ManageStructuralMatch(current_surface, params); | ||
| 378 | } | ||
| 379 | if (current_surface->GetSizeInBytes() <= candidate_size) { | ||
| 380 | return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, | ||
| 381 | false); | ||
| 382 | } | ||
| 383 | std::optional<TView> view = current_surface->EmplaceView(params, gpu_addr); | ||
| 384 | if (view.has_value()) { | ||
| 385 | const bool is_mirage = !current_surface->MatchFormat(params.pixel_format); | ||
| 386 | if (is_mirage) { | ||
| 387 | LOG_CRITICAL(HW_GPU, "Mirage View Unsupported"); | ||
| 388 | return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, | ||
| 389 | false); | ||
| 390 | } | ||
| 391 | return {current_surface, *view}; | ||
| 392 | } | ||
| 393 | return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false); | ||
| 394 | } else { | ||
| 395 | std::optional<std::pair<TSurface, TView>> view = | ||
| 396 | ReconstructSurface(overlaps, params, gpu_addr, host_ptr); | ||
| 397 | if (view.has_value()) { | ||
| 398 | return *view; | ||
| 399 | } | ||
| 400 | return RecycleSurface(overlaps, params, gpu_addr, host_ptr, preserve_contents, false); | ||
| 401 | } | ||
| 193 | } | 402 | } |
| 194 | 403 | ||
| 195 | TView* LoadSurfaceView(GPUVAddr gpu_addr, VAddr cpu_addr, u8* host_ptr, | 404 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, |
| 196 | const SurfaceParams& params, bool preserve_contents) { | 405 | bool preserve_contents) { |
| 197 | const auto new_surface{GetUncachedSurface(params)}; | 406 | auto new_surface{GetUncachedSurface(gpu_addr, params)}; |
| 198 | Register(new_surface, gpu_addr, cpu_addr, host_ptr); | 407 | Register(new_surface); |
| 199 | if (preserve_contents) { | 408 | if (preserve_contents) { |
| 200 | LoadSurface(new_surface); | 409 | LoadSurface(new_surface); |
| 201 | } | 410 | } |
| 202 | return new_surface->GetView(gpu_addr, params); | 411 | return {new_surface, new_surface->GetMainView()}; |
| 203 | } | 412 | } |
| 204 | 413 | ||
| 205 | void LoadSurface(const std::shared_ptr<TSurface>& surface) { | 414 | void LoadSurface(const TSurface& surface) { |
| 206 | surface->LoadBuffer(); | 415 | staging_buffer.resize(surface->GetHostSizeInBytes()); |
| 207 | surface->UploadTexture(); | 416 | surface->LoadBuffer(*memory_manager, staging_buffer); |
| 208 | surface->MarkAsModified(false); | 417 | surface->UploadTexture(staging_buffer); |
| 418 | surface->MarkAsModified(false, Tick()); | ||
| 209 | } | 419 | } |
| 210 | 420 | ||
| 211 | void FlushSurface(const std::shared_ptr<TSurface>& surface) { | 421 | void FlushSurface(const TSurface& surface) { |
| 212 | if (!surface->IsModified()) { | 422 | if (!surface->IsModified()) { |
| 213 | return; | 423 | return; |
| 214 | } | 424 | } |
| 215 | surface->DownloadTexture(); | 425 | staging_buffer.resize(surface->GetHostSizeInBytes()); |
| 216 | surface->FlushBuffer(); | 426 | surface->DownloadTexture(staging_buffer); |
| 427 | surface->FlushBuffer(staging_buffer); | ||
| 428 | surface->MarkAsModified(false, Tick()); | ||
| 217 | } | 429 | } |
| 218 | 430 | ||
| 219 | std::vector<std::shared_ptr<TSurface>> GetSurfacesInRegion(CacheAddr cache_addr, | 431 | std::vector<TSurface> GetSurfacesInRegion(CacheAddr cache_addr, std::size_t size) const { |
| 220 | std::size_t size) const { | ||
| 221 | if (size == 0) { | 432 | if (size == 0) { |
| 222 | return {}; | 433 | return {}; |
| 223 | } | 434 | } |
| 224 | const IntervalType interval{cache_addr, cache_addr + size}; | 435 | const IntervalType interval{cache_addr, cache_addr + size}; |
| 225 | 436 | ||
| 226 | std::vector<std::shared_ptr<TSurface>> surfaces; | 437 | std::vector<TSurface> surfaces; |
| 227 | for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { | 438 | for (auto& pair : boost::make_iterator_range(registered_surfaces.equal_range(interval))) { |
| 228 | surfaces.push_back(*pair.second.begin()); | 439 | for (auto& s : pair.second) { |
| 440 | if (!s || !s->IsRegistered()) { | ||
| 441 | continue; | ||
| 442 | } | ||
| 443 | surfaces.push_back(s); | ||
| 444 | } | ||
| 229 | } | 445 | } |
| 230 | return surfaces; | 446 | return surfaces; |
| 231 | } | 447 | } |
| 232 | 448 | ||
| 233 | void ReserveSurface(const SurfaceParams& params, std::shared_ptr<TSurface> surface) { | 449 | void RegisterInnerCache(TSurface& surface) { |
| 450 | GPUVAddr start = surface->GetGpuAddr() >> inner_cache_page_bits; | ||
| 451 | const GPUVAddr end = (surface->GetGpuAddrEnd() - 1) >> inner_cache_page_bits; | ||
| 452 | while (start <= end) { | ||
| 453 | inner_cache[start].push_back(surface); | ||
| 454 | start++; | ||
| 455 | } | ||
| 456 | } | ||
| 457 | |||
| 458 | void UnregisterInnerCache(TSurface& surface) { | ||
| 459 | GPUVAddr start = surface->GetGpuAddr() >> inner_cache_page_bits; | ||
| 460 | const GPUVAddr end = (surface->GetGpuAddrEnd() - 1) >> inner_cache_page_bits; | ||
| 461 | while (start <= end) { | ||
| 462 | inner_cache[start].remove(surface); | ||
| 463 | start++; | ||
| 464 | } | ||
| 465 | } | ||
| 466 | |||
| 467 | std::vector<TSurface> GetSurfacesInRegionInner(const GPUVAddr gpu_addr, const std::size_t size) { | ||
| 468 | if (size == 0) { | ||
| 469 | return {}; | ||
| 470 | } | ||
| 471 | const GPUVAddr gpu_addr_end = gpu_addr + size; | ||
| 472 | GPUVAddr start = gpu_addr >> inner_cache_page_bits; | ||
| 473 | const GPUVAddr end = (gpu_addr_end - 1) >> inner_cache_page_bits; | ||
| 474 | std::vector<TSurface> surfaces; | ||
| 475 | while (start <= end) { | ||
| 476 | std::list<TSurface>& list = inner_cache[start]; | ||
| 477 | for (auto& s : list) { | ||
| 478 | if (!s->IsPicked() && s->Overlaps(gpu_addr, gpu_addr_end)) { | ||
| 479 | s->MarkAsPicked(true); | ||
| 480 | surfaces.push_back(s); | ||
| 481 | } | ||
| 482 | } | ||
| 483 | start++; | ||
| 484 | } | ||
| 485 | for (auto& s : surfaces) { | ||
| 486 | s->MarkAsPicked(false); | ||
| 487 | } | ||
| 488 | return surfaces; | ||
| 489 | } | ||
| 490 | |||
| 491 | void ReserveSurface(const SurfaceParams& params, TSurface surface) { | ||
| 234 | surface_reserve[params].push_back(std::move(surface)); | 492 | surface_reserve[params].push_back(std::move(surface)); |
| 235 | } | 493 | } |
| 236 | 494 | ||
| 237 | std::shared_ptr<TSurface> TryGetReservedSurface(const SurfaceParams& params) { | 495 | TSurface TryGetReservedSurface(const SurfaceParams& params) { |
| 238 | auto search{surface_reserve.find(params)}; | 496 | auto search{surface_reserve.find(params)}; |
| 239 | if (search == surface_reserve.end()) { | 497 | if (search == surface_reserve.end()) { |
| 240 | return {}; | 498 | return {}; |
| @@ -247,21 +505,41 @@ private: | |||
| 247 | return {}; | 505 | return {}; |
| 248 | } | 506 | } |
| 249 | 507 | ||
| 250 | IntervalType GetSurfaceInterval(std::shared_ptr<TSurface> surface) const { | 508 | IntervalType GetInterval(const void* host_ptr, const std::size_t size) const { |
| 251 | return IntervalType::right_open(surface->GetCacheAddr(), | 509 | const CacheAddr addr = ToCacheAddr(host_ptr); |
| 252 | surface->GetCacheAddr() + surface->GetSizeInBytes()); | 510 | return IntervalType::right_open(addr, addr + size); |
| 253 | } | 511 | } |
| 254 | 512 | ||
| 513 | struct RenderInfo { | ||
| 514 | RenderTargetConfig config; | ||
| 515 | TSurface target; | ||
| 516 | TView view; | ||
| 517 | }; | ||
| 518 | |||
| 519 | struct DepthBufferInfo { | ||
| 520 | TSurface target; | ||
| 521 | TView view; | ||
| 522 | }; | ||
| 523 | |||
| 255 | VideoCore::RasterizerInterface& rasterizer; | 524 | VideoCore::RasterizerInterface& rasterizer; |
| 525 | Tegra::MemoryManager* memory_manager; | ||
| 256 | 526 | ||
| 257 | u64 ticks{}; | 527 | u64 ticks{}; |
| 258 | 528 | ||
| 259 | IntervalMap registered_surfaces; | 529 | IntervalMap registered_surfaces; |
| 260 | 530 | ||
| 531 | static constexpr u64 inner_cache_page_bits{20}; | ||
| 532 | static constexpr u64 inner_cache_page_size{1 << inner_cache_page_bits}; | ||
| 533 | std::unordered_map<GPUVAddr, std::list<TSurface>> inner_cache; | ||
| 534 | |||
| 261 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have | 535 | /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have |
| 262 | /// previously been used. This is to prevent surfaces from being constantly created and | 536 | /// previously been used. This is to prevent surfaces from being constantly created and |
| 263 | /// destroyed when used with different surface parameters. | 537 | /// destroyed when used with different surface parameters. |
| 264 | std::unordered_map<SurfaceParams, std::list<std::shared_ptr<TSurface>>> surface_reserve; | 538 | std::unordered_map<SurfaceParams, std::list<TSurface>> surface_reserve; |
| 539 | std::array<RenderInfo, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> render_targets; | ||
| 540 | DepthBufferInfo depth_buffer; | ||
| 541 | |||
| 542 | std::vector<u8> staging_buffer; | ||
| 265 | }; | 543 | }; |
| 266 | 544 | ||
| 267 | } // namespace VideoCommon | 545 | } // namespace VideoCommon |