diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index d36fa6dcee..5177a14342 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -188,6 +188,8 @@ public: int GetTargetHeight() const { return currentRenderVfb_ ? currentRenderVfb_->height : 272; } int GetTargetBufferWidth() const { return currentRenderVfb_ ? currentRenderVfb_->bufferWidth : 480; } int GetTargetBufferHeight() const { return currentRenderVfb_ ? currentRenderVfb_->bufferHeight : 272; } + int GetTargetStride() const { return currentRenderVfb_ ? currentRenderVfb_->fb_stride : 512; } + GEBufferFormat GetTargetFormat() const { return currentRenderVfb_ ? currentRenderVfb_->format : displayFormat_; } u32 PrevDisplayFramebufAddr() { return prevDisplayFramebuf_ ? (0x04000000 | prevDisplayFramebuf_->fb_address) : 0; diff --git a/GPU/GLES/SoftwareTransform.cpp b/GPU/GLES/SoftwareTransform.cpp index 32bee314b9..54833007d9 100644 --- a/GPU/GLES/SoftwareTransform.cpp +++ b/GPU/GLES/SoftwareTransform.cpp @@ -25,6 +25,7 @@ #include "GPU/Common/TransformCommon.h" #include "GPU/GLES/Framebuffer.h" #include "GPU/GLES/ShaderManager.h" +#include "GPU/GLES/TextureCache.h" #include "GPU/GLES/TransformPipeline.h" // This is the software transform pipeline, which is necessary for supporting RECT @@ -379,7 +380,6 @@ void TransformDrawEngine::SoftwareTransformAndDraw( // Here's the best opportunity to try to detect rectangles used to clear the screen, and // replace them with real OpenGL clears. This can provide a speedup on certain mobile chips. - // Disabled for now - depth does not come out exactly the same. // // An alternative option is to simply ditch all the verts except the first and last to create a single // rectangle out of many. Quite a small optimization though. @@ -421,6 +421,27 @@ void TransformDrawEngine::SoftwareTransformAndDraw( return; } + if (gstate_c.flipTexture && transformed[0].v < 0.0f && transformed[0].v > 1.0f - heightFactor) { + // Okay, so we're texturing from outside the framebuffer, but inside the texture height. + // Breath of Fire 3 does this to access a render surface at +curTextureHeight. + const u32 bpp = framebufferManager_->GetTargetFormat() == GE_FORMAT_8888 ? 4 : 2; + const u32 fb_size = bpp * framebufferManager_->GetTargetStride() * gstate_c.curTextureHeight; + if (textureCache_->SetOffsetTexture(fb_size)) { + const float oldWidthFactor = widthFactor; + const float oldHeightFactor = heightFactor; + widthFactor = (float) w / (float) gstate_c.curTextureWidth; + heightFactor = (float) h / (float) gstate_c.curTextureHeight; + + for (int index = 0; index < maxIndex; ++index) { + transformed[index].u *= widthFactor / oldWidthFactor; + // Inverse it back to scale to the new FBO, and add 1.0f to account for old FBO. + transformed[index].v = 1.0f - transformed[index].v - 1.0f; + transformed[index].v *= heightFactor / oldHeightFactor; + transformed[index].v = 1.0f - transformed[index].v; + } + } + } + // Step 2: expand rectangles. const TransformedVertex *drawBuffer = transformed; int numTrans = 0; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 0dc8ca95ab..b545002325 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -883,17 +883,17 @@ bool SetDebugTexture() { } #endif -void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) { - entry->framebuffer->usageFlags |= FB_USAGE_TEXTURE; +void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { + framebuffer->usageFlags |= FB_USAGE_TEXTURE; bool useBufferedRendering = g_Config.iRenderingMode != FB_NON_BUFFERED_MODE; if (useBufferedRendering) { GLuint program = 0; if (entry->status & TexCacheEntry::STATUS_DEPALETTIZE) { - program = depalShaderCache_->GetDepalettizeShader(entry->framebuffer->format); + program = depalShaderCache_->GetDepalettizeShader(framebuffer->format); } if (program) { GLuint clutTexture = depalShaderCache_->GetClutTexture(clutHash_, clutBuf_); - FBO *depalFBO = framebufferManager_->GetTempFBO(entry->framebuffer->renderWidth, entry->framebuffer->renderHeight, FBO_8888); + FBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, FBO_8888); fbo_bind_as_render_target(depalFBO); static const float pos[12] = { -1, -1, -1, @@ -925,7 +925,7 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) { glBindTexture(GL_TEXTURE_2D, clutTexture); glActiveTexture(GL_TEXTURE0); - framebufferManager_->BindFramebufferColor(entry->framebuffer, true); + framebufferManager_->BindFramebufferColor(framebuffer, true); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); entry->status |= TexCacheEntry::STATUS_TEXPARAM_DIRTY; @@ -939,7 +939,7 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) { #if !defined(USING_GLES2) glDisable(GL_LOGIC_OP); #endif - glViewport(0, 0, entry->framebuffer->renderWidth, entry->framebuffer->renderHeight); + glViewport(0, 0, framebuffer->renderWidth, framebuffer->renderHeight); glVertexAttribPointer(a_position, 3, GL_FLOAT, GL_FALSE, 12, pos); glVertexAttribPointer(a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, uv); @@ -961,27 +961,67 @@ void TextureCache::SetTextureFramebuffer(TexCacheEntry *entry) { gstate_c.textureSimpleAlpha = alphaStatus == TexCacheEntry::STATUS_ALPHA_SIMPLE; } else { entry->status &= ~TexCacheEntry::STATUS_DEPALETTIZE; - framebufferManager_->BindFramebufferColor(entry->framebuffer); + framebufferManager_->BindFramebufferColor(framebuffer); - gstate_c.textureFullAlpha = entry->framebuffer->format == GE_FORMAT_565; + gstate_c.textureFullAlpha = framebuffer->format == GE_FORMAT_565; gstate_c.textureSimpleAlpha = gstate_c.textureFullAlpha; } // Keep the framebuffer alive. - entry->framebuffer->last_frame_used = gpuStats.numFlips; + 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->bufferWidth; - gstate_c.curTextureHeight = entry->framebuffer->bufferHeight; + gstate_c.curTextureWidth = framebuffer->bufferWidth; + gstate_c.curTextureHeight = framebuffer->bufferHeight; gstate_c.flipTexture = true; UpdateSamplingParams(*entry, true); } else { - if (entry->framebuffer->fbo) - entry->framebuffer->fbo = 0; + if (framebuffer->fbo) + framebuffer->fbo = 0; glBindTexture(GL_TEXTURE_2D, 0); } } +bool TextureCache::SetOffsetTexture(u32 offset) { + if (g_Config.iRenderingMode != FB_BUFFERED_MODE) { + return false; + } + u32 texaddr = gstate.getTextureAddress(0); + if (!Memory::IsValidAddress(texaddr) || !Memory::IsValidAddress(texaddr + offset)) { + return false; + } + + u64 cachekey = (u64)(texaddr & 0x0FFFFFFF) << 32; + TexCache::iterator iter = cache.find(cachekey); + if (iter == cache.end()) { + return false; + } + TexCacheEntry *entry = &iter->second; + + texaddr += offset; + cachekey = (u64)(texaddr & 0x0FFFFFFF) << 32; + + for (size_t i = 0, n = fbCache_.size(); i < n; ++i) { + auto framebuffer = fbCache_[i]; + // This is a rough heuristic, because sometimes our framebuffers are too tall. + static const u32 MAX_SUBAREA_Y_OFFSET = 32; + + // Must be in VRAM so | 0x04000000 it is, and ignore any uncached bit. + const u32 addr = (framebuffer->fb_address | 0x04000000) & 0x0FFFFFFF; + const u64 cacheKeyStart = (u64)addr << 32; + // If it has a clut, those are the low 32 bits, so it'll be inside this range. + // Also, if it's a subsample of the buffer, it'll also be within the FBO. + const u64 cacheKeyEnd = cacheKeyStart + ((u64)(framebuffer->fb_stride * MAX_SUBAREA_Y_OFFSET) << 32); + + if (cachekey >= cacheKeyStart && cachekey < cacheKeyEnd) { + SetTextureFramebuffer(entry, framebuffer); + return true; + } + } + + return false; +} + void TextureCache::SetTexture(bool force) { #ifdef DEBUG_TEXTURES if (SetDebugTexture()) { @@ -1053,7 +1093,7 @@ void TextureCache::SetTexture(bool force) { // Check for FBO - slow! if (entry->framebuffer) { if (match) { - SetTextureFramebuffer(entry); + SetTextureFramebuffer(entry, entry->framebuffer); lastBoundTexture = -1; entry->lastFrame = gpuStats.numFlips; return; @@ -1266,7 +1306,7 @@ void TextureCache::SetTexture(bool force) { // If we ended up with a framebuffer, attach it - no texture decoding needed. if (entry->framebuffer) { - SetTextureFramebuffer(entry); + SetTextureFramebuffer(entry, entry->framebuffer); lastBoundTexture = -1; entry->lastFrame = gpuStats.numFlips; return; diff --git a/GPU/GLES/TextureCache.h b/GPU/GLES/TextureCache.h index e1b04efbd1..ab23a3b17c 100644 --- a/GPU/GLES/TextureCache.h +++ b/GPU/GLES/TextureCache.h @@ -57,6 +57,7 @@ public: ~TextureCache(); void SetTexture(bool force = false); + bool SetOffsetTexture(u32 offset); void Clear(bool delete_them); void StartFrame(); @@ -180,7 +181,7 @@ private: void UpdateCurrentClut(); void AttachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer, bool exactMatch); void DetachFramebuffer(TexCacheEntry *entry, u32 address, VirtualFramebuffer *framebuffer); - void SetTextureFramebuffer(TexCacheEntry *entry); + void SetTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer); TexCacheEntry *GetEntryAt(u32 texaddr);