/////////////////////////////////////////////////////////////////////////////// // 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; }