diff options
| -rw-r--r-- | src/video_core/renderer_opengl/gl_texture_cache.cpp | 10 | ||||
| -rw-r--r-- | src/video_core/texture_cache/surface_base.h | 18 | ||||
| -rw-r--r-- | src/video_core/texture_cache/surface_params.cpp | 4 | ||||
| -rw-r--r-- | src/video_core/texture_cache/surface_params.h | 14 | ||||
| -rw-r--r-- | src/video_core/texture_cache/texture_cache.h | 26 |
5 files changed, 53 insertions, 19 deletions
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp index 9e9734f9e..e6f08a764 100644 --- a/src/video_core/renderer_opengl/gl_texture_cache.cpp +++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp | |||
| @@ -195,17 +195,17 @@ OGLTexture CreateTexture(const SurfaceParams& params, GLenum target, GLenum inte | |||
| 195 | 195 | ||
| 196 | switch (params.target) { | 196 | switch (params.target) { |
| 197 | case SurfaceTarget::Texture1D: | 197 | case SurfaceTarget::Texture1D: |
| 198 | glTextureStorage1D(texture.handle, params.num_levels, internal_format, params.width); | 198 | glTextureStorage1D(texture.handle, params.emulated_levels, internal_format, params.width); |
| 199 | break; | 199 | break; |
| 200 | case SurfaceTarget::Texture2D: | 200 | case SurfaceTarget::Texture2D: |
| 201 | case SurfaceTarget::TextureCubemap: | 201 | case SurfaceTarget::TextureCubemap: |
| 202 | glTextureStorage2D(texture.handle, params.num_levels, internal_format, params.width, | 202 | glTextureStorage2D(texture.handle, params.emulated_levels, internal_format, params.width, |
| 203 | params.height); | 203 | params.height); |
| 204 | break; | 204 | break; |
| 205 | case SurfaceTarget::Texture3D: | 205 | case SurfaceTarget::Texture3D: |
| 206 | case SurfaceTarget::Texture2DArray: | 206 | case SurfaceTarget::Texture2DArray: |
| 207 | case SurfaceTarget::TextureCubeArray: | 207 | case SurfaceTarget::TextureCubeArray: |
| 208 | glTextureStorage3D(texture.handle, params.num_levels, internal_format, params.width, | 208 | glTextureStorage3D(texture.handle, params.emulated_levels, internal_format, params.width, |
| 209 | params.height, params.depth); | 209 | params.height, params.depth); |
| 210 | break; | 210 | break; |
| 211 | default: | 211 | default: |
| @@ -245,7 +245,7 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { | |||
| 245 | // TODO(Rodrigo): Optimize alignment | 245 | // TODO(Rodrigo): Optimize alignment |
| 246 | SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); | 246 | SCOPE_EXIT({ glPixelStorei(GL_PACK_ROW_LENGTH, 0); }); |
| 247 | 247 | ||
| 248 | for (u32 level = 0; level < params.num_levels; ++level) { | 248 | for (u32 level = 0; level < params.emulated_levels; ++level) { |
| 249 | glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); | 249 | glPixelStorei(GL_PACK_ALIGNMENT, std::min(8U, params.GetRowAlignment(level))); |
| 250 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); | 250 | glPixelStorei(GL_PACK_ROW_LENGTH, static_cast<GLint>(params.GetMipWidth(level))); |
| 251 | const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level); | 251 | const std::size_t mip_offset = params.GetHostMipmapLevelOffset(level); |
| @@ -264,7 +264,7 @@ void CachedSurface::DownloadTexture(std::vector<u8>& staging_buffer) { | |||
| 264 | void CachedSurface::UploadTexture(std::vector<u8>& staging_buffer) { | 264 | void CachedSurface::UploadTexture(std::vector<u8>& staging_buffer) { |
| 265 | MICROPROFILE_SCOPE(OpenGL_Texture_Upload); | 265 | MICROPROFILE_SCOPE(OpenGL_Texture_Upload); |
| 266 | SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); }); | 266 | SCOPE_EXIT({ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); }); |
| 267 | for (u32 level = 0; level < params.num_levels; ++level) { | 267 | for (u32 level = 0; level < params.emulated_levels; ++level) { |
| 268 | UploadTextureMipmap(level, staging_buffer); | 268 | UploadTextureMipmap(level, staging_buffer); |
| 269 | } | 269 | } |
| 270 | } | 270 | } |
diff --git a/src/video_core/texture_cache/surface_base.h b/src/video_core/texture_cache/surface_base.h index 77c2d6758..70b5258c9 100644 --- a/src/video_core/texture_cache/surface_base.h +++ b/src/video_core/texture_cache/surface_base.h | |||
| @@ -32,6 +32,12 @@ enum class MatchStructureResult : u32 { | |||
| 32 | None = 2, | 32 | None = 2, |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | enum class MatchTopologyResult : u32 { | ||
| 36 | FullMatch = 0, | ||
| 37 | CompressUnmatch = 1, | ||
| 38 | None = 2, | ||
| 39 | }; | ||
| 40 | |||
| 35 | class StagingCache { | 41 | class StagingCache { |
| 36 | public: | 42 | public: |
| 37 | StagingCache() {} | 43 | StagingCache() {} |
| @@ -136,12 +142,20 @@ public: | |||
| 136 | params.target == SurfaceTarget::Texture2D && params.num_levels == 1; | 142 | params.target == SurfaceTarget::Texture2D && params.num_levels == 1; |
| 137 | } | 143 | } |
| 138 | 144 | ||
| 139 | bool MatchesTopology(const SurfaceParams& rhs) const { | 145 | MatchTopologyResult MatchesTopology(const SurfaceParams& rhs) const { |
| 140 | const u32 src_bpp{params.GetBytesPerPixel()}; | 146 | const u32 src_bpp{params.GetBytesPerPixel()}; |
| 141 | const u32 dst_bpp{rhs.GetBytesPerPixel()}; | 147 | const u32 dst_bpp{rhs.GetBytesPerPixel()}; |
| 142 | const bool ib1 = params.IsBuffer(); | 148 | const bool ib1 = params.IsBuffer(); |
| 143 | const bool ib2 = rhs.IsBuffer(); | 149 | const bool ib2 = rhs.IsBuffer(); |
| 144 | return std::tie(src_bpp, params.is_tiled, ib1) == std::tie(dst_bpp, rhs.is_tiled, ib2); | 150 | if (std::tie(src_bpp, params.is_tiled, ib1) == std::tie(dst_bpp, rhs.is_tiled, ib2)) { |
| 151 | const bool cb1 = params.IsCompressed(); | ||
| 152 | const bool cb2 = rhs.IsCompressed(); | ||
| 153 | if (cb1 == cb2) { | ||
| 154 | return MatchTopologyResult::FullMatch; | ||
| 155 | } | ||
| 156 | return MatchTopologyResult::CompressUnmatch; | ||
| 157 | } | ||
| 158 | return MatchTopologyResult::None; | ||
| 145 | } | 159 | } |
| 146 | 160 | ||
| 147 | MatchStructureResult MatchesStructure(const SurfaceParams& rhs) const { | 161 | MatchStructureResult MatchesStructure(const SurfaceParams& rhs) const { |
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp index d9d157d02..77c09264a 100644 --- a/src/video_core/texture_cache/surface_params.cpp +++ b/src/video_core/texture_cache/surface_params.cpp | |||
| @@ -85,6 +85,7 @@ SurfaceParams SurfaceParams::CreateForTexture(Core::System& system, | |||
| 85 | } | 85 | } |
| 86 | params.pitch = params.is_tiled ? 0 : config.tic.Pitch(); | 86 | params.pitch = params.is_tiled ? 0 : config.tic.Pitch(); |
| 87 | params.num_levels = config.tic.max_mip_level + 1; | 87 | params.num_levels = config.tic.max_mip_level + 1; |
| 88 | params.emulated_levels = std::min(params.num_levels, params.MaxPossibleMipmap()); | ||
| 88 | params.is_layered = params.IsLayered(); | 89 | params.is_layered = params.IsLayered(); |
| 89 | return params; | 90 | return params; |
| 90 | } | 91 | } |
| @@ -109,6 +110,7 @@ SurfaceParams SurfaceParams::CreateForDepthBuffer( | |||
| 109 | params.depth = 1; | 110 | params.depth = 1; |
| 110 | params.pitch = 0; | 111 | params.pitch = 0; |
| 111 | params.num_levels = 1; | 112 | params.num_levels = 1; |
| 113 | params.emulated_levels = 1; | ||
| 112 | params.is_layered = false; | 114 | params.is_layered = false; |
| 113 | return params; | 115 | return params; |
| 114 | } | 116 | } |
| @@ -139,6 +141,7 @@ SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::siz | |||
| 139 | params.depth = 1; | 141 | params.depth = 1; |
| 140 | params.target = SurfaceTarget::Texture2D; | 142 | params.target = SurfaceTarget::Texture2D; |
| 141 | params.num_levels = 1; | 143 | params.num_levels = 1; |
| 144 | params.emulated_levels = 1; | ||
| 142 | params.is_layered = false; | 145 | params.is_layered = false; |
| 143 | return params; | 146 | return params; |
| 144 | } | 147 | } |
| @@ -163,6 +166,7 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface( | |||
| 163 | params.target = SurfaceTarget::Texture2D; | 166 | params.target = SurfaceTarget::Texture2D; |
| 164 | params.depth = 1; | 167 | params.depth = 1; |
| 165 | params.num_levels = 1; | 168 | params.num_levels = 1; |
| 169 | params.emulated_levels = 1; | ||
| 166 | params.is_layered = params.IsLayered(); | 170 | params.is_layered = params.IsLayered(); |
| 167 | return params; | 171 | return params; |
| 168 | } | 172 | } |
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h index c3affd621..5fde695b6 100644 --- a/src/video_core/texture_cache/surface_params.h +++ b/src/video_core/texture_cache/surface_params.h | |||
| @@ -160,6 +160,19 @@ public: | |||
| 160 | return std::min(t_src_height, t_dst_height); | 160 | return std::min(t_src_height, t_dst_height); |
| 161 | } | 161 | } |
| 162 | 162 | ||
| 163 | u32 MaxPossibleMipmap() const { | ||
| 164 | const u32 max_mipmap_w = Common::Log2Ceil32(width) + 1U; | ||
| 165 | const u32 max_mipmap_h = Common::Log2Ceil32(height) + 1U; | ||
| 166 | const u32 max_mipmap = std::max(max_mipmap_w, max_mipmap_h); | ||
| 167 | if (target != VideoCore::Surface::SurfaceTarget::Texture3D) | ||
| 168 | return max_mipmap; | ||
| 169 | return std::max(max_mipmap, Common::Log2Ceil32(depth) + 1U); | ||
| 170 | } | ||
| 171 | |||
| 172 | bool IsCompressed() const { | ||
| 173 | return GetDefaultBlockHeight() > 1 || GetDefaultBlockWidth() > 1; | ||
| 174 | } | ||
| 175 | |||
| 163 | /// Returns the default block width. | 176 | /// Returns the default block width. |
| 164 | u32 GetDefaultBlockWidth() const { | 177 | u32 GetDefaultBlockWidth() const { |
| 165 | return VideoCore::Surface::GetDefaultBlockWidth(pixel_format); | 178 | return VideoCore::Surface::GetDefaultBlockWidth(pixel_format); |
| @@ -205,6 +218,7 @@ public: | |||
| 205 | u32 depth; | 218 | u32 depth; |
| 206 | u32 pitch; | 219 | u32 pitch; |
| 207 | u32 num_levels; | 220 | u32 num_levels; |
| 221 | u32 emulated_levels; | ||
| 208 | VideoCore::Surface::PixelFormat pixel_format; | 222 | VideoCore::Surface::PixelFormat pixel_format; |
| 209 | VideoCore::Surface::ComponentType component_type; | 223 | VideoCore::Surface::ComponentType component_type; |
| 210 | VideoCore::Surface::SurfaceType type; | 224 | VideoCore::Surface::SurfaceType type; |
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h index d2093e581..69ef7a2bd 100644 --- a/src/video_core/texture_cache/texture_cache.h +++ b/src/video_core/texture_cache/texture_cache.h | |||
| @@ -305,7 +305,7 @@ private: | |||
| 305 | * due to topological reasons. | 305 | * due to topological reasons. |
| 306 | **/ | 306 | **/ |
| 307 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, | 307 | RecycleStrategy PickStrategy(std::vector<TSurface>& overlaps, const SurfaceParams& params, |
| 308 | const GPUVAddr gpu_addr, const bool untopological) { | 308 | const GPUVAddr gpu_addr, const MatchTopologyResult untopological) { |
| 309 | if (Settings::values.use_accurate_gpu_emulation) { | 309 | if (Settings::values.use_accurate_gpu_emulation) { |
| 310 | return RecycleStrategy::Flush; | 310 | return RecycleStrategy::Flush; |
| 311 | } | 311 | } |
| @@ -320,8 +320,8 @@ private: | |||
| 320 | } | 320 | } |
| 321 | } | 321 | } |
| 322 | // Untopological decision | 322 | // Untopological decision |
| 323 | if (untopological) { | 323 | if (untopological == MatchTopologyResult::CompressUnmatch) { |
| 324 | return RecycleStrategy::Ignore; | 324 | return RecycleStrategy::Flush; |
| 325 | } | 325 | } |
| 326 | return RecycleStrategy::Ignore; | 326 | return RecycleStrategy::Ignore; |
| 327 | } | 327 | } |
| @@ -341,7 +341,7 @@ private: | |||
| 341 | std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, | 341 | std::pair<TSurface, TView> RecycleSurface(std::vector<TSurface>& overlaps, |
| 342 | const SurfaceParams& params, const GPUVAddr gpu_addr, | 342 | const SurfaceParams& params, const GPUVAddr gpu_addr, |
| 343 | const bool preserve_contents, | 343 | const bool preserve_contents, |
| 344 | const bool untopological) { | 344 | const MatchTopologyResult untopological) { |
| 345 | const bool do_load = Settings::values.use_accurate_gpu_emulation && preserve_contents; | 345 | const bool do_load = Settings::values.use_accurate_gpu_emulation && preserve_contents; |
| 346 | for (auto surface : overlaps) { | 346 | for (auto surface : overlaps) { |
| 347 | Unregister(surface); | 347 | Unregister(surface); |
| @@ -502,9 +502,10 @@ private: | |||
| 502 | // matches at certain level we are pretty much done. | 502 | // matches at certain level we are pretty much done. |
| 503 | if (l1_cache.count(cache_addr) > 0) { | 503 | if (l1_cache.count(cache_addr) > 0) { |
| 504 | TSurface current_surface = l1_cache[cache_addr]; | 504 | TSurface current_surface = l1_cache[cache_addr]; |
| 505 | if (!current_surface->MatchesTopology(params)) { | 505 | auto topological_result = current_surface->MatchesTopology(params); |
| 506 | if (topological_result != MatchTopologyResult::FullMatch) { | ||
| 506 | std::vector<TSurface> overlaps{current_surface}; | 507 | std::vector<TSurface> overlaps{current_surface}; |
| 507 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true); | 508 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, topological_result); |
| 508 | } | 509 | } |
| 509 | MatchStructureResult s_result = current_surface->MatchesStructure(params); | 510 | MatchStructureResult s_result = current_surface->MatchesStructure(params); |
| 510 | if (s_result != MatchStructureResult::None && | 511 | if (s_result != MatchStructureResult::None && |
| @@ -534,8 +535,9 @@ private: | |||
| 534 | // we do a topological test to ensure we can find some relationship. If it fails | 535 | // we do a topological test to ensure we can find some relationship. If it fails |
| 535 | // inmediatly recycle the texture | 536 | // inmediatly recycle the texture |
| 536 | for (auto surface : overlaps) { | 537 | for (auto surface : overlaps) { |
| 537 | if (!surface->MatchesTopology(params)) { | 538 | auto topological_result = surface->MatchesTopology(params); |
| 538 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, true); | 539 | if (topological_result != MatchTopologyResult::FullMatch) { |
| 540 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, topological_result); | ||
| 539 | } | 541 | } |
| 540 | } | 542 | } |
| 541 | 543 | ||
| @@ -553,7 +555,7 @@ private: | |||
| 553 | return *view; | 555 | return *view; |
| 554 | } | 556 | } |
| 555 | } | 557 | } |
| 556 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | 558 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, MatchTopologyResult::FullMatch); |
| 557 | } | 559 | } |
| 558 | // Now we check if the candidate is a mipmap/layer of the overlap | 560 | // Now we check if the candidate is a mipmap/layer of the overlap |
| 559 | std::optional<TView> view = | 561 | std::optional<TView> view = |
| @@ -576,13 +578,13 @@ private: | |||
| 576 | pair.first->EmplaceView(params, gpu_addr, candidate_size); | 578 | pair.first->EmplaceView(params, gpu_addr, candidate_size); |
| 577 | if (mirage_view) | 579 | if (mirage_view) |
| 578 | return {pair.first, *mirage_view}; | 580 | return {pair.first, *mirage_view}; |
| 579 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | 581 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, MatchTopologyResult::FullMatch); |
| 580 | } | 582 | } |
| 581 | return {current_surface, *view}; | 583 | return {current_surface, *view}; |
| 582 | } | 584 | } |
| 583 | // The next case is unsafe, so if we r in accurate GPU, just skip it | 585 | // The next case is unsafe, so if we r in accurate GPU, just skip it |
| 584 | if (Settings::values.use_accurate_gpu_emulation) { | 586 | if (Settings::values.use_accurate_gpu_emulation) { |
| 585 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | 587 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, MatchTopologyResult::FullMatch); |
| 586 | } | 588 | } |
| 587 | // This is the case the texture is a part of the parent. | 589 | // This is the case the texture is a part of the parent. |
| 588 | if (current_surface->MatchesSubTexture(params, gpu_addr)) { | 590 | if (current_surface->MatchesSubTexture(params, gpu_addr)) { |
| @@ -599,7 +601,7 @@ private: | |||
| 599 | } | 601 | } |
| 600 | } | 602 | } |
| 601 | // We failed all the tests, recycle the overlaps into a new texture. | 603 | // We failed all the tests, recycle the overlaps into a new texture. |
| 602 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, false); | 604 | return RecycleSurface(overlaps, params, gpu_addr, preserve_contents, MatchTopologyResult::FullMatch); |
| 603 | } | 605 | } |
| 604 | 606 | ||
| 605 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, | 607 | std::pair<TSurface, TView> InitializeSurface(GPUVAddr gpu_addr, const SurfaceParams& params, |