gsdx-ogl: implement a new hack UserHacks_UnscaleSprite for opengl

UserHacks_UnscaleSprite = 1 will unscale flat sprites
UserHacks_UnscaleSprite = 2 will unscale all  sprites (don't work well so far)

The idea of the hack is to redo the interpolation of texture coordinate
based on the non-upscaled pixel position.

It avoids various glitches but sprites aren't upscaled anymore (so no
more anti-aliasing, potentially a coefficient can be added).
This commit is contained in:
Gregory Hainaut 2015-04-19 18:22:20 +02:00
parent 6124eb844e
commit 31f8c065db
8 changed files with 208 additions and 44 deletions

View File

@ -116,6 +116,7 @@ GSDeviceOGL::~GSDeviceOGL()
// Delete HW FX
delete m_vs_cb;
delete m_gs_cb;
delete m_ps_cb;
gl_DeleteSamplers(1, &m_palette_ss);
m_shader->Delete(m_apitrace);
@ -668,10 +669,15 @@ GLuint GSDeviceOGL::CompileVS(VSSelector sel, int logz)
/* Note: must be here because tfx_glsl is static */
GLuint GSDeviceOGL::CompileGS()
{
// Don't use a dynamic selector to only compile a single GS.
// If configuration is updated, shader will be recompiled
int unscale_sprite = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_UnscaleSprite", 0) : 0;
std::string macro = format("#define GS_SPRITE %d\n", unscale_sprite);
#ifdef ENABLE_GLES
return 0;
#else
return m_shader->Compile("tfx_vgs.glsl", "gs_main", GL_GEOMETRY_SHADER, tfx_vgs_glsl, "");
return m_shader->Compile("tfx_vgs.glsl", "gs_main", GL_GEOMETRY_SHADER, tfx_vgs_glsl, macro);
#endif
}
@ -697,6 +703,7 @@ GLuint GSDeviceOGL::CompilePS(PSSelector sel)
+ format("#define PS_TCOFFSETHACK %d\n", sel.tcoffsethack)
+ format("#define PS_POINT_SAMPLER %d\n", sel.point_sampler)
+ format("#define PS_IIP %d\n", sel.iip)
+ format("#define PS_SPRITE %d\n", sel.sprite)
;
return m_shader->Compile("tfx.glsl", "ps_main", GL_FRAGMENT_SHADER, tfx_fs_all_glsl, macro);

View File

@ -334,6 +334,31 @@ class GSDeviceOGL : public GSDevice
}
};
__aligned(struct, 32) GSConstantBuffer
{
GSVector4 rt_size;
GSConstantBuffer()
{
rt_size = GSVector4::zero();
}
__forceinline bool Update(const GSConstantBuffer* cb)
{
GSVector4i* a = (GSVector4i*)this;
GSVector4i* b = (GSVector4i*)cb;
if(!(a[0] == b[0]).alltrue())
{
a[0] = b[0];
return true;
}
return false;
}
};
struct PSSelector
{
union
@ -352,6 +377,7 @@ class GSDeviceOGL : public GSDevice
uint32 tcoffsethack:1;
uint32 point_sampler:1;
uint32 iip:1;
uint32 sprite:1;
// Next param will be handle by subroutine
uint32 colclip:2;
uint32 atst:3;
@ -366,7 +392,7 @@ class GSDeviceOGL : public GSDevice
uint32 key;
};
operator uint32() {return key & 0x1fffffff;}
operator uint32() {return key & 0x3fffffff;}
PSSelector() : key(0) {}
};
@ -528,9 +554,11 @@ class GSDeviceOGL : public GSDevice
GLuint m_rt_ss;
GSUniformBufferOGL* m_vs_cb;
GSUniformBufferOGL* m_gs_cb;
GSUniformBufferOGL* m_ps_cb;
VSConstantBuffer m_vs_cb_cache;
GSConstantBuffer m_gs_cb_cache;
PSConstantBuffer m_ps_cb_cache;
GSTexture* CreateSurface(int type, int w, int h, bool msaa, int format);
@ -624,7 +652,7 @@ class GSDeviceOGL : public GSDevice
void SetupVS(VSSelector sel);
void SetupGS(bool enable);
void SetupPS(PSSelector sel);
void SetupCB(const VSConstantBuffer* vs_cb, const PSConstantBuffer* ps_cb);
void SetupCB(const VSConstantBuffer* vs_cb, const PSConstantBuffer* ps_cb, const GSConstantBuffer* gs_cb);
void SetupSampler(PSSamplerSelector ssel);
void SetupOM(OMDepthStencilSelector dssel, OMBlendSelector bsel, uint8 afix);
GLuint GetSamplerID(PSSamplerSelector ssel);

