From 9098905dd13bb68f2fe49a9590688b76cc999fdd Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Sat, 11 May 2019 03:15:49 -0300
Subject: [PATCH] gl_framebuffer_cache: Use a hashed struct to cache
 framebuffers

---
 src/video_core/CMakeLists.txt                 |  2 +
 .../renderer_opengl/gl_framebuffer_cache.cpp  | 73 +++++++++++++++++++
 .../renderer_opengl/gl_framebuffer_cache.h    | 68 +++++++++++++++++
 .../renderer_opengl/gl_rasterizer.cpp         | 59 +--------------
 .../renderer_opengl/gl_rasterizer.h           |  6 +-
 src/video_core/texture_cache/texture_cache.h  |  2 +-
 6 files changed, 148 insertions(+), 62 deletions(-)
 create mode 100644 src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
 create mode 100644 src/video_core/renderer_opengl/gl_framebuffer_cache.h

diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 470fbceda2..9d43f03d27 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -41,6 +41,8 @@ add_library(video_core STATIC
     renderer_opengl/gl_buffer_cache.h
     renderer_opengl/gl_device.cpp
     renderer_opengl/gl_device.h
+    renderer_opengl/gl_framebuffer_cache.cpp
+    renderer_opengl/gl_framebuffer_cache.h
     renderer_opengl/gl_global_cache.cpp
     renderer_opengl/gl_global_cache.h
     renderer_opengl/gl_rasterizer.cpp
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
new file mode 100644
index 0000000000..bb9f9b81f8
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.cpp
@@ -0,0 +1,73 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <tuple>
+
+#include "common/cityhash.h"
+#include "common/scope_exit.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
+#include "video_core/renderer_opengl/gl_state.h"
+
+namespace OpenGL {
+
+using Maxwell = Tegra::Engines::Maxwell3D::Regs;
+
+FramebufferCacheOpenGL::FramebufferCacheOpenGL() = default;
+
+FramebufferCacheOpenGL::~FramebufferCacheOpenGL() = default;
+
+GLuint FramebufferCacheOpenGL::GetFramebuffer(const FramebufferCacheKey& key) {
+    const auto [entry, is_cache_miss] = cache.try_emplace(key);
+    auto& framebuffer{entry->second};
+    if (is_cache_miss) {
+        framebuffer = CreateFramebuffer(key);
+    }
+    return framebuffer.handle;
+}
+
+OGLFramebuffer FramebufferCacheOpenGL::CreateFramebuffer(const FramebufferCacheKey& key) {
+    OGLFramebuffer framebuffer;
+    framebuffer.Create();
+
+    // TODO(Rodrigo): Use DSA here after Nvidia fixes their framebuffer DSA bugs.
+    local_state.draw.draw_framebuffer = framebuffer.handle;
+    local_state.ApplyFramebufferState();
+
+    if (key.is_single_buffer) {
+        if (key.color_attachments[0] != GL_NONE && key.colors[0]) {
+            key.colors[0]->Attach(key.color_attachments[0]);
+            glDrawBuffer(key.color_attachments[0]);
+        } else {
+            glDrawBuffer(GL_NONE);
+        }
+    } else {
+        for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
+            if (key.colors[index]) {
+                key.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index));
+            }
+        }
+        glDrawBuffers(key.colors_count, key.color_attachments.data());
+    }
+
+    if (key.zeta) {
+        key.zeta->Attach(key.stencil_enable ? GL_DEPTH_STENCIL_ATTACHMENT : GL_DEPTH_ATTACHMENT);
+    }
+
+    return framebuffer;
+}
+
+std::size_t FramebufferCacheKey::Hash() const {
+    static_assert(sizeof(*this) % sizeof(u64) == 0, "Unaligned struct");
+    return static_cast<std::size_t>(
+        Common::CityHash64(reinterpret_cast<const char*>(this), sizeof(*this)));
+}
+
+bool FramebufferCacheKey::operator==(const FramebufferCacheKey& rhs) const {
+    return std::tie(is_single_buffer, stencil_enable, colors_count, color_attachments, colors,
+                    zeta) == std::tie(rhs.is_single_buffer, rhs.stencil_enable, rhs.colors_count,
+                                      rhs.color_attachments, rhs.colors, rhs.zeta);
+}
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_framebuffer_cache.h b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
new file mode 100644
index 0000000000..a3a9963538
--- /dev/null
+++ b/src/video_core/renderer_opengl/gl_framebuffer_cache.h
@@ -0,0 +1,68 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <cstddef>
+#include <unordered_map>
+
+#include <glad/glad.h>
+
+#include "common/common_types.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
+#include "video_core/renderer_opengl/gl_state.h"
+#include "video_core/renderer_opengl/gl_texture_cache.h"
+
+namespace OpenGL {
+
+struct alignas(sizeof(u64)) FramebufferCacheKey {
+    bool is_single_buffer = false;
+    bool stencil_enable = false;
+    u16 colors_count = 0;
+
+    std::array<GLenum, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> color_attachments{};
+    std::array<View, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors;
+    View zeta;
+
+    std::size_t Hash() const;
+
+    bool operator==(const FramebufferCacheKey& rhs) const;
+
+    bool operator!=(const FramebufferCacheKey& rhs) const {
+        return !operator==(rhs);
+    }
+};
+
+} // namespace OpenGL
+
+namespace std {
+
+template <>
+struct hash<OpenGL::FramebufferCacheKey> {
+    std::size_t operator()(const OpenGL::FramebufferCacheKey& k) const noexcept {
+        return k.Hash();
+    }
+};
+
+} // namespace std
+
+namespace OpenGL {
+
+class FramebufferCacheOpenGL {
+public:
+    FramebufferCacheOpenGL();
+    ~FramebufferCacheOpenGL();
+
+    GLuint GetFramebuffer(const FramebufferCacheKey& key);
+
+private:
+    OGLFramebuffer CreateFramebuffer(const FramebufferCacheKey& key);
+
+    OpenGLState local_state;
+    std::unordered_map<FramebufferCacheKey, OGLFramebuffer> cache;
+};
+
+} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 2872dbdebc..8218c51435 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -78,26 +78,6 @@ struct DrawParameters {
     }
 };
 
