You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2410 lines
93 KiB
2410 lines
93 KiB
///////////////////////////////////////////////////////////////////////////////
|
|
// Copyright (C) Microsoft Corporation, 2000.
|
|
//
|
|
// pshdrval.cpp
|
|
//
|
|
// Direct3D Reference Device - PixelShader validation
|
|
//
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
#include "pch.cpp"
|
|
#pragma hdrstop
|
|
|
|
// Use these macros when looking at CPSInstruction derived members of the current instruction (CBaseInstruction)
|
|
#define _CURR_PS_INST ((CPSInstruction*)m_pCurrInst)
|
|
#define _PREV_PS_INST (m_pCurrInst?((CPSInstruction*)(m_pCurrInst->m_pPrevInst)):NULL)
|
|
|
|
#define SWIZZLE_XYZZ (D3DVS_X_X | D3DVS_Y_Y | D3DVS_Z_Z | D3DVS_W_Z)
|
|
#define SWIZZLE_XYZW (D3DVS_X_X | D3DVS_Y_Y | D3DVS_Z_Z | D3DVS_W_W)
|
|
#define SWIZZLE_XYWW (D3DVS_X_X | D3DVS_Y_Y | D3DVS_Z_W | D3DVS_W_W)
|
|
|
|
#define ZAPPED_ALPHA_TEXT "Note that an unfortunate effect of the phase marker earlier in the shader is "\
|
|
"that the moment it is encountered in certain hardware, values previously "\
|
|
"written to alpha in any r# register, including the one noted here, are lost. "\
|
|
"In order to read alpha from an r# register after the phase marker, write to it first."
|
|
|
|
#define ZAPPED_ALPHA_TEXT2 "Note that an unfortunate effect of the phase marker in the shader is "\
|
|
"that the moment it is encountered in certain hardware, values previously "\
|
|
"written to alpha in any r# register, including r0, are lost. "\
|
|
"So after a phase marker, the alpha component of r0 must be written."
|
|
|
|
#define ZAPPED_BLUE_TEXT "Note that when texcrd is used with a .xy(==.rg) writemask, "\
|
|
"as it is in this shader, a side effect is that anything previously "\
|
|
"written to the z(==b) component of the destination r# register is lost "\
|
|
"and this component becomes uninitialized. In order to read blue again, write to it first."
|
|
|
|
#define ZAPPED_BLUE_TEXT2 "Note that when texcrd is used with a .xy(==.rg) writemask, "\
|
|
"as it is in this shader, a side effect is that anything previously "\
|
|
"written to the z(==b) component of the destination r# register is lost "\
|
|
"and this component becomes uninitialized. The blue component of r0 must to be written after the texcrd."
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::CPShaderValidator14
|
|
//-----------------------------------------------------------------------------
|
|
CPShaderValidator14::CPShaderValidator14( const DWORD* pCode,
|
|
const D3DCAPS8* pCaps,
|
|
DWORD Flags )
|
|
: CBasePShaderValidator( pCode, pCaps, Flags )
|
|
{
|
|
// Note that the base constructor initialized m_ReturnCode to E_FAIL.
|
|
// Only set m_ReturnCode to S_OK if validation has succeeded,
|
|
// before exiting this constructor.
|
|
|
|
m_Phase = 2; // default to second pass.
|
|
m_pPhaseMarkerInst = NULL;
|
|
m_bPhaseMarkerInShader = FALSE;
|
|
m_TempRegsWithZappedAlpha = 0;
|
|
m_TempRegsWithZappedBlue = 0;
|
|
|
|
if( !m_bBaseInitOk )
|
|
return;
|
|
|
|
ValidateShader(); // If successful, m_ReturnCode will be set to S_OK.
|
|
// Call GetStatus() on this object to determine validation outcome.
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::IsCurrInstTexOp
|
|
//-----------------------------------------------------------------------------
|
|
void CPShaderValidator14::IsCurrInstTexOp()
|
|
{
|
|
DXGASSERT(m_pCurrInst);
|
|
|
|
switch (m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEX:
|
|
case D3DSIO_TEXCOORD:
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_TEXDEPTH:
|
|
_CURR_PS_INST->m_bTexOp = TRUE;
|
|
break;
|
|
}
|
|
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_TEXDEPTH:
|
|
case D3DSIO_TEXCOORD:
|
|
_CURR_PS_INST->m_bTexOpThatReadsTexture = FALSE;
|
|
break;
|
|
case D3DSIO_TEX:
|
|
_CURR_PS_INST->m_bTexOpThatReadsTexture = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
#define MAX_NUM_STAGES_2_0 6 // #defined because there are dependencies.
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::InitValidation
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::InitValidation()
|
|
{
|
|
switch( m_Version >> 16 )
|
|
{
|
|
case 0xfffe:
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: 0x%x indicates a vertex shader. Pixel shader version token must be of the form 0xffff****.",
|
|
m_Version);
|
|
return FALSE;
|
|
case 0xffff:
|
|
break; // pixelshader - ok.
|
|
default:
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: 0x%x is invalid. Pixel shader version token must be of the form 0xffff****. Aborting pixel shader validation.",
|
|
m_Version);
|
|
return FALSE;
|
|
}
|
|
|
|
if( m_pCaps )
|
|
{
|
|
if( (m_pCaps->PixelShaderVersion & 0x0000FFFF) < (m_Version & 0x0000FFFF) )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: Pixel shader version %d.%d is too high for device. Maximum supported version is %d.%d. Aborting shader validation.",
|
|
D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version),
|
|
D3DSHADER_VERSION_MAJOR(m_pCaps->PixelShaderVersion),D3DSHADER_VERSION_MINOR(m_pCaps->PixelShaderVersion));
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
switch(m_Version)
|
|
{
|
|
case D3DPS_VERSION(1,4): // DX8.1
|
|
m_pInputRegFile = new CRegisterFile(2,FALSE,2,TRUE); // #regs, bWritable, max# reads/instruction, pre-shader initialized
|
|
m_pConstRegFile = new CRegisterFile(8,FALSE,2,TRUE);
|
|
m_pTextureRegFile = new CRegisterFile(MAX_NUM_STAGES_2_0,FALSE, 1,TRUE);
|
|
m_pTempRegFile = new CRegisterFile(MAX_NUM_STAGES_2_0,TRUE,3,FALSE);
|
|
break;
|
|
default:
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: %d.%d is not a supported pixel shader version. Aborting pixel shader validation.",
|
|
D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version));
|
|
return FALSE;
|
|
}
|
|
if( NULL == m_pInputRegFile ||
|
|
NULL == m_pConstRegFile ||
|
|
NULL == m_pTextureRegFile ||
|
|
NULL == m_pTempRegFile )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory.");
|
|
return FALSE;
|
|
}
|
|
|
|
const DWORD* pCurrToken = m_pCurrToken;
|
|
|
|
// Loop through all the instructions to see if a phase change marker is present.
|
|
while( *pCurrToken != D3DPS_END() )
|
|
{
|
|
D3DSHADER_INSTRUCTION_OPCODE_TYPE Type = (D3DSHADER_INSTRUCTION_OPCODE_TYPE)(*pCurrToken & D3DSI_OPCODE_MASK);
|
|
|
|
if( D3DSIO_COMMENT == Type )
|
|
{
|
|
// Skip comments
|
|
DWORD NumDWORDs = ((*pCurrToken) & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
|
|
pCurrToken += (NumDWORDs+1);
|
|
continue;
|
|
}
|
|
|
|
if( D3DSIO_PHASE == Type )
|
|
{
|
|
m_bPhaseMarkerInShader = TRUE;
|
|
m_Phase = 1;
|
|
}
|
|
|
|
pCurrToken++;
|
|
|
|
// Dst param
|
|
if (*pCurrToken & (1L<<31))
|
|
{
|
|
pCurrToken++;
|
|
if( D3DSIO_DEF == Type )
|
|
{
|
|
pCurrToken += 4;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// Decode src param(s)
|
|
while (*pCurrToken & (1L<<31))
|
|
{
|
|
pCurrToken++;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::ApplyPerInstructionRules
|
|
//
|
|
// Returns FALSE if shader validation must terminate.
|
|
// Returns TRUE if validation may proceed to next instruction.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::ApplyPerInstructionRules()
|
|
{
|
|
if( ! Rule_InstructionRecognized() ) return FALSE; // Bail completely on unrecognized instr.
|
|
if( ! Rule_InstructionSupportedByVersion() ) goto EXIT;
|
|
if( ! Rule_ValidParamCount() ) goto EXIT;
|
|
if( ! Rule_ValidMarker() ) goto EXIT; // must be before any rule that needs to know what the current phase is
|
|
|
|
// Rules that examine source parameters
|
|
if( ! Rule_ValidSrcParams() ) goto EXIT;
|
|
if( ! Rule_MultipleDependentTextureReads() ) goto EXIT; // needs to be after _ValidSrcParams(), and before _ValidDstParam(), _SrcInitialized()
|
|
if( ! Rule_SrcInitialized() ) goto EXIT; // needs to be before _ValidDstParam()
|
|
|
|
if( ! Rule_ValidDstParam() ) goto EXIT;
|
|
if( ! Rule_ValidRegisterPortUsage() ) goto EXIT;
|
|
if( ! Rule_TexOpAfterArithmeticOp() ) goto EXIT;
|
|
if( ! Rule_ValidTexOpStageAndRegisterUsage() ) goto EXIT;
|
|
if( ! Rule_LimitedUseOfProjModifier() ) goto EXIT;
|
|
if( ! Rule_ValidTEXDEPTHInstruction() ) goto EXIT;
|
|
if( ! Rule_ValidTEXKILLInstruction() ) goto EXIT;
|
|
if( ! Rule_ValidBEMInstruction() ) goto EXIT;
|
|
if( ! Rule_ValidDEFInstruction() ) goto EXIT;
|
|
if( ! Rule_ValidInstructionPairing() ) goto EXIT;
|
|
if( ! Rule_ValidInstructionCount() ) goto EXIT;
|
|
EXIT:
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::ApplyPostInstructionsRules
|
|
//-----------------------------------------------------------------------------
|
|
void CPShaderValidator14::ApplyPostInstructionsRules()
|
|
{
|
|
Rule_ValidInstructionCount(); // see if we went over the limits
|
|
Rule_R0Written();
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Per Instruction Rules
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_InstructionRecognized
|
|
//
|
|
// ** Rule:
|
|
// Is the instruction opcode known? (regardless of shader version)
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// FALSE when instruction not recognized.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_InstructionRecognized()
|
|
{
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_MOV:
|
|
case D3DSIO_ADD:
|
|
case D3DSIO_SUB:
|
|
case D3DSIO_MUL:
|
|
case D3DSIO_MAD:
|
|
case D3DSIO_LRP:
|
|
case D3DSIO_DP3:
|
|
case D3DSIO_TEX:
|
|
case D3DSIO_TEXBEM:
|
|
case D3DSIO_TEXBEML:
|
|
case D3DSIO_CND:
|
|
case D3DSIO_TEXCOORD:
|
|
case D3DSIO_TEXM3x2PAD:
|
|
case D3DSIO_TEXM3x2TEX:
|
|
case D3DSIO_TEXM3x3PAD:
|
|
case D3DSIO_TEXM3x3TEX:
|
|
case D3DSIO_TEXM3x3SPEC:
|
|
case D3DSIO_TEXM3x3VSPEC:
|
|
case D3DSIO_TEXREG2AR:
|
|
case D3DSIO_TEXREG2GB:
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_END:
|
|
case D3DSIO_NOP:
|
|
case D3DSIO_DEF:
|
|
case D3DSIO_TEXM3x2DEPTH:
|
|
case D3DSIO_TEXDP3:
|
|
case D3DSIO_TEXREG2RGB:
|
|
case D3DSIO_DP4:
|
|
case D3DSIO_CMP:
|
|
case D3DSIO_TEXDP3TEX:
|
|
case D3DSIO_TEXM3x3:
|
|
case D3DSIO_TEXDEPTH:
|
|
case D3DSIO_BEM:
|
|
case D3DSIO_PHASE:
|
|
return TRUE; // instruction recognized - ok.
|
|
}
|
|
|
|
// if we get here, the instruction is not recognized
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Unrecognized instruction. Aborting pixel shader validation.");
|
|
m_ErrorCount++;
|
|
return FALSE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_InstructionSupportedByVersion
|
|
//
|
|
// ** Rule:
|
|
// Is the instruction supported by the current pixel shader version?
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// FALSE when instruction not supported by version.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_InstructionSupportedByVersion()
|
|
{
|
|
if( D3DPS_VERSION(1,4) <= m_Version ) // 1.3 and above
|
|
{
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_MOV:
|
|
case D3DSIO_ADD:
|
|
case D3DSIO_SUB:
|
|
case D3DSIO_MUL:
|
|
case D3DSIO_MAD:
|
|
case D3DSIO_LRP:
|
|
case D3DSIO_DP3:
|
|
case D3DSIO_DEF:
|
|
case D3DSIO_CND:
|
|
case D3DSIO_CMP:
|
|
case D3DSIO_DP4:
|
|
case D3DSIO_BEM:
|
|
case D3DSIO_TEX:
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_TEXDEPTH:
|
|
case D3DSIO_TEXCOORD:
|
|
case D3DSIO_PHASE:
|
|
return TRUE; // instruction supported - ok.
|
|
}
|
|
}
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_END:
|
|
case D3DSIO_NOP:
|
|
return TRUE; // instruction supported - ok.
|
|
}
|
|
|
|
// if we get here, the instruction is not supported.
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Instruction not supported by version %d.%d pixel shader.",
|
|
D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version));
|
|
m_ErrorCount++;
|
|
return FALSE; // no more checks on this instruction
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidParamCount
|
|
//
|
|
// ** Rule:
|
|
// Is the parameter count correct for the instruction?
|
|
//
|
|
// DEF is a special case that is treated as having only 1 dest parameter,
|
|
// even though there are also 4 source parameters. The 4 source params for DEF
|
|
// are immediate float values, so there is nothing to check, and no way of
|
|
// knowing whether or not those parameter tokens were actually present in the
|
|
// token list - all the validator can do is skip over 4 DWORDS (which it does).
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// FALSE when the parameter count is incorrect.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidParamCount()
|
|
{
|
|
BOOL bBadParamCount = FALSE;
|
|
|
|
if (m_pCurrInst->m_SrcParamCount + m_pCurrInst->m_DstParamCount > SHADER_INSTRUCTION_MAX_PARAMS) bBadParamCount = TRUE;
|
|
switch (m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_NOP:
|
|
case D3DSIO_PHASE:
|
|
bBadParamCount = (m_pCurrInst->m_DstParamCount != 0) || (m_pCurrInst->m_SrcParamCount != 0); break;
|
|
case D3DSIO_MOV:
|
|
bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 1); break;
|
|
case D3DSIO_ADD:
|
|
case D3DSIO_SUB:
|
|
case D3DSIO_MUL:
|
|
case D3DSIO_DP3:
|
|
case D3DSIO_DP4:
|
|
case D3DSIO_BEM:
|
|
bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 2); break;
|
|
case D3DSIO_MAD:
|
|
case D3DSIO_LRP:
|
|
case D3DSIO_CND:
|
|
case D3DSIO_CMP:
|
|
bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 3); break;
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_TEXDEPTH:
|
|
case D3DSIO_DEF: // we skipped the last 4 parameters (float vector) - nothing to check
|
|
bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 0); break;
|
|
case D3DSIO_TEX:
|
|
case D3DSIO_TEXCOORD:
|
|
bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 1); break;
|
|
}
|
|
|
|
if (bBadParamCount)
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid parameter count.");
|
|
m_ErrorCount++;
|
|
return FALSE; // no more checks on this instruction
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidSrcParams
|
|
//
|
|
// ** Rule:
|
|
// for each source parameter,
|
|
// if current instruction is a texture instruction, then
|
|
// if texcrd, source register type must be t# (texture coordinate input)
|
|
// else source register type must be t# or r# (temp)
|
|
// register number must be in range
|
|
// _DZ and _DW are the only source modifiers allowed
|
|
// no source selector allowed,
|
|
// except texcrd/texld which can have: .xyz(==.xyzz), nothing(=.xyzw), and .xyw(=.xyww)
|
|
// else (non texture instruction)
|
|
// if in phase 1 of shader, v# registers not allowed
|
|
// t# registers not allowed (only const or temp allowed)
|
|
// register number must be in range
|
|
// source modifier must be one of:
|
|
// _NONE/_NEG/_BIAS/_BIASNEG/_SIGN/_SIGNNEG/_X2/_X2NEG
|
|
// source selector must be one of:
|
|
// _NOSWIZZLE/_REPLICATEALPHA/RED/GREEN/BLUE
|
|
//
|
|
// Note that the parameter count for D3DSIO_DEF is treated as 1
|
|
// (dest only), so this rule does nothing for it.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
// Errors in any of the source parameters causes m_bSrcParamError[i]
|
|
// to be TRUE, so later rules that only apply when a particular source
|
|
// parameter was valid know whether they need to execute or not.
|
|
// e.g. Rule_SrcInitialized.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidSrcParams()
|
|
{
|
|
static DWORD s_TexcrdSrcSwizzle[MAX_NUM_STAGES_2_0];
|
|
static BOOL s_bSeenTexcrdSrcSwizzle[MAX_NUM_STAGES_2_0];
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
for( UINT i = 0; i < MAX_NUM_STAGES_2_0; i++ )
|
|
s_bSeenTexcrdSrcSwizzle[i] = FALSE;
|
|
}
|
|
|
|
for( UINT i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
|
|
{
|
|
BOOL bFoundSrcError = FALSE;
|
|
SRCPARAM* pSrcParam = &(m_pCurrInst->m_SrcParam[i]);
|
|
DWORD Swizzle = pSrcParam->m_SwizzleShift;
|
|
char szSourceName[32];
|
|
switch(i + 1)
|
|
{
|
|
case 1:
|
|
if( 1 == m_pCurrInst->m_SrcParamCount )
|
|
sprintf( szSourceName, "(Source param) " );
|
|
else
|
|
sprintf( szSourceName, "(First source param) " );
|
|
break;
|
|
case 2:
|
|
sprintf( szSourceName, "(Second source param) " );
|
|
break;
|
|
case 3:
|
|
sprintf( szSourceName, "(Third source param) " );
|
|
break;
|
|
default:
|
|
DXGASSERT(FALSE);
|
|
}
|
|
if( _CURR_PS_INST->m_bTexOp )
|
|
{
|
|
UINT ValidRegNum = 0;
|
|
switch (m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEXCOORD:
|
|
if( D3DSPR_TEXTURE != pSrcParam->m_RegType )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sSource register type must be texture coordinate input (t#) for texcrd instruction.",
|
|
szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
ValidRegNum = m_pTextureRegFile->GetNumRegs(); break;
|
|
break;
|
|
default:
|
|
switch(pSrcParam->m_RegType)
|
|
{
|
|
case D3DSPR_TEMP: ValidRegNum = m_pTempRegFile->GetNumRegs(); break;
|
|
case D3DSPR_TEXTURE: ValidRegNum = m_pTextureRegFile->GetNumRegs(); break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sSource register type must be temp (r#) or texture coordinate input (t#) for tex* instruction.",
|
|
szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
goto LOOP_CONTINUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
if( pSrcParam->m_RegNum >= ValidRegNum )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid register number: %d. Max allowed for this type is %d.",
|
|
szSourceName, pSrcParam->m_RegNum, ValidRegNum - 1);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
|
|
BOOL bGenericSrcModError = FALSE;
|
|
switch(pSrcParam->m_SrcMod)
|
|
{
|
|
case D3DSPSM_NONE:
|
|
break;
|
|
case D3DSPSM_DZ:
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEX:
|
|
if( D3DSPR_TEMP != pSrcParam->m_RegType )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"_dz(=_db) modifier on source param for texld only allowed if source is a temp register (r#)." );
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
if( 1 == m_Phase )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"_dz(=_db) modifier on source param for texld only allowed in second phase of a shader.");
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
if( (SWIZZLE_XYZZ != Swizzle) &&
|
|
(SWIZZLE_XYZW != Swizzle) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"_dz(=_db) modifier on source param for texld must be paired with source selector .xyz(=.rgb). "\
|
|
"Note: Using no selector is treated same as .xyz here.");
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
break;
|
|
case D3DSIO_TEXCOORD:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"_dz(=_db) modifier cannot be used on source parameter for texcrd. "\
|
|
"It is only available to texld instruction, when source parameter is temp register (r#).");
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
break;
|
|
default:
|
|
bGenericSrcModError = TRUE; break;
|
|
}
|
|
break;
|
|
case D3DSPSM_DW:
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEX:
|
|
if( D3DSPR_TEXTURE != pSrcParam->m_RegType )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"_dw(=_da) modifier on source param for texld only allowed if source is a texture coordinate register (t#)." );
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
// falling through
|
|
case D3DSIO_TEXCOORD:
|
|
if( SWIZZLE_XYWW != Swizzle )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"_dw(=_da) modifier on source param must be paired with source selector .xyw(=.rga)." );
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
break;
|
|
default:
|
|
bGenericSrcModError = TRUE; break;
|
|
}
|
|
break;
|
|
default:
|
|
bGenericSrcModError = TRUE; break;
|
|
}
|
|
if( bGenericSrcModError )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid source modifier for tex* instruction.", szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
|
|
switch (m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEXCOORD:
|
|
if( (SWIZZLE_XYZZ != Swizzle) &&
|
|
(SWIZZLE_XYZW != Swizzle) &&
|
|
(SWIZZLE_XYWW != Swizzle) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Source for texcrd requires component selector .xyw(==.rga), or .xyz(==.rgb). "\
|
|
"Note: Using no selector is treated same as .xyz here.");
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
break;
|
|
case D3DSIO_TEX:
|
|
if( D3DSPR_TEXTURE == pSrcParam->m_RegType )
|
|
{
|
|
if( (SWIZZLE_XYZZ != Swizzle) &&
|
|
(SWIZZLE_XYZW != Swizzle) &&
|
|
(SWIZZLE_XYWW != Swizzle) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Using a texture coordinate register (t#) as source for texld requires component selector .xyw(=.rga), or .xyz(=.rgb). "\
|
|
"Note: Using no selector is treated same as .xyz here.");
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
|
|
}
|
|
}
|
|
else if( D3DSPR_TEMP == pSrcParam->m_RegType )
|
|
{
|
|
if( (SWIZZLE_XYZZ != Swizzle) &&
|
|
(SWIZZLE_XYZW != Swizzle) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Using a temp register (r#) as source for texld requires component selector .xyz(==.rgb). "\
|
|
"Note: Using no selector is treated same as .xyz here.");
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
switch (pSrcParam->m_SwizzleShift)
|
|
{
|
|
case D3DSP_NOSWIZZLE:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid source selector for tex* instruction.", szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEXCOORD:
|
|
case D3DSIO_TEX:
|
|
if( D3DSPR_TEXTURE != pSrcParam->m_RegType )
|
|
break;
|
|
|
|
// Verify that if a specific t# register is read more than once, each read uses the same source selector.
|
|
if( s_bSeenTexcrdSrcSwizzle[pSrcParam->m_RegNum] )
|
|
{
|
|
// only check rgb swizzle (ignore a)
|
|
if( (Swizzle & (0x3F << D3DVS_SWIZZLE_SHIFT)) != (s_TexcrdSrcSwizzle[pSrcParam->m_RegNum] & (0x3F << D3DVS_SWIZZLE_SHIFT) ))
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Texture coordinate register t%d read more than once in shader with different source selector (swizzle). "\
|
|
"Multiple reads of identical texture coordinate register throughout shader must all use identical source selector. "\
|
|
"Note this does not restrict mixing use and non-use of a source modifier (i.e. _dw/_da or _dz/_db, depending what the swizzle allows) on these coordinate register reads.",
|
|
pSrcParam->m_RegNum);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
}
|
|
s_bSeenTexcrdSrcSwizzle[pSrcParam->m_RegNum] = TRUE;
|
|
s_TexcrdSrcSwizzle[pSrcParam->m_RegNum] = Swizzle;
|
|
break;
|
|
}
|
|
|
|
}
|
|
else // not a tex op
|
|
{
|
|
UINT ValidRegNum = 0;
|
|
switch(pSrcParam->m_RegType)
|
|
{
|
|
case D3DSPR_INPUT:
|
|
if( 1 == m_Phase )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInput registers (v#) are not available in phase 1 of the shader.", szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
else
|
|
{
|
|
ValidRegNum = m_pInputRegFile->GetNumRegs();
|
|
}
|
|
break;
|
|
case D3DSPR_CONST: ValidRegNum = m_pConstRegFile->GetNumRegs(); break;
|
|
case D3DSPR_TEMP: ValidRegNum = m_pTextureRegFile->GetNumRegs(); break;
|
|
case D3DSPR_TEXTURE:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sTexture coordinate registers (t#) are not available to arithmetic instructions.", szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid register type.", szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
|
|
if( (!bFoundSrcError) && (pSrcParam->m_RegNum >= ValidRegNum) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid register number: %d. Max allowed for this type is %d.",
|
|
szSourceName, pSrcParam->m_RegNum, ValidRegNum - 1);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
|
|
switch( pSrcParam->m_SrcMod )
|
|
{
|
|
case D3DSPSM_NONE:
|
|
case D3DSPSM_NEG:
|
|
case D3DSPSM_BIAS:
|
|
case D3DSPSM_BIASNEG:
|
|
case D3DSPSM_SIGN:
|
|
case D3DSPSM_SIGNNEG:
|
|
case D3DSPSM_COMP:
|
|
case D3DSPSM_X2:
|
|
case D3DSPSM_X2NEG:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid source modifier.",
|
|
szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
|
|
switch( pSrcParam->m_SwizzleShift )
|
|
{
|
|
case D3DSP_NOSWIZZLE:
|
|
case D3DSP_REPLICATERED:
|
|
case D3DSP_REPLICATEGREEN:
|
|
case D3DSP_REPLICATEBLUE:
|
|
case D3DSP_REPLICATEALPHA:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%sInvalid source selector.",
|
|
szSourceName);
|
|
m_ErrorCount++;
|
|
bFoundSrcError = TRUE;
|
|
}
|
|
}
|
|
LOOP_CONTINUE:
|
|
if( bFoundSrcError )
|
|
{
|
|
m_bSrcParamError[i] = TRUE; // needed in Rule_SrcInitialized
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_LimitedUseOfProjModifier
|
|
//
|
|
// ** Rule:
|
|
// _dz may only appear at most 2 times in shader.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_LimitedUseOfProjModifier()
|
|
{
|
|
static UINT s_ProjZModifierCount;
|
|
static BOOL s_bSpewedError;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_ProjZModifierCount = 0;
|
|
s_bSpewedError = FALSE;
|
|
}
|
|
|
|
for( UINT i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
|
|
{
|
|
if( m_bSrcParamError[i] )
|
|
continue;
|
|
|
|
if( D3DSPSM_DZ == m_pCurrInst->m_SrcParam[i].m_SrcMod)
|
|
{
|
|
s_ProjZModifierCount++;
|
|
}
|
|
|
|
if( (2 < s_ProjZModifierCount) && (FALSE == s_bSpewedError) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "_dz(=_db) modifier may only be used at most 2 times in a shader." );
|
|
s_bSpewedError = TRUE;
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_SrcInitialized
|
|
//
|
|
// ** Rule:
|
|
// for each source parameter,
|
|
// if source is a TEMP register then
|
|
// the components flagged in the component read mask
|
|
// (computed elsewhere) for the paramter must have been initialized
|
|
//
|
|
// When checking if a component has been written previously,
|
|
// it must have been written in a previous cycle - so in the
|
|
// case of co-issued instructions, initialization of a component
|
|
// by one co-issued instruction is not available to the other for read.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction. This rule must be called before Rule_ValidDstParam().
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
// NOTE: This rule also updates the access history to indicate reads of the
|
|
// affected components of each source register.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_SrcInitialized()
|
|
{
|
|
DSTPARAM* pDstParam = &(m_pCurrInst->m_DstParam);
|
|
|
|
BOOL bDestParamIsSrc = pDstParam->m_ComponentReadMask;
|
|
UINT SrcParamCount = bDestParamIsSrc ? 1 : m_pCurrInst->m_SrcParamCount; // assumes if dest param is src,
|
|
// there are no source params in instruction
|
|
for( UINT i = 0; i < SrcParamCount; i++ )
|
|
{
|
|
DWORD UninitializedComponentsMask = 0;
|
|
CAccessHistoryNode* pWriterInCurrCycle[4] = {0, 0, 0, 0};
|
|
UINT NumUninitializedComponents = 0;
|
|
UINT RegNum = bDestParamIsSrc ? pDstParam->m_RegNum : m_pCurrInst->m_SrcParam[i].m_RegNum;
|
|
D3DSHADER_PARAM_REGISTER_TYPE Type = bDestParamIsSrc ? pDstParam->m_RegType : m_pCurrInst->m_SrcParam[i].m_RegType;
|
|
DWORD ComponentReadMask = bDestParamIsSrc ? pDstParam->m_ComponentReadMask : m_pCurrInst->m_SrcParam[i].m_ComponentReadMask;
|
|
CRegisterFile* pRegFile = NULL;
|
|
char* RegChar = NULL;
|
|
|
|
if( !bDestParamIsSrc && m_bSrcParamError[i] )
|
|
continue;
|
|
|
|
switch( Type )
|
|
{
|
|
case D3DSPR_TEMP:
|
|
pRegFile = m_pTempRegFile;
|
|
RegChar = "r";
|
|
break;
|
|
case D3DSPR_TEXTURE:
|
|
pRegFile = m_pTextureRegFile;
|
|
RegChar = "t";
|
|
break;
|
|
case D3DSPR_INPUT:
|
|
pRegFile = m_pInputRegFile;
|
|
RegChar = "v";
|
|
break;
|
|
case D3DSPR_CONST:
|
|
pRegFile = m_pConstRegFile;
|
|
RegChar = "c";
|
|
break;
|
|
}
|
|
if( !pRegFile )
|
|
continue;
|
|
|
|
if( RegNum >= pRegFile->GetNumRegs() )
|
|
continue;
|
|
|
|
// check for read of uninitialized components
|
|
if( D3DSPR_TEMP == Type ) // only bother doing this for temp regs, since everything else is initialized.
|
|
{
|
|
for( UINT Component = 0; Component < 4; Component++ )
|
|
{
|
|
if( !(ComponentReadMask & COMPONENT_MASKS[Component]) )
|
|
continue;
|
|
|
|
CAccessHistoryNode* pPreviousWriter = pRegFile->m_pAccessHistory[Component][RegNum].m_pMostRecentWriter;
|
|
CBaseInstruction* pCurrInst = m_pCurrInst;
|
|
|
|
// If co-issue, find the real previous writer.
|
|
while( pPreviousWriter
|
|
&& ((CPSInstruction*)pPreviousWriter->m_pInst)->m_CycleNum == _CURR_PS_INST->m_CycleNum )
|
|
{
|
|
pWriterInCurrCycle[Component] = pPreviousWriter; // log read just before this write for co-issue
|
|
pPreviousWriter = pPreviousWriter->m_pPreviousWriter;
|
|
}
|
|
|
|
// Even if pPreviousWriter == NULL, the component could have been initialized pre-shader.
|
|
// So to check for initialization, we look at m_bInitialized below, rather than pPreviousWrite
|
|
if(pPreviousWriter == NULL && !pRegFile->m_pAccessHistory[Component][RegNum].m_bPreShaderInitialized)
|
|
{
|
|
NumUninitializedComponents++;
|
|
UninitializedComponentsMask |= COMPONENT_MASKS[Component];
|
|
}
|
|
}
|
|
|
|
if( NumUninitializedComponents )
|
|
{
|
|
if( (UninitializedComponentsMask & COMPONENT_MASKS[3]) &&
|
|
(m_TempRegsWithZappedAlpha & (1 << RegNum ) ) &&
|
|
(UninitializedComponentsMask & COMPONENT_MASKS[2]) &&
|
|
(m_TempRegsWithZappedBlue & (1 << RegNum ) ) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Read of uninitialized component%s(*) in %s%d: %s. "\
|
|
ZAPPED_BLUE_TEXT " Also: " ZAPPED_ALPHA_TEXT,
|
|
NumUninitializedComponents > 1 ? "s" : "",
|
|
RegChar, RegNum, MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,TRUE));
|
|
}
|
|
else if( (UninitializedComponentsMask & COMPONENT_MASKS[3]) &&
|
|
(m_TempRegsWithZappedAlpha & (1 << RegNum ) ) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Read of uninitialized component%s(*) in %s%d: %s. "\
|
|
ZAPPED_ALPHA_TEXT,
|
|
NumUninitializedComponents > 1 ? "s" : "",
|
|
RegChar, RegNum, MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,TRUE));
|
|
}
|
|
else if( (UninitializedComponentsMask & COMPONENT_MASKS[2]) &&
|
|
(m_TempRegsWithZappedBlue & (1 << RegNum ) ) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Read of uninitialized component%s(*) in %s%d: %s. "\
|
|
ZAPPED_BLUE_TEXT,
|
|
NumUninitializedComponents > 1 ? "s" : "",
|
|
RegChar, RegNum, MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,TRUE));
|
|
}
|
|
else
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Read of uninitialized component%s(*) in %s%d: %s",
|
|
NumUninitializedComponents > 1 ? "s" : "",
|
|
RegChar, RegNum, MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,TRUE));
|
|
}
|
|
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
|
|
// Update register file to indicate READ.
|
|
// Multiple reads of the same register component by the current instruction
|
|
// will only be logged as one read in the access history.
|
|
|
|
for( UINT Component = 0; Component < 4; Component++ )
|
|
{
|
|
#define PREV_READER(_CHAN,_REG) \
|
|
((NULL == pRegFile->m_pAccessHistory[_CHAN][_REG].m_pMostRecentReader) ? NULL :\
|
|
pRegFile->m_pAccessHistory[_CHAN][_REG].m_pMostRecentReader->m_pInst)
|
|
|
|
if( !(ComponentReadMask & COMPONENT_MASKS[Component]) )
|
|
continue;
|
|
|
|
if( NULL != pWriterInCurrCycle[Component] )
|
|
{
|
|
if( !pWriterInCurrCycle[Component]->m_pPreviousReader ||
|
|
pWriterInCurrCycle[Component]->m_pPreviousReader->m_pInst != m_pCurrInst )
|
|
{
|
|
if( !pRegFile->m_pAccessHistory[Component][RegNum].InsertReadBeforeWrite(
|
|
pWriterInCurrCycle[Component], m_pCurrInst ) )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory");
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
}
|
|
else if( PREV_READER(Component,RegNum) != m_pCurrInst )
|
|
{
|
|
if( !pRegFile->m_pAccessHistory[Component][RegNum].NewAccess(m_pCurrInst,FALSE) )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory");
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_MultipleDependentTextureReads
|
|
//
|
|
// ** Rule:
|
|
//
|
|
// Multiple dependent texture reads are disallowed. So texture read results
|
|
// can be used as an address in a subsequent read, but the results from that
|
|
// second read cannot be used as an address in yet another subsequent read.
|
|
//
|
|
// As pseudocode:
|
|
//
|
|
// if current instruction (x) is a tex-op that reads a texture
|
|
// for each source param of x
|
|
// if the register is a texture register
|
|
// and there exists a previous writer (y),
|
|
// and y is a tex op that reads a texture
|
|
// if there exists a souce parameter of y that was previously
|
|
// written by an instruction that reads a texture (z)
|
|
// SPEW(Error)
|
|
//
|
|
// NOTE that it is assumed that tex ops must write to all components, so
|
|
// only the read/write history for the R component is being checked.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction. This rule must be called before Rule_ValidDstParam(),
|
|
// and Rule_SrcInitialized()
|
|
// but after Rule_ValidSrcParams()
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
BOOL CPShaderValidator14::Rule_MultipleDependentTextureReads()
|
|
{
|
|
DSTPARAM* pDstParam = &(m_pCurrInst->m_DstParam);
|
|
UINT DstRegNum = pDstParam->m_RegNum;
|
|
char RegChar;
|
|
#define THREE_TUPLE 3
|
|
|
|
if( !_CURR_PS_INST->m_bTexOp )
|
|
return TRUE;
|
|
|
|
BOOL bDestParamIsSrc = pDstParam->m_ComponentReadMask;
|
|
|
|
UINT SrcParamCount = bDestParamIsSrc ? 1 : m_pCurrInst->m_SrcParamCount; // assumes if dest param is src,
|
|
// there are no source params in instruction
|
|
if( D3DSPR_TEMP != pDstParam->m_RegType )
|
|
return TRUE;
|
|
|
|
for( UINT SrcParam = 0; SrcParam < SrcParamCount; SrcParam++ )
|
|
{
|
|
|
|
if( !bDestParamIsSrc && m_bSrcParamError[SrcParam] )
|
|
continue;
|
|
|
|
SRCPARAM* pSrcParam = bDestParamIsSrc ? NULL : &(m_pCurrInst->m_SrcParam[SrcParam]);
|
|
UINT SrcRegNum = bDestParamIsSrc ? DstRegNum : pSrcParam->m_RegNum;
|
|
CRegisterFile* pSrcRegFile = NULL;
|
|
|
|
switch( bDestParamIsSrc ? pDstParam->m_RegType : pSrcParam->m_RegType )
|
|
{
|
|
case D3DSPR_TEMP:
|
|
pSrcRegFile = m_pTempRegFile;
|
|
RegChar = 'r';
|
|
break;
|
|
case D3DSPR_TEXTURE:
|
|
pSrcRegFile = m_pTextureRegFile;
|
|
RegChar = 't';
|
|
break;
|
|
}
|
|
if( !pSrcRegFile )
|
|
continue;
|
|
|
|
if( SrcRegNum >= pSrcRegFile->GetNumRegs() )
|
|
continue;
|
|
|
|
for( UINT SrcComp = 0; SrcComp < THREE_TUPLE; SrcComp++ ) // Tex ops only read 3-tuples.
|
|
{
|
|
CAccessHistoryNode* pPreviousWriter = pSrcRegFile->m_pAccessHistory[SrcComp][SrcRegNum].m_pMostRecentWriter;
|
|
CPSInstruction* pInst = pPreviousWriter ? (CPSInstruction*)pPreviousWriter->m_pInst : NULL;
|
|
|
|
if( !pInst || !pInst->m_bTexOp )
|
|
continue;
|
|
|
|
// If the previous writer was in the current phase of the shader, spew an error.
|
|
if( !m_pPhaseMarkerInst || (pInst->m_CycleNum > m_pPhaseMarkerInst->m_CycleNum) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"The current tex* instruction reads from %c%d, which was written earlier by another "\
|
|
"tex* instruction in the same block of tex* instructions. Dependent reads "\
|
|
"are not permitted within a single block of tex* instructions. To perform a dependent read, "\
|
|
"separate texture coordinate derivation from the tex* instruction using the coordinates "\
|
|
"with a 'phase' marker.",
|
|
RegChar,SrcRegNum );
|
|
|
|
m_ErrorCount++;
|
|
|
|
return TRUE; // Lets only spew this warning once per instruction.
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidDstParam
|
|
//
|
|
// ** Rule:
|
|
// I instruction is D3DSIO_DEF, then do nothing - this case has its own separate rule
|
|
// The dst register must be writable.
|
|
// If the instruction has a dest parameter (i.e. every instruction except NOP), then
|
|
// the dst register must be of type D3DSPR_TEXTURE, and
|
|
// register # must be within range
|
|
// if instruction is a texture instruction, then
|
|
// the dst register must be of type D3DSPR_TEMP, and
|
|
// the writemask must be D3DSP_WRITEMASK_ALL
|
|
// or (.rgb for texcrd, .rg for texcrd with _dw source mod), and
|
|
// the dst modifier must be D3DSPDM_NONE (or _SAT on version > 1.1), and
|
|
// the dst shift must be none
|
|
// else (non tex instruction)
|
|
// the dst modifier must be D3DSPDM_NONE or _SATURATE, and
|
|
// dst shift must be /2, none, *2, or *4
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
// NOTE: After checking the dst parameter, if no error was found,
|
|
// the write to the appropriate component(s) of the destination register
|
|
// is recorded by this function, so subsequent rules may check for previous
|
|
// write to registers.
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidDstParam() // could break this down for more granularity
|
|
{
|
|
BOOL bFoundDstError = FALSE;
|
|
DSTPARAM* pDstParam = &(m_pCurrInst->m_DstParam);
|
|
UINT RegNum = pDstParam->m_RegNum;
|
|
if( D3DSIO_DEF == m_pCurrInst->m_Type )
|
|
{
|
|
// _DEF is a special instruction whose dest is a const register.
|
|
// We do the checking for this in a separate function.
|
|
// Also, we don't need to keep track of the fact that
|
|
// this instruction wrote to a register (done below),
|
|
// since _DEF just declares a constant.
|
|
return TRUE;
|
|
}
|
|
|
|
if( pDstParam->m_bParamUsed )
|
|
{
|
|
UINT ValidRegNum = 0;
|
|
BOOL bWritingToDest = TRUE;
|
|
|
|
switch( pDstParam->m_RegType )
|
|
{
|
|
case D3DSPR_TEMP:
|
|
ValidRegNum = m_pTempRegFile->GetNumRegs();
|
|
break;
|
|
case D3DSPR_TEXTURE:
|
|
ValidRegNum = m_pTempRegFile->GetNumRegs();
|
|
break;
|
|
}
|
|
|
|
if( D3DSIO_TEXKILL == m_pCurrInst->m_Type )
|
|
{
|
|
bWritingToDest = FALSE;
|
|
}
|
|
|
|
if( 0 == ValidRegNum ||
|
|
(D3DSPR_TEXTURE == pDstParam->m_RegType && bWritingToDest) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid register type for destination param." );
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
else if( RegNum >= ValidRegNum )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid destination register number: %d. Max allowed for this register type is %d.",
|
|
RegNum, ValidRegNum - 1);
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
|
|
if( _CURR_PS_INST->m_bTexOp )
|
|
{
|
|
switch( m_pCurrInst->m_Type )
|
|
{
|
|
case D3DSIO_TEXCOORD:
|
|
if( D3DSPSM_DW == m_pCurrInst->m_SrcParam[0].m_SrcMod )
|
|
{
|
|
if( (D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1) != pDstParam->m_WriteMask )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "texcrd with _dw(=_da) source modifier must use .xy(=.rg) destination writemask.");
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( (D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2) != pDstParam->m_WriteMask )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "texcrd must use .xyz(=.rgb) destination writemask.");
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
}
|
|
break;
|
|
case D3DSIO_TEX:
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_TEXDEPTH:
|
|
if( D3DSP_WRITEMASK_ALL != pDstParam->m_WriteMask )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "texld/texkill/texdepth instructions must write all components." );
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
switch( pDstParam->m_DstMod )
|
|
{
|
|
case D3DSPDM_NONE:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Instruction modifiers not allowed for tex* instructions." );
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
switch( pDstParam->m_DstShift )
|
|
{
|
|
case DSTSHIFT_NONE:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Destination shift not allowed for tex* instructions." );
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
switch( pDstParam->m_DstMod )
|
|
{
|
|
case D3DSPDM_NONE:
|
|
case D3DSPDM_SATURATE:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid instruction modifier." );
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
|
|
switch( pDstParam->m_DstShift )
|
|
{
|
|
case DSTSHIFT_NONE:
|
|
case DSTSHIFT_X2:
|
|
case DSTSHIFT_X4:
|
|
case DSTSHIFT_X8:
|
|
case DSTSHIFT_D2:
|
|
case DSTSHIFT_D4:
|
|
case DSTSHIFT_D8:
|
|
break;
|
|
default:
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid destination shift." );
|
|
m_ErrorCount++;
|
|
bFoundDstError = TRUE;
|
|
}
|
|
}
|
|
|
|
// Update register file to indicate write.
|
|
if( !bFoundDstError && bWritingToDest)
|
|
{
|
|
CRegisterFile* pRegFile = NULL;
|
|
DWORD WriteMask = pDstParam->m_WriteMask;
|
|
|
|
switch( pDstParam->m_RegType )
|
|
{
|
|
case D3DSPR_TEMP: pRegFile = m_pTempRegFile; break;
|
|
}
|
|
|
|
if( pRegFile )
|
|
{
|
|
if( WriteMask & D3DSP_WRITEMASK_0 )
|
|
pRegFile->m_pAccessHistory[0][RegNum].NewAccess(m_pCurrInst,TRUE);
|
|
|
|
if( WriteMask & D3DSP_WRITEMASK_1 )
|
|
pRegFile->m_pAccessHistory[1][RegNum].NewAccess(m_pCurrInst,TRUE);
|
|
|
|
if( WriteMask & D3DSP_WRITEMASK_2 )
|
|
pRegFile->m_pAccessHistory[2][RegNum].NewAccess(m_pCurrInst,TRUE);
|
|
else if( D3DSIO_TEXCOORD == m_pCurrInst->m_Type )
|
|
{
|
|
// texcrd without b writemask uninitializes b channel.
|
|
// alpha also gets uninitialized, but phase marker alpha-nuke takes care of that anyway,
|
|
// and if the texcrd was in the first phase, noone could have written to the register
|
|
// so there would be nothing to nuke.
|
|
if( pRegFile->m_pAccessHistory[2][RegNum].m_pMostRecentWriter )
|
|
{
|
|
m_pTempRegFile->m_pAccessHistory[2][RegNum].~CAccessHistory();
|
|
m_pTempRegFile->m_pAccessHistory[2][RegNum].CAccessHistory::CAccessHistory();
|
|
m_TempRegsWithZappedBlue |= 1 << RegNum;
|
|
}
|
|
}
|
|
|
|
if( WriteMask & D3DSP_WRITEMASK_3 )
|
|
pRegFile->m_pAccessHistory[3][RegNum].NewAccess(m_pCurrInst,TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidRegisterPortUsage
|
|
//
|
|
// ** Rule:
|
|
// Each register class (TEXTURE,INPUT,CONST) may only appear as parameters
|
|
// in an individual instruction up to a maximum number of times.
|
|
//
|
|
// Multiple accesses to the same register number (in the same register class)
|
|
// only count as one access.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidRegisterPortUsage()
|
|
{
|
|
UINT i, j;
|
|
UINT TempRegPortUsage[SHADER_INSTRUCTION_MAX_SRCPARAMS];
|
|
UINT InputRegPortUsage[SHADER_INSTRUCTION_MAX_SRCPARAMS];
|
|
UINT ConstRegPortUsage[SHADER_INSTRUCTION_MAX_SRCPARAMS];
|
|
UINT TextureRegPortUsage[SHADER_INSTRUCTION_MAX_SRCPARAMS];
|
|
UINT NumUniqueTempRegs = 0;
|
|
UINT NumUniqueInputRegs = 0;
|
|
UINT NumUniqueConstRegs = 0;
|
|
UINT NumUniqueTextureRegs = 0;
|
|
D3DSHADER_PARAM_REGISTER_TYPE RegType;
|
|
UINT RegNum;
|
|
|
|
static UINT s_TempRegPortUsageAcrossCoIssue[SHADER_INSTRUCTION_MAX_SRCPARAMS*2];
|
|
static UINT s_InputRegPortUsageAcrossCoIssue[SHADER_INSTRUCTION_MAX_SRCPARAMS*2];
|
|
static UINT s_ConstRegPortUsageAcrossCoIssue[SHADER_INSTRUCTION_MAX_SRCPARAMS*2];
|
|
static UINT s_TextureRegPortUsageAcrossCoIssue[SHADER_INSTRUCTION_MAX_SRCPARAMS*2];
|
|
static UINT s_NumUniqueTempRegsAcrossCoIssue;
|
|
static UINT s_NumUniqueInputRegsAcrossCoIssue;
|
|
static UINT s_NumUniqueConstRegsAcrossCoIssue;
|
|
static UINT s_NumUniqueTextureRegsAcrossCoIssue;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_NumUniqueTempRegsAcrossCoIssue = 0;
|
|
s_NumUniqueInputRegsAcrossCoIssue = 0;
|
|
s_NumUniqueConstRegsAcrossCoIssue = 0;
|
|
s_NumUniqueTextureRegsAcrossCoIssue = 0;
|
|
}
|
|
|
|
for( i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
|
|
{
|
|
UINT* pRegPortUsage = NULL;
|
|
UINT* pNumUniqueRegs = NULL;
|
|
|
|
RegType = m_pCurrInst->m_SrcParam[i].m_RegType;
|
|
RegNum = m_pCurrInst->m_SrcParam[i].m_RegNum;
|
|
|
|
switch( RegType )
|
|
{
|
|
case D3DSPR_TEMP:
|
|
pRegPortUsage = TempRegPortUsage;
|
|
pNumUniqueRegs = &NumUniqueTempRegs;
|
|
break;
|
|
case D3DSPR_INPUT:
|
|
pRegPortUsage = InputRegPortUsage;
|
|
pNumUniqueRegs = &NumUniqueInputRegs;
|
|
break;
|
|
case D3DSPR_CONST:
|
|
pRegPortUsage = ConstRegPortUsage;
|
|
pNumUniqueRegs = &NumUniqueConstRegs;
|
|
break;
|
|
case D3DSPR_TEXTURE:
|
|
pRegPortUsage = TextureRegPortUsage;
|
|
pNumUniqueRegs = &NumUniqueTextureRegs;
|
|
break;
|
|
}
|
|
|
|
if( !pRegPortUsage ) continue;
|
|
|
|
BOOL bRegAlreadyAccessed = FALSE;
|
|
for( j = 0; j < *pNumUniqueRegs; j++ )
|
|
{
|
|
if( pRegPortUsage[j] == RegNum )
|
|
{
|
|
bRegAlreadyAccessed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( !bRegAlreadyAccessed )
|
|
{
|
|
pRegPortUsage[*pNumUniqueRegs] = RegNum;
|
|
(*pNumUniqueRegs)++;
|
|
}
|
|
|
|
}
|
|
|
|
if( NumUniqueTempRegs > m_pTempRegFile->GetNumReadPorts() )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different temp registers (r#) read by instruction. Max. different temp registers readable per instruction is %d.",
|
|
NumUniqueTempRegs, m_pTempRegFile->GetNumReadPorts());
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( NumUniqueInputRegs > m_pInputRegFile->GetNumReadPorts() )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different input registers (v#) read by instruction. Max. different input registers readable per instruction is %d.",
|
|
NumUniqueInputRegs, m_pInputRegFile->GetNumReadPorts());
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( NumUniqueConstRegs > m_pConstRegFile->GetNumReadPorts() )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different constant registers (c#) read by instruction. Max. different constant registers readable per instruction is %d.",
|
|
NumUniqueConstRegs, m_pConstRegFile->GetNumReadPorts());
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( NumUniqueTextureRegs > m_pTextureRegFile->GetNumReadPorts() )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different texture coordinate registers (t#) read by instruction. Max. different texture registers readable per instruction is %d.",
|
|
NumUniqueTextureRegs, m_pTextureRegFile->GetNumReadPorts());
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
// Read port limit for different register numbers of any one register type across co-issued instructions is MAX_READPORTS_ACROSS_COISSUE total.
|
|
|
|
if( _CURR_PS_INST->m_bCoIssue && _PREV_PS_INST && !(_PREV_PS_INST->m_bCoIssue)) // second 2 clauses are just a simple sanity check -> co-issue only involved 2 instructions.
|
|
{
|
|
for( i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
|
|
{
|
|
UINT* pRegPortUsageAcrossCoIssue = NULL;
|
|
UINT* pNumUniqueRegsAcrossCoIssue = NULL;
|
|
|
|
RegType = m_pCurrInst->m_SrcParam[i].m_RegType;
|
|
RegNum = m_pCurrInst->m_SrcParam[i].m_RegNum;
|
|
|
|
switch( RegType )
|
|
{
|
|
case D3DSPR_TEMP:
|
|
pRegPortUsageAcrossCoIssue = s_TempRegPortUsageAcrossCoIssue;
|
|
pNumUniqueRegsAcrossCoIssue = &s_NumUniqueTempRegsAcrossCoIssue;
|
|
break;
|
|
case D3DSPR_INPUT:
|
|
pRegPortUsageAcrossCoIssue = s_InputRegPortUsageAcrossCoIssue;
|
|
pNumUniqueRegsAcrossCoIssue = &s_NumUniqueInputRegsAcrossCoIssue;
|
|
break;
|
|
case D3DSPR_CONST:
|
|
pRegPortUsageAcrossCoIssue = s_ConstRegPortUsageAcrossCoIssue;
|
|
pNumUniqueRegsAcrossCoIssue = &s_NumUniqueConstRegsAcrossCoIssue;
|
|
break;
|
|
case D3DSPR_TEXTURE:
|
|
pRegPortUsageAcrossCoIssue = s_TextureRegPortUsageAcrossCoIssue;
|
|
pNumUniqueRegsAcrossCoIssue = &s_NumUniqueTextureRegsAcrossCoIssue;
|
|
break;
|
|
}
|
|
|
|
if( !pRegPortUsageAcrossCoIssue ) continue;
|
|
|
|
BOOL bRegAlreadyAccessed = FALSE;
|
|
for( j = 0; j < *pNumUniqueRegsAcrossCoIssue; j++ )
|
|
{
|
|
if( pRegPortUsageAcrossCoIssue[j] == RegNum )
|
|
{
|
|
bRegAlreadyAccessed = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if( !bRegAlreadyAccessed )
|
|
{
|
|
pRegPortUsageAcrossCoIssue[*pNumUniqueRegsAcrossCoIssue] = RegNum;
|
|
(*pNumUniqueRegsAcrossCoIssue)++;
|
|
}
|
|
}
|
|
|
|
#define MAX_READPORTS_ACROSS_COISSUE 3
|
|
|
|
if( s_NumUniqueTempRegsAcrossCoIssue > MAX_READPORTS_ACROSS_COISSUE )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"%d different temp registers (r#) read over 2 co-issued instructions. "\
|
|
"Max. different register numbers from any one register type readable across co-issued instructions is %d.",
|
|
s_NumUniqueTempRegsAcrossCoIssue, MAX_READPORTS_ACROSS_COISSUE);
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( s_NumUniqueInputRegsAcrossCoIssue > MAX_READPORTS_ACROSS_COISSUE )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"%d different input registers (v#) read over 2 co-issued instructions. "\
|
|
"Max. different register numbers from any one register type readable across co-issued instructions is %d.",
|
|
s_NumUniqueInputRegsAcrossCoIssue, MAX_READPORTS_ACROSS_COISSUE);
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( s_NumUniqueConstRegsAcrossCoIssue > MAX_READPORTS_ACROSS_COISSUE )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"%d different constant registers (c#) read over 2 co-issued instructions. "\
|
|
"Max. different register numbers from any one register type readable across co-issued instructions is %d.",
|
|
s_NumUniqueConstRegsAcrossCoIssue, MAX_READPORTS_ACROSS_COISSUE);
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( s_NumUniqueTextureRegsAcrossCoIssue > MAX_READPORTS_ACROSS_COISSUE )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"%d different texture coordinate registers (t#) read over 2 co-issued instructions. "\
|
|
"Max. different register numbers from any one register type readable across co-issued instructions is %d.",
|
|
s_NumUniqueTextureRegsAcrossCoIssue, MAX_READPORTS_ACROSS_COISSUE);
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
|
|
if( !_CURR_PS_INST->m_bCoIssue )
|
|
{
|
|
// Copy all state to static vars so that in case next instruction is co-issued with this one,
|
|
// cross-coissue read port limit of 3 can be enforced.
|
|
memcpy(&s_TempRegPortUsageAcrossCoIssue,&TempRegPortUsage,NumUniqueTempRegs*sizeof(UINT));
|
|
memcpy(&s_InputRegPortUsageAcrossCoIssue,&InputRegPortUsage,NumUniqueInputRegs*sizeof(UINT));
|
|
memcpy(&s_ConstRegPortUsageAcrossCoIssue,&ConstRegPortUsage,NumUniqueConstRegs*sizeof(UINT));
|
|
memcpy(&s_TextureRegPortUsageAcrossCoIssue,&TextureRegPortUsage,NumUniqueTextureRegs*sizeof(UINT));
|
|
s_NumUniqueTempRegsAcrossCoIssue = NumUniqueTempRegs;
|
|
s_NumUniqueInputRegsAcrossCoIssue = NumUniqueInputRegs;
|
|
s_NumUniqueConstRegsAcrossCoIssue = NumUniqueConstRegs;
|
|
s_NumUniqueTextureRegsAcrossCoIssue = NumUniqueTextureRegs;
|
|
}
|
|
else
|
|
{
|
|
// reset counts because the next instruction cannot be co-issued with this one.
|
|
s_NumUniqueTempRegsAcrossCoIssue = 0;
|
|
s_NumUniqueInputRegsAcrossCoIssue = 0;
|
|
s_NumUniqueConstRegsAcrossCoIssue = 0;
|
|
s_NumUniqueTextureRegsAcrossCoIssue = 0;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidTexOpStageAndRegisterUsage
|
|
//
|
|
// ** Rule:
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidTexOpStageAndRegisterUsage()
|
|
{
|
|
static DWORD s_RegUsed; // bitfield representing if a retister has been used as a destination in this block of tex ops.
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_RegUsed = 0;
|
|
}
|
|
else if( D3DSIO_PHASE == m_pCurrInst->m_Type )
|
|
{
|
|
s_RegUsed = 0;
|
|
}
|
|
|
|
if( !_CURR_PS_INST->m_bTexOp )
|
|
return TRUE;
|
|
|
|
if( D3DSPR_TEMP != m_pCurrInst->m_DstParam.m_RegType )
|
|
return TRUE;
|
|
|
|
UINT RegNum = m_pCurrInst->m_DstParam.m_RegNum;
|
|
if( RegNum >= m_pTempRegFile->GetNumRegs() )
|
|
return TRUE; // error spewed elsewhere
|
|
|
|
if( s_RegUsed & (1<<RegNum) )
|
|
{
|
|
if( 1 == m_Phase )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Register r%d (and thus texture stage %d) already used as a destination for a tex* instruction in this block of the shader. "\
|
|
"Second use of this register as a tex* destination is only available after the phase marker. ",
|
|
RegNum, RegNum );
|
|
}
|
|
else // 2 == m_Phase
|
|
{
|
|
if( m_bPhaseMarkerInShader )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Register r%d (and thus texture stage %d) already used as a destination for a tex* instruction in this block of the shader. "\
|
|
"An r# register may be used as the destination for a tex* instruction at most once before the phase marker and once after. ",
|
|
RegNum, RegNum );
|
|
}
|
|
else // no phase marker present. Different spew to indicate
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Register r%d (and thus texture stage %d) already used as a destination for a tex* instruction in this block of the shader. "\
|
|
"To perform two tex* instructions with the same destination register, they must be separated by inserting a phase marker. ",
|
|
RegNum, RegNum );
|
|
}
|
|
}
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
s_RegUsed |= (1<<RegNum);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_TexOpAfterArithmeticOp
|
|
//
|
|
// ** Rule:
|
|
// Tex ops (see IsTexOp() for which instructions are considered tex ops)
|
|
// must appear before any other instruction, with the exception of DEF or NOP.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_TexOpAfterArithmeticOp()
|
|
{
|
|
static BOOL s_bSeenArithmeticOp;
|
|
static BOOL s_bRuleDisabled;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_bSeenArithmeticOp = FALSE;
|
|
}
|
|
|
|
if( !(_CURR_PS_INST->m_bTexOp)
|
|
&& (D3DSIO_NOP != m_pCurrInst->m_Type)
|
|
&& (D3DSIO_DEF != m_pCurrInst->m_Type)
|
|
&& (D3DSIO_PHASE != m_pCurrInst->m_Type) )
|
|
{
|
|
s_bSeenArithmeticOp = TRUE;
|
|
return TRUE;
|
|
}
|
|
|
|
if( D3DSIO_PHASE == m_pCurrInst->m_Type )
|
|
{
|
|
s_bSeenArithmeticOp = FALSE; // reset flag because we are in new phase of shader.
|
|
return TRUE;
|
|
}
|
|
|
|
if( _CURR_PS_INST->m_bTexOp && s_bSeenArithmeticOp )
|
|
{
|
|
if( m_bPhaseMarkerInShader )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "tex* instructions cannot be after arithmetic instructions "\
|
|
"within one phase of the shader. Each phase can have a block of "\
|
|
"tex* instructions followed by a block of arithmetic instructions. " );
|
|
}
|
|
else
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "tex* instructions cannot be after arithmetic instructions. "\
|
|
"The exception is if a phase marker is present in the shader - "\
|
|
"this separates a shader into two phases. Each phase may have "\
|
|
"a set of tex* instructions followed by a set of arithmetic instructions. " );
|
|
}
|
|
m_ErrorCount++;
|
|
s_bRuleDisabled = TRUE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidMarker
|
|
//
|
|
// ** Rule:
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// FALSE if more than one marker encountered. Else TRUE
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidMarker()
|
|
{
|
|
static BOOL s_bSeenMarker;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_bSeenMarker = FALSE;
|
|
}
|
|
|
|
if( D3DSIO_PHASE != m_pCurrInst->m_Type )
|
|
return TRUE;
|
|
|
|
if( s_bSeenMarker )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Multiple phase markers not permitted. Aborting shader validation." );
|
|
m_ErrorCount++;
|
|
return FALSE;
|
|
}
|
|
|
|
s_bSeenMarker = TRUE;
|
|
m_pPhaseMarkerInst = (CPSInstruction*)m_pCurrInst;
|
|
m_Phase++;
|
|
|
|
// Loop through all temp registers and nuke alpha access history (if any).
|
|
// Remember what we nuked, so if the shader tries to read one of these nuked alphas, we
|
|
// can debug spew that certain hardware is wacko and can't help but commit this atrocity.
|
|
for( UINT i = 0; i < m_pTempRegFile->GetNumRegs(); i++ )
|
|
{
|
|
if( m_pTempRegFile->m_pAccessHistory[3][i].m_pMostRecentWriter )
|
|
{
|
|
m_pTempRegFile->m_pAccessHistory[3][i].~CAccessHistory();
|
|
m_pTempRegFile->m_pAccessHistory[3][i].CAccessHistory::CAccessHistory();
|
|
m_TempRegsWithZappedAlpha |= 1 << i;
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidTEXKILLInstruction
|
|
//
|
|
// ** Rule:
|
|
// texkill may only be present in phase 2
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidTEXKILLInstruction()
|
|
{
|
|
if( (D3DSIO_TEXKILL == m_pCurrInst->m_Type) && (1 == m_Phase))
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "When a phase marker is present in a shader, texkill is only permitted after the phase marker." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidBEMInstruction
|
|
//
|
|
// ** Rule:
|
|
// bem must have writemask .r, .g or .rg
|
|
// bem may only be present once in a shader, in phase 1.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidBEMInstruction()
|
|
{
|
|
static BOOL s_bSeenBem;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_bSeenBem = FALSE;
|
|
}
|
|
|
|
if( (D3DSIO_BEM == m_pCurrInst->m_Type))
|
|
{
|
|
if( s_bSeenBem )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "bem may only be used once in a shader." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( 2 == m_Phase )
|
|
{
|
|
if( m_bPhaseMarkerInShader )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "bem may only be used before the phase marker." );
|
|
}
|
|
else
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "To use bem, a phase marker must be present later in the shader." );
|
|
}
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( m_pCurrInst->m_DstParam.m_WriteMask != (D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1))
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Writemask for bem must be '.rg'" );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
for( UINT i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
|
|
{
|
|
SRCPARAM* pSrcParam = &(m_pCurrInst->m_SrcParam[i]);
|
|
|
|
if(m_bSrcParamError[i])
|
|
continue;
|
|
|
|
if( 0 == i )
|
|
{
|
|
if( (D3DSPR_TEMP != pSrcParam->m_RegType) &&
|
|
(D3DSPR_CONST != pSrcParam->m_RegType) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "First source parameter for bem must be temp (r#) or constant (c#) register." );
|
|
m_ErrorCount++;
|
|
|
|
}
|
|
}
|
|
else if( 1 == i )
|
|
{
|
|
if( (D3DSPR_TEMP != pSrcParam->m_RegType ) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Second source parameter for bem must be temp (r#) register." );
|
|
m_ErrorCount++;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidTEXDEPTHInstruction
|
|
//
|
|
// ** Rule:
|
|
// texdepth must operate on r5.
|
|
// texdepth may only be present after a phase marker.
|
|
// texdepth may only be used once.
|
|
// Once texdepth has been used in a shader, r5 is no longer available
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidTEXDEPTHInstruction()
|
|
{
|
|
static BOOL s_bSeenTexDepth;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_bSeenTexDepth = FALSE;
|
|
}
|
|
|
|
if( D3DSIO_TEXDEPTH == m_pCurrInst->m_Type )
|
|
{
|
|
if( s_bSeenTexDepth )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Only one use of texdepth is permitted." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
s_bSeenTexDepth = TRUE;
|
|
|
|
DSTPARAM* pDstParam = &m_pCurrInst->m_DstParam;
|
|
if( (5 != pDstParam->m_RegNum) || (D3DSPR_TEMP != pDstParam->m_RegType) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Destination for texdepth must be r5." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( (D3DSIO_TEXDEPTH == m_pCurrInst->m_Type) && (1 == m_Phase))
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "When a phase marker is present in a shader, texdepth is only permitted after the phase marker." );
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
else if( s_bSeenTexDepth )
|
|
{
|
|
UINT RegNum;
|
|
D3DSHADER_PARAM_REGISTER_TYPE RegType;
|
|
for( UINT i = 0; i <= m_pCurrInst->m_SrcParamCount; i++ )
|
|
{
|
|
if( m_pCurrInst->m_SrcParamCount == i )
|
|
{
|
|
RegNum = m_pCurrInst->m_DstParam.m_RegNum;
|
|
RegType = m_pCurrInst->m_DstParam.m_RegType;
|
|
}
|
|
else
|
|
{
|
|
RegNum = m_pCurrInst->m_SrcParam[i].m_RegNum;
|
|
RegType = m_pCurrInst->m_SrcParam[i].m_RegType;
|
|
}
|
|
if( (5 == RegNum) && (D3DSPR_TEMP == RegType) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "After texdepth instruction, r5 is no longer available in shader." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidDEFInstruction
|
|
//
|
|
// ** Rule:
|
|
// For the DEF instruction, make sure the dest parameter is a valid constant,
|
|
// and it has no modifiers.
|
|
//
|
|
// NOTE that we are pretending this instruction only has a dst parameter.
|
|
// We skipped over the 4 source parameters since they are immediate floats,
|
|
// for which there is nothing that can be checked.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidDEFInstruction()
|
|
{
|
|
static BOOL s_bDEFInstructionAllowed;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_bDEFInstructionAllowed = TRUE;
|
|
}
|
|
|
|
if( D3DSIO_COMMENT != m_pCurrInst->m_Type &&
|
|
D3DSIO_DEF != m_pCurrInst->m_Type )
|
|
{
|
|
s_bDEFInstructionAllowed = FALSE;
|
|
}
|
|
else if( D3DSIO_DEF == m_pCurrInst->m_Type )
|
|
{
|
|
if( !s_bDEFInstructionAllowed )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Const declaration (def) must appear before other instructions." );
|
|
m_ErrorCount++;
|
|
}
|
|
DSTPARAM* pDstParam = &m_pCurrInst->m_DstParam;
|
|
if( D3DSP_WRITEMASK_ALL != pDstParam->m_WriteMask ||
|
|
D3DSPDM_NONE != pDstParam->m_DstMod ||
|
|
DSTSHIFT_NONE != pDstParam->m_DstShift ||
|
|
D3DSPR_CONST != pDstParam->m_RegType
|
|
)
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Destination for def instruction must be of the form c# (# = reg number, no modifiers)." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
// Check that the register number is in bounds
|
|
if( D3DSPR_CONST == pDstParam->m_RegType &&
|
|
pDstParam->m_RegNum >= m_pConstRegFile->GetNumRegs() )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid const register num: %d. Max allowed is %d.",
|
|
pDstParam->m_RegNum,m_pConstRegFile->GetNumRegs() - 1);
|
|
m_ErrorCount++;
|
|
|
|
}
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidInstructionPairing
|
|
//
|
|
// ** Rule:
|
|
// - If an instruction is co-issued with another instruction,
|
|
// make sure that both do not write to any of RGB at the same time,
|
|
// and that neither instruction individually writes to all of RGBA.
|
|
//
|
|
// - Co-issue can only involve 2 instructions,
|
|
// so consecutive instructions cannot have the "+" prefix (D3DSI_COISSUE).
|
|
//
|
|
// - Co-issue of instructions only applies to pixel blend instructions (non tex-ops).
|
|
//
|
|
// - The first color blend instruction cannot have "+" (D3DSI_COISSUE) set either.
|
|
//
|
|
// - NOP may not be used in a co-issue pair.
|
|
//
|
|
// - DP3 (dot product) always uses the color/vector pipeline (even if it is not writing
|
|
// to color components). Thus:
|
|
// - An instruction co-issued with a dot-product can only write to alpha.
|
|
// - A dot-product that writes to alpha cannot be co-issued.
|
|
// - Two dot-products cannot be co-issued.
|
|
//
|
|
// - For version <= 1.0, coissued instructions must write to the same register.
|
|
//
|
|
// ------------------
|
|
// examples:
|
|
//
|
|
// valid pair: mov r0.a, c0
|
|
// +add r1.rgb, v1, c1 (note dst reg #'s can be different)
|
|
//
|
|
// another valid pair: mov r0.a, c0
|
|
// +add r0.rgb, v1, c1
|
|
//
|
|
// another valid pair: dp3 r0.rgb, t1, v1
|
|
// +mul r0.a, t0, v0
|
|
//
|
|
// another valid pair: mov r0.a, c0
|
|
// +add r0.a, t0, t1
|
|
//
|
|
// invalid pair: mov r0.rgb, c0
|
|
// +add r0, t0, t1 (note the dst writes to rgba)
|
|
//
|
|
// another invalid pair: mov r1.rgb, c1
|
|
// +dp3 r0.a, t0, t1 (dp3 is using up color/vector pipe)
|
|
//
|
|
// ** When to call:
|
|
// Per instruction.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidInstructionPairing()
|
|
{
|
|
static BOOL s_bSeenArithOp;
|
|
BOOL bCurrInstCoIssuable = TRUE;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
s_bSeenArithOp = FALSE;
|
|
}
|
|
|
|
if( !_CURR_PS_INST->m_bTexOp )
|
|
{
|
|
switch( m_pCurrInst->m_Type )
|
|
{
|
|
case D3DSIO_PHASE:
|
|
case D3DSIO_DEF:
|
|
case D3DSIO_NOP:
|
|
case D3DSIO_DP4:
|
|
bCurrInstCoIssuable = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( D3DSIO_PHASE == m_pCurrInst->m_Type )
|
|
{
|
|
s_bSeenArithOp = FALSE;
|
|
}
|
|
else if( bCurrInstCoIssuable )
|
|
{
|
|
s_bSeenArithOp = TRUE;
|
|
}
|
|
|
|
if( !_CURR_PS_INST->m_bCoIssue )
|
|
return TRUE;
|
|
|
|
if( _CURR_PS_INST->m_bTexOp )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Cannot set co-issue ('+') on a tex* instruction. Co-issue only applies to arithmetic instructions." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
if( !s_bSeenArithOp || NULL == m_pCurrInst->m_pPrevInst )
|
|
{
|
|
if( D3DSIO_PHASE == m_pCurrInst->m_Type )
|
|
{
|
|
// cannot have co-issue set because we haven't seen an arithmetic op above.
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Phase marker cannot be co-issued.");
|
|
}
|
|
else
|
|
{
|
|
// cannot have co-issue set because we haven't seen an arithmetic op above.
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Instruction cannot have co-issue ('+') set without a previous arithmetic instruction to pair with.");
|
|
}
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
if( _PREV_PS_INST->m_bCoIssue )
|
|
{
|
|
// consecutive instructions cannot have co-issue set.
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Cannot set co-issue ('+') on consecutive instructions." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
for( UINT i = 0; i < 2; i++ )
|
|
{
|
|
CBaseInstruction* pInst;
|
|
if( 0 == i )
|
|
pInst = m_pCurrInst;
|
|
else
|
|
pInst = m_pCurrInst->m_pPrevInst;
|
|
|
|
switch( pInst->m_Type )
|
|
{
|
|
case D3DSIO_PHASE:
|
|
// Phase marker cannot be co-issued
|
|
Spew( SPEW_INSTRUCTION_ERROR, pInst, "phase marker cannot be co-issued." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
case D3DSIO_DEF:
|
|
// DEF cannot be co-issued
|
|
Spew( SPEW_INSTRUCTION_ERROR, pInst, "def cannot be co-issued." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
case D3DSIO_NOP:
|
|
// NOP cannot be co-issued
|
|
Spew( SPEW_INSTRUCTION_ERROR, pInst, "nop cannot be co-issued." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
case D3DSIO_DP4:
|
|
// DP4 cannot be co-issued
|
|
Spew( SPEW_INSTRUCTION_ERROR, pInst, "dp4 cannot be co-issued." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
case D3DSIO_BEM:
|
|
// BEM cannot be co-issued
|
|
Spew( SPEW_INSTRUCTION_ERROR, pInst, "bem cannot be co-issued." );
|
|
m_ErrorCount++;
|
|
return TRUE;
|
|
}
|
|
|
|
}
|
|
|
|
#define COLOR_WRITE_MASK (D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2)
|
|
#define ALPHA_WRITE_MASK D3DSP_WRITEMASK_3
|
|
DWORD CurrInstWriteMask = 0;
|
|
DWORD PrevInstWriteMask = 0;
|
|
|
|
if( m_pCurrInst->m_DstParam.m_bParamUsed )
|
|
CurrInstWriteMask = m_pCurrInst->m_DstParam.m_WriteMask;
|
|
if( m_pCurrInst->m_pPrevInst->m_DstParam.m_bParamUsed )
|
|
PrevInstWriteMask = m_pCurrInst->m_pPrevInst->m_DstParam.m_WriteMask;
|
|
|
|
if( D3DSIO_DP3 == m_pCurrInst->m_Type &&
|
|
D3DSIO_DP3 == m_pCurrInst->m_pPrevInst->m_Type )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Co-issued instructions cannot both be dp3, since each require use of the color pipe to execute." );
|
|
m_ErrorCount++;
|
|
}
|
|
else if( D3DSIO_DP3 == m_pCurrInst->m_Type )
|
|
{
|
|
if( COLOR_WRITE_MASK & PrevInstWriteMask )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst->m_pPrevInst,
|
|
"dp3 needs color pipe to execute, so instruction co-issued with it cannot write to color components." );
|
|
m_ErrorCount++;
|
|
}
|
|
if( D3DSP_WRITEMASK_3 & CurrInstWriteMask ) // alpha in addition to the implied rgb for dp3
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"dp3 which writes alpha cannot co-issue since it uses up both the alpha and color pipes." );
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
else if( D3DSIO_DP3 == m_pCurrInst->m_pPrevInst->m_Type )
|
|
{
|
|
if( COLOR_WRITE_MASK & CurrInstWriteMask )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst->m_pPrevInst,
|
|
"dp3 needs color pipe to execute, so instruction co-issued with it cannot write to color components." );
|
|
m_ErrorCount++;
|
|
}
|
|
if( D3DSP_WRITEMASK_3 & PrevInstWriteMask ) // alpha in addition to the implied rgb for dp3
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst->m_pPrevInst,
|
|
"dp3 which writes alpha cannot co-issue since it uses up both the alpha and color pipes." );
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
|
|
if( (PrevInstWriteMask & ALPHA_WRITE_MASK) && (PrevInstWriteMask & COLOR_WRITE_MASK))
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst->m_pPrevInst,
|
|
"Individual instruction in co-issue pair cannot write both alpha and color component(s)." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( (CurrInstWriteMask & ALPHA_WRITE_MASK) && (CurrInstWriteMask & COLOR_WRITE_MASK))
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Individual instruction in co-issue pair cannot write both alpha and color component(s)." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( CurrInstWriteMask & PrevInstWriteMask )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"Co-issued instructions cannot both write to the same component(s). One instruction must write to alpha and the other may write to any combination of red/green/blue. Destination registers may differ." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
if( !((CurrInstWriteMask | PrevInstWriteMask) & ALPHA_WRITE_MASK) )
|
|
{
|
|
Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
|
|
"One of the instructions in a co-issue pair must write to alpha only (.a writemask)." );
|
|
m_ErrorCount++;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_ValidInstructionCount
|
|
//
|
|
// ** Rule:
|
|
// Make sure instruction count for pixel shader version has not been exceeded.
|
|
//
|
|
// Co-issued pixel blending instructions only
|
|
// count as one instruction towards the limit.
|
|
//
|
|
// The def instruction, nop, and comments (already stripped), do not count
|
|
// toward any limits.
|
|
//
|
|
// ** When to call:
|
|
// Per instruction AND after all instructions seen.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_ValidInstructionCount()
|
|
{
|
|
static UINT s_MaxTexOpCount;
|
|
static UINT s_MaxArithmeticOpCount;
|
|
|
|
if( NULL == m_pCurrInst )
|
|
return TRUE;
|
|
|
|
if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
|
|
{
|
|
m_TexOpCount = 0;
|
|
m_BlendOpCount = 0;
|
|
m_TotalOpCount = 0;
|
|
|
|
switch(m_Version)
|
|
{
|
|
default:
|
|
case D3DPS_VERSION(1,4): // DX8.1
|
|
s_MaxTexOpCount = 6;
|
|
s_MaxArithmeticOpCount = 8;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( m_bSeenAllInstructions || D3DSIO_PHASE == m_pCurrInst->m_Type )
|
|
{
|
|
if( m_pCurrInst && (D3DSIO_PHASE == m_pCurrInst->m_Type) )
|
|
{
|
|
if( m_TexOpCount > s_MaxTexOpCount )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Too many (%d) tex* instructions before phase marker. Max. allowed in a phase is %d.",
|
|
m_TexOpCount, s_MaxTexOpCount);
|
|
m_ErrorCount++;
|
|
}
|
|
if( m_BlendOpCount > s_MaxArithmeticOpCount )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Too many (%d) arithmetic instructions before phase marker. Max. allowed in a phase (counting any co-issued pairs as 1) is %d.",
|
|
m_BlendOpCount, s_MaxArithmeticOpCount);
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
else // 2 == m_Phase
|
|
{
|
|
if( m_bPhaseMarkerInShader )
|
|
{
|
|
if( m_TexOpCount > s_MaxTexOpCount )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Too many (%d) tex* instructions after phase marker. Max. allowed in a phase is %d.",
|
|
m_TexOpCount, s_MaxTexOpCount);
|
|
m_ErrorCount++;
|
|
}
|
|
if( m_BlendOpCount > s_MaxArithmeticOpCount )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Too many (%d) arithmetic instructions after phase marker. Max. allowed in a phase (counting any co-issued pairs as 1) is %d.",
|
|
m_BlendOpCount, s_MaxArithmeticOpCount);
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
else // defaulted to phase 2 because no phase marker was in shader
|
|
{
|
|
if( m_TexOpCount > s_MaxTexOpCount )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Too many (%d) tex* instructions. Max. allowed is %d. Note that adding a phase marker to the shader would double the number of instructions available.",
|
|
m_TexOpCount, s_MaxTexOpCount);
|
|
m_ErrorCount++;
|
|
}
|
|
if( m_BlendOpCount > s_MaxArithmeticOpCount )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "Too many (%d) arithmetic instructions. Max. allowed (counting any co-issued pairs as 1) is %d. Note that adding a phase marker to the shader would double the number of instructions available.",
|
|
m_BlendOpCount, s_MaxArithmeticOpCount);
|
|
m_ErrorCount++;
|
|
}
|
|
}
|
|
}
|
|
if( m_pCurrInst && D3DSIO_PHASE == m_pCurrInst->m_Type )
|
|
{
|
|
// reset counters for next phase.
|
|
m_TexOpCount = 0;
|
|
m_BlendOpCount = 0;
|
|
m_TotalOpCount = 0;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
switch(m_pCurrInst->m_Type)
|
|
{
|
|
case D3DSIO_TEX:
|
|
case D3DSIO_TEXCOORD:
|
|
case D3DSIO_TEXKILL:
|
|
case D3DSIO_TEXDEPTH:
|
|
m_TexOpCount++;
|
|
m_TotalOpCount++;
|
|
break;
|
|
case D3DSIO_MOV:
|
|
case D3DSIO_ADD:
|
|
case D3DSIO_SUB:
|
|
case D3DSIO_MUL:
|
|
case D3DSIO_MAD:
|
|
case D3DSIO_LRP:
|
|
case D3DSIO_DP3:
|
|
case D3DSIO_CND:
|
|
case D3DSIO_CMP:
|
|
case D3DSIO_DP4:
|
|
if( !_CURR_PS_INST->m_bCoIssue )
|
|
{
|
|
m_BlendOpCount++;
|
|
m_TotalOpCount++;
|
|
}
|
|
break;
|
|
case D3DSIO_BEM:
|
|
m_BlendOpCount+=2;
|
|
m_TotalOpCount+=2;
|
|
break;
|
|
case D3DSIO_END:
|
|
case D3DSIO_NOP:
|
|
case D3DSIO_DEF:
|
|
break;
|
|
default:
|
|
DXGASSERT(FALSE);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// CPShaderValidator14::Rule_R0Written
|
|
//
|
|
// ** Rule:
|
|
// All components (r,g,b,a) of register R0 must have been written by the
|
|
// pixel shader.
|
|
//
|
|
// ** When to call:
|
|
// After all instructions have been seen.
|
|
//
|
|
// ** Returns:
|
|
// Always TRUE.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
BOOL CPShaderValidator14::Rule_R0Written()
|
|
{
|
|
UINT NumUninitializedComponents = 0;
|
|
DWORD UninitializedComponentsMask = 0;
|
|
|
|
for( UINT i = 0; i < NUM_COMPONENTS_IN_REGISTER; i++ )
|
|
{
|
|
if( NULL == m_pTempRegFile->m_pAccessHistory[i][0].m_pMostRecentWriter )
|
|
{
|
|
NumUninitializedComponents++;
|
|
UninitializedComponentsMask |= COMPONENT_MASKS[i];
|
|
}
|
|
}
|
|
if( NumUninitializedComponents )
|
|
{
|
|
if( (UninitializedComponentsMask & COMPONENT_MASKS[3]) &&
|
|
(m_TempRegsWithZappedAlpha & (1 << 0 /*regnum=0*/ ) ) &&
|
|
(UninitializedComponentsMask & COMPONENT_MASKS[2]) &&
|
|
(m_TempRegsWithZappedBlue & (1 << 0 /*regnum=0*/ ) ) )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "r0 must be written by shader. Uninitialized component%s(*): %s. "\
|
|
ZAPPED_BLUE_TEXT2 " Also: " ZAPPED_ALPHA_TEXT2,
|
|
NumUninitializedComponents > 1 ? "s" : "", MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,FALSE));
|
|
}
|
|
else if( (UninitializedComponentsMask & COMPONENT_MASKS[3]) &&
|
|
(m_TempRegsWithZappedAlpha & (1 << 0 /*regnum=0*/ ) ) )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "r0 must be written by shader. Uninitialized component%s(*): %s. "\
|
|
ZAPPED_ALPHA_TEXT2,
|
|
NumUninitializedComponents > 1 ? "s" : "", MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,FALSE));
|
|
}
|
|
else if( (UninitializedComponentsMask & COMPONENT_MASKS[2]) &&
|
|
(m_TempRegsWithZappedBlue & (1 << 0 /*regnum=0*/ ) ) )
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "r0 must be written by shader. Uninitialized component%s(*): %s. "\
|
|
ZAPPED_BLUE_TEXT2,
|
|
NumUninitializedComponents > 1 ? "s" : "", MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,FALSE));
|
|
}
|
|
else
|
|
{
|
|
Spew( SPEW_GLOBAL_ERROR, NULL, "r0 must be written by shader. Uninitialized component%s(*): %s",
|
|
NumUninitializedComponents > 1 ? "s" : "", MakeAffectedComponentsText(UninitializedComponentsMask,TRUE,FALSE));
|
|
}
|
|
|
|
m_ErrorCount++;
|
|
}
|
|
return TRUE;
|
|
}
|