Jelajahi Sumber

Merge pull request #1160 from bunnei/surface-reserve

gl_rasterizer_cache: Several improvements
bunnei 8 tahun lalu
induk
melakukan
0dce6d7008

+ 58 - 17
src/video_core/renderer_opengl/gl_rasterizer_cache.cpp

@@ -780,17 +780,30 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params, bool pres
         } else if (preserve_contents) {
             // If surface parameters changed and we care about keeping the previous data, recreate
             // the surface from the old one
-            return RecreateSurface(surface, params);
+            UnregisterSurface(surface);
+            Surface new_surface{RecreateSurface(surface, params)};
+            RegisterSurface(new_surface);
+            return new_surface;
         } else {
             // Delete the old surface before creating a new one to prevent collisions.
             UnregisterSurface(surface);
         }
     }
 
+    // Try to get a previously reserved surface
+    surface = TryGetReservedSurface(params);
+
     // No surface found - create a new one
-    surface = std::make_shared<CachedSurface>(params);
-    RegisterSurface(surface);
-    LoadSurface(surface);
+    if (!surface) {
+        surface = std::make_shared<CachedSurface>(params);
+        ReserveSurface(surface);
+        RegisterSurface(surface);
+    }
+
+    // Only load surface from memory if we care about the contents
+    if (preserve_contents) {
+        LoadSurface(surface);
+    }
 
     return surface;
 }
@@ -799,13 +812,18 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
                                                const SurfaceParams& new_params) {
     // Verify surface is compatible for blitting
     const auto& params{surface->GetSurfaceParams()};
-    ASSERT(params.type == new_params.type);
-    ASSERT_MSG(params.GetCompressionFactor(params.pixel_format) == 1,
-               "Compressed texture reinterpretation is not supported");
 
     // Create a new surface with the new parameters, and blit the previous surface to it
     Surface new_surface{std::make_shared<CachedSurface>(new_params)};
 
+    // If format is unchanged, we can do a faster blit without reinterpreting pixel data
+    if (params.pixel_format == new_params.pixel_format) {
+        BlitTextures(surface->Texture().handle, params.GetRect(), new_surface->Texture().handle,
+                     new_surface->GetSurfaceParams().GetRect(), params.type,
+                     read_framebuffer.handle, draw_framebuffer.handle);
+        return new_surface;
+    }
+
     auto source_format = GetFormatTuple(params.pixel_format, params.component_type);
     auto dest_format = GetFormatTuple(new_params.pixel_format, new_params.component_type);
 
@@ -818,9 +836,13 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
 
     glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo.handle);
     glBufferData(GL_PIXEL_PACK_BUFFER, buffer_size, nullptr, GL_STREAM_DRAW_ARB);
-    glGetTextureImage(surface->Texture().handle, 0, source_format.format, source_format.type,
-                      params.SizeInBytes(), nullptr);
-
+    if (source_format.compressed) {
+        glGetCompressedTextureImage(surface->Texture().handle, 0,
+                                    static_cast<GLsizei>(params.SizeInBytes()), nullptr);
+    } else {
+        glGetTextureImage(surface->Texture().handle, 0, source_format.format, source_format.type,
+                          static_cast<GLsizei>(params.SizeInBytes()), nullptr);
+    }
     // If the new texture is bigger than the previous one, we need to fill in the rest with data
     // from the CPU.
     if (params.SizeInBytes() < new_params.SizeInBytes()) {
@@ -846,17 +868,21 @@ Surface RasterizerCacheOpenGL::RecreateSurface(const Surface& surface,
     const auto& dest_rect{new_params.GetRect()};
 
     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo.handle);
-    glTextureSubImage2D(
-        new_surface->Texture().handle, 0, 0, 0, static_cast<GLsizei>(dest_rect.GetWidth()),
-        static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format, dest_format.type, nullptr);
+    if (dest_format.compressed) {
+        glCompressedTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0,
+                                  static_cast<GLsizei>(dest_rect.GetWidth()),
+                                  static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
+                                  static_cast<GLsizei>(new_params.SizeInBytes()), nullptr);
+    } else {
+        glTextureSubImage2D(new_surface->Texture().handle, 0, 0, 0,
+                            static_cast<GLsizei>(dest_rect.GetWidth()),
+                            static_cast<GLsizei>(dest_rect.GetHeight()), dest_format.format,
+                            dest_format.type, nullptr);
+    }
     glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0);
 
     pbo.Release();
 
