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