Skip to content

Commit

Permalink
GS/HW: Add new HPO - Align to Native With Texture Offset
Browse files Browse the repository at this point in the history
  • Loading branch information
refractionpcsx2 committed Feb 24, 2025
1 parent 02789eb commit da4a480
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 8 deletions.
7 changes: 6 additions & 1 deletion pcsx2-qt/Settings/GraphicsSettingsWidget.ui
Original file line number Diff line number Diff line change
Expand Up @@ -1055,7 +1055,12 @@
</item>
<item>
<property name="text">
<string>Align To Native</string>
<string>Align to Native</string>
</property>
</item>
<item>
<property name="text">
<string>Align to Native - with Texture Offset</string>
</property>
</item>
</widget>
Expand Down
1 change: 1 addition & 0 deletions pcsx2/Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ enum class GSHalfPixelOffset : u8
Special,
SpecialAggressive,
Native,
NativeWTexOffset,
MaxCount
};

Expand Down
86 changes: 81 additions & 5 deletions pcsx2/GS/Renderers/HW/GSRendererHW.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ void GSRendererHW::ConvertSpriteTextureShuffle(u32& process_rg, u32& process_ba,
GSVector4 GSRendererHW::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex)
{
if (GSConfig.UserHacks_HalfPixelOffset <= GSHalfPixelOffset::Normal ||
GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::Native ||
GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native ||
GetUpscaleMultiplier() == 1.0f || m_downscale_source || tex->GetScale() == 1.0f)
{
return GSVector4(0.0f);
Expand Down Expand Up @@ -5160,6 +5160,40 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,

const GSVector4 half_pixel = RealignTargetTextureCoordinate(tex);
m_conf.cb_vs.texture_offset = GSVector2(half_pixel.x, half_pixel.y);

if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
{
if (PRIM->FST)
{
if (tex->m_scale > 1.0f)
{
if (!m_downscale_source)
{
m_conf.cb_vs.texture_offset.x = 4.0f + (0.5f * (tex->m_scale / 2));
m_conf.cb_vs.texture_offset.y = 4.0f + (0.5f * (tex->m_scale / 2));
}
}
/*else
{
m_conf.cb_vs.texture_offset.x = 8.0f;
m_conf.cb_vs.texture_offset.y = 8.0f;
}*/
}
else
{
if (tex->m_scale > 1.0f && !m_downscale_source)
{
const GSVertex* v = &m_vertex.buff[0];
const float tw = static_cast<float>(1 << m_cached_ctx.TEX0.TW);
const float th = static_cast<float>(1 << m_cached_ctx.TEX0.TH);
const float q = v[0].RGBAQ.Q;

m_conf.cb_vs.texture_offset.x = 0.5f * q / tw;
m_conf.cb_vs.texture_offset.y = 0.5f * q / th;
}
}
}

}
else if (tex->m_target)
{
Expand Down Expand Up @@ -5211,6 +5245,39 @@ __ri void GSRendererHW::EmulateTextureSampler(const GSTextureCache::Target* rt,
const GSVector4 half_pixel = RealignTargetTextureCoordinate(tex);
m_conf.cb_vs.texture_offset = GSVector2(half_pixel.x, half_pixel.y);

if (GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
{
if (PRIM->FST)
{
if (tex->m_scale > 1.0f)
{
if (!m_downscale_source)
{
m_conf.cb_vs.texture_offset.x = 4.0f + (0.5f * (tex->m_scale / 2));
m_conf.cb_vs.texture_offset.y = 4.0f + (0.5f * (tex->m_scale / 2));
}
}
/*else
{
m_conf.cb_vs.texture_offset.x = 4.0f;
m_conf.cb_vs.texture_offset.y = 4.0f;
}*/
}
else
{
if (tex->m_scale > 1.0f && !m_downscale_source)
{
const GSVertex* v = &m_vertex.buff[0];
const float tw = static_cast<float>(1 << m_cached_ctx.TEX0.TW);
const float th = static_cast<float>(1 << m_cached_ctx.TEX0.TH);
const float q = v[0].RGBAQ.Q;

m_conf.cb_vs.texture_offset.x = 0.5f * q / tw;
m_conf.cb_vs.texture_offset.y = 0.5f * q / th;
}
}
}

if (m_vt.m_primclass == GS_SPRITE_CLASS && GSLocalMemory::m_psm[m_cached_ctx.TEX0.PSM].pal > 0 && m_index.tail >= 4)
{
HandleManualDeswizzle();
Expand Down Expand Up @@ -5540,7 +5607,7 @@ __ri void GSRendererHW::HandleTextureHazards(const GSTextureCache::Target* rt, c
// When using native HPO, the top-left column/row of pixels are often not drawn. Clamp these away to avoid sampling black,
// causing bleeding into the edges of the downsampled texture.
const u32 downsample_factor = static_cast<u32>(src_target->GetScale());
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native) ?
const GSVector2i clamp_min = (GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native) ?
GSVector2i(0, 0) :
GSVector2i(downsample_factor, downsample_factor);
GSVector4i copy_rect = tmm.coverage;
Expand Down Expand Up @@ -6272,7 +6339,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
float sx, sy, ox2, oy2;
const float ox = static_cast<float>(static_cast<int>(m_context->XYOFFSET.OFX));
const float oy = static_cast<float>(static_cast<int>(m_context->XYOFFSET.OFY));
if (GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native && rtscale > 1.0f)

if ((GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native || m_channel_shuffle) && rtscale > 1.0f)
{
sx = 2.0f * rtscale / (rtsize.x << 4);
sy = 2.0f * rtscale / (rtsize.y << 4);
Expand Down Expand Up @@ -6301,8 +6369,16 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
const int unscaled_y = rt_or_ds ? rt_or_ds->GetUnscaledHeight() : 0;
sx = 2.0f / (unscaled_x << 4);
sy = 2.0f / (unscaled_y << 4);
ox2 = -1.0f / unscaled_x;
oy2 = -1.0f / unscaled_y;
if ((!tex || !tex->m_from_target || tex->m_scale > 1.0f) && GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::NativeWTexOffset)
{
ox2 = (-1.0f / (unscaled_x * rtscale));
oy2 = (-1.0f / (unscaled_y * rtscale));
}
else
{
ox2 = -1.0f / unscaled_x;
oy2 = -1.0f / unscaled_y;
}
}

m_conf.cb_vs.vertex_scale = GSVector2(sx, sy);
Expand Down
4 changes: 2 additions & 2 deletions pcsx2/GS/Renderers/HW/GSTextureCache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2132,8 +2132,8 @@ GSTextureCache::Target* GSTextureCache::LookupTarget(GIFRegTEX0 TEX0, const GSVe
// And invalidate the target, we're drawing over it so we don't care what's there.
// We can't do this when upscaling, because of the vertex offset, the top/left rows often aren't drawn.
GL_INS("TC: Invalidating%s target %s[%x] because it's completely overwritten.", to_string(type),
(scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset == GSHalfPixelOffset::Native) ? "[clearing] " : "", dst->m_TEX0.TBP0);
if (scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset != GSHalfPixelOffset::Native)
(scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset >= GSHalfPixelOffset::Native) ? "[clearing] " : "", dst->m_TEX0.TBP0);
if (scale > 1.0f && GSConfig.UserHacks_HalfPixelOffset < GSHalfPixelOffset::Native)
{
if (dst->m_type == RenderTarget)
g_gs_device->ClearRenderTarget(dst->m_texture, 0);
Expand Down
1 change: 1 addition & 0 deletions pcsx2/ImGui/FullscreenUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3810,6 +3810,7 @@ void FullscreenUI::DrawGraphicsSettingsPage(SettingsInterface* bsi, bool show_ad
FSUI_NSTR("Special (Texture)"),
FSUI_NSTR("Special (Texture - Aggressive)"),
FSUI_NSTR("Align To Native"),
FSUI_NSTR("Align to Native - With Texture Offset"),
};
static constexpr const char* s_native_scaling_options[] = {
FSUI_NSTR("Normal (Default)"),
Expand Down

0 comments on commit da4a480

Please sign in to comment.