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] 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 fc96c761c6..b413058436 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -161,7 +161,7 @@ public: 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 a3899b0646..debeee4808 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 86226907f0..f1e47b257b 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 36d72ef0af..097e036be5 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 b00958a296..863e509cba 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 7a679a8566..628f133f98 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 f39c43a730..e2a02e3497 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 7c94269ff8..8240e45137 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 026540f443..a4c958fde3 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 cd0655dc24..b9ea154e26 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 1121a59da5..27c95fd839 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