From 3f9f047375dd9aae7eadcb957747fa8db01544bf Mon Sep 17 00:00:00 2001
From: bunnei <bunneidev@gmail.com>
Date: Sun, 24 Jun 2018 17:42:29 -0400
Subject: [PATCH] gl_rasterizer: Implement AccelerateDisplay to forward
 textures to framebuffers.

---
 src/video_core/rasterizer_interface.h         |  5 ++--
 .../renderer_opengl/gl_rasterizer.cpp         | 25 +++++++++++++++---
 .../renderer_opengl/gl_rasterizer_cache.cpp   | 26 +++++++++++++++++++
 .../renderer_opengl/gl_rasterizer_cache.h     | 11 ++++++++
 .../renderer_opengl/renderer_opengl.cpp       |  1 -
 .../renderer_opengl/renderer_opengl.h         |  2 +-
 6 files changed, 62 insertions(+), 8 deletions(-)

diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index f0e48a802..145e58334 100644
--- a/src/video_core/rasterizer_interface.h
+++ b/src/video_core/rasterizer_interface.h
@@ -51,9 +51,8 @@ public:
     }
 
     /// Attempt to use a faster method to display the framebuffer to screen
-    virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
-                                   VAddr framebuffer_addr, u32 pixel_stride,
-                                   ScreenInfo& screen_info) {
+    virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
+                                   u32 pixel_stride, ScreenInfo& screen_info) {
         return false;
     }
 
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index bc463fc30..f9b0ce434 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -468,11 +468,30 @@ bool RasterizerOpenGL::AccelerateFill(const void* config) {
     return true;
 }
 
-bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& framebuffer,
+bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
                                          VAddr framebuffer_addr, u32 pixel_stride,
                                          ScreenInfo& screen_info) {
-    // TODO(bunnei): ImplementMe
-    return false;
+    if (!framebuffer_addr) {
+        return {};
+    }
+
+    MICROPROFILE_SCOPE(OpenGL_CacheManagement);
+
+    const auto& surface{res_cache.TryFindFramebufferSurface(framebuffer_addr)};
+    if (!surface) {
+        return {};
+    }
+
+    // Verify that the cached surface is the same size and format as the requested framebuffer
+    const auto& params{surface->GetSurfaceParams()};
+    const auto& pixel_format{SurfaceParams::PixelFormatFromGPUPixelFormat(config.pixel_format)};
+    ASSERT_MSG(params.width == config.width, "Framebuffer width is different");
+    ASSERT_MSG(params.height == config.height, "Framebuffer height is different");
+    ASSERT_MSG(params.pixel_format == pixel_format, "Framebuffer pixel_format is different");
+
+    screen_info.display_texture = surface->Texture().handle;
+
+    return true;
 }
 
 void RasterizerOpenGL::SamplerInfo::Create() {
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
index 882490f47..919931d64 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp
@@ -436,3 +436,29 @@ Surface RasterizerCacheOpenGL::GetSurface(const SurfaceParams& params) {
 
     return surface;
 }
+
+Surface RasterizerCacheOpenGL::TryFindFramebufferSurface(VAddr cpu_addr) const {
+    // Tries to find the GPU address of a framebuffer based on the CPU address. This is because
+    // final output framebuffers are specified by CPU address, but internally our GPU cache uses GPU
+    // addresses. We iterate through all cached framebuffers, and compare their starting CPU address
+    // to the one provided. This is obviously not great, and won't work if the framebuffer overlaps
+    // surfaces.
+
+    std::vector<Surface> surfaces;
+    for (const auto& surface : surface_cache) {
+        const auto& params = surface.second->GetSurfaceParams();
+        const VAddr surface_cpu_addr = params.GetCpuAddr();
+        if (cpu_addr >= surface_cpu_addr && cpu_addr < (surface_cpu_addr + params.size_in_bytes)) {
+            ASSERT_MSG(cpu_addr == surface_cpu_addr, "overlapping surfaces are unsupported");
+            surfaces.push_back(surface.second);
+        }
+    }
+
+    if (surfaces.empty()) {
+        return {};
+    }
+
+    ASSERT_MSG(surfaces.size() == 1, ">1 surface is unsupported");
+
+    return surfaces[0];
+}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
index 9f1209b0f..53ff2722d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h
@@ -220,6 +220,16 @@ struct SurfaceParams {
         }
     }
 
+    static PixelFormat PixelFormatFromGPUPixelFormat(Tegra::FramebufferConfig::PixelFormat format) {
+        switch (format) {
+        case Tegra::FramebufferConfig::PixelFormat::ABGR8:
+            return PixelFormat::ABGR8;
+        default:
+            NGLOG_CRITICAL(HW_GPU, "Unimplemented format={}", static_cast<u32>(format));
+            UNREACHABLE();
+        }
+    }
+
     static SurfaceType GetFormatType(PixelFormat pixel_format) {
         if (static_cast<size_t>(pixel_format) < MaxPixelFormat) {
             return SurfaceType::ColorTexture;
@@ -302,6 +312,7 @@ public:
                                                     const MathUtil::Rectangle<s32>& viewport);
     void LoadSurface(const Surface& surface);
     void FlushSurface(const Surface& surface);
+    Surface TryFindFramebufferSurface(VAddr cpu_addr) const;
 
 private:
     Surface GetSurface(const SurfaceParams& params);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index f33766bfd..e3bb2cbb8 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -150,7 +150,6 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
                                          screen_info)) {
         // Reset the screen info's display texture to its own permanent texture
         screen_info.display_texture = screen_info.texture.resource.handle;
-        screen_info.display_texcoords = MathUtil::Rectangle<float>(0.f, 0.f, 1.f, 1.f);
 
         Memory::RasterizerFlushVirtualRegion(framebuffer_addr, size_in_bytes,
                                              Memory::FlushMode::Flush);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 2cc6d9a00..21f0d298c 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -27,7 +27,7 @@ struct TextureInfo {
 /// Structure used for storing information about the display target for the Switch screen
 struct ScreenInfo {
     GLuint display_texture;
-    MathUtil::Rectangle<float> display_texcoords;
+    const MathUtil::Rectangle<float> display_texcoords{0.0f, 0.0f, 1.0f, 1.0f};
     TextureInfo texture;
 };