-struct FramebufferCacheKey {
-    bool is_single_buffer = false;
-    bool stencil_enable = false;
-
-    std::array<GLenum, Maxwell::NumRenderTargets> color_attachments{};
-    std::array<View, Tegra::Engines::Maxwell3D::Regs::NumRenderTargets> colors{};
-    u32 colors_count = 0;
-
-    View zeta = nullptr;
-
-    auto Tie() const {
-        return std::tie(is_single_buffer, stencil_enable, color_attachments, colors, colors_count,
-                        zeta);
-    }
-
-    bool operator<(const FramebufferCacheKey& rhs) const {
-        return Tie() < rhs.Tie();
-    }
-};
-
 RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
                                    ScreenInfo& info)
     : texture_cache{system, *this}, shader_cache{*this, system, emu_window, device},
@@ -355,42 +335,6 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
     gpu.dirty_flags.shaders = false;
 }
 
-void RasterizerOpenGL::SetupCachedFramebuffer(const FramebufferCacheKey& fbkey,
-                                              OpenGLState& current_state) {
-    const auto [entry, is_cache_miss] = framebuffer_cache.try_emplace(fbkey);
-    auto& framebuffer = entry->second;
-
-    if (is_cache_miss)
-        framebuffer.Create();
-
-    current_state.draw.draw_framebuffer = framebuffer.handle;
-    current_state.ApplyFramebufferState();
-
-    if (!is_cache_miss)
-        return;
-
-    if (fbkey.is_single_buffer) {
-        if (fbkey.color_attachments[0] != GL_NONE && fbkey.colors[0]) {
-            fbkey.colors[0]->Attach(fbkey.color_attachments[0]);
-            glDrawBuffer(fbkey.color_attachments[0]);
-        } else {
-            glDrawBuffer(GL_NONE);
-        }
-    } else {
-        for (std::size_t index = 0; index < Maxwell::NumRenderTargets; ++index) {
-            if (fbkey.colors[index]) {
-                fbkey.colors[index]->Attach(GL_COLOR_ATTACHMENT0 + static_cast<GLenum>(index));
-            }
-        }
-        glDrawBuffers(fbkey.colors_count, fbkey.color_attachments.data());
-    }
-
-    if (fbkey.zeta) {
-        fbkey.zeta->Attach(fbkey.stencil_enable ? GL_DEPTH_STENCIL_ATTACHMENT
-                                                : GL_DEPTH_ATTACHMENT);
-    }
-}
-
 std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
     const auto& regs = system.GPU().Maxwell3D().regs;
 
