From 945e603072e2febef26487679c1d131be848eee6 Mon Sep 17 00:00:00 2001 From: xebra Date: Sun, 12 Feb 2017 03:27:38 +0900 Subject: [PATCH] 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); }