diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index b4426690e5..f7b9a33475 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -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) { @@ -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; @@ -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 - if (fb_stride > 0 && fb_stride <= 512) { - if (fb_stride == viewport_width) { - drawing_width = viewport_width; - drawing_height = viewport_height; - } else { + // 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 = scissor_height; + drawing_height = std::max(drawing_height, scissor_height); } } else { - drawing_width = default_width; - drawing_height = default_height; + // 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); } + // 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; + } + } + + 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) { @@ -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(); @@ -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; @@ -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. @@ -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; @@ -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); } @@ -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; @@ -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) { @@ -1057,7 +1106,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); } @@ -1130,7 +1178,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]); @@ -1150,12 +1198,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); @@ -1375,14 +1423,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(); } diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 7dd127b1ff..d36fa6dcee 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -186,6 +186,8 @@ public: 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; @@ -233,6 +235,7 @@ private: 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); diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 47cd3a49c9..c257feef90 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -461,6 +461,8 @@ void TransformDrawEngine::ApplyDrawState(int prim) { } } + bool throughmode = gstate.isModeThrough(); + float renderWidthFactor, renderHeightFactor; float renderWidth, renderHeight; float renderX, renderY; @@ -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; @@ -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(); @@ -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, diff --git a/GPU/GLES/StencilBuffer.cpp b/GPU/GLES/StencilBuffer.cpp index 53937add18..196d4f0c38 100644 --- a/GPU/GLES/StencilBuffer.cpp +++ b/GPU/GLES/StencilBuffer.cpp @@ -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(); @@ -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); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index c24bdde5b2..0dc8ca95ab 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -971,8 +971,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; UpdateSamplingParams(*entry, true); } else {