Merge pull request #6239 from unknownbrackets/bof3-workaround

Try to workaround Breath of Fire 3's render-to-tex
This commit is contained in:
Henrik Rydgård 2014-06-06 20:42:13 +02:00
commit 96787a95d7
4 changed files with 81 additions and 17 deletions

View File

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

View File

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

View File

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

View File

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