From 945e603072e2febef26487679c1d131be848eee6 Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 12 Feb 2017 03:27:38 +0900 Subject: [PATCH 1/4] Fix fake mipmap issue related #5350 --- GPU/Common/TextureCacheCommon.cpp | 2 +- GPU/Directx9/GPU_DX9.cpp | 8 ++++--- GPU/Directx9/TextureCacheDX9.cpp | 35 +++++++++++++++++++++++-------- GPU/GLES/GPU_GLES.cpp | 8 ++++--- GPU/GLES/TextureCacheGLES.cpp | 22 +++++++++++++------ GPU/Vulkan/GPU_Vulkan.cpp | 8 ++++--- GPU/Vulkan/TextureCacheVulkan.cpp | 20 +++++++++++++----- 7 files changed, 73 insertions(+), 30 deletions(-) diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index c824fc0ef4..d95cb033fd 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -90,7 +90,7 @@ void TextureCacheCommon::GetSamplingParams(int &minFilt, int &magFilt, bool &sCl sClamp = gstate.isTexCoordClampedS(); tClamp = gstate.isTexCoordClampedT(); - bool noMip = (gstate.texlevel & 0xFFFFFF) == 0x000001 || (gstate.texlevel & 0xFFFFFF) == 0x100001 ; // Fix texlevel at 0 + bool noMip = gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST; if (maxLevel == 0) { // Enforce no mip filtering, for safety. diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 3f87dc2e0f..082487e553 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -100,7 +100,7 @@ static const CommandTableEntry commandTable[] = { { GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE }, - { GE_CMD_TEXLEVEL, 0, DIRTY_TEXTURE_PARAMS }, // Flushing on this is EXPENSIVE in Gran Turismo and of little use + { GE_CMD_TEXLEVEL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, // Allow flushing only (Mode == 1 && Level > 0) { GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE | DIRTY_UVSCALEOFFSET }, { GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, @@ -662,7 +662,8 @@ void GPU_DX9::FastRunLoop(DisplayList &list) { const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two const u32 diff = op ^ gstate.cmdmem[cmd]; // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) + && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). drawEngine_.Flush(); } gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... @@ -684,7 +685,8 @@ void GPU_DX9::FinishDeferred() { inline void GPU_DX9::CheckFlushOp(int cmd, u32 diff) { const u8 cmdFlags = cmdInfo_[cmd].flags; - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) + && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). if (dumpThisFrame_) { NOTICE_LOG(G3D, "================ FLUSH ================"); } diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index eccb387ec8..b7e7a44dd7 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -604,7 +604,10 @@ void TextureCacheDX9::SetTexture(bool force) { lastBoundTexture = INVALID_TEX; } - u32 texaddr = gstate.getTextureAddress(0); + u8 level = 0; + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + level = (gstate.texlevel >> 20) & 0xF; + u32 texaddr = gstate.getTextureAddress(level); if (!Memory::IsValidAddress(texaddr)) { // Bind a null texture and return. pD3Ddevice->SetTexture(0, NULL); @@ -612,9 +615,9 @@ void TextureCacheDX9::SetTexture(bool force) { return; } - const u16 dim = gstate.getTextureDimension(0); - int w = gstate.getTextureWidth(0); - int h = gstate.getTextureHeight(0); + const u16 dim = gstate.getTextureDimension(level); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); GETextureFormat format = gstate.getTextureFormat(); if (format >= 11) { @@ -1011,7 +1014,11 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry, bool replaceImage maxLevel = 0; } - LoadTextureLevel(*entry, replaced, 0, maxLevel, replaceImages, scaleFactor, dstFmt); + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) { + u8 level = (gstate.texlevel >> 20) & 0xF; + LoadTextureLevel(*entry, replaced, level, maxLevel, replaceImages, scaleFactor, dstFmt); + } else + LoadTextureLevel(*entry, replaced, 0, maxLevel, replaceImages, scaleFactor, dstFmt); LPDIRECT3DTEXTURE9 &texture = DxTex(entry); if (!texture) { return; @@ -1095,7 +1102,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re int h = gstate.getTextureHeight(level); LPDIRECT3DTEXTURE9 &texture = DxTex(&entry); - if (level == 0 && (!replaceImages || texture == nullptr)) { + if ((level == 0 || gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) && (!replaceImages || texture == nullptr)) { // Create texture D3DPOOL pool = D3DPOOL_MANAGED; int usage = 0; @@ -1115,7 +1122,11 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re tfmt = D3DFMT_A8R8G8B8; } } - HRESULT hr = pD3Ddevice->CreateTexture(tw, th, levels, usage, tfmt, pool, &texture, NULL); + HRESULT hr; + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + hr = pD3Ddevice->CreateTexture(tw, th, 1, usage, tfmt, pool, &texture, NULL); + else + hr = pD3Ddevice->CreateTexture(tw, th, levels, usage, tfmt, pool, &texture, NULL); if (FAILED(hr)) { INFO_LOG(G3D, "Failed to create D3D texture"); ReleaseTexture(&entry); @@ -1124,7 +1135,10 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re } D3DLOCKED_RECT rect; - texture->LockRect(level, &rect, NULL, 0); + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + texture->LockRect(0, &rect, NULL, 0); + else + texture->LockRect(level, &rect, NULL, 0); gpuStats.numTexturesDecoded++; if (replaced.GetSize(level, w, h)) { @@ -1191,7 +1205,10 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re } } - texture->UnlockRect(level); + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + texture->UnlockRect(0); + else + texture->UnlockRect(level); } bool TextureCacheDX9::DecodeTexture(u8 *output, const GPUgstate &state) diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index 60ff75517d..b53dc84fa2 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -104,7 +104,7 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, {GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, {GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE}, - {GE_CMD_TEXLEVEL, 0, DIRTY_TEXTURE_PARAMS}, // Flushing on this is EXPENSIVE in Gran Turismo and of little use + {GE_CMD_TEXLEVEL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, // Allow flushing only (Mode == 1 && Level > 0) {GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE|DIRTY_UVSCALEOFFSET}, {GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, {GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, @@ -876,7 +876,8 @@ void GPU_GLES::FastRunLoop(DisplayList &list) { const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two const u32 diff = op ^ gstate.cmdmem[cmd]; // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) + && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). drawEngine_.Flush(); } gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... @@ -901,7 +902,8 @@ void GPU_GLES::FinishDeferred() { inline void GPU_GLES::CheckFlushOp(int cmd, u32 diff) { const u8 cmdFlags = cmdInfo_[cmd].flags; - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) + && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). if (dumpThisFrame_) { NOTICE_LOG(G3D, "================ FLUSH ================"); } diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index 62496b9e27..c9a6122dd2 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -686,7 +686,10 @@ void TextureCacheGLES::SetTexture(bool force) { lastBoundTexture = INVALID_TEX; } - u32 texaddr = gstate.getTextureAddress(0); + u8 level = 0; + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + level = (gstate.texlevel >> 20) & 0xF; + u32 texaddr = gstate.getTextureAddress(level); if (!Memory::IsValidAddress(texaddr)) { // Bind a null texture and return. glBindTexture(GL_TEXTURE_2D, 0); @@ -694,9 +697,9 @@ void TextureCacheGLES::SetTexture(bool force) { return; } - const u16 dim = gstate.getTextureDimension(0); - int w = gstate.getTextureWidth(0); - int h = gstate.getTextureHeight(0); + const u16 dim = gstate.getTextureDimension(level); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); GETextureFormat format = gstate.getTextureFormat(); if (format >= 11) { @@ -1055,7 +1058,11 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag // be as good quality as the game's own (might even be better in some cases though). // Always load base level texture here - LoadTextureLevel(*entry, replaced, 0, replaceImages, scaleFactor, dstFmt); + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) { + u8 level = (gstate.texlevel >> 20) & 0xF; + LoadTextureLevel(*entry, replaced, level, replaceImages, scaleFactor, dstFmt); + } else + LoadTextureLevel(*entry, replaced, 0, replaceImages, scaleFactor, dstFmt); // Mipmapping only enable when texture scaling disable if (maxLevel > 0 && scaleFactor == 1) { @@ -1271,7 +1278,10 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData); } else { PROFILE_THIS_SCOPE("loadtex"); - glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData); + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components2, dstFmt, pixelData); + else + glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData); if (!lowMemoryMode_) { GLenum err = glGetError(); if (err == GL_OUT_OF_MEMORY) { diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index c487329946..b912af1990 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -100,7 +100,7 @@ static const CommandTableEntry commandTable[] = { { GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE }, - { GE_CMD_TEXLEVEL, 0, DIRTY_TEXTURE_PARAMS }, // Flushing on this is EXPENSIVE in Gran Turismo and of little use + { GE_CMD_TEXLEVEL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, // Allow flushing only (Mode == 1 && Level > 0) { GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE | DIRTY_UVSCALEOFFSET }, { GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, @@ -710,7 +710,8 @@ void GPU_Vulkan::FastRunLoop(DisplayList &list) { const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two const u32 diff = op ^ gstate.cmdmem[cmd]; // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) + && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). drawEngine_.Flush(curCmd_); } gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... @@ -733,7 +734,8 @@ void GPU_Vulkan::FinishDeferred() { inline void GPU_Vulkan::CheckFlushOp(int cmd, u32 diff) { const u8 cmdFlags = cmdInfo_[cmd].flags; - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) + && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). if (dumpThisFrame_) { NOTICE_LOG(G3D, "================ FLUSH ================"); } diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index d3cf4472c3..aedad28a57 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -623,16 +623,19 @@ void TextureCacheVulkan::SetTexture() { } #endif - u32 texaddr = gstate.getTextureAddress(0); + u8 level = 0; + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + level = (gstate.texlevel >> 20) & 0xF; + u32 texaddr = gstate.getTextureAddress(level); if (!Memory::IsValidAddress(texaddr)) { // Bind a null texture and return. lastBoundTexture = nullptr; return; } - const u16 dim = gstate.getTextureDimension(0); - int w = gstate.getTextureWidth(0); - int h = gstate.getTextureHeight(0); + const u16 dim = gstate.getTextureDimension(level); + int w = gstate.getTextureWidth(level); + int h = gstate.getTextureHeight(level); if (texaddr == 0x04000000 && w == 2 && h == 2) { // Nonsense bootup texture. Discard. } @@ -1082,6 +1085,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, VulkanPushBuff } if (entry->vkTex) { + u8 level = (gstate.texlevel >> 20) & 0xF; + bool fakeMipmap = gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && level > 0; // Upload the texture data. for (int i = 0; i <= maxLevel; i++) { int mipWidth = gstate.getTextureWidth(i) * scaleFactor; @@ -1098,7 +1103,12 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, VulkanPushBuff if (replaced.Valid()) { replaced.Load(i, data, stride); } else { - LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); + if (fakeMipmap) { + LoadTextureLevel(*entry, (uint8_t *)data, stride, level, scaleFactor, dstFmt); + entry->vkTex->texture_->UploadMip(0, mipWidth, mipHeight, texBuf, bufferOffset, stride / bpp); + break; + } else + LoadTextureLevel(*entry, (uint8_t *)data, stride, i, scaleFactor, dstFmt); if (replacer.Enabled()) { replacer.NotifyTextureDecoded(replacedInfo, data, stride, i, mipWidth, mipHeight); } From ef536ae54d0664043ab87be41e28c0d5ea0ef184 Mon Sep 17 00:00:00 2001 From: xebra Date: Tue, 14 Feb 2017 06:02:26 +0900 Subject: [PATCH 2/4] Move wasted checks in FastRunLoop() to Execute_TexLevel() --- GPU/Directx9/GPU_DX9.cpp | 8 +++----- GPU/GLES/GPU_GLES.cpp | 8 +++----- GPU/GPUCommon.cpp | 7 +++++++ GPU/GPUCommon.h | 1 + GPU/Vulkan/GPU_Vulkan.cpp | 8 +++----- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/GPU/Directx9/GPU_DX9.cpp b/GPU/Directx9/GPU_DX9.cpp index 082487e553..230ac35b42 100644 --- a/GPU/Directx9/GPU_DX9.cpp +++ b/GPU/Directx9/GPU_DX9.cpp @@ -100,7 +100,7 @@ static const CommandTableEntry commandTable[] = { { GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE }, - { GE_CMD_TEXLEVEL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, // Allow flushing only (Mode == 1 && Level > 0) + { GE_CMD_TEXLEVEL, FLAG_EXECUTEONCHANGE, DIRTY_TEXTURE_PARAMS, &GPUCommon::Execute_TexLevel }, { GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE | DIRTY_UVSCALEOFFSET }, { GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, @@ -662,8 +662,7 @@ void GPU_DX9::FastRunLoop(DisplayList &list) { const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two const u32 diff = op ^ gstate.cmdmem[cmd]; // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) - && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { drawEngine_.Flush(); } gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... @@ -685,8 +684,7 @@ void GPU_DX9::FinishDeferred() { inline void GPU_DX9::CheckFlushOp(int cmd, u32 diff) { const u8 cmdFlags = cmdInfo_[cmd].flags; - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) - && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { if (dumpThisFrame_) { NOTICE_LOG(G3D, "================ FLUSH ================"); } diff --git a/GPU/GLES/GPU_GLES.cpp b/GPU/GLES/GPU_GLES.cpp index b53dc84fa2..f4d5ae585d 100644 --- a/GPU/GLES/GPU_GLES.cpp +++ b/GPU/GLES/GPU_GLES.cpp @@ -104,7 +104,7 @@ static const CommandTableEntry commandTable[] = { {GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, {GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, {GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE}, - {GE_CMD_TEXLEVEL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, // Allow flushing only (Mode == 1 && Level > 0) + {GE_CMD_TEXLEVEL, FLAG_EXECUTEONCHANGE, DIRTY_TEXTURE_PARAMS, &GPUCommon::Execute_TexLevel}, {GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE|DIRTY_UVSCALEOFFSET}, {GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, {GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS}, @@ -876,8 +876,7 @@ void GPU_GLES::FastRunLoop(DisplayList &list) { const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two const u32 diff = op ^ gstate.cmdmem[cmd]; // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) - && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { drawEngine_.Flush(); } gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... @@ -902,8 +901,7 @@ void GPU_GLES::FinishDeferred() { inline void GPU_GLES::CheckFlushOp(int cmd, u32 diff) { const u8 cmdFlags = cmdInfo_[cmd].flags; - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) - && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { if (dumpThisFrame_) { NOTICE_LOG(G3D, "================ FLUSH ================"); } diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index e9a0836ba6..cd85e46959 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1106,6 +1106,13 @@ void GPUCommon::Execute_TexOffsetV(u32 op, u32 diff) { gstate_c.uv.vOff = getFloat24(op); } +void GPUCommon::Execute_TexLevel(u32 op, u32 diff) { + if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0) { + Flush(); + gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); + } +} + void GPUCommon::Execute_Bezier(u32 op, u32 diff) { // This also make skipping drawing very effective. framebufferManager_->SetRenderFrameBuffer(gstate_c.IsDirty(DIRTY_FRAMEBUF), gstate_c.skipDrawReason); diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index d991bd1a2e..3016434030 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -107,6 +107,7 @@ public: void Execute_TexScaleV(u32 op, u32 diff); void Execute_TexOffsetU(u32 op, u32 diff); void Execute_TexOffsetV(u32 op, u32 diff); + void Execute_TexLevel(u32 op, u32 diff); void Execute_WorldMtxNum(u32 op, u32 diff); void Execute_WorldMtxData(u32 op, u32 diff); diff --git a/GPU/Vulkan/GPU_Vulkan.cpp b/GPU/Vulkan/GPU_Vulkan.cpp index b912af1990..d3f0cf5b77 100644 --- a/GPU/Vulkan/GPU_Vulkan.cpp +++ b/GPU/Vulkan/GPU_Vulkan.cpp @@ -100,7 +100,7 @@ static const CommandTableEntry commandTable[] = { { GE_CMD_TEXSIZE6, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXSIZE7, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXFORMAT, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE }, - { GE_CMD_TEXLEVEL, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, // Allow flushing only (Mode == 1 && Level > 0) + { GE_CMD_TEXLEVEL, FLAG_EXECUTEONCHANGE, DIRTY_TEXTURE_PARAMS, &GPUCommon::Execute_TexLevel }, { GE_CMD_TEXADDR0, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_IMAGE | DIRTY_UVSCALEOFFSET }, { GE_CMD_TEXADDR1, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, { GE_CMD_TEXADDR2, FLAG_FLUSHBEFOREONCHANGE, DIRTY_TEXTURE_PARAMS }, @@ -710,8 +710,7 @@ void GPU_Vulkan::FastRunLoop(DisplayList &list) { const u8 cmdFlags = info.flags; // If we stashed the cmdFlags in the top bits of the cmdmem, we could get away with one table lookup instead of two const u32 diff = op ^ gstate.cmdmem[cmd]; // Inlined CheckFlushOp here to get rid of the dumpThisFrame_ check. - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) - && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { drawEngine_.Flush(curCmd_); } gstate.cmdmem[cmd] = op; // TODO: no need to write if diff==0... @@ -734,8 +733,7 @@ void GPU_Vulkan::FinishDeferred() { inline void GPU_Vulkan::CheckFlushOp(int cmd, u32 diff) { const u8 cmdFlags = cmdInfo_[cmd].flags; - if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE) - && (cmd != 0xc8 || (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0)))) { // Avoid always flushing when texlevel(0xc8). + if ((cmdFlags & FLAG_FLUSHBEFORE) || (diff && (cmdFlags & FLAG_FLUSHBEFOREONCHANGE))) { if (dumpThisFrame_) { NOTICE_LOG(G3D, "================ FLUSH ================"); } From 7975addad0599a6db5e74605316c05b572ede78d Mon Sep 17 00:00:00 2001 From: xebra Date: Wed, 15 Feb 2017 00:51:56 +0900 Subject: [PATCH 3/4] Fix small issue. --- GPU/GPUCommon.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index cd85e46959..15fa493ea6 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1107,10 +1107,14 @@ void GPUCommon::Execute_TexOffsetV(u32 op, u32 diff) { } void GPUCommon::Execute_TexLevel(u32 op, u32 diff) { + if (diff == 0xFFFFFFFF) return; + + gstate.texlevel ^= diff; if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && (0x00FF0000 & gstate.texlevel) != 0) { Flush(); gstate_c.Dirty(DIRTY_TEXTURE_PARAMS); } + gstate.texlevel ^= diff; } void GPUCommon::Execute_Bezier(u32 op, u32 diff) { From e2f9dc8f2826bec7e683e17c9de6d8b0bfb2ccf6 Mon Sep 17 00:00:00 2001 From: xebra Date: Wed, 15 Feb 2017 00:56:53 +0900 Subject: [PATCH 4/4] Add support compat to fake mipmap hacks. --- Core/Compatibility.cpp | 1 + Core/Compatibility.h | 1 + GPU/Common/TextureCacheCommon.h | 5 +++++ GPU/Directx9/TextureCacheDX9.cpp | 12 ++++++------ GPU/GLES/TextureCacheGLES.cpp | 6 +++--- GPU/Vulkan/TextureCacheVulkan.cpp | 4 ++-- assets/compat.ini | 11 ++++++++++- 7 files changed, 28 insertions(+), 12 deletions(-) diff --git a/Core/Compatibility.cpp b/Core/Compatibility.cpp index 7493edf7d6..5f26b33db5 100644 --- a/Core/Compatibility.cpp +++ b/Core/Compatibility.cpp @@ -51,6 +51,7 @@ void Compatibility::CheckSettings(IniFile &iniFile, const std::string &gameID) { CheckSetting(iniFile, gameID, "ClearToRAM", &flags_.ClearToRAM); CheckSetting(iniFile, gameID, "Force04154000Download", &flags_.Force04154000Download); CheckSetting(iniFile, gameID, "DrawSyncEatCycles", &flags_.DrawSyncEatCycles); + CheckSetting(iniFile, gameID, "FakeMipmapChange", &flags_.FakeMipmapChange); } void Compatibility::CheckSetting(IniFile &iniFile, const std::string &gameID, const char *option, bool *flag) { diff --git a/Core/Compatibility.h b/Core/Compatibility.h index 991b824d73..449c01e3f3 100644 --- a/Core/Compatibility.h +++ b/Core/Compatibility.h @@ -51,6 +51,7 @@ struct CompatFlags { bool ClearToRAM; bool Force04154000Download; bool DrawSyncEatCycles; + bool FakeMipmapChange; }; class IniFile; diff --git a/GPU/Common/TextureCacheCommon.h b/GPU/Common/TextureCacheCommon.h index 56ea4f3fb1..bf56a55114 100644 --- a/GPU/Common/TextureCacheCommon.h +++ b/GPU/Common/TextureCacheCommon.h @@ -23,6 +23,7 @@ #include "Common/CommonTypes.h" #include "Common/MemoryUtil.h" #include "Core/TextureReplacer.h" +#include "Core/System.h" #include "GPU/Common/GPUDebugInterface.h" #include "GPU/Common/TextureDecoder.h" @@ -102,6 +103,10 @@ public: return cache.size(); } + bool IsFakeMipmapChange() { + return PSP_CoreParameter().compat.flags().FakeMipmapChange && gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST; + } + // Wow this is starting to grow big. Soon need to start looking at resizing it. // Must stay a POD. struct TexCacheEntry { diff --git a/GPU/Directx9/TextureCacheDX9.cpp b/GPU/Directx9/TextureCacheDX9.cpp index b7e7a44dd7..c7e7e438ea 100644 --- a/GPU/Directx9/TextureCacheDX9.cpp +++ b/GPU/Directx9/TextureCacheDX9.cpp @@ -605,7 +605,7 @@ void TextureCacheDX9::SetTexture(bool force) { } u8 level = 0; - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) level = (gstate.texlevel >> 20) & 0xF; u32 texaddr = gstate.getTextureAddress(level); if (!Memory::IsValidAddress(texaddr)) { @@ -1014,7 +1014,7 @@ void TextureCacheDX9::BuildTexture(TexCacheEntry *const entry, bool replaceImage maxLevel = 0; } - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) { + if (IsFakeMipmapChange()) { u8 level = (gstate.texlevel >> 20) & 0xF; LoadTextureLevel(*entry, replaced, level, maxLevel, replaceImages, scaleFactor, dstFmt); } else @@ -1102,7 +1102,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re int h = gstate.getTextureHeight(level); LPDIRECT3DTEXTURE9 &texture = DxTex(&entry); - if ((level == 0 || gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) && (!replaceImages || texture == nullptr)) { + if ((level == 0 || IsFakeMipmapChange()) && (!replaceImages || texture == nullptr)) { // Create texture D3DPOOL pool = D3DPOOL_MANAGED; int usage = 0; @@ -1123,7 +1123,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re } } HRESULT hr; - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) hr = pD3Ddevice->CreateTexture(tw, th, 1, usage, tfmt, pool, &texture, NULL); else hr = pD3Ddevice->CreateTexture(tw, th, levels, usage, tfmt, pool, &texture, NULL); @@ -1135,7 +1135,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re } D3DLOCKED_RECT rect; - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) texture->LockRect(0, &rect, NULL, 0); else texture->LockRect(level, &rect, NULL, 0); @@ -1205,7 +1205,7 @@ void TextureCacheDX9::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &re } } - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) texture->UnlockRect(0); else texture->UnlockRect(level); diff --git a/GPU/GLES/TextureCacheGLES.cpp b/GPU/GLES/TextureCacheGLES.cpp index c9a6122dd2..40a634c7f6 100644 --- a/GPU/GLES/TextureCacheGLES.cpp +++ b/GPU/GLES/TextureCacheGLES.cpp @@ -687,7 +687,7 @@ void TextureCacheGLES::SetTexture(bool force) { } u8 level = 0; - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) level = (gstate.texlevel >> 20) & 0xF; u32 texaddr = gstate.getTextureAddress(level); if (!Memory::IsValidAddress(texaddr)) { @@ -1058,7 +1058,7 @@ void TextureCacheGLES::BuildTexture(TexCacheEntry *const entry, bool replaceImag // be as good quality as the game's own (might even be better in some cases though). // Always load base level texture here - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) { + if (IsFakeMipmapChange()) { u8 level = (gstate.texlevel >> 20) & 0xF; LoadTextureLevel(*entry, replaced, level, replaceImages, scaleFactor, dstFmt); } else @@ -1278,7 +1278,7 @@ void TextureCacheGLES::LoadTextureLevel(TexCacheEntry &entry, ReplacedTexture &r glTexSubImage2D(GL_TEXTURE_2D, level, 0, 0, w, h, components2, dstFmt, pixelData); } else { PROFILE_THIS_SCOPE("loadtex"); - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) glTexImage2D(GL_TEXTURE_2D, 0, components, w, h, 0, components2, dstFmt, pixelData); else glTexImage2D(GL_TEXTURE_2D, level, components, w, h, 0, components2, dstFmt, pixelData); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index aedad28a57..4eb9d162c0 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -624,7 +624,7 @@ void TextureCacheVulkan::SetTexture() { #endif u8 level = 0; - if (gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST) + if (IsFakeMipmapChange()) level = (gstate.texlevel >> 20) & 0xF; u32 texaddr = gstate.getTextureAddress(level); if (!Memory::IsValidAddress(texaddr)) { @@ -1086,7 +1086,7 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry, VulkanPushBuff if (entry->vkTex) { u8 level = (gstate.texlevel >> 20) & 0xF; - bool fakeMipmap = gstate.getTexLevelMode() == GE_TEXLEVEL_MODE_CONST && level > 0; + bool fakeMipmap = IsFakeMipmapChange() && level > 0; // Upload the texture data. for (int i = 0; i <= maxLevel; i++) { int mipWidth = gstate.getTextureWidth(i) * scaleFactor; diff --git a/assets/compat.ini b/assets/compat.ini index 7ea055b3d5..00adc21e42 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -152,4 +152,13 @@ ULJS00145 = true ULKS46183 = true ULJS00260 = true ULJS19041 = true -NPJH50843 = true \ No newline at end of file +NPJH50843 = true + +[FakeMipmapChange] +# This hacks separates each mipmap to independent textures to display wrong-size mipmaps. +# For example this requires games like Tactics Ogre(Japanese) to display multi bytes fonts stored in mipmaps. +# See issue #5350. +# Tactics Ogre(Japanese) +ULJM05753 = true +NPJH50348 = true +ULJM06009 = true