From 8b67975a47e87fb346ba1b5197bc518c6817e625 Mon Sep 17 00:00:00 2001 From: Henrik Rydgard Date: Mon, 19 Nov 2012 23:29:14 +0100 Subject: [PATCH] GL: Framebuffer management using FBOs. Fixes flicker in Lumines and many others. --- Core/HLE/sceDisplay.cpp | 9 +- GPU/GLES/DisplayListInterpreter.cpp | 131 ++++++++++++++++++++++++++-- GPU/GLES/DisplayListInterpreter.h | 31 ++++++- GPU/GLES/Framebuffer.cpp | 23 +++-- GPU/GLES/Framebuffer.h | 1 + android/jni/EmuScreen.cpp | 2 + native | 2 +- 7 files changed, 181 insertions(+), 18 deletions(-) diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 49a0aa7b21..7b52576a86 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -128,6 +128,7 @@ void hleEnterVblank(u64 userdata, int cyclesLate) DEBUG_LOG(HLE, "Setting latched framebuffer %08x (prev: %08x)", latchedFramebuf.topaddr, framebuf.topaddr); framebuf = latchedFramebuf; framebufIsLatched = false; + gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); } // Draw screen overlays before blitting. Saves and restores the Ge context. @@ -145,9 +146,10 @@ void hleEnterVblank(u64 userdata, int cyclesLate) PPGeDrawText("This is PPGeDraw speaking", 10, 100, 0, 0.5f, 0xFFFFFFFF); PPGeEnd(); */ - // Yeah, this has to be the right moment to end the frame. Should possibly blit the right buffer - // depending on what's set in sceDisplaySetFramebuf, in order to support half-framerate games - - // an initial hack could be to NOT end the frame if the buffer didn't change? that should work okay. + // Yeah, this has to be the right moment to end the frame. Give the graphics backend opportunity + // to blit the framebuffer, in order to support half-framerate games that otherwise wouldn't have + // anything to draw here. + gpu->CopyDisplayToOutput(); { host->EndFrame(); host->BeginFrame(); @@ -215,6 +217,7 @@ void sceDisplaySetFramebuf() { // Write immediately to the current framebuffer parameters framebuf = fbstate; + gpu->SetDisplayFramebuffer(framebuf.topaddr, framebuf.pspFramebufLinesize, framebuf.pspFramebufFormat); } else if (topaddr != 0) { diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index b7ea0f84b9..8e77be0aef 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -31,6 +31,7 @@ #include "../../Core/MemMap.h" #include "../../Core/Host.h" #include "../../Core/Config.h" +#include "../../Core/System.h" #include "../GPUState.h" #include "../ge_constants.h" @@ -53,14 +54,27 @@ ShaderManager shaderManager; extern u32 curTextureWidth; extern u32 curTextureHeight; +GLES_GPU::~GLES_GPU() +{ + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) + { + fbo_destroy((*iter)->fbo); + delete (*iter); + } + vfbs_.clear(); +} + void GLES_GPU::InitClear(int renderWidth, int renderHeight) { renderWidth_ = renderWidth; renderHeight_ = renderHeight; + widthFactor_ = (float)renderWidth / 480.0f; + heightFactor_ = (float)renderHeight / 272.0f; glClearColor(0,0,0,1); // glClearColor(1,0,1,1); glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glViewport(0, 0, renderWidth_, renderHeight_); } void GLES_GPU::BeginFrame() @@ -75,14 +89,110 @@ void GLES_GPU::BeginFrame() void GLES_GPU::SetDisplayFramebuffer(u32 framebuf, u32 stride, int format) { - // TODO + displayFramebufPtr_ = framebuf; + displayStride_ = stride; + displayFormat_ = format; } void GLES_GPU::CopyDisplayToOutput() { - // TODO + VirtualFramebuffer *vfb = GetDisplayFBO(); + fbo_unbind(); + + glViewport(0, 0, PSP_CoreParameter().outputWidth, PSP_CoreParameter().outputHeight); + + currentRenderVfb_ = 0; + + if (!vfb) { + // No framebuffer to display! Clear to black. + glClearColor(0,0,0,1); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + return; + } + + glDisable(GL_CULL_FACE); + glDisable(GL_DEPTH_TEST); + + fbo_bind_color_as_texture(vfb->fbo, 0); + + // These are in the output pixel coordinates + DrawActiveTexture(480, 272, true); + + shaderManager.DirtyShader(); + shaderManager.DirtyUniform(DIRTY_ALL); + + // Restore some state + ExecuteOp(gstate.cmdmem[GE_CMD_CULLFACEENABLE], 0xFFFFFFFF); + ExecuteOp(gstate.cmdmem[GE_CMD_ZTESTENABLE], 0xFFFFFFFF); } +GLES_GPU::VirtualFramebuffer *GLES_GPU::GetDisplayFBO() +{ + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) + { + if (((*iter)->fb_address & 0x3FFFFFF) == (displayFramebufPtr_ & 0x3FFFFFF)) { + // Could check w to but whatever + return *iter; + } + } + return 0; +} + +void GLES_GPU::SetRenderFrameBuffer() +{ + // Get parameters + u32 fb_address = (gstate.fbptr & 0xFFE000) | ((gstate.fbwidth & 0xFF0000) << 8); + int fb_stride = gstate.fbwidth & 0x3C0; + + u32 z_address = (gstate.zbptr & 0xFFE000) | ((gstate.zbwidth & 0xFF0000) << 8); + int z_stride = gstate.zbwidth & 0x3C0; + + // Yeah this is not completely right. but it'll do for now. + int drawing_width = ((gstate.region2) & 0x3FF) + 1; + int drawing_height = ((gstate.region2 >> 10) & 0x3FF) + 1; + + int fmt = gstate.framebufpixformat & 3; + + // Find a matching framebuffer + VirtualFramebuffer *vfb = 0; + for (auto iter = vfbs_.begin(); iter != vfbs_.end(); ++iter) + { + VirtualFramebuffer *v = *iter; + if (v->fb_address == fb_address) { + // Let's not be so picky for now. Let's say this is the one. + vfb = v; + // Update fb stride in case it changed + vfb->fb_stride = fb_stride; + break; + } + } + + // None found? Create one. + if (!vfb) { + vfb = new VirtualFramebuffer; + vfb->fb_address = fb_address; + vfb->fb_stride = fb_stride; + vfb->z_address = z_address; + vfb->z_stride = z_stride; + vfb->width = drawing_width; + vfb->height = drawing_height; + vfb->fbo = fbo_create(vfb->width * widthFactor_, vfb->height * heightFactor_, 1, true); + vfbs_.push_back(vfb); + fbo_bind_as_render_target(vfb->fbo); + glViewport(0, 0, renderWidth_, renderHeight_); + currentRenderVfb_ = vfb; + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); + return; + } + + if (vfb != currentRenderVfb_) + { + // Use it as a render target. + fbo_bind_as_render_target(vfb->fbo); + glViewport(0, 0, renderWidth_, renderHeight_); + currentRenderVfb_ = vfb; + } +} bool GLES_GPU::ProcessDLQueue() { @@ -279,6 +389,8 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_PRIM: { + SetRenderFrameBuffer(); + u32 count = data & 0xFFFF; u32 type = data >> 16; static const char* types[7] = { @@ -584,7 +696,11 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) } break; -// case GE_CMD_TRANSFERSRC: + case GE_CMD_TRANSFERSRC: + { + // Nothing to do, the next one prints + } + break; case GE_CMD_TRANSFERSRCW: { @@ -593,7 +709,12 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) DEBUG_LOG(G3D,"Block Transfer Src: %08x W: %i", xferSrc, xferSrcW); break; } -// case GE_CMD_TRANSFERDST: + + case GE_CMD_TRANSFERDST: + { + // Nothing to do, the next one prints + } + break; case GE_CMD_TRANSFERDSTW: { @@ -629,7 +750,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_TRANSFERSTART: { - DEBUG_LOG(G3D, "DL Texture Transfer Start: PixFormat %i", data); + ERROR_LOG(G3D, "UNIMPL DL Block Transfer Start: PixFormat %i", data); // TODO: Here we should check if the transfer overlaps a framebuffer or any textures, // and take appropriate action. If not, this should just be a block transfer within // GPU memory which could be implemented by a copy loop. diff --git a/GPU/GLES/DisplayListInterpreter.h b/GPU/GLES/DisplayListInterpreter.h index 4001484dcf..e79cfd2f0e 100644 --- a/GPU/GLES/DisplayListInterpreter.h +++ b/GPU/GLES/DisplayListInterpreter.h @@ -17,16 +17,20 @@ #pragma once +#include #include #include "../GPUInterface.h" +#include "gfx_es2/fbo.h" + class ShaderManager; class GLES_GPU : public GPUInterface { public: GLES_GPU() : interruptsEnabled_(true), dlIdGenerator(1) {} + ~GLES_GPU(); virtual void InitClear(int renderWidth, int renderHeight); virtual u32 EnqueueList(u32 listpc, u32 stall); virtual void UpdateStall(int listid, u32 newstall); @@ -42,6 +46,7 @@ public: virtual void BeginFrame(); private: + bool ProcessDLQueue(); bool interruptsEnabled_; @@ -52,6 +57,9 @@ private: int renderWidth_; int renderHeight_; + float widthFactor_; + float heightFactor_; + struct CmdProcessorState { u32 pc; @@ -76,6 +84,27 @@ private: u32 stackptr; bool finished; - u8 bezierBuf[16000]; + struct VirtualFramebuffer { + u32 fb_address; + u32 z_address; + int fb_stride; + int z_stride; + // There's also a top left of the drawing region, but meh... + int width; + int height; + + int format; // virtual, right now they are all RGBA8888 + FBO *fbo; + }; + + void SetRenderFrameBuffer(); // Uses parameters computed from gstate + // TODO: Break out into some form of FBO manager + VirtualFramebuffer *GetDisplayFBO(); + + std::list vfbs_; + + VirtualFramebuffer *currentRenderVfb_; + + u8 bezierBuf[16000]; }; diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 18fde6c1d8..e1ec0d9983 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -103,9 +103,6 @@ void DisplayDrawer_Init() glsl_bind(draw2dprogram); glUniform1i(draw2dprogram->sampler0, 0); - Matrix4x4 ortho; - ortho.setOrtho(0, 480, 272, 0, -1, 1); - glUniformMatrix4fv(draw2dprogram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); glsl_unbind(); // And an initial clear. We don't clear per frame as the games are supposed to handle that @@ -125,9 +122,6 @@ void DisplayDrawer_Shutdown() void DisplayDrawer_DrawFramebuffer(u8 *framebuf, int pixelFormat, int linesize) { - float u1 = 1.0f; - float v1 = 1.0f; - for (int y = 0; y < 272; y++) { switch (pixelFormat) @@ -196,10 +190,23 @@ void DisplayDrawer_DrawFramebuffer(u8 *framebuf, int pixelFormat, int linesize) glBindTexture(GL_TEXTURE_2D,backbufTex); glTexSubImage2D(GL_TEXTURE_2D,0,0,0,480,272, GL_RGBA, GL_UNSIGNED_BYTE, realFB); - const float pos[12] = {0,0,0, 480,0,0, 480,272,0, 0,272,0}; - const float texCoords[8] = {0, 0, u1, 0, u1, v1, 0, v1}; + DrawActiveTexture(480, 272); +} + +void DrawActiveTexture(int w, int h, bool flip) +{ + float u2 = 1.0f; + float v1 = flip ? 1.0 : 0.0f; + float v2 = flip ? 0.0 : 1.0f; + + const float pos[12] = {0,0,0, w,0,0, w,h,0, 0,h,0}; + const float texCoords[8] = {0, v1, u2, v1, u2, v2, 0, v2}; glsl_bind(draw2dprogram); + Matrix4x4 ortho; + ortho.setOrtho(0, 480, 272, 0, -1, 1); + glUniformMatrix4fv(draw2dprogram->u_viewproj, 1, GL_FALSE, ortho.getReadPtr()); + glEnableVertexAttribArray(draw2dprogram->a_position); glEnableVertexAttribArray(draw2dprogram->a_texcoord0); glVertexAttribPointer(draw2dprogram->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos); diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index f2eb189f5a..8b540dc71a 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -32,3 +32,4 @@ enum PspDisplayPixelFormat { void DisplayDrawer_Init(); void DisplayDrawer_DrawFramebuffer(u8 *framebuf, int pixelFormat, int linesize); void DisplayDrawer_Shutdown(); +void DrawActiveTexture(int w, int h, bool flip = false); diff --git a/android/jni/EmuScreen.cpp b/android/jni/EmuScreen.cpp index 38df5a67d7..df98e1001e 100644 --- a/android/jni/EmuScreen.cpp +++ b/android/jni/EmuScreen.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include "gfx_es2/glsl_program.h" +#include "gfx_es2/fbo.h" #include "input/input_state.h" #include "ui/ui.h" @@ -137,6 +138,7 @@ void EmuScreen::update(InputState &input) } if (input.pad_buttons_down & (PAD_BUTTON_MENU | PAD_BUTTON_BACK)) { + fbo_unbind(); screenManager()->push(new InGameMenuScreen()); } } diff --git a/native b/native index 7e36cef3ae..0598fc425c 160000 --- a/native +++ b/native @@ -1 +1 @@ -Subproject commit 7e36cef3aeac81dd8071422be93b00128a0cb110 +Subproject commit 0598fc425c6b898af9fee5b49b2054b2fffa55e6