From 85e9ba897d6228dd03f9cece1bcf04902d6d9e20 Mon Sep 17 00:00:00 2001 From: BreadFish64 Date: Tue, 2 Feb 2021 20:46:25 -0600 Subject: [PATCH] gl_rasterizer_cache: Recycle host textures Allocating new textures has fairly high driver overhead. We can avoid some of this by reusing the textures from destroyed surfaces since the game will probably create more textures with the same dimensions and format. --- .../renderer_opengl/gl_rasterizer_cache.cpp | 19 +++++-- .../renderer_opengl/gl_rasterizer_cache.h | 56 ++++++++++++++++--- 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index c85496d296..e2c6bd1f34 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp @@ -494,6 +494,10 @@ static bool FillSurface(const Surface& surface, const u8* fill_data, return true; } +CachedSurface::~CachedSurface() { + owner.host_texture_recycler.emplace(*this, std::move(texture)); +} + bool CachedSurface::CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const { if (type == SurfaceType::Fill && IsRegionValid(fill_interval) && @@ -1893,12 +1897,17 @@ Surface RasterizerCacheOpenGL::CreateSurface(const SurfaceParams& params) { Surface surface = std::make_shared(*this); static_cast(*surface) = params; - surface->texture.Create(); - - surface->gl_buffer.resize(0); surface->invalid_regions.insert(surface->GetInterval()); - AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format), - surface->GetScaledWidth(), surface->GetScaledHeight()); + + auto recycled_texture = host_texture_recycler.find(params); + if (recycled_texture == host_texture_recycler.end()) { + surface->texture.Create(); + AllocateSurfaceTexture(surface->texture.handle, GetFormatTuple(surface->pixel_format), + surface->GetScaledWidth(), surface->GetScaledHeight()); + } else { + surface->texture = std::move(recycled_texture->second); + host_texture_recycler.erase(recycled_texture); + } return surface; } diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index 1a9b6a3b31..fc261316db 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h @@ -36,6 +36,36 @@ class RasterizerCacheOpenGL; class TextureFilterer; class FormatReinterpreterOpenGL; +struct FormatTuple { + GLint internal_format; + GLenum format; + GLenum type; +}; + +constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; + +const FormatTuple& GetFormatTuple(SurfaceParams::PixelFormat pixel_format); + +struct HostTextureTag { + GLint internal_format; + GLenum format; + u32 width; + u32 height; + HostTextureTag(const SurfaceParams& params) noexcept { + auto format_tuple = GetFormatTuple(params.pixel_format); + internal_format = format_tuple.internal_format; + format = format_tuple.format; + // The type in the format tuple is irrelevant for the tag since the type is only for + // interpreting data on upload/download + width = params.GetScaledWidth(); + height = params.GetScaledHeight(); + } + bool operator==(const HostTextureTag& rhs) const noexcept { + return std::tie(internal_format, format, width, height) == + std::tie(rhs.internal_format, rhs.format, rhs.width, rhs.height); + }; +}; + struct TextureCubeConfig { PAddr px; PAddr nx; @@ -59,6 +89,18 @@ struct TextureCubeConfig { } // namespace OpenGL namespace std { +template <> +struct hash { + std::size_t operator()(const OpenGL::HostTextureTag& tag) const noexcept { + std::size_t hash = 0; + boost::hash_combine(hash, tag.format); + boost::hash_combine(hash, tag.internal_format); + boost::hash_combine(hash, tag.width); + boost::hash_combine(hash, tag.height); + return hash; + } +}; + template <> struct hash { std::size_t operator()(const OpenGL::TextureCubeConfig& config) const noexcept { @@ -139,6 +181,7 @@ private: struct CachedSurface : SurfaceParams, std::enable_shared_from_this { CachedSurface(RasterizerCacheOpenGL& owner) : owner{owner} {} + ~CachedSurface(); bool CanFill(const SurfaceParams& dest_surface, SurfaceInterval fill_interval) const; bool CanCopy(const SurfaceParams& dest_surface, SurfaceInterval copy_interval) const; @@ -326,17 +369,12 @@ private: std::unordered_map texture_cube_cache; public: + // Textures from destroyed surfaces are stored here to be recyled to reduce allocation overhead + // in the driver + std::unordered_multimap host_texture_recycler; + std::unique_ptr texture_filterer; std::unique_ptr format_reinterpreter; }; -struct FormatTuple { - GLint internal_format; - GLenum format; - GLenum type; -}; - -constexpr FormatTuple tex_tuple = {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE}; - -const FormatTuple& GetFormatTuple(SurfaceParams::PixelFormat pixel_format); } // namespace OpenGL