From 6b76bcf07050fab27460f8cade98ce9ced9c3f2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 3 Sep 2021 00:06:49 +0200 Subject: [PATCH 1/2] Add new texture filtering "Auto Max Quality" that tweaks texture filtering for best quality. It does this by enforcing mipmapping and minification filters, and always autogenerates mipmaps and enforces anisotropic filtering for all modes (if that's separately enabled). This looks nice and flicker free in most games without any additional tweaking, including GTA and Burnout which have long been painfully flickery in the distance due to undersampling. Needs a bit more testing before merge, maybe. Fixes #13888 --- Core/Config.h | 2 +- Core/ConfigValues.h | 7 +++++++ GPU/Common/SplineCommon.cpp | 2 +- GPU/Common/TextureCacheCommon.cpp | 22 +++++++++++++++++++++- GPU/Common/TextureDecoder.h | 7 +------ GPU/Vulkan/DrawEngineVulkan.cpp | 4 ++++ GPU/Vulkan/TextureCacheVulkan.cpp | 9 +++++++++ UI/GameSettingsScreen.cpp | 2 +- Windows/MainWindowMenu.cpp | 5 +++-- Windows/ppsspp.rc | 1 + Windows/resource.h | 1 + 11 files changed, 50 insertions(+), 12 deletions(-) diff --git a/Core/Config.h b/Core/Config.h index fc96c761c694..b41305843676 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -161,7 +161,7 @@ struct Config { bool bVendorBugChecksEnabled; int iRenderingMode; // 0 = non-buffered rendering 1 = buffered rendering - int iTexFiltering; // 1 = off , 2 = nearest , 3 = linear , 4 = linear(CG) + int iTexFiltering; // 1 = auto , 2 = nearest , 3 = linear , 4 = auto max quality int iBufFilter; // 1 = linear, 2 = nearest int iSmallDisplayZoomType; // Used to fit display into screen 0 = stretch, 1 = partial stretch, 2 = auto scaling, 3 = manual scaling. float fSmallDisplayOffsetX; // Along with Y it goes from 0.0 to 1.0, XY (0.5, 0.5) = center of the screen diff --git a/Core/ConfigValues.h b/Core/ConfigValues.h index a3899b0646c3..debeee4808f9 100644 --- a/Core/ConfigValues.h +++ b/Core/ConfigValues.h @@ -45,6 +45,13 @@ enum { ROTATION_AUTO_HORIZONTAL = 5, }; +enum TextureFiltering { + TEX_FILTER_AUTO = 1, + TEX_FILTER_FORCE_NEAREST = 2, + TEX_FILTER_FORCE_LINEAR = 3, + TEX_FILTER_AUTO_MAX_QUALITY = 4, +}; + enum BufferFilter { SCALE_LINEAR = 1, SCALE_NEAREST = 2, diff --git a/GPU/Common/SplineCommon.cpp b/GPU/Common/SplineCommon.cpp index 86226907f04a..f1e47b257bc3 100644 --- a/GPU/Common/SplineCommon.cpp +++ b/GPU/Common/SplineCommon.cpp @@ -532,7 +532,7 @@ void DrawEngineCommon::SubmitCurve(const void *control_points, const void *indic int vertexSize = vdecoder->VertexSize(); if (vertexSize != sizeof(SimpleVertex)) { - ERROR_LOG(G3D, "Something went really wrong, vertex size: %i vs %i", vertexSize, (int)sizeof(SimpleVertex)); + ERROR_LOG(G3D, "Something went really wrong, vertex size: %d vs %d", vertexSize, (int)sizeof(SimpleVertex)); } // Make an array of pointers to the control points, to get rid of indices. diff --git a/GPU/Common/TextureCacheCommon.cpp b/GPU/Common/TextureCacheCommon.cpp index 36d72ef0af97..097e036be5e1 100644 --- a/GPU/Common/TextureCacheCommon.cpp +++ b/GPU/Common/TextureCacheCommon.cpp @@ -241,10 +241,18 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac } break; case TEX_FILTER_FORCE_NEAREST: - default: // Just force to nearest without checks. Safe (but ugly). forceFiltering = TEX_FILTER_FORCE_NEAREST; break; + case TEX_FILTER_AUTO_MAX_QUALITY: + default: + forceFiltering = TEX_FILTER_AUTO_MAX_QUALITY; + if (gstate.isModeThrough() && g_Config.iInternalResolution != 1) { + bool uglyColorTest = gstate.isColorTestEnabled() && !IsColorTestTriviallyTrue() && gstate.getColorTestRef() != 0; + if (uglyColorTest) + forceFiltering = TEX_FILTER_FORCE_NEAREST; + } + break; } } @@ -260,6 +268,18 @@ SamplerCacheKey TextureCacheCommon::GetSamplingParams(int maxLevel, const TexCac key.magFilt = 0; key.minFilt = 0; break; + case TEX_FILTER_AUTO_MAX_QUALITY: + // NOTE: We do not override magfilt here. If a game should have pixellated filtering, + // let it keep it. But we do enforce minification and mipmap filtering and max out the level. + // Later we'll also auto-generate any missing mipmaps. + key.minFilt = 1; + key.mipFilt = 1; + key.maxLevel = 9 * 256; + key.lodBias = 0.0f; + if (gstate_c.Supports(GPU_SUPPORTS_ANISOTROPY) && g_Config.iAnisotropyLevel > 0) { + key.aniso = true; + } + break; } return key; diff --git a/GPU/Common/TextureDecoder.h b/GPU/Common/TextureDecoder.h index b00958a29667..863e509cba13 100644 --- a/GPU/Common/TextureDecoder.h +++ b/GPU/Common/TextureDecoder.h @@ -27,16 +27,11 @@ enum CheckAlphaResult { #include "Common/Common.h" #include "Common/Swap.h" #include "Core/MemMap.h" +#include "Core/ConfigValues.h" #include "GPU/ge_constants.h" #include "GPU/Common/TextureDecoderNEON.h" #include "GPU/GPUState.h" -enum TextureFiltering { - TEX_FILTER_AUTO = 1, - TEX_FILTER_FORCE_NEAREST = 2, - TEX_FILTER_FORCE_LINEAR = 3, -}; - void SetupTextureDecoder(); // Pitch must be aligned to 16 bits (as is the case on a PSP) diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 7a679a85665a..628f133f9827 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -819,6 +819,10 @@ void DrawEngineVulkan::DoFlush() { } shaderManager_->GetShaders(prim, lastVType_, &vshader, &fshader, true, useHWTessellation_, decOptions_.expandAllWeightsToFloat); // usehwtransform + if (!vshader) { + // We're screwed. + return; + } _dbg_assert_msg_(vshader->UseHWTransform(), "Bad vshader"); Draw::NativeObject object = framebufferManager_->UseBufferedRendering() ? Draw::NativeObject::FRAMEBUFFER_RENDERPASS : Draw::NativeObject::BACKBUFFER_RENDERPASS; diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index f39c43a730ae..e2a02e3497a3 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -751,6 +751,15 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { // such as when using replacement textures - but let's keep the same amount of levels. int maxLevelToGenerate = maxLevel; + if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY) { + // Boost the number of mipmaps. + int maxPossibleMipmaps = log2i(std::min(gstate.getTextureWidth(0), gstate.getTextureHeight(0))); + if (maxPossibleMipmaps != maxLevelToGenerate) { + maxLevelToGenerate = maxPossibleMipmaps; + } + } + + // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index 7c94269ff8cf..8240e451374e 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -547,7 +547,7 @@ void GameSettingsScreen::CreateViews() { PopupMultiChoice *anisoFiltering = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAnisotropyLevel, gr->T("Anisotropic Filtering"), anisoLevels, 0, ARRAY_SIZE(anisoLevels), gr->GetName(), screenManager())); anisoFiltering->SetDisabledPtr(&g_Config.bSoftwareRendering); - static const char *texFilters[] = { "Auto", "Nearest", "Linear" }; + static const char *texFilters[] = { "Auto", "Nearest", "Linear", "Auto Max Quality"}; graphicsSettings->Add(new PopupMultiChoice(&g_Config.iTexFiltering, gr->T("Texture Filter"), texFilters, 1, ARRAY_SIZE(texFilters), gr->GetName(), screenManager())); static const char *bufFilters[] = { "Linear", "Nearest", }; diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index 026540f443f7..a4c958fde3b9 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -1225,11 +1225,12 @@ namespace MainWindow { ID_OPTIONS_TEXTUREFILTERING_AUTO, ID_OPTIONS_NEARESTFILTERING, ID_OPTIONS_LINEARFILTERING, + ID_OPTIONS_AUTOMAXQUALITYFILTERING, }; if (g_Config.iTexFiltering < TEX_FILTER_AUTO) g_Config.iTexFiltering = TEX_FILTER_AUTO; - else if (g_Config.iTexFiltering > TEX_FILTER_FORCE_LINEAR) - g_Config.iTexFiltering = TEX_FILTER_FORCE_LINEAR; + else if (g_Config.iTexFiltering > TEX_FILTER_AUTO_MAX_QUALITY) + g_Config.iTexFiltering = TEX_FILTER_AUTO_MAX_QUALITY; for (int i = 0; i < ARRAY_SIZE(texfilteringitems); i++) { CheckMenuItem(menu, texfilteringitems[i], MF_BYCOMMAND | ((i + 1) == g_Config.iTexFiltering ? MF_CHECKED : MF_UNCHECKED)); diff --git a/Windows/ppsspp.rc b/Windows/ppsspp.rc index cd0655dc2441..b9ea154e2627 100644 --- a/Windows/ppsspp.rc +++ b/Windows/ppsspp.rc @@ -632,6 +632,7 @@ BEGIN MENUITEM "Auto", ID_OPTIONS_TEXTUREFILTERING_AUTO MENUITEM "Nearest", ID_OPTIONS_NEARESTFILTERING MENUITEM "Linear", ID_OPTIONS_LINEARFILTERING + MENUITEM "Auto Max Quality", ID_OPTIONS_AUTOMAXQUALITYFILTERING END POPUP "Screen Scaling Filter", ID_OPTIONS_SCREENFILTER_MENU BEGIN diff --git a/Windows/resource.h b/Windows/resource.h index 1121a59da5ca..27c95fd8396e 100644 --- a/Windows/resource.h +++ b/Windows/resource.h @@ -207,6 +207,7 @@ #define ID_OPTIONS_HARDWARETRANSFORM 40030 #define IDC_STEPHLE 40032 #define ID_OPTIONS_LINEARFILTERING 40033 +#define ID_OPTIONS_AUTOMAXQUALITYFILTERING 40043 #define ID_FILE_QUICKSAVESTATE 40034 #define ID_FILE_QUICKLOADSTATE 40035 #define ID_FILE_QUICKSAVESTATE_HC 40036 From 1df31e930465f37a650c128c3d680cc12a85cffb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 5 Sep 2021 23:54:41 +0200 Subject: [PATCH 2/2] Fix windows menus for the new tex filtering options. --- GPU/D3D11/TextureCacheD3D11.cpp | 3 +++ GPU/Vulkan/TextureCacheVulkan.cpp | 8 +++----- Windows/MainWindowMenu.cpp | 8 +++++--- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 24f20071a549..be61a5ae48dc 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -447,7 +447,10 @@ void TextureCacheD3D11::BuildTexture(TexCacheEntry *const entry) { // Adjust maxLevel to actually present levels.. bool badMipSizes = false; + + // maxLevel here is the max level to upload. Not the count. int maxLevel = entry->maxLevel; + for (int i = 0; i <= maxLevel; i++) { // If encountering levels pointing to nothing, adjust max level. u32 levelTexaddr = gstate.getTextureAddress(i); diff --git a/GPU/Vulkan/TextureCacheVulkan.cpp b/GPU/Vulkan/TextureCacheVulkan.cpp index e2a02e3497a3..c009cce9a516 100644 --- a/GPU/Vulkan/TextureCacheVulkan.cpp +++ b/GPU/Vulkan/TextureCacheVulkan.cpp @@ -754,12 +754,9 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { if (g_Config.iTexFiltering == TEX_FILTER_AUTO_MAX_QUALITY) { // Boost the number of mipmaps. int maxPossibleMipmaps = log2i(std::min(gstate.getTextureWidth(0), gstate.getTextureHeight(0))); - if (maxPossibleMipmaps != maxLevelToGenerate) { - maxLevelToGenerate = maxPossibleMipmaps; - } + maxLevelToGenerate = maxPossibleMipmaps; } - // If GLES3 is available, we can preallocate the storage, which makes texture loading more efficient. VkFormat dstFmt = GetDestFormat(GETextureFormat(entry->format), gstate.getClutPaletteFormat()); @@ -807,7 +804,8 @@ void TextureCacheVulkan::BuildTexture(TexCacheEntry *const entry) { } } - // TODO + // TODO: Support mip levels for upscaled images. + // Probably can just remove this check? if (scaleFactor > 1) { maxLevel = 0; } diff --git a/Windows/MainWindowMenu.cpp b/Windows/MainWindowMenu.cpp index a4c958fde3b9..dac18e2a1e66 100644 --- a/Windows/MainWindowMenu.cpp +++ b/Windows/MainWindowMenu.cpp @@ -325,6 +325,7 @@ namespace MainWindow { TranslateMenuItem(menu, ID_OPTIONS_TEXTUREFILTERING_AUTO); TranslateMenuItem(menu, ID_OPTIONS_NEARESTFILTERING); TranslateMenuItem(menu, ID_OPTIONS_LINEARFILTERING); + TranslateMenuItem(menu, ID_OPTIONS_AUTOMAXQUALITYFILTERING); TranslateMenuItem(menu, ID_OPTIONS_SCREENFILTER_MENU); TranslateMenuItem(menu, ID_OPTIONS_BUFLINEARFILTER); TranslateMenuItem(menu, ID_OPTIONS_BUFNEARESTFILTER); @@ -979,9 +980,10 @@ namespace MainWindow { g_Config.iShowFPSCounter = g_Config.iShowFPSCounter ? 0 : 3; // 3 = both speed and FPS break; - case ID_OPTIONS_TEXTUREFILTERING_AUTO: setTexFiltering(TEX_FILTER_AUTO); break; - case ID_OPTIONS_NEARESTFILTERING: setTexFiltering(TEX_FILTER_FORCE_NEAREST); break; - case ID_OPTIONS_LINEARFILTERING: setTexFiltering(TEX_FILTER_FORCE_LINEAR); break; + case ID_OPTIONS_TEXTUREFILTERING_AUTO: setTexFiltering(TEX_FILTER_AUTO); break; + case ID_OPTIONS_NEARESTFILTERING: setTexFiltering(TEX_FILTER_FORCE_NEAREST); break; + case ID_OPTIONS_LINEARFILTERING: setTexFiltering(TEX_FILTER_FORCE_LINEAR); break; + case ID_OPTIONS_AUTOMAXQUALITYFILTERING: setTexFiltering(TEX_FILTER_AUTO_MAX_QUALITY); break; case ID_OPTIONS_BUFLINEARFILTER: setBufFilter(SCALE_LINEAR); break; case ID_OPTIONS_BUFNEARESTFILTER: setBufFilter(SCALE_NEAREST); break;