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 txN = tb.x | (int(input.p.x) & 7);
int txH = tb.x | ((int(input.p.x) + 4) & 7); int txH = tb.x | ((int(input.p.x) + 4) & 7);
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; txN *= PS_SCALE_FACTOR;
txH *= PS_SCALE_FACTOR; txH *= PS_SCALE_FACTOR;
ty *= PS_SCALE_FACTOR; ty *= PS_SCALE_FACTOR;
}
// TODO investigate texture gather // TODO investigate texture gather
float4 cN = Texture.Load(int3(txN, ty, 0)); float4 cN = Texture.Load(int3(txN, ty, 0));

View File

@ -43,7 +43,7 @@
#define PS_TALES_OF_ABYSS_HLE 0 #define PS_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0 #define PS_URBAN_CHAOS_HLE 0
#define PS_INVALID_TEX0 0 #define PS_INVALID_TEX0 0
#define PS_SCALE_FACTOR 1 #define PS_SCALE_FACTOR 1.0
#define PS_HDR 0 #define PS_HDR 0
#define PS_COLCLIP 0 #define PS_COLCLIP 0
#define PS_BLEND_A 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 txN = tb.x | (int(gl_FragCoord.x) & 7);
int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7); int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7);
txN *= PS_SCALE_FACTOR; if (floor(PS_SCALE_FACTOR) != PS_SCALE_FACTOR)
txH *= PS_SCALE_FACTOR; {
ty *= 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 // TODO investigate texture gather
vec4 cN = texelFetch(TextureSampler, ivec2(txN, ty), 0); vec4 cN = texelFetch(TextureSampler, ivec2(txN, ty), 0);

View File

@ -1,5 +1,5 @@
#ifndef PS_SCALE_FACTOR #ifndef PS_SCALE_FACTOR
#define PS_SCALE_FACTOR 1 #define PS_SCALE_FACTOR 1.0
#endif #endif
#ifdef VERTEX_SHADER #ifdef VERTEX_SHADER
@ -269,9 +269,18 @@ void ps_convert_rgba_8i()
int txN = tb.x | (int(gl_FragCoord.x) & 7); int txN = tb.x | (int(gl_FragCoord.x) & 7);
int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7); int txH = tb.x | ((int(gl_FragCoord.x) + 4) & 7);
txN *= PS_SCALE_FACTOR; if (floor(PS_SCALE_FACTOR) != PS_SCALE_FACTOR)
txH *= PS_SCALE_FACTOR; {
ty *= 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 // TODO investigate texture gather
vec4 cN = texelFetch(samp0, ivec2(txN, ty), 0); 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_TALES_OF_ABYSS_HLE 0
#define PS_URBAN_CHAOS_HLE 0 #define PS_URBAN_CHAOS_HLE 0
#define PS_INVALID_TEX0 0 #define PS_INVALID_TEX0 0
#define PS_SCALE_FACTOR 1 #define PS_SCALE_FACTOR 1.0
#define PS_HDR 0 #define PS_HDR 0
#define PS_COLCLIP 0 #define PS_COLCLIP 0
#define PS_BLEND_A 0 #define PS_BLEND_A 0

View File

@ -177,7 +177,41 @@ GraphicsSettingsWidget::GraphicsSettingsWidget(SettingsDialog* dialog, QWidget*
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// HW Settings // 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.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::BindWidgetToIntSetting(sif, m_ui.trilinearFiltering, "EmuCore/GS", "TriFilter", static_cast<int>(TriFiltering::Automatic), -1);
SettingWidgetBinder::BindWidgetToEnumSetting( SettingWidgetBinder::BindWidgetToEnumSetting(

View File

@ -388,48 +388,7 @@
</widget> </widget>
</item> </item>
<item row="0" column="1"> <item row="0" column="1">
<widget class="QComboBox" name="upscaleMultiplier"> <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>
</item> </item>
<item row="1" column="0"> <item row="1" column="0">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="label_6">

View File

@ -560,7 +560,7 @@ struct Pcsx2Config
float OsdScale{100.0}; float OsdScale{100.0};
GSRendererType Renderer{GSRendererType::Auto}; GSRendererType Renderer{GSRendererType::Auto};
uint UpscaleMultiplier{1}; float UpscaleMultiplier{1.0f};
HWMipmapLevel HWMipmap{HWMipmapLevel::Automatic}; HWMipmapLevel HWMipmap{HWMipmapLevel::Automatic};
AccBlendLevel AccurateBlendingUnit{AccBlendLevel::Basic}; 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)", 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)", "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)"}; "Blend (Bottom Field First, Half FPS)", "Automatic (Default)"};
static constexpr const char* s_resolution_options[] = { static const char* s_resolution_options[] = {
"Native (PS2)", "Native (PS2)",
"1.25x Native",
"1.5x Native",
"1.75x Native",
"2x Native (~720p)", "2x Native (~720p)",
"2.25x Native",
"2.5x Native",
"2.75x Native",
"3x Native (~1080p)", "3x Native (~1080p)",
"3.5x Native",
"4x Native (~1440p/2K)", "4x Native (~1440p/2K)",
"5x Native (~1620p)", "5x Native (~1620p)",
"6x Native (~2160p/4K)", "6x Native (~2160p/4K)",
"7x Native (~2520p)", "7x Native (~2520p)",
"8x Native (~2880p)", "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_mipmapping_options[] = {"Automatic (Default)", "Off", "Basic (Generated Mipmaps)", "Full (PS2 Mipmaps)"};
static constexpr const char* s_bilinear_options[] = { static constexpr const char* s_bilinear_options[] = {
"Nearest", "Bilinear (Forced)", "Bilinear (PS2)", "Bilinear (Forced excluding sprite)"}; "Nearest", "Bilinear (Forced)", "Bilinear (PS2)", "Bilinear (Forced excluding sprite)"};
@ -2612,8 +2636,8 @@ void FullscreenUI::DrawGraphicsSettingsPage()
MenuHeading("Rendering"); MenuHeading("Rendering");
if (is_hardware) if (is_hardware)
{ {
DrawIntListSetting(bsi, "Internal Resolution", "Multiplies the render resolution by the specified factor (upscaling).", DrawStringListSetting(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); "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", 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); 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", 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. // 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. // Let's keep it disabled to ease debug.
m_nativeres = GSConfig.UpscaleMultiplier == 1; m_nativeres = GSConfig.UpscaleMultiplier == 1.0f;
m_mipmap = GSConfig.Mipmap; m_mipmap = GSConfig.Mipmap;
s_n = 0; 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) 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 // 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); 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; offset = 1.0f;
} }
@ -363,7 +363,8 @@ bool GSRenderer::Merge(int field)
resolution.y = std::min(max_resolution.y, resolution.y); 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; ds = fs;
// When interlace(FRAME) mode, the rect is half height, so it needs to be stretched. // 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 #ifndef PCSX2_CORE
return GSVector4i(0, 0, src->GetWidth(), src->GetHeight()); return GSVector4i(0, 0, src->GetWidth(), src->GetHeight());
#else #else
const int upscale = GSConfig.UpscaleMultiplier; const float upscale = GSConfig.UpscaleMultiplier;
const GSVector2i size(src->GetSize()); const GSVector2i size(src->GetSize());
const int left = GSConfig.Crop[0] * upscale; const int left = static_cast<int>(static_cast<float>(GSConfig.Crop[0]) * upscale);
const int top = GSConfig.Crop[1] * upscale; const int top = static_cast<int>(static_cast<float>(GSConfig.Crop[1]) * upscale);
const int right = size.x - (GSConfig.Crop[2] * upscale); const int right = size.x - static_cast<int>(static_cast<float>(GSConfig.Crop[2]) * upscale);
const int bottom = size.y - (GSConfig.Crop[3] * upscale); const int bottom = size.y - static_cast<int>(static_cast<float>(GSConfig.Crop[3]) * upscale);
return GSVector4i(left, top, right, bottom); return GSVector4i(left, top, right, bottom);
#endif #endif
} }

