GS: Support fractional upscale

This commit is contained in:
Connor McLaughlin 2022-07-19 20:46:59 +10:00 committed by refractionpcsx2
parent 44bad588b4
commit 6c17f7ad49
30 changed files with 241 additions and 168 deletions

View File

@ -288,9 +288,18 @@ PS_OUTPUT ps_convert_rgba_8i(PS_INPUT input)
int txN = tb.x | (int(input.p.x) & 7);
int txH = tb.x | ((int(input.p.x) + 4) & 7);
txN *= PS_SCALE_FACTOR;
txH *= PS_SCALE_FACTOR;
ty *= PS_SCALE_FACTOR;
if (floor(PS_SCALE_FACTOR) != PS_SCALE_FACTOR)
{
txN = (int)((float)txN * PS_SCALE_FACTOR);
txH = (int)((float)txH * PS_SCALE_FACTOR);
ty = (int)((float)ty * PS_SCALE_FACTOR);
}
else
{
txN *= PS_SCALE_FACTOR;
txH *= PS_SCALE_FACTOR;
ty *= PS_SCALE_FACTOR;
}
// TODO investigate texture gather
float4 cN = Texture.Load(int3(txN, ty, 0));

View File

@ -43,7 +43,7 @@
#define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0
#define PS_INVALID_TEX0 0
#define PS_SCALE_FACTOR 1
#define PS_SCALE_FACTOR 1.0
#define PS_HDR 0
#define PS_COLCLIP 0
#define PS_BLEND_A 0

View File

@ -235,9 +235,18 @@ void ps_convert_rgba_8i()
int txN = tb.x | (int(gl_FragCoord.x) & 7);
int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7);
txN *= PS_SCALE_FACTOR;
txH *= PS_SCALE_FACTOR;
ty *= PS_SCALE_FACTOR;
if (floor(PS_SCALE_FACTOR) != PS_SCALE_FACTOR)
{
txN = int(float(txN) * PS_SCALE_FACTOR);
txH = int(float(txH) * PS_SCALE_FACTOR);
ty = int(float(ty) * PS_SCALE_FACTOR);
}
else
{
txN *= int(PS_SCALE_FACTOR);
txH *= int(PS_SCALE_FACTOR);
ty *= int(PS_SCALE_FACTOR);
}
// TODO investigate texture gather
vec4 cN = texelFetch(TextureSampler, ivec2(txN, ty), 0);

View File

@ -1,5 +1,5 @@
#ifndef PS_SCALE_FACTOR
#define PS_SCALE_FACTOR 1
#define PS_SCALE_FACTOR 1.0
#endif
#ifdef VERTEX_SHADER
@ -269,9 +269,18 @@ void ps_convert_rgba_8i()
int txN = tb.x | (int(gl_FragCoord.x) & 7);
int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7);
txN *= PS_SCALE_FACTOR;
txH *= PS_SCALE_FACTOR;
ty *= PS_SCALE_FACTOR;
if (floor(PS_SCALE_FACTOR) != PS_SCALE_FACTOR)
{
txN = int(float(txN) * PS_SCALE_FACTOR);
txH = int(float(txH) * PS_SCALE_FACTOR);
ty = int(float(ty) * PS_SCALE_FACTOR);
}
else
{
txN *= int(PS_SCALE_FACTOR);
txH *= int(PS_SCALE_FACTOR);
ty *= int(PS_SCALE_FACTOR);
}
// TODO investigate texture gather
vec4 cN = texelFetch(samp0, ivec2(txN, ty), 0);

View File

@ -333,7 +333,7 @@ void main()
#define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0
#define PS_INVALID_TEX0 0
#define PS_SCALE_FACTOR 1
#define PS_SCALE_FACTOR 1.0
#define PS_HDR 0
#define PS_COLCLIP 0
#define PS_BLEND_A 0

View File