@@ -556,7 +500,7 @@ std::pair<bool, bool> RasterizerOpenGL::ConfigureFramebuffers(
                                depth_surface->GetSurfaceParams().type == SurfaceType::DepthStencil;
     }
 
-    SetupCachedFramebuffer(fbkey, current_state);
+    current_state.draw.draw_framebuffer = framebuffer_cache.GetFramebuffer(fbkey);
     SyncViewport(current_state);
 
     return current_depth_stencil_usage = {static_cast<bool>(depth_surface), fbkey.stencil_enable};
@@ -638,6 +582,7 @@ void RasterizerOpenGL::Clear() {
     clear_state.ApplyDepth();
     clear_state.ApplyStencilTest();
     clear_state.ApplyViewport();
+    clear_state.ApplyFramebufferState();
 
     if (use_color) {
         glClearBufferfv(GL_COLOR, regs.clear_buffers.RT, regs.clear_color);
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index be5ac1b9f5..2f13d9758a 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -23,6 +23,7 @@
 #include "video_core/rasterizer_interface.h"
 #include "video_core/renderer_opengl/gl_buffer_cache.h"
 #include "video_core/renderer_opengl/gl_device.h"
+#include "video_core/renderer_opengl/gl_framebuffer_cache.h"
 #include "video_core/renderer_opengl/gl_global_cache.h"
 #include "video_core/renderer_opengl/gl_resource_manager.h"
 #include "video_core/renderer_opengl/gl_sampler_cache.h"
@@ -49,7 +50,6 @@ namespace OpenGL {
 
 struct ScreenInfo;
 struct DrawParameters;
-struct FramebufferCacheKey;
 
 class RasterizerOpenGL : public VideoCore::RasterizerInterface {
 public:
@@ -193,6 +193,7 @@ private:
     ShaderCacheOpenGL shader_cache;
     GlobalRegionCacheOpenGL global_cache;
     SamplerCacheOpenGL sampler_cache;
+    FramebufferCacheOpenGL framebuffer_cache;
 
     Core::System& system;
     ScreenInfo& screen_info;
@@ -203,7 +204,6 @@ private:
              OGLVertexArray>
         vertex_array_cache;
 
-    std::map<FramebufferCacheKey, OGLFramebuffer> framebuffer_cache;
     FramebufferConfigState current_framebuffer_config_state;
     std::pair<bool, bool> current_depth_stencil_usage{};
 
@@ -226,8 +226,6 @@ private:
 
     void SetupShaders(GLenum primitive_mode);
 
-    void SetupCachedFramebuffer(const FramebufferCacheKey& fbkey, OpenGLState& current_state);
-
     enum class AccelDraw { Disabled, Arrays, Indexed };
     AccelDraw accelerate_draw = AccelDraw::Disabled;
 
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 7058399e24..419c0de5e9 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -133,7 +133,7 @@ public:
             return {};
         }
 
-        if (regs.color_mask[index].raw != 0) {
+        if (regs.color_mask[index].raw == 0) {
             SetEmptyColorBuffer(index);
             return {};
         }