View File

@ -32,6 +32,7 @@ GSRendererOGL::GSRendererOGL()
UserHacks_AlphaStencil = !!theApp.GetConfig("UserHacks_AlphaStencil", 0) && !!theApp.GetConfig("UserHacks", 0);
UserHacks_DateGL4 = !!theApp.GetConfig("UserHacks_DateGL4", 0);
m_pixelcenter = GSVector2(-0.5f, -0.5f);
UserHacks_Unscale_sprite = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_UnscaleSprite", 0) : 0;
UserHacks_TCOffset = !!theApp.GetConfig("UserHacks", 0) ? theApp.GetConfig("UserHacks_TCOffset", 0) : 0;
UserHacks_TCO_x = (UserHacks_TCOffset & 0xFFFF) / -1000.0f;
@ -165,10 +166,19 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
GSDeviceOGL* dev = (GSDeviceOGL*)m_dev;
GSDeviceOGL::VSSelector vs_sel;
GSDeviceOGL::VSConstantBuffer vs_cb;
// Blend
GSDeviceOGL::GSConstantBuffer gs_cb;
GSDeviceOGL::PSSelector ps_sel;
GSDeviceOGL::PSConstantBuffer ps_cb;
GSDeviceOGL::PSSamplerSelector ps_ssel;
GSDeviceOGL::OMBlendSelector om_bsel;
GSDeviceOGL::OMDepthStencilSelector om_dssel;
// Blend
if(!IsOpaque())
{
@ -238,8 +248,6 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
// om
GSDeviceOGL::OMDepthStencilSelector om_dssel;
if(context->TEST.ZTE)
{
om_dssel.ztst = context->TEST.ZTST;
@ -255,9 +263,17 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
om_dssel.fba = context->FBA.FBA;
}
// vs
bool hack_enabled = (UserHacks_Unscale_sprite > 1) || ((UserHacks_Unscale_sprite == 1) && !m_vt.IsLinear());
if (hack_enabled && (m_vt.m_primclass == GS_SPRITE_CLASS) && GLLoader::found_geometry_shader) {
bool blit = tex && (tex->m_texture->GetWidth() == rtsize.x) && (tex->m_texture->GetHeight() == rtsize.y);
if (!blit) {
ps_sel.sprite = 1;
gs_cb.rt_size = GSVector4(rtsize.x / rtscale.x, rtsize.y / rtscale.y) / 2.0f;
}
}
GSDeviceOGL::VSSelector vs_sel;
// vs
vs_sel.tme = PRIM->TME;
vs_sel.fst = PRIM->FST;
@ -298,8 +314,6 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
}
// FIXME Opengl support half pixel center (as dx10). Code could be easier!!!
GSDeviceOGL::VSConstantBuffer vs_cb;
float sx = 2.0f * rtscale.x / (rtsize.x << 4);
float sy = 2.0f * rtscale.y / (rtsize.y << 4);
float ox = (float)(int)context->XYOFFSET.OFX;
@ -323,12 +337,6 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
vs_cb.Vertex_Scale_Offset = GSVector4(sx, sy, ox * sx + ox2 + 1, oy * sy + oy2 + 1);
// END of FIXME
// ps
GSDeviceOGL::PSSelector ps_sel;
GSDeviceOGL::PSSamplerSelector ps_ssel;
GSDeviceOGL::PSConstantBuffer ps_cb;
// GS_SPRITE_CLASS are already flat (either by CPU or the GS)
ps_sel.iip = (m_vt.m_primclass == GS_SPRITE_CLASS) ? 1 : PRIM->IIP;
@ -481,7 +489,7 @@ void GSRendererOGL::DrawPrims(GSTexture* rt, GSTexture* ds, GSTextureCache::Sour
SetupIA();
dev->SetupOM(om_dssel, om_bsel, afix);
dev->SetupCB(&vs_cb, &ps_cb);
dev->SetupCB(&vs_cb, &ps_cb, ps_sel.sprite ? &gs_cb : NULL);
if (advance_DATE) {
// Create an r32i image that will contain primitive ID

View File

@ -39,6 +39,7 @@ class GSRendererOGL : public GSRendererHW
bool UserHacks_AlphaHack;
bool UserHacks_AlphaStencil;
bool UserHacks_DateGL4;
int UserHacks_Unscale_sprite;
unsigned int UserHacks_TCOffset;
float UserHacks_TCO_x, UserHacks_TCO_y;

View File

@ -25,10 +25,12 @@
static const uint32 g_vs_cb_index = 20;
static const uint32 g_ps_cb_index = 21;
static const uint32 g_gs_cb_index = 22;
void GSDeviceOGL::CreateTextureFX()
{
m_vs_cb = new GSUniformBufferOGL(g_vs_cb_index, sizeof(VSConstantBuffer));
m_gs_cb = new GSUniformBufferOGL(g_gs_cb_index, sizeof(GSConstantBuffer));
m_ps_cb = new GSUniformBufferOGL(g_ps_cb_index, sizeof(PSConstantBuffer));
// warning 1 sampler by image unit. So you cannot reuse m_ps_ss...
@ -117,7 +119,7 @@ GSBlendStateOGL* GSDeviceOGL::CreateBlend(OMBlendSelector bsel, uint8 afix)
return bs;
}
void GSDeviceOGL::SetupCB(const VSConstantBuffer* vs_cb, const PSConstantBuffer* ps_cb)
void GSDeviceOGL::SetupCB(const VSConstantBuffer* vs_cb, const PSConstantBuffer* ps_cb, const GSConstantBuffer* gs_cb)
{
if(m_vs_cb_cache.Update(vs_cb)) {
m_vs_cb->upload(vs_cb);
@ -127,6 +129,10 @@ void GSDeviceOGL::SetupCB(const VSConstantBuffer* vs_cb, const PSConstantBuffer*
m_ps_cb->upload(ps_cb);
}
if (gs_cb && m_gs_cb_cache.Update(gs_cb)) {
m_gs_cb->upload(gs_cb);
}
}
void GSDeviceOGL::SetupVS(VSSelector sel)

View File

@ -38,6 +38,7 @@
#define PS_POINT_SAMPLER 0
#define PS_TCOFFSETHACK 0
#define PS_IIP 1
#define PS_SPRITE 0
#endif
#ifdef FRAGMENT_SHADER
@ -47,6 +48,11 @@ in SHADER
vec4 t;
vec4 c;
flat vec4 fc;
#if PS_SPRITE == 1
flat vec4 flat_T;
flat vec2 flat_P;
vec2 alpha;
#endif
} PSin;
#define PSin_t (PSin.t)
@ -430,7 +436,21 @@ void fog(inout vec4 c, float f)
vec4 ps_color()
{
vec4 t = sample_color(PSin_t.xy, PSin_t.w);
#if PS_SPRITE == 1
// Reinterpolate manually the texture coordinate.
// trunc => native resolution. Maybe we can add an option to choose a value between
// trunc and current.
vec2 factor = vec2(trunc(PSin.alpha.x), floor(PSin.alpha.y));
//vec2 factor = trunc(PSin.alpha);
factor *= PSin.flat_P;
vec2 txy;
txy.x = mix(PSin.flat_T.x, PSin.flat_T.y, factor.x);
txy.y = mix(PSin.flat_T.z, PSin.flat_T.w, factor.y);
#else
vec2 txy = PSin_t.xy;
#endif
vec4 t = sample_color(txy, PSin_t.w);
vec4 zero = vec4(0.0f, 0.0f, 0.0f, 0.0f);
vec4 one = vec4(1.0f, 1.0f, 1.0f, 1.0f);

View File

@ -7,13 +7,6 @@
#define VS_LOGZ 0
#endif
struct vertex
{
vec4 t;
vec4 c;
vec4 fc;
};
#ifdef VERTEX_SHADER
layout(location = 0) in vec2 i_st;
layout(location = 2) in vec4 i_c;
@ -158,6 +151,11 @@ void vs_main()
#endif
#ifdef GEOMETRY_SHADER
#ifndef GS_SPRITE
#define GS_SPRITE 0
#endif
in gl_PerVertex {
invariant vec4 gl_Position;
float gl_PointSize;
@ -188,26 +186,63 @@ out SHADER
vec4 t;
vec4 c;
flat vec4 fc;
#if GS_SPRITE > 0
flat vec4 flat_T;
flat vec2 flat_P;
vec2 alpha;
#endif
} GSout;
layout(std140, binding = 22) uniform cb22
{
vec4 rt_size;
};
struct vertex
{
vec4 t;
vec4 c;
vec2 a;
};
void out_vertex(in vertex v)
{
GSout.t = v.t;
GSout.c = v.c;
GSout.fc = v.fc;
#if GS_SPRITE > 0
GSout.alpha = v.a;
#endif
gl_PrimitiveID = gl_PrimitiveIDIn;
EmitVertex();
}
void out_flat(in vec2 dp)
{
// Flat output
GSout.fc = GSin[1].fc;
#if GS_SPRITE > 0
GSout.flat_T = vec4(GSin[0].t.x, GSin[1].t.x, GSin[0].t.y, GSin[1].t.y);
GSout.flat_P = 1.0f / dp;
#endif
}
layout(lines) in;
layout(triangle_strip, max_vertices = 6) out;
void gs_main()
{
// Rescale from -1 1 to 0:1 (require window size)
#if GS_SPRITE > 0
vec2 dp = (gl_in[1].gl_Position.xy - gl_in[0].gl_Position.xy) * rt_size.xy;
#else
vec2 dp = vec2(0.0f, 0.0f);
#endif
// left top => GSin[0];
// right bottom => GSin[1];
vertex rb = vertex(GSin[1].t, GSin[1].c, GSin[1].fc);
vertex lt = vertex(GSin[0].t, GSin[0].c, GSin[0].fc);
vertex rb = vertex(GSin[1].t, GSin[1].c, dp);
vertex lt = vertex(GSin[0].t, GSin[0].c, vec2(0.0f, 0.0f));
vec4 rb_p = gl_in[1].gl_Position;
vec4 lb_p = gl_in[1].gl_Position;
@ -225,10 +260,12 @@ void gs_main()
vertex lb = rb;
lb_p.x = lt_p.x;
lb.t.x = lt.t.x;
lb.a.x = lt.a.x;
vertex rt = rb;
rt_p.y = lt_p.y;
rt.t.y = lt.t.y;
rt.a.y = lt.a.y;
// Triangle 1
gl_Position = lt_p;
@ -238,8 +275,8 @@ void gs_main()
out_vertex(lb);
gl_Position = rt_p;
out_flat(dp);
out_vertex(rt);
EndPrimitive();
// Triangle 2
@ -250,8 +287,8 @@ void gs_main()
out_vertex(rt);
gl_Position = rb_p;
out_flat(dp);
out_vertex(rb);
EndPrimitive();
}

View File

@ -458,13 +458,6 @@ static const char* tfx_vgs_glsl =
"#define VS_LOGZ 0\n"
"#endif\n"
"\n"
"struct vertex\n"
"{\n"
" vec4 t;\n"
" vec4 c;\n"
" vec4 fc;\n"
"};\n"
"\n"
"#ifdef VERTEX_SHADER\n"
"layout(location = 0) in vec2 i_st;\n"
"layout(location = 2) in vec4 i_c;\n"
@ -609,6 +602,11 @@ static const char* tfx_vgs_glsl =
"#endif\n"
"\n"
"#ifdef GEOMETRY_SHADER\n"
"\n"
"#ifndef GS_SPRITE\n"
"#define GS_SPRITE 0\n"
"#endif\n"
"\n"
"in gl_PerVertex {\n"
" invariant vec4 gl_Position;\n"
" float gl_PointSize;\n"
@ -639,26 +637,63 @@ static const char* tfx_vgs_glsl =
" vec4 t;\n"
" vec4 c;\n"
" flat vec4 fc;\n"
"#if GS_SPRITE > 0\n"
" flat vec4 flat_T;\n"
" flat vec2 flat_P;\n"
" vec2 alpha;\n"
"#endif\n"
"} GSout;\n"
"\n"
"layout(std140, binding = 22) uniform cb22\n"
"{\n"
" vec4 rt_size;\n"
"};\n"
"\n"
"\n"
"struct vertex\n"
"{\n"
" vec4 t;\n"
" vec4 c;\n"
" vec2 a;\n"
"};\n"
"\n"
"void out_vertex(in vertex v)\n"
"{\n"
" GSout.t = v.t;\n"
" GSout.c = v.c;\n"
" GSout.fc = v.fc;\n"
"#if GS_SPRITE > 0\n"
" GSout.alpha = v.a;\n"
"#endif\n"
" gl_PrimitiveID = gl_PrimitiveIDIn;\n"
" EmitVertex();\n"
"}\n"
"\n"
"void out_flat(in vec2 dp)\n"
"{\n"
" // Flat output\n"
" GSout.fc = GSin[1].fc;\n"
"#if GS_SPRITE > 0\n"
" GSout.flat_T = vec4(GSin[0].t.x, GSin[1].t.x, GSin[0].t.y, GSin[1].t.y);\n"
" GSout.flat_P = 1.0f / dp;\n"
"#endif\n"
"}\n"
"\n"
"layout(lines) in;\n"
"layout(triangle_strip, max_vertices = 6) out;\n"
"\n"
"void gs_main()\n"
"{\n"
" // Rescale from -1 1 to 0:1 (require window size)\n"
"#if GS_SPRITE > 0\n"
" vec2 dp = (gl_in[1].gl_Position.xy - gl_in[0].gl_Position.xy) * rt_size.xy;\n"
"#else\n"
" vec2 dp = vec2(0.0f, 0.0f);\n"
"#endif\n"
"\n"
" // left top => GSin[0];\n"
" // right bottom => GSin[1];\n"
" vertex rb = vertex(GSin[1].t, GSin[1].c, GSin[1].fc);\n"
" vertex lt = vertex(GSin[0].t, GSin[0].c, GSin[0].fc);\n"
" vertex rb = vertex(GSin[1].t, GSin[1].c, dp);\n"
" vertex lt = vertex(GSin[0].t, GSin[0].c, vec2(0.0f, 0.0f));\n"
"\n"
" vec4 rb_p = gl_in[1].gl_Position;\n"
" vec4 lb_p = gl_in[1].gl_Position;\n"
@ -676,10 +711,12 @@ static const char* tfx_vgs_glsl =
" vertex lb = rb;\n"
" lb_p.x = lt_p.x;\n"
" lb.t.x = lt.t.x;\n"
" lb.a.x = lt.a.x;\n"
"\n"
" vertex rt = rb;\n"
" rt_p.y = lt_p.y;\n"
" rt.t.y = lt.t.y;\n"
" rt.a.y = lt.a.y;\n"
"\n"
" // Triangle 1\n"
" gl_Position = lt_p;\n"
@ -689,8 +726,8 @@ static const char* tfx_vgs_glsl =
" out_vertex(lb);\n"
"\n"
" gl_Position = rt_p;\n"
" out_flat(dp);\n"
" out_vertex(rt);\n"
"\n"
" EndPrimitive();\n"
"\n"
" // Triangle 2\n"
@ -701,8 +738,8 @@ static const char* tfx_vgs_glsl =
" out_vertex(rt);\n"
"\n"
" gl_Position = rb_p;\n"
" out_flat(dp);\n"
" out_vertex(rb);\n"
"\n"
" EndPrimitive();\n"
"}\n"
"\n"
@ -750,6 +787,7 @@ static const char* tfx_fs_all_glsl =
"#define PS_POINT_SAMPLER 0\n"
"#define PS_TCOFFSETHACK 0\n"
"#define PS_IIP 1\n"
"#define PS_SPRITE 0\n"
"#endif\n"
"\n"
"#ifdef FRAGMENT_SHADER\n"
@ -759,6 +797,11 @@ static const char* tfx_fs_all_glsl =
" vec4 t;\n"
" vec4 c;\n"
" flat vec4 fc;\n"
"#if PS_SPRITE == 1\n"
" flat vec4 flat_T;\n"
" flat vec2 flat_P;\n"
" vec2 alpha;\n"
"#endif\n"
"} PSin;\n"
"\n"
"#define PSin_t (PSin.t)\n"
@ -1142,7 +1185,21 @@ static const char* tfx_fs_all_glsl =
"\n"
"vec4 ps_color()\n"
"{\n"
" vec4 t = sample_color(PSin_t.xy, PSin_t.w);\n"
"#if PS_SPRITE == 1\n"
" // Reinterpolate manually the texture coordinate.\n"
" // trunc => native resolution. Maybe we can add an option to choose a value between\n"
" // trunc and current.\n"
" vec2 factor = vec2(trunc(PSin.alpha.x), floor(PSin.alpha.y));\n"
" //vec2 factor = trunc(PSin.alpha);\n"
" factor *= PSin.flat_P;\n"
" vec2 txy;\n"
" txy.x = mix(PSin.flat_T.x, PSin.flat_T.y, factor.x);\n"
" txy.y = mix(PSin.flat_T.z, PSin.flat_T.w, factor.y);\n"
"\n"
"#else\n"
" vec2 txy = PSin_t.xy;\n"
"#endif\n"
" vec4 t = sample_color(txy, PSin_t.w);\n"
"\n"
" vec4 zero = vec4(0.0f, 0.0f, 0.0f, 0.0f);\n"
" vec4 one = vec4(1.0f, 1.0f, 1.0f, 1.0f);\n"