View File

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

View File

@ -143,7 +143,7 @@ bool GSDevice11::Create()
} }
ShaderMacro sm_convert(m_shader_cache.GetFeatureLevel()); 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(); 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) 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) D3D_SHADER_MACRO* GSDevice11::ShaderMacro::GetPtr(void)

View File

@ -104,6 +104,7 @@ public:
public: public:
ShaderMacro(D3D_FEATURE_LEVEL fl); ShaderMacro(D3D_FEATURE_LEVEL fl);
void AddMacro(const char* n, int d); void AddMacro(const char* n, int d);
void AddMacro(const char* n, std::string d);
D3D_SHADER_MACRO* GetPtr(void); 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()); 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_FST", sel.fst);
sm.AddMacro("PS_WMS", sel.wms); sm.AddMacro("PS_WMS", sel.wms);
sm.AddMacro("PS_WMT", sel.wmt); 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) 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) 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) GSDevice12::ComPtr<ID3DBlob> GSDevice12::GetUtilityPixelShader(const std::string& source, const char* entry_point)
{ {
ShaderMacro sm_model(m_shader_cache.GetFeatureLevel()); 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); 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(); return it->second.get();
ShaderMacro sm(m_shader_cache.GetFeatureLevel()); 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_FST", sel.fst);
sm.AddMacro("PS_WMS", sel.wms); sm.AddMacro("PS_WMS", sel.wms);
sm.AddMacro("PS_WMT", sel.wmt); sm.AddMacro("PS_WMT", sel.wmt);

View File

@ -102,6 +102,7 @@ public:
public: public:
ShaderMacro(D3D_FEATURE_LEVEL fl); ShaderMacro(D3D_FEATURE_LEVEL fl);
void AddMacro(const char* n, int d); void AddMacro(const char* n, int d);
void AddMacro(const char* n, std::string d);
D3D_SHADER_MACRO* GetPtr(void); 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. // Include negative display offsets in the height here.
crtc_size.y = std::max(crtc_size.y, real_h); 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() void GSRendererHW::SetTCOffset()
@ -171,10 +172,10 @@ void GSRendererHW::SetGameCRC(u32 crc, int options)
bool GSRendererHW::CanUpscale() bool GSRendererHW::CanUpscale()
{ {
return GSConfig.UpscaleMultiplier != 1; return GSConfig.UpscaleMultiplier != 1.0f;
} }
int GSRendererHW::GetUpscaleMultiplier() float GSRendererHW::GetUpscaleMultiplier()
{ {
return GSConfig.UpscaleMultiplier; return GSConfig.UpscaleMultiplier;
} }
@ -661,7 +662,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(bool& write_ba, bool& read_ba)
GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex) 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); return GSVector4(0.0f);
const GSVertex* v = &m_vertex.buff[0]; const GSVertex* v = &m_vertex.buff[0];
@ -798,7 +799,7 @@ void GSRendererHW::MergeSprite(GSTextureCache::Source* tex)
GSVector2 GSRendererHW::GetTextureScaleFactor() GSVector2 GSRendererHW::GetTextureScaleFactor()
{ {
const float f_upscale = static_cast<float>(GetUpscaleMultiplier()); const float f_upscale = GetUpscaleMultiplier();
return GSVector2(f_upscale, f_upscale); 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); 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) 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++) for (unsigned int i = 0; i < m_vertex.next; i++)
m_vertex.buff[i].UV &= 0x3FEF3FEF; 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(); const GSDevice::FeatureSupport features = g_gs_device->Features();
ASSERT(VerifyIndices()); ASSERT(VerifyIndices());

