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:
Gregory Hainaut 2015-04-03 18:11:47 +02:00
parent 22710d9b44
commit e1a5736583
4 changed files with 52 additions and 2 deletions

View File

@ -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;
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;
// 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.wmt = context->CLAMP.WMT;

View File

@ -32,6 +32,7 @@ GSRendererHW::GSRendererHW(GSTextureCache* tc)
{
m_upscale_multiplier = theApp.GetConfig("upscale_multiplier", 1);
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)
{
@ -54,6 +55,31 @@ GSRendererHW::GSRendererHW(GSTextureCache* tc)
{
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()
@ -295,6 +321,23 @@ void GSRendererHW::Draw()
context->FRAME.FBMSK = fm;
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);

View File

@ -35,7 +35,8 @@ private:
bool m_reset;
int m_upscale_multiplier;
int m_userhacks_skipdraw;
int m_sub_texel_offset;
#pragma region hacks
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;
bool m_userhacks_stretch_sprite;
public:
GSRendererHW(GSTextureCache* tc);
virtual ~GSRendererHW();

View File

@ -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;
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;
// 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.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
// bug on opengl
// 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 h = tex->m_texture->GetHeight();