Source code of Windows XP (NT5)
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.

1836 lines
69 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) Microsoft Corporation, 2000.
  3. //
  4. // vshdrval.cpp
  5. //
  6. // Direct3D Reference Device - VertexShader validation
  7. //
  8. ///////////////////////////////////////////////////////////////////////////////
  9. #include "pch.cpp"
  10. #pragma hdrstop
  11. // Use these macros when looking at CVSInstruction derived members of the current instruction (CBaseInstruction)
  12. #define _CURR_VS_INST ((CVSInstruction*)m_pCurrInst)
  13. #define _PREV_VS_INST (m_pCurrInst?((CVSInstruction*)(m_pCurrInst->m_pPrevInst)):NULL)
  14. //-----------------------------------------------------------------------------
  15. // VertexShader Validation Rule Coverage
  16. //
  17. // Below is the list of rules in "DX8 VertexShader Version Specification",
  18. // matched to the function(s) in this file which enforce them.
  19. // Note that the mapping from rules to funtions can be 1->n or n->1
  20. //
  21. // Generic Rules
  22. // -------------
  23. //
  24. // VS-G1: Rule_oPosWritten
  25. // VS-G2: Rule_ValidAddressRegWrite
  26. //
  27. // Vertex Shader Version 1.0 Rules
  28. // ------------------------------
  29. //
  30. // VS.1.0-1: Rule_ValidAddressRegWrite
  31. //
  32. // Vertex Shader Version 1.1 Rules
  33. // ------------------------------
  34. //
  35. // VS.1.1-1: Rule_ValidInstructionCount
  36. // VS.1.1-2: Rule_ValidAddressRegWrite, Rule_ValidSrcParams
  37. // VS.1.1-3: Rule_ValidFRCInstruction
  38. // VS.1.1-4: ?
  39. //
  40. //-----------------------------------------------------------------------------
  41. //-----------------------------------------------------------------------------
  42. // CVSInstruction::CalculateComponentReadMasks(DWORD dwVersion)
  43. //-----------------------------------------------------------------------------
  44. void CVSInstruction::CalculateComponentReadMasks(DWORD dwVersion)
  45. {
  46. for( UINT i = 0; i < m_SrcParamCount; i++ )
  47. {
  48. DWORD PostSwizzleComponentReadMask = 0;
  49. switch( m_Type )
  50. {
  51. case D3DSIO_ADD:
  52. case D3DSIO_FRC:
  53. case D3DSIO_MAD:
  54. case D3DSIO_MAX:
  55. case D3DSIO_MIN:
  56. case D3DSIO_MOV:
  57. case D3DSIO_MUL:
  58. case D3DSIO_SLT:
  59. case D3DSIO_SGE:
  60. PostSwizzleComponentReadMask = m_DstParam.m_WriteMask; // per-component ops.
  61. break;
  62. case D3DSIO_DP3:
  63. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
  64. break;
  65. case D3DSIO_DP4:
  66. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
  67. break;
  68. case D3DSIO_LIT:
  69. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_3;
  70. break;
  71. case D3DSIO_DST:
  72. if( 0 == i ) PostSwizzleComponentReadMask = D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
  73. else if( 1 == i ) PostSwizzleComponentReadMask = D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_3;
  74. break;
  75. case D3DSIO_EXP:
  76. case D3DSIO_LOG:
  77. case D3DSIO_EXPP:
  78. case D3DSIO_LOGP:
  79. case D3DSIO_RCP:
  80. case D3DSIO_RSQ:
  81. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_3;
  82. break;
  83. case D3DSIO_M3x2:
  84. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
  85. break;
  86. case D3DSIO_M3x3:
  87. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
  88. break;
  89. case D3DSIO_M3x4:
  90. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2;
  91. break;
  92. case D3DSIO_M4x3:
  93. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
  94. break;
  95. case D3DSIO_M4x4:
  96. PostSwizzleComponentReadMask = D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1 | D3DSP_WRITEMASK_2 | D3DSP_WRITEMASK_3;
  97. break;
  98. case D3DSIO_NOP:
  99. default:
  100. break;
  101. }
  102. // Now that we know which components of the source will be used by the instruction,
  103. // we need to figure out which components of the actual source register need to be read to provide the data,
  104. // taking into account source component swizzling.
  105. m_SrcParam[i].m_ComponentReadMask = 0;
  106. for( UINT j = 0; j < 4; j++ )
  107. {
  108. if( PostSwizzleComponentReadMask & COMPONENT_MASKS[j] )
  109. m_SrcParam[i].m_ComponentReadMask |= COMPONENT_MASKS[(m_SrcParam[i].m_SwizzleShift >> (D3DVS_SWIZZLE_SHIFT + j*2)) & 3];
  110. }
  111. }
  112. }
  113. //-----------------------------------------------------------------------------
  114. // CVShaderValidator::CVShaderValidator
  115. //-----------------------------------------------------------------------------
  116. CVShaderValidator::CVShaderValidator( const DWORD* pCode,
  117. const DWORD* pDecl,
  118. const D3DCAPS8* pCaps,
  119. DWORD Flags )
  120. : CBaseShaderValidator( pCode, pCaps, Flags )
  121. {
  122. // Note that the base constructor initialized m_ReturnCode to E_FAIL.
  123. // Only set m_ReturnCode to S_OK if validation has succeeded,
  124. // before exiting this constructor.
  125. m_pDecl = pDecl;
  126. m_bFixedFunction = pDecl && !pCode;
  127. if( pCaps )
  128. {
  129. m_dwMaxVertexShaderConst = pCaps->MaxVertexShaderConst;
  130. m_bIgnoreConstantInitializationChecks = FALSE;
  131. }
  132. else
  133. {
  134. m_dwMaxVertexShaderConst = 0;
  135. m_bIgnoreConstantInitializationChecks = TRUE;
  136. }
  137. m_pTempRegFile = NULL;
  138. m_pInputRegFile = NULL;
  139. m_pConstRegFile = NULL;
  140. m_pAddrRegFile = NULL;
  141. m_pTexCrdOutputRegFile = NULL;
  142. m_pAttrOutputRegFile = NULL;
  143. m_pRastOutputRegFile = NULL;
  144. if( NULL == pCode && NULL == pDecl )
  145. {
  146. Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: Code and declaration pointers passed into shader vertex shader validator cannot both be NULL.");
  147. return;
  148. }
  149. if( !m_bBaseInitOk )
  150. return;
  151. ValidateShader(); // If successful, m_ReturnCode will be set to S_OK.
  152. // Call GetStatus() on this object to determine validation outcome.
  153. }
  154. //-----------------------------------------------------------------------------
  155. // CVShaderValidator::~CVShaderValidator
  156. //-----------------------------------------------------------------------------
  157. CVShaderValidator::~CVShaderValidator()
  158. {
  159. delete m_pTempRegFile;
  160. delete m_pInputRegFile;
  161. delete m_pConstRegFile;
  162. delete m_pAddrRegFile;
  163. delete m_pTexCrdOutputRegFile;
  164. delete m_pAttrOutputRegFile;
  165. delete m_pRastOutputRegFile;
  166. }
  167. //-----------------------------------------------------------------------------
  168. // CVShaderValidator::AllocateNewInstruction
  169. //-----------------------------------------------------------------------------
  170. CBaseInstruction* CVShaderValidator::AllocateNewInstruction(CBaseInstruction*pPrevInst)
  171. {
  172. return new CVSInstruction((CVSInstruction*)pPrevInst);
  173. }
  174. //-----------------------------------------------------------------------------
  175. // CVShaderValidator::DecodeNextInstruction
  176. //-----------------------------------------------------------------------------
  177. BOOL CVShaderValidator::DecodeNextInstruction()
  178. {
  179. m_pCurrInst->m_Type = (D3DSHADER_INSTRUCTION_OPCODE_TYPE)(*m_pCurrToken & D3DSI_OPCODE_MASK);
  180. if( m_pCurrInst->m_Type == D3DSIO_COMMENT )
  181. {
  182. ParseCommentForAssemblerMessages(m_pCurrToken); // does not advance m_pCurrToken
  183. // Skip comments
  184. DWORD NumDWORDs = ((*m_pCurrToken) & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT;
  185. m_pCurrToken += (NumDWORDs+1);
  186. return TRUE;
  187. }
  188. // If the assembler has sent us file and/or line number messages,
  189. // received by ParseCommentForAssemblerMesssages(),
  190. // then bind this information to the current instruction.
  191. // This info can be used in error spew to direct the shader developer
  192. // to exactly where a problem is located.
  193. m_pCurrInst->SetSpewFileNameAndLineNumber(m_pLatestSpewFileName,m_pLatestSpewLineNumber);
  194. m_SpewInstructionCount++; // only used for spew, not for any limits
  195. m_pCurrInst->m_SpewInstructionCount = m_SpewInstructionCount;
  196. DWORD dwReservedBits = VS_INST_TOKEN_RESERVED_MASK;
  197. if( (*m_pCurrToken) & dwReservedBits )
  198. {
  199. Spew(SPEW_INSTRUCTION_ERROR,m_pCurrInst,"Reserved bit(s) set in instruction parameter token! Aborting validation.");
  200. return FALSE;
  201. }
  202. m_pCurrToken++;
  203. // Decode dst param
  204. if (*m_pCurrToken & (1L<<31))
  205. {
  206. (m_pCurrInst->m_DstParamCount)++;
  207. DecodeDstParam( &m_pCurrInst->m_DstParam, *m_pCurrToken );
  208. if( (*m_pCurrToken) & VS_DSTPARAM_TOKEN_RESERVED_MASK )
  209. {
  210. Spew(SPEW_INSTRUCTION_ERROR,m_pCurrInst,"Reserved bit(s) set in destination parameter token! Aborting validation.");
  211. return FALSE;
  212. }
  213. m_pCurrToken++;
  214. }
  215. // Decode src param(s)
  216. while (*m_pCurrToken & (1L<<31))
  217. {
  218. (m_pCurrInst->m_SrcParamCount)++;
  219. if( (m_pCurrInst->m_DstParamCount + m_pCurrInst->m_SrcParamCount) > SHADER_INSTRUCTION_MAX_PARAMS )
  220. {
  221. m_pCurrInst->m_SrcParamCount--;
  222. m_pCurrToken++; // eat up extra parameters and skip to next
  223. continue;
  224. }
  225. // Below: index is [SrcParamCount - 1] because m_SrcParam array needs 0 based index.
  226. DecodeSrcParam( &(m_pCurrInst->m_SrcParam[m_pCurrInst->m_SrcParamCount - 1]),*m_pCurrToken );
  227. if( (*m_pCurrToken) & VS_SRCPARAM_TOKEN_RESERVED_MASK )
  228. {
  229. Spew(SPEW_INSTRUCTION_ERROR,m_pCurrInst,"Reserved bit(s) set in source %d parameter token! Aborting validation.",
  230. m_pCurrInst->m_SrcParamCount);
  231. return FALSE;
  232. }
  233. m_pCurrToken++;
  234. }
  235. // Figure out which components of each source operand actually need to be read,
  236. // taking into account destination write mask, the type of instruction, source swizzle, etc.
  237. m_pCurrInst->CalculateComponentReadMasks(m_Version);
  238. return TRUE;
  239. }
  240. //-----------------------------------------------------------------------------
  241. // CVShaderValidator::InitValidation
  242. //-----------------------------------------------------------------------------
  243. BOOL CVShaderValidator::InitValidation()
  244. {
  245. if( m_bFixedFunction )
  246. {
  247. m_pTempRegFile = new CRegisterFile(0,FALSE,0,TRUE);// #regs, bWritable, max# reads/instruction, pre-shader initialized
  248. m_pInputRegFile = new CRegisterFile(17,FALSE,0,TRUE);
  249. m_pConstRegFile = new CRegisterFile(0,FALSE,0,TRUE);
  250. m_pAddrRegFile = new CRegisterFile(0,FALSE,0,TRUE);
  251. m_pTexCrdOutputRegFile = new CRegisterFile(0,FALSE,0,TRUE);
  252. m_pAttrOutputRegFile = new CRegisterFile(0,FALSE,0,TRUE);
  253. m_pRastOutputRegFile = new CRegisterFile(0,FALSE,0,TRUE);
  254. }
  255. else
  256. {
  257. if( m_pCaps )
  258. {
  259. if( (m_pCaps->VertexShaderVersion & 0x0000FFFF) < (m_Version & 0x0000FFFF) )
  260. {
  261. Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: Vertex shader version %d.%d is too high for device. Maximum supported version is %d.%d. Aborting shader validation.",
  262. D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version),
  263. D3DSHADER_VERSION_MAJOR(m_pCaps->VertexShaderVersion),D3DSHADER_VERSION_MINOR(m_pCaps->VertexShaderVersion));
  264. return FALSE;
  265. }
  266. }
  267. switch( m_Version >> 16 )
  268. {
  269. case 0xffff:
  270. Spew( SPEW_GLOBAL_ERROR, NULL, "Version token: 0x%x indicates a pixel shader. Vertex shader version token must be of the form 0xfffe****.",
  271. m_Version);
  272. return FALSE;
  273. case 0xfffe:
  274. break; // vertexshader - ok.
  275. default:
  276. Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: 0x%x is invalid. Vertex shader version token must be of the form 0xfffe****. Aborting vertex shader validation.",
  277. m_Version);
  278. return FALSE;
  279. }
  280. switch(m_Version)
  281. {
  282. case D3DVS_VERSION(1,0): // DX8
  283. m_pTempRegFile = new CRegisterFile(12,TRUE,3,FALSE);// #regs, bWritable, max# reads/instruction, pre-shader initialized
  284. m_pInputRegFile = new CRegisterFile(16,FALSE,1,TRUE);
  285. if( m_bIgnoreConstantInitializationChecks )
  286. m_pConstRegFile = new CRegisterFile(0,FALSE,1,TRUE); // still creating register file so we can validate number of read ports
  287. else
  288. m_pConstRegFile = new CRegisterFile(m_dwMaxVertexShaderConst,FALSE,1,TRUE);
  289. m_pAddrRegFile = new CRegisterFile(0,TRUE,0,FALSE);
  290. m_pTexCrdOutputRegFile = new CRegisterFile(8,TRUE,0,FALSE);
  291. m_pAttrOutputRegFile = new CRegisterFile(2,TRUE,0,FALSE);
  292. m_pRastOutputRegFile = new CRegisterFile(3,TRUE,0,FALSE);
  293. break;
  294. case D3DVS_VERSION(1,1): // DX8
  295. m_pTempRegFile = new CRegisterFile(12,TRUE,3,FALSE);// #regs, bWritable, max# reads/instruction, pre-shader initialized
  296. m_pInputRegFile = new CRegisterFile(16,FALSE,1,TRUE);
  297. if( m_bIgnoreConstantInitializationChecks )
  298. m_pConstRegFile = new CRegisterFile(0,FALSE,1,TRUE); // still creating register file so we can validate number of read ports
  299. else
  300. m_pConstRegFile = new CRegisterFile(m_dwMaxVertexShaderConst,FALSE,1,TRUE);
  301. m_pAddrRegFile = new CRegisterFile(1,TRUE,0,FALSE);
  302. m_pTexCrdOutputRegFile = new CRegisterFile(8,TRUE,0,FALSE);
  303. m_pAttrOutputRegFile = new CRegisterFile(2,TRUE,0,FALSE);
  304. m_pRastOutputRegFile = new CRegisterFile(3,TRUE,0,FALSE);
  305. break;
  306. default:
  307. Spew( SPEW_GLOBAL_ERROR, NULL, "Version Token: %d.%d is not a supported vertex shader version. Aborting vertex shader validation.",
  308. D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version));
  309. return FALSE;
  310. }
  311. }
  312. if( NULL == m_pTempRegFile ||
  313. NULL == m_pInputRegFile ||
  314. NULL == m_pConstRegFile ||
  315. NULL == m_pAddrRegFile ||
  316. NULL == m_pTexCrdOutputRegFile ||
  317. NULL == m_pAttrOutputRegFile ||
  318. NULL == m_pRastOutputRegFile )
  319. {
  320. Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory.");
  321. return FALSE;
  322. }
  323. ValidateDeclaration(); // no matter what happens here, we can continue checking shader code, if present.
  324. if( m_bFixedFunction ) // no shader code - fixed function, so we only validate declaration
  325. {
  326. if( 0 == m_ErrorCount )
  327. m_ReturnCode = S_OK;
  328. return FALSE; // returning false just makes validation stop here (not for indicating success/failure of validation)
  329. }
  330. return TRUE;
  331. }
  332. //-----------------------------------------------------------------------------
  333. // CVShaderValidator::ValidateDeclaration
  334. //-----------------------------------------------------------------------------
  335. void CVShaderValidator::ValidateDeclaration()
  336. {
  337. if( !m_pDecl ) // no shader declaration passed in.
  338. return;
  339. DXGASSERT(m_pInputRegFile);
  340. typedef struct _NORMAL_GEN {
  341. UINT DestReg;
  342. UINT SourceReg;
  343. UINT TokenNum;
  344. } NORMAL_GEN;
  345. const DWORD* pCurrToken = m_pDecl;
  346. DWORD MaxStreams = 0;
  347. UINT TokenNum = 1;
  348. UINT NumInputRegs = m_pInputRegFile->GetNumRegs();
  349. BOOL bInStream = FALSE;
  350. BOOL* pVertexStreamDeclared = NULL;
  351. BOOL bInTessStream = FALSE;
  352. BOOL bTessStreamDeclared = FALSE;
  353. BOOL bAtLeastOneDataDefinition = FALSE;
  354. NORMAL_GEN* pNormalGenOperations = new NORMAL_GEN[m_pInputRegFile->GetNumRegs()];
  355. UINT NumNormalGenOperations = 0;
  356. BOOL bErrorInForLoop = FALSE;
  357. if( NULL == pNormalGenOperations )
  358. {
  359. Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory. Aborting shader decl. validation.");
  360. m_ErrorCount++;
  361. goto Exit;
  362. }
  363. DXGASSERT(m_pConstRegFile && m_pInputRegFile); // if we have a declaration, we better have these two register files
  364. DXGASSERT(!m_bIgnoreConstantInitializationChecks); // we better have d3d8 caps if we have a decl to verify!
  365. if( m_pCaps ) // only validate stream numbers when caps present
  366. {
  367. MaxStreams = m_pCaps->MaxStreams;
  368. if( MaxStreams > 0 )
  369. {
  370. pVertexStreamDeclared = new BOOL[MaxStreams];
  371. if( NULL == pVertexStreamDeclared )
  372. {
  373. Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory. Aborting shader decl. validation.");
  374. m_ErrorCount++;
  375. goto Exit;
  376. }
  377. for( UINT i = 0; i < MaxStreams; i++ )
  378. pVertexStreamDeclared[i] = FALSE;
  379. }
  380. }
  381. // The constructor for the input register file assumed that the input regs were initialized,
  382. // but now that we are parsing a shader declaration,
  383. // we can check initialization of input registers.
  384. for( UINT i = 0; i < 4; i++ )
  385. {
  386. for( UINT j = 0; j < m_pInputRegFile->GetNumRegs(); j++ )
  387. m_pInputRegFile->m_pAccessHistory[i][j].m_bPreShaderInitialized = FALSE;
  388. }
  389. // Now parse the declaration.
  390. while( D3DVSD_END() != *pCurrToken )
  391. {
  392. DWORD Token = *pCurrToken;
  393. switch( (Token & D3DVSD_TOKENTYPEMASK) >> D3DVSD_TOKENTYPESHIFT )
  394. {
  395. case D3DVSD_TOKEN_NOP:
  396. break;
  397. case D3DVSD_TOKEN_STREAM:
  398. {
  399. UINT StreamNum = (Token & D3DVSD_STREAMNUMBERMASK) >> D3DVSD_STREAMNUMBERSHIFT;
  400. bInTessStream = (Token & D3DVSD_STREAMTESSMASK) >> D3DVSD_STREAMTESSSHIFT;
  401. bInStream = !bInTessStream;
  402. bAtLeastOneDataDefinition = FALSE;
  403. if( bInStream )
  404. {
  405. if( m_pCaps && (StreamNum >= MaxStreams) )
  406. {
  407. if( MaxStreams )
  408. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Stream number %d is out of range. Max allowed is %d. Aborting shader decl. validation.",
  409. TokenNum, StreamNum, m_pCaps->MaxStreams - 1);
  410. else
  411. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Stream number %d is out of range. There are no streams available. Aborting shader decl. validation.",
  412. TokenNum, StreamNum, m_pCaps->MaxStreams - 1);
  413. m_ErrorCount++;
  414. goto Exit;
  415. }
  416. }
  417. else if( StreamNum > 0 )
  418. {
  419. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Stream number must not be specified for tesselator stream.",
  420. TokenNum);
  421. m_ErrorCount++;
  422. }
  423. if( bInStream && pVertexStreamDeclared )
  424. {
  425. if( TRUE == pVertexStreamDeclared[StreamNum] )
  426. {
  427. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Stream number %d has already been declared. Aborting shader decl. validation.",
  428. TokenNum, StreamNum );
  429. m_ErrorCount++;
  430. goto Exit;
  431. }
  432. pVertexStreamDeclared[StreamNum] = TRUE;
  433. }
  434. if( bInTessStream )
  435. {
  436. if( bTessStreamDeclared )
  437. {
  438. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Tesselation stream has already been declared. Aborting shader decl. validation.",
  439. TokenNum);
  440. m_ErrorCount++;
  441. goto Exit;
  442. }
  443. bTessStreamDeclared = TRUE;
  444. }
  445. break;
  446. }
  447. case D3DVSD_TOKEN_STREAMDATA:
  448. if( !bInStream )
  449. {
  450. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Cannot set stream input without first setting stream #. Aborting shader decl. validation.",
  451. TokenNum);
  452. m_ErrorCount++;
  453. goto Exit;
  454. }
  455. if( (Token & D3DVSD_DATALOADTYPEMASK) >> D3DVSD_DATALOADTYPESHIFT ) // SKIP
  456. {
  457. if( m_bFixedFunction )
  458. {
  459. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: SKIP not permitted in fixed-function declarations.",
  460. TokenNum);
  461. m_ErrorCount++;
  462. break;
  463. }
  464. }
  465. else
  466. {
  467. UINT RegNum = (Token & D3DVSD_VERTEXREGMASK) >> D3DVSD_VERTEXREGSHIFT;
  468. if( RegNum >= m_pInputRegFile->GetNumRegs() )
  469. {
  470. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Invalid reg num: %d. Max allowed is %d.",
  471. TokenNum, RegNum, m_pInputRegFile->GetNumRegs() - 1);
  472. m_ErrorCount++;
  473. break;
  474. }
  475. switch( (Token & D3DVSD_DATATYPEMASK) >> D3DVSD_DATATYPESHIFT )
  476. {
  477. case D3DVSDT_FLOAT1:
  478. case D3DVSDT_FLOAT2:
  479. case D3DVSDT_FLOAT3:
  480. case D3DVSDT_FLOAT4:
  481. case D3DVSDT_D3DCOLOR:
  482. case D3DVSDT_UBYTE4:
  483. case D3DVSDT_SHORT2:
  484. case D3DVSDT_SHORT4:
  485. break;
  486. default:
  487. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Unrecognized stream data type.",
  488. TokenNum);
  489. m_ErrorCount++;
  490. break;
  491. }
  492. bErrorInForLoop = FALSE;
  493. for( UINT i = 0; i < 4; i++ )
  494. {
  495. if( TRUE == m_pInputRegFile->m_pAccessHistory[i][RegNum].m_bPreShaderInitialized )
  496. {
  497. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Input register %d already declared.",
  498. TokenNum, RegNum);
  499. m_ErrorCount++;
  500. bErrorInForLoop = TRUE;
  501. break;
  502. }
  503. m_pInputRegFile->m_pAccessHistory[i][RegNum].m_bPreShaderInitialized = TRUE;
  504. }
  505. if( bErrorInForLoop )
  506. break;
  507. bAtLeastOneDataDefinition = TRUE;
  508. }
  509. break;
  510. case D3DVSD_TOKEN_TESSELLATOR:
  511. {
  512. if( !bInTessStream )
  513. {
  514. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Cannot set tesselator stream input without first setting tesselator stream #. Aborting shader decl. validation.",
  515. TokenNum);
  516. m_ErrorCount++;
  517. goto Exit;
  518. }
  519. DWORD InRegNum = (Token & D3DVSD_VERTEXREGINMASK) >> D3DVSD_VERTEXREGINSHIFT;
  520. DWORD RegNum = (Token & D3DVSD_VERTEXREGMASK) >> D3DVSD_VERTEXREGSHIFT;
  521. BOOL bNormalGen = !(Token & 0x10000000); // TODO: Why isnt there a const for this in the d3d api headers?
  522. if( RegNum >= m_pInputRegFile->GetNumRegs() )
  523. {
  524. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Invalid reg num: %d. Max allowed is %d.",
  525. TokenNum, RegNum, m_pInputRegFile->GetNumRegs() - 1);
  526. m_ErrorCount++;
  527. break;
  528. }
  529. if( bNormalGen )
  530. {
  531. if( InRegNum >= m_pInputRegFile->GetNumRegs() )
  532. {
  533. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Invalid input reg num: %d. Max allowed is %d.",
  534. TokenNum, InRegNum, m_pInputRegFile->GetNumRegs() - 1);
  535. m_ErrorCount++;
  536. break;
  537. }
  538. bErrorInForLoop = FALSE;
  539. for( UINT i = 0; i < NumNormalGenOperations; i++ )
  540. {
  541. if( pNormalGenOperations[i].DestReg == RegNum )
  542. {
  543. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Duplicate declaration of input register %d as destination for normal generation.",
  544. TokenNum, RegNum );
  545. m_ErrorCount++;
  546. bErrorInForLoop = TRUE;
  547. break;
  548. }
  549. }
  550. if( bErrorInForLoop )
  551. break;
  552. // Defer checking of initialization of inputs for normal gen until the entire declaration has been seen.
  553. // Also, defer setting of normal gen destination reg. to initialized,
  554. // in order to disallow normal generation loops.
  555. pNormalGenOperations[NumNormalGenOperations].DestReg = RegNum;
  556. pNormalGenOperations[NumNormalGenOperations].SourceReg = InRegNum;
  557. pNormalGenOperations[NumNormalGenOperations].TokenNum = TokenNum; // used later for spew
  558. NumNormalGenOperations++;
  559. }
  560. else
  561. {
  562. if( ((Token & D3DVSD_DATATYPEMASK) >> D3DVSD_DATATYPESHIFT) != D3DVSDT_FLOAT2 )
  563. {
  564. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Tess datatype must be FLOAT2 for UV generation.",
  565. TokenNum);
  566. m_ErrorCount++;
  567. break;
  568. }
  569. if( InRegNum > 0 )
  570. {
  571. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Input register number must not be specified (does not apply) for UV tesselation.",
  572. TokenNum);
  573. m_ErrorCount++;
  574. break;
  575. }
  576. for( UINT i = 0; i < 4; i++ )
  577. {
  578. if( TRUE == m_pInputRegFile->m_pAccessHistory[i][RegNum].m_bPreShaderInitialized )
  579. {
  580. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Input register %d already declared.",
  581. TokenNum, RegNum);
  582. m_ErrorCount++;
  583. break;
  584. }
  585. m_pInputRegFile->m_pAccessHistory[i][RegNum].m_bPreShaderInitialized = TRUE;
  586. }
  587. }
  588. bAtLeastOneDataDefinition = TRUE;
  589. break;
  590. }
  591. case D3DVSD_TOKEN_CONSTMEM:
  592. {
  593. DWORD ConstCount = (Token & D3DVSD_CONSTCOUNTMASK) >> D3DVSD_CONSTCOUNTSHIFT;
  594. DWORD MaxOffset = ((Token & D3DVSD_CONSTADDRESSMASK) >> D3DVSD_CONSTADDRESSSHIFT) + ConstCount;
  595. DWORD NumConstRegs = m_pConstRegFile->GetNumRegs();
  596. DXGASSERT(NumConstRegs > 0);
  597. if( (bInStream || bInTessStream) && !bAtLeastOneDataDefinition )
  598. {
  599. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Stream selector token must be followed by at least one stream data definition token.",
  600. TokenNum);
  601. m_ErrorCount++;
  602. }
  603. if( 0 == NumConstRegs )
  604. {
  605. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Write to const register %d is not valid. There are no constant registers available.",
  606. TokenNum,MaxOffset );
  607. }
  608. else if( MaxOffset > NumConstRegs )
  609. {
  610. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Write to const register %d is out of range. Max offset is %d.",
  611. TokenNum,MaxOffset,m_pConstRegFile->GetNumRegs() - 1 );
  612. m_ErrorCount++;
  613. }
  614. pCurrToken += ConstCount*4;
  615. bInStream = bInTessStream = FALSE;
  616. break;
  617. }
  618. case D3DVSD_TOKEN_EXT:
  619. pCurrToken += ((Token & D3DVSD_EXTCOUNTMASK) >> D3DVSD_EXTCOUNTSHIFT);
  620. if( (bInStream || bInTessStream) && !bAtLeastOneDataDefinition )
  621. {
  622. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Stream selector token must be followed by at least one stream data definition token.",
  623. TokenNum);
  624. m_ErrorCount++;
  625. }
  626. bInStream = bInTessStream = FALSE;
  627. break;
  628. default:
  629. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Unrecognized stream declaration token. Aborting shader decl. validation.",
  630. TokenNum);
  631. m_ErrorCount++;
  632. goto Exit;
  633. }
  634. pCurrToken++;
  635. }
  636. // Make sure inputs to normal gen operations have been initialized
  637. for( UINT i = 0; i < NumNormalGenOperations; i++ )
  638. {
  639. for( UINT Component = 0; Component < 4; Component++ )
  640. {
  641. if( FALSE == m_pInputRegFile->m_pAccessHistory[Component][pNormalGenOperations[i].SourceReg].m_bPreShaderInitialized )
  642. {
  643. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token %d: Source input register %d for normal generation has not been declared.",
  644. pNormalGenOperations[i].TokenNum, pNormalGenOperations[i].SourceReg);
  645. m_ErrorCount++;
  646. break;
  647. }
  648. }
  649. }
  650. // Set outputs of normal gen operations to initialized
  651. for( UINT i = 0; i < NumNormalGenOperations; i++ )
  652. {
  653. for( UINT Component = 0; Component < 4; Component++ )
  654. {
  655. if( TRUE == m_pInputRegFile->m_pAccessHistory[Component][pNormalGenOperations[i].DestReg].m_bPreShaderInitialized )
  656. {
  657. Spew( SPEW_GLOBAL_ERROR, NULL, "Declaration Token #%d: Input reg %d specified as destination for normal generation is already declared elsewhere.",
  658. pNormalGenOperations[i].TokenNum, pNormalGenOperations[i].DestReg);
  659. m_ErrorCount++;
  660. break;
  661. }
  662. m_pInputRegFile->m_pAccessHistory[Component][pNormalGenOperations[i].DestReg].m_bPreShaderInitialized = TRUE;
  663. }
  664. }
  665. Exit:
  666. if( pVertexStreamDeclared )
  667. delete [] pVertexStreamDeclared;
  668. if( pNormalGenOperations )
  669. delete [] pNormalGenOperations;
  670. }
  671. //-----------------------------------------------------------------------------
  672. // CVShaderValidator::ApplyPerInstructionRules
  673. //
  674. // Returns FALSE if shader validation must terminate.
  675. // Returns TRUE if validation may proceed to next instruction.
  676. //-----------------------------------------------------------------------------
  677. BOOL CVShaderValidator::ApplyPerInstructionRules()
  678. {
  679. if( ! Rule_InstructionRecognized() ) return FALSE; // Bail completely on unrecognized instr.
  680. if( ! Rule_InstructionSupportedByVersion() ) goto EXIT;
  681. if( ! Rule_ValidParamCount() ) goto EXIT;
  682. if( ! Rule_ValidSrcParams() ) goto EXIT;
  683. if( ! Rule_SrcInitialized() ) goto EXIT; // needs to be before ValidDstParam()
  684. if( ! Rule_ValidAddressRegWrite() ) goto EXIT;
  685. if( ! Rule_ValidDstParam() ) goto EXIT;
  686. if( ! Rule_ValidFRCInstruction() ) goto EXIT;
  687. if( ! Rule_ValidRegisterPortUsage() ) goto EXIT;
  688. if( ! Rule_ValidInstructionCount() ) goto EXIT;
  689. EXIT:
  690. return TRUE;
  691. }
  692. //-----------------------------------------------------------------------------
  693. // CVShaderValidator::ApplyPostInstructionsRules
  694. //-----------------------------------------------------------------------------
  695. void CVShaderValidator::ApplyPostInstructionsRules()
  696. {
  697. Rule_ValidInstructionCount(); // see if we went over the limits
  698. Rule_oPosWritten();
  699. }
  700. //-----------------------------------------------------------------------------
  701. //
  702. // Per Instruction Rules
  703. //
  704. //-----------------------------------------------------------------------------
  705. //-----------------------------------------------------------------------------
  706. // CVShaderValidator::Rule_InstructionRecognized
  707. //
  708. // ** Rule:
  709. // Is the instruction opcode known? (regardless of shader version)
  710. //
  711. // ** When to call:
  712. // Per instruction.
  713. //
  714. // ** Returns:
  715. // FALSE when instruction not recognized.
  716. //
  717. //-----------------------------------------------------------------------------
  718. BOOL CVShaderValidator::Rule_InstructionRecognized()
  719. {
  720. switch(m_pCurrInst->m_Type)
  721. {
  722. case D3DSIO_MOV:
  723. case D3DSIO_ADD:
  724. case D3DSIO_MAD:
  725. case D3DSIO_MUL:
  726. case D3DSIO_RCP:
  727. case D3DSIO_RSQ:
  728. case D3DSIO_DP3:
  729. case D3DSIO_DP4:
  730. case D3DSIO_MIN:
  731. case D3DSIO_MAX:
  732. case D3DSIO_SLT:
  733. case D3DSIO_SGE:
  734. case D3DSIO_EXPP:
  735. case D3DSIO_LOGP:
  736. case D3DSIO_LIT:
  737. case D3DSIO_DST:
  738. case D3DSIO_M4x4:
  739. case D3DSIO_M4x3:
  740. case D3DSIO_M3x4:
  741. case D3DSIO_M3x3:
  742. case D3DSIO_M3x2:
  743. case D3DSIO_FRC:
  744. case D3DSIO_EXP:
  745. case D3DSIO_LOG:
  746. case D3DSIO_END:
  747. case D3DSIO_NOP:
  748. return TRUE; // instruction recognized - ok.
  749. }
  750. // if we get here, the instruction is not recognized
  751. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Unrecognized instruction. Aborting vertex shader validation." );
  752. m_ErrorCount++;
  753. return FALSE;
  754. }
  755. //-----------------------------------------------------------------------------
  756. // CVShaderValidator::Rule_InstructionSupportedByVersion
  757. //
  758. // ** Rule:
  759. // Is the instruction supported by the current pixel shader version?
  760. //
  761. // ** When to call:
  762. // Per instruction.
  763. //
  764. // ** Returns:
  765. // FALSE when instruction not supported by version.
  766. //
  767. //-----------------------------------------------------------------------------
  768. BOOL CVShaderValidator::Rule_InstructionSupportedByVersion()
  769. {
  770. if( D3DVS_VERSION(1,0) <= m_Version ) // 1.0 and above
  771. {
  772. switch(m_pCurrInst->m_Type)
  773. {
  774. case D3DSIO_MOV:
  775. case D3DSIO_ADD:
  776. case D3DSIO_MAD:
  777. case D3DSIO_MUL:
  778. case D3DSIO_RCP:
  779. case D3DSIO_RSQ:
  780. case D3DSIO_DP3:
  781. case D3DSIO_DP4:
  782. case D3DSIO_MIN:
  783. case D3DSIO_MAX:
  784. case D3DSIO_SLT:
  785. case D3DSIO_SGE:
  786. case D3DSIO_EXPP:
  787. case D3DSIO_LOGP:
  788. case D3DSIO_LIT:
  789. case D3DSIO_DST:
  790. case D3DSIO_M4x4:
  791. case D3DSIO_M4x3:
  792. case D3DSIO_M3x4:
  793. case D3DSIO_M3x3:
  794. case D3DSIO_M3x2:
  795. case D3DSIO_FRC:
  796. case D3DSIO_EXP:
  797. case D3DSIO_LOG:
  798. return TRUE; // instruction supported - ok.
  799. }
  800. }
  801. switch(m_pCurrInst->m_Type)
  802. {
  803. case D3DSIO_END:
  804. case D3DSIO_NOP:
  805. return TRUE; // instruction supported - ok.
  806. }
  807. // if we get here, the instruction is not supported.
  808. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Instruction not supported by version %d.%d vertex shader.",
  809. D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version));
  810. m_ErrorCount++;
  811. return FALSE; // no more checks on this instruction
  812. }
  813. //-----------------------------------------------------------------------------
  814. // CVShaderValidator::Rule_ValidParamCount
  815. //
  816. // ** Rule:
  817. // Is the parameter count correct for the instruction?
  818. // The count includes dest + source parameters.
  819. //
  820. // DEF is a special case that is treated as having only 1 dest parameter,
  821. // even though there are also 4 source parameters. The 4 source params for DEF
  822. // are immediate float values, so there is nothing to check, and no way of
  823. // knowing whether or not those parameter tokens were actually present in the
  824. // token list - all the validator can do is skip over 4 DWORDS (which it does).
  825. //
  826. // ** When to call:
  827. // Per instruction.
  828. //
  829. // ** Returns:
  830. //
  831. // FALSE when the parameter count is incorrect.
  832. //
  833. //-----------------------------------------------------------------------------
  834. BOOL CVShaderValidator::Rule_ValidParamCount()
  835. {
  836. BOOL bBadParamCount = FALSE;
  837. if ((m_pCurrInst->m_DstParamCount + m_pCurrInst->m_SrcParamCount) > SHADER_INSTRUCTION_MAX_PARAMS) bBadParamCount = TRUE;
  838. switch (m_pCurrInst->m_Type)
  839. {
  840. case D3DSIO_NOP:
  841. bBadParamCount = (m_pCurrInst->m_DstParamCount != 0) || (m_pCurrInst->m_SrcParamCount != 0); break;
  842. case D3DSIO_EXP:
  843. case D3DSIO_EXPP:
  844. case D3DSIO_FRC:
  845. case D3DSIO_LOG:
  846. case D3DSIO_LOGP:
  847. case D3DSIO_LIT:
  848. case D3DSIO_MOV:
  849. case D3DSIO_RCP:
  850. case D3DSIO_RSQ:
  851. bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 1); break;
  852. case D3DSIO_ADD:
  853. case D3DSIO_DP3:
  854. case D3DSIO_DP4:
  855. case D3DSIO_DST:
  856. case D3DSIO_M3x2:
  857. case D3DSIO_M3x3:
  858. case D3DSIO_M3x4:
  859. case D3DSIO_M4x3:
  860. case D3DSIO_M4x4:
  861. case D3DSIO_MAX:
  862. case D3DSIO_MIN:
  863. case D3DSIO_MUL:
  864. case D3DSIO_SGE:
  865. case D3DSIO_SLT:
  866. bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 2); break;
  867. case D3DSIO_MAD:
  868. bBadParamCount = (m_pCurrInst->m_DstParamCount != 1) || (m_pCurrInst->m_SrcParamCount != 3); break;
  869. }
  870. if (bBadParamCount)
  871. {
  872. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid parameter count." );
  873. m_ErrorCount++;
  874. return FALSE; // no more checks on this instruction
  875. }
  876. return TRUE;
  877. }
  878. //-----------------------------------------------------------------------------
  879. // CVShaderValidator::Rule_ValidSrcParams
  880. //
  881. // ** Rule:
  882. // For each source parameter,
  883. // Source register type must be D3DSPR_TEMP/_INPUT/_CONST.
  884. // Register # must be within range for register type,
  885. // including the special case where matrix macro ops read source reg# + offset.
  886. // Modifier must be D3DSPSM_NONE or _NEG.
  887. // If version is < 1.1, addressmode must be absolute.
  888. // If the register type is not _CONST, addressmode must be absolute.
  889. // If relative addressing is used for constants, a0.x must be referenced.
  890. // Swizzle cannot be used for vector*matrix instructions.
  891. //
  892. // ** When to call:
  893. // Per instruction.
  894. //
  895. // ** Returns:
  896. // Always TRUE.
  897. //
  898. // Errors in any of the source parameters causes m_bSrcParamError[i]
  899. // to be TRUE, so later rules that only apply when a particular source
  900. // parameter was valid know whether they need to execute or not.
  901. // e.g. Rule_SrcInitialized.
  902. //
  903. //-----------------------------------------------------------------------------
  904. BOOL CVShaderValidator::Rule_ValidSrcParams() // could break this down for more granularity
  905. {
  906. for( UINT i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
  907. {
  908. DXGASSERT(i < 3);
  909. BOOL bFoundSrcError = FALSE;
  910. SRCPARAM* pSrcParam = &(m_pCurrInst->m_SrcParam[i]);
  911. UINT ValidRegNum = 0;
  912. BOOL bSkipOutOfRangeCheck = FALSE;
  913. char* SourceName[3] = {"first", "second", "third"};
  914. switch(pSrcParam->m_RegType)
  915. {
  916. case D3DSPR_TEMP: ValidRegNum = m_pTempRegFile->GetNumRegs(); break;
  917. case D3DSPR_INPUT: ValidRegNum = m_pInputRegFile->GetNumRegs(); break;
  918. case D3DSPR_CONST:
  919. if(m_bIgnoreConstantInitializationChecks)
  920. bSkipOutOfRangeCheck = TRUE;
  921. else
  922. ValidRegNum = m_pConstRegFile->GetNumRegs();
  923. break;
  924. default:
  925. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid reg type for %s source param.", SourceName[i]);
  926. m_ErrorCount++;
  927. bFoundSrcError = TRUE;
  928. }
  929. if( (!bFoundSrcError) && (!bSkipOutOfRangeCheck))
  930. {
  931. UINT NumConsecutiveRegistersUsed = 1;
  932. if( 1 == i )
  933. {
  934. switch( m_pCurrInst->m_Type )
  935. {
  936. case D3DSIO_M3x2:
  937. NumConsecutiveRegistersUsed = 2;
  938. break;
  939. case D3DSIO_M3x3:
  940. NumConsecutiveRegistersUsed = 3;
  941. break;
  942. case D3DSIO_M3x4:
  943. NumConsecutiveRegistersUsed = 4;
  944. break;
  945. case D3DSIO_M4x3:
  946. NumConsecutiveRegistersUsed = 3;
  947. break;
  948. case D3DSIO_M4x4:
  949. NumConsecutiveRegistersUsed = 4;
  950. break;
  951. }
  952. }
  953. if((pSrcParam->m_RegNum >= ValidRegNum) && (D3DVS_ADDRMODE_ABSOLUTE == pSrcParam->m_AddressMode))
  954. {
  955. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid reg num: %d for %s source param. Max allowed for this type is %d.",
  956. pSrcParam->m_RegNum, SourceName[i], ValidRegNum - 1);
  957. m_ErrorCount++;
  958. bFoundSrcError = TRUE;
  959. }
  960. else if( NumConsecutiveRegistersUsed > 1 )
  961. {
  962. if( pSrcParam->m_RegNum + NumConsecutiveRegistersUsed - 1 >= ValidRegNum )
  963. {
  964. if( !((D3DSPR_CONST == pSrcParam->m_RegType) && (D3DVS_ADDRMODE_RELATIVE == pSrcParam->m_AddressMode)) )
  965. {
  966. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
  967. "Reg num: %d for %s source param on matrix instruction causes attempt to access out of range register number %d. Max allowed for this type is %d.",
  968. pSrcParam->m_RegNum, SourceName[i], pSrcParam->m_RegNum + NumConsecutiveRegistersUsed - 1, ValidRegNum - 1);
  969. }
  970. m_ErrorCount++;
  971. bFoundSrcError = TRUE;
  972. }
  973. }
  974. }
  975. switch( pSrcParam->m_SrcMod )
  976. {
  977. case D3DSPSM_NEG:
  978. if( 1 == i )
  979. {
  980. switch( m_pCurrInst->m_Type )
  981. {
  982. case D3DSIO_M3x2:
  983. case D3DSIO_M3x3:
  984. case D3DSIO_M3x4:
  985. case D3DSIO_M4x3:
  986. case D3DSIO_M4x4:
  987. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Cannot negate second source parameter to vector*matrix instructions.");
  988. m_ErrorCount++;
  989. bFoundSrcError = TRUE;
  990. break;
  991. }
  992. }
  993. break;
  994. case D3DSPSM_NONE:
  995. break;
  996. default:
  997. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid src mod for %s source param.",
  998. SourceName[i]);
  999. m_ErrorCount++;
  1000. bFoundSrcError = TRUE;
  1001. }
  1002. if( pSrcParam->m_AddressMode != D3DVS_ADDRMODE_ABSOLUTE
  1003. &&
  1004. ( m_Version < D3DVS_VERSION(1,1) || pSrcParam->m_RegType != D3DSPR_CONST )
  1005. )
  1006. {
  1007. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Address mode must be absolute (%s source param).",
  1008. SourceName[i]);
  1009. m_ErrorCount++;
  1010. bFoundSrcError = TRUE;
  1011. }
  1012. if( (pSrcParam->m_AddressMode == D3DVS_ADDRMODE_RELATIVE) &&
  1013. (D3DSPR_CONST == pSrcParam->m_RegType) )
  1014. {
  1015. if( pSrcParam->m_RelativeAddrComponent != D3DSP_WRITEMASK_0 )
  1016. {
  1017. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Relative addressing of constant register must reference a0.x only.",
  1018. SourceName[i]);
  1019. m_ErrorCount++;
  1020. bFoundSrcError = TRUE;
  1021. }
  1022. }
  1023. if( pSrcParam->m_SwizzleShift != D3DSP_NOSWIZZLE )
  1024. {
  1025. if( 1 == i )
  1026. {
  1027. switch( m_pCurrInst->m_Type )
  1028. {
  1029. case D3DSIO_M3x2:
  1030. case D3DSIO_M3x3:
  1031. case D3DSIO_M3x4:
  1032. case D3DSIO_M4x3:
  1033. case D3DSIO_M4x4:
  1034. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Cannot swizzle second source parameter to vector*matrix instructions.");
  1035. m_ErrorCount++;
  1036. bFoundSrcError = TRUE;
  1037. break;
  1038. }
  1039. }
  1040. }
  1041. if( bFoundSrcError )
  1042. {
  1043. m_bSrcParamError[i] = TRUE; // needed in Rule_SrcInitialized
  1044. }
  1045. }
  1046. return TRUE;
  1047. }
  1048. //-----------------------------------------------------------------------------
  1049. // CVShaderValidator::Rule_SrcInitialized
  1050. //
  1051. // ** Rule:
  1052. // for each source parameter,
  1053. // The register type must be _TEMP, _INPUT or _CONST.
  1054. // Certain components of the register need to have been initialized, depending
  1055. // on what the instruction is and also taking into account the source swizzle.
  1056. // For reads of the _CONST register file, do no validation.
  1057. //
  1058. // ** When to call:
  1059. // Per instruction. This rule must be called before Rule_ValidDstParam().
  1060. //
  1061. // ** Returns:
  1062. // Always TRUE.
  1063. //
  1064. // NOTE: This rule also updates the access history to indicate reads of the
  1065. // affected components of each source register.
  1066. //-----------------------------------------------------------------------------
  1067. BOOL CVShaderValidator::Rule_SrcInitialized()
  1068. {
  1069. DSTPARAM* pDstParam = &(m_pCurrInst->m_DstParam);
  1070. for( UINT i = 0; i < m_pCurrInst->m_SrcParamCount; i++ )
  1071. {
  1072. SRCPARAM* pSrcParam = &(m_pCurrInst->m_SrcParam[i]);
  1073. UINT RegNum = pSrcParam->m_RegNum;
  1074. CRegisterFile* pRegFile = NULL;
  1075. char* RegChar = NULL;
  1076. UINT NumConsecutiveRegistersUsed = 1; // more than one for matrix mul macros.
  1077. DWORD RelativeAddrComponent = 0;
  1078. if( m_bSrcParamError[i] ) continue;
  1079. switch( pSrcParam->m_RegType )
  1080. {
  1081. case D3DSPR_TEMP:
  1082. pRegFile = m_pTempRegFile;
  1083. RegChar = "r";
  1084. break;
  1085. case D3DSPR_INPUT:
  1086. pRegFile = m_pInputRegFile;
  1087. RegChar = "v";
  1088. break;
  1089. case D3DSPR_CONST:
  1090. if( D3DVS_ADDRMODE_RELATIVE == pSrcParam->m_AddressMode )
  1091. {
  1092. // make sure a0 was initialized.
  1093. pRegFile = m_pAddrRegFile;
  1094. RegChar = "a";
  1095. RegNum = 0;
  1096. RelativeAddrComponent = pSrcParam->m_RelativeAddrComponent;
  1097. break;
  1098. }
  1099. continue; // no validation for const register reads (no need to update access history either).
  1100. }
  1101. if( !pRegFile ) continue;
  1102. if( 1 == i )
  1103. {
  1104. switch( m_pCurrInst->m_Type )
  1105. {
  1106. case D3DSIO_M3x2:
  1107. NumConsecutiveRegistersUsed = 2;
  1108. break;
  1109. case D3DSIO_M3x3:
  1110. NumConsecutiveRegistersUsed = 3;
  1111. break;
  1112. case D3DSIO_M3x4:
  1113. NumConsecutiveRegistersUsed = 4;
  1114. break;
  1115. case D3DSIO_M4x3:
  1116. NumConsecutiveRegistersUsed = 3;
  1117. break;
  1118. case D3DSIO_M4x4:
  1119. NumConsecutiveRegistersUsed = 4;
  1120. break;
  1121. }
  1122. }
  1123. // check for read of uninitialized components
  1124. for( UINT j = 0; j < (RelativeAddrComponent?1:NumConsecutiveRegistersUsed); j++ ) // will loop for macro matrix instructions
  1125. {
  1126. DWORD UninitializedComponentsMask = 0;
  1127. UINT NumUninitializedComponents = 0;
  1128. for( UINT k = 0; k < 4; k++ )
  1129. {
  1130. if( (RelativeAddrComponent ? RelativeAddrComponent : pSrcParam->m_ComponentReadMask) & COMPONENT_MASKS[k] )
  1131. {
  1132. if( NULL == pRegFile->m_pAccessHistory[k][RegNum + j].m_pMostRecentWriter &&
  1133. !pRegFile->m_pAccessHistory[k][RegNum + j].m_bPreShaderInitialized )
  1134. {
  1135. NumUninitializedComponents++;
  1136. UninitializedComponentsMask |= COMPONENT_MASKS[k];
  1137. }
  1138. }
  1139. }
  1140. if( NumUninitializedComponents )
  1141. {
  1142. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Read of uninitialized component%s(*) in %s%d: %s",
  1143. NumUninitializedComponents > 1 ? "s" : "",
  1144. RegChar, RegNum + j, MakeAffectedComponentsText(UninitializedComponentsMask,FALSE,TRUE));
  1145. m_ErrorCount++;
  1146. }
  1147. // Update register file to indicate READ.
  1148. // Multiple reads of the same register component by the current instruction
  1149. // will only be logged as one read in the access history.
  1150. for( UINT k = 0; k < 4; k++ )
  1151. {
  1152. #define PREV_READER(_CHAN,_REG) \
  1153. ((NULL == pRegFile->m_pAccessHistory[_CHAN][_REG].m_pMostRecentReader) ? NULL :\
  1154. pRegFile->m_pAccessHistory[_CHAN][_REG].m_pMostRecentReader->m_pInst)
  1155. if((RelativeAddrComponent ? RelativeAddrComponent : pSrcParam->m_ComponentReadMask) & COMPONENT_MASKS[k])
  1156. {
  1157. if( PREV_READER(k,RegNum) != m_pCurrInst )
  1158. {
  1159. if( !pRegFile->m_pAccessHistory[k][RegNum].NewAccess(m_pCurrInst,FALSE) )
  1160. {
  1161. Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory");
  1162. m_ErrorCount++;
  1163. }
  1164. }
  1165. }
  1166. }
  1167. }
  1168. }
  1169. return TRUE;
  1170. }
  1171. //-----------------------------------------------------------------------------
  1172. // CVShaderValidator::Rule_ValidAddressRegWrite
  1173. //
  1174. // ** Rule:
  1175. // Address register may only be written by MOV, and only for version >= 1.1.
  1176. // Register format must be a0.x
  1177. //
  1178. // ** When to call:
  1179. // Per instruction.
  1180. //
  1181. // ** Returns:
  1182. // Always TRUE
  1183. //
  1184. //-----------------------------------------------------------------------------
  1185. BOOL CVShaderValidator::Rule_ValidAddressRegWrite()
  1186. {
  1187. DSTPARAM* pDstParam = &(m_pCurrInst->m_DstParam);
  1188. if( pDstParam->m_bParamUsed )
  1189. {
  1190. if( D3DSPR_ADDR == pDstParam->m_RegType )
  1191. {
  1192. if( m_Version < D3DVS_VERSION(1,1) )
  1193. {
  1194. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Address register not available for vertex shader version %d.%d. Version 1.1 required.",
  1195. D3DSHADER_VERSION_MAJOR(m_Version),D3DSHADER_VERSION_MINOR(m_Version) );
  1196. m_ErrorCount++;
  1197. }
  1198. if( D3DSIO_MOV == m_pCurrInst->m_Type )
  1199. {
  1200. if( 0 != pDstParam->m_RegNum ||
  1201. D3DSP_WRITEMASK_0 != pDstParam->m_WriteMask ||
  1202. D3DSPDM_NONE != pDstParam->m_DstMod ||
  1203. DSTSHIFT_NONE != pDstParam->m_DstShift )
  1204. {
  1205. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Format for address register must be a0.x." );
  1206. m_ErrorCount++;
  1207. }
  1208. }
  1209. else
  1210. {
  1211. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Only the mov instruction is allowed to write to the address register." );
  1212. m_ErrorCount++;
  1213. }
  1214. }
  1215. }
  1216. return TRUE;
  1217. }
  1218. //-----------------------------------------------------------------------------
  1219. // CVShaderValidator::Rule_ValidDstParam
  1220. //
  1221. // ** Rule:
  1222. // Dst register type must be temp/addr/rastout/attrout/texcrdout,
  1223. // and reg num must be within range for register type.
  1224. //
  1225. // There can be no dst modifiers or shifts with vertex shaders.
  1226. //
  1227. // The writemask cannot be 'none'.
  1228. //
  1229. // ** When to call:
  1230. // Per instruction.
  1231. //
  1232. // ** Returns:
  1233. // Always TRUE.
  1234. //
  1235. // NOTE: After checking the dst parameter, if no error was found,
  1236. // the write to the appropriate component(s) of the destination register
  1237. // is recorded by this function, so subsequent rules may check for previous
  1238. // write to registers.
  1239. //-----------------------------------------------------------------------------
  1240. BOOL CVShaderValidator::Rule_ValidDstParam() // could break this down for more granularity
  1241. {
  1242. BOOL bFoundDstError = FALSE;
  1243. DSTPARAM* pDstParam = &(m_pCurrInst->m_DstParam);
  1244. UINT RegNum = pDstParam->m_RegNum;
  1245. if( pDstParam->m_bParamUsed )
  1246. {
  1247. UINT ValidRegNum = 0;
  1248. BOOL bWritable = FALSE;
  1249. switch( pDstParam->m_RegType )
  1250. {
  1251. case D3DSPR_TEMP:
  1252. bWritable = m_pTempRegFile->IsWritable(); //(TRUE)
  1253. ValidRegNum = m_pTempRegFile->GetNumRegs();
  1254. break;
  1255. case D3DSPR_ADDR:
  1256. bWritable = m_pAddrRegFile->IsWritable(); //(TRUE)
  1257. ValidRegNum = m_pAddrRegFile->GetNumRegs();
  1258. break;
  1259. case D3DSPR_RASTOUT:
  1260. bWritable = m_pRastOutputRegFile->IsWritable(); //(TRUE)
  1261. ValidRegNum = m_pRastOutputRegFile->GetNumRegs();
  1262. break;
  1263. case D3DSPR_ATTROUT:
  1264. bWritable = m_pAttrOutputRegFile->IsWritable(); //(TRUE)
  1265. ValidRegNum = m_pAttrOutputRegFile->GetNumRegs();
  1266. break;
  1267. case D3DSPR_TEXCRDOUT:
  1268. bWritable = m_pTexCrdOutputRegFile->IsWritable(); //(TRUE)
  1269. ValidRegNum = m_pTexCrdOutputRegFile->GetNumRegs();
  1270. break;
  1271. }
  1272. if( !bWritable || !ValidRegNum )
  1273. {
  1274. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid reg type for dest param." );
  1275. m_ErrorCount++;
  1276. bFoundDstError = TRUE;
  1277. }
  1278. else if( RegNum >= ValidRegNum )
  1279. {
  1280. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Invalid dest reg num: %d. Max allowed for this reg type is %d.", RegNum, ValidRegNum - 1);
  1281. m_ErrorCount++;
  1282. bFoundDstError = TRUE;
  1283. }
  1284. switch( pDstParam->m_DstMod )
  1285. {
  1286. case D3DSPDM_NONE:
  1287. break;
  1288. default:
  1289. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Dst modifiers not allowed for vertex shaders." );
  1290. m_ErrorCount++;
  1291. bFoundDstError = TRUE;
  1292. }
  1293. switch( pDstParam->m_DstShift )
  1294. {
  1295. case DSTSHIFT_NONE:
  1296. break;
  1297. default:
  1298. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Dest shifts not allowed for vertex shaders." );
  1299. m_ErrorCount++;
  1300. bFoundDstError = TRUE;
  1301. }
  1302. if( 0 == pDstParam->m_WriteMask )
  1303. {
  1304. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Dest write mask cannot be empty." );
  1305. m_ErrorCount++;
  1306. bFoundDstError = TRUE;
  1307. }
  1308. // Update register file to indicate write.
  1309. if( !bFoundDstError )
  1310. {
  1311. CRegisterFile* pRegFile = NULL;
  1312. switch( pDstParam->m_RegType )
  1313. {
  1314. case D3DSPR_TEMP:
  1315. pRegFile = m_pTempRegFile;
  1316. break;
  1317. case D3DSPR_ADDR:
  1318. pRegFile = m_pAddrRegFile;
  1319. break;
  1320. case D3DSPR_RASTOUT:
  1321. pRegFile = m_pRastOutputRegFile;
  1322. break;
  1323. case D3DSPR_ATTROUT:
  1324. pRegFile = m_pAttrOutputRegFile;
  1325. break;
  1326. case D3DSPR_TEXCRDOUT:
  1327. pRegFile = m_pTexCrdOutputRegFile;
  1328. break;
  1329. }
  1330. if( pRegFile )
  1331. {
  1332. if( pDstParam->m_WriteMask & D3DSP_WRITEMASK_0 )
  1333. pRegFile->m_pAccessHistory[0][RegNum].NewAccess(m_pCurrInst,TRUE);
  1334. if( pDstParam->m_WriteMask & D3DSP_WRITEMASK_1 )
  1335. pRegFile->m_pAccessHistory[1][RegNum].NewAccess(m_pCurrInst,TRUE);
  1336. if( pDstParam->m_WriteMask & D3DSP_WRITEMASK_2 )
  1337. pRegFile->m_pAccessHistory[2][RegNum].NewAccess(m_pCurrInst,TRUE);
  1338. if( pDstParam->m_WriteMask & D3DSP_WRITEMASK_3 )
  1339. pRegFile->m_pAccessHistory[3][RegNum].NewAccess(m_pCurrInst,TRUE);
  1340. }
  1341. }
  1342. }
  1343. return TRUE;
  1344. }
  1345. //-----------------------------------------------------------------------------
  1346. // CVShaderValidator::Rule_ValidFRCInstruction
  1347. //
  1348. // ** Rule:
  1349. // The only valid write masks for the FRC instruction are .y and .xy
  1350. //
  1351. // ** When to call:
  1352. // Per instruction.
  1353. //
  1354. // ** Returns:
  1355. // Always TRUE.
  1356. //
  1357. //-----------------------------------------------------------------------------
  1358. BOOL CVShaderValidator::Rule_ValidFRCInstruction()
  1359. {
  1360. if( NULL == m_pCurrInst )
  1361. return TRUE;
  1362. if( D3DSIO_FRC == m_pCurrInst->m_Type )
  1363. {
  1364. if( ( (D3DSP_WRITEMASK_0 | D3DSP_WRITEMASK_1) != m_pCurrInst->m_DstParam.m_WriteMask ) &&
  1365. ( D3DSP_WRITEMASK_1 != m_pCurrInst->m_DstParam.m_WriteMask ) )
  1366. {
  1367. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
  1368. "The only valid write masks for the FRC instruction are .xy and .y." );
  1369. m_ErrorCount++;
  1370. }
  1371. }
  1372. return TRUE;
  1373. }
  1374. //-----------------------------------------------------------------------------
  1375. // CVShaderValidator::Rule_ValidRegisterPortUsage
  1376. //
  1377. // ** Rule:
  1378. // Each register class (TEMP,TEXTURE,INPUT,CONST) may only appear as parameters
  1379. // in an individual instruction up to a maximum number of times.
  1380. //
  1381. // In additon there is special treatment for constant registers:
  1382. // - absolute and relative addressing of constants cannot be combined
  1383. // - relative addressing of constants can be used more than once in an
  1384. // instruction, as long as each instance is identical
  1385. //
  1386. // For matrix ops,
  1387. // - multiple constant registers of any type (including relative offset)
  1388. // can never be paired as sources
  1389. // - multiple input registers (same or different) can never be paired as sources
  1390. //
  1391. // ** When to call:
  1392. // Per instruction.
  1393. //
  1394. // ** Returns:
  1395. // Always TRUE.
  1396. //
  1397. //-----------------------------------------------------------------------------
  1398. BOOL CVShaderValidator::Rule_ValidRegisterPortUsage()
  1399. {
  1400. UINT TempRegAccessCount = 0;
  1401. UINT TempRegAccess[SHADER_INSTRUCTION_MAX_SRCPARAMS];
  1402. UINT InputRegAccessCount = 0;
  1403. UINT InputRegAccess[SHADER_INSTRUCTION_MAX_SRCPARAMS];
  1404. UINT ConstRegAccessCount = 0; // mad r0, c0, c0, c1 counts as *2* const reg accesses
  1405. UINT ConstRegAccess[SHADER_INSTRUCTION_MAX_SRCPARAMS];
  1406. BOOL bMatrixOp = FALSE;
  1407. BOOL bSeenRelativeAddr = FALSE;
  1408. UINT SeenRelativeAddrBase = 0;
  1409. DWORD SeenRelativeAddrComp = 0;
  1410. BOOL bSeenAbsoluteAddr = FALSE;
  1411. UINT NumConsecutiveRegistersUsed = 1;
  1412. UINT NumConstRegs = 0; // mad r0, c0, c0, c1 counts as *3* const reg accesses with this variable
  1413. UINT NumInputRegs = 0; // mad r0, v0, v0, v1 counts as *3* input reg accesses with this variable
  1414. switch( m_pCurrInst->m_Type )
  1415. {
  1416. case D3DSIO_M3x2:
  1417. NumConsecutiveRegistersUsed = 2;
  1418. bMatrixOp = TRUE;
  1419. break;
  1420. case D3DSIO_M3x3:
  1421. NumConsecutiveRegistersUsed = 3;
  1422. bMatrixOp = TRUE;
  1423. break;
  1424. case D3DSIO_M3x4:
  1425. NumConsecutiveRegistersUsed = 4;
  1426. bMatrixOp = TRUE;
  1427. break;
  1428. case D3DSIO_M4x3:
  1429. NumConsecutiveRegistersUsed = 3;
  1430. bMatrixOp = TRUE;
  1431. break;
  1432. case D3DSIO_M4x4:
  1433. NumConsecutiveRegistersUsed = 4;
  1434. bMatrixOp = TRUE;
  1435. break;
  1436. default:
  1437. break;
  1438. }
  1439. for( UINT i = 0; i < SHADER_INSTRUCTION_MAX_SRCPARAMS; i++ )
  1440. {
  1441. D3DSHADER_PARAM_REGISTER_TYPE RegType;
  1442. UINT RegNum;
  1443. if( !m_pCurrInst->m_SrcParam[i].m_bParamUsed ) continue;
  1444. RegType = m_pCurrInst->m_SrcParam[i].m_RegType;
  1445. RegNum = m_pCurrInst->m_SrcParam[i].m_RegNum;
  1446. UINT* pCount = NULL;
  1447. UINT* pAccess = NULL;
  1448. switch( RegType )
  1449. {
  1450. case D3DSPR_TEMP:
  1451. pCount = &TempRegAccessCount;
  1452. pAccess = TempRegAccess;
  1453. break;
  1454. case D3DSPR_INPUT:
  1455. NumInputRegs++;
  1456. pCount = &InputRegAccessCount;
  1457. pAccess = InputRegAccess;
  1458. break;
  1459. case D3DSPR_CONST:
  1460. NumConstRegs++;
  1461. pCount = &ConstRegAccessCount;
  1462. pAccess = ConstRegAccess;
  1463. if( D3DVS_ADDRMODE_RELATIVE == m_pCurrInst->m_SrcParam[i].m_AddressMode )
  1464. {
  1465. if( bSeenAbsoluteAddr )
  1466. {
  1467. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
  1468. "Absolute and relative addressing of constant registers cannot be combined in one instruction.");
  1469. m_ErrorCount++;
  1470. }
  1471. else if( bSeenRelativeAddr &&
  1472. ((SeenRelativeAddrBase != RegNum) || (SeenRelativeAddrComp != m_pCurrInst->m_SrcParam[i].m_RelativeAddrComponent)))
  1473. {
  1474. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
  1475. "Different relative addressing of constant registers cannot be combined in one instruction.");
  1476. m_ErrorCount++;
  1477. }
  1478. bSeenRelativeAddr = TRUE;
  1479. SeenRelativeAddrBase = RegNum;
  1480. SeenRelativeAddrComp = m_pCurrInst->m_SrcParam[i].m_RelativeAddrComponent;
  1481. }
  1482. else
  1483. {
  1484. if( bSeenRelativeAddr )
  1485. {
  1486. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst,
  1487. "Absolute and relative addressing of constant registers cannot be combined in one instruction.");
  1488. m_ErrorCount++;
  1489. }
  1490. bSeenAbsoluteAddr = TRUE;
  1491. }
  1492. break;
  1493. }
  1494. if( pCount && pAccess )
  1495. {
  1496. BOOL bNewRegNumberAccessed = TRUE;
  1497. for( UINT j = 0; j < *pCount; j++ )
  1498. {
  1499. if( RegNum == pAccess[j] )
  1500. {
  1501. bNewRegNumberAccessed = FALSE;
  1502. break;
  1503. }
  1504. }
  1505. if( bNewRegNumberAccessed )
  1506. {
  1507. pAccess[*pCount] = RegNum;
  1508. (*pCount)++;
  1509. }
  1510. }
  1511. }
  1512. if( TempRegAccessCount > m_pTempRegFile->GetNumReadPorts() )
  1513. {
  1514. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different temp registers (r#) read by instruction. Max. different temp registers readable per instruction is %d.",
  1515. TempRegAccessCount, m_pTempRegFile->GetNumReadPorts());
  1516. m_ErrorCount++;
  1517. }
  1518. if( InputRegAccessCount > m_pInputRegFile->GetNumReadPorts() )
  1519. {
  1520. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different input registers (v#) read by instruction. Max. different input registers readable per instruction is %d.",
  1521. InputRegAccessCount, m_pInputRegFile->GetNumReadPorts());
  1522. m_ErrorCount++;
  1523. }
  1524. if( ConstRegAccessCount > m_pConstRegFile->GetNumReadPorts() )
  1525. {
  1526. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "%d different constant registers (c#) read by instruction. Max. different constant registers readable per instruction is %d.",
  1527. ConstRegAccessCount, m_pConstRegFile->GetNumReadPorts());
  1528. m_ErrorCount++;
  1529. }
  1530. if( bMatrixOp )
  1531. {
  1532. if(1 < NumConstRegs)
  1533. {
  1534. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Multiple constant registers cannot be read by a matrix op.");
  1535. m_ErrorCount++;
  1536. }
  1537. if(1 < NumInputRegs)
  1538. {
  1539. Spew( SPEW_INSTRUCTION_ERROR, m_pCurrInst, "Multiple input registers cannot be read by a matrix op.");
  1540. m_ErrorCount++;
  1541. }
  1542. }
  1543. return TRUE;
  1544. }
  1545. //-----------------------------------------------------------------------------
  1546. // CVShaderValidator::Rule_ValidInstructionCount
  1547. //
  1548. // ** Rule:
  1549. // Make sure instruction count for vertex shader version has not been exceeded.
  1550. //
  1551. // Nop, and comments (already stripped) do not count towards the limit.
  1552. //
  1553. // ** When to call:
  1554. // Per instruction AND after all instructions seen.
  1555. //
  1556. // ** Returns:
  1557. // Always TRUE.
  1558. //
  1559. //-----------------------------------------------------------------------------
  1560. BOOL CVShaderValidator::Rule_ValidInstructionCount()
  1561. {
  1562. static UINT s_OpCount;
  1563. static UINT s_MaxTotalOpCount;
  1564. if( NULL == m_pCurrInst->m_pPrevInst ) // First instruction - initialize static vars
  1565. {
  1566. s_OpCount = 0;
  1567. switch(m_Version)
  1568. {
  1569. case D3DVS_VERSION(1,0):
  1570. case D3DVS_VERSION(1,1):
  1571. default:
  1572. s_MaxTotalOpCount = 128;
  1573. break;
  1574. }
  1575. }
  1576. if( m_bSeenAllInstructions )
  1577. {
  1578. if( s_OpCount > s_MaxTotalOpCount )
  1579. {
  1580. Spew( SPEW_GLOBAL_ERROR, NULL, "Number of instruction slots used too high: %d. Max. allowed is %d.",
  1581. s_OpCount, s_MaxTotalOpCount);
  1582. m_ErrorCount++;
  1583. }
  1584. return TRUE;
  1585. }
  1586. switch( m_pCurrInst->m_Type )
  1587. {
  1588. case D3DSIO_NOP:
  1589. s_OpCount += 0; break;
  1590. case D3DSIO_ADD:
  1591. case D3DSIO_DP3:
  1592. case D3DSIO_DP4:
  1593. case D3DSIO_DST:
  1594. case D3DSIO_EXPP:
  1595. case D3DSIO_LIT:
  1596. case D3DSIO_LOGP:
  1597. case D3DSIO_MAD:
  1598. case D3DSIO_MAX:
  1599. case D3DSIO_MIN:
  1600. case D3DSIO_MOV:
  1601. case D3DSIO_MUL:
  1602. case D3DSIO_RCP:
  1603. case D3DSIO_RSQ:
  1604. case D3DSIO_SGE:
  1605. case D3DSIO_SLT:
  1606. s_OpCount += 1; break;
  1607. case D3DSIO_M3x2:
  1608. s_OpCount += 2; break;
  1609. case D3DSIO_FRC:
  1610. case D3DSIO_M3x3:
  1611. case D3DSIO_M4x3:
  1612. s_OpCount += 3; break;
  1613. case D3DSIO_M3x4:
  1614. case D3DSIO_M4x4:
  1615. s_OpCount += 4; break;
  1616. case D3DSIO_EXP:
  1617. case D3DSIO_LOG:
  1618. s_OpCount += 10; break;
  1619. }
  1620. return TRUE;
  1621. }
  1622. //-----------------------------------------------------------------------------
  1623. // CVShaderValidator::Rule_oPosWritten
  1624. //
  1625. // ** Rule:
  1626. // First two channels (x,y) of oPos output register must be written.
  1627. //
  1628. // ** When to call:
  1629. // After all instructions have been seen.
  1630. //
  1631. // ** Returns:
  1632. // Always TRUE.
  1633. //
  1634. //-----------------------------------------------------------------------------
  1635. BOOL CVShaderValidator::Rule_oPosWritten()
  1636. {
  1637. UINT NumUninitializedComponents = 0;
  1638. DWORD UninitializedComponentsMask = 0;
  1639. for( UINT i = 0; i < 2; i++ ) // looking at component 0 (X) and component 1 (Y)
  1640. {
  1641. if( NULL == m_pRastOutputRegFile->m_pAccessHistory[i][0].m_pMostRecentWriter )
  1642. {
  1643. NumUninitializedComponents++;
  1644. UninitializedComponentsMask |= COMPONENT_MASKS[i];
  1645. }
  1646. }
  1647. if( 1 == NumUninitializedComponents )
  1648. {
  1649. Spew( SPEW_GLOBAL_ERROR, NULL, "Vertex shader must minimally write first two (x,y) components of oPos output register. Affected component%s(*): %s",
  1650. NumUninitializedComponents > 1 ? "s" : "", MakeAffectedComponentsText(UninitializedComponentsMask,FALSE,TRUE));
  1651. m_ErrorCount++;
  1652. }
  1653. else if( 2 == NumUninitializedComponents )
  1654. {
  1655. Spew( SPEW_GLOBAL_ERROR, NULL, "Vertex shader must minimally write first two (x,y) components of oPos output register.");
  1656. m_ErrorCount++;
  1657. }
  1658. return TRUE;
  1659. }
  1660. //-----------------------------------------------------------------------------
  1661. //
  1662. // CVShaderValidator Wrapper Functions
  1663. //
  1664. //-----------------------------------------------------------------------------
  1665. //-----------------------------------------------------------------------------
  1666. // ValidateVertexShaderInternal
  1667. //-----------------------------------------------------------------------------
  1668. BOOL ValidateVertexShaderInternal( const DWORD* pCode,
  1669. const DWORD* pDecl,
  1670. const D3DCAPS8* pCaps )
  1671. {
  1672. CVShaderValidator Validator(pCode,pDecl,pCaps,0);
  1673. return SUCCEEDED(Validator.GetStatus()) ? TRUE : FALSE;
  1674. }
  1675. //-----------------------------------------------------------------------------
  1676. // ValidateVertexShader
  1677. //
  1678. // Don't forget to call "free" on the buffer returned in ppBuf.
  1679. //-----------------------------------------------------------------------------
  1680. HRESULT WINAPI ValidateVertexShader( const DWORD* pCode,
  1681. const DWORD* pDecl,
  1682. const D3DCAPS8* pCaps,
  1683. const DWORD Flags,
  1684. char** const ppBuf )
  1685. {
  1686. CVShaderValidator Validator(pCode,pDecl,pCaps,Flags);
  1687. if( ppBuf )
  1688. {
  1689. *ppBuf = (char*)HeapAlloc(GetProcessHeap(), 0, Validator.GetRequiredLogBufferSize());
  1690. if( NULL == *ppBuf )
  1691. OutputDebugString("Out of memory.\n");
  1692. else
  1693. Validator.WriteLogToBuffer(*ppBuf);
  1694. }
  1695. return Validator.GetStatus();
  1696. }