Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Another attempt at framebuffer size estimate changes #6211

Merged
merged 11 commits into from
Jun 3, 2014
172 changes: 110 additions & 62 deletions GPU/GLES/Framebuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ void FramebufferManager::MakePixelTexture(const u8 *srcPixels, GEBufferFormat sr
void FramebufferManager::DrawPixels(VirtualFramebuffer *vfb, int dstX, int dstY, const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, int width, int height) {
MakePixelTexture(srcPixels, srcPixelFormat, srcStride, width, height);
DisableState();
DrawActiveTexture(0, dstX, dstY, width, height, vfb->width, vfb->height, false, 0.0f, 0.0f, 1.0f, 1.0f);
DrawActiveTexture(0, dstX, dstY, width, height, vfb->bufferWidth, vfb->bufferHeight, false, 0.0f, 0.0f, 1.0f, 1.0f);
}

void FramebufferManager::DrawFramebuffer(const u8 *srcPixels, GEBufferFormat srcPixelFormat, int srcStride, bool applyPostShader) {
Expand Down Expand Up @@ -621,7 +621,7 @@ VirtualFramebuffer *FramebufferManager::GetVFBAt(u32 addr) {
VirtualFramebuffer *match = NULL;
for (size_t i = 0; i < vfbs_.size(); ++i) {
VirtualFramebuffer *v = vfbs_[i];
if (MaskedEqual(v->fb_address, addr) && v->format == displayFormat_ && v->width >= 480) {
if (MaskedEqual(v->fb_address, addr)) {
// Could check w too but whatever
if (match == NULL || match->last_frame_render < v->last_frame_render) {
match = v;
Expand All @@ -637,38 +637,75 @@ VirtualFramebuffer *FramebufferManager::GetVFBAt(u32 addr) {
}

// Heuristics to figure out the size of FBO to create.
static void EstimateDrawingSize(int &drawing_width, int &drawing_height) {
int default_width = 480;
int default_height = 272;
int viewport_width = (int) gstate.getViewportX1();
int viewport_height = (int) gstate.getViewportY1();
int region_width = gstate.getRegionX2() + 1;
int region_height = gstate.getRegionY2() + 1;
int scissor_width = gstate.getScissorX2() + 1;
int scissor_height = gstate.getScissorY2() + 1;
int fb_stride = gstate.FrameBufStride();
void FramebufferManager::EstimateDrawingSize(int &drawing_width, int &drawing_height) {
const int viewport_width = (int) gstate.getViewportX1();
const int viewport_height = (int) gstate.getViewportY1();
const int region_width = gstate.getRegionX2() + 1;
const int region_height = gstate.getRegionY2() + 1;
const int scissor_width = gstate.getScissorX2() + 1;
const int scissor_height = gstate.getScissorY2() + 1;
const int fb_stride = std::max(gstate.FrameBufStride(), 4);

DEBUG_LOG(SCEGE,"viewport : %ix%i, region : %ix%i , scissor: %ix%i, stride: %i, %i", viewport_width,viewport_height, region_width, region_height, scissor_width, scissor_height, fb_stride, gstate.isModeThrough());

// Viewport may return 0x0 for example FF Type-0 / God of War and we set it to 480x272
if (viewport_width <= 1 && viewport_height <=1) {
viewport_width = default_width;
viewport_height = default_height;
// TODO: God of War sets region = 1024x1024 on a buffer that is used only as 64x64, and occupies 1024x64 of VRAM.
// In this case, viewport=64x64, region=1024x1024, scissor varies (480x272 sometimes), stride=1024

// Games don't always set any of these. Take the greatest parameter that looks valid based on stride.
if (viewport_width > 4 && viewport_width <= fb_stride) {
drawing_width = viewport_width;
drawing_height = viewport_height;
// Some games specify a viewport with 0.5, but don't have VRAM for 273. 480x272 is the buffer size.
if (viewport_width == 481 && region_width == 480 && viewport_height == 273 && region_height == 272) {
drawing_width = 480;
drawing_height = 272;
}
// Sometimes region is set larger than the VRAM for the framebuffer.
if (region_width <= fb_stride && region_width > drawing_width) {
drawing_width = region_width;
drawing_height = std::max(drawing_height, region_height);
}
// Scissor is often set to a subsection of the framebuffer, so we pay the least attention to it.
if (scissor_width <= fb_stride && scissor_width > drawing_width) {
drawing_width = scissor_width;
drawing_height = std::max(drawing_height, scissor_height);
}
} else {
// If viewport wasn't valid, let's just take the greatest anything regardless of stride.
drawing_width = std::min(std::max(region_width, scissor_width), fb_stride);
drawing_height = std::max(region_height, scissor_height);
}

if (fb_stride > 0 && fb_stride <= 512) {
if (fb_stride == viewport_width) {
drawing_width = viewport_width;
drawing_height = viewport_height;
} else {
drawing_width = scissor_width;
// Assume no buffer is > 512 tall, it couldn't be textured or displayed fully if so.
if (drawing_height > 512) {
if (region_height < 512) {
drawing_height = region_height;
} else if (scissor_height < 512) {
drawing_height = scissor_height;
}
} else {
drawing_width = default_width;
drawing_height = default_height;
}

if (viewport_width != region_width) {
// The majority of the time, these are equal. If not, let's check what we know.
const u32 fb_address = gstate.getFrameBufAddress();
u32 nearest_address = 0xFFFFFFFF;
for (size_t i = 0; i < vfbs_.size(); ++i) {
const u32 other_address = vfbs_[i]->fb_address | 0x44000000;
if (other_address > fb_address && other_address < nearest_address) {
nearest_address = other_address;
}
}

// Unless the game is using overlapping buffers, the next buffer should be far enough away.
// This catches some cases where we can know this.
// Hmm. The problem is that we could only catch it for the first of two buffers...
const u32 bpp = gstate.FrameBufFormat() == GE_FORMAT_8888 ? 4 : 2;
int avail_height = (nearest_address - fb_address) / (fb_stride * bpp);
if (avail_height < drawing_height && avail_height == region_height) {
drawing_width = std::min(region_width, fb_stride);
drawing_height = avail_height;
}
}
}

void FramebufferManager::DestroyFramebuf(VirtualFramebuffer *v) {
Expand Down Expand Up @@ -724,15 +761,11 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
gstate_c.framebufChanged = false;

// Get parameters
u32 fb_address = gstate.getFrameBufRawAddress();
int fb_stride = gstate.FrameBufStride();
const u32 fb_address = gstate.getFrameBufRawAddress();
const int fb_stride = gstate.FrameBufStride();

u32 z_address = gstate.getDepthBufRawAddress();
int z_stride = gstate.DepthBufStride();

// 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;
const u32 z_address = gstate.getDepthBufRawAddress();
const int z_stride = gstate.DepthBufStride();

GEBufferFormat fmt = gstate.FrameBufFormat();

Expand All @@ -741,9 +774,6 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
int drawing_width, drawing_height;
EstimateDrawingSize(drawing_width, drawing_height);

int buffer_width = drawing_width;
int buffer_height = drawing_height;

// Find a matching framebuffer
VirtualFramebuffer *vfb = 0;
size_t i;
Expand All @@ -753,31 +783,35 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
vfb = v;
// Update fb stride in case it changed
vfb->fb_stride = fb_stride;
if (v->width < drawing_width && v->height < drawing_height) {
v->width = drawing_width;
v->height = drawing_height;
}
if (v->format != fmt) {
v->width = drawing_width;
v->height = drawing_height;
v->format = fmt;
}
v->format = fmt;
v->width = drawing_width;
v->height = drawing_height;
break;
}
}

VirtualFramebuffer *destroyVfb = 0;
if (vfb) {
if ((drawing_width != vfb->bufferWidth || drawing_height != vfb->bufferHeight)) {
// If it's newly wrong, or changing every frame, just keep track.
if (vfb->newWidth != drawing_width || vfb->newHeight != drawing_height) {
// Even if it's not newly wrong, if this is larger we need to resize up.
if (vfb->width > vfb->bufferWidth || vfb->height > vfb->bufferHeight) {
destroyVfb = vfb;
vfb = NULL;
} else if (vfb->newWidth != drawing_width || vfb->newHeight != drawing_height) {
// If it's newly wrong, or changing every frame, just keep track.
vfb->newWidth = drawing_width;
vfb->newHeight = drawing_height;
vfb->lastFrameNewSize = gpuStats.numFlips;
} else if (vfb->lastFrameNewSize + FBO_OLD_AGE < gpuStats.numFlips) {
// Okay, it's changed for a while (and stayed that way.) Let's start over.
DestroyFramebuf(vfb);
vfbs_.erase(vfbs_.begin() + i);
vfb = NULL;
// But only if we really need to, to avoid blinking.
bool needsRecreate = vfb->bufferWidth > fb_stride;
needsRecreate = needsRecreate || vfb->newWidth > vfb->bufferWidth || vfb->newWidth * 2 < vfb->bufferWidth;
needsRecreate = needsRecreate || vfb->newHeight > vfb->newHeight || vfb->newHeight * 2 < vfb->newHeight;
if (needsRecreate) {
destroyVfb = vfb;
vfb = NULL;
}
}
} else {
// It's not different, let's keep track of that too.
Expand Down Expand Up @@ -809,8 +843,8 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
vfb->lastFrameNewSize = gpuStats.numFlips;
vfb->renderWidth = (u16)(drawing_width * renderWidthFactor);
vfb->renderHeight = (u16)(drawing_height * renderHeightFactor);
vfb->bufferWidth = buffer_width;
vfb->bufferHeight = buffer_height;
vfb->bufferWidth = drawing_width;
vfb->bufferHeight = drawing_height;
vfb->format = fmt;
vfb->usageFlags = FB_USAGE_RENDERTARGET;
vfb->dirtyAfterDisplay = true;
Expand Down Expand Up @@ -843,6 +877,12 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
vfb->fbo = fbo_create(vfb->renderWidth, vfb->renderHeight, 1, true, vfb->colorDepth);
if (vfb->fbo) {
fbo_bind_as_render_target(vfb->fbo);

if (destroyVfb) {
// Copy over the contents of the framebuffer we're replacing.
BlitFramebuffer_(vfb, 0, 0, destroyVfb, 0, 0, vfb->width, vfb->height, 0);
fbo_bind_as_render_target(vfb->fbo);
}
} else {
ERROR_LOG(SCEGE, "Error creating FBO! %i x %i", vfb->renderWidth, vfb->renderHeight);
}
Expand All @@ -852,6 +892,17 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
gstate_c.skipDrawReason |= SKIPDRAW_NON_DISPLAYED_FB;
}

if (destroyVfb) {
// Do this before notifying of creation at the same address.
DestroyFramebuf(destroyVfb);
destroyVfb = 0;
vfbs_.erase(vfbs_.begin() + i);

INFO_LOG(SCEGE, "Resizing FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format);
} else {
INFO_LOG(SCEGE, "Creating FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format);
}

textureCache_->NotifyFramebuffer(vfb->fb_address, vfb, NOTIFY_FB_CREATED);

vfb->last_frame_render = gpuStats.numFlips;
Expand All @@ -867,8 +918,6 @@ void FramebufferManager::DoSetRenderFrameBuffer() {
framebufRangeEnd_ = fb_address_mem + byteSize;
}

INFO_LOG(SCEGE, "Creating FBO for %08x : %i x %i x %i", vfb->fb_address, vfb->width, vfb->height, vfb->format);

// Let's check for depth buffer overlap. Might be interesting.
bool sharingReported = false;
for (size_t i = 0, end = vfbs_.size(); i < end; ++i) {
Expand Down Expand Up @@ -1059,7 +1108,6 @@ void FramebufferManager::BindFramebufferColor(VirtualFramebuffer *framebuffer, b

fbo_bind_as_render_target(currentRenderVfb_->fbo);
fbo_bind_color_as_texture(renderCopy, 0);
glstate.viewport.restore();
} else {
fbo_bind_color_as_texture(framebuffer->fbo, 0);
}
Expand Down Expand Up @@ -1132,7 +1180,7 @@ void FramebufferManager::CopyDisplayToOutput() {
if (!usePostShader_) {
glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
// These are in the output display coordinates
DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 0.0f, 0.0f, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height);
DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 0.0f, 0.0f, 480.0f / (float)vfb->bufferWidth, 272.0f / (float)vfb->bufferHeight);
} else if (usePostShader_ && extraFBOs_.size() == 1 && !postShaderAtOutputResolution_) {
// An additional pass, post-processing shader to the extra FBO.
fbo_bind_as_render_target(extraFBOs_[0]);
Expand All @@ -1152,12 +1200,12 @@ void FramebufferManager::CopyDisplayToOutput() {
colorTexture = fbo_get_color_texture(extraFBOs_[0]);
glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
// These are in the output display coordinates
DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 0.0f, 0.0f, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height);
DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 0.0f, 0.0f, 480.0f / (float)vfb->bufferWidth, 272.0f / (float)vfb->bufferHeight);
} else {
// Use post-shader, but run shader at output resolution.
glstate.viewport.set(0, 0, PSP_CoreParameter().pixelWidth, PSP_CoreParameter().pixelHeight);
// These are in the output display coordinates
DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 0.0f, 0.0f, 480.0f / (float)vfb->width, 272.0f / (float)vfb->height, postShaderProgram_);
DrawActiveTexture(colorTexture, x, y, w, h, (float)PSP_CoreParameter().pixelWidth, (float)PSP_CoreParameter().pixelHeight, true, 0.0f, 0.0f, 480.0f / (float)vfb->bufferWidth, 272.0f / (float)vfb->bufferHeight, postShaderProgram_);
}

glBindTexture(GL_TEXTURE_2D, 0);
Expand Down Expand Up @@ -1380,14 +1428,14 @@ void FramebufferManager::BlitFramebuffer_(VirtualFramebuffer *dst, int dstX, int
// Make sure our 2D drawing program is ready. Compiles only if not already compiled.
CompileDraw2DProgram();

glViewport(0, 0, dst->width, dst->height);
glViewport(0, 0, dst->renderWidth, dst->renderHeight);
DisableState();

// The first four coordinates are relative to the 6th and 7th arguments of DrawActiveTexture.
// Should maybe revamp that interface.
float srcW = src->width;
float srcH = src->height;
DrawActiveTexture(0, dstX, dstY, w, h, dst->width, dst->height, false, srcX / srcW, srcY / srcH, (srcX + w) / srcW, (srcY + h) / srcH, draw2dprogram_);
float srcW = src->bufferWidth;
float srcH = src->bufferHeight;
DrawActiveTexture(0, dstX, dstY, w, h, dst->bufferWidth, dst->bufferHeight, !flip, srcX / srcW, srcY / srcH, (srcX + w) / srcW, (srcY + h) / srcH, draw2dprogram_);
glBindTexture(GL_TEXTURE_2D, 0);
textureCache_->ForgetLastTexture();
}
Expand Down
3 changes: 3 additions & 0 deletions GPU/GLES/Framebuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,8 @@ class FramebufferManager {
int GetRenderHeight() const { return currentRenderVfb_ ? currentRenderVfb_->renderHeight : 272; }
int GetTargetWidth() const { return currentRenderVfb_ ? currentRenderVfb_->width : 480; }
int GetTargetHeight() const { return currentRenderVfb_ ? currentRenderVfb_->height : 272; }
int GetTargetBufferWidth() const { return currentRenderVfb_ ? currentRenderVfb_->bufferWidth : 480; }
int GetTargetBufferHeight() const { return currentRenderVfb_ ? currentRenderVfb_->bufferHeight : 272; }

u32 PrevDisplayFramebufAddr() {
return prevDisplayFramebuf_ ? (0x04000000 | prevDisplayFramebuf_->fb_address) : 0;
Expand Down Expand Up @@ -233,6 +235,7 @@ class FramebufferManager {

void SetNumExtraFBOs(int num);

void EstimateDrawingSize(int &drawing_width, int &drawing_height);
static void DisableState();
static void ClearBuffer();
static bool MaskedEqual(u32 addr1, u32 addr2);
Expand Down
11 changes: 7 additions & 4 deletions GPU/GLES/StateMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -461,6 +461,8 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
}
}

bool throughmode = gstate.isModeThrough();

float renderWidthFactor, renderHeightFactor;
float renderWidth, renderHeight;
float renderX, renderY;
Expand All @@ -470,8 +472,8 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
renderY = 0.0f;
renderWidth = framebufferManager_->GetRenderWidth();
renderHeight = framebufferManager_->GetRenderHeight();
renderWidthFactor = (float)renderWidth / framebufferManager_->GetTargetWidth();
renderHeightFactor = (float)renderHeight / framebufferManager_->GetTargetHeight();
renderWidthFactor = (float)renderWidth / framebufferManager_->GetTargetBufferWidth();
renderHeightFactor = (float)renderHeight / framebufferManager_->GetTargetBufferHeight();
} else {
// TODO: Aspect-ratio aware and centered
float pixelW = PSP_CoreParameter().pixelWidth;
Expand All @@ -481,8 +483,6 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
renderHeightFactor = renderHeight / 272.0f;
}

bool throughmode = gstate.isModeThrough();

// Scissor
int scissorX1 = gstate.getScissorX1();
int scissorY1 = gstate.getScissorY1();
Expand Down Expand Up @@ -518,6 +518,9 @@ void TransformDrawEngine::ApplyDrawState(int prim) {
float offsetY = gstate.getOffsetY();

if (throughmode) {
// If the buffer is too large, offset the viewport to the top.
renderY += renderHeight - framebufferManager_->GetTargetHeight() * renderHeightFactor;

// No viewport transform here. Let's experiment with using region.
glstate.viewport.set(
renderX + (0 + regionX1) * renderWidthFactor,
Expand Down
4 changes: 2 additions & 2 deletions GPU/GLES/StencilBuffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ bool FramebufferManager::NotifyStencilUpload(u32 addr, int size) {

shaderManager_->DirtyLastShader();

MakePixelTexture(Memory::GetPointer(addr), dstBuffer->format, dstBuffer->fb_stride, dstBuffer->width, dstBuffer->height);
MakePixelTexture(Memory::GetPointer(addr), dstBuffer->format, dstBuffer->fb_stride, dstBuffer->renderWidth, dstBuffer->renderHeight);
DisableState();
glstate.colorMask.set(GL_FALSE, GL_FALSE, GL_FALSE, GL_TRUE);
glstate.stencilTest.enable();
Expand Down Expand Up @@ -136,7 +136,7 @@ bool FramebufferManager::NotifyStencilUpload(u32 addr, int size) {
glStencilMask(i);
glUniform1f(u_stencilValue, i * (1.0f / 255.0f));
}
DrawActiveTexture(0, 0, 0, dstBuffer->width, dstBuffer->height, dstBuffer->width, dstBuffer->height, false, 0.0f, 0.0f, 1.0f, 1.0f, stencilUploadProgram_);
DrawActiveTexture(0, 0, 0, dstBuffer->width, dstBuffer->height, dstBuffer->bufferWidth, dstBuffer->bufferHeight, false, 0.0f, 0.0f, 1.0f, 1.0f, stencilUploadProgram_);
}
glStencilMask(0xFF);

Expand Down
4 changes: 2 additions & 2 deletions GPU/GLES/TextureCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -959,8 +959,8 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) {
entry->framebuffer->last_frame_used = gpuStats.numFlips;

// We need to force it, since we may have set it on a texture before attaching.
gstate_c.curTextureWidth = entry->framebuffer->width;
gstate_c.curTextureHeight = entry->framebuffer->height;
gstate_c.curTextureWidth = entry->framebuffer->bufferWidth;
gstate_c.curTextureHeight = entry->framebuffer->bufferHeight;
gstate_c.flipTexture = true;
gstate_c.textureFullAlpha = entry->framebuffer->format == GE_FORMAT_565;
gstate_c.textureSimpleAlpha = gstate_c.textureFullAlpha;
Expand Down