From f6ef3f7ba64667069ae9b80def06bf22a111af29 Mon Sep 17 00:00:00 2001
From: Connor McLaughlin <mclaughc@outlook.com>
Date: Sat, 14 Sep 2019 20:45:26 +1000
Subject: [PATCH] GPU: Saving/loading of VRAM

---
 src/pse/gpu.cpp           | 18 ++++++++++++++++
 src/pse/gpu.h             |  1 +
 src/pse/gpu_hw_opengl.cpp | 43 +++++++++++++++++++++++++++++++++++++++
 src/pse/gpu_hw_opengl.h   |  1 +
 4 files changed, 63 insertions(+)

diff --git a/src/pse/gpu.cpp b/src/pse/gpu.cpp
index c3222fa48..1e1032b86 100644
--- a/src/pse/gpu.cpp
+++ b/src/pse/gpu.cpp
@@ -66,6 +66,22 @@ bool GPU::DoState(StateWrapper& sw)
     UpdateGPUSTAT();
   }
 
+  if (!sw.DoMarker("GPU-VRAM"))
+    return false;
+
+  if (sw.IsReading())
+  {
+    std::vector<u16> vram;
+    sw.Do(&vram);
+    UpdateVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT, vram.data());
+  }
+  else
+  {
+    std::vector<u16> vram(VRAM_WIDTH * VRAM_HEIGHT);
+    ReadVRAM(0, 0, VRAM_WIDTH, VRAM_HEIGHT, vram.data());
+    sw.Do(&vram);
+  }
+
   return !sw.HasError();
 }
 
@@ -476,6 +492,8 @@ void GPU::UpdateDisplay()
   m_system->IncrementFrameNumber();
 }
 
+void GPU::ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) {}
+
 void GPU::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) {}
 
 void GPU::UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) {}
diff --git a/src/pse/gpu.h b/src/pse/gpu.h
index 180f55883..c2631b756 100644
--- a/src/pse/gpu.h
+++ b/src/pse/gpu.h
@@ -122,6 +122,7 @@ protected:
 
   // Rendering in the backend
   virtual void UpdateDisplay();
+  virtual void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer);
   virtual void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color);
   virtual void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data);
   virtual void DispatchRenderCommand(RenderCommand rc, u32 num_vertices);
diff --git a/src/pse/gpu_hw_opengl.cpp b/src/pse/gpu_hw_opengl.cpp
index 734a848c8..bf4dafd77 100644
--- a/src/pse/gpu_hw_opengl.cpp
+++ b/src/pse/gpu_hw_opengl.cpp
@@ -193,12 +193,55 @@ inline u32 ConvertRGBA5551ToRGBA8888(u16 color)
   return ZeroExtend32(r) | (ZeroExtend32(g) << 8) | (ZeroExtend32(b) << 16) | (ZeroExtend32(a) << 24);
 }
 
+inline u16 ConvertRGBA8888ToRGBA5551(u32 color)
+{
+  const u16 r = Truncate16((color >> 3) & 0x1Fu);
+  const u16 g = Truncate16((color >> 11) & 0x1Fu);
+  const u16 b = Truncate16((color >> 19) & 0x1Fu);
+  const u16 a = Truncate16((color >> 31) & 0x01u);
+
+  return r | (g << 5) | (b << 10) | (a << 15);
+}
+
 void GPU_HW_OpenGL::UpdateDisplay()
 {
   GPU_HW::UpdateDisplay();
   m_system->GetHostInterface()->SetDisplayTexture(m_framebuffer_texture.get(), 0, 0, VRAM_WIDTH, VRAM_HEIGHT);
 }
 
+void GPU_HW_OpenGL::ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer)
+{
+  // we need to convert RGBA8 -> RGBA5551
+  std::vector<u32> temp_buffer(width * height);
+  glBindFramebuffer(GL_READ_FRAMEBUFFER, m_framebuffer_fbo_id);
+  glReadPixels(x, y, width, height, GL_RGBA, GL_UNSIGNED_BYTE, temp_buffer.data());
+
+  // reverse copy because of lower-left origin
+  const u32 source_stride = width * sizeof(u32);
+  const u8* source_ptr = reinterpret_cast<const u8*>(temp_buffer.data()) + (source_stride * (height - 1));
+  const u32 dst_stride = width * sizeof(u16);
+  u8* dst_ptr = static_cast<u8*>(buffer);
+  for (u32 row = 0; row < height; row++)
+  {
+    const u8* source_row_ptr = source_ptr;
+    u8* dst_row_ptr = dst_ptr;
+
+    for (u32 col = 0; col < width; col++)
+    {
+      u32 src_col;
+      std::memcpy(&src_col, source_row_ptr, sizeof(src_col));
+      source_row_ptr += sizeof(src_col);
+
+      const u16 dst_col = ConvertRGBA8888ToRGBA5551(src_col);
+      std::memcpy(dst_row_ptr, &dst_col, sizeof(dst_col));
+      dst_row_ptr += sizeof(dst_col);
+    }
+
+    source_ptr -= source_stride;
+    dst_ptr += dst_stride;
+  }
+}
+
 void GPU_HW_OpenGL::FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color)
 {
   glBindFramebuffer(GL_FRAMEBUFFER, m_framebuffer_fbo_id);
diff --git a/src/pse/gpu_hw_opengl.h b/src/pse/gpu_hw_opengl.h
index 995d8bc96..bdfdb0f81 100644
--- a/src/pse/gpu_hw_opengl.h
+++ b/src/pse/gpu_hw_opengl.h
@@ -18,6 +18,7 @@ public:
 
 protected:
   void UpdateDisplay() override;
+  void ReadVRAM(u32 x, u32 y, u32 width, u32 height, void* buffer) override;
   void FillVRAM(u32 x, u32 y, u32 width, u32 height, u32 color) override;
   void UpdateVRAM(u32 x, u32 y, u32 width, u32 height, const void* data) override;
   void UpdateTexturePageTexture() override;