From 0224ae13c4678cc214ccb1f3dc6b8b5c6de73d70 Mon Sep 17 00:00:00 2001
From: zhupengfei <zhupf321@gmail.com>
Date: Sat, 26 Jan 2019 22:34:55 +0800
Subject: [PATCH] video_core: Implement frame dumping

Two PBOs are used to speed up pixel copying process. To avoid getting the wrong speed/FPS, a new parameter is added to DrawScreens about whether to increase the frame count.
---
 .../renderer_opengl/renderer_opengl.cpp       | 73 ++++++++++++++++++-
 .../renderer_opengl/renderer_opengl.h         | 23 ++++++
 2 files changed, 94 insertions(+), 2 deletions(-)

diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index 874e3d8cc..4740ae86b 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -12,7 +12,9 @@
 #include "common/logging/log.h"
 #include "core/core.h"
 #include "core/core_timing.h"
+#include "core/dumping/backend.h"
 #include "core/frontend/emu_window.h"
+#include "core/frontend/framebuffer_layout.h"
 #include "core/hw/gpu.h"
 #include "core/hw/hw.h"
 #include "core/hw/lcd.h"
@@ -204,7 +206,38 @@ void RendererOpenGL::SwapBuffers() {
         VideoCore::g_renderer_screenshot_requested = false;
     }
 
+    if (cleanup_video_dumping.exchange(false)) {
+        ReleaseVideoDumpingGLObjects();
+    }
+
+    if (Core::System::GetInstance().VideoDumper().IsDumping()) {
+        if (prepare_video_dumping.exchange(false)) {
+            InitVideoDumpingGLObjects();
+        }
+
+        const auto& layout = Core::System::GetInstance().VideoDumper().GetLayout();
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, frame_dumping_framebuffer.handle);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_dumping_framebuffer.handle);
+        DrawScreens(layout);
+
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, frame_dumping_pbos[current_pbo].handle);
+        glReadPixels(0, 0, layout.width, layout.height, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, 0);
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, frame_dumping_pbos[next_pbo].handle);
+
+        GLubyte* pixels = static_cast<GLubyte*>(glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY));
+        VideoDumper::VideoFrame frame_data{layout.width, layout.height, pixels};
+        Core::System::GetInstance().VideoDumper().AddVideoFrame(frame_data);
+
+        glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+        glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
+        glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+        current_pbo = (current_pbo + 1) % 2;
+        next_pbo = (current_pbo + 1) % 2;
+    }
+
     DrawScreens(render_window.GetFramebufferLayout());
+    m_current_frame++;
 
     Core::System::GetInstance().perf_stats.EndSystemFrame();
 
@@ -634,13 +667,49 @@ void RendererOpenGL::DrawScreens(const Layout::FramebufferLayout& layout) {
                                             (float)bottom_screen.GetHeight());
         }
     }
-
-    m_current_frame++;
 }
 
 /// Updates the framerate
 void RendererOpenGL::UpdateFramerate() {}
 
+void RendererOpenGL::PrepareVideoDumping() {
+    prepare_video_dumping = true;
+}
+
+void RendererOpenGL::CleanupVideoDumping() {
+    cleanup_video_dumping = true;
+}
+
+void RendererOpenGL::InitVideoDumpingGLObjects() {
+    const auto& layout = Core::System::GetInstance().VideoDumper().GetLayout();
+
+    frame_dumping_framebuffer.Create();
+    glGenRenderbuffers(1, &frame_dumping_renderbuffer);
+    glBindRenderbuffer(GL_RENDERBUFFER, frame_dumping_renderbuffer);
+    glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB8, layout.width, layout.height);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame_dumping_framebuffer.handle);
+    glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
+                              frame_dumping_renderbuffer);
+    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+
+    for (auto& buffer : frame_dumping_pbos) {
+        buffer.Create();
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, buffer.handle);
+        glBufferData(GL_PIXEL_PACK_BUFFER, layout.width * layout.height * 4, nullptr,
+                     GL_STREAM_READ);
+        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);
+    }
+}
+
+void RendererOpenGL::ReleaseVideoDumpingGLObjects() {
+    frame_dumping_framebuffer.Release();
+    glDeleteRenderbuffers(1, &frame_dumping_renderbuffer);
+
+    for (auto& buffer : frame_dumping_pbos) {
+        buffer.Release();
+    }
+}
+
 static const char* GetSource(GLenum source) {
 #define RET(s)                                                                                     \
     case GL_DEBUG_SOURCE_##s:                                                                      \
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 75a1a83d6..19f31ab52 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -50,6 +50,12 @@ public:
     /// Shutdown the renderer
     void ShutDown() override;
 
+    /// Prepares for video dumping (e.g. create necessary buffers, etc)
+    void PrepareVideoDumping() override;
+
+    /// Cleans up after video dumping is ended
+    void CleanupVideoDumping() override;
+
 private:
     void InitOpenGLObjects();
     void ReloadSampler();
@@ -69,6 +75,9 @@ private:
     // Fills active OpenGL texture with the given RGB color.
     void LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color_b, const TextureInfo& texture);
 
+    void InitVideoDumpingGLObjects();
+    void ReleaseVideoDumpingGLObjects();
+
     OpenGLState state;
 
     // OpenGL object IDs
@@ -94,6 +103,20 @@ private:
     // Shader attribute input indices
     GLuint attrib_position;
     GLuint attrib_tex_coord;
+
+    // Frame dumping
+    OGLFramebuffer frame_dumping_framebuffer;
+    GLuint frame_dumping_renderbuffer;
+
+    // Whether prepare/cleanup video dumping has been requested.
+    // They will be executed on next frame.
+    std::atomic_bool prepare_video_dumping = false;
+    std::atomic_bool cleanup_video_dumping = false;
+
+    // PBOs used to dump frames faster
+    std::array<OGLBuffer, 2> frame_dumping_pbos;
+    GLuint current_pbo = 1;
+    GLuint next_pbo = 0;
 };
 
 } // namespace OpenGL