View File

@ -177,7 +177,7 @@ public:
void SetGameCRC(u32 crc, int options) override; void SetGameCRC(u32 crc, int options) override;
bool CanUpscale() override; bool CanUpscale() override;
int GetUpscaleMultiplier() override; float GetUpscaleMultiplier() override;
void Lines2Sprites(); void Lines2Sprites();
bool VerifyIndices(); bool VerifyIndices();
template <GSHWDrawConfig::VSExpand Expand> void ExpandIndices(); 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) if (GSConfig.UserHacks_HalfPixelOffset == 1 && hack)
{ {
modxy = static_cast<float>(g_gs_renderer->GetUpscaleMultiplier()); modxy = g_gs_renderer->GetUpscaleMultiplier();
switch (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 2: case 4: case 6: case 8: modxy += 0.2f; break;
case 3: case 7: modxy += 0.1f; 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_draw_sync_fence = MRCTransfer([m_dev.dev newFence]);
m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]); m_fn_constants = MRCTransfer([MTLFunctionConstantValues new]);
u8 upscale = std::max(1, theApp.GetConfigI("upscale_multiplier")); vector_float2 upscale2 = vector2(GSConfig.UpscaleMultiplier, GSConfig.UpscaleMultiplier);
vector_uchar2 upscale2 = vector2(upscale, upscale); [m_fn_constants setConstantValue:&upscale2 type:MTLDataTypeFloat2 atIndex:GSMTLConstantIndex_SCALING_FACTOR];
[m_fn_constants setConstantValue:&upscale2 type:MTLDataTypeUChar2 atIndex:GSMTLConstantIndex_SCALING_FACTOR];
setFnConstantB(m_fn_constants, m_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH); setFnConstantB(m_fn_constants, m_dev.features.framebuffer_fetch, GSMTLConstantIndex_FRAMEBUFFER_FETCH);
m_hw_vertex = MRCTransfer([MTLVertexDescriptor new]); m_hw_vertex = MRCTransfer([MTLVertexDescriptor new]);

View File

@ -19,7 +19,7 @@
using namespace metal; using namespace metal;
constant uchar2 SCALING_FACTOR [[function_constant(GSMTLConstantIndex_SCALING_FACTOR)]]; constant float2 SCALING_FACTOR [[function_constant(GSMTLConstantIndex_SCALING_FACTOR)]];
struct ConvertShaderData 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 txN = tb.x | (uint(data.p.x) & 7);
uint txH = tb.x | ((uint(data.p.x) + 4) & 7); uint txH = tb.x | ((uint(data.p.x) + 4) & 7);
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; txN *= SCALING_FACTOR.x;
txH *= SCALING_FACTOR.x; txH *= SCALING_FACTOR.x;
ty *= SCALING_FACTOR.y; ty *= SCALING_FACTOR.y;
}
// TODO investigate texture gather // TODO investigate texture gather
float4 cN = res.texture.read(uint2(txN, ty)); float4 cN = res.texture.read(uint2(txN, ty));

