gsdx-ogl: alternate implementation of half pixel offset

The previous implementation of HPO adds an offset on vertex position. It
doesn't always work beside it moves the rendering window.

The new implementation will add a texture offset so that instead to sample
the middle of the GS texel, we will sample the middle of the real texture texel.

It must be manually enabled with
* UserHacks_HalfPixelOffset_New = 1 (keep a small offset as intended by GS effect)
* UserHacks_HalfPixelOffset_New = 2 (no offset)

v2: always apply a 0.5 offset in case of float coordinates (Tales of Abyss)
Might break other games but few of them uses float coordinates to read
back the target
This commit is contained in:
Gregory Hainaut 2016-06-21 09:51:47 +02:00
parent c2229e3c0b
commit 61a7c747e1
5 changed files with 66 additions and 3 deletions

View File

@ -39,6 +39,7 @@ GSRendererOGL::GSRendererOGL()
UserHacks_TCO_y = ((UserHacks_TCOffset >> 16) & 0xFFFF) / -1000.0f;
UserHacks_merge_sprite = theApp.GetConfigB("UserHacks_merge_pp_sprite");
UserHacks_unscale_pt_ln = theApp.GetConfigB("UserHacks_unscale_point_line");
UserHacks_HPO = theApp.GetConfigI("UserHacks_HalfPixelOffset_New");
m_prim_overlap = PRIM_OVERLAP_UNKNOW;
ResetStates();
@ -49,6 +50,7 @@ GSRendererOGL::GSRendererOGL()
UserHacks_TCO_y = 0;
UserHacks_merge_sprite = false;
UserHacks_unscale_pt_ln = false;
UserHacks_HPO = 0;
}
}
@ -736,6 +738,59 @@ void GSRendererOGL::EmulateBlending(bool DATE_GL42)
}
}
void GSRendererOGL::RealignTargetTextureCoordinate(const GSTextureCache::Source* tex)
{
if (!UserHacks_HPO || GetUpscaleMultiplier() == 1) return;
GSVertex* v = &m_vertex.buff[0];
const GSVector2& scale = tex->m_texture->GetScale();
bool linear = m_vt.IsLinear();
int t_position = v[0].U;
GSVector4 half_offset(0.0f);
// FIXME Let's start with something wrong same mess on X and Y
// FIXME Maybe it will be enough to check linear
if (PRIM->FST) {
if (UserHacks_HPO > 1) {
if (!linear && t_position == 8) {
half_offset.x = 8;
half_offset.y = 8;
} else if (linear && t_position == 16) {
half_offset.x = 16;
half_offset.y = 16;
}
} else {
if (!linear && t_position == 8) {
half_offset.x = 8 - 8 / scale.x;
half_offset.y = 8 - 8 / scale.y;
} else if (linear && t_position == 16) {
half_offset.x = 16 - 16 / scale.x;
half_offset.y = 16 - 16 / scale.y;
}
}
GL_INS("offset detected %f,%f t_pos %d (linear %d, scale %f)",
half_offset.x, half_offset.y, t_position, linear, scale.x);
} else if (m_vt.m_eq.q) {
float tw = (float)(1 << m_context->TEX0.TW);
float th = (float)(1 << m_context->TEX0.TH);
float q = v[0].RGBAQ.Q;
// Tales of Abyss
half_offset.x = 0.5f * q / tw;
half_offset.y = 0.5f * q / th;
GL_INS("ST offset detected %f,%f (linear %d, scale %f)",
half_offset.x, half_offset.y, linear, scale.x);
}
vs_cb.TextureOffset = GSVector4(half_offset);
}
void GSRendererOGL::EmulateTextureSampler(const GSTextureCache::Source* tex)
{
GSDeviceOGL* dev = (GSDeviceOGL*)m_dev;
@ -829,6 +884,8 @@ void GSRendererOGL::EmulateTextureSampler(const GSTextureCache::Source* tex)
// The purpose of texture shuffle is to move color channel. Extra interpolation is likely a bad idea.
bilinear &= m_vt.IsLinear();
RealignTargetTextureCoordinate(tex);
} else if (tex->m_target) {
// Use an old target. AEM and index aren't resolved it must be done
// on the GPU
@ -877,6 +934,8 @@ void GSRendererOGL::EmulateTextureSampler(const GSTextureCache::Source* tex)
bilinear &= m_vt.IsLinear();
}
RealignTargetTextureCoordinate(tex);
} else if (tex->m_palette) {
// Use a standard 8 bits texture. AEM is already done on the CLUT
// Therefore you only need to set the index
@ -1158,6 +1217,7 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
bool ate_second_pass = m_context->TEST.DoSecondPass();
ResetStates();
vs_cb.TextureOffset = GSVector4(0.0f);
ASSERT(m_dev != NULL);
GSDeviceOGL* dev = (GSDeviceOGL*)m_dev;
@ -1354,7 +1414,7 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
//The resulting shifted output aligns better with common blending / corona / blurring effects,
//but introduces a few bad pixels on the edges.
if (rt && rt->LikelyOffset)
if (rt && rt->LikelyOffset && !UserHacks_HPO)
{
ox2 *= rt->OffsetHack_modx;
oy2 *= rt->OffsetHack_mody;

View File

@ -54,6 +54,7 @@ class GSRendererOGL final : public GSRendererHW
float UserHacks_TCO_x, UserHacks_TCO_y;
bool UserHacks_merge_sprite;
bool UserHacks_unscale_pt_ln;
int UserHacks_HPO;
GSDeviceOGL::VSConstantBuffer vs_cb;
GSDeviceOGL::PSConstantBuffer ps_cb;
@ -75,6 +76,7 @@ class GSRendererOGL final : public GSRendererHW
inline void ResetStates();
inline void Lines2Sprites();
inline void SetupIA(const float& sx, const float& sy);
inline void RealignTargetTextureCoordinate(const GSTextureCache::Source* tex);
inline void EmulateTextureShuffleAndFbmask();
inline void EmulateChannelShuffle(GSTexture** rt, const GSTextureCache::Source* tex);
inline void EmulateBlending(bool DATE_GL42);

View File

@ -351,6 +351,7 @@ void GSdxApp::Init()
m_default_configuration["UserHacks_DisableGsMemClear"] = "0";
m_default_configuration["UserHacks_DisablePartialInvalidation"] = "0";
m_default_configuration["UserHacks_HalfPixelOffset"] = "0";
m_default_configuration["UserHacks_HalfPixelOffset_New"] = "0";
m_default_configuration["UserHacks_merge_pp_sprite"] = "0";
m_default_configuration["UserHacks_MSAA"] = "0";
m_default_configuration["UserHacks_unscale_point_line"] = "0";

View File

@ -27,7 +27,7 @@ void texture_coord()
vec2 uv = vec2(i_uv) - TextureOffset.xy;
// Float coordinate
VSout.t_float.xy = i_st - TextureOffset.zw; // FIXME or .xy check final code
VSout.t_float.xy = i_st - TextureOffset.xy;
VSout.t_float.w = i_q;
// Integer coordinate => normalized

View File

@ -770,7 +770,7 @@ static const char* const tfx_vgs_glsl =
" vec2 uv = vec2(i_uv) - TextureOffset.xy;\n"
"\n"
" // Float coordinate\n"
" VSout.t_float.xy = i_st - TextureOffset.zw; // FIXME or .xy check final code\n"
" VSout.t_float.xy = i_st - TextureOffset.xy;\n"
" VSout.t_float.w = i_q;\n"
"\n"
" // Integer coordinate => normalized\n"