Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GS/HW: Add all levels/unclamped mipmap modes,credit by stenzek #11787

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions pcsx2-qt/Settings/GraphicsSettingsWidget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.anisotropicFiltering, "EmuCore/GS", "MaxAnisotropy",
s_anisotropic_filtering_entries, s_anisotropic_filtering_values, "0");
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.dithering, "EmuCore/GS", "dithering_ps2", 2);
SettingWidgetBinder::BindWidgetToBoolSetting(sif, m_ui.mipmapping, "EmuCore/GS", "hw_mipmap", true);
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.hwMipmapMode, "EmuCore/GS", "hw_mipmap_mode",
static_cast<int>(GSHWMipmapMode::Enabled));
SettingWidgetBinder::BindWidgetToIntSetting(
sif, m_ui.blending, "EmuCore/GS", "accurate_blending_unit", static_cast<int>(AccBlendLevel::Basic));
SettingWidgetBinder::BindWidgetToIntSetting(
Expand Down Expand Up @@ -506,8 +507,8 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsWindow* dialog, QWidget*
"FMV resolution will remain unchanged, as the video files are pre-rendered."));

dialog->registerWidgetHelp(
m_ui.mipmapping, tr("Mipmapping"), tr("Checked"), tr("Enables mipmapping, which some games require to render correctly."));

