mirror of https://github.com/PCSX2/pcsx2.git
gsdx: anti-upscale-glitch hack UserHacks_SkipDraw
2x upscaling is pixel perfects. Bigger upscaling is better but not yet perfect Feedbacks are welcomes (note it doesn't solve all upscaling issue, only wrong texture sampling) For the history: If you have a texture of [0;16[ texels and draws a primitive [0;16[ The formulae to sample last pixels of texture is 0.5 + (16*s-1)/(16*s) * 16 Native (s==1): 15.5 (good) 2x (s==2): 16 (bad, outside of the texture) 4x (s==4): 16.25 (bad, really outside of the texure))
This commit is contained in:
parent
22710d9b44
commit
e1a5736583
|
@ -287,6 +287,8 @@ void GSRendererDX::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sourc
|
||||||
const GSLocalMemory::psm_t &cpsm = psm.pal > 0 ? GSLocalMemory::m_psm[context->TEX0.CPSM] : psm;
|
const GSLocalMemory::psm_t &cpsm = psm.pal > 0 ? GSLocalMemory::m_psm[context->TEX0.CPSM] : psm;
|
||||||
bool bilinear = m_filter == 2 ? m_vt.IsLinear() : m_filter != 0;
|
bool bilinear = m_filter == 2 ? m_vt.IsLinear() : m_filter != 0;
|
||||||
bool simple_sample = !tex->m_palette && cpsm.fmt == 0 && context->CLAMP.WMS < 3 && context->CLAMP.WMT < 3;
|
bool simple_sample = !tex->m_palette && cpsm.fmt == 0 && context->CLAMP.WMS < 3 && context->CLAMP.WMT < 3;
|
||||||
|
// Don't do extra filtering on sprite (it creates various upscaling issue)
|
||||||
|
bilinear &= !((m_vt.m_primclass == GS_SPRITE_CLASS) && (m_userhacks_stretch_sprite));
|
||||||
|
|
||||||
ps_sel.wms = context->CLAMP.WMS;
|
ps_sel.wms = context->CLAMP.WMS;
|
||||||
ps_sel.wmt = context->CLAMP.WMT;
|
ps_sel.wmt = context->CLAMP.WMT;
|
||||||
|
|
|
@ -32,6 +32,7 @@ GSRendererHW::GSRendererHW(GSTextureCache* tc)
|
||||||
{
|
{
|
||||||
m_upscale_multiplier = theApp.GetConfig("upscale_multiplier", 1);
|
m_upscale_multiplier = theApp.GetConfig("upscale_multiplier", 1);
|
||||||
m_userhacks_skipdraw = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_SkipDraw", 0) : 0;
|
m_userhacks_skipdraw = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_SkipDraw", 0) : 0;
|
||||||
|
m_userhacks_stretch_sprite = !!theApp.GetConfig("UserHacks_stretch_sprite", 0);
|
||||||
|
|
||||||
if(!m_nativeres)
|
if(!m_nativeres)
|
||||||
{
|
{
|
||||||
|
@ -54,6 +55,31 @@ GSRendererHW::GSRendererHW(GSTextureCache* tc)
|
||||||
{
|
{
|
||||||
m_upscale_multiplier = 1;
|
m_upscale_multiplier = 1;
|
||||||
}
|
}
|
||||||
|
// When you upscale sprite will sample invalid data of the texture. The idea is to add a subtexel offset
|
||||||
|
// so the sampling remains inside the texture.
|
||||||
|
// The strict minimal value can be computed with this formulae (you need to round above, [1;2[ must be rounded to 2).
|
||||||
|
//
|
||||||
|
// s: m_upscale_multiplier
|
||||||
|
// WH: Width or Height of the texture
|
||||||
|
// 0.5: initial offset of texture (not sure it is always true)
|
||||||
|
// L: length of primitive in pixels (after upscaling)
|
||||||
|
//
|
||||||
|
// Full formulae is
|
||||||
|
// 0.5 + (L - 1)* (WH-offset)/L < WH
|
||||||
|
//
|
||||||
|
// A reduced formulae is: (hypothesis 1:1 mapping => L == s*WH)
|
||||||
|
// offset > ((0.5)*s -1)/(s-1/WH)*16
|
||||||
|
//
|
||||||
|
// Rendering is perfect for 2x but some issues remains on higher scaling
|
||||||
|
switch (m_upscale_multiplier) {
|
||||||
|
case 1: m_sub_texel_offset = 0; break;
|
||||||
|
case 2: m_sub_texel_offset = 1; break;
|
||||||
|
case 3: m_sub_texel_offset = 3; break; // texture of 2 texels need 4 (Is is used?)
|
||||||
|
case 4: m_sub_texel_offset = 5; break;
|
||||||
|
case 5: m_sub_texel_offset = 6; break;
|
||||||
|
case 6: m_sub_texel_offset = 6; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GSRendererHW::~GSRendererHW()
|
GSRendererHW::~GSRendererHW()
|
||||||
|
@ -295,6 +321,23 @@ void GSRendererHW::Draw()
|
||||||
context->FRAME.FBMSK = fm;
|
context->FRAME.FBMSK = fm;
|
||||||
context->ZBUF.ZMSK = zm != 0;
|
context->ZBUF.ZMSK = zm != 0;
|
||||||
|
|
||||||
|
// Hack to avoid black line in various games.
|
||||||
|
if (m_userhacks_stretch_sprite && (m_vt.m_primclass == GS_SPRITE_CLASS)) {
|
||||||
|
size_t count = m_vertex.next;
|
||||||
|
GSVertex* v = &m_vertex.buff[0];
|
||||||
|
for(size_t i = 0; i < count; i += 2) {
|
||||||
|
if (v[i+1].U < v[i].U)
|
||||||
|
v[i+1].U += m_sub_texel_offset;
|
||||||
|
else
|
||||||
|
v[i+1].U -= m_sub_texel_offset;
|
||||||
|
if (v[i+1].V < v[i].V)
|
||||||
|
v[i+1].V += m_sub_texel_offset;
|
||||||
|
else
|
||||||
|
v[i+1].V -= m_sub_texel_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
DrawPrims(rt->m_texture, ds->m_texture, tex);
|
DrawPrims(rt->m_texture, ds->m_texture, tex);
|
||||||
|
|
|
@ -35,7 +35,8 @@ private:
|
||||||
bool m_reset;
|
bool m_reset;
|
||||||
int m_upscale_multiplier;
|
int m_upscale_multiplier;
|
||||||
int m_userhacks_skipdraw;
|
int m_userhacks_skipdraw;
|
||||||
|
int m_sub_texel_offset;
|
||||||
|
|
||||||
#pragma region hacks
|
#pragma region hacks
|
||||||
|
|
||||||
typedef bool (GSRendererHW::*OI_Ptr)(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
typedef bool (GSRendererHW::*OI_Ptr)(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t);
|
||||||
|
@ -134,6 +135,8 @@ protected:
|
||||||
|
|
||||||
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
|
virtual void DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Source* tex) = 0;
|
||||||
|
|
||||||
|
bool m_userhacks_stretch_sprite;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GSRendererHW(GSTextureCache* tc);
|
GSRendererHW(GSTextureCache* tc);
|
||||||
virtual ~GSRendererHW();
|
virtual ~GSRendererHW();
|
||||||
|
|
|
@ -414,6 +414,8 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
const GSLocalMemory::psm_t &cpsm = psm.pal > 0 ? GSLocalMemory::m_psm[context->TEX0.CPSM] : psm;
|
const GSLocalMemory::psm_t &cpsm = psm.pal > 0 ? GSLocalMemory::m_psm[context->TEX0.CPSM] : psm;
|
||||||
bool bilinear = m_filter == 2 ? m_vt.IsLinear() : m_filter != 0;
|
bool bilinear = m_filter == 2 ? m_vt.IsLinear() : m_filter != 0;
|
||||||
bool simple_sample = !tex->m_palette && cpsm.fmt == 0 && context->CLAMP.WMS < 3 && context->CLAMP.WMT < 3;
|
bool simple_sample = !tex->m_palette && cpsm.fmt == 0 && context->CLAMP.WMS < 3 && context->CLAMP.WMT < 3;
|
||||||
|
// Don't do extra filtering on sprite (it creates various upscaling issue)
|
||||||
|
bilinear &= !((m_vt.m_primclass == GS_SPRITE_CLASS) && (m_userhacks_stretch_sprite));
|
||||||
|
|
||||||
ps_sel.wms = context->CLAMP.WMS;
|
ps_sel.wms = context->CLAMP.WMS;
|
||||||
ps_sel.wmt = context->CLAMP.WMT;
|
ps_sel.wmt = context->CLAMP.WMT;
|
||||||
|
@ -426,7 +428,7 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
|
||||||
// FIXME the ati is currently disabled on the shader. I need to find a .gs to test that we got same
|
// FIXME the ati is currently disabled on the shader. I need to find a .gs to test that we got same
|
||||||
// bug on opengl
|
// bug on opengl
|
||||||
// FIXME for the moment disable it on subroutine (it will kill my perf for nothings)
|
// FIXME for the moment disable it on subroutine (it will kill my perf for nothings)
|
||||||
ps_sel.point_sampler = !(bilinear && simple_sample) && !GLLoader::found_GL_ARB_shader_subroutine;
|
ps_sel.point_sampler = 0; // !(bilinear && simple_sample) && !GLLoader::found_GL_ARB_shader_subroutine;
|
||||||
|
|
||||||
int w = tex->m_texture->GetWidth();
|
int w = tex->m_texture->GetWidth();
|
||||||
int h = tex->m_texture->GetHeight();
|
int h = tex->m_texture->GetHeight();
|
||||||
|
|
Loading…
Reference in New Issue