From 6af44910c1bc1775e11176a48fffacc70478b7cc Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 22 Sep 2013 19:03:31 -0700 Subject: [PATCH] Add an interface to return the current framebuffer. --- GPU/Common/GPUDebugInterface.h | 94 ++++++++++++++++++++++++++++++++++ GPU/GLES/Framebuffer.cpp | 25 +++++++++ GPU/GLES/Framebuffer.h | 2 + GPU/GLES/GLES_GPU.cpp | 5 ++ GPU/GLES/GLES_GPU.h | 2 + GPU/Software/SoftGpu.cpp | 8 +++ GPU/Software/SoftGpu.h | 5 +- 7 files changed, 140 insertions(+), 1 deletion(-) diff --git a/GPU/Common/GPUDebugInterface.h b/GPU/Common/GPUDebugInterface.h index e70f790e8a..6214a7c511 100644 --- a/GPU/Common/GPUDebugInterface.h +++ b/GPU/Common/GPUDebugInterface.h @@ -30,6 +30,93 @@ struct GPUDebugOp { std::string desc; }; +struct GPUDebugBuffer { + GPUDebugBuffer() : alloc_(false), data_(NULL) { + } + + GPUDebugBuffer(void *data, u32 stride, u32 height, GEBufferFormat fmt) + : alloc_(false), data_((u8 *)data), stride_(stride), height_(height), fmt_(fmt) { + } + + GPUDebugBuffer(GPUDebugBuffer &&other) { + alloc_ = other.alloc_; + data_ = other.data_; + height_ = other.height_; + stride_ = other.stride_; + fmt_ = other.fmt_; + other.alloc_ = false; + other.data_ = NULL; + } + + ~GPUDebugBuffer() { + Free(); + } + + GPUDebugBuffer &operator = (GPUDebugBuffer &&other) { + if (this != &other) { + Free(); + alloc_ = other.alloc_; + data_ = other.data_; + height_ = other.height_; + stride_ = other.stride_; + fmt_ = other.fmt_; + other.alloc_ = false; + other.data_ = NULL; + } + + return *this; + } + + void Allocate(u32 stride, u32 height, GEBufferFormat fmt) { + if (alloc_ && stride_ == stride && height_ == height && fmt_ == fmt) { + // Already allocated the right size. + return; + } + + Free(); + alloc_ = true; + height_ = height; + stride_ = stride; + fmt_ = fmt; + + u32 pixelSize = 2; + if (fmt == GE_FORMAT_8888) { + pixelSize = 4; + }; + + data_ = new u8[pixelSize * stride * height]; + } + + void Free() { + if (alloc_ && data_ != NULL) { + delete [] data_; + } + data_ = NULL; + } + + u8 *GetData() const { + return data_; + } + + u32 GetHeight() const { + return height_; + } + + u32 GetStride() const { + return stride_; + } + + GEBufferFormat GetFormat() const { + return fmt_; + } + + bool alloc_; + u8 *data_; + u32 height_; + u32 stride_; + GEBufferFormat fmt_; +}; + class GPUDebugInterface { public: virtual bool GetCurrentDisplayList(DisplayList &list) = 0; @@ -49,6 +136,13 @@ public: virtual u32 GetIndexAddress() = 0; virtual GPUgstate GetGState() = 0; + // Needs to be called from the GPU thread, so on the same thread as a notification is fine. + // Calling from a separate thread (e.g. UI) may fail. + virtual bool GetCurrentFramebuffer(GPUDebugBuffer &buffer) { + // False means unsupported. + return false; + } + // TODO: // cached framebuffers / textures / vertices? // get content of framebuffer / texture diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index f7b7fce47b..5d8e61a653 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -1310,3 +1310,28 @@ void FramebufferManager::UpdateFromMemory(u32 addr, int size) { void FramebufferManager::Resized() { resized_ = true; } + +bool FramebufferManager::GetCurrentFramebuffer(GPUDebugBuffer &buffer) +{ + u32 fb_address = (gstate.fbptr & 0xFFFFFF) | ((gstate.fbwidth & 0xFF0000) << 8); + int fb_stride = gstate.fbwidth & 0x3C0; + + VirtualFramebuffer *vfb = currentRenderVfb_; + if (!vfb) { + vfb = GetVFBAt(fb_address); + } + + if (!vfb) { + // If there's no vfb and we're drawing there, must be memory? + buffer = GPUDebugBuffer(Memory::GetPointer(fb_address), fb_stride, 512, gstate.FrameBufFormat()); + return true; + } + + buffer.Allocate(vfb->fb_stride, vfb->height, GE_FORMAT_8888); + + fbo_bind_for_read(vfb->fbo); + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glReadPixels(0, 0, vfb->fb_stride, vfb->height, GL_RGBA, GL_UNSIGNED_BYTE, buffer.GetData()); + + return true; +} diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 321ec746fe..f199d392c2 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -170,6 +170,8 @@ public: void DestroyFramebuf(VirtualFramebuffer *vfb); + bool GetCurrentFramebuffer(GPUDebugBuffer &buffer); + private: void CompileDraw2DProgram(); diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index b1d141f289..541ceea0ac 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -1504,3 +1504,8 @@ void GLES_GPU::DoState(PointerWrap &p) { gstate_c.textureChanged = true; framebufferManager_.DestroyAllFBOs(); } + +bool GLES_GPU::GetCurrentFramebuffer(GPUDebugBuffer &buffer) +{ + return framebufferManager_.GetCurrentFramebuffer(buffer); +} diff --git a/GPU/GLES/GLES_GPU.h b/GPU/GLES/GLES_GPU.h index 88af7bc694..7df50b4eef 100644 --- a/GPU/GLES/GLES_GPU.h +++ b/GPU/GLES/GLES_GPU.h @@ -66,6 +66,8 @@ public: } std::vector GetFramebufferList(); + bool GetCurrentFramebuffer(GPUDebugBuffer &buffer); + protected: virtual void FastRunLoop(DisplayList &list); virtual void ProcessEvent(GPUEvent ev); diff --git a/GPU/Software/SoftGpu.cpp b/GPU/Software/SoftGpu.cpp index 4238d4c941..90165055da 100644 --- a/GPU/Software/SoftGpu.cpp +++ b/GPU/Software/SoftGpu.cpp @@ -743,3 +743,11 @@ void SoftGPU::UpdateMemory(u32 dest, u32 src, int size) // Nothing to update. InvalidateCache(dest, size, GPU_INVALIDATE_HINT); } + +bool SoftGPU::GetCurrentFramebuffer(GPUDebugBuffer &buffer) +{ + // We don't know the height, so just use 512, which should be the max (hopefully?) + // TODO: Could check clipping and such, though...? + buffer = GPUDebugBuffer(fb.data, gstate.FrameBufStride(), 512, gstate.FrameBufFormat()); + return true; +} diff --git a/GPU/Software/SoftGpu.h b/GPU/Software/SoftGpu.h index 3474a170ca..f511f48757 100644 --- a/GPU/Software/SoftGpu.h +++ b/GPU/Software/SoftGpu.h @@ -17,7 +17,8 @@ #pragma once -#include "../GPUCommon.h" +#include "GPU/GPUCommon.h" +#include "GPU/Common/GPUDebugInterface.h" typedef struct { union { @@ -72,6 +73,8 @@ public: fullInfo = "Software"; } + virtual bool GetCurrentFramebuffer(GPUDebugBuffer &buffer); + protected: virtual void FastRunLoop(DisplayList &list); virtual void ProcessEvent(GPUEvent ev);