HLSL Pixel Shader tweaks;

Fully declare stages, to avoid "error X4000: variable 'stages' used without having been completely initialized"
Add alphakill support to fixed function
Use macro-based alphakill declaration in template (same as in fixed function)
Updated comments to differentiate between texture stage and combiner stage
Rename st into ts (texture stage) where appropriate
Fixed a typo in Brdf macro (was s1, must be ts now)
Updated Reflect to use C0 (uppercase, instead of lowercase host c0)
Initialized stage to zero for use in the C0 macro, now used Reflect
Fixed type of CombinerOutputMapping to actualle be PS_COMBINEROUTPUT_OUTPUTMAPPING
Introduced and used varios PS_*_MASK defines
Added some comments on pixel shader verification and test-case discovery.
This commit is contained in:
PatrickvL 2021-04-14 13:13:40 +02:00
parent d46abef9a1
commit 061e38af6f
6 changed files with 149 additions and 99 deletions

View File

@ -61,6 +61,10 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
#if 0
// Compiler-defines/symbols which must be defined when their bit/value is set in the corresponding register :
// Generated by PixelShader.cpp::BuildShader()
// Data from X_D3DTSS_ALPHAKILL :
#define ALPHAKILL {false, false, false, false}
// Bits from PSCombinerCount (a.k.a. PSCombinerCountFlags) :
#define PS_COMBINERCOUNT 2
@ -74,10 +78,10 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
#define PS_COMPAREMODE_2(in) CM_LT(in.x) CM_LT(in.y) CM_LT(in.z) CM_LT(in.w)
#define PS_COMPAREMODE_3(in) CM_LT(in.x) CM_LT(in.y) CM_LT(in.z) CM_LT(in.w)
// Input texture register mappings for stage 1, 2 and 3 (stage 0 has no input-texture)
// Input texture register mappings for texture stage 1, 2 and 3 (stage 0 has no input-texture)
static const int PS_INPUTTEXTURE_[4] = { -1, 0, 0, 0 };
// Dot mappings for stage 1, 2 and 3 (stage 0 performs no dot product)
// Dot mappings for texture stage 1, 2 and 3 (stage 0 performs no dot product)
#define PS_DOTMAPPING_1 PS_DOTMAPPING_MINUS1_TO_1_D3D
#define PS_DOTMAPPING_2 PS_DOTMAPPING_MINUS1_TO_1_D3D
#define PS_DOTMAPPING_3 PS_DOTMAPPING_MINUS1_TO_1_D3D
@ -93,14 +97,14 @@ uniform const float FRONTFACE_FACTOR : register(c27); // Note : PSH_XBOX_CONSTA
// Second raw string :
R"DELIMITER(
// PS_COMBINERCOUNT_UNIQUE_C0 steers whether for C0 to use stage-specific constants c0_0 .. c0_7, or c0_0 for all stages
// PS_COMBINERCOUNT_UNIQUE_C0 steers whether for C0 to use combiner stage-specific constants c0_0 .. c0_7, or c0_0 for all stages
#ifdef PS_COMBINERCOUNT_UNIQUE_C0
#define C0 c0_[stage] // concatenate stage to form c0_0 .. c0_7
#else // PS_COMBINERCOUNT_SAME_C0
#define C0 c0_[0] // always resolve to c0_0
#endif
// PS_COMBINERCOUNT_UNIQUE_C1 steers whether for C1 to use stage-specific constants c1_0 .. c1_7, or c1_0 for all stages
// PS_COMBINERCOUNT_UNIQUE_C1 steers whether for C1 to use combiner stage-specific constants c1_0 .. c1_7, or c1_0 for all stages
#ifdef PS_COMBINERCOUNT_UNIQUE_C1
#define C1 c1_[stage] // concatenate stage to form c1_0 .. c1_7
#else // PS_COMBINERCOUNT_SAME_C1
@ -218,35 +222,38 @@ float m21(const float input)
// TODO : Generate sampler status?
sampler samplers[4] : register(s0);
// Generated alphakill contents are based on X_D3DTSS_ALPHAKILL (we avoid using a constant, to allow false's to be optimized away)
// bool alphakill[4] = {false, false, false, false}; // Generated by PixelShader.cpp::BuildShader()
// Declare alphakill as a variable (avoiding a constant, to allow false's to be optimized away) :
#ifndef ALPHAKILL
#define ALPHAKILL {false, false, false, false}
#endif
static bool alphakill[4] = ALPHAKILL;
// Actual texture sampling per stage (always uses the s sampling vector variable as input)
// Actual texture sampling per texture stage (ts), using the sampling vector (s) as input,
// abstracting away the specifics of accessing above sampler declarations (usefull for future Direct3D 10+ sampler arrays)
float4 Sample2D(int st, float3 s)
float4 Sample2D(int ts, float3 s)
{
float4 result = tex2D(samplers[st], s.xy); // Ignores s.z (and whatever it's set to, will be optimized away by the compiler, see [1] below)
if (alphakill[st])
float4 result = tex2D(samplers[ts], s.xy); // Ignores s.z (and whatever it's set to, will be optimized away by the compiler, see [1] below)
if (alphakill[ts])
if (result.a == 0)
discard;
return result;
}
float4 Sample3D(int st, float3 s)
float4 Sample3D(int ts, float3 s)
{
float4 result = tex3D(samplers[st], s.xyz);
if (alphakill[st])
float4 result = tex3D(samplers[ts], s.xyz);
if (alphakill[ts])
if (result.a == 0)
discard;
return result;
}
float4 Sample6F(int st, float3 s)
float4 Sample6F(int ts, float3 s)
{
float4 result = texCUBE(samplers[st], s.xyz);
if (alphakill[st])
float4 result = texCUBE(samplers[ts], s.xyz);
if (alphakill[ts])
if (result.a == 0)
discard;
@ -260,44 +267,44 @@ float4 Sample6F(int st, float3 s)
#define t3 t[3]
// Resolve a stage number via 'input texture (index) mapping' to it's corresponding output texture register (rgba?)
#define src(st) t[PS_INPUTTEXTURE_[st]]
#define src(ts) t[PS_INPUTTEXTURE_[ts]]
// Calculate the dot result for a given stage. Since any given stage is input-mapped to always be less than or equal the stage it appears in, this won't cause read-ahead issues
// Calculate the dot result for a given texture stage. Since any given stage is input-mapped to always be less than or equal the stage it appears in, this won't cause read-ahead issues
// Test case: BumpDemo demo
#define CalcDot(st) PS_DOTMAPPING_ ## st(src(st)); dot_[st] = dot(iT[st].xyz, dm)
#define CalcDot(ts) PS_DOTMAPPING_ ## ts(src(ts)); dot_[ts] = dot(iT[ts].xyz, dm)
// Addressing operations
#define Passthru(st) float4(saturate(iT[st].xyz), 1) // Clamps input texture coordinates to the range [0..1]
#define Brdf(st) float3(t[st-2].y, t[s1-1].y, t[st-2].x - t[st-1].x) // TODO : Complete 16 bit phi/sigma retrieval from float4 texture register. Perhaps use CalcHiLo?
#define Normal2(st) float3(dot_[st-1], dot_[st], 0) // Preceding and current stage dot result. Will be input for Sample2D.
#define Normal3(st) float3(dot_[st-2], dot_[st-1], dot_[st]) // Two preceding and current stage dot result.
#define Passthru(ts) float4(saturate(iT[ts].xyz), 1) // Clamps input texture coordinates to the range [0..1]
#define Brdf(ts) float3(t[ts-2].y, t[ts-1].y, t[ts-2].x - t[ts-1].x) // TODO : Complete 16 bit phi/sigma retrieval from float4 texture register. Perhaps use CalcHiLo?
#define Normal2(ts) float3(dot_[ts-1], dot_[ts], 0) // Preceding and current stage dot result. Will be input for Sample2D.
#define Normal3(ts) float3(dot_[ts-2], dot_[ts-1], dot_[ts]) // Two preceding and current stage dot result.
#define Eye float3(iT[1].w, iT[2].w, iT[3].w) // 4th (q) component of input texture coordinates 1, 2 and 3. Only used by texm3x3vspec/PS_TEXTUREMODES_DOT_RFLCT_SPEC, always at stage 3. TODO : Map iT[1/2/3] through PS_INPUTTEXTURE_[]?
#define Reflect(n, e) 2 * (dot(n, e) / dot(n, n)) * n - e // https://documentation.help/directx8_c/texm3x3vspec.htm
#define BumpEnv(st) float3(iT[st].x + (BEM[st].x * src(st).r) + (BEM[st].y * src(st).g), iT[st].y + (BEM[st].z * src(st).r) + (BEM[st].w * src(st).g), 0) // Will be input for Sample2D. TODO : Compact into a regular 2x2 maxtrix multiplication.
#define LSO(st) (LUM[st].x * src(st).b) + LUM[st].y // Uses PSH_XBOX_CONSTANT_LUM .x = D3DTSS_BUMPENVLSCALE .y = D3DTSS_BUMPENVLOFFSET
#define BumpEnv(ts) float3(iT[ts].x + (BEM[ts].x * src(ts).r) + (BEM[ts].y * src(ts).g), iT[ts].y + (BEM[ts].z * src(ts).r) + (BEM[ts].w * src(ts).g), 0) // Will be input for Sample2D. TODO : Compact into a regular 2x2 maxtrix multiplication.
#define LSO(ts) (LUM[ts].x * src(ts).b) + LUM[ts].y // Uses PSH_XBOX_CONSTANT_LUM .x = D3DTSS_BUMPENVLSCALE .y = D3DTSS_BUMPENVLOFFSET
// Implementations for all possible texture modes, with stage as argument (prefixed with valid stages and corresponding pixel shader 1.3 assembly texture addressing instructions)
// For ease of understanding, all follow this plan : Optional specifics, or dot calculation (some with normal selection) and sampling vector determination. All end by deriving a value and assigning this to the stage's texture register.
/*0123 tex */ #define PS_TEXTUREMODES_NONE(st) v = black; t[st] = v // Seems to work
/*0123 tex */ #define PS_TEXTUREMODES_PROJECT2D(st) s = iT[st].xyz; v = Sample2D(st, s); t[st] = v // Seems to work (are x/w and y/w implicit?) [1]
/*0123 tex */ #define PS_TEXTUREMODES_PROJECT3D(st) s = iT[st].xyz; v = Sample3D(st, s); t[st] = v // Seems to work (is z/w implicit?)
/*0123 tex */ #define PS_TEXTUREMODES_CUBEMAP(st) s = iT[st].xyz; v = Sample6F(st, s); t[st] = v // TODO : Test
/*0123 texcoord */ #define PS_TEXTUREMODES_PASSTHRU(st) v = Passthru(st); t[st] = v // Seems to work
/*0123 texkill */ #define PS_TEXTUREMODES_CLIPPLANE(st) PS_COMPAREMODE_ ## st(iT[st]); v = black; t[st] = v // Seems to work (setting black to texture register, in case it gets read)
/*-123 texbem */ #define PS_TEXTUREMODES_BUMPENVMAP(st) s = BumpEnv(st); v = Sample2D(st, s); t[st] = v // Seems to work
/*-123 texbeml */ #define PS_TEXTUREMODES_BUMPENVMAP_LUM(st) PS_TEXTUREMODES_BUMPENVMAP(st); v.rgb *= LSO(st); t[st] = v // TODO : Test
/*--23 texbrdf */ #define PS_TEXTUREMODES_BRDF(st) s = Brdf(st); v = Sample3D(st, s); t[st] = v // TODO : Test (t[st-2] is 16 bit eyePhi,eyeSigma; t[st-1] is lightPhi,lightSigma)
/*--23 texm3x2tex */ #define PS_TEXTUREMODES_DOT_ST(st) CalcDot(st); n = Normal2(st); s = n; v = Sample2D(st, s); t[st] = v // TODO : Test
/*--23 texm3x2depth */ #define PS_TEXTUREMODES_DOT_ZW(st) CalcDot(st); n = Normal2(st); if (n.y==0) v=1;else v = n.x / n.y; t[st] = v // TODO : Make depth-check use result of division, but how?
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(st) CalcDot(st); n = Normal3(st); s = n; v = Sample6F(st, s); t[st] = v // TODO : Test
/*---3 texm3x3vspec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(st) CalcDot(st); n = Normal3(st); s = Reflect(n, Eye); v = Sample6F(st, s); t[st] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_3D(st) CalcDot(st); n = Normal3(st); s = n; v = Sample3D(st, s); t[st] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_CUBE(st) CalcDot(st); n = Normal3(st); s = n; v = Sample6F(st, s); t[st] = v // TODO : Test
/*-123 texreg2ar */ #define PS_TEXTUREMODES_DPNDNT_AR(st) s = src(st).arg; v = Sample2D(st, s); t[st] = v // TODO : Test [1]
/*-123 texreg2bg */ #define PS_TEXTUREMODES_DPNDNT_GB(st) s = src(st).gba; v = Sample2D(st, s); t[st] = v // TODO : Test [1]
// TODO replace dm with dot_[st]? Confirm BumpDemo 'Cubemap only' modes
/*-12- texm3x2pad */ #define PS_TEXTUREMODES_DOTPRODUCT(st) CalcDot(st); v = float4(dm, 0); t[st] = v // TODO : Test all dot mapping (setting texture register, in case it gets read - test-case : BumpDemo)
/*---3 texm3x3spec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST(st) CalcDot(st); n = Normal3(st); s = Reflect(n, c0); v = Sample6F(st, s); t[st] = v // TODO : Test
/*0123 tex */ #define PS_TEXTUREMODES_NONE(ts) v = black; t[ts] = v // Seems to work
/*0123 tex */ #define PS_TEXTUREMODES_PROJECT2D(ts) s = iT[ts].xyz; v = Sample2D(ts, s); t[ts] = v // Seems to work (are x/w and y/w implicit?) [1]
/*0123 tex */ #define PS_TEXTUREMODES_PROJECT3D(ts) s = iT[ts].xyz; v = Sample3D(ts, s); t[ts] = v // Seems to work (is z/w implicit?)
/*0123 tex */ #define PS_TEXTUREMODES_CUBEMAP(ts) s = iT[ts].xyz; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*0123 texcoord */ #define PS_TEXTUREMODES_PASSTHRU(ts) v = Passthru(ts); t[ts] = v // Seems to work
/*0123 texkill */ #define PS_TEXTUREMODES_CLIPPLANE(ts) PS_COMPAREMODE_ ## ts(iT[ts]); v = black; t[ts] = v // Seems to work (setting black to texture register, in case it gets read)
/*-123 texbem */ #define PS_TEXTUREMODES_BUMPENVMAP(ts) s = BumpEnv(ts); v = Sample2D(ts, s); t[ts] = v // Seems to work
/*-123 texbeml */ #define PS_TEXTUREMODES_BUMPENVMAP_LUM(ts) PS_TEXTUREMODES_BUMPENVMAP(ts); v.rgb *= LSO(ts); t[ts] = v // TODO : Test
/*--23 texbrdf */ #define PS_TEXTUREMODES_BRDF(ts) s = Brdf(ts); v = Sample3D(ts, s); t[ts] = v // TODO : Test (t[ts-2] is 16 bit eyePhi,eyeSigma; t[ts-1] is lightPhi,lightSigma)
/*--23 texm3x2tex */ #define PS_TEXTUREMODES_DOT_ST(ts) CalcDot(ts); n = Normal2(ts); s = n; v = Sample2D(ts, s); t[ts] = v // TODO : Test
/*--23 texm3x2depth */ #define PS_TEXTUREMODES_DOT_ZW(ts) CalcDot(ts); n = Normal2(ts); if (n.y==0) v=1;else v = n.x / n.y; t[ts] = v // TODO : Make depth-check use result of division, but how?
/*--2- texm3x3diff */ #define PS_TEXTUREMODES_DOT_RFLCT_DIFF(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3vspec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC(ts) CalcDot(ts); n = Normal3(ts); s = Reflect(n, Eye); v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_3D(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample3D(ts, s); t[ts] = v // TODO : Test
/*---3 texm3x3tex */ #define PS_TEXTUREMODES_DOT_STR_CUBE(ts) CalcDot(ts); n = Normal3(ts); s = n; v = Sample6F(ts, s); t[ts] = v // TODO : Test
/*-123 texreg2ar */ #define PS_TEXTUREMODES_DPNDNT_AR(ts) s = src(ts).arg; v = Sample2D(ts, s); t[ts] = v // TODO : Test [1]
/*-123 texreg2bg */ #define PS_TEXTUREMODES_DPNDNT_GB(ts) s = src(ts).gba; v = Sample2D(ts, s); t[ts] = v // TODO : Test [1]
// TODO replace dm with dot_[ts]? Confirm BumpDemo 'Cubemap only' modes
/*-12- texm3x2pad */ #define PS_TEXTUREMODES_DOTPRODUCT(ts) CalcDot(ts); v = float4(dm, 0); t[ts] = v // TODO : Test all dot mapping (setting texture register, in case it gets read - test-case : BumpDemo)
/*---3 texm3x3spec */ #define PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST(ts) CalcDot(ts); n = Normal3(ts); s = Reflect(n, C0); v = Sample6F(ts, s); t[ts] = v // TODO : Test
// [1] Note : 3rd component set to s.z is just an (ignored) placeholder to produce a float3 (made unique, to avoid the potential complexity of repeated components)
PS_OUTPUT main(const PS_INPUT xIn)
@ -318,7 +325,7 @@ PS_OUTPUT main(const PS_INPUT xIn)
float4 sum, prod; // Special purpose registers for xfc (final combiner) operation
// Helper variables
int stage; // Write-only variable, generated prefixing each 'opcode', for use in C0 and C1 macro's (and should thus get optimized away)
int stage = 0; // Write-only variable, emitted as prefix-comment before each 'opcode', used in C0 and C1 macro's (and should thus get optimized away), initialized to zero for use of C0 in PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST
float4 tmp;
float H, L; // HILO (high/low) temps
float dot_[4];

View File

@ -183,6 +183,12 @@ TextureArgs ExecuteTextureStage(
else if (type == SAMPLE_CUBE)
t = texCUBE(samplers[i], TexCoords[i].xyz + offset.xyz);
#ifdef ENABLE_FF_ALPHAKILL
if (stage.ALPHAKILL)
if (t.a == 0)
discard;
#endif
// Assign the final value for TEXTURE
ctx.TEXTURE = t * factor;

View File

@ -87,7 +87,11 @@ namespace FixedFunctionPixelShader {
alignas(16) float COLORKEYOP; // Unimplemented Xbox extension!
alignas(16) float COLORSIGN; // Unimplemented Xbox extension!
#ifdef ENABLE_FF_ALPHAKILL
alignas(16) float ALPHAKILL; // Xbox extension!
#else
alignas(16) float ALPHAKILL; // Unimplemented Xbox extension!
#endif
// TEXTURETRANSFORMFLAGS handled by the VS
alignas(16) float BUMPENVMAT00;
alignas(16) float BUMPENVMAT01;

View File

@ -191,7 +191,7 @@ void CombinerStageHlsl(std::stringstream& hlsl, RPSCombinerStageChannel& stage,
"d_bd2" // y = (x - 0.5) / 2 // PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTRIGHT_1_BIAS= 0x38L // Subtracts 0.5 from outputs and divides by 2
};
std::string output_modifier = output_modifier_str[(stage.CombinerOutputMapping & 0x38) >> 3];
std::string output_modifier = output_modifier_str[stage.CombinerOutputMapping >> 3];
// Concatenate it all together into an opcode 'call' (which resolves into macro expressions)
hlsl << opcode_comment[opcode][0] << '(' << arguments.str() << ' ' << output_modifier;
@ -296,11 +296,11 @@ void BuildShader(DecodedRegisterCombiner* pShader, std::stringstream& hlsl)
hlsl << hlsl_template[0]; // Start with the HLSL template header
hlsl << "\nstatic bool alphakill[4] = {"
hlsl << "\n#define ALPHAKILL {"
<< (pShader->AlphaKill[0] ? "true, " : "false, ")
<< (pShader->AlphaKill[1] ? "true, " : "false, ")
<< (pShader->AlphaKill[2] ? "true, " : "false, ")
<< (pShader->AlphaKill[3] ? "true};" : "false};");
<< (pShader->AlphaKill[3] ? "true}" : "false}");
hlsl << "\n#define PS_COMBINERCOUNT " << pShader->NumberOfCombiners;
if (pShader->NumberOfCombiners > 0) {

View File

@ -102,9 +102,9 @@ extern XboxTextureStateConverter XboxTextureStates; // Declared in Direct3D9.cpp
void RPSRegisterObject::Decode(uint8_t Value)
{
Reg = (PS_REGISTER)(Value & PS_REGISTER_EF_PROD); // = mask = 0x0f
Reg = (PS_REGISTER)(Value & PS_REGISTER_MASK); // = 0x0f
// Validate correctness
// Validate correctness (see NOTE below)
if (Reg == 6) LOG_TEST_CASE("Unknown PS_REGISTER : 6");
if (Reg == 7) LOG_TEST_CASE("Unknown PS_REGISTER : 7");
}
@ -115,8 +115,8 @@ void RPSInputRegister::Decode(uint8_t Value, unsigned stage_nr, bool isRGB)
{
RPSRegisterObject::Decode(Value);
Channel = (PS_CHANNEL)(Value & PS_CHANNEL_ALPHA); // = mask = 0x10
InputMapping = (PS_INPUTMAPPING)(Value & PS_INPUTMAPPING_SIGNED_NEGATE); // = mask = 0xe0
Channel = (PS_CHANNEL)(Value & PS_CHANNEL_MASK); // = 0x10
InputMapping = (PS_INPUTMAPPING)(Value & PS_INPUTMAPPING_MASK); // = 0xe0
if (stage_nr == 9) {
// In final combiner stage, convert C0 into FC0, and C1 into FC1, to discern them as separate registers
@ -124,7 +124,7 @@ void RPSInputRegister::Decode(uint8_t Value, unsigned stage_nr, bool isRGB)
if (Reg == PS_REGISTER_C1) Reg = PS_REGISTER_FC1;
}
// Validate correctness
// Validate correctness (see NOTE below)
if (stage_nr <= xbox::X_PSH_COMBINECOUNT) {
if (Reg == PS_REGISTER_FOG) {
if (!isRGB) LOG_TEST_CASE("PS_REGISTER_FOG input not allowed in Alpha register combiner");
@ -150,10 +150,10 @@ void RPSCombinerOutput::Decode(uint8_t Value, uint16_t PSInputs, unsigned stage_
RPSRegisterObject::Decode(Value);
// Decode PSAlphaInputs / PSRGBInputs :
Input[0].Decode((PSInputs >> 8) & 0xFF, stage_nr, isRGB);
Input[1].Decode((PSInputs >> 0) & 0xFF, stage_nr, isRGB);
Input[0].Decode((uint8_t)(PSInputs >> 8), stage_nr, isRGB);
Input[1].Decode((uint8_t)(PSInputs >> 0), stage_nr, isRGB);
// Validate correctness
// Validate correctness (see NOTE below)
if (Reg == PS_REGISTER_C0) LOG_TEST_CASE("PS_REGISTER_C0 not allowed as output");
if (Reg == PS_REGISTER_C1) LOG_TEST_CASE("PS_REGISTER_C1 not allowed as output");
if (Reg == PS_REGISTER_FOG) LOG_TEST_CASE("PS_REGISTER_FOG not allowed as output");
@ -166,9 +166,9 @@ void RPSCombinerOutput::Decode(uint8_t Value, uint16_t PSInputs, unsigned stage_
void RPSCombinerStageChannel::Decode(uint32_t PSInputs, uint32_t PSOutputs, unsigned stage_nr, bool isRGB)
{
// Decode PSAlphaOutputs / PSRGBOutputs and PSAlphaInputs / PSRGBInputs :
OutputCD.Decode((PSOutputs >> 0) & 0xF, (PSInputs >> 0 ) & 0xFFFF, stage_nr, isRGB);
OutputAB.Decode((PSOutputs >> 4) & 0xF, (PSInputs >> 16) & 0xFFFF, stage_nr, isRGB);
OutputMUX_SUM.Decode((PSOutputs >> 8) & 0xF);
OutputCD.Decode((uint8_t)(PSOutputs >> 0), (uint16_t)(PSInputs >> 0 ), stage_nr, isRGB);
OutputAB.Decode((uint8_t)(PSOutputs >> 4), (uint16_t)(PSInputs >> 16), stage_nr, isRGB);
OutputMUX_SUM.Decode((uint8_t)(PSOutputs >> 8));
// Get the combiner output flags :
PS_COMBINEROUTPUT CombinerOutputFlags = (PS_COMBINEROUTPUT)(PSOutputs >> 12);
@ -177,11 +177,11 @@ void RPSCombinerStageChannel::Decode(uint32_t PSInputs, uint32_t PSOutputs, unsi
OutputCD.DotProduct = (CombinerOutputFlags & PS_COMBINEROUTPUT_CD_DOT_PRODUCT) > 0; // False=Multiply, True=DotProduct
OutputAB.DotProduct = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_DOT_PRODUCT) > 0; // False=Multiply, True=DotProduct
AB_CD_MUX = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_CD_MUX) > 0; // False=AB+CD, True=MUX(AB,CD) based on R0.a
CombinerOutputMapping = (PS_COMBINEROUTPUT)(CombinerOutputFlags & PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTRIGHT_1_BIAS); // = mask = 0x38
CombinerOutputMapping = (PS_COMBINEROUTPUT_OUTPUTMAPPING)(CombinerOutputFlags & PS_COMBINEROUTPUT_OUTPUTMAPPING_MASK); // = 0x38
OutputCD.BlueToAlpha = (CombinerOutputFlags & PS_COMBINEROUTPUT_CD_BLUE_TO_ALPHA) >> 6; // 0=Alpha-to-Alpha, 1=Blue-to-Alpha
OutputAB.BlueToAlpha = (CombinerOutputFlags & PS_COMBINEROUTPUT_AB_BLUE_TO_ALPHA) >> 7; // 0=Alpha-to-Alpha, 1=Blue-to-Alpha
// Discover test-cases
// Discover test-cases (see TODO below)
// Check for 'discard-all-outputs'
if (OutputAB.DotProduct || OutputCD.DotProduct) {
if ((OutputAB.Reg == PS_REGISTER_DISCARD) && (OutputCD.Reg == PS_REGISTER_DISCARD)) LOG_TEST_CASE("All two outputs discarded");
@ -189,7 +189,7 @@ void RPSCombinerStageChannel::Decode(uint32_t PSInputs, uint32_t PSOutputs, unsi
// if ((OutputAB.Reg == PS_REGISTER_DISCARD) && (OutputCD.Reg == PS_REGISTER_DISCARD) && (OutputMUX_SUM.Reg == PS_REGISTER_DISCARD)) LOG_TEST_CASE("All three outputs discarded"); // Test-case : XDK sample : Minnaert (on Stage2.Alpha)
}
// Validate correctness
// Validate correctness (see NOTE below)
if ((PSOutputs & ~0x000FFFFF) > 0) LOG_TEST_CASE("Unknown PS_COMBINEROUTPUT flag bits detected");
if (CombinerOutputMapping == PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTLEFT_2_BIAS) LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTLEFT_2_BIAS unsupported on NV2A?");
if (CombinerOutputMapping == PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTRIGHT_1_BIAS) LOG_TEST_CASE("PS_COMBINEROUTPUT_SHIFTRIGHT_1_BIAS unsupported on NV2A?");
@ -211,27 +211,27 @@ void RPSCombinerStageChannel::Decode(uint32_t PSInputs, uint32_t PSOutputs, unsi
void RPSFinalCombiner::Decode(const uint32_t PSFinalCombinerInputsABCD, const uint32_t PSFinalCombinerInputsEFG)
{
Input[0].Decode((PSFinalCombinerInputsABCD >> 24) & 0xFF, /*stage_nr=*/9, /*isRGB=*/true);
Input[1].Decode((PSFinalCombinerInputsABCD >> 16) & 0xFF, /*stage_nr=*/9, /*isRGB=*/true);
Input[2].Decode((PSFinalCombinerInputsABCD >> 8) & 0xFF, /*stage_nr=*/9, /*isRGB=*/true);
Input[3].Decode((PSFinalCombinerInputsABCD >> 0) & 0xFF, /*stage_nr=*/9, /*isRGB=*/true);
Input[4].Decode((PSFinalCombinerInputsEFG >> 24) & 0xFF, /*stage_nr=*/9, /*isRGB=*/true);
Input[5].Decode((PSFinalCombinerInputsEFG >> 16) & 0xFF, /*stage_nr=*/9, /*isRGB=*/true);
Input[6].Decode((PSFinalCombinerInputsEFG >> 8) & 0xFF, /*stage_nr=*/9, /*isRGB=*/false); // Note : Final combiner input G must be a single component, and must thus be decoded as Alpha
Input[0].Decode((uint8_t)(PSFinalCombinerInputsABCD >> 24), /*stage_nr=*/9, /*isRGB=*/true);
Input[1].Decode((uint8_t)(PSFinalCombinerInputsABCD >> 16), /*stage_nr=*/9, /*isRGB=*/true);
Input[2].Decode((uint8_t)(PSFinalCombinerInputsABCD >> 8), /*stage_nr=*/9, /*isRGB=*/true);
Input[3].Decode((uint8_t)(PSFinalCombinerInputsABCD >> 0), /*stage_nr=*/9, /*isRGB=*/true);
Input[4].Decode((uint8_t)(PSFinalCombinerInputsEFG >> 24), /*stage_nr=*/9, /*isRGB=*/true);
Input[5].Decode((uint8_t)(PSFinalCombinerInputsEFG >> 16), /*stage_nr=*/9, /*isRGB=*/true);
Input[6].Decode((uint8_t)(PSFinalCombinerInputsEFG >> 8), /*stage_nr=*/9, /*isRGB=*/false); // Note : Final combiner input G must be a single component, and must thus be decoded as Alpha
PS_FINALCOMBINERSETTING FinalCombinerSettingFlags = (PS_FINALCOMBINERSETTING)((PSFinalCombinerInputsEFG >> 0) & 0xFF);
ComplementV1 = FinalCombinerSettingFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_V1;
ComplementR0 = FinalCombinerSettingFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0;
ClampSum = FinalCombinerSettingFlags & PS_FINALCOMBINERSETTING_CLAMP_SUM;
ComplementV1 = (FinalCombinerSettingFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_V1) > 0;
ComplementR0 = (FinalCombinerSettingFlags & PS_FINALCOMBINERSETTING_COMPLEMENT_R0) > 0;
ClampSum = (FinalCombinerSettingFlags & PS_FINALCOMBINERSETTING_CLAMP_SUM) > 0;
// Discover test-cases
// Discover test-cases (see TODO below)
// if (Input[0].Channel != PS_CHANNEL_ALPHA) LOG_TEST_CASE("PS_CHANNEL_RGB/PS_CHANNEL_BLUE detected on final combiner A input"); // Note : test-case ModifyPixelShader uses PS_REGISTER_FOG.rgb and seems to expect .rgb handling (not PS_CHANNEL_BLUE's .b)
if (Input[4].Channel == PS_CHANNEL_ALPHA) LOG_TEST_CASE("PS_CHANNEL_ALPHA detected on final combiner E input"); // Need test-case to determine how this should behave (calculating EF_PROD) : .aaa instead of .rgb?
if (Input[5].Channel == PS_CHANNEL_ALPHA) LOG_TEST_CASE("PS_CHANNEL_ALPHA detected on final combiner F input"); // Need test-case to determine how this should behave (calculating EF_PROD) : .aaa instead of .rgb?
// if (Input[6].Channel == PS_CHANNEL_BLUE) LOG_TEST_CASE("PS_CHANNEL_ALPHA detected on final combiner G input"); // PS_CHANNEL_BLUE (==0==PS_CHANNEL_RGB) uses G.b
// if (Input[6].Channel == PS_CHANNEL_ALPHA) LOG_TEST_CASE("PS_CHANNEL_ALPHA detected on final combiner G input"); // PS_CHANNEL_ALPHA (==1) uses .a Test-case : XDK samples BumpDemo,BumpEarth,BumpLens,Explosion
// Validate correctness
// Validate correctness (see NOTE below)
if ((FinalCombinerSettingFlags & ~0xE0) > 0) LOG_TEST_CASE("Unknown FinalCombinerSetting bits detected");
}
@ -240,9 +240,9 @@ void RPSFinalCombiner::Decode(const uint32_t PSFinalCombinerInputsABCD, const ui
void DecodedRegisterCombiner::GetPSTextureModes(xbox::X_D3DPIXELSHADERDEF* pPSDef, PS_TEXTUREMODES psTextureModes[xbox::X_D3DTS_STAGECOUNT])
{
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
psTextureModes[i] = (PS_TEXTUREMODES)((pPSDef->PSTextureModes >> (i * 5)) & 0x1F);
psTextureModes[i] = (PS_TEXTUREMODES)((pPSDef->PSTextureModes >> (i * 5)) & PS_TEXTUREMODES_MASK);
// Discover test-cases
// Discover test-cases (see TODO below)
// if (psTextureModes[i] == PS_TEXTUREMODES_NONE) LOG_TEST_CASE("PS_TEXTUREMODES_NONE");
// if (psTextureModes[i] == PS_TEXTUREMODES_PROJECT2D) LOG_TEST_CASE("PS_TEXTUREMODES_PROJECT2D");
if (psTextureModes[i] == PS_TEXTUREMODES_PROJECT3D) LOG_TEST_CASE("PS_TEXTUREMODES_PROJECT3D"); // Test-case: XDK sample TechCertGame,NoSortAlphaBlend,VolumeLight
@ -263,7 +263,7 @@ void DecodedRegisterCombiner::GetPSTextureModes(xbox::X_D3DPIXELSHADERDEF* pPSDe
if (psTextureModes[i] == PS_TEXTUREMODES_DOTPRODUCT) LOG_TEST_CASE("PS_TEXTUREMODES_DOTPRODUCT");
if (psTextureModes[i] == PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST) LOG_TEST_CASE("PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST");
// Validate correctness
// Validate correctness (see NOTE below)
if (psTextureModes[i] == PS_TEXTUREMODES_BUMPENVMAP) if (i < 1) LOG_TEST_CASE("PS_TEXTUREMODES_BUMPENVMAP only allowed in stage 1, 2 or 3");
if (psTextureModes[i] == PS_TEXTUREMODES_BUMPENVMAP_LUM) if (i < 1) LOG_TEST_CASE("PS_TEXTUREMODES_BUMPENVMAP_LUM only allowed in stage 1, 2 or 3");
if (psTextureModes[i] == PS_TEXTUREMODES_BRDF) if (i < 2) LOG_TEST_CASE("PS_TEXTUREMODES_BRDF only allowed in stage 2 or 3");
@ -286,7 +286,7 @@ void DecodedRegisterCombiner::GetPSTextureModes(xbox::X_D3DPIXELSHADERDEF* pPSDe
if (psTextureModes[i] > PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST) LOG_TEST_CASE("Invalid PS_TEXTUREMODES in stage?");
}
// Validate correctness
// Validate correctness (see NOTE below)
if ((pPSDef->PSTextureModes & ~0x000FFFFF) > 0) LOG_TEST_CASE("Unknown PSTextureModes bits detected");
}
@ -294,9 +294,9 @@ void DecodedRegisterCombiner::GetPSDotMapping(xbox::X_D3DPIXELSHADERDEF* pPSDef,
{
psDotMapping[0] = (PS_DOTMAPPING)(0);
for (int i = 1; i < xbox::X_D3DTS_STAGECOUNT; i++) {
psDotMapping[i] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> ((i - 1) * 4)) & 0x7);
psDotMapping[i] = (PS_DOTMAPPING)((pPSDef->PSDotMapping >> ((i - 1) * 4)) & PS_DOTMAPPING_MASK);
// Discover test-cases
// Discover test-cases (see TODO below)
// if (psDotMapping[i] == PS_DOTMAPPING_ZERO_TO_ONE) LOG_TEST_CASE("PS_DOTMAPPING_ZERO_TO_ONE"); // Note : Most common scenario, no need for test-cases
if (psDotMapping[i] == PS_DOTMAPPING_MINUS1_TO_1_D3D) LOG_TEST_CASE("PS_DOTMAPPING_MINUS1_TO_1_D3D"); // Test-case : XDK samples BumpDemo, Minnaert
if (psDotMapping[i] == PS_DOTMAPPING_MINUS1_TO_1_GL) LOG_TEST_CASE("PS_DOTMAPPING_MINUS1_TO_1_GL");
@ -307,22 +307,22 @@ void DecodedRegisterCombiner::GetPSDotMapping(xbox::X_D3DPIXELSHADERDEF* pPSDef,
if (psDotMapping[i] == PS_DOTMAPPING_HILO_HEMISPHERE) LOG_TEST_CASE("PS_DOTMAPPING_HILO_HEMISPHERE");
}
// Validate correctness
if ((pPSDef->PSDotMapping & ~0x00000777) > 0) LOG_TEST_CASE("Unknown PSDotMapping bits detected");
// Validate correctness (see NOTE below)
if ((pPSDef->PSDotMapping & ~0x00000777) > 0) LOG_TEST_CASE("Unknown PSDotMapping bits detected");
}
void DecodedRegisterCombiner::GetPSCompareModes(xbox::X_D3DPIXELSHADERDEF* pPSDef, bool psCompareModes[xbox::X_D3DTS_STAGECOUNT][4])
{
for (int i = 0; i < xbox::X_D3DTS_STAGECOUNT; i++) {
uint32_t CompareMode = (pPSDef->PSCompareMode >> (i * 4)) & 0xF;
uint32_t CompareMode = (pPSDef->PSCompareMode >> (i * 4)) & PS_COMPAREMODE_MASK;
psCompareModes[i][0] = (CompareMode & PS_COMPAREMODE_S_GE) > 0;
psCompareModes[i][1] = (CompareMode & PS_COMPAREMODE_T_GE) > 0;
psCompareModes[i][2] = (CompareMode & PS_COMPAREMODE_R_GE) > 0;
psCompareModes[i][3] = (CompareMode & PS_COMPAREMODE_Q_GE) > 0;
}
// Validate correctness
if ((pPSDef->PSCompareMode & ~0x0000FFFF) > 0) LOG_TEST_CASE("Unknown PSCompareMode bits detected");
// Validate correctness (see NOTE below)
if ((pPSDef->PSCompareMode & ~0x0000FFFF) > 0) LOG_TEST_CASE("Unknown PSCompareMode bits detected");
}
void DecodedRegisterCombiner::GetPSInputTexture(xbox::X_D3DPIXELSHADERDEF* pPSDef, int psInputTexture[xbox::X_D3DTS_STAGECOUNT])
@ -332,21 +332,22 @@ void DecodedRegisterCombiner::GetPSInputTexture(xbox::X_D3DPIXELSHADERDEF* pPSDe
psInputTexture[2] = (pPSDef->PSInputTexture >> 16) & 0x1; // Stage 2 can use stage 0 or 1
psInputTexture[3] = (pPSDef->PSInputTexture >> 20) & 0x3; // Stage 3 can only use stage 0, 1 or 2
// Discover test-cases
// Discover test-cases (see TODO below)
if (psInputTexture[2] == 0) LOG_TEST_CASE("PS_INPUTTEXTURE(2) uses texture 0");
// if (psInputTexture[2] == 1) LOG_TEST_CASE("PS_INPUTTEXTURE(2) uses texture 1"); // Test-case : XDK sample BumpEarth,Explosion,ZSprite
if (psInputTexture[2] == 2) LOG_TEST_CASE("PS_INPUTTEXTURE(2) uses texture 2");
if (psInputTexture[3] == 0) LOG_TEST_CASE("PS_INPUTTEXTURE(3) uses texture 0");
// if (psInputTexture[3] == 1) LOG_TEST_CASE("PS_INPUTTEXTURE(3) uses texture 1"); // Test-case : XDK sample Explosion,ZSprite
if (psInputTexture[3] == 2) LOG_TEST_CASE("PS_INPUTTEXTURE(3) uses texture 2");
if (psInputTexture[3] == 3) LOG_TEST_CASE("PS_INPUTTEXTURE(3) uses texture 3");
// Validate correctness
if ((pPSDef->PSInputTexture & ~0x00310000) > 0) LOG_TEST_CASE("Unknown PSInputTexture bits detected");
// Validate correctness (see NOTE below)
if (psInputTexture[3] == 3) LOG_TEST_CASE("PS_INPUTTEXTURE(3) incorrectly uses texture 3");
if ((pPSDef->PSInputTexture & ~0x00310000) > 0) LOG_TEST_CASE("Unknown PSInputTexture bits detected");
}
void DecodedRegisterCombiner::Decode(xbox::X_D3DPIXELSHADERDEF *pPSDef)
{
NumberOfCombiners = (pPSDef->PSCombinerCount >> 0) & 0xF;
uint32_t CombinerCountFlags = (pPSDef->PSCombinerCount >> 8);
uint32_t CombinerCountFlags = (pPSDef->PSCombinerCount >> 8); // = PS_COMBINERCOUNTFLAGS
CombinerMuxesOnMsb = (CombinerCountFlags & PS_COMBINERCOUNT_MUX_MSB) > 0;
CombinerHasUniqueC0 = (CombinerCountFlags & PS_COMBINERCOUNT_UNIQUE_C0) > 0;
@ -368,18 +369,21 @@ void DecodedRegisterCombiner::Decode(xbox::X_D3DPIXELSHADERDEF *pPSDef)
FinalCombiner.Decode(pPSDef->PSFinalCombinerInputsABCD, pPSDef->PSFinalCombinerInputsEFG);
}
TexModeAdjust = (pPSDef->PSFinalCombinerConstants >> 8) & PS_GLOBALFLAGS_TEXMODE_ADJUST;
TexModeAdjust = ((pPSDef->PSFinalCombinerConstants >> PS_GLOBALFLAGS_SHIFT) & PS_GLOBALFLAGS_TEXMODE_ADJUST) > 0;
// Discover test-cases
// Discover test-cases (see TODO below)
if (NumberOfCombiners == 0) LOG_TEST_CASE("NumberOfCombiners is zero");
if (!CombinerMuxesOnMsb) LOG_TEST_CASE("PS_COMBINERCOUNT_MUX_LSB detected"); // Test case required for how to implement the FCS_MUX check on LSB (see PS_COMBINERCOUNT_MUX_LSB in CxbxPixelShaderTemplate.hlsl) Note : test-case ModifyPixelShader hits this by mistake
if (TexModeAdjust) LOG_TEST_CASE("PS_GLOBALFLAGS_TEXMODE_ADJUST detected");
// Validate correctness
// Validate correctness (see NOTE below)
if (NumberOfCombiners > 8) LOG_TEST_CASE("NumberOfCombiners bigger than maximum (of 8)");
if ((pPSDef->PSCombinerCount & ~0x0001110F) > 0) LOG_TEST_CASE("Unknown PSCombinerCount bits detected");
}
// * TODO : For all "Discover test-cases" LOG_TEST_CASE's that lack sufficient test-case mentions, find some, note them in an EOL comment, and comment out the entire check.
// * NOTE : For all "Validate correctness" LOG_TEST_CASE's that ever get hit, investigate what caused it, what should be done, implement that, and update the verification.
/* PSH_RECOMPILED_SHADER */
typedef struct s_CxbxPSDef {
@ -466,7 +470,7 @@ typedef struct s_CxbxPSDef {
}
// Pre-decode TexModeAdjust, which impacts AdjustTextureModes
DecodedTexModeAdjust = (PSDef.PSFinalCombinerConstants >> 8) & PS_GLOBALFLAGS_TEXMODE_ADJUST;
DecodedTexModeAdjust = ((PSDef.PSFinalCombinerConstants >> PS_GLOBALFLAGS_SHIFT) & PS_GLOBALFLAGS_TEXMODE_ADJUST) > 0;
// Pre-decode hasFinalCombiner, which impacts AdjustFinalCombiner
DecodedHasFinalCombiner = (PSDef.PSFinalCombinerInputsABCD > 0) || (PSDef.PSFinalCombinerInputsEFG > 0);
@ -669,7 +673,11 @@ std::string GetFixedFunctionShaderTemplate() {
std::string_view GetD3DTOPString(int d3dtop) {
static constexpr std::string_view opToString[] = {
#ifdef ENABLE_FF_ALPHAKILL
"X_D3DTOP_DISABLE", // 0 (Was UNDEFINED, but that doesn't compile)
#else
"UNDEFINED", // 0
#endif
"X_D3DTOP_DISABLE", // 1
"X_D3DTOP_SELECTARG1", // 2
"X_D3DTOP_SELECTARG2", // 3
@ -698,7 +706,11 @@ std::string_view GetD3DTOPString(int d3dtop) {
"X_D3DTOP_BUMPENVMAPLUMINANCE", // 26
};
#ifdef ENABLE_FF_ALPHAKILL
if (d3dtop < 0 || d3dtop > 26) {
#else
if (d3dtop < 1 || d3dtop > 26) {
#endif
EmuLog(LOG_LEVEL::ERROR2, "Unmapped texture operation %d", d3dtop);
d3dtop = 0; // undefined
}
@ -872,11 +884,16 @@ IDirect3DPixelShader9* GetFixedFunctionShader()
stageSetup << '\n';
for (int i = 0; i < 4; i++) {
#ifdef ENABLE_FF_ALPHAKILL
// Even when a stage is disabled, we still have to fully initialize it's values, to prevent
// "error X4000: variable 'stages' used without having been completely initialized"
#else
// The stage is initialized to be disabled
// We don't have to output anything
if (states[i].COLOROP == X_D3DTOP_DISABLE)
continue;
#endif
std::string target = "stages[" + std::to_string(i) + "].";
auto s = states[i];
@ -1111,8 +1128,8 @@ void DxbxUpdateActivePixelShader() // NOPATCH
LOG_TEST_CASE("Two sided lighting");
// VFACE is positive for clockwise faces
// If Xbox designates counter-clockwise as front-facing, we invert VFACE
auto cwFrontface = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FRONTFACE) == 0x900; // clockwise;
frontfaceFactor = cwFrontface ? 1 : -1;
auto cwFrontface = XboxRenderStates.GetXboxRenderState(xbox::X_D3DRS_FRONTFACE) == 0x900; // clockwise; = NV097_SET_FRONT_FACE_V_CW = NV2A_FRONT_FACE_CW
frontfaceFactor = cwFrontface ? 1.0 : -1.0;
}
fColor[PSH_XBOX_CONSTANT_FRONTFACE_FACTOR].r = frontfaceFactor;

View File

@ -107,6 +107,8 @@ enum PS_TEXTUREMODES
PS_TEXTUREMODES_DOTPRODUCT= 0x11L, // - * * - PSInputTexture
PS_TEXTUREMODES_DOT_RFLCT_SPEC_CONST= 0x12L, // - - - * Sample, PSInputTexture, PSDotMapping
// 0x13-0x1f reserved
PS_TEXTUREMODES_MASK= 0x1fL
};
// =========================================================================================================
@ -137,6 +139,8 @@ enum PS_DOTMAPPING
PS_DOTMAPPING_HILO_HEMISPHERE_D3D= 0x05L, // - * * *
PS_DOTMAPPING_HILO_HEMISPHERE_GL= 0x06L, // - * * *
PS_DOTMAPPING_HILO_HEMISPHERE= 0x07L, // - * * *
PS_DOTMAPPING_MASK= 0x07L
};
// =========================================================================================================
@ -161,6 +165,8 @@ enum PS_COMPAREMODE
PS_COMPAREMODE_Q_LT= 0x00L,
PS_COMPAREMODE_Q_GE= 0x08L,
PS_COMPAREMODE_MASK= 0x0fL
};
// =========================================================================================================
@ -284,6 +290,8 @@ enum PS_INPUTMAPPING
PS_INPUTMAPPING_HALFBIAS_NEGATE= 0xa0L, // 1/2 - max(0,x) = -1*max(0,x) + 0.5 invalid for final combiner
PS_INPUTMAPPING_SIGNED_IDENTITY= 0xc0L, // x = 1* x + 0.0 invalid for final combiner
PS_INPUTMAPPING_SIGNED_NEGATE= 0xe0L, // -x = -1* x + 0.0 invalid for final combiner
PS_INPUTMAPPING_MASK= 0xe0L
};
enum PS_REGISTER
@ -304,6 +312,8 @@ enum PS_REGISTER
PS_REGISTER_V1R0_SUM= 0x0eL, // r A.k.a. _REG_SPECLIT
PS_REGISTER_EF_PROD= 0x0fL, // r A.k.a. _REG_EF_PROD
PS_REGISTER_MASK= 0x0fL,
// These constant values can be represented as a combination of 0, and an input modifier
// But they're not registers
// PS_REGISTER_ONE= PS_REGISTER_ZERO | PS_INPUTMAPPING_UNSIGNED_INVERT, // 0x20 r OK for final combiner
@ -326,6 +336,8 @@ enum PS_CHANNEL
PS_CHANNEL_RGB= 0x00, // used as RGB source
PS_CHANNEL_BLUE= 0x00, // used as ALPHA source
PS_CHANNEL_ALPHA= 0x10, // used as RGB or ALPHA source
PS_CHANNEL_MASK= 0x10
};
enum PS_FINALCOMBINERSETTING
@ -362,6 +374,8 @@ enum PS_COMBINEROUTPUT_OUTPUTMAPPING
PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTLEFT_2_BIAS= 0x28L, // y = (x - 0.5) * 4 Note : Cxbx inferred method; May not be supported on NV2A
PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTRIGHT_1= 0x30L, // y = x / 2
PS_COMBINEROUTPUT_OUTPUTMAPPING_SHIFTRIGHT_1_BIAS= 0x38L, // y = (x - 0.5) / 2 Note : Cxbx inferred method; May not be supported on NV2A
PS_COMBINEROUTPUT_OUTPUTMAPPING_MASK= 0x38L
};
enum PS_COMBINEROUTPUT
@ -429,6 +443,8 @@ enum PS_GLOBALFLAGS
PS_GLOBALFLAGS_NO_TEXMODE_ADJUST= 0x0000L, // don't adjust texture modes
PS_GLOBALFLAGS_TEXMODE_ADJUST= 0x0001L, // adjust texture modes according to set texture
PS_GLOBALFLAGS_SHIFT= 8
};
@ -464,7 +480,7 @@ struct RPSCombinerStageChannel {
RPSCombinerOutput OutputAB; // Contains InputA and InputB (as Input1 and Input2)
RPSRegisterObject OutputMUX_SUM;
bool AB_CD_MUX; // False=AB+CD, True=MUX(AB,CD) based on R0.a
PS_COMBINEROUTPUT CombinerOutputMapping;
PS_COMBINEROUTPUT_OUTPUTMAPPING CombinerOutputMapping;
void Decode(uint32_t PSInputs, uint32_t PSOutputs, unsigned stage_nr, bool isRGB);
};