@ -177,7 +177,41 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
//////////////////////////////////////////////////////////////////////////
// HW Settings
//////////////////////////////////////////////////////////////////////////
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", 1, 1);
static const char* upscale_entries[] = {
"Native (PS2)",
"1.25x Native",
"1.5x Native",
"1.75x Native",
"2x Native (~720p)",
"2.25x Native",
"2.5x Native",
"2.75x Native",
"3x Native (~1080p)",
"3.5x Native",
"4x Native (~1440p/2K)",
"5x Native (~1620p)",
"6x Native (~2160p/4K)",
"7x Native (~2520p)",
"8x Native (~2880p)",
nullptr};
static const char* upscale_values[] = {
"1",
"1.25",
"1.5",
"1.75",
"2",
"2.25",
"2.5",
"2.75",
"3",
"3.5",
"4",
"5",
"6",
"7",
"8",
nullptr };
SettingWidgetBinder::BindWidgetToEnumSetting(sif, m_ui.upscaleMultiplier, "EmuCore/GS", "upscale_multiplier", upscale_entries, upscale_values, "1.0");
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.textureFiltering, "EmuCore/GS", "filter", static_cast<int>(BiFiltering::PS2));
SettingWidgetBinder::BindWidgetToIntSetting(sif, m_ui.trilinearFiltering, "EmuCore/GS", "TriFilter", static_cast<int>(TriFiltering::Automatic), -1);
SettingWidgetBinder::BindWidgetToEnumSetting(

View File

@ -388,48 +388,7 @@
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="upscaleMultiplier">
<item>
<property name="text">
<string>Native (PS2)</string>
</property>
</item>
<item>
<property name="text">
<string>2x Native (~720p)</string>
</property>
</item>
<item>
<property name="text">
<string>3x Native (~1080p)</string>
</property>
</item>
<item>
<property name="text">
<string>4x Native (~1440p/2K)</string>
</property>
</item>
<item>
<property name="text">
<string>5x Native (~1620p)</string>
</property>
</item>
<item>
<property name="text">
<string>6x Native (~2160p/4K)</string>
</property>
</item>
<item>
<property name="text">
<string>7x Native (~2520p)</string>
</property>
</item>
<item>
<property name="text">
<string>8x Native (~2880p)</string>
</property>
</item>
</widget>
<widget class="QComboBox" name="upscaleMultiplier" />
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_6">

View File

@ -560,7 +560,7 @@ struct Pcsx2Config
float OsdScale{100.0};
GSRendererType Renderer{GSRendererType::Auto};
uint UpscaleMultiplier{1};
float UpscaleMultiplier{1.0f};
HWMipmapLevel HWMipmap{HWMipmapLevel::Automatic};
AccBlendLevel AccurateBlendingUnit{AccBlendLevel::Basic};

View File

@ -2534,16 +2534,40 @@ void FullscreenUI::DrawGraphicsSettingsPage()
static constexpr const char* s_deinterlacing_options[] = {"None", "Weave (Top Field First, Sawtooth)",
"Weave (Bottom Field First, Sawtooth)", "Bob (Top Field First)", "Bob (Bottom Field First)", "Blend (Top Field First, Half FPS)",
"Blend (Bottom Field First, Half FPS)", "Automatic (Default)"};
static constexpr const char* s_resolution_options[] = {
static const char* s_resolution_options[] = {
"Native (PS2)",
"1.25x Native",
"1.5x Native",
"1.75x Native",
"2x Native (~720p)",
"2.25x Native",
"2.5x Native",
"2.75x Native",
"3x Native (~1080p)",
"3.5x Native",
"4x Native (~1440p/2K)",
"5x Native (~1620p)",
"6x Native (~2160p/4K)",
"7x Native (~2520p)",
"8x Native (~2880p)",
};
static const char* s_resolution_values[] = {
"1",
"1.25",
"1.5",
"1.75",
"2",
"2.25",
"2.5",
"2.75",
"3",
"3.5",
"4",
"5",
"6",
"7",
"8",
};
static constexpr const char* s_mipmapping_options[] = {"Automatic (Default)", "Off", "Basic (Generated Mipmaps)", "Full (PS2 Mipmaps)"};
static constexpr const char* s_bilinear_options[] = {
"Nearest", "Bilinear (Forced)", "Bilinear (PS2)", "Bilinear (Forced excluding sprite)"};
@ -2612,8 +2636,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
MenuHeading("Rendering");
if (is_hardware)
{
DrawIntListSetting(bsi, "Internal Resolution", "Multiplies the render resolution by the specified factor (upscaling).",
"EmuCore/GS", "upscale_multiplier", 1, s_resolution_options, std::size(s_resolution_options), 1);
DrawStringListSetting(bsi, "Internal Resolution", "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));
DrawIntListSetting(bsi, "Mipmapping", "Determines how mipmaps are used when rendering textures.", "EmuCore/GS", "mipmap_hw",
static_cast<int>(HWMipmapLevel::Automatic), s_mipmapping_options, std::size(s_mipmapping_options), -1);
DrawIntListSetting(bsi, "Bilinear Filtering", "Selects where bilinear filtering is utilized when rendering textures.", "EmuCore/GS",

View File

@ -51,7 +51,7 @@ GSState::GSState()
{
// m_nativeres seems to be a hack. Unfortunately it impacts draw call number which make debug painful in the replayer.
// Let's keep it disabled to ease debug.
m_nativeres = GSConfig.UpscaleMultiplier == 1;
m_nativeres = GSConfig.UpscaleMultiplier == 1.0f;
m_mipmap = GSConfig.Mipmap;
s_n = 0;

View File

@ -326,7 +326,7 @@ bool GSRenderer::Merge(int field)
if (m_regs->SMODE2.FFMD && !is_bob && !GSConfig.DisableInterlaceOffset && GSConfig.InterlaceMode != GSInterlaceMode::Off)
{
// We do half because FFMD is a half sized framebuffer, then we offset by 1 in the shader for the actual interlace
if(GetUpscaleMultiplier() > 1)
if(GetUpscaleMultiplier() > 1.0f)
interlace_offset += ((((tex[1] ? tex[1]->GetScale().y : tex[0]->GetScale().y) + 0.5f) * 0.5f) - 1.0f) * static_cast<float>(field ^ field2);
offset = 1.0f;
}
@ -363,7 +363,8 @@ bool GSRenderer::Merge(int field)
resolution.y = std::min(max_resolution.y, resolution.y);
}
fs = resolution * GSVector2i(GetUpscaleMultiplier());
fs = GSVector2i(static_cast<int>(static_cast<float>(resolution.x) * GetUpscaleMultiplier()),
static_cast<int>(static_cast<float>(resolution.y) * GetUpscaleMultiplier()));
ds = fs;
// When interlace(FRAME) mode, the rect is half height, so it needs to be stretched.
@ -545,12 +546,12 @@ static GSVector4i CalculateDrawSrcRect(const GSTexture* src)
#ifndef PCSX2_CORE
return GSVector4i(0, 0, src->GetWidth(), src->GetHeight());
#else
const int upscale = GSConfig.UpscaleMultiplier;
const float upscale = GSConfig.UpscaleMultiplier;
const GSVector2i size(src->GetSize());
const int left = GSConfig.Crop[0] * upscale;
const int top = GSConfig.Crop[1] * upscale;
const int right = size.x - (GSConfig.Crop[2] * upscale);
const int bottom = size.y - (GSConfig.Crop[3] * upscale);
const int left = static_cast<int>(static_cast<float>(GSConfig.Crop[0]) * upscale);
const int top = static_cast<int>(static_cast<float>(GSConfig.Crop[1]) * upscale);
const int right = size.x - static_cast<int>(static_cast<float>(GSConfig.Crop[2]) * upscale);
const int bottom = size.y - static_cast<int>(static_cast<float>(GSConfig.Crop[3]) * upscale);
return GSVector4i(left, top, right, bottom);
#endif
}

View File

@ -59,7 +59,7 @@ public:
virtual void VSync(u32 field, bool registers_written);
virtual bool CanUpscale() { return false; }
virtual int GetUpscaleMultiplier() { return 1; }
virtual float GetUpscaleMultiplier() { return 1.0f; }
virtual GSVector2 GetTextureScaleFactor() { return { 1.0f, 1.0f }; }
GSVector2i GetInternalResolution();

View File

@ -143,7 +143,7 @@ bool GSDevice11::Create()
}
ShaderMacro sm_convert(m_shader_cache.GetFeatureLevel());
sm_convert.AddMacro("PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
sm_convert.AddMacro("PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier));
D3D_SHADER_MACRO* sm_convert_ptr = sm_convert.GetPtr();
@ -1312,7 +1312,12 @@ GSDevice11::ShaderMacro::ShaderMacro(D3D_FEATURE_LEVEL fl)
void GSDevice11::ShaderMacro::AddMacro(const char* n, int d)
{
mlist.emplace_back(n, std::to_string(d));
AddMacro(n, std::to_string(d));
}
void GSDevice11::ShaderMacro::AddMacro(const char* n, std::string d)
{
mlist.emplace_back(n, std::move(d));
}
D3D_SHADER_MACRO* GSDevice11::ShaderMacro::GetPtr(void)

View File

@ -104,6 +104,7 @@ public:
public:
ShaderMacro(D3D_FEATURE_LEVEL fl);
void AddMacro(const char* n, int d);
void AddMacro(const char* n, std::string d);
D3D_SHADER_MACRO* GetPtr(void);
};

View File

@ -156,7 +156,7 @@ void GSDevice11::SetupPS(const PSSelector& sel, const GSHWDrawConfig::PSConstant
{
ShaderMacro sm(m_shader_cache.GetFeatureLevel());
sm.AddMacro("PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
sm.AddMacro("PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier));
sm.AddMacro("PS_FST", sel.fst);
sm.AddMacro("PS_WMS", sel.wms);
sm.AddMacro("PS_WMT", sel.wmt);

View File

@ -75,7 +75,12 @@ GSDevice12::ShaderMacro::ShaderMacro(D3D_FEATURE_LEVEL fl)
void GSDevice12::ShaderMacro::AddMacro(const char* n, int d)
{
mlist.emplace_back(n, std::to_string(d));
AddMacro(n, std::to_string(d));
}
void GSDevice12::ShaderMacro::AddMacro(const char* n, std::string d)
{
mlist.emplace_back(n, std::move(d));
}
D3D_SHADER_MACRO* GSDevice12::ShaderMacro::GetPtr(void)
@ -978,7 +983,7 @@ GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityVertexShader(const std::strin
GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string& source, const char* entry_point)
{
ShaderMacro sm_model(m_shader_cache.GetFeatureLevel());
sm_model.AddMacro("PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
sm_model.AddMacro("PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier));
return m_shader_cache.GetPixelShader(source, sm_model.GetPtr(), entry_point);
}
@ -1519,7 +1524,7 @@ const ID3DBlob* GSDevice12::GetTFXPixelShader(const GSHWDrawConfig::PSSelector&
return it->second.get();
ShaderMacro sm(m_shader_cache.GetFeatureLevel());
sm.AddMacro("PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
sm.AddMacro("PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier));
sm.AddMacro("PS_FST", sel.fst);
sm.AddMacro("PS_WMS", sel.wms);
sm.AddMacro("PS_WMT", sel.wmt);

View File

@ -102,6 +102,7 @@ public:
public:
ShaderMacro(D3D_FEATURE_LEVEL fl);
void AddMacro(const char* n, int d);
void AddMacro(const char* n, std::string d);
D3D_SHADER_MACRO* GetPtr(void);
};

View File

@ -93,7 +93,8 @@ GSVector2i GSRendererHW::GetOutputSize(int real_h)
// Include negative display offsets in the height here.
crtc_size.y = std::max(crtc_size.y, real_h);
return crtc_size * GSVector2i(static_cast<int>(GSConfig.UpscaleMultiplier), static_cast<int>(GSConfig.UpscaleMultiplier));
return GSVector2i(static_cast<float>(crtc_size.x) * GSConfig.UpscaleMultiplier,
static_cast<float>(crtc_size.y) * GSConfig.UpscaleMultiplier);
}
void GSRendererHW::SetTCOffset()
@ -171,10 +172,10 @@ void GSRendererHW::SetGameCRC(u32 crc, int options)
bool GSRendererHW::CanUpscale()
{
return GSConfig.UpscaleMultiplier != 1;
return GSConfig.UpscaleMultiplier != 1.0f;
}
int GSRendererHW::GetUpscaleMultiplier()
float GSRendererHW::GetUpscaleMultiplier()
{
return GSConfig.UpscaleMultiplier;
}
@ -661,7 +662,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex)
{
if (GSConfig.UserHacks_HalfPixelOffset <= 1 || GetUpscaleMultiplier() == 1)
if (GSConfig.UserHacks_HalfPixelOffset <= 1 || GetUpscaleMultiplier() == 1.0f)
return GSVector4(0.0f);
const GSVertex* v = &m_vertex.buff[0];
@ -798,7 +799,7 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
GSVector2 GSRendererHW::GetTextureScaleFactor()
{
const float f_upscale = static_cast<float>(GetUpscaleMultiplier());
const float f_upscale = GetUpscaleMultiplier();
return GSVector2(f_upscale, f_upscale);
}
@ -835,7 +836,8 @@ GSVector2i GSRendererHW::GetTargetSize(GSVector2i* unscaled_size)
GL_INS("Target size for %x %u %u: %ux%u", m_context->FRAME.FBP, m_context->FRAME.FBW, m_context->FRAME.PSM, width, height);
return GSVector2i(static_cast<int>(width * GSConfig.UpscaleMultiplier), static_cast<int>(height * GSConfig.UpscaleMultiplier));
return GSVector2i(static_cast<int>(static_cast<float>(width) * GSConfig.UpscaleMultiplier),
static_cast<int>(static_cast<float>(height) * GSConfig.UpscaleMultiplier));
}
void GSRendererHW::InvalidateVideoMem(const GIFRegBITBLTBUF& BITBLTBUF, const GSVector4i& r)
@ -1963,7 +1965,7 @@ void GSRendererHW::SetupIA(const float& sx, const float& sy)
for (unsigned int i = 0; i < m_vertex.next; i++)
m_vertex.buff[i].UV &= 0x3FEF3FEF;
}
const bool unscale_pt_ln = !GSConfig.UserHacks_DisableSafeFeatures && (GetUpscaleMultiplier() != 1);
const bool unscale_pt_ln = !GSConfig.UserHacks_DisableSafeFeatures && (GetUpscaleMultiplier() != 1.0f);
const GSDevice::FeatureSupport features = g_gs_device->Features();
ASSERT(VerifyIndices());

View File

@ -177,7 +177,7 @@ public:
void SetGameCRC(u32 crc, int options) override;
bool CanUpscale() override;
int GetUpscaleMultiplier() override;
float GetUpscaleMultiplier() override;
void Lines2Sprites();
bool VerifyIndices();
template <GSHWDrawConfig::VSExpand Expand> void ExpandIndices();

View File

@ -1864,8 +1864,8 @@ GSTextureCache::Source* GSTextureCache::CreateSource(const GIFRegTEX0& TEX0, con
if (GSConfig.UserHacks_HalfPixelOffset == 1 && hack)
{
modxy = static_cast<float>(g_gs_renderer->GetUpscaleMultiplier());
switch (g_gs_renderer->GetUpscaleMultiplier())
modxy = g_gs_renderer->GetUpscaleMultiplier();
switch (static_cast<int>(std::round(g_gs_renderer->GetUpscaleMultiplier())))
{
case 2: case 4: case 6: case 8: modxy += 0.2f; break;
case 3: case 7: modxy += 0.1f; break;

View File

@ -611,9 +611,8 @@ bool GSDeviceMTL::Create()
m_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
u8 upscale = std::max(1, theApp.GetConfigI("upscale_multiplier"));
vector_uchar2 upscale2 = vector2(upscale, upscale);
[m_fn_constants setConstantValue:&upscale2 type:MTLDataTypeUChar2 atIndex:GSMTLConstantIndex_SCALING_FACTOR];
vector_float2 upscale2 = vector2(GSConfig.UpscaleMultiplier, GSConfig.UpscaleMultiplier);
[m_fn_constants setConstantValue:&upscale2 type:MTLDataTypeFloat2 atIndex:GSMTLConstantIndex_SCALING_FACTOR];
setFnConstantB(m_fn_constants, m_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
m_hw_vertex = MRCTransfer([MTLVertexDescriptor new]);

View File

@ -19,7 +19,7 @@
using namespace metal;
constant uchar2 SCALING_FACTOR [[function_constant(GSMTLConstantIndex_SCALING_FACTOR)]];
constant float2 SCALING_FACTOR [[function_constant(GSMTLConstantIndex_SCALING_FACTOR)]];
struct ConvertShaderData
{

View File

@ -268,9 +268,18 @@ fragment float4 ps_convert_rgba_8i(ConvertShaderData data [[stage_in]], ConvertP
uint txN = tb.x | (uint(data.p.x) & 7);
uint txH = tb.x | ((uint(data.p.x) + 4) & 7);
txN *= SCALING_FACTOR.x;
txH *= SCALING_FACTOR.x;
ty *= SCALING_FACTOR.y;
if (floor(SCALING_FACTOR.x) != SCALING_FACTOR.x)
{
txN = (int)((float)txN * SCALING_FACTOR.x);
txH = (int)((float)txH * SCALING_FACTOR.x);
ty = (int)((float)ty * SCALING_FACTOR.y);
}
else
{
txN *= SCALING_FACTOR.x;
txH *= SCALING_FACTOR.x;
ty *= SCALING_FACTOR.y;
}
// TODO investigate texture gather
float4 cN = res.texture.read(uint2(txN, ty));

View File

@ -502,7 +502,7 @@ struct PSMain
float4 sample_depth(float2 st)
{
float2 uv_f = float2(clamp_wrap_uv_depth(ushort2(st))) * (float2(SCALING_FACTOR) * float2(1.f / 16.f));
float2 uv_f = float2(clamp_wrap_uv_depth(ushort2(st))) * (SCALING_FACTOR * float2(1.f / 16.f));
ushort2 uv = ushort2(uv_f);
float4 t = float4(0);
@ -778,7 +778,7 @@ struct PSMain
if (PS_DITHER == 2)
fpos = ushort2(in.p.xy);
else
fpos = ushort2(in.p.xy / float2(SCALING_FACTOR));
fpos = ushort2(in.p.xy / SCALING_FACTOR);
C.rgb += cb.dither_matrix[fpos.y & 3][fpos.x & 3];
}

View File

@ -237,7 +237,7 @@ bool GSDeviceOGL::Create()
GLint point_range[2] = {};
glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_range);
m_features.point_expand = (point_range[0] <= static_cast<GLint>(GSConfig.UpscaleMultiplier) && point_range[1] >= static_cast<GLint>(GSConfig.UpscaleMultiplier));
m_features.point_expand = (point_range[0] <= GSConfig.UpscaleMultiplier && point_range[1] >= GSConfig.UpscaleMultiplier);
Console.WriteLn("Using %s for point expansion.", m_features.point_expand ? "hardware" : "geometry shaders");
@ -380,7 +380,7 @@ bool GSDeviceOGL::Create()
{
const char* name = shaderName(static_cast<ShaderConvert>(i));
const std::string macro_sel = (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I) ?
StringUtil::StdStringFromFormat("#define PS_SCALE_FACTOR %d\n", GSConfig.UpscaleMultiplier) :
fmt::format("#define PS_SCALE_FACTOR {}\n", GSConfig.UpscaleMultiplier) :
std::string();
const std::string ps(GetShaderSource(name, GL_FRAGMENT_SHADER, m_shader_common_header, *convert_glsl, macro_sel));
if (!m_shader_cache.GetProgram(&m_convert.ps[i], m_convert.vs, {}, ps))
@ -456,7 +456,7 @@ bool GSDeviceOGL::Create()
for (size_t i = 0; i < std::size(m_merge_obj.ps); i++)
{
const std::string ps(GetShaderSource(StringUtil::StdStringFromFormat("ps_main%d", i), GL_FRAGMENT_SHADER, m_shader_common_header, *shader, {}));
const std::string ps(GetShaderSource(fmt::format("ps_main{}", i), GL_FRAGMENT_SHADER, m_shader_common_header, *shader, {}));
if (!m_shader_cache.GetProgram(&m_merge_obj.ps[i], m_convert.vs, {}, ps))
return false;
m_merge_obj.ps[i].SetFormattedName("Merge pipe %zu", i);
@ -479,7 +479,7 @@ bool GSDeviceOGL::Create()
for (size_t i = 0; i < std::size(m_interlace.ps); i++)
{
const std::string ps(GetShaderSource(StringUtil::StdStringFromFormat("ps_main%d", i), GL_FRAGMENT_SHADER, m_shader_common_header, *shader, {}));
const std::string ps(GetShaderSource(fmt::format("ps_main{}", i), GL_FRAGMENT_SHADER, m_shader_common_header, *shader, {}));
if (!m_shader_cache.GetProgram(&m_interlace.ps[i], m_convert.vs, {}, ps))
return false;
m_interlace.ps[i].SetFormattedName("Merge pipe %zu", i);
@ -538,7 +538,7 @@ bool GSDeviceOGL::Create()
for (size_t i = 0; i < std::size(m_date.primid_ps); i++)
{
const std::string ps(GetShaderSource(
StringUtil::StdStringFromFormat("ps_stencil_image_init_%d", i),
fmt::format("ps_stencil_image_init_{}", i),
GL_FRAGMENT_SHADER, m_shader_common_header, *convert_glsl, {}));
m_shader_cache.GetProgram(&m_date.primid_ps[i], m_convert.vs, {}, ps);
m_date.primid_ps[i].SetFormattedName("PrimID Destination Alpha Init %d", i);
@ -1027,11 +1027,11 @@ std::string GSDeviceOGL::GetVSSource(VSSelector sel)
Console.WriteLn("Compiling new vertex shader with selector 0x%" PRIX64, sel.key);
#endif
std::string macro = StringUtil::StdStringFromFormat("#define VS_INT_FST %d\n", sel.int_fst)
+ StringUtil::StdStringFromFormat("#define VS_IIP %d\n", sel.iip)
+ StringUtil::StdStringFromFormat("#define VS_POINT_SIZE %d\n", sel.point_size);
std::string macro = fmt::format("#define VS_INT_FST {}\n", static_cast<u32>(sel.int_fst))
+ fmt::format("#define VS_IIP {}\n", static_cast<u32>(sel.iip))
+ fmt::format("#define VS_POINT_SIZE {}\n", static_cast<u32>(sel.point_size));
if (sel.point_size)
macro += StringUtil::StdStringFromFormat("#define VS_POINT_SIZE_VALUE %d\n", GSConfig.UpscaleMultiplier);
macro += fmt::format("#define VS_POINT_SIZE_VALUE {}\n", GSConfig.UpscaleMultiplier);
std::string src = GenGlslHeader("vs_main", GL_VERTEX_SHADER, macro);
src += m_shader_common_header;
@ -1045,9 +1045,9 @@ std::string GSDeviceOGL::GetGSSource(GSSelector sel)
Console.WriteLn("Compiling new geometry shader with selector 0x%" PRIX64, sel.key);
#endif
std::string macro = StringUtil::StdStringFromFormat("#define GS_POINT %d\n", sel.point)
+ StringUtil::StdStringFromFormat("#define GS_LINE %d\n", sel.line)
+ StringUtil::StdStringFromFormat("#define GS_IIP %d\n", sel.iip);
std::string macro = fmt::format("#define GS_POINT {}\n", static_cast<u32>(sel.point))
+ fmt::format("#define GS_LINE {}\n", static_cast<u32>(sel.line))
+ fmt::format("#define GS_IIP {}\n", static_cast<u32>(sel.iip));
std::string src = GenGlslHeader("gs_main", GL_GEOMETRY_SHADER, macro);
src += m_shader_common_header;
@ -1061,53 +1061,53 @@ std::string GSDeviceOGL::GetPSSource(const PSSelector& sel)
Console.WriteLn("Compiling new pixel shader with selector 0x%" PRIX64 "%08X", sel.key_hi, sel.key_lo);
#endif
std::string macro = StringUtil::StdStringFromFormat("#define PS_FST %d\n", sel.fst)
+ StringUtil::StdStringFromFormat("#define PS_WMS %d\n", sel.wms)
+ StringUtil::StdStringFromFormat("#define PS_WMT %d\n", sel.wmt)
+ StringUtil::StdStringFromFormat("#define PS_AEM_FMT %d\n", sel.aem_fmt)
+ StringUtil::StdStringFromFormat("#define PS_PAL_FMT %d\n", sel.pal_fmt)
+ StringUtil::StdStringFromFormat("#define PS_DFMT %d\n", sel.dfmt)
+ StringUtil::StdStringFromFormat("#define PS_DEPTH_FMT %d\n", sel.depth_fmt)
+ StringUtil::StdStringFromFormat("#define PS_CHANNEL_FETCH %d\n", sel.channel)
+ StringUtil::StdStringFromFormat("#define PS_URBAN_CHAOS_HLE %d\n", sel.urban_chaos_hle)
+ StringUtil::StdStringFromFormat("#define PS_TALES_OF_ABYSS_HLE %d\n", sel.tales_of_abyss_hle)
+ StringUtil::StdStringFromFormat("#define PS_TEX_IS_FB %d\n", sel.tex_is_fb)
+ StringUtil::StdStringFromFormat("#define PS_INVALID_TEX0 %d\n", sel.invalid_tex0)
+ StringUtil::StdStringFromFormat("#define PS_AEM %d\n", sel.aem)
+ StringUtil::StdStringFromFormat("#define PS_TFX %d\n", sel.tfx)
+ StringUtil::StdStringFromFormat("#define PS_TCC %d\n", sel.tcc)
+ StringUtil::StdStringFromFormat("#define PS_ATST %d\n", sel.atst)
+ StringUtil::StdStringFromFormat("#define PS_FOG %d\n", sel.fog)
+ StringUtil::StdStringFromFormat("#define PS_CLR_HW %d\n", sel.clr_hw)
+ StringUtil::StdStringFromFormat("#define PS_FBA %d\n", sel.fba)
+ StringUtil::StdStringFromFormat("#define PS_LTF %d\n", sel.ltf)
+ StringUtil::StdStringFromFormat("#define PS_AUTOMATIC_LOD %d\n", sel.automatic_lod)
+ StringUtil::StdStringFromFormat("#define PS_MANUAL_LOD %d\n", sel.manual_lod)
+ StringUtil::StdStringFromFormat("#define PS_COLCLIP %d\n", sel.colclip)
+ StringUtil::StdStringFromFormat("#define PS_DATE %d\n", sel.date)
+ StringUtil::StdStringFromFormat("#define PS_TCOFFSETHACK %d\n", sel.tcoffsethack)
+ StringUtil::StdStringFromFormat("#define PS_POINT_SAMPLER %d\n", sel.point_sampler)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_A %d\n", sel.blend_a)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_B %d\n", sel.blend_b)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_C %d\n", sel.blend_c)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_D %d\n", sel.blend_d)
+ StringUtil::StdStringFromFormat("#define PS_IIP %d\n", sel.iip)
+ StringUtil::StdStringFromFormat("#define PS_SHUFFLE %d\n", sel.shuffle)
+ StringUtil::StdStringFromFormat("#define PS_READ_BA %d\n", sel.read_ba)
+ StringUtil::StdStringFromFormat("#define PS_WRITE_RG %d\n", sel.write_rg)
+ StringUtil::StdStringFromFormat("#define PS_FBMASK %d\n", sel.fbmask)
+ StringUtil::StdStringFromFormat("#define PS_HDR %d\n", sel.hdr)
+ StringUtil::StdStringFromFormat("#define PS_DITHER %d\n", sel.dither)
+ StringUtil::StdStringFromFormat("#define PS_ZCLAMP %d\n", sel.zclamp)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_MIX %d\n", sel.blend_mix)
+ StringUtil::StdStringFromFormat("#define PS_FIXED_ONE_A %d\n", sel.fixed_one_a)
+ StringUtil::StdStringFromFormat("#define PS_PABE %d\n", sel.pabe)
+ StringUtil::StdStringFromFormat("#define PS_SCANMSK %d\n", sel.scanmsk)
+ StringUtil::StdStringFromFormat("#define PS_SCALE_FACTOR %d\n", GSConfig.UpscaleMultiplier)
+ StringUtil::StdStringFromFormat("#define PS_NO_COLOR %d\n", sel.no_color)
+ StringUtil::StdStringFromFormat("#define PS_NO_COLOR1 %d\n", sel.no_color1)
+ StringUtil::StdStringFromFormat("#define PS_NO_ABLEND %d\n", sel.no_ablend)
+ StringUtil::StdStringFromFormat("#define PS_ONLY_ALPHA %d\n", sel.only_alpha)
std::string macro = fmt::format("#define PS_FST {}\n", sel.fst)
+ fmt::format("#define PS_WMS {}\n", sel.wms)
+ fmt::format("#define PS_WMT {}\n", sel.wmt)
+ fmt::format("#define PS_AEM_FMT {}\n", sel.aem_fmt)
+ fmt::format("#define PS_PAL_FMT {}\n", sel.pal_fmt)
+ fmt::format("#define PS_DFMT {}\n", sel.dfmt)
+ fmt::format("#define PS_DEPTH_FMT {}\n", sel.depth_fmt)
+ fmt::format("#define PS_CHANNEL_FETCH {}\n", sel.channel)
+ fmt::format("#define PS_URBAN_CHAOS_HLE {}\n", sel.urban_chaos_hle)
+ fmt::format("#define PS_TALES_OF_ABYSS_HLE {}\n", sel.tales_of_abyss_hle)
+ fmt::format("#define PS_TEX_IS_FB {}\n", sel.tex_is_fb)
+ fmt::format("#define PS_INVALID_TEX0 {}\n", sel.invalid_tex0)
+ fmt::format("#define PS_AEM {}\n", sel.aem)
+ fmt::format("#define PS_TFX {}\n", sel.tfx)
+ fmt::format("#define PS_TCC {}\n", sel.tcc)
+ fmt::format("#define PS_ATST {}\n", sel.atst)
+ fmt::format("#define PS_FOG {}\n", sel.fog)
+ fmt::format("#define PS_CLR_HW {}\n", sel.clr_hw)
+ fmt::format("#define PS_FBA {}\n", sel.fba)
+ fmt::format("#define PS_LTF {}\n", sel.ltf)
+ fmt::format("#define PS_AUTOMATIC_LOD {}\n", sel.automatic_lod)
+ fmt::format("#define PS_MANUAL_LOD {}\n", sel.manual_lod)
+ fmt::format("#define PS_COLCLIP {}\n", sel.colclip)
+ fmt::format("#define PS_DATE {}\n", sel.date)
+ fmt::format("#define PS_TCOFFSETHACK {}\n", sel.tcoffsethack)
+ fmt::format("#define PS_POINT_SAMPLER {}\n", sel.point_sampler)
+ fmt::format("#define PS_BLEND_A {}\n", sel.blend_a)
+ fmt::format("#define PS_BLEND_B {}\n", sel.blend_b)
+ fmt::format("#define PS_BLEND_C {}\n", sel.blend_c)
+ fmt::format("#define PS_BLEND_D {}\n", sel.blend_d)
+ fmt::format("#define PS_IIP {}\n", sel.iip)
+ fmt::format("#define PS_SHUFFLE {}\n", sel.shuffle)
+ fmt::format("#define PS_READ_BA {}\n", sel.read_ba)
+ fmt::format("#define PS_WRITE_RG {}\n", sel.write_rg)
+ fmt::format("#define PS_FBMASK {}\n", sel.fbmask)
+ fmt::format("#define PS_HDR {}\n", sel.hdr)
+ fmt::format("#define PS_DITHER {}\n", sel.dither)
+ fmt::format("#define PS_ZCLAMP {}\n", sel.zclamp)
+ fmt::format("#define PS_BLEND_MIX {}\n", sel.blend_mix)
+ fmt::format("#define PS_FIXED_ONE_A {}\n", sel.fixed_one_a)
+ fmt::format("#define PS_PABE {}\n", sel.pabe)
+ fmt::format("#define PS_SCANMSK {}\n", sel.scanmsk)
+ fmt::format("#define PS_SCALE_FACTOR {}\n", GSConfig.UpscaleMultiplier)
+ fmt::format("#define PS_NO_COLOR {}\n", sel.no_color)
+ fmt::format("#define PS_NO_COLOR1 {}\n", sel.no_color1)
+ fmt::format("#define PS_NO_ABLEND {}\n", sel.no_ablend)
+ fmt::format("#define PS_ONLY_ALPHA {}\n", sel.only_alpha)
;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro);
@ -1130,7 +1130,7 @@ bool GSDeviceOGL::DownloadTexture(GSTexture* src, const GSVector4i& rect, GSText
// Copy a sub part of texture (same as below but force a conversion)
void GSDeviceOGL::BlitRect(GSTexture* sTex, const GSVector4i& r, const GSVector2i& dsize, bool at_origin, bool linear)
{
GL_PUSH(StringUtil::StdStringFromFormat("CopyRectConv from %d", static_cast<GSTextureOGL*>(sTex)->GetID()).c_str());
GL_PUSH(fmt::format("CopyRectConv from {}", static_cast<GSTextureOGL*>(sTex)->GetID()).c_str());
g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// NOTE: This previously used glCopyTextureSubImage2D(), but this appears to leak memory in

View File

@ -1102,7 +1102,7 @@ VkShaderModule GSDeviceVK::GetUtilityVertexShader(const std::string& source, con
std::stringstream ss;
AddShaderHeader(ss);
AddShaderStageMacro(ss, true, false, false);
AddMacro(ss, "PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
AddMacro(ss, "PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier).c_str());
if (replace_main)
ss << "#define " << replace_main << " main\n";
ss << source;
@ -1115,7 +1115,7 @@ VkShaderModule GSDeviceVK::GetUtilityFragmentShader(const std::string& source, c
std::stringstream ss;
AddShaderHeader(ss);
AddShaderStageMacro(ss, false, false, true);
AddMacro(ss, "PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
AddMacro(ss, "PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier).c_str());
if (replace_main)
ss << "#define " << replace_main << " main\n";
ss << source;
@ -1894,7 +1894,7 @@ VkShaderModule GSDeviceVK::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
AddMacro(ss, "VS_IIP", sel.iip);
AddMacro(ss, "VS_POINT_SIZE", sel.point_size);
if (sel.point_size)
AddMacro(ss, "VS_POINT_SIZE_VALUE", GSConfig.UpscaleMultiplier);
AddMacro(ss, "VS_POINT_SIZE_VALUE", StringUtil::ToChars(GSConfig.UpscaleMultiplier).c_str());
ss << m_tfx_source;
VkShaderModule mod = g_vulkan_shader_cache->GetVertexShader(ss.str());
@ -1978,7 +1978,7 @@ VkShaderModule GSDeviceVK::GetTFXFragmentShader(const GSHWDrawConfig::PSSelector
AddMacro(ss, "PS_ZCLAMP", sel.zclamp);
AddMacro(ss, "PS_PABE", sel.pabe);
AddMacro(ss, "PS_SCANMSK", sel.scanmsk);
AddMacro(ss, "PS_SCALE_FACTOR", GSConfig.UpscaleMultiplier);
AddMacro(ss, "PS_SCALE_FACTOR", StringUtil::ToChars(GSConfig.UpscaleMultiplier).c_str());
AddMacro(ss, "PS_TEX_IS_FB", sel.tex_is_fb);
AddMacro(ss, "PS_NO_COLOR", sel.no_color);
AddMacro(ss, "PS_NO_COLOR1", sel.no_color1);

View File

@ -458,13 +458,13 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
return (static_cast<int>(config.UserHacks_TextureInsideRt) == value);
case GSHWFixId::AlignSprite:
return (config.UpscaleMultiplier == 1 || static_cast<int>(config.UserHacks_AlignSpriteX) == value);
return (config.UpscaleMultiplier <= 1.0f || static_cast<int>(config.UserHacks_AlignSpriteX) == value);
case GSHWFixId::MergeSprite:
return (config.UpscaleMultiplier == 1 || static_cast<int>(config.UserHacks_MergePPSprite) == value);
return (config.UpscaleMultiplier <= 1.0f || static_cast<int>(config.UserHacks_MergePPSprite) == value);
case GSHWFixId::WildArmsHack:
return (config.UpscaleMultiplier == 1 || static_cast<int>(config.UserHacks_WildHack) == value);
return (config.UpscaleMultiplier <= 1.0f || static_cast<int>(config.UserHacks_WildHack) == value);
case GSHWFixId::PointListPalette:
return (static_cast<int>(config.PointListPalette) == value);
@ -485,10 +485,10 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
return (config.UserHacks_HalfBottomOverride == value);
case GSHWFixId::HalfPixelOffset:
return (config.UpscaleMultiplier == 1 || config.UserHacks_HalfPixelOffset == value);
return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_HalfPixelOffset == value);
case GSHWFixId::RoundSprite:
return (config.UpscaleMultiplier == 1 || config.UserHacks_RoundSprite == value);
return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_RoundSprite == value);
case GSHWFixId::TexturePreloading:
return (static_cast<int>(config.TexturePreloading) <= value);

View File

@ -500,7 +500,8 @@ void Pcsx2Config::GSOptions::LoadSaveIniSettings(SettingsWrapper& wrap)
#define GSSettingIntEx(var, name) SettingsWrapBitfieldEx(var, name)
#define GSSettingBool(var) SettingsWrapBitBool(var)
#define GSSettingBoolEx(var, name) SettingsWrapBitBoolEx(var, name)
#define GSSettingFloat(var) SettingsWrapBitfield(var)
#define GSSettingFloat(var) SettingsWrapEntry(var)
#define GSSettingFloatEx(var, name) SettingsWrapEntryEx(var, name)
#define GSSettingIntEnumEx(var, name) SettingsWrapIntEnumEx(var, name)
#define GSSettingString(var) SettingsWrapEntry(var)
#define GSSettingStringEx(var, name) SettingsWrapEntryEx(var, name)
@ -515,6 +516,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
#define GSSettingBool(var) var = theApp.GetConfigB(#var)
#define GSSettingBoolEx(var, name) var = theApp.GetConfigB(name)
#define GSSettingFloat(var) var = static_cast<float>(theApp.GetConfigI(#var))
#define GSSettingFloatEx(var, name) var = static_cast<float>(theApp.GetConfigI(name))
#define GSSettingIntEnumEx(var, name) var = static_cast<decltype(var)>(theApp.GetConfigI(name))
#define GSSettingString(var) var = theApp.GetConfigS(#var)
#define GSSettingStringEx(var, name) var = theApp.GetConfigS(name)
@ -583,8 +585,10 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingFloat(OsdScale);
GSSettingIntEnumEx(Renderer, "Renderer");
GSSettingIntEx(UpscaleMultiplier, "upscale_multiplier");
UpscaleMultiplier = std::clamp(UpscaleMultiplier, 1u, 8u);
GSSettingFloatEx(UpscaleMultiplier, "upscale_multiplier");
// ~51x would the upper bound here for 32768x32768 textures, but you'll run out VRAM long before then.
UpscaleMultiplier = std::clamp(UpscaleMultiplier, 0.5f, 50.0f);
GSSettingIntEnumEx(HWMipmap, "mipmap_hw");
GSSettingIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
@ -661,7 +665,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
void Pcsx2Config::GSOptions::MaskUpscalingHacks()
{
if (UpscaleMultiplier != 1 && ManualUserHacks)
if (UpscaleMultiplier > 1.0f && ManualUserHacks)
return;
UserHacks_AlignSpriteX = false;

View File

@ -15,4 +15,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache.
static constexpr u32 SHADER_CACHE_VERSION = 10;
static constexpr u32 SHADER_CACHE_VERSION = 11;

View File

@ -407,7 +407,7 @@ void VMManager::RequestDisplaySize(float scale /*= 0.0f*/)
if (scale != 0.0f)
{
// unapply the upscaling, then apply the scale
scale = (1.0f / static_cast<float>(GSConfig.UpscaleMultiplier)) * scale;
scale = (1.0f / GSConfig.UpscaleMultiplier) * scale;
width *= scale;
height *= scale;
}
@ -1829,6 +1829,8 @@ void VMManager::WarnAboutUnsafeSettings()
messages += ICON_FA_TACHOMETER_ALT " Cycle rate/skip is not at default, this may crash or make games run too slow.\n";
if (EmuConfig.SPU2.SynchMode != Pcsx2Config::SPU2Options::SynchronizationMode::TimeStretch)
messages += ICON_FA_VOLUME_MUTE " Audio is not using time stretch synchronization, this may break FMVs.\n";
if (EmuConfig.GS.UpscaleMultiplier < 1.0f)
messages += ICON_FA_TV " Upscale multiplier is below native, this will break rendering.\n";
if (EmuConfig.GS.HWMipmap != HWMipmapLevel::Automatic)
messages += ICON_FA_IMAGES " Mipmapping is not set to automatic. This may break rendering in some games.\n";
if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2)