diff --git a/GPU/Common/FramebufferCommon.h b/GPU/Common/FramebufferCommon.h index e01ea005f7..9eed4663ab 100644 --- a/GPU/Common/FramebufferCommon.h +++ b/GPU/Common/FramebufferCommon.h @@ -115,6 +115,12 @@ extern GPUgstate gstate; void GetFramebufferHeuristicInputs(FramebufferHeuristicParams *params, const GPUgstate &gstate); +enum BindFramebufferColorFlags { + BINDFBCOLOR_SKIP_COPY = 0, + BINDFBCOLOR_MAY_COPY = 1, + BINDFBCOLOR_MAY_COPY_WITH_UV = 3, +}; + class FramebufferManagerCommon { public: FramebufferManagerCommon(); diff --git a/GPU/Directx9/FramebufferDX9.cpp b/GPU/Directx9/FramebufferDX9.cpp index 89d7b930ba..7325b16fa0 100644 --- a/GPU/Directx9/FramebufferDX9.cpp +++ b/GPU/Directx9/FramebufferDX9.cpp @@ -608,7 +608,7 @@ namespace DX9 { return offscreen; } - void FramebufferManagerDX9::BindFramebufferColor(int stage, VirtualFramebuffer *framebuffer, bool skipCopy) { + void FramebufferManagerDX9::BindFramebufferColor(int stage, VirtualFramebuffer *framebuffer, int flags) { if (framebuffer == NULL) { framebuffer = currentRenderVfb_; } @@ -621,6 +621,7 @@ namespace DX9 { // currentRenderVfb_ will always be set when this is called, except from the GE debugger. // Let's just not bother with the copy in that case. + bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0; if (GPUStepping::IsStepping() || g_Config.bDisableSlowFramebufEffects) { skipCopy = true; } @@ -630,7 +631,22 @@ namespace DX9 { if (renderCopy) { VirtualFramebuffer copyInfo = *framebuffer; copyInfo.fbo = renderCopy; - BlitFramebuffer(©Info, 0, 0, framebuffer, 0, 0, framebuffer->drawnWidth, framebuffer->drawnHeight, 0, false); + + int x = 0; + int y = 0; + int w = framebuffer->drawnWidth; + int h = framebuffer->drawnHeight; + + // If max is not > min, we probably could not detect it. Skip. + // See the vertex decoder, where this is updated. + if ((flags & BINDFBCOLOR_MAY_COPY_WITH_UV) != 0 && gstate_c.vertMaxU > gstate_c.vertMinU) { + x = gstate_c.vertMinU; + y = gstate_c.vertMinV; + w = gstate_c.vertMaxU - x; + h = gstate_c.vertMaxV - y; + } + + BlitFramebuffer(©Info, x, y, framebuffer, x, y, w, h, 0, false); RebindFramebuffer(); pD3Ddevice->SetTexture(stage, fbo_get_color_texture(renderCopy)); diff --git a/GPU/Directx9/FramebufferDX9.h b/GPU/Directx9/FramebufferDX9.h index f577340838..0c18190e17 100644 --- a/GPU/Directx9/FramebufferDX9.h +++ b/GPU/Directx9/FramebufferDX9.h @@ -71,7 +71,7 @@ public: void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst); - void BindFramebufferColor(int stage, VirtualFramebuffer *framebuffer, bool skipCopy = false); + void BindFramebufferColor(int stage, VirtualFramebuffer *framebuffer, int flags); virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; diff --git a/GPU/Directx9/StateMappingDX9.cpp b/GPU/Directx9/StateMappingDX9.cpp index 12f19545ae..06ce0251b1 100644 --- a/GPU/Directx9/StateMappingDX9.cpp +++ b/GPU/Directx9/StateMappingDX9.cpp @@ -815,7 +815,7 @@ void TransformDrawEngineDX9::ApplyDrawStateLate() { textureCache_->ApplyTexture(); if (fboTexNeedBind_) { - framebufferManager_->BindFramebufferColor(1, nullptr, false); + framebufferManager_->BindFramebufferColor(1, nullptr, BINDFBCOLOR_MAY_COPY_WITH_UV); // If we are rendering at a higher resolution, linear is probably best for the dest color. pD3Ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); pD3Ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index effcb7b93d..8368aba6e7 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -960,12 +960,60 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame float xoff = -0.5f / framebuffer->renderWidth; float yoff = 0.5f / framebuffer->renderHeight; - const float pos[12 + 8] = { - -1 + xoff, 1 + yoff, 0, 0, 0, - 1 + xoff, 1 + yoff, 0, 1, 0, - 1 + xoff, -1 + yoff, 0, 1, 1, - -1 + xoff, -1 + yoff, 0, 0, 1, + struct Pos { + Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { + } + float x; + float y; + float z; }; + struct UV { + UV(float u_, float v_) : u(u_), v(v_) { + } + float u; + float v; + }; + + struct PosUV { + Pos pos; + UV uv; + }; + + PosUV verts[4] = { + { { -1 + xoff, 1 + yoff, -1 }, { 0, 0 } }, + { { 1 + xoff, 1 + yoff, -1 }, { 1, 0 } }, + { { 1 + xoff, -1 + yoff, -1 }, { 1, 1 } }, + { { -1 + xoff, -1 + yoff, -1 }, { 0, 1 } }, + }; + + // If min is not < max, then we don't have values (wasn't set during decode.) + if (gstate_c.vertMinV < gstate_c.vertMaxV) { + const float invWidth = 1.0f / (float)framebuffer->bufferWidth; + const float invHeight = 1.0f / (float)framebuffer->bufferHeight; + // Inverse of half = double. + const float invHalfWidth = invWidth * 2.0f; + const float invHalfHeight = invHeight * 2.0f; + + const float left = gstate_c.vertMinU * invHalfWidth - 1.0f + xoff; + const float right = gstate_c.vertMaxU * invHalfWidth - 1.0f + xoff; + const float top = gstate_c.vertMinV * invHalfHeight - 1.0f + yoff; + const float bottom = gstate_c.vertMaxV * invHalfHeight - 1.0f + yoff; + // Points are: BL, BR, TR, TL. + verts[0].pos = Pos(left, bottom, -1.0f); + verts[1].pos = Pos(right, bottom, -1.0f); + verts[2].pos = Pos(right, top, -1.0f); + verts[3].pos = Pos(left, top, -1.0f); + + // And also the UVs, same order. + const float uvleft = gstate_c.vertMinU * invWidth; + const float uvright = gstate_c.vertMaxU * invWidth; + const float uvtop = 1.0f - gstate_c.vertMinV * invHeight; + const float uvbottom = 1.0f - gstate_c.vertMaxV * invHeight; + verts[0].uv = UV(uvleft, uvbottom); + verts[1].uv = UV(uvright, uvbottom); + verts[2].uv = UV(uvright, uvtop); + verts[3].uv = UV(uvleft, uvtop); + } shaderManager_->DirtyLastShader(); @@ -977,7 +1025,7 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame pD3Ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); pD3Ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_NONE); - framebufferManager_->BindFramebufferColor(0, framebuffer, true); + framebufferManager_->BindFramebufferColor(0, framebuffer, BINDFBCOLOR_SKIP_COPY); pD3Ddevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); pD3Ddevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); pD3Ddevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_NONE); @@ -999,7 +1047,7 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame vp.Height = framebuffer->renderHeight; pD3Ddevice->SetViewport(&vp); - HRESULT hr = pD3Ddevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, pos, (3 + 2) * sizeof(float)); + HRESULT hr = pD3Ddevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, (3 + 2) * sizeof(float)); if (FAILED(hr)) { ERROR_LOG_REPORT(G3D, "Depal render failed: %08x", hr); } @@ -1011,7 +1059,7 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame framebufferManager_->RebindFramebuffer(); } else { - framebufferManager_->BindFramebufferColor(0, framebuffer); + framebufferManager_->BindFramebufferColor(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV); } SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); diff --git a/GPU/GLES/Framebuffer.cpp b/GPU/GLES/Framebuffer.cpp index 56c115879d..c44014f772 100644 --- a/GPU/GLES/Framebuffer.cpp +++ b/GPU/GLES/Framebuffer.cpp @@ -838,7 +838,7 @@ FBO *FramebufferManager::GetTempFBO(u16 w, u16 h, FBOColorDepth depth) { return fbo; } -void FramebufferManager::BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, bool skipCopy) { +void FramebufferManager::BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags) { if (framebuffer == NULL) { framebuffer = currentRenderVfb_; } @@ -856,6 +856,7 @@ void FramebufferManager::BindFramebufferColor(int stage, u32 fbRawAddress, Virtu // currentRenderVfb_ will always be set when this is called, except from the GE debugger. // Let's just not bother with the copy in that case. + bool skipCopy = (flags & BINDFBCOLOR_MAY_COPY) == 0; if (GPUStepping::IsStepping() || g_Config.bDisableSlowFramebufEffects) { skipCopy = true; } @@ -865,9 +866,23 @@ void FramebufferManager::BindFramebufferColor(int stage, u32 fbRawAddress, Virtu if (renderCopy) { VirtualFramebuffer copyInfo = *framebuffer; copyInfo.fbo = renderCopy; - BlitFramebuffer(©Info, 0, 0, framebuffer, 0, 0, framebuffer->drawnWidth, framebuffer->drawnHeight, 0, false); - RebindFramebuffer(); + int x = 0; + int y = 0; + int w = framebuffer->drawnWidth; + int h = framebuffer->drawnHeight; + + // If max is not > min, we probably could not detect it. Skip. + // See the vertex decoder, where this is updated. + if ((flags & BINDFBCOLOR_MAY_COPY_WITH_UV) != 0 && gstate_c.vertMaxU > gstate_c.vertMinU) { + x = gstate_c.vertMinU; + y = gstate_c.vertMinV; + w = gstate_c.vertMaxU - x; + h = gstate_c.vertMaxV - y; + } + + BlitFramebuffer(©Info, x, y, framebuffer, x, y, w, h, 0, false); + fbo_bind_color_as_texture(renderCopy, 0); } else { fbo_bind_color_as_texture(framebuffer->fbo, 0); diff --git a/GPU/GLES/Framebuffer.h b/GPU/GLES/Framebuffer.h index 1b39d0e20d..3ba92cd577 100644 --- a/GPU/GLES/Framebuffer.h +++ b/GPU/GLES/Framebuffer.h @@ -98,7 +98,7 @@ public: void BlitFramebufferDepth(VirtualFramebuffer *src, VirtualFramebuffer *dst); // For use when texturing from a framebuffer. May create a duplicate if target. - void BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, bool skipCopy = false); + void BindFramebufferColor(int stage, u32 fbRawAddress, VirtualFramebuffer *framebuffer, int flags); // Reads a rectangular subregion of a framebuffer to the right position in its backing memory. virtual void ReadFramebufferToMemory(VirtualFramebuffer *vfb, bool sync, int x, int y, int w, int h) override; diff --git a/GPU/GLES/StateMapping.cpp b/GPU/GLES/StateMapping.cpp index 8ff8d7980b..0e5ec22a13 100644 --- a/GPU/GLES/StateMapping.cpp +++ b/GPU/GLES/StateMapping.cpp @@ -904,7 +904,9 @@ void TransformDrawEngine::ApplyDrawStateLate() { textureCache_->ApplyTexture(); if (fboTexNeedBind_) { - framebufferManager_->BindFramebufferColor(GL_TEXTURE1, gstate.getFrameBufRawAddress(), nullptr); + framebufferManager_->BindFramebufferColor(GL_TEXTURE1, gstate.getFrameBufRawAddress(), nullptr, BINDFBCOLOR_MAY_COPY_WITH_UV); + framebufferManager_->RebindFramebuffer(); + glActiveTexture(GL_TEXTURE1); // If we are rendering at a higher resolution, linear is probably best for the dest color. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 946d519fa2..2026d7b2e7 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -1023,20 +1023,64 @@ void TextureCache::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuf GLuint clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); FBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, FBO_8888); fbo_bind_as_render_target(depalFBO); - static const float pos[12] = { - -1, -1, -1, - 1, -1, -1, - 1, 1, -1, - -1, 1, -1 + + struct Pos { + Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { + } + float x; + float y; + float z; }; - static const float uv[8] = { - 0, 0, - 1, 0, - 1, 1, - 0, 1, + struct UV { + UV(float u_, float v_) : u(u_), v(v_) { + } + float u; + float v; + }; + + Pos pos[4] = { + {-1, -1, -1}, + { 1, -1, -1}, + { 1, 1, -1}, + {-1, 1, -1}, + }; + UV uv[4] = { + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, }; static const GLubyte indices[4] = { 0, 1, 3, 2 }; + // If min is not < max, then we don't have values (wasn't set during decode.) + if (gstate_c.vertMinV < gstate_c.vertMaxV) { + const float invWidth = 1.0f / (float)framebuffer->bufferWidth; + const float invHeight = 1.0f / (float)framebuffer->bufferHeight; + // Inverse of half = double. + const float invHalfWidth = invWidth * 2.0f; + const float invHalfHeight = invHeight * 2.0f; + + const float left = gstate_c.vertMinU * invHalfWidth - 1.0f; + const float right = gstate_c.vertMaxU * invHalfWidth - 1.0f; + const float top = -(gstate_c.vertMinV * invHalfHeight - 1.0f); + const float bottom = -(gstate_c.vertMaxV * invHalfHeight - 1.0f); + // Points are: BL, BR, TR, TL. + pos[0] = Pos(left, bottom, -1.0f); + pos[1] = Pos(right, bottom, -1.0f); + pos[2] = Pos(right, top, -1.0f); + pos[3] = Pos(left, top, -1.0f); + + // And also the UVs, same order. + const float uvleft = gstate_c.vertMinU * invWidth; + const float uvright = gstate_c.vertMaxU * invWidth; + const float uvtop = 1.0f - gstate_c.vertMinV * invHeight; + const float uvbottom = 1.0f - gstate_c.vertMaxV * invHeight; + uv[0] = UV(uvleft, uvbottom); + uv[1] = UV(uvright, uvbottom); + uv[2] = UV(uvright, uvtop); + uv[3] = UV(uvleft, uvtop); + } + shaderManager_->DirtyLastShader(); glUseProgram(depal->program); @@ -1050,7 +1094,7 @@ void TextureCache::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuf glBindTexture(GL_TEXTURE_2D, clutTexture); glActiveTexture(GL_TEXTURE0); - framebufferManager_->BindFramebufferColor(GL_TEXTURE0, gstate.getFrameBufRawAddress(), framebuffer, true); + framebufferManager_->BindFramebufferColor(GL_TEXTURE0, gstate.getFrameBufRawAddress(), framebuffer, BINDFBCOLOR_SKIP_COPY); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); @@ -1073,11 +1117,11 @@ void TextureCache::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuf fbo_bind_color_as_texture(depalFBO, 0); glstate.Restore(); - framebufferManager_->RebindFramebuffer(); } else { - framebufferManager_->BindFramebufferColor(GL_TEXTURE0, gstate.getFrameBufRawAddress(), framebuffer); + framebufferManager_->BindFramebufferColor(GL_TEXTURE0, gstate.getFrameBufRawAddress(), framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV); } + framebufferManager_->RebindFramebuffer(); SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); lastBoundTexture = -1;