m_ui.hwMipmapMode, tr("Mipmapping"), tr("Enabled"), tr("Enables mipmapping, which many games require to render correctly. "
"Unclamped allows higher texture detail levels to be used, but may break certain graphical effects."));
dialog->registerWidgetHelp(
m_ui.textureFiltering, tr("Texture Filtering"), tr("Bilinear (PS2)"),
tr("Changes what filtering algorithm is used to map textures to surfaces.<br> "
Expand Down
40 changes: 32 additions & 8 deletions pcsx2-qt/Settings/GraphicsSettingsWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -611,22 +611,46 @@
</item>
<item row="7" column="0" colspan="2">
<layout class="QGridLayout" name="hardwareRenderingOptionsLayout">
<item row="0" column="1">
<item row="0" column="0">
<widget class="QCheckBox" name="enableHWFixes">
<property name="text">
<string>Manual Hardware Renderer Fixes</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QCheckBox" name="mipmapping">
<property name="text">
<string>Mipmapping</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QLabel" name="hwMipmappingLabel">
<property name="text">
<string>Mipmapping:</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="hwMipmapMode">
<item>
<property name="text">
<string>Disabled</string>
</property>
</item>
<item>
<property name="text">
<string>Enabled</string>
</property>
</item>
<item>
<property name="text">
<string>All Levels</string>
</property>
</item>
<item>
<property name="text">
<string>Unclamped</string>
</property>
</item>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="softwareRenderingTab">
Expand Down
11 changes: 10 additions & 1 deletion pcsx2/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,15 @@ enum class BiFiltering : u8
Forced_But_Sprite,
};

enum class GSHWMipmapMode : u8
{
Disabled,
Enabled,
AllLevels,
Unclamped,
MaxCount
};

enum class TriFiltering : s8
{
Automatic = -1,
Expand Down Expand Up @@ -637,7 +646,6 @@ struct Pcsx2Config
AutoFlushSW : 1,
PreloadFrameWithGSData : 1,
Mipmap : 1,
HWMipmap : 1,
ManualUserHacks : 1,
UserHacks_AlignSpriteX : 1,
UserHacks_CPUFBConversion : 1,
Expand Down Expand Up @@ -694,6 +702,7 @@ struct Pcsx2Config
float UpscaleMultiplier = 1.0f;

AccBlendLevel AccurateBlendingUnit = AccBlendLevel::Basic;
GSHWMipmapMode HWMipmapMode = GSHWMipmapMode::Disabled;
BiFiltering TextureFiltering = BiFiltering::PS2;
TexturePreloadingLevel TexturePreloading = TexturePreloadingLevel::Full;
GSDumpCompressionMethod GSDumpCompression = GSDumpCompressionMethod::Zstandard;
Expand Down
7 changes: 4 additions & 3 deletions pcsx2/GS/GS.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -762,7 +762,7 @@ void GSUpdateConfig(const Pcsx2Config::GSOptions& new_config)

// reload texture cache when trilinear filtering or TC options change
if (
(GSIsHardwareRenderer() && GSConfig.HWMipmap != old_config.HWMipmap) ||
(GSIsHardwareRenderer() && GSConfig.HWMipmapMode != old_config.HWMipmapMode) ||
GSConfig.TexturePreloading != old_config.TexturePreloading ||
GSConfig.TriFilter != old_config.TriFilter ||
GSConfig.GPUPaletteConversion != old_config.GPUPaletteConversion ||
Expand Down Expand Up @@ -1138,9 +1138,10 @@ BEGIN_HOTKEY_LIST(g_gs_hotkeys){"Screenshot", TRANSLATE_NOOP("Hotkeys", "Graphic
[](s32 pressed) {
if (!pressed)
{
EmuConfig.GS.HWMipmap = !EmuConfig.GS.HWMipmap;
EmuConfig.GS.HWMipmapMode =
(EmuConfig.GS.HWMipmapMode >= GSHWMipmapMode::Enabled) ? GSHWMipmapMode::Disabled : GSHWMipmapMode::Enabled;
Host::AddKeyedOSDMessage("ToggleMipmapMode",
EmuConfig.GS.HWMipmap ?
(EmuConfig.GS.HWMipmapMode >= GSHWMipmapMode::Enabled) ?
TRANSLATE_STR("Hotkeys", "Hardware mipmapping is now enabled.") :
TRANSLATE_STR("Hotkeys", "Hardware mipmapping is now disabled."),
Host::OSD_INFO_DURATION);
Expand Down
73 changes: 49 additions & 24 deletions pcsx2/GS/Renderers/HW/GSRendererHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ GSRendererHW::GSRendererHW()
: GSRenderer()
{
MULTI_ISA_SELECT(GSRendererHWPopulateFunctions)(*this);
m_mipmap = GSConfig.HWMipmap;
m_mipmap = (GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled);
SetTCOffset();

pxAssert(!g_texture_cache);
Expand Down Expand Up @@ -88,7 +88,7 @@ void GSRendererHW::Reset(bool hardware_reset)
void GSRendererHW::UpdateSettings(const Pcsx2Config::GSOptions& old_config)
{
GSRenderer::UpdateSettings(old_config);
m_mipmap = GSConfig.HWMipmap;
m_mipmap = (GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled);
SetTCOffset();
}

Expand Down Expand Up @@ -2398,7 +2398,8 @@ void GSRendererHW::Draw()
GIFRegTEX0 TEX0 = {};
GSTextureCache::Source* src = nullptr;
TextureMinMaxResult tmm;

bool mipmap_active = false;

// Disable texture mapping if the blend is black and using alpha from vertex.
if (m_process_texture)
{
Expand All @@ -2407,7 +2408,8 @@ void GSRendererHW::Draw()
m_lod = GSVector2i(0, 0);

// Code from the SW renderer
if (IsMipMapActive())
mipmap_active = IsMipMapActive();
if (mipmap_active)
{
const int interpolation = (context->TEX1.MMIN & 1) + 1; // 1: round, 2: tri

Expand Down Expand Up @@ -2464,25 +2466,39 @@ void GSRendererHW::Draw()
m_lod.x = std::min<int>(m_lod.x, mxl);
m_lod.y = std::min<int>(m_lod.y, mxl);

TEX0 = (m_lod.x == 0) ? m_cached_ctx.TEX0 : GetTex0Layer(m_lod.x);
if (GSConfig.HWMipmapMode < GSHWMipmapMode::AllLevels)
{
TEX0 = (m_lod.x == 0) ? m_cached_ctx.TEX0 : GetTex0Layer(m_lod.x);

// upload the full chain (with offset) for the hash cache, in case some other texture uses more levels
// for basic mipmapping, we can get away with just doing the base image, since all the mips get generated anyway.
hash_lod_range = GSVector2i(m_lod.x, GSConfig.HWMipmap ? mxl : m_lod.x);
// upload the full chain (with offset) for the hash cache, in case some other texture uses more levels
// for basic mipmapping, we can get away with just doing the base image, since all the mips get generated anyway.
hash_lod_range = GSVector2i(m_lod.x, mxl);

MIP_CLAMP.MINU >>= m_lod.x;
MIP_CLAMP.MINV >>= m_lod.x;
MIP_CLAMP.MAXU >>= m_lod.x;
MIP_CLAMP.MAXV >>= m_lod.x;
MIP_CLAMP.MINU >>= m_lod.x;
MIP_CLAMP.MINV >>= m_lod.x;
MIP_CLAMP.MAXU >>= m_lod.x;
MIP_CLAMP.MAXV >>= m_lod.x;

for (int i = 0; i < m_lod.x; i++)
for (int i = 0; i < m_lod.x; i++)
{
m_vt.m_min.t *= 0.5f;
m_vt.m_max.t *= 0.5f;
}
}
else
{
m_vt.m_min.t *= 0.5f;
m_vt.m_max.t *= 0.5f;
hash_lod_range = GSVector2i(0, mxl);
TEX0 = m_cached_ctx.TEX0;
}

GL_CACHE("Mipmap LOD %d %d (%f %f) new size %dx%d (K %d L %u)", m_lod.x, m_lod.y, m_vt.m_lod.x, m_vt.m_lod.y, 1 << TEX0.TW, 1 << TEX0.TH, m_context->TEX1.K, m_context->TEX1.L);
GL_CACHE("Mipmap LOD %d %d (%f %f) new size %dx%d (K %d L %u)", m_lod.x, m_lod.y, m_vt.m_lod.x, m_vt.m_lod.y,
(1 << m_cached_ctx.TEX0.TW) >> m_lod.x, (1 << m_cached_ctx.TEX0.TH) >> m_lod.x, m_context->TEX1.K, m_context->TEX1.L);
}
else if (GSConfig.HWMipmapMode >= GSHWMipmapMode::AllLevels && m_context->TEX1.MXL > 0 && !m_context->TEX1.LCM)
{
mipmap_active = true;
hash_lod_range = GSVector2i(0, std::min<int>(static_cast<int>(m_context->TEX1.MXL), 6));
TEX0 = m_cached_ctx.TEX0;
GL_CACHE("Looking up all %d texture LODs", hash_lod_range.y); }
else
{
TEX0 = m_cached_ctx.TEX0;
Expand Down Expand Up @@ -2563,7 +2579,7 @@ void GSRendererHW::Draw()
else
{
src = tex_psm.depth ? g_texture_cache->LookupDepthSource(true, TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME.Block(), req_color, req_alpha) :
g_texture_cache->LookupSource(true, TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmap || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr,
g_texture_cache->LookupSource(true, TEX0, env.TEXA, MIP_CLAMP, tmm.coverage, (GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled || GSConfig.TriFilter == TriFiltering::Forced) ? &hash_lod_range : nullptr,
possible_shuffle, m_vt.IsLinear(), m_cached_ctx.FRAME.Block(), req_color, req_alpha);

if (!src) [[unlikely]]
Expand Down Expand Up @@ -2958,7 +2974,7 @@ void GSRendererHW::Draw()
}

// Round 2
if (IsMipMapActive() && GSConfig.HWMipmap && !tex_psm.depth && !src->m_from_hash_cache)
if (mipmap_active && !tex_psm.depth && !src->m_from_hash_cache)
{
// Upload remaining texture layers
const GSVector4 tmin = m_vt.m_min.t;
Expand Down Expand Up @@ -4910,7 +4926,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
const bool shader_emulated_sampler = tex->m_palette || (tex->m_target && !m_conf.ps.shuffle && cpsm.fmt != 0) ||
complex_wms_wmt || psm.depth || target_region;
const bool can_trilinear = !tex->m_palette && !tex->m_target && !m_conf.ps.shuffle;
const bool trilinear_manual = need_mipmap && GSConfig.HWMipmap;
bool trilinear_manual = need_mipmap && GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled;

bool bilinear = m_vt.IsLinear();
int trilinear = 0;
Expand All @@ -4925,7 +4941,7 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
if (can_trilinear)
{
trilinear = static_cast<u8>(GS_MIN_FILTER::Linear_Mipmap_Linear);
trilinear_auto = !tex->m_target && (!need_mipmap || !GSConfig.HWMipmap);
trilinear_auto = !tex->m_target && (!need_mipmap || GSConfig.HWMipmapMode == GSHWMipmapMode::Disabled);
}
}
break;
Expand All @@ -4934,10 +4950,10 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
case TriFiltering::Automatic:
{
// Can only use PS2 trilinear when mipmapping is enabled.
if (need_mipmap && GSConfig.HWMipmap && can_trilinear)
if (need_mipmap && GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled && can_trilinear)
{
trilinear = m_context->TEX1.MMIN;
trilinear_auto = !tex->m_target && !GSConfig.HWMipmap;
trilinear_auto = !tex->m_target && GSConfig.HWMipmapMode == GSHWMipmapMode::Disabled;
}
}
break;
Expand All @@ -4946,6 +4962,12 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
default:
break;
}
if (GSConfig.HWMipmapMode >= GSHWMipmapMode::Unclamped && !shader_emulated_sampler &&
m_context->TEX1.MXL > 0 && m_context->TEX1.MMIN >= 2 && m_context->TEX1.MMIN <= 5 && !m_context->TEX1.LCM)
{
trilinear = static_cast<u8>(m_vt.IsLinear() ? GS_MIN_FILTER::Linear_Mipmap_Linear : GS_MIN_FILTER::Nearest_Mipmap_Linear);
trilinear_manual = false;
}

// 1 and 0 are equivalent
m_conf.ps.wms = (wms & 2 || target_region) ? wms : 0;
Expand Down Expand Up @@ -5146,7 +5168,10 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
{
m_conf.cb_ps.LODParams.x = static_cast<float>(m_context->TEX1.K) / 16.0f;
m_conf.cb_ps.LODParams.y = static_cast<float>(1 << m_context->TEX1.L);
m_conf.cb_ps.LODParams.z = static_cast<float>(m_lod.x); // Offset because first layer is m_lod, dunno if we can do better
m_conf.cb_ps.LODParams.z = (GSConfig.HWMipmapMode >= GSHWMipmapMode::Unclamped) ?
std::log2(rt ? rt->GetScale() : ds->GetScale()) :
static_cast<float>((GSConfig.HWMipmapMode == GSHWMipmapMode::AllLevels) ? 0.0f : m_lod.x);

m_conf.cb_ps.LODParams.w = static_cast<float>(m_lod.y);
m_conf.ps.manual_lod = 1;
}
Expand Down
9 changes: 7 additions & 2 deletions pcsx2/GS/Renderers/HW/GSTextureCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4447,7 +4447,9 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
{
// lod won't contain the full range when using basic mipmapping, only that
// which is hashed, so we just allocate the full thing.
tlevels = GSConfig.HWMipmap ? std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th)) : -1;
tlevels = (GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled) ?
std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th)) :
-1;
src->m_lod = *lod;
}

Expand Down Expand Up @@ -5390,7 +5392,10 @@ GSTextureCache::HashCacheEntry* GSTextureCache::LookupHashCache(const GIFRegTEX0
// expand/upload texture
const int tw = region.HasX() ? region.GetWidth() : (1 << TEX0.TW);
const int th = region.HasY() ? region.GetHeight() : (1 << TEX0.TH);
const int tlevels = lod ? (GSConfig.HWMipmap ? std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th)) : -1) : 1;
const int tlevels = lod ? ((GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled) ?
std::min(lod->y - lod->x + 1, GSDevice::GetMipmapLevelsForSize(tw, th)) :
-1) :
1;
GSTexture* tex = g_gs_device->CreateTexture(tw, th, tlevels, paltex ? GSTexture::Format::UNorm8 : GSTexture::Format::Color);
if (!tex)
{
Expand Down
2 changes: 1 addition & 1 deletion pcsx2/GS/Renderers/HW/GSTextureReplacements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -661,7 +661,7 @@ void GSTextureReplacements::PrecacheReplacementTextures()

// predict whether the requests will come with mipmaps
// TODO: This will be wrong for hw mipmap games like Jak.
const bool mipmap = GSConfig.HWMipmap || GSConfig.TriFilter == TriFiltering::Forced;
const bool mipmap = (GSConfig.HWMipmapMode >= GSHWMipmapMode::Enabled || GSConfig.TriFilter == TriFiltering::Forced);

// pretty simple, just go through the filenames and if any aren't cached, cache them
for (const auto& it : s_replacement_texture_filenames)
Expand Down
8 changes: 5 additions & 3 deletions pcsx2/GameDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -622,7 +622,7 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
return (static_cast<int>(config.PCRTCOverscan) == value);

case GSHWFixId::Mipmap:
return (static_cast<int>(config.HWMipmap) == value);
return (static_cast<int>(config.HWMipmapMode) == value);

case GSHWFixId::TrilinearFiltering:
return (config.TriFilter == TriFiltering::Automatic || static_cast<int>(config.TriFilter) == value);
Expand Down Expand Up @@ -780,8 +780,10 @@ void GameDatabaseSchema::GameEntry::applyGSHardwareFixes(Pcsx2Config::GSOptions&
break;

case GSHWFixId::Mipmap:
config.HWMipmap = (value > 0);
break;
{
if (value >= 0 && value < static_cast<int>(GSHWMipmapMode::MaxCount))
config.HWMipmapMode = static_cast<GSHWMipmapMode>(value > 0);
}

case GSHWFixId::TrilinearFiltering:
{
Expand Down
15 changes: 11 additions & 4 deletions pcsx2/ImGui/FullscreenUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3569,6 +3569,12 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
"11",
"12",
};
static constexpr const char* s_mipmapping_options[] = {
FSUI_NSTR("Disabled"),
FSUI_NSTR("Enabled"),
FSUI_NSTR("All Levels"),
FSUI_NSTR("Unclamped"),
};
static constexpr const char* s_bilinear_options[] = {
FSUI_NSTR("Nearest"),
FSUI_NSTR("Bilinear (Forced)"),
Expand Down Expand Up @@ -3702,6 +3708,9 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
DrawStringListSetting(bsi, FSUI_CSTR("Internal Resolution"),
FSUI_CSTR("Multiplies the render resolution by the specified factor (upscaling)."), "EmuCore/GS", "upscale_multiplier",
"1.000000", s_resolution_options, s_resolution_values, std::size(s_resolution_options), true);
DrawIntListSetting(
bsi, FSUI_CSTR("Mipmapping"), FSUI_CSTR("Enables emulation of the GS's texture mipmapping."), "EmuCore/GS", "hw_mipmap_mode",
static_cast<int>(GSHWMipmapMode::Enabled), s_mipmapping_options, std::size(s_mipmapping_options), true);
DrawIntListSetting(bsi, FSUI_CSTR("Bilinear Filtering"),
FSUI_CSTR("Selects where bilinear filtering is utilized when rendering textures."), "EmuCore/GS", "filter",
static_cast<int>(BiFiltering::PS2), s_bilinear_options, std::size(s_bilinear_options), true);
Expand All @@ -3721,8 +3730,6 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
"Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games."),
"EmuCore/GS", "texture_preloading", static_cast<int>(TexturePreloadingLevel::Off), s_preloading_options,
std::size(s_preloading_options), true);
DrawToggleSetting(
bsi, FSUI_CSTR("Mipmapping"), FSUI_CSTR("Enables emulation of the GS's texture mipmapping."), "EmuCore/GS", "hw_mipmap", true);
}
else
{
Expand Down Expand Up @@ -7027,6 +7034,8 @@ TRANSLATE_NOOP("FullscreenUI", "Enables internal Anti-Blur hacks. Less accurate
TRANSLATE_NOOP("FullscreenUI", "Rendering");
TRANSLATE_NOOP("FullscreenUI", "Internal Resolution");
TRANSLATE_NOOP("FullscreenUI", "Multiplies the render resolution by the specified factor (upscaling).");
TRANSLATE_NOOP("FullscreenUI", "Mipmapping");
TRANSLATE_NOOP("FullscreenUI", "Enables emulation of the GS's texture mipmapping.");
TRANSLATE_NOOP("FullscreenUI", "Bilinear Filtering");
TRANSLATE_NOOP("FullscreenUI", "Selects where bilinear filtering is utilized when rendering textures.");
TRANSLATE_NOOP("FullscreenUI", "Trilinear Filtering");
Expand All @@ -7039,8 +7048,6 @@ TRANSLATE_NOOP("FullscreenUI", "Blending Accuracy");
TRANSLATE_NOOP("FullscreenUI", "Determines the level of accuracy when emulating blend modes not supported by the host graphics API.");
TRANSLATE_NOOP("FullscreenUI", "Texture Preloading");
TRANSLATE_NOOP("FullscreenUI", "Uploads full textures to the GPU on use, rather than only the utilized regions. Can improve performance in some games.");
TRANSLATE_NOOP("FullscreenUI", "Mipmapping");
TRANSLATE_NOOP("FullscreenUI", "Enables emulation of the GS's texture mipmapping.");
TRANSLATE_NOOP("FullscreenUI", "Software Rendering Threads");
TRANSLATE_NOOP("FullscreenUI", "Number of threads to use in addition to the main GS thread for rasterization.");
TRANSLATE_NOOP("FullscreenUI", "Auto Flush (Software)");
Expand Down
Loading