diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 30207f1c90b8..215730f6c657 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -19,6 +19,7 @@ #include "Common/MemoryUtil.h" #include "Core/Config.h" #include "Core/Reporting.h" +#include "Core/System.h" #include "GPU/Common/FramebufferCommon.h" #include "GPU/Common/TextureCacheCommon.h" #include "GPU/Common/TextureDecoder.h" @@ -164,6 +165,46 @@ void TextureCacheCommon::NotifyFramebuffer(u32 address, VirtualFramebuffer *fram } } +void TextureCacheCommon::NotifyConfigChanged() { + int scaleFactor; + + // 0 means automatic texture scaling, up to 5x, based on resolution. + if (g_Config.iTexScalingLevel == 0) { + scaleFactor = g_Config.iInternalResolution; + // Automatic resolution too? Okay. + if (scaleFactor == 0) { + if (!g_Config.IsPortrait()) { + scaleFactor = (PSP_CoreParameter().pixelWidth + 479) / 480; + } else { + scaleFactor = (PSP_CoreParameter().pixelHeight + 479) / 480; + } + } + + // Mobile devices don't get the higher scale factors, too expensive. Very rough way to decide though... + if (!gstate_c.Supports(GPU_IS_MOBILE)) { + scaleFactor = std::min(5, scaleFactor); + } else { + scaleFactor = std::min(3, scaleFactor); + } + } else { + scaleFactor = g_Config.iTexScalingLevel; + } + + if (!gstate_c.Supports(GPU_SUPPORTS_OES_TEXTURE_NPOT)) { + // Reduce the scale factor to a power of two (e.g. 2 or 4) if textures must be a power of two. + while ((scaleFactor & (scaleFactor - 1)) != 0) { + --scaleFactor; + } + } + + // Just in case, small display with auto resolution or something. + if (scaleFactor <= 0) { + scaleFactor = 1; + } + + standardScaleFactor_ = scaleFactor; +} + void TextureCacheCommon::LoadClut(u32 clutAddr, u32 loadBytes) { clutTotalBytes_ = loadBytes; clutRenderAddress_ = 0xFFFFFFFF; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 847a1c291797..8376093561d0 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -45,6 +45,7 @@ class TextureCacheCommon { // FramebufferManager keeps TextureCache updated about what regions of memory are being rendered to. void NotifyFramebuffer(u32 address, VirtualFramebuffer *framebuffer, FramebufferNotification msg); + void NotifyConfigChanged(); int AttachedDrawingHeight(); @@ -154,6 +155,7 @@ class TextureCacheCommon { u32 clutTotalBytes_; u32 clutMaxBytes_; u32 clutRenderAddress_; + int standardScaleFactor_; }; inline bool TextureCacheCommon::TexCacheEntry::Matches(u16 dim2, u8 format2, u8 maxLevel2) { diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index e0a230bc08d3..46bb33e3d9c5 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -444,6 +444,7 @@ DIRECTX9_GPU::DIRECTX9_GPU() // Some of our defaults are different from hw defaults, let's assert them. // We restore each frame anyway, but here is convenient for tests. dxstate.Restore(); + textureCache_.NotifyConfigChanged(); } void DIRECTX9_GPU::UpdateCmdInfo() { @@ -537,6 +538,7 @@ void DIRECTX9_GPU::BeginFrameInternal() { if (resized_) { UpdateCmdInfo(); transformDraw_.Resized(); + textureCache_.NotifyConfigChanged(); resized_ = false; } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index 5354f7c27103..117e87811572 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -804,97 +804,99 @@ void TextureCacheDX9::ApplyTexture() { nextTexture_ = nullptr; } -void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { - LPDIRECT3DPIXELSHADER9 pshader = nullptr; - const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); - if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { - pshader = depalShaderCache_->GetDepalettizePixelShader(clutFormat, framebuffer->drawnFormat); - } - - if (pshader) { - LPDIRECT3DTEXTURE9 clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - - FBO_DX9 *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, FBO_8888); - fbo_bind_as_render_target(depalFBO); +class TextureShaderApplierDX9 { +public: + struct Pos { + Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { + } + Pos() { + } - float xoff = -0.5f / framebuffer->renderWidth; - float yoff = 0.5f / framebuffer->renderHeight; + float x; + float y; + float z; + }; + struct UV { + UV(float u_, float v_) : u(u_), v(v_) { + } + UV() { + } - struct Pos { - Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { - } - float x; - float y; - float z; + float u; + float v; + }; + + struct PosUV { + Pos pos; + UV uv; + }; + + TextureShaderApplierDX9(LPDIRECT3DPIXELSHADER9 pshader, float bufferW, float bufferH, int renderW, int renderH, float xoff, float yoff) + : pshader_(pshader), bufferW_(bufferW), bufferH_(bufferH), renderW_(renderW), renderH_(renderH) { + static const Pos pos[4] = { + {-1, 1, 0}, + { 1, 1, 0}, + { 1, -1, 0}, + {-1, -1, 0}, }; - struct UV { - UV(float u_, float v_) : u(u_), v(v_) { - } - float u; - float v; + static const UV uv[4] = { + {0, 0}, + {1, 0}, + {1, 1}, + {0, 1}, }; - 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 } }, - }; + for (int i = 0; i < 4; ++i) { + verts_[i].pos = pos[i]; + verts_[i].pos.x += xoff; + verts_[i].pos.y += yoff; + verts_[i].uv = uv[i]; + } + } + void ApplyBounds(const KnownVertexBounds &bounds, u32 uoff, u32 voff, float xoff, float yoff) { // If min is not < max, then we don't have values (wasn't set during decode.) - if (gstate_c.vertBounds.minV < gstate_c.vertBounds.maxV) { - const float invWidth = 1.0f / (float)framebuffer->bufferWidth; - const float invHeight = 1.0f / (float)framebuffer->bufferHeight; + if (bounds.minV < bounds.maxV) { + const float invWidth = 1.0f / bufferW_; + const float invHeight = 1.0f / bufferH_; // Inverse of half = double. const float invHalfWidth = invWidth * 2.0f; const float invHalfHeight = invHeight * 2.0f; - const int u1 = gstate_c.vertBounds.minU + gstate_c.curTextureXOffset; - const int v1 = gstate_c.vertBounds.minV + gstate_c.curTextureYOffset; - const int u2 = gstate_c.vertBounds.maxU + gstate_c.curTextureXOffset; - const int v2 = gstate_c.vertBounds.maxV + gstate_c.curTextureYOffset; + const int u1 = bounds.minU + uoff; + const int v1 = bounds.minV + voff; + const int u2 = bounds.maxU + uoff; + const int v2 = bounds.maxV + voff; const float left = u1 * invHalfWidth - 1.0f + xoff; const float right = u2 * invHalfWidth - 1.0f + xoff; const float top = v1 * invHalfHeight - 1.0f + yoff; const float bottom = v2 * 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); + 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 = u1 * invWidth; const float uvright = u2 * invWidth; - const float uvtop = v1 * invHeight; // TODO: Seems we should ditch the "1.0f - " + const float uvtop = v1 * invHeight; const float uvbottom = v2 * 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); + 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(); - - pD3Ddevice->SetPixelShader(pshader); - pD3Ddevice->SetVertexShader(depalShaderCache_->GetDepalettizeVertexShader()); + void Use(LPDIRECT3DVERTEXSHADER9 vshader) { + pD3Ddevice->SetPixelShader(pshader_); + pD3Ddevice->SetVertexShader(vshader); pD3Ddevice->SetVertexDeclaration(pFramebufferVertexDecl); - pD3Ddevice->SetTexture(1, clutTexture); - pD3Ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); - pD3Ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); - pD3Ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_NONE); - - 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); + } + void Shade() { pD3Ddevice->SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE); pD3Ddevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, FALSE); pD3Ddevice->SetRenderState(D3DRS_COLORWRITEENABLE, D3DCOLORWRITEENABLE_RED | D3DCOLORWRITEENABLE_GREEN | D3DCOLORWRITEENABLE_BLUE | D3DCOLORWRITEENABLE_ALPHA); @@ -903,22 +905,63 @@ void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFrame pD3Ddevice->SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE); pD3Ddevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE); - DXSetViewport(0, 0, framebuffer->renderWidth, framebuffer->renderHeight); - HRESULT hr = pD3Ddevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts, (3 + 2) * sizeof(float)); + DXSetViewport(0, 0, renderW_, renderH_); + HRESULT hr = pD3Ddevice->DrawPrimitiveUP(D3DPT_TRIANGLEFAN, 2, verts_, (3 + 2) * sizeof(float)); if (FAILED(hr)) { ERROR_LOG_REPORT(G3D, "Depal render failed: %08x", hr); } - framebufferManager_->RebindFramebuffer(); - fbo_bind_color_as_texture(depalFBO, 0); dxstate.Restore(); - dxstate.viewport.restore(); + } + +protected: + LPDIRECT3DPIXELSHADER9 pshader_; + PosUV verts_[4]; + float bufferW_; + float bufferH_; + int renderW_; + int renderH_; +}; + +void TextureCacheDX9::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { + LPDIRECT3DPIXELSHADER9 pshader = nullptr; + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { + pshader = depalShaderCache_->GetDepalettizePixelShader(clutFormat, framebuffer->drawnFormat); + } + + if (pshader) { + LPDIRECT3DTEXTURE9 clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); + + FBO_DX9 *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, FBO_8888); + fbo_bind_as_render_target(depalFBO); + shaderManager_->DirtyLastShader(); + + float xoff = -0.5f / framebuffer->renderWidth; + float yoff = 0.5f / framebuffer->renderHeight; + + TextureShaderApplierDX9 shaderApply(pshader, framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight, xoff, yoff); + shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset, xoff, yoff); + shaderApply.Use(depalShaderCache_->GetDepalettizeVertexShader()); + + pD3Ddevice->SetTexture(1, clutTexture); + pD3Ddevice->SetSamplerState(1, D3DSAMP_MINFILTER, D3DTEXF_POINT); + pD3Ddevice->SetSamplerState(1, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + pD3Ddevice->SetSamplerState(1, D3DSAMP_MIPFILTER, D3DTEXF_NONE); - framebufferManager_->RebindFramebuffer(); + 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); + + shaderApply.Shade(); + + fbo_bind_color_as_texture(depalFBO, 0); } else { framebufferManager_->BindFramebufferColor(0, framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); } + framebufferManager_->RebindFramebuffer(); SetFramebufferSamplingParams(framebuffer->bufferWidth, framebuffer->bufferHeight); lastBoundTexture = INVALID_TEX; @@ -1145,7 +1188,7 @@ void TextureCacheDX9::SetTexture(bool force) { } } - if (match && (entry->status & TexCacheEntry::STATUS_TO_SCALE) && g_Config.iTexScalingLevel != 1 && texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED) { + if (match && (entry->status & TexCacheEntry::STATUS_TO_SCALE) && standardScaleFactor_ != 1 && texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED) { if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { // INFO_LOG(G3D, "Reloading texture to do the scaling we skipped.."); match = false; @@ -1172,7 +1215,7 @@ void TextureCacheDX9::SetTexture(bool force) { gpuStats.numTextureInvalidations++; DEBUG_LOG(G3D, "Texture different or overwritten, reloading at %08x: %s", texaddr, reason); if (doDelete) { - if (entry->maxLevel == maxLevel && entry->dim == gstate.getTextureDimension(0) && entry->format == format && g_Config.iTexScalingLevel == 1) { + if (entry->maxLevel == maxLevel && entry->dim == gstate.getTextureDimension(0) && entry->format == format && standardScaleFactor_ == 1) { // Actually, if size and number of levels match, let's try to avoid deleting and recreating. // Instead, let's use glTexSubImage to replace the images. replaceImages = true; @@ -1291,21 +1334,7 @@ void TextureCacheDX9::SetTexture(bool force) { // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. D3DFORMAT dstFmt = GetDestFormat(format, gstate.getClutPaletteFormat()); - int scaleFactor; - // Auto-texture scale upto 5x rendering resolution - if (g_Config.iTexScalingLevel == 0) { - scaleFactor = g_Config.iInternalResolution; - if (scaleFactor == 0) { - scaleFactor = (PSP_CoreParameter().renderWidth + 479) / 480; - } - - scaleFactor = std::min(4, scaleFactor); - if (scaleFactor == 3) { - scaleFactor = 2; - } - } else { - scaleFactor = g_Config.iTexScalingLevel; - } + int scaleFactor = standardScaleFactor_; // Rachet down scale factor in low-memory mode. if (lowMemoryMode_) { @@ -1585,7 +1614,7 @@ void *TextureCacheDX9::DecodeTextureLevel(GETextureFormat format, GEPaletteForma ERROR_LOG_REPORT(G3D, "NO finalbuf! Will crash!"); } - if (!(g_Config.iTexScalingLevel == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE)) && w != bufw) { + if (!(standardScaleFactor_ == 1 && gstate_c.Supports(GPU_SUPPORTS_UNPACK_SUBIMAGE)) && w != bufw) { int pixelSize; switch (dstFmt) { case D3DFMT_A4R4G4B4: diff --git a/GPU/GLES/GLES_GPU.cpp b/GPU/GLES/GLES_GPU.cpp index 152d14a6bece..e849dc9ef91c 100644 --- a/GPU/GLES/GLES_GPU.cpp +++ b/GPU/GLES/GLES_GPU.cpp @@ -454,8 +454,9 @@ GLES_GPU::GLES_GPU(GraphicsContext *ctx) // Some of our defaults are different from hw defaults, let's assert them. // We restore each frame anyway, but here is convenient for tests. - transformDraw_.RestoreVAO(); glstate.Restore(); + transformDraw_.RestoreVAO(); + textureCache_.NotifyConfigChanged(); } GLES_GPU::~GLES_GPU() { @@ -715,6 +716,7 @@ void GLES_GPU::BeginFrameInternal() { CheckGPUFeatures(); UpdateCmdInfo(); transformDraw_.Resized(); + textureCache_.NotifyConfigChanged(); } UpdateVsyncInterval(resized_); resized_ = false; diff --git a/GPU/GLES/TextureCache.cpp b/GPU/GLES/TextureCache.cpp index 16b0b39f566d..4d7e77ab529b 100644 --- a/GPU/GLES/TextureCache.cpp +++ b/GPU/GLES/TextureCache.cpp @@ -879,101 +879,101 @@ void TextureCache::ApplyTexture() { nextTexture_ = nullptr; } -void TextureCache::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { - DepalShader *depal = nullptr; - const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); - if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { - depal = depalShaderCache_->GetDepalettizeShader(clutFormat, framebuffer->drawnFormat); - } - if (depal) { - GLuint clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); - FBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, FBO_8888); - fbo_bind_as_render_target(depalFBO); +class TextureShaderApplier { +public: + struct Pos { + Pos(float x_, float y_, float z_) : x(x_), y(y_), z(z_) { + } + Pos() { + } - 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; - }; + float x; + float y; + float z; + }; + struct UV { + UV(float u_, float v_) : u(u_), v(v_) { + } + UV() { + } - Pos pos[4] = { + float u; + float v; + }; + + TextureShaderApplier(DepalShader *shader, float bufferW, float bufferH, int renderW, int renderH) + : shader_(shader), bufferW_(bufferW), bufferH_(bufferH), renderW_(renderW), renderH_(renderH) { + static const Pos pos[4] = { {-1, -1, -1}, { 1, -1, -1}, { 1, 1, -1}, {-1, 1, -1}, }; - UV uv[4] = { + memcpy(pos_, pos, sizeof(pos_)); + + static const UV uv[4] = { {0, 0}, {1, 0}, {1, 1}, {0, 1}, }; - static const GLubyte indices[4] = { 0, 1, 3, 2 }; + memcpy(uv_, uv, sizeof(uv_)); + } + void ApplyBounds(const KnownVertexBounds &bounds, u32 uoff, u32 voff) { // If min is not < max, then we don't have values (wasn't set during decode.) - if (gstate_c.vertBounds.minV < gstate_c.vertBounds.maxV) { - const float invWidth = 1.0f / (float)framebuffer->bufferWidth; - const float invHeight = 1.0f / (float)framebuffer->bufferHeight; + if (bounds.minV < bounds.maxV) { + const float invWidth = 1.0f / bufferW_; + const float invHeight = 1.0f / bufferH_; // Inverse of half = double. const float invHalfWidth = invWidth * 2.0f; const float invHalfHeight = invHeight * 2.0f; - const int u1 = gstate_c.vertBounds.minU + gstate_c.curTextureXOffset; - const int v1 = gstate_c.vertBounds.minV + gstate_c.curTextureYOffset; - const int u2 = gstate_c.vertBounds.maxU + gstate_c.curTextureXOffset; - const int v2 = gstate_c.vertBounds.maxV + gstate_c.curTextureYOffset; + const int u1 = bounds.minU + uoff; + const int v1 = bounds.minV + voff; + const int u2 = bounds.maxU + uoff; + const int v2 = bounds.maxV + voff; const float left = u1 * invHalfWidth - 1.0f; const float right = u2 * invHalfWidth - 1.0f; const float top = v1 * invHalfHeight - 1.0f; const float bottom = v2 * 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); + 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 = u1 * invWidth; const float uvright = u2 * invWidth; const float uvtop = v1 * invHeight; const float uvbottom = v2 * invHeight; - uv[0] = UV(uvleft, uvbottom); - uv[1] = UV(uvright, uvbottom); - uv[2] = UV(uvright, uvtop); - uv[3] = UV(uvleft, uvtop); + 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); + void Use(TransformDrawEngine *transformDraw) { + glUseProgram(shader_->program); // Restore will rebind all of the state below. if (gstate_c.Supports(GPU_SUPPORTS_VAO)) { - transformDraw_->BindBuffer(pos, sizeof(pos), uv, sizeof(uv)); - transformDraw_->BindElementBuffer(indices, sizeof(indices)); + static const GLubyte indices[4] = { 0, 1, 3, 2 }; + transformDraw->BindBuffer(pos_, sizeof(pos_), uv_, sizeof(uv_)); + transformDraw->BindElementBuffer(indices, sizeof(indices)); } else { glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); } - glEnableVertexAttribArray(depal->a_position); - glEnableVertexAttribArray(depal->a_texcoord0); - - glActiveTexture(GL_TEXTURE3); - glBindTexture(GL_TEXTURE_2D, clutTexture); - glActiveTexture(GL_TEXTURE0); + glEnableVertexAttribArray(shader_->a_position); + glEnableVertexAttribArray(shader_->a_texcoord0); + } - 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); + void Shade() { + static const GLubyte indices[4] = { 0, 1, 3, 2 }; glstate.blend.force(false); glstate.colorMask.force(true, true, true, true); @@ -984,22 +984,60 @@ void TextureCache::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuf #if !defined(USING_GLES2) glstate.colorLogicOp.force(false); #endif - glViewport(0, 0, framebuffer->renderWidth, framebuffer->renderHeight); + glViewport(0, 0, renderW_, renderH_); if (gstate_c.Supports(GPU_SUPPORTS_VAO)) { - glVertexAttribPointer(depal->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0); - glVertexAttribPointer(depal->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos)); + glVertexAttribPointer(shader_->a_position, 3, GL_FLOAT, GL_FALSE, 12, 0); + glVertexAttribPointer(shader_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, (void *)sizeof(pos_)); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, 0); } else { - glVertexAttribPointer(depal->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos); - glVertexAttribPointer(depal->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, uv); + glVertexAttribPointer(shader_->a_position, 3, GL_FLOAT, GL_FALSE, 12, pos_); + glVertexAttribPointer(shader_->a_texcoord0, 2, GL_FLOAT, GL_FALSE, 8, uv_); glDrawElements(GL_TRIANGLE_STRIP, 4, GL_UNSIGNED_BYTE, indices); } - glDisableVertexAttribArray(depal->a_position); - glDisableVertexAttribArray(depal->a_texcoord0); + glDisableVertexAttribArray(shader_->a_position); + glDisableVertexAttribArray(shader_->a_texcoord0); - fbo_bind_color_as_texture(depalFBO, 0); glstate.Restore(); + } + +protected: + DepalShader *shader_; + Pos pos_[4]; + UV uv_[4]; + float bufferW_; + float bufferH_; + int renderW_; + int renderH_; +}; + +void TextureCache::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFramebuffer *framebuffer) { + DepalShader *depal = nullptr; + const GEPaletteFormat clutFormat = gstate.getClutPaletteFormat(); + if ((entry->status & TexCacheEntry::STATUS_DEPALETTIZE) && !g_Config.bDisableSlowFramebufEffects) { + depal = depalShaderCache_->GetDepalettizeShader(clutFormat, framebuffer->drawnFormat); + } + if (depal) { + GLuint clutTexture = depalShaderCache_->GetClutTexture(clutFormat, clutHash_, clutBuf_); + FBO *depalFBO = framebufferManager_->GetTempFBO(framebuffer->renderWidth, framebuffer->renderHeight, FBO_8888); + fbo_bind_as_render_target(depalFBO); + shaderManager_->DirtyLastShader(); + + TextureShaderApplier shaderApply(depal, framebuffer->bufferWidth, framebuffer->bufferHeight, framebuffer->renderWidth, framebuffer->renderHeight); + shaderApply.ApplyBounds(gstate_c.vertBounds, gstate_c.curTextureXOffset, gstate_c.curTextureYOffset); + shaderApply.Use(transformDraw_); + + glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, clutTexture); + glActiveTexture(GL_TEXTURE0); + + 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); + + shaderApply.Shade(); + + fbo_bind_color_as_texture(depalFBO, 0); } else { framebufferManager_->BindFramebufferColor(GL_TEXTURE0, gstate.getFrameBufRawAddress(), framebuffer, BINDFBCOLOR_MAY_COPY_WITH_UV | BINDFBCOLOR_APPLY_TEX_OFFSET); } @@ -1230,7 +1268,7 @@ void TextureCache::SetTexture(bool force) { } } - if (match && (entry->status & TexCacheEntry::STATUS_TO_SCALE) && g_Config.iTexScalingLevel != 1 && texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED) { + if (match && (entry->status & TexCacheEntry::STATUS_TO_SCALE) && standardScaleFactor_ != 1 && texelsScaledThisFrame_ < TEXCACHE_MAX_TEXELS_SCALED) { if ((entry->status & TexCacheEntry::STATUS_CHANGE_FREQUENT) == 0) { // INFO_LOG(G3D, "Reloading texture to do the scaling we skipped.."); match = false; @@ -1255,7 +1293,7 @@ void TextureCache::SetTexture(bool force) { gpuStats.numTextureInvalidations++; DEBUG_LOG(G3D, "Texture different or overwritten, reloading at %08x: %s", texaddr, reason); if (doDelete) { - if (entry->maxLevel == maxLevel && entry->dim == gstate.getTextureDimension(0) && entry->format == format && g_Config.iTexScalingLevel == 1) { + if (entry->maxLevel == maxLevel && entry->dim == gstate.getTextureDimension(0) && entry->format == format && standardScaleFactor_ == 1) { // Actually, if size and number of levels match, let's try to avoid deleting and recreating. // Instead, let's use glTexSubImage to replace the images. replaceImages = true; @@ -1380,27 +1418,7 @@ void TextureCache::SetTexture(bool force) { // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. GLenum dstFmt = GetDestFormat(format, gstate.getClutPaletteFormat()); - int scaleFactor; - // Auto-texture scale upto 5x rendering resolution - if (g_Config.iTexScalingLevel == 0) { - scaleFactor = g_Config.iInternalResolution; - if (scaleFactor == 0) { - scaleFactor = (PSP_CoreParameter().renderWidth + 479) / 480; - } - - // Mobile devices don't get the higher scale factors, too expensive. Very rough way to decide though... - if (!gstate_c.Supports(GPU_IS_MOBILE)) { - bool supportNpot = gstate_c.Supports(GPU_SUPPORTS_OES_TEXTURE_NPOT); - scaleFactor = std::min(supportNpot ? 5 : 4, scaleFactor); - if (!supportNpot && scaleFactor == 3) { - scaleFactor = 2; - } - } else { - scaleFactor = std::min(gstate_c.Supports(GPU_SUPPORTS_OES_TEXTURE_NPOT) ? 3 : 2, scaleFactor); - } - } else { - scaleFactor = g_Config.iTexScalingLevel; - } + int scaleFactor = standardScaleFactor_; // Rachet down scale factor in low-memory mode. if (lowMemoryMode_) {