-    // Update cache accordingly
-    UnregisterSurface(surface);
-    RegisterSurface(new_surface);
-
     return new_surface;
 }
 
@@ -931,6 +957,21 @@ void RasterizerCacheOpenGL::UnregisterSurface(const Surface& surface) {
     surface_cache.erase(search);
 }
 
+void RasterizerCacheOpenGL::ReserveSurface(const Surface& surface) {
+    const auto& surface_reserve_key{SurfaceReserveKey::Create(surface->GetSurfaceParams())};
+    surface_reserve[surface_reserve_key] = surface;
+}
+
+Surface RasterizerCacheOpenGL::TryGetReservedSurface(const SurfaceParams& params) {
+    const auto& surface_reserve_key{SurfaceReserveKey::Create(params)};
+    auto search{surface_reserve.find(surface_reserve_key)};
+    if (search != surface_reserve.end()) {
+        RegisterSurface(search->second);
+        return search->second;
+    }
+    return {};
+}
+
 template <typename Map, typename Interval>
 constexpr auto RangeFromInterval(Map& map, const Interval& interval) {
     return boost::make_iterator_range(map.equal_range(interval));

+ 33 - 0
src/video_core/renderer_opengl/gl_rasterizer_cache.h

@@ -11,6 +11,7 @@
 #include <boost/icl/interval_map.hpp>
 
 #include "common/common_types.h"
+#include "common/hash.h"
 #include "common/math_util.h"
 #include "video_core/engines/maxwell_3d.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -682,6 +683,27 @@ struct SurfaceParams {
     u32 cache_height;
 };
 
+}; // namespace OpenGL
+
+/// Hashable variation of SurfaceParams, used for a key in the surface cache
+struct SurfaceReserveKey : Common::HashableStruct<OpenGL::SurfaceParams> {
+    static SurfaceReserveKey Create(const OpenGL::SurfaceParams& params) {
+        SurfaceReserveKey res;
+        res.state = params;
+        return res;
+    }
+};
+namespace std {
+template <>
+struct hash<SurfaceReserveKey> {
+    size_t operator()(const SurfaceReserveKey& k) const {
+        return k.Hash();
+    }
+};
+} // namespace std
+
+namespace OpenGL {
+
 class CachedSurface final {
 public:
     CachedSurface(const SurfaceParams& params);
@@ -752,12 +774,23 @@ private:
     /// Remove surface from the cache
     void UnregisterSurface(const Surface& surface);
 
+    /// Reserves a unique surface that can be reused later
+    void ReserveSurface(const Surface& surface);
+
+    /// Tries to get a reserved surface for the specified parameters
+    Surface TryGetReservedSurface(const SurfaceParams& params);
+
     /// Increase/decrease the number of surface in pages touching the specified region
     void UpdatePagesCachedCount(Tegra::GPUVAddr addr, u64 size, int delta);
 
     std::unordered_map<Tegra::GPUVAddr, Surface> surface_cache;
     PageMap cached_pages;
 
+    /// The surface reserve is a "backup" cache, this is where we put unique surfaces that have
+    /// previously been used. This is to prevent surfaces from being constantly created and
+    /// destroyed when used with different surface parameters.
+    std::unordered_map<SurfaceReserveKey, Surface> surface_reserve;
+
     OGLFramebuffer read_framebuffer;
     OGLFramebuffer draw_framebuffer;
 };