mirror of
https://github.com/PCSX2/pcsx2.git
synced 2025-12-16 04:08:48 +00:00
GS/HW: Refactor alpha test method selection.
This commit is contained in:
parent
f0705bf13a
commit
04fd253744
@ -33,6 +33,14 @@
|
||||
#define AFAIL_RGB_ONLY 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ATST_NONE
|
||||
#define PS_ATST_NONE 0
|
||||
#define PS_ATST_LEQUAL 1
|
||||
#define PS_ATST_GEQUAL 2
|
||||
#define PS_ATST_EQUAL 3
|
||||
#define PS_ATST_NOTEQUAL 4
|
||||
#endif
|
||||
|
||||
#ifndef PS_FST
|
||||
#define PS_IIP 0
|
||||
#define PS_FST 0
|
||||
@ -154,7 +162,7 @@ struct PS_OUTPUT
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
#if PS_ZCLAMP || (PS_DEPTH_FEEDBACK && AFAIL_NEEDS_DEPTH)
|
||||
#if PS_ZCLAMP
|
||||
float depth : SV_Depth;
|
||||
#endif
|
||||
};
|
||||
@ -729,27 +737,27 @@ bool atst(float4 C)
|
||||
{
|
||||
float a = C.a;
|
||||
|
||||
if(PS_ATST == 1)
|
||||
{
|
||||
return (a <= AREF);
|
||||
}
|
||||
else if(PS_ATST == 2)
|
||||
{
|
||||
return (a >= AREF);
|
||||
}
|
||||
else if(PS_ATST == 3)
|
||||
{
|
||||
return (abs(a - AREF) <= 0.5f);
|
||||
}
|
||||
else if(PS_ATST == 4)
|
||||
{
|
||||
return (abs(a - AREF) >= 0.5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
#if PS_ATST == PS_ATST_LEQUAL
|
||||
|
||||
return (a <= AREF);
|
||||
|
||||
#elif PS_ATST == PS_ATST_GEQUAL
|
||||
|
||||
return (a >= AREF);
|
||||
|
||||
#elif PS_ATST == PS_ATST_EQUAL
|
||||
|
||||
return (abs(a - AREF) <= 0.5f);
|
||||
|
||||
#elif PS_ATST == PS_ATST_NOTEQUAL
|
||||
|
||||
return (abs(a - AREF) >= 0.5f);
|
||||
|
||||
#else
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
float4 fog(float4 c, float f)
|
||||
@ -1225,7 +1233,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#endif
|
||||
|
||||
// Alpha test with feedback
|
||||
#if (PS_AFAIL == AFAIL_FB_ONLY) && PS_DEPTH_FEEDBACK
|
||||
#if (PS_AFAIL == AFAIL_FB_ONLY) && PS_DEPTH_FEEDBACK && PS_ZCLAMP
|
||||
if (!atst_pass)
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
|
||||
#elif (PS_AFAIL == AFAIL_ZB_ONLY) && PS_COLOR_FEEDBACK
|
||||
@ -1237,7 +1245,7 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
#if PS_COLOR_FEEDBACK && PS_NO_COLOR1 // No dual src blend
|
||||
output.c0.a = RtTexture.Load(int3(input.p.xy, 0)).a;
|
||||
#endif
|
||||
#if PS_DEPTH_FEEDBACK
|
||||
#if PS_DEPTH_FEEDBACK && PS_ZCLAMP
|
||||
input.p.z = DepthTexture.Load(int3(input.p.xy, 0)).r;
|
||||
#endif
|
||||
}
|
||||
@ -1249,8 +1257,6 @@ PS_OUTPUT ps_main(PS_INPUT input)
|
||||
|
||||
#if PS_ZCLAMP
|
||||
output.depth = min(input.p.z, MaxDepthPS);
|
||||
#elif PS_DEPTH_FEEDBACK && AFAIL_NEEDS_DEPTH
|
||||
output.depth = input.p.z; // Output depth value for ATST pass/fail
|
||||
#endif
|
||||
|
||||
return output;
|
||||
|
||||
@ -23,6 +23,14 @@
|
||||
#define AFAIL_RGB_ONLY 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ATST_NONE
|
||||
#define PS_ATST_NONE 0
|
||||
#define PS_ATST_LEQUAL 1
|
||||
#define PS_ATST_GEQUAL 2
|
||||
#define PS_ATST_EQUAL 3
|
||||
#define PS_ATST_NOTEQUAL 4
|
||||
#endif
|
||||
|
||||
// TEX_COORD_DEBUG output the uv coordinate as color. It is useful
|
||||
// to detect bad sampling due to upscaling
|
||||
//#define TEX_COORD_DEBUG
|
||||
@ -655,17 +663,26 @@ bool atst(vec4 C)
|
||||
{
|
||||
float a = C.a;
|
||||
|
||||
#if (PS_ATST == 1)
|
||||
#if PS_ATST == PS_ATST_LEQUAL
|
||||
|
||||
return (a <= AREF);
|
||||
#elif (PS_ATST == 2)
|
||||
|
||||
#elif PS_ATST == PS_ATST_GEQUAL
|
||||
|
||||
return (a >= AREF);
|
||||
#elif (PS_ATST == 3)
|
||||
|
||||
#elif PS_ATST == PS_ATST_EQUAL
|
||||
|
||||
return (abs(a - AREF) <= 0.5f);
|
||||
#elif (PS_ATST == 4)
|
||||
|
||||
#elif PS_ATST == PS_ATST_NOTEQUAL
|
||||
|
||||
return (abs(a - AREF) >= 0.5f);
|
||||
|
||||
#else
|
||||
// nothing to do
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -1172,7 +1189,7 @@ void ps_main()
|
||||
#endif
|
||||
|
||||
// Alpha test with feedback
|
||||
#if (PS_AFAIL == AFAIL_FB_ONLY) && NEEDS_DEPTH
|
||||
#if (PS_AFAIL == AFAIL_FB_ONLY) && NEEDS_DEPTH && PS_ZCLAMP
|
||||
if (!atst_pass)
|
||||
FragCoord.z = sample_from_depth().r;
|
||||
#elif (PS_AFAIL == AFAIL_ZB_ONLY) && NEEDS_RT
|
||||
@ -1184,7 +1201,7 @@ void ps_main()
|
||||
#if NEEDS_RT && PS_NO_COLOR1 // No dual src blend
|
||||
C.a = sample_from_rt().a;
|
||||
#endif
|
||||
#if NEEDS_DEPTH
|
||||
#if NEEDS_DEPTH && PS_ZCLAMP
|
||||
FragCoord.z = sample_from_depth().r;
|
||||
#endif
|
||||
}
|
||||
@ -1201,7 +1218,5 @@ void ps_main()
|
||||
|
||||
#if PS_ZCLAMP
|
||||
gl_FragDepth = min(FragCoord.z, MaxDepthPS);
|
||||
#elif NEEDS_DEPTH && AFAIL_NEEDS_DEPTH
|
||||
gl_FragDepth = FragCoord.z; // Output depth value for ATST pass/fail
|
||||
#endif
|
||||
}
|
||||
|
||||
@ -257,6 +257,14 @@ void main()
|
||||
#define AFAIL_RGB_ONLY 3
|
||||
#endif
|
||||
|
||||
#ifndef PS_ATST_NONE
|
||||
#define PS_ATST_NONE 0
|
||||
#define PS_ATST_LEQUAL 1
|
||||
#define PS_ATST_GEQUAL 2
|
||||
#define PS_ATST_EQUAL 3
|
||||
#define PS_ATST_NOTEQUAL 4
|
||||
#endif
|
||||
|
||||
#ifndef PS_FST
|
||||
#define PS_FST 0
|
||||
#define PS_WMS 0
|
||||
@ -920,28 +928,27 @@ bool atst(vec4 C)
|
||||
{
|
||||
float a = C.a;
|
||||
|
||||
#if (PS_ATST == 1)
|
||||
{
|
||||
return (a <= AREF);
|
||||
}
|
||||
#elif (PS_ATST == 2)
|
||||
{
|
||||
return (a >= AREF);
|
||||
}
|
||||
#elif (PS_ATST == 3)
|
||||
{
|
||||
return (abs(a - AREF) <= 0.5f);
|
||||
}
|
||||
#elif (PS_ATST == 4)
|
||||
{
|
||||
return (abs(a - AREF) >= 0.5f);
|
||||
}
|
||||
#else
|
||||
{
|
||||
// nothing to do
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
#if PS_ATST == PS_ATST_LEQUAL
|
||||
|
||||
return (a <= AREF);
|
||||
|
||||
#elif PS_ATST == PS_ATST_GEQUAL
|
||||
|
||||
return (a >= AREF);
|
||||
|
||||
#elif PS_ATST == PS_ATST_EQUAL
|
||||
|
||||
return (abs(a - AREF) <= 0.5f);
|
||||
|
||||
#elif PS_ATST == PS_ATST_NOTEQUAL
|
||||
|
||||
return (abs(a - AREF) >= 0.5f);
|
||||
|
||||
#else
|
||||
|
||||
return true;
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
vec4 fog(vec4 c, float f)
|
||||
@ -1447,7 +1454,7 @@ void main()
|
||||
#endif
|
||||
|
||||
// Alpha test with feedback
|
||||
#if (PS_AFAIL == AFAIL_FB_ONLY) && PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if (PS_AFAIL == AFAIL_FB_ONLY) && PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && PS_ZCLAMP
|
||||
if (!atst_pass)
|
||||
FragCoord.z = sample_from_depth().r;
|
||||
#elif (PS_AFAIL == AFAIL_ZB_ONLY) && PS_FEEDBACK_LOOP_IS_NEEDED_RT
|
||||
@ -1459,7 +1466,7 @@ void main()
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_RT && PS_NO_COLOR1 // No dual src blend
|
||||
o_col0.a = sample_from_rt().a;
|
||||
#endif
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH
|
||||
#if PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && PS_ZCLAMP
|
||||
FragCoord.z = sample_from_depth().r;
|
||||
#endif
|
||||
}
|
||||
@ -1468,8 +1475,6 @@ void main()
|
||||
|
||||
#if PS_ZCLAMP
|
||||
gl_FragDepth = min(FragCoord.z, MaxDepthPS);
|
||||
#elif PS_FEEDBACK_LOOP_IS_NEEDED_DEPTH && AFAIL_NEEDS_DEPTH
|
||||
gl_FragDepth = FragCoord.z; // Output depth value for ATST pass/fail
|
||||
#endif
|
||||
#endif // PS_DATE
|
||||
}
|
||||
|
||||
@ -201,6 +201,15 @@ enum GS_AFAIL
|
||||
AFAIL_RGB_ONLY = 3,
|
||||
};
|
||||
|
||||
enum GS_ALPHA_BITS
|
||||
{
|
||||
ALPHA_ABC_CS = 0,
|
||||
ALPHA_ABC_CD = 1,
|
||||
ALPHA_C_AS = 0,
|
||||
ALPHA_C_AD = 1,
|
||||
ALPHA_C_FIX = 2,
|
||||
};
|
||||
|
||||
enum class GS_MIN_FILTER : uint8_t
|
||||
{
|
||||
Nearest = 0,
|
||||
@ -773,8 +782,6 @@ REG64_(GIFReg, TEST)
|
||||
u32 _PAD1 : 13;
|
||||
u32 _PAD2 : 32;
|
||||
REG_END2
|
||||
__forceinline bool DoFirstPass() const { return !ATE || ATST != ATST_NEVER; } // not all pixels fail automatically
|
||||
__forceinline bool DoSecondPass() const { return ATE && ATST != ATST_ALWAYS && AFAIL != AFAIL_KEEP; } // pixels may fail, write fb/z
|
||||
__forceinline u32 GetAFAIL(u32 fpsm) const { return (AFAIL == AFAIL_RGB_ONLY && (fpsm & 0xF) != 0) ? static_cast<u32>(AFAIL_FB_ONLY) : AFAIL; } // FB Only when not 32bit Framebuffer
|
||||
REG_END2
|
||||
|
||||
|
||||
@ -327,6 +327,16 @@ struct alignas(16) GSHWDrawConfig
|
||||
__fi bool UseExpandIndexBuffer() const { return (expand == VSExpand::Point || expand == VSExpand::Sprite); }
|
||||
};
|
||||
static_assert(sizeof(VSSelector) == 1, "VSSelector is a single byte");
|
||||
|
||||
enum PSAlphaTest
|
||||
{
|
||||
PS_ATST_NONE = 0,
|
||||
PS_ATST_LEQUAL = 1,
|
||||
PS_ATST_GEQUAL = 2,
|
||||
PS_ATST_EQUAL = 3,
|
||||
PS_ATST_NOTEQUAL = 4
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
#pragma pack(push, 4)
|
||||
struct PSSelector
|
||||
@ -400,7 +410,7 @@ struct alignas(16) GSHWDrawConfig
|
||||
u32 dither : 2;
|
||||
u32 dither_adjust : 1;
|
||||
|
||||
// Depth clamp
|
||||
// Depth clamp - also indicates SW depth write.
|
||||
u32 zclamp : 1;
|
||||
|
||||
// Hack
|
||||
|
||||
@ -5117,6 +5117,33 @@ void GSRendererHW::SetupIA(float target_scale, float sx, float sy, bool req_vert
|
||||
m_conf.nindices = m_index.tail;
|
||||
}
|
||||
|
||||
void GSRendererHW::GetZClampConfigVSPS(const HWCachedCtx& cached_ctx, const GSVertexTrace& vt, const bool force_enable_ps, GSHWDrawConfig& config)
|
||||
{
|
||||
const u32 max_z = 0xFFFFFFFF >> (GSLocalMemory::m_psm[cached_ctx.ZBUF.PSM].fmt * 8);
|
||||
const bool large_z = static_cast<u32>(GSVector4i(vt.m_max.p).z) > max_z;
|
||||
|
||||
config.cb_vs.max_depth = GSVector2i(0xFFFFFFFF);
|
||||
config.cb_ps.TA_MaxDepth_Af.z = 0.0f;
|
||||
config.ps.zclamp = 0;
|
||||
|
||||
// Clamp in the vertex shader for primitives that have flat colors.
|
||||
const bool clamp_vs = large_z && (vt.m_primclass == GS_SPRITE_CLASS || vt.m_primclass == GS_POINT_CLASS) && !force_enable_ps;
|
||||
|
||||
// Otherwise clamp in the pixel shader (performance note: may prevent early Z test);
|
||||
// Force clamping in the pixel shader is used when the pixels shader modifies Z for any other reason.
|
||||
// We bundle these together to avoid creating extra pipeline combinations.
|
||||
const bool clamp_ps = !clamp_vs && (large_z || force_enable_ps) && !cached_ctx.ZBUF.ZMSK;
|
||||
|
||||
if (clamp_vs)
|
||||
config.cb_vs.max_depth = GSVector2i(max_z);
|
||||
|
||||
if (clamp_ps)
|
||||
{
|
||||
config.cb_ps.TA_MaxDepth_Af.z = static_cast<float>(max_z) * 0x1p-32f;
|
||||
config.ps.zclamp = 1;
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
|
||||
{
|
||||
if (ds && m_cached_ctx.TEST.ZTE)
|
||||
@ -5130,27 +5157,7 @@ void GSRendererHW::EmulateZbuffer(const GSTextureCache::Target* ds)
|
||||
m_conf.depth.ztst = ZTST_ALWAYS;
|
||||
}
|
||||
|
||||
// On the real GS we appear to do clamping on the max z value the format allows.
|
||||
// Clamping is done after rasterization.
|
||||
const u32 max_z = 0xFFFFFFFF >> (GSLocalMemory::m_psm[m_cached_ctx.ZBUF.PSM].fmt * 8);
|
||||
const bool clamp_z = static_cast<u32>(GSVector4i(m_vt.m_max.p).z) > max_z;
|
||||
|
||||
m_conf.cb_vs.max_depth = GSVector2i(0xFFFFFFFF);
|
||||
//ps_cb.MaxDepth = GSVector4(0.0f, 0.0f, 0.0f, 1.0f);
|
||||
m_conf.ps.zclamp = 0;
|
||||
|
||||
if (clamp_z)
|
||||
{
|
||||
if (m_vt.m_primclass == GS_SPRITE_CLASS || m_vt.m_primclass == GS_POINT_CLASS)
|
||||
{
|
||||
m_conf.cb_vs.max_depth = GSVector2i(max_z);
|
||||
}
|
||||
else if (!m_cached_ctx.ZBUF.ZMSK)
|
||||
{
|
||||
m_conf.cb_ps.TA_MaxDepth_Af.z = static_cast<float>(max_z) * 0x1p-32f;
|
||||
m_conf.ps.zclamp = 1;
|
||||
}
|
||||
}
|
||||
GetZClampConfigVSPS(m_cached_ctx, m_vt, false, m_conf);
|
||||
}
|
||||
|
||||
void GSRendererHW::EmulateTextureShuffleAndFbmask(GSTextureCache::Target* rt, GSTextureCache::Source* tex)
|
||||
@ -7189,51 +7196,332 @@ bool GSRendererHW::CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextu
|
||||
return false;
|
||||
}
|
||||
|
||||
void GSRendererHW::EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2)
|
||||
void GSRendererHW::GetAlphaTestConfigPS(const u32 atst, const u8 aref, const bool invert_test, u32& ps_atst_out, float& aref_out)
|
||||
{
|
||||
static const u32 inverted_atst[] = {ATST_ALWAYS, ATST_NEVER, ATST_GEQUAL, ATST_GREATER, ATST_NOTEQUAL, ATST_LESS, ATST_LEQUAL, ATST_EQUAL};
|
||||
static const u32 inverted_atst[] = {
|
||||
ATST_ALWAYS,
|
||||
ATST_NEVER,
|
||||
ATST_GEQUAL,
|
||||
ATST_GREATER,
|
||||
ATST_NOTEQUAL,
|
||||
ATST_LESS,
|
||||
ATST_LEQUAL,
|
||||
ATST_EQUAL
|
||||
};
|
||||
|
||||
if (!m_cached_ctx.TEST.ATE)
|
||||
return;
|
||||
constexpr float small_val = 0x100p-23f;
|
||||
|
||||
// Check for pass 2, otherwise do pass 1.
|
||||
const int atst = pass_2 ? inverted_atst[m_cached_ctx.TEST.ATST] : m_cached_ctx.TEST.ATST;
|
||||
const float aref = static_cast<float>(m_cached_ctx.TEST.AREF);
|
||||
|
||||
switch (atst)
|
||||
switch (invert_test ? inverted_atst[atst] : atst)
|
||||
{
|
||||
case ATST_LESS:
|
||||
AREF = aref - 0.1f;
|
||||
ps.atst = 1;
|
||||
aref_out = static_cast<float>(aref) - small_val;
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_LEQUAL;
|
||||
break;
|
||||
case ATST_LEQUAL:
|
||||
AREF = aref - 0.1f + 1.0f;
|
||||
ps.atst = 1;
|
||||
aref_out = static_cast<float>(aref) - small_val + 1.0f;
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_LEQUAL;
|
||||
break;
|
||||
case ATST_GEQUAL:
|
||||
AREF = aref - 0.1f;
|
||||
ps.atst = 2;
|
||||
aref_out = static_cast<float>(aref) - small_val;
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_GEQUAL;
|
||||
break;
|
||||
case ATST_GREATER:
|
||||
AREF = aref - 0.1f + 1.0f;
|
||||
ps.atst = 2;
|
||||
aref_out = static_cast<float>(aref) - small_val + 1.0f;
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_GEQUAL;
|
||||
break;
|
||||
case ATST_EQUAL:
|
||||
AREF = aref;
|
||||
ps.atst = 3;
|
||||
aref_out = static_cast<float>(aref);
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_EQUAL;
|
||||
break;
|
||||
case ATST_NOTEQUAL:
|
||||
AREF = aref;
|
||||
ps.atst = 4;
|
||||
aref_out = static_cast<float>(aref);
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_NOTEQUAL;
|
||||
break;
|
||||
case ATST_NEVER: // Draw won't be done so no need to implement it in shader
|
||||
case ATST_NEVER:
|
||||
case ATST_ALWAYS:
|
||||
default:
|
||||
ps.atst = 0;
|
||||
ps_atst_out = GSHWDrawConfig::PS_ATST_NONE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GSRendererHW::GetAlphaTestConfig(
|
||||
// Inputs
|
||||
const HWCachedCtx& cached_ctx,
|
||||
const GSVertexTrace& vt,
|
||||
const PRIM_OVERLAP prim_overlap,
|
||||
const GIFRegALPHA& ALPHA,
|
||||
const GSDevice::FeatureSupport& features,
|
||||
// In/outputs
|
||||
GSHWDrawConfig& config, bool& DATE, bool& DATE_BARRIER, bool& DATE_one, bool& DATE_PRIMID)
|
||||
{
|
||||
if (!cached_ctx.TEST.ATE)
|
||||
return;
|
||||
|
||||
GL_PUSH("HW: Alpha test config");
|
||||
|
||||
// Temp pixel shader constants for the setup.
|
||||
u32 ps_atst;
|
||||
float ps_aref;
|
||||
|
||||
u32 atst = cached_ctx.TEST.ATST;
|
||||
u32 afail = cached_ctx.TEST.AFAIL;
|
||||
u8 aref = cached_ctx.TEST.AREF;
|
||||
const bool zwe = cached_ctx.DepthWrite();
|
||||
|
||||
// First make some simplifications.
|
||||
if (afail == AFAIL_RGB_ONLY && !config.colormask.wa)
|
||||
afail = AFAIL_FB_ONLY;
|
||||
|
||||
if (!zwe && !config.colormask.wrgba)
|
||||
atst = ATST_NEVER;
|
||||
|
||||
if ((afail == AFAIL_FB_ONLY && !zwe) ||
|
||||
(afail == AFAIL_RGB_ONLY && !config.colormask.wa && !zwe) ||
|
||||
(afail == AFAIL_ZB_ONLY && !config.colormask.wrgba))
|
||||
{
|
||||
// Failing alpha test is a NOP
|
||||
atst = ATST_ALWAYS;
|
||||
}
|
||||
|
||||
if ((afail == AFAIL_FB_ONLY && !config.colormask.wrgba) ||
|
||||
(afail == AFAIL_RGB_ONLY && (!(config.colormask.wrgba & 7))) ||
|
||||
(afail == AFAIL_ZB_ONLY && !zwe))
|
||||
{
|
||||
// Passing alpha test is a NOP
|
||||
afail = AFAIL_KEEP;
|
||||
}
|
||||
|
||||
GL_INS("Using: ATST = %s, AFAIL = %s", GSUtil::GetATSTName(atst), GSUtil::GetAFAILName(afail));
|
||||
|
||||
if (atst == ATST_NEVER)
|
||||
{
|
||||
GL_INS("Alpha test prevents color/depth write.");
|
||||
config.colormask.wrgba = 0;
|
||||
config.depth.zwe = 0;
|
||||
}
|
||||
|
||||
if (atst == ATST_ALWAYS)
|
||||
{
|
||||
GL_INS("Alpha test is a NOP.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (afail == AFAIL_KEEP)
|
||||
{
|
||||
// Accurate alpha test by discarding failing pixels.
|
||||
GL_INS("Shader AFAIL discard (accurate)");
|
||||
GetAlphaTestConfigPS(atst, aref, false, ps_atst, ps_aref);
|
||||
config.ps.atst = ps_atst;
|
||||
config.cb_ps.FogColor_AREF.a = ps_aref;
|
||||
config.ps.afail = AFAIL_KEEP;
|
||||
return;
|
||||
}
|
||||
|
||||
// If true, the result of the Z test and output Z does NOT depend on overlapping Z writes to the same pixel.
|
||||
const bool independent_z =
|
||||
(cached_ctx.TEST.ZTST == ZTST_GEQUAL && vt.m_eq.z) ||
|
||||
(cached_ctx.TEST.ZTST == ZTST_ALWAYS) ||
|
||||
!zwe ||
|
||||
(prim_overlap == PRIM_OVERLAP_NO);
|
||||
|
||||
// If true, the written RGB does NOT depend on overlapping alpha writes to the same pixel
|
||||
const bool independent_rgb =
|
||||
(ALPHA.C != ALPHA_C_AD) ||
|
||||
!config.colormask.wa ||
|
||||
(prim_overlap == PRIM_OVERLAP_NO);
|
||||
|
||||
// Flags to determine if we can achieve full accuracy with less passes.
|
||||
const bool simple_fb_only = (afail == AFAIL_FB_ONLY) && independent_z;
|
||||
const bool simple_rgb_only = (afail == AFAIL_RGB_ONLY) && independent_z && independent_rgb;
|
||||
const bool simple_zb_only = (afail == AFAIL_ZB_ONLY) && independent_z;
|
||||
|
||||
// Determine where RT and/or depth are needed for the feedback methods.
|
||||
const bool afail_needs_rt = (afail == AFAIL_ZB_ONLY) || (afail == AFAIL_RGB_ONLY);
|
||||
const bool afail_needs_depth = (afail == AFAIL_FB_ONLY) || ((afail == AFAIL_RGB_ONLY) && zwe);
|
||||
|
||||
// Determine whether the feedback methods require a single pass.
|
||||
const bool feedback_one_pass = simple_fb_only || simple_rgb_only || simple_zb_only;
|
||||
|
||||
// If we are already have the required barriers for the accurate feedback path.
|
||||
const bool already_have_barriers =
|
||||
((config.require_one_barrier && feedback_one_pass) || config.require_full_barrier) &&
|
||||
(features.texture_barrier || features.multidraw_fb_copy);
|
||||
|
||||
// This is to prevent us from using dual-source blend with FBfetch, which breaks Intel GPUs on Metal.
|
||||
// Setting afail to RGB_ONLY without enabling color1 will enable this mode in the shader.
|
||||
const bool rgb_only_fbfetch = simple_rgb_only && features.framebuffer_fetch;
|
||||
|
||||
if (GSConfig.HWAFAILFeedback || already_have_barriers || rgb_only_fbfetch)
|
||||
{
|
||||
// Use RT and/or depth sampling for accurate AFAIL in the shader.
|
||||
GL_INS("Alpha test with RT/depth feedback (accurate)");
|
||||
GetAlphaTestConfigPS(atst, aref, false, ps_atst, ps_aref);
|
||||
config.ps.atst = ps_atst;
|
||||
config.cb_ps.FogColor_AREF.a = ps_aref;
|
||||
config.ps.afail = afail;
|
||||
config.ps.color_feedback |= afail_needs_rt;
|
||||
config.ps.depth_feedback |= afail_needs_depth;
|
||||
|
||||
if (!features.framebuffer_fetch || afail_needs_depth)
|
||||
{
|
||||
// Only enable barriers if we either do not have FB-fetch or need depth feedback (there's no FB-fetch for depth).
|
||||
config.require_one_barrier |= feedback_one_pass;
|
||||
config.require_full_barrier |= !feedback_one_pass;
|
||||
}
|
||||
|
||||
// Handle SW depth writing and/or testing.
|
||||
if (afail_needs_depth && zwe)
|
||||
{
|
||||
GL_INS("Enable SW depth write for depth feedback");
|
||||
GetZClampConfigVSPS(cached_ctx, vt, true, config); // Z clamp is a proxy for SW Z write.
|
||||
|
||||
if (cached_ctx.DepthRead())
|
||||
{
|
||||
GL_INS("Enable SW depth testing for depth feedback");
|
||||
config.ps.ztst = cached_ctx.TEST.ZTST;
|
||||
config.depth.ztst = ZTST_ALWAYS;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// The following methods may need an alpha second pass.
|
||||
std::memcpy(&config.alpha_second_pass.ps, &config.ps, sizeof(config.ps));
|
||||
std::memcpy(&config.alpha_second_pass.colormask, &config.colormask, sizeof(config.colormask));
|
||||
std::memcpy(&config.alpha_second_pass.depth, &config.depth, sizeof(config.depth));
|
||||
|
||||
config.alpha_second_pass.colormask.wrgba = config.colormask.wrgba;
|
||||
config.alpha_second_pass.depth.zwe = zwe;
|
||||
|
||||
if (simple_fb_only || simple_zb_only || simple_rgb_only)
|
||||
{
|
||||
// In these cases we can do accurate AFAIL in one or two passes.
|
||||
|
||||
if (simple_fb_only)
|
||||
{
|
||||
// First pass is to update color; second pass is to update Z.
|
||||
GL_INS("Alpha test: RGBA then Z (accurate)");
|
||||
config.depth.zwe = false; // Disable Z write on first pass
|
||||
config.alpha_second_pass.colormask.wrgba = 0; // Disable color write on second pass
|
||||
}
|
||||
else if (simple_zb_only)
|
||||
{
|
||||
// First pass is to update Z; second pass is to update color.
|
||||
GL_INS("Alpha test: Z then RGBA (accurate)");
|
||||
config.colormask.wrgba = 0; // Disable color on first pass
|
||||
config.alpha_second_pass.depth.zwe = false; // Disable Z write on second pass
|
||||
}
|
||||
else if (simple_rgb_only)
|
||||
{
|
||||
// First pass is to update color; second pass is to update Z;
|
||||
GL_INS("Alpha test: RGBA (A with dual-source blend), then Z (accurate)");
|
||||
|
||||
config.depth.zwe = false; // Disable Z write on first pass
|
||||
config.alpha_second_pass.colormask.wrgba = 0; // Disable color write on second pass
|
||||
|
||||
// First pass must be set up to do AFAIL with dual source blend.
|
||||
GetAlphaTestConfigPS(atst, aref, false, ps_atst, ps_aref);
|
||||
config.ps.atst = ps_atst;
|
||||
config.cb_ps.FogColor_AREF.a = ps_aref;
|
||||
config.ps.no_color1 = false; // Tells shader to use dual source blending AFAIL.
|
||||
|
||||
if (!config.blend.enable)
|
||||
{
|
||||
config.blend = GSHWDrawConfig::BlendState(true, GSDevice::CONST_ONE, GSDevice::CONST_ZERO,
|
||||
GSDevice::OP_ADD, GSDevice::SRC1_ALPHA, GSDevice::INV_SRC1_ALPHA, false, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (config.blend_multi_pass.enable)
|
||||
{
|
||||
config.blend_multi_pass.blend.src_factor_alpha = GSDevice::SRC1_ALPHA;
|
||||
config.blend_multi_pass.blend.dst_factor_alpha = GSDevice::INV_SRC1_ALPHA;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.blend.src_factor_alpha = GSDevice::SRC1_ALPHA;
|
||||
config.blend.dst_factor_alpha = GSDevice::INV_SRC1_ALPHA;
|
||||
}
|
||||
}
|
||||
|
||||
// Swap stencil DATE for PrimID DATE, for both Z on and off cases.
|
||||
// Because we're making some pixels pass, but not updating A, the stencil won't be synced.
|
||||
if (DATE && !DATE_BARRIER && features.primitive_id)
|
||||
{
|
||||
if (!DATE_PRIMID)
|
||||
GL_INS("HW: Swap stencil DATE for PrimID, due to AFAIL");
|
||||
|
||||
DATE_one = false;
|
||||
DATE_PRIMID = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Enable alpha test and discard failing fragments on second pass.
|
||||
GetAlphaTestConfigPS(atst, aref, true, ps_atst, ps_aref);
|
||||
config.alpha_second_pass.ps.atst = ps_atst;
|
||||
config.alpha_second_pass.ps_aref = ps_aref;
|
||||
config.alpha_second_pass.ps.afail = AFAIL_KEEP;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Last ditch effort: use approximate pass/fail method.
|
||||
GL_INS("Alpha test with pass/fail (approximate)");
|
||||
|
||||
// Enable alpha test and discard failing fragments on first pass.
|
||||
GetAlphaTestConfigPS(atst, aref, true, ps_atst, ps_aref);
|
||||
config.ps.atst = ps_atst;
|
||||
config.cb_ps.FogColor_AREF.a = ps_aref;
|
||||
config.ps.afail = AFAIL_KEEP;
|
||||
|
||||
// Determine the buffer write mask for failing fragments on the seconds pass.
|
||||
if (afail == AFAIL_FB_ONLY)
|
||||
{
|
||||
config.alpha_second_pass.depth.zwe = false;
|
||||
}
|
||||
else if (afail == AFAIL_ZB_ONLY)
|
||||
{
|
||||
config.alpha_second_pass.colormask.wrgba = 0;
|
||||
}
|
||||
else if (afail == AFAIL_RGB_ONLY)
|
||||
{
|
||||
config.alpha_second_pass.colormask.wrgba = config.colormask.wrgba & 7;
|
||||
}
|
||||
|
||||
// Enable alpha test and discard passing fragments on second pass.
|
||||
GetAlphaTestConfigPS(atst, aref, true, ps_atst, ps_aref);
|
||||
config.alpha_second_pass.ps.atst = ps_atst;
|
||||
config.alpha_second_pass.ps_aref = ps_aref;
|
||||
config.alpha_second_pass.ps.afail = AFAIL_KEEP;
|
||||
}
|
||||
|
||||
// Degenerate cases with no writes on the first/second pass should have been optimized out earlier.
|
||||
pxAssert(config.colormask.wrgba || config.depth.zwe);
|
||||
if (config.alpha_second_pass.enable)
|
||||
{
|
||||
pxAssertRel(config.alpha_second_pass.colormask.wrgba || config.alpha_second_pass.depth.zwe,
|
||||
"Alpha second pass has no color/depth write.");
|
||||
}
|
||||
|
||||
// Some housekeeping for the second pass.
|
||||
if (config.alpha_second_pass.colormask.wrgba == 0)
|
||||
{
|
||||
config.alpha_second_pass.ps.DisableColorOutput();
|
||||
}
|
||||
if (config.alpha_second_pass.ps.IsFeedbackLoopRT() || config.alpha_second_pass.ps.IsFeedbackLoopDepth())
|
||||
{
|
||||
config.alpha_second_pass.require_one_barrier = config.require_one_barrier;
|
||||
config.alpha_second_pass.require_full_barrier = config.require_full_barrier;
|
||||
if (config.require_full_barrier)
|
||||
{
|
||||
// Should be unreachable as we should have used the accurate feedback path;
|
||||
Console.Warning("Using full barriers on alpha second pass.");
|
||||
GL_INS("Warning: Using full barriers on alpha second pass.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void GSRendererHW::CleanupDraw(bool invalidate_temp_src)
|
||||
{
|
||||
// Remove any RT source.
|
||||
@ -7716,132 +8004,8 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
rt->m_alpha_max = rt_new_alpha_max;
|
||||
rt->m_alpha_min = rt_new_alpha_min;
|
||||
}
|
||||
|
||||
// Alpha test afail configuration
|
||||
// Warning must be done after EmulateZbuffer
|
||||
bool ate_first_pass = m_cached_ctx.TEST.DoFirstPass();
|
||||
bool ate_second_pass = m_cached_ctx.TEST.DoSecondPass();
|
||||
|
||||
// Check if we should force a feedback loop for AFAIL
|
||||
if (ate_first_pass && ate_second_pass && GSConfig.HWAFAILFeedback &&
|
||||
(features.texture_barrier || features.multidraw_fb_copy))
|
||||
{
|
||||
const bool possible_zb_only = (m_cached_ctx.TEST.AFAIL == AFAIL_ZB_ONLY) && m_conf.depth.zwe;
|
||||
const bool possible_rgb_only = (m_cached_ctx.TEST.AFAIL == AFAIL_RGB_ONLY) && rt && m_conf.colormask.wa;
|
||||
const bool possible_fb_only = (m_cached_ctx.TEST.AFAIL == AFAIL_FB_ONLY) && rt && m_conf.colormask.wrgba;
|
||||
|
||||
const bool afail_needs_rt = possible_zb_only || possible_rgb_only;
|
||||
const bool afail_needs_depth = possible_fb_only || possible_rgb_only;
|
||||
|
||||
if (afail_needs_rt)
|
||||
{
|
||||
m_conf.ps.color_feedback = rt && m_conf.colormask.wrgba;
|
||||
ate_second_pass = false;
|
||||
m_conf.ps.afail = m_cached_ctx.TEST.AFAIL;
|
||||
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
|
||||
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
|
||||
}
|
||||
|
||||
if (afail_needs_depth)
|
||||
{
|
||||
m_conf.ps.depth_feedback = m_conf.depth.zwe && !m_cached_ctx.ZBUF.ZMSK;
|
||||
ate_second_pass = false;
|
||||
m_conf.ps.afail = m_cached_ctx.TEST.AFAIL;
|
||||
m_conf.require_one_barrier |= (m_prim_overlap == PRIM_OVERLAP_NO);
|
||||
m_conf.require_full_barrier |= (m_prim_overlap != PRIM_OVERLAP_NO);
|
||||
if (m_cached_ctx.TEST.ZTE && m_cached_ctx.TEST.ZTST == ZTST_GEQUAL || m_cached_ctx.TEST.ZTST == ZTST_GREATER)
|
||||
{
|
||||
// Enable SW depth testing and disable HW depth testing.
|
||||
m_conf.ps.ztst = m_cached_ctx.TEST.ZTST;
|
||||
m_conf.depth.ztst = ZTST_ALWAYS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ate_RGBA_then_Z = false;
|
||||
bool ate_RGB_then_Z = false;
|
||||
GL_INS("HW: %sAlpha Test, ATST=%s, AFAIL=%s", (ate_first_pass && ate_second_pass) ? "Complex" : "",
|
||||
GSUtil::GetATSTName(m_cached_ctx.TEST.ATST), GSUtil::GetAFAILName(m_cached_ctx.TEST.AFAIL));
|
||||
if (ate_first_pass && ate_second_pass)
|
||||
{
|
||||
const bool commutative_depth = (m_conf.depth.ztst == ZTST_GEQUAL && m_vt.m_eq.z) || (m_conf.depth.ztst == ZTST_ALWAYS) || !m_conf.depth.zwe;
|
||||
const bool commutative_alpha = (m_context->ALPHA.C != 1) || !m_conf.colormask.wa; // when either Alpha Src or a constant, or not updating A
|
||||
|
||||
ate_RGBA_then_Z = (afail_type == AFAIL_FB_ONLY) && commutative_depth;
|
||||
ate_RGB_then_Z = (afail_type == AFAIL_RGB_ONLY) && commutative_depth && commutative_alpha;
|
||||
}
|
||||
|
||||
if (ate_RGBA_then_Z)
|
||||
{
|
||||
GL_INS("HW: Alternate ATE handling: ate_RGBA_then_Z");
|
||||
// Render all color but don't update depth
|
||||
// ATE is disabled here
|
||||
m_conf.depth.zwe = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
float aref = m_conf.cb_ps.FogColor_AREF.a;
|
||||
EmulateATST(aref, m_conf.ps, false);
|
||||
|
||||
// avoid redundant cbuffer updates
|
||||
m_conf.cb_ps.FogColor_AREF.a = aref;
|
||||
m_conf.alpha_second_pass.ps_aref = aref;
|
||||
|
||||
if (ate_RGB_then_Z)
|
||||
{
|
||||
GL_INS("HW: Alternate ATE handling: ate_RGB_then_Z");
|
||||
|
||||
// Blending might be off, ensure it's enabled.
|
||||
// We write the alpha pass/fail to SRC1_ALPHA, which is used to update A.
|
||||
m_conf.ps.afail = AFAIL_RGB_ONLY;
|
||||
if ((features.framebuffer_fetch && m_conf.require_one_barrier) || m_conf.require_full_barrier)
|
||||
{
|
||||
// We're reading the rt anyways, use it for AFAIL
|
||||
// This ensures we don't attempt to use fbfetch + blend, which breaks Intel GPUs on Metal
|
||||
// Setting afail to RGB_ONLY without enabling color1 will enable this mode in the shader, so nothing more to do here.
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.ps.no_color1 = false;
|
||||
if (!m_conf.blend.enable)
|
||||
{
|
||||
m_conf.blend = GSHWDrawConfig::BlendState(true, GSDevice::CONST_ONE, GSDevice::CONST_ZERO,
|
||||
GSDevice::OP_ADD, GSDevice::SRC1_ALPHA, GSDevice::INV_SRC1_ALPHA, false, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_conf.blend_multi_pass.enable)
|
||||
{
|
||||
m_conf.blend_multi_pass.no_color1 = false;
|
||||
m_conf.blend_multi_pass.blend.src_factor_alpha = GSDevice::SRC1_ALPHA;
|
||||
m_conf.blend_multi_pass.blend.dst_factor_alpha = GSDevice::INV_SRC1_ALPHA;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.blend.src_factor_alpha = GSDevice::SRC1_ALPHA;
|
||||
m_conf.blend.dst_factor_alpha = GSDevice::INV_SRC1_ALPHA;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If Z writes are on, unfortunately we can't single pass it.
|
||||
// But we can write Z in the second pass instead.
|
||||
ate_RGBA_then_Z = m_conf.depth.zwe;
|
||||
ate_second_pass &= ate_RGBA_then_Z;
|
||||
m_conf.depth.zwe = false;
|
||||
|
||||
// Swap stencil DATE for PrimID DATE, for both Z on and off cases.
|
||||
// Because we're making some pixels pass, but not update A, the stencil won't be synced.
|
||||
if (DATE && !DATE_BARRIER && features.primitive_id)
|
||||
{
|
||||
if (!DATE_PRIMID)
|
||||
GL_INS("HW: Swap stencil DATE for PrimID, due to AFAIL");
|
||||
|
||||
DATE_one = false;
|
||||
DATE_PRIMID = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
GetAlphaTestConfig(m_cached_ctx, m_vt, m_prim_overlap, m_context->ALPHA, features, m_conf, DATE, DATE_BARRIER, DATE_one, DATE_PRIMID);
|
||||
|
||||
// No point outputting colours if we're just writing depth.
|
||||
// We might still need the framebuffer for DATE, though.
|
||||
@ -8040,9 +8204,10 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
if (features.framebuffer_fetch)
|
||||
{
|
||||
// Intel GPUs on Metal lock up if you try to use DSB and framebuffer fetch at once
|
||||
// We should never need to do that (since using framebuffer fetch means you should be able to do all blending in shader), but sometimes it slips through
|
||||
// We should never need to do that (since using framebuffer fetch means you should be able to do all blending in shader),
|
||||
// but sometimes it slips through
|
||||
if (m_conf.require_one_barrier || m_conf.require_full_barrier)
|
||||
pxAssert(!m_conf.blend.enable);
|
||||
pxAssert(!m_conf.blend.enable || m_conf.ps.no_color1);
|
||||
|
||||
if (!m_conf.ps.IsFeedbackLoopDepth())
|
||||
{
|
||||
@ -8084,90 +8249,6 @@ __ri void GSRendererHW::DrawPrims(GSTextureCache::Target* rt, GSTextureCache::Ta
|
||||
HandleProvokingVertexFirst();
|
||||
|
||||
SetupIA(rtscale, sx, sy, m_channel_shuffle_width != 0);
|
||||
|
||||
if (ate_second_pass)
|
||||
{
|
||||
pxAssert(!m_conf.ps.pabe);
|
||||
|
||||
std::memcpy(&m_conf.alpha_second_pass.ps, &m_conf.ps, sizeof(m_conf.ps));
|
||||
std::memcpy(&m_conf.alpha_second_pass.colormask, &m_conf.colormask, sizeof(m_conf.colormask));
|
||||
std::memcpy(&m_conf.alpha_second_pass.depth, &m_conf.depth, sizeof(m_conf.depth));
|
||||
|
||||
// Not doing single pass AFAIL.
|
||||
m_conf.alpha_second_pass.ps.afail = AFAIL_KEEP;
|
||||
|
||||
if (ate_RGBA_then_Z)
|
||||
{
|
||||
// Enable ATE as first pass to update the depth
|
||||
// of pixels that passed the alpha test
|
||||
EmulateATST(m_conf.alpha_second_pass.ps_aref, m_conf.alpha_second_pass.ps, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// second pass will process the pixels that failed
|
||||
// the alpha test
|
||||
EmulateATST(m_conf.alpha_second_pass.ps_aref, m_conf.alpha_second_pass.ps, true);
|
||||
}
|
||||
|
||||
bool z = m_conf.depth.zwe;
|
||||
bool r = m_conf.colormask.wr;
|
||||
bool g = m_conf.colormask.wg;
|
||||
bool b = m_conf.colormask.wb;
|
||||
bool a = m_conf.colormask.wa;
|
||||
switch (afail_type)
|
||||
{
|
||||
case AFAIL_KEEP: z = r = g = b = a = false; break; // none
|
||||
case AFAIL_FB_ONLY: z = false; break; // rgba
|
||||
case AFAIL_ZB_ONLY: r = g = b = a = false; break; // z
|
||||
case AFAIL_RGB_ONLY: z = a = false; break; // rgb
|
||||
default: ASSUME(0);
|
||||
}
|
||||
|
||||
// Depth test should be disabled when depth writes are masked and similarly, Alpha test must be disabled
|
||||
// when writes to all of the alpha bits in the Framebuffer are masked.
|
||||
if (ate_RGBA_then_Z)
|
||||
{
|
||||
z = !m_cached_ctx.ZBUF.ZMSK;
|
||||
r = g = b = a = false;
|
||||
}
|
||||
|
||||
m_conf.alpha_second_pass.enable = true;
|
||||
|
||||
if (z || r || g || b || a)
|
||||
{
|
||||
m_conf.alpha_second_pass.depth.zwe = z;
|
||||
m_conf.alpha_second_pass.colormask.wr = r;
|
||||
m_conf.alpha_second_pass.colormask.wg = g;
|
||||
m_conf.alpha_second_pass.colormask.wb = b;
|
||||
m_conf.alpha_second_pass.colormask.wa = a;
|
||||
if (m_conf.alpha_second_pass.colormask.wrgba == 0)
|
||||
{
|
||||
m_conf.alpha_second_pass.ps.DisableColorOutput();
|
||||
}
|
||||
if (m_conf.alpha_second_pass.ps.IsFeedbackLoopRT())
|
||||
{
|
||||
m_conf.alpha_second_pass.require_one_barrier = m_conf.require_one_barrier;
|
||||
m_conf.alpha_second_pass.require_full_barrier = m_conf.require_full_barrier;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_conf.alpha_second_pass.enable = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ate_first_pass)
|
||||
{
|
||||
if (!m_conf.alpha_second_pass.enable)
|
||||
return;
|
||||
|
||||
// RenderHW always renders first pass, replace first pass with second
|
||||
std::memcpy(&m_conf.ps, &m_conf.alpha_second_pass.ps, sizeof(m_conf.ps));
|
||||
std::memcpy(&m_conf.colormask, &m_conf.alpha_second_pass.colormask, sizeof(m_conf.colormask));
|
||||
std::memcpy(&m_conf.depth, &m_conf.alpha_second_pass.depth, sizeof(m_conf.depth));
|
||||
m_conf.cb_ps.FogColor_AREF.a = m_conf.alpha_second_pass.ps_aref;
|
||||
m_conf.alpha_second_pass.enable = false;
|
||||
}
|
||||
|
||||
if (!m_channel_shuffle_width)
|
||||
g_gs_device->RenderHW(m_conf);
|
||||
|
||||
@ -38,6 +38,31 @@ private:
|
||||
using OI_Ptr = bool(*)(GSRendererHW& r, GSTexture* rt, GSTexture* ds, GSTextureCache::Source* t); // OI - Before draw
|
||||
using MV_Ptr = bool(*)(GSRendererHW& r); // MV - Move
|
||||
|
||||
// We modify some of the context registers to optimize away unnecessary operations.
|
||||
// Instead of messing with the real context, we copy them and use those instead.
|
||||
struct HWCachedCtx
|
||||
{
|
||||
GIFRegTEX0 TEX0;
|
||||
GIFRegTEXA TEXA;
|
||||
GIFRegCLAMP CLAMP;
|
||||
GIFRegTEST TEST;
|
||||
GIFRegFRAME FRAME;
|
||||
GIFRegZBUF ZBUF;
|
||||
|
||||
__ri bool DepthRead() const { return TEST.ZTE && (TEST.ZTST == ZTST_GEQUAL || TEST.ZTST == ZTST_GREATER); }
|
||||
|
||||
__ri bool DepthWrite() const
|
||||
{
|
||||
if (TEST.ATE && TEST.ATST == ATST_NEVER &&
|
||||
TEST.AFAIL != AFAIL_ZB_ONLY) // alpha test, all pixels fail, z buffer is not updated
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ZBUF.ZMSK == 0 && TEST.ZTE != 0; // ZTE == 0 is bug on the real hardware, write is blocked then
|
||||
}
|
||||
};
|
||||
|
||||
// Require special argument
|
||||
bool OI_BlitFMV(GSTextureCache::Target* _rt, GSTextureCache::Source* t, const GSVector4i& r_draw);
|
||||
bool TryGSMemClear(bool no_rt, bool preserve_rt, bool invalidate_rt, u32 rt_end_bp, bool no_ds,
|
||||
@ -109,8 +134,18 @@ private:
|
||||
bool CanUseTexIsFB(const GSTextureCache::Target* rt, const GSTextureCache::Source* tex,
|
||||
const TextureMinMaxResult& tmm);
|
||||
|
||||
static void GetZClampConfigVSPS(const HWCachedCtx& cached_ctx, const GSVertexTrace& vt, const bool force_enable_ps, GSHWDrawConfig& config);
|
||||
void EmulateZbuffer(const GSTextureCache::Target* ds);
|
||||
void EmulateATST(float& AREF, GSHWDrawConfig::PSSelector& ps, bool pass_2);
|
||||
static void GetAlphaTestConfigPS(const u32 atst, const u8 aref, const bool invert_test, u32& ps_atst_out, float& aref_out);
|
||||
static void GetAlphaTestConfig(
|
||||
// Inputs
|
||||
const HWCachedCtx& cached_ctx,
|
||||
const GSVertexTrace& vt,
|
||||
const PRIM_OVERLAP prim_overlap,
|
||||
const GIFRegALPHA& ALPHA,
|
||||
const GSDevice::FeatureSupport& features,
|
||||
// In/outputs
|
||||
GSHWDrawConfig& config, bool& DATE, bool& DATE_BARRIER, bool& DATE_one, bool& DATE_PRIMID);
|
||||
|
||||
void SetTCOffset();
|
||||
bool NextDrawColClip() const;
|
||||
@ -137,31 +172,6 @@ private:
|
||||
bool IsUsingCsInBlend();
|
||||
bool IsUsingAsInBlend();
|
||||
|
||||
// We modify some of the context registers to optimize away unnecessary operations.
|
||||
// Instead of messing with the real context, we copy them and use those instead.
|
||||
struct HWCachedCtx
|
||||
{
|
||||
GIFRegTEX0 TEX0;
|
||||
GIFRegTEXA TEXA;
|
||||
GIFRegCLAMP CLAMP;
|
||||
GIFRegTEST TEST;
|
||||
GIFRegFRAME FRAME;
|
||||
GIFRegZBUF ZBUF;
|
||||
|
||||
__ri bool DepthRead() const { return TEST.ZTE && TEST.ZTST >= 2; }
|
||||
|
||||
__ri bool DepthWrite() const
|
||||
{
|
||||
if (TEST.ATE && TEST.ATST == ATST_NEVER &&
|
||||
TEST.AFAIL != AFAIL_ZB_ONLY) // alpha test, all pixels fail, z buffer is not updated
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ZBUF.ZMSK == 0 && TEST.ZTE != 0; // ZTE == 0 is bug on the real hardware, write is blocked then
|
||||
}
|
||||
};
|
||||
|
||||
// CRC Hacks
|
||||
bool IsBadFrame();
|
||||
GSC_Ptr m_gsc = nullptr;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user