View File

@ -502,7 +502,7 @@ struct PSMain
float4 sample_depth(float2 st) 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); ushort2 uv = ushort2(uv_f);
float4 t = float4(0); float4 t = float4(0);
@ -778,7 +778,7 @@ struct PSMain
if (PS_DITHER == 2) if (PS_DITHER == 2)
fpos = ushort2(in.p.xy); fpos = ushort2(in.p.xy);
else 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]; C.rgb += cb.dither_matrix[fpos.y & 3][fpos.x & 3];
} }

View File

@ -237,7 +237,7 @@ bool GSDeviceOGL::Create()
GLint point_range[2] = {}; GLint point_range[2] = {};
glGetIntegerv(GL_ALIASED_POINT_SIZE_RANGE, point_range); 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"); 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 char* name = shaderName(static_cast<ShaderConvert>(i));
const std::string macro_sel = (static_cast<ShaderConvert>(i) == ShaderConvert::RGBA_TO_8I) ? 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(); std::string();
const std::string ps(GetShaderSource(name, GL_FRAGMENT_SHADER, m_shader_common_header, *convert_glsl, macro_sel)); 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)) 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++) 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)) if (!m_shader_cache.GetProgram(&m_merge_obj.ps[i], m_convert.vs, {}, ps))
return false; return false;
m_merge_obj.ps[i].SetFormattedName("Merge pipe %zu", i); 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++) 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)) if (!m_shader_cache.GetProgram(&m_interlace.ps[i], m_convert.vs, {}, ps))
return false; return false;
m_interlace.ps[i].SetFormattedName("Merge pipe %zu", i); 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++) for (size_t i = 0; i < std::size(m_date.primid_ps); i++)
{ {
const std::string ps(GetShaderSource( 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, {})); GL_FRAGMENT_SHADER, m_shader_common_header, *convert_glsl, {}));
m_shader_cache.GetProgram(&m_date.primid_ps[i], m_convert.vs, {}, ps); 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); 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); Console.WriteLn("Compiling new vertex shader with selector 0x%" PRIX64, sel.key);
#endif #endif
std::string macro = StringUtil::StdStringFromFormat("#define VS_INT_FST %d\n", sel.int_fst) std::string macro = fmt::format("#define VS_INT_FST {}\n", static_cast<u32>(sel.int_fst))
+ StringUtil::StdStringFromFormat("#define VS_IIP %d\n", sel.iip) + fmt::format("#define VS_IIP {}\n", static_cast<u32>(sel.iip))
+ StringUtil::StdStringFromFormat("#define VS_POINT_SIZE %d\n", sel.point_size); + fmt::format("#define VS_POINT_SIZE {}\n", static_cast<u32>(sel.point_size));
if (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); std::string src = GenGlslHeader("vs_main", GL_VERTEX_SHADER, macro);
src += m_shader_common_header; 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); Console.WriteLn("Compiling new geometry shader with selector 0x%" PRIX64, sel.key);
#endif #endif
std::string macro = StringUtil::StdStringFromFormat("#define GS_POINT %d\n", sel.point) std::string macro = fmt::format("#define GS_POINT {}\n", static_cast<u32>(sel.point))
+ StringUtil::StdStringFromFormat("#define GS_LINE %d\n", sel.line) + fmt::format("#define GS_LINE {}\n", static_cast<u32>(sel.line))
+ StringUtil::StdStringFromFormat("#define GS_IIP %d\n", sel.iip); + fmt::format("#define GS_IIP {}\n", static_cast<u32>(sel.iip));
std::string src = GenGlslHeader("gs_main", GL_GEOMETRY_SHADER, macro); std::string src = GenGlslHeader("gs_main", GL_GEOMETRY_SHADER, macro);
src += m_shader_common_header; 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); Console.WriteLn("Compiling new pixel shader with selector 0x%" PRIX64 "%08X", sel.key_hi, sel.key_lo);
#endif #endif
std::string macro = StringUtil::StdStringFromFormat("#define PS_FST %d\n", sel.fst) std::string macro = fmt::format("#define PS_FST {}\n", sel.fst)
+ StringUtil::StdStringFromFormat("#define PS_WMS %d\n", sel.wms) + fmt::format("#define PS_WMS {}\n", sel.wms)
+ StringUtil::StdStringFromFormat("#define PS_WMT %d\n", sel.wmt) + fmt::format("#define PS_WMT {}\n", sel.wmt)
+ StringUtil::StdStringFromFormat("#define PS_AEM_FMT %d\n", sel.aem_fmt) + fmt::format("#define PS_AEM_FMT {}\n", sel.aem_fmt)
+ StringUtil::StdStringFromFormat("#define PS_PAL_FMT %d\n", sel.pal_fmt) + fmt::format("#define PS_PAL_FMT {}\n", sel.pal_fmt)
+ StringUtil::StdStringFromFormat("#define PS_DFMT %d\n", sel.dfmt) + fmt::format("#define PS_DFMT {}\n", sel.dfmt)
+ StringUtil::StdStringFromFormat("#define PS_DEPTH_FMT %d\n", sel.depth_fmt) + fmt::format("#define PS_DEPTH_FMT {}\n", sel.depth_fmt)
+ StringUtil::StdStringFromFormat("#define PS_CHANNEL_FETCH %d\n", sel.channel) + fmt::format("#define PS_CHANNEL_FETCH {}\n", sel.channel)
+ StringUtil::StdStringFromFormat("#define PS_URBAN_CHAOS_HLE %d\n", sel.urban_chaos_hle) + fmt::format("#define PS_URBAN_CHAOS_HLE {}\n", sel.urban_chaos_hle)
+ StringUtil::StdStringFromFormat("#define PS_TALES_OF_ABYSS_HLE %d\n", sel.tales_of_abyss_hle) + fmt::format("#define PS_TALES_OF_ABYSS_HLE {}\n", sel.tales_of_abyss_hle)
+ StringUtil::StdStringFromFormat("#define PS_TEX_IS_FB %d\n", sel.tex_is_fb) + fmt::format("#define PS_TEX_IS_FB {}\n", sel.tex_is_fb)
+ StringUtil::StdStringFromFormat("#define PS_INVALID_TEX0 %d\n", sel.invalid_tex0) + fmt::format("#define PS_INVALID_TEX0 {}\n", sel.invalid_tex0)
+ StringUtil::StdStringFromFormat("#define PS_AEM %d\n", sel.aem) + fmt::format("#define PS_AEM {}\n", sel.aem)
+ StringUtil::StdStringFromFormat("#define PS_TFX %d\n", sel.tfx) + fmt::format("#define PS_TFX {}\n", sel.tfx)
+ StringUtil::StdStringFromFormat("#define PS_TCC %d\n", sel.tcc) + fmt::format("#define PS_TCC {}\n", sel.tcc)
+ StringUtil::StdStringFromFormat("#define PS_ATST %d\n", sel.atst) + fmt::format("#define PS_ATST {}\n", sel.atst)
+ StringUtil::StdStringFromFormat("#define PS_FOG %d\n", sel.fog) + fmt::format("#define PS_FOG {}\n", sel.fog)
+ StringUtil::StdStringFromFormat("#define PS_CLR_HW %d\n", sel.clr_hw) + fmt::format("#define PS_CLR_HW {}\n", sel.clr_hw)
+ StringUtil::StdStringFromFormat("#define PS_FBA %d\n", sel.fba) + fmt::format("#define PS_FBA {}\n", sel.fba)
+ StringUtil::StdStringFromFormat("#define PS_LTF %d\n", sel.ltf) + fmt::format("#define PS_LTF {}\n", sel.ltf)
+ StringUtil::StdStringFromFormat("#define PS_AUTOMATIC_LOD %d\n", sel.automatic_lod) + fmt::format("#define PS_AUTOMATIC_LOD {}\n", sel.automatic_lod)
+ StringUtil::StdStringFromFormat("#define PS_MANUAL_LOD %d\n", sel.manual_lod) + fmt::format("#define PS_MANUAL_LOD {}\n", sel.manual_lod)
+ StringUtil::StdStringFromFormat("#define PS_COLCLIP %d\n", sel.colclip) + fmt::format("#define PS_COLCLIP {}\n", sel.colclip)
+ StringUtil::StdStringFromFormat("#define PS_DATE %d\n", sel.date) + fmt::format("#define PS_DATE {}\n", sel.date)
+ StringUtil::StdStringFromFormat("#define PS_TCOFFSETHACK %d\n", sel.tcoffsethack) + fmt::format("#define PS_TCOFFSETHACK {}\n", sel.tcoffsethack)
+ StringUtil::StdStringFromFormat("#define PS_POINT_SAMPLER %d\n", sel.point_sampler) + fmt::format("#define PS_POINT_SAMPLER {}\n", sel.point_sampler)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_A %d\n", sel.blend_a) + fmt::format("#define PS_BLEND_A {}\n", sel.blend_a)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_B %d\n", sel.blend_b) + fmt::format("#define PS_BLEND_B {}\n", sel.blend_b)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_C %d\n", sel.blend_c) + fmt::format("#define PS_BLEND_C {}\n", sel.blend_c)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_D %d\n", sel.blend_d) + fmt::format("#define PS_BLEND_D {}\n", sel.blend_d)
+ StringUtil::StdStringFromFormat("#define PS_IIP %d\n", sel.iip) + fmt::format("#define PS_IIP {}\n", sel.iip)
+ StringUtil::StdStringFromFormat("#define PS_SHUFFLE %d\n", sel.shuffle) + fmt::format("#define PS_SHUFFLE {}\n", sel.shuffle)
+ StringUtil::StdStringFromFormat("#define PS_READ_BA %d\n", sel.read_ba) + fmt::format("#define PS_READ_BA {}\n", sel.read_ba)
+ StringUtil::StdStringFromFormat("#define PS_WRITE_RG %d\n", sel.write_rg) + fmt::format("#define PS_WRITE_RG {}\n", sel.write_rg)
+ StringUtil::StdStringFromFormat("#define PS_FBMASK %d\n", sel.fbmask) + fmt::format("#define PS_FBMASK {}\n", sel.fbmask)
+ StringUtil::StdStringFromFormat("#define PS_HDR %d\n", sel.hdr) + fmt::format("#define PS_HDR {}\n", sel.hdr)
+ StringUtil::StdStringFromFormat("#define PS_DITHER %d\n", sel.dither) + fmt::format("#define PS_DITHER {}\n", sel.dither)
+ StringUtil::StdStringFromFormat("#define PS_ZCLAMP %d\n", sel.zclamp) + fmt::format("#define PS_ZCLAMP {}\n", sel.zclamp)
+ StringUtil::StdStringFromFormat("#define PS_BLEND_MIX %d\n", sel.blend_mix) + fmt::format("#define PS_BLEND_MIX {}\n", sel.blend_mix)
+ StringUtil::StdStringFromFormat("#define PS_FIXED_ONE_A %d\n", sel.fixed_one_a) + fmt::format("#define PS_FIXED_ONE_A {}\n", sel.fixed_one_a)
+ StringUtil::StdStringFromFormat("#define PS_PABE %d\n", sel.pabe) + fmt::format("#define PS_PABE {}\n", sel.pabe)
+ StringUtil::StdStringFromFormat("#define PS_SCANMSK %d\n", sel.scanmsk) + fmt::format("#define PS_SCANMSK {}\n", sel.scanmsk)
+ StringUtil::StdStringFromFormat("#define PS_SCALE_FACTOR %d\n", GSConfig.UpscaleMultiplier) + fmt::format("#define PS_SCALE_FACTOR {}\n", GSConfig.UpscaleMultiplier)
+ StringUtil::StdStringFromFormat("#define PS_NO_COLOR %d\n", sel.no_color) + fmt::format("#define PS_NO_COLOR {}\n", sel.no_color)
+ StringUtil::StdStringFromFormat("#define PS_NO_COLOR1 %d\n", sel.no_color1) + fmt::format("#define PS_NO_COLOR1 {}\n", sel.no_color1)
+ StringUtil::StdStringFromFormat("#define PS_NO_ABLEND %d\n", sel.no_ablend) + fmt::format("#define PS_NO_ABLEND {}\n", sel.no_ablend)
+ StringUtil::StdStringFromFormat("#define PS_ONLY_ALPHA %d\n", sel.only_alpha) + fmt::format("#define PS_ONLY_ALPHA {}\n", sel.only_alpha)
; ;
std::string src = GenGlslHeader("ps_main", GL_FRAGMENT_SHADER, macro); 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) // 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) 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); g_perfmon.Put(GSPerfMon::TextureCopies, 1);
// NOTE: This previously used glCopyTextureSubImage2D(), but this appears to leak memory in // 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; std::stringstream ss;
AddShaderHeader(ss); AddShaderHeader(ss);
AddShaderStageMacro(ss, true, false, false); 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) if (replace_main)
ss << "#define " << replace_main << " main\n"; ss << "#define " << replace_main << " main\n";
ss << source; ss << source;
@ -1115,7 +1115,7 @@ VkShaderModule GSDeviceVK::GetUtilityFragmentShader(const std::string& source, c
std::stringstream ss; std::stringstream ss;
AddShaderHeader(ss); AddShaderHeader(ss);
AddShaderStageMacro(ss, false, false, true); 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) if (replace_main)
ss << "#define " << replace_main << " main\n"; ss << "#define " << replace_main << " main\n";
ss << source; ss << source;
@ -1894,7 +1894,7 @@ VkShaderModule GSDeviceVK::GetTFXVertexShader(GSHWDrawConfig::VSSelector sel)
AddMacro(ss, "VS_IIP", sel.iip); AddMacro(ss, "VS_IIP", sel.iip);
AddMacro(ss, "VS_POINT_SIZE", sel.point_size); AddMacro(ss, "VS_POINT_SIZE", sel.point_size);
if (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; ss << m_tfx_source;
VkShaderModule mod = g_vulkan_shader_cache->GetVertexShader(ss.str()); 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_ZCLAMP", sel.zclamp);
AddMacro(ss, "PS_PABE", sel.pabe); AddMacro(ss, "PS_PABE", sel.pabe);
AddMacro(ss, "PS_SCANMSK", sel.scanmsk); 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_TEX_IS_FB", sel.tex_is_fb);
AddMacro(ss, "PS_NO_COLOR", sel.no_color); AddMacro(ss, "PS_NO_COLOR", sel.no_color);
AddMacro(ss, "PS_NO_COLOR1", sel.no_color1); 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); return (static_cast<int>(config.UserHacks_TextureInsideRt) == value);
case GSHWFixId::AlignSprite: 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: 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: 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: case GSHWFixId::PointListPalette:
return (static_cast<int>(config.PointListPalette) == value); return (static_cast<int>(config.PointListPalette) == value);
@ -485,10 +485,10 @@ bool GameDatabaseSchema::GameEntry::configMatchesHWFix(const Pcsx2Config::GSOpti
return (config.UserHacks_HalfBottomOverride == value); return (config.UserHacks_HalfBottomOverride == value);
case GSHWFixId::HalfPixelOffset: case GSHWFixId::HalfPixelOffset:
return (config.UpscaleMultiplier == 1 || config.UserHacks_HalfPixelOffset == value); return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_HalfPixelOffset == value);
case GSHWFixId::RoundSprite: case GSHWFixId::RoundSprite:
return (config.UpscaleMultiplier == 1 || config.UserHacks_RoundSprite == value); return (config.UpscaleMultiplier <= 1.0f || config.UserHacks_RoundSprite == value);
case GSHWFixId::TexturePreloading: case GSHWFixId::TexturePreloading:
return (static_cast<int>(config.TexturePreloading) <= value); 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 GSSettingIntEx(var, name) SettingsWrapBitfieldEx(var, name)
#define GSSettingBool(var) SettingsWrapBitBool(var) #define GSSettingBool(var) SettingsWrapBitBool(var)
#define GSSettingBoolEx(var, name) SettingsWrapBitBoolEx(var, name) #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 GSSettingIntEnumEx(var, name) SettingsWrapIntEnumEx(var, name)
#define GSSettingString(var) SettingsWrapEntry(var) #define GSSettingString(var) SettingsWrapEntry(var)
#define GSSettingStringEx(var, name) SettingsWrapEntryEx(var, name) #define GSSettingStringEx(var, name) SettingsWrapEntryEx(var, name)
@ -515,6 +516,7 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
#define GSSettingBool(var) var = theApp.GetConfigB(#var) #define GSSettingBool(var) var = theApp.GetConfigB(#var)
#define GSSettingBoolEx(var, name) var = theApp.GetConfigB(name) #define GSSettingBoolEx(var, name) var = theApp.GetConfigB(name)
#define GSSettingFloat(var) var = static_cast<float>(theApp.GetConfigI(#var)) #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 GSSettingIntEnumEx(var, name) var = static_cast<decltype(var)>(theApp.GetConfigI(name))
#define GSSettingString(var) var = theApp.GetConfigS(#var) #define GSSettingString(var) var = theApp.GetConfigS(#var)
#define GSSettingStringEx(var, name) var = theApp.GetConfigS(name) #define GSSettingStringEx(var, name) var = theApp.GetConfigS(name)
@ -583,8 +585,10 @@ void Pcsx2Config::GSOptions::ReloadIniSettings()
GSSettingFloat(OsdScale); GSSettingFloat(OsdScale);
GSSettingIntEnumEx(Renderer, "Renderer"); GSSettingIntEnumEx(Renderer, "Renderer");
GSSettingIntEx(UpscaleMultiplier, "upscale_multiplier"); GSSettingFloatEx(UpscaleMultiplier, "upscale_multiplier");
UpscaleMultiplier = std::clamp(UpscaleMultiplier, 1u, 8u);
// ~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(HWMipmap, "mipmap_hw");
GSSettingIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit"); GSSettingIntEnumEx(AccurateBlendingUnit, "accurate_blending_unit");
@ -661,7 +665,7 @@ void Pcsx2Config::GSOptions::MaskUserHacks()
void Pcsx2Config::GSOptions::MaskUpscalingHacks() void Pcsx2Config::GSOptions::MaskUpscalingHacks()
{ {
if (UpscaleMultiplier != 1 && ManualUserHacks) if (UpscaleMultiplier > 1.0f && ManualUserHacks)
return; return;
UserHacks_AlignSpriteX = false; UserHacks_AlignSpriteX = false;

View File

@ -15,4 +15,4 @@
/// Version number for GS and other shaders. Increment whenever any of the contents of the /// Version number for GS and other shaders. Increment whenever any of the contents of the
/// shaders change, to invalidate the cache. /// 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) if (scale != 0.0f)
{ {
// unapply the upscaling, then apply the scale // unapply the upscaling, then apply the scale
scale = (1.0f / static_cast<float>(GSConfig.UpscaleMultiplier)) * scale; scale = (1.0f / GSConfig.UpscaleMultiplier) * scale;
width *= scale; width *= scale;
height *= 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"; 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) 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"; 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) if (EmuConfig.GS.HWMipmap != HWMipmapLevel::Automatic)
messages += ICON_FA_IMAGES " Mipmapping is not set to automatic. This may break rendering in some games.\n"; messages += ICON_FA_IMAGES " Mipmapping is not set to automatic. This may break rendering in some games.\n";
if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2) if (EmuConfig.GS.TextureFiltering != BiFiltering::PS2)