GL: Framebuffer management using FBOs. Fixes flicker in Lumines and many others.

This commit is contained in:
Henrik Rydgard 2012-11-19 23:29:14 +01:00
parent 91e1cce17e
commit 8b67975a47
7 changed files with 181 additions and 18 deletions

View File

@ -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)
{

View File

@ -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.

View File

@ -17,16 +17,20 @@
#pragma once
#include <list>
#include <vector>
#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<VirtualFramebuffer *> vfbs_;
VirtualFramebuffer *currentRenderVfb_;
u8 bezierBuf[16000];
};

View File

@ -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);

View File

@ -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);

View File

@ -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());
}
}

2
native

@ -1 +1 @@
Subproject commit 7e36cef3aeac81dd8071422be93b00128a0cb110
Subproject commit 0598fc425c6b898af9fee5b49b2054b2fffa55e6