334 lines
13 KiB
C++
334 lines
13 KiB
C++
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
// 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)
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CPSInstruction::CalculateComponentReadMasks()
|
||
|
//
|
||
|
// Figure out which components of each source parameter is read by a pixelshader
|
||
|
// instruction. For certain pixelshader instructions, the some components
|
||
|
// are also read from the dest parameter.
|
||
|
//
|
||
|
// Note: When this function is changed, the changes need to be ported to
|
||
|
// refrast's CalculateSourceReadMasks() function in rast\pshader.cpp
|
||
|
// (Though that function does not care about channels read from the dest parameter
|
||
|
// like this one does).
|
||
|
//-----------------------------------------------------------------------------
|
||
|
void CPSInstruction::CalculateComponentReadMasks(DWORD dwVersion)
|
||
|
{
|
||
|
UINT i, j;
|
||
|
|
||
|
switch( m_Type ) // instructions that actually read from the *Destination* register...
|
||
|
{
|
||
|
case D3DSIO_TEXM3x2DEPTH:
|
||
|
case D3DSIO_TEXDEPTH:
|
||
|
m_DstParam.m_ComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1;
|
||
|
break;
|
||
|
case D3DSIO_TEXKILL:
|
||
|
if( (D3DPS_VERSION(1,4) == dwVersion) && (D3DSPR_TEMP == m_DstParam.m_RegType) )
|
||
|
{
|
||
|
// for ps.1.4, texkill on an r# register only reads rgb
|
||
|
m_DstParam.m_ComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
m_DstParam.m_ComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
for( i = 0; i < m_SrcParamCount; i++ )
|
||
|
{
|
||
|
DWORD NeededComponents;
|
||
|
DWORD ReadComponents = 0;
|
||
|
|
||
|
switch( m_Type )
|
||
|
{
|
||
|
case D3DSIO_TEX: // only in ps.1.4 does texld have source parameter
|
||
|
if( D3DPS_VERSION(1,4) == dwVersion )
|
||
|
{
|
||
|
// for ps.1.4, texld has a source parameter
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
|
||
|
}
|
||
|
else // versions < ps.1.4 don't have a src param on tex, so we shouldn't get here. But maybe in ps.2.0...
|
||
|
{
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
|
||
|
}
|
||
|
break;
|
||
|
case D3DSIO_TEXCOORD:
|
||
|
if( D3DPS_VERSION(1,4) == dwVersion )
|
||
|
{
|
||
|
// for ps.1.4, texcrd has a source parameter
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
|
||
|
}
|
||
|
else // versions < ps.1.4 don't have a src param on texcoord, so we shouldn't get here. But maybe in ps.2.0...
|
||
|
{
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
|
||
|
}
|
||
|
break;
|
||
|
case D3DSIO_TEXBEM:
|
||
|
case D3DSIO_TEXBEML:
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1;
|
||
|
break;
|
||
|
case D3DSIO_DP3:
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
|
||
|
break;
|
||
|
case D3DSIO_DP4:
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
|
||
|
break;
|
||
|
case D3DSIO_BEM: // ps.1.4
|
||
|
NeededComponents = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1;
|
||
|
break;
|
||
|
default:
|
||
|
// standard component-wise instruction,
|
||
|
// OR an op we know reads .rgba and we also know it will be validated to .rgba writemask
|
||
|
NeededComponents = m_DstParam.m_WriteMask;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
// Figure out which components of this source parameter are read (taking into account swizzle)
|
||
|
for(j = 0; j < 4; j++)
|
||
|
{
|
||
|
if( NeededComponents & COMPONENT_MASKS[j] )
|
||
|
ReadComponents |= COMPONENT_MASKS[(m_SrcParam[i].m_SwizzleShift >> (D3DVS_SWIZZLE_SHIFT + 2*j)) & 0x3];
|
||
|
}
|
||
|
m_SrcParam[i].m_ComponentReadMask = ReadComponents;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CBasePShaderValidator::CBasePShaderValidator
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBasePShaderValidator::CBasePShaderValidator( const DWORD* pCode,
|
||
|
const D3DCAPS8* pCaps,
|
||
|
DWORD Flags )
|
||
|
: CBaseShaderValidator( 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_CycleNum = 0;
|
||
|
m_TexOpCount = 0;
|
||
|
m_BlendOpCount = 0;
|
||
|
m_TotalOpCount = 0;
|
||
|
|
||
|
m_pTempRegFile = NULL;
|
||
|
m_pInputRegFile = NULL;
|
||
|
m_pConstRegFile = NULL;
|
||
|
m_pTextureRegFile = NULL;
|
||
|
|
||
|
if( !m_bBaseInitOk )
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CBasePShaderValidator::~CBasePShaderValidator
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBasePShaderValidator::~CBasePShaderValidator()
|
||
|
{
|
||
|
delete m_pTempRegFile;
|
||
|
delete m_pInputRegFile;
|
||
|
delete m_pConstRegFile;
|
||
|
delete m_pTextureRegFile;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CBasePShaderValidator::AllocateNewInstruction
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBaseInstruction* CBasePShaderValidator::AllocateNewInstruction(CBaseInstruction*pPrevInst)
|
||
|
{
|
||
|
return new CPSInstruction((CPSInstruction*)pPrevInst);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// CBasePShaderValidator::DecodeNextInstruction
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BOOL CBasePShaderValidator::DecodeNextInstruction()
|
||
|
{
|
||
|
m_pCurrInst->m_Type = (D3DSHADER_INSTRUCTION_OPCODE_TYPE)(*m_pCurrToken & D3DSI_OPCODE_MASK);
|
||
|
|
||
|
if( D3DSIO_COMMENT == m_pCurrInst->m_Type )
|
||
|
{
|
||
|
ParseCommentForAssemblerMessages(m_pCurrToken); // does not advance m_pCurrToken
|
||
|
|
||
|
// Skip comments
|
||
|
DWORD NumDWORDs = ((*m_pCurrToken) & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
|
||
|
m_pCurrToken += (NumDWORDs+1);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
// Find out if the instruction is a TexOp and/or TexMOp. Needed by multiple validation rules,
|
||
|
// as well as further below in DecodeNextInstruction.
|
||
|
IsCurrInstTexOp();
|
||
|
|
||
|
// If the assembler has sent us file and/or line number messages,
|
||
|
// received by ParseCommentForAssemblerMesssages(),
|
||
|
// then bind this information to the current instruction.
|
||
|
// This info can be used in error spew to direct the shader developer
|
||
|
// to exactly where a problem is located.
|
||
|
m_pCurrInst->SetSpewFileNameAndLineNumber(m_pLatestSpewFileName,m_pLatestSpewLineNumber);
|
||
|
|
||
|
if( *m_pCurrToken & D3DSI_COISSUE )
|
||
|
{
|
||
|
_CURR_PS_INST->m_bCoIssue = TRUE;
|
||
|
}
|
||
|
else if( D3DSIO_NOP != m_pCurrInst->m_Type )
|
||
|
{
|
||
|
m_CycleNum++; // First cycle is 1. (co-issued instructions will have same cycle number)
|
||
|
}
|
||
|
_CURR_PS_INST->m_CycleNum = m_CycleNum;
|
||
|
|
||
|
m_SpewInstructionCount++; // only used for spew, not for any limits
|
||
|
m_pCurrInst->m_SpewInstructionCount = m_SpewInstructionCount;
|
||
|
|
||
|
DWORD dwReservedBits = PS_INST_TOKEN_RESERVED_MASK;
|
||
|
|
||
|
if( (*m_pCurrToken) & dwReservedBits )
|
||
|
{
|
||
|
Spew(SPEW_INSTRUCTION_ERROR,m_pCurrInst,"Reserved bit(s) set in instruction parameter token! Aborting validation.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
m_pCurrToken++;
|
||
|
|
||
|
// Decode dst param
|
||
|
if (*m_pCurrToken & (1L<<31))
|
||
|
{
|
||
|
(m_pCurrInst->m_DstParamCount)++;
|
||
|
DecodeDstParam( &m_pCurrInst->m_DstParam, *m_pCurrToken );
|
||
|
if( (*m_pCurrToken) & PS_DSTPARAM_TOKEN_RESERVED_MASK )
|
||
|
{
|
||
|
Spew(SPEW_INSTRUCTION_ERROR,m_pCurrInst,"Reserved bit(s) set in destination parameter token! Aborting validation.");
|
||
|
return FALSE;
|
||
|
}
|
||
|
m_pCurrToken++;
|
||
|
if( D3DSIO_DEF == m_pCurrInst->m_Type )
|
||
|
{
|
||
|
// Skip source params (float vector) - nothing to check
|
||
|
// This is the only instruction with 4 source params,
|
||
|
// and further, this is the only instruction that has
|
||
|
// raw numbers as parameters. This justifies the
|
||
|
// special case treatment here - we pretend
|
||
|
// D3DSIO_DEF only has a dst param (which we will check).
|
||
|
m_pCurrToken += 4;
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Decode src param(s)
|
||
|
while (*m_pCurrToken & (1L<<31))
|
||
|
{
|
||
|
(m_pCurrInst->m_SrcParamCount)++;
|
||
|
if( (m_pCurrInst->m_SrcParamCount + m_pCurrInst->m_DstParamCount) > SHADER_INSTRUCTION_MAX_PARAMS )
|
||
|
{
|
||
|
m_pCurrInst->m_SrcParamCount--;
|
||
|
m_pCurrToken++; // eat up extra parameters and skip to next
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
// Below: index is [SrcParamCount - 1] because m_SrcParam array needs 0 based index.
|
||
|
DecodeSrcParam( &(m_pCurrInst->m_SrcParam[m_pCurrInst->m_SrcParamCount - 1]),*m_pCurrToken );
|
||
|
|
||
|
if( (*m_pCurrToken) & PS_SRCPARAM_TOKEN_RESERVED_MASK )
|
||
|
{
|
||
|
Spew(SPEW_INSTRUCTION_ERROR,m_pCurrInst,"Reserved bit(s) set in source %d parameter token! Aborting validation.",
|
||
|
m_pCurrInst->m_SrcParamCount);
|
||
|
return FALSE;
|
||
|
}
|
||
|
m_pCurrToken++;
|
||
|
}
|
||
|
|
||
|
// Figure out which components of each source operand actually need to be read,
|
||
|
// taking into account destination write mask, the type of instruction, source swizzle, etc.
|
||
|
// (must be after IsCurrInstTexOp() )
|
||
|
m_pCurrInst->CalculateComponentReadMasks(m_Version);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
//
|
||
|
// CBasePShaderValidator Wrapper Functions
|
||
|
//
|
||
|
//-----------------------------------------------------------------------------
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// GetNewPSValidator
|
||
|
//
|
||
|
// Called by ValidatePixelShaderInternal and ValidatePixelShader below.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
CBasePShaderValidator* GetNewPSValidator( const DWORD* pCode,
|
||
|
const D3DCAPS8* pCaps,
|
||
|
const DWORD Flags )
|
||
|
{
|
||
|
if( !pCode )
|
||
|
return NULL;
|
||
|
else if( D3DPS_VERSION(1,4) > *pCode )
|
||
|
return new CPShaderValidator10(pCode,pCaps,Flags);
|
||
|
else
|
||
|
return new CPShaderValidator14(pCode,pCaps,Flags);
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// ValidatePixelShaderInternal
|
||
|
//-----------------------------------------------------------------------------
|
||
|
BOOL ValidatePixelShaderInternal( const DWORD* pCode, const D3DCAPS8* pCaps )
|
||
|
{
|
||
|
CBasePShaderValidator * pValidator = NULL;
|
||
|
BOOL bSuccess = FALSE;
|
||
|
|
||
|
pValidator = GetNewPSValidator( pCode, pCaps, 0 );
|
||
|
if( NULL == pValidator )
|
||
|
{
|
||
|
OutputDebugString("Out of memory.\n");
|
||
|
return bSuccess;
|
||
|
}
|
||
|
bSuccess = SUCCEEDED(pValidator->GetStatus()) ? TRUE : FALSE;
|
||
|
delete pValidator;
|
||
|
return bSuccess;
|
||
|
}
|
||
|
|
||
|
//-----------------------------------------------------------------------------
|
||
|
// ValidatePixelShader
|
||
|
//
|
||
|
// Don't forget to call "free" on the buffer returned in ppBuf.
|
||
|
//-----------------------------------------------------------------------------
|
||
|
HRESULT WINAPI ValidatePixelShader( const DWORD* pCode,
|
||
|
const D3DCAPS8* pCaps,
|
||
|
const DWORD Flags,
|
||
|
char** const ppBuf )
|
||
|
{
|
||
|
CBasePShaderValidator * pValidator = NULL;
|
||
|
HRESULT hr;
|
||
|
|
||
|
pValidator = GetNewPSValidator( pCode, pCaps, Flags );
|
||
|
if( NULL == pValidator )
|
||
|
{
|
||
|
OutputDebugString("Out of memory.\n");
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
if( ppBuf )
|
||
|
{
|
||
|
*ppBuf = (char*)HeapAlloc(GetProcessHeap(), 0, pValidator->GetRequiredLogBufferSize());
|
||
|
if( NULL == *ppBuf )
|
||
|
OutputDebugString("Out of memory.\n");
|
||
|
else
|
||
|
pValidator->WriteLogToBuffer(*ppBuf);
|
||
|
}
|
||
|
hr = pValidator->GetStatus();
|
||
|
delete pValidator;
|
||
|
return hr;
|
||
|
}
|