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.

584 lines
20 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. // Copyright (C) Microsoft Corporation, 2000.
  3. //
  4. // valbase.cpp
  5. //
  6. // Direct3D Reference Device - PixelShader validation common infrastructure
  7. //
  8. ///////////////////////////////////////////////////////////////////////////////
  9. #include "pch.cpp"
  10. #pragma hdrstop
  11. //-----------------------------------------------------------------------------
  12. // DSTPARAM::DSTPARAM
  13. //-----------------------------------------------------------------------------
  14. DSTPARAM::DSTPARAM()
  15. {
  16. m_bParamUsed = FALSE;
  17. m_RegNum = (UINT)-1;
  18. m_WriteMask = 0;
  19. m_DstMod = D3DSPDM_NONE;
  20. m_DstShift = (DSTSHIFT)-1;
  21. m_RegType = (D3DSHADER_PARAM_REGISTER_TYPE)-1;
  22. m_ComponentReadMask = 0;
  23. }
  24. //-----------------------------------------------------------------------------
  25. // SRCPARAM::SRCPARAM
  26. //-----------------------------------------------------------------------------
  27. SRCPARAM::SRCPARAM()
  28. {
  29. m_bParamUsed = FALSE;
  30. m_RegNum = (UINT)-1;
  31. m_SwizzleShift = D3DSP_NOSWIZZLE;
  32. m_AddressMode = D3DVS_ADDRMODE_ABSOLUTE;
  33. m_RelativeAddrComponent = 0;
  34. m_SrcMod = D3DSPSM_NONE;
  35. m_RegType = (D3DSHADER_PARAM_REGISTER_TYPE)-1;
  36. m_ComponentReadMask = D3DSP_WRITEMASK_ALL;
  37. }
  38. //-----------------------------------------------------------------------------
  39. // CBaseInstruction::CBaseInstruction
  40. //-----------------------------------------------------------------------------
  41. CBaseInstruction::CBaseInstruction(CBaseInstruction* pPrevInst)
  42. {
  43. m_Type = D3DSIO_NOP;
  44. m_SrcParamCount = 0;
  45. m_DstParamCount = 0;
  46. m_pPrevInst = pPrevInst;
  47. m_pNextInst = NULL;
  48. m_pSpewLineNumber = NULL;
  49. m_pSpewFileName = NULL;
  50. m_SpewInstructionCount = 0;
  51. if( pPrevInst )
  52. {
  53. pPrevInst->m_pNextInst = this;
  54. }
  55. }
  56. //-----------------------------------------------------------------------------
  57. // CBaseInstruction::SetSpewFileNameAndLineNumber
  58. //-----------------------------------------------------------------------------
  59. void CBaseInstruction::SetSpewFileNameAndLineNumber(const char* pFileName, const DWORD* pLineNumber)
  60. {
  61. m_pSpewFileName = pFileName;
  62. m_pSpewLineNumber = pLineNumber;
  63. }
  64. //-----------------------------------------------------------------------------
  65. // CBaseInstruction::MakeInstructionLocatorString
  66. //
  67. // Don't forget to 'delete' the string returned.
  68. //-----------------------------------------------------------------------------
  69. char* CBaseInstruction::MakeInstructionLocatorString()
  70. {
  71. for(UINT Length = 128; Length < 65536; Length *= 2)
  72. {
  73. int BytesStored;
  74. char *pBuffer = new char[Length];
  75. if( !pBuffer )
  76. {
  77. OutputDebugString("Out of memory.\n");
  78. return NULL;
  79. }
  80. if( m_pSpewFileName )
  81. {
  82. BytesStored = _snprintf( pBuffer, Length, "%s(%d) : ",
  83. m_pSpewFileName, m_pSpewLineNumber ? *m_pSpewLineNumber : 1);
  84. }
  85. else
  86. {
  87. BytesStored = _snprintf( pBuffer, Length, "(Statement %d) ",
  88. m_SpewInstructionCount );
  89. }
  90. if( BytesStored >= 0 )
  91. return pBuffer;
  92. delete [] pBuffer;
  93. }
  94. return NULL;
  95. }
  96. //-----------------------------------------------------------------------------
  97. // CAccessHistoryNode::CAccessHistoryNode
  98. //-----------------------------------------------------------------------------
  99. CAccessHistoryNode::CAccessHistoryNode( CAccessHistoryNode* pPreviousAccess,
  100. CAccessHistoryNode* pPreviousWriter,
  101. CAccessHistoryNode* pPreviousReader,
  102. CBaseInstruction* pInst,
  103. BOOL bWrite )
  104. {
  105. DXGASSERT(pInst);
  106. m_pNextAccess = NULL;
  107. m_pPreviousAccess = pPreviousAccess;
  108. if( m_pPreviousAccess )
  109. m_pPreviousAccess->m_pNextAccess = this;
  110. m_pPreviousWriter = pPreviousWriter;
  111. m_pPreviousReader = pPreviousReader;
  112. m_pInst = pInst;
  113. m_bWrite = bWrite;
  114. m_bRead = !bWrite;
  115. }
  116. //-----------------------------------------------------------------------------
  117. // CAccessHistory::CAccessHistory
  118. //-----------------------------------------------------------------------------
  119. CAccessHistory::CAccessHistory()
  120. {
  121. m_pFirstAccess = NULL;
  122. m_pMostRecentAccess = NULL;
  123. m_pMostRecentWriter = NULL;
  124. m_pMostRecentReader = NULL;
  125. m_bPreShaderInitialized = FALSE;
  126. }
  127. //-----------------------------------------------------------------------------
  128. // CAccessHistory::~CAccessHistory
  129. //-----------------------------------------------------------------------------
  130. CAccessHistory::~CAccessHistory()
  131. {
  132. CAccessHistoryNode* pCurrNode = m_pFirstAccess;
  133. CAccessHistoryNode* pDeleteMe;
  134. while( pCurrNode )
  135. {
  136. pDeleteMe = pCurrNode;
  137. pCurrNode = pCurrNode->m_pNextAccess;
  138. delete pDeleteMe;
  139. }
  140. }
  141. //-----------------------------------------------------------------------------
  142. // CAccessHistory::NewAccess
  143. //-----------------------------------------------------------------------------
  144. BOOL CAccessHistory::NewAccess(CBaseInstruction* pInst, BOOL bWrite )
  145. {
  146. m_pMostRecentAccess = new CAccessHistoryNode( m_pMostRecentAccess,
  147. m_pMostRecentWriter,
  148. m_pMostRecentReader,
  149. pInst,
  150. bWrite );
  151. if( NULL == m_pMostRecentAccess )
  152. {
  153. return FALSE; // out of memory
  154. }
  155. if( m_pFirstAccess == NULL )
  156. {
  157. m_pFirstAccess = m_pMostRecentAccess;
  158. }
  159. if( bWrite )
  160. {
  161. m_pMostRecentWriter = m_pMostRecentAccess;
  162. }
  163. else // it is a read.
  164. {
  165. m_pMostRecentReader = m_pMostRecentAccess;
  166. }
  167. return TRUE;
  168. }
  169. //-----------------------------------------------------------------------------
  170. // CAccessHistory::InsertReadBeforeWrite
  171. //-----------------------------------------------------------------------------
  172. BOOL CAccessHistory::InsertReadBeforeWrite(CAccessHistoryNode* pWriteNode, CBaseInstruction* pInst)
  173. {
  174. DXGASSERT(pWriteNode && pWriteNode->m_bWrite && pInst );
  175. // append new node after node before pWriteNode
  176. CAccessHistoryNode* pReadBeforeWrite
  177. = new CAccessHistoryNode( pWriteNode->m_pPreviousAccess,
  178. pWriteNode->m_pPreviousWriter,
  179. pWriteNode->m_pPreviousReader,
  180. pInst,
  181. FALSE);
  182. if( NULL == pReadBeforeWrite )
  183. {
  184. return FALSE; // out of memory
  185. }
  186. // Patch up all the dangling pointers
  187. // Pointer to first access may change
  188. if( m_pFirstAccess == pWriteNode )
  189. {
  190. m_pFirstAccess = pReadBeforeWrite;
  191. }
  192. // Pointer to most recent reader may change
  193. if( m_pMostRecentReader == pWriteNode->m_pPreviousReader )
  194. {
  195. m_pMostRecentReader = pReadBeforeWrite;
  196. }
  197. // Update all m_pPreviousRead pointers that need to be updated to point to the newly
  198. // inserted read.
  199. CAccessHistoryNode* pCurrAccess = pWriteNode;
  200. while(pCurrAccess &&
  201. !(pCurrAccess->m_bRead && pCurrAccess->m_pPreviousAccess && pCurrAccess->m_pPreviousAccess->m_bRead) )
  202. {
  203. pCurrAccess->m_pPreviousReader = pReadBeforeWrite;
  204. pCurrAccess = pCurrAccess->m_pPreviousAccess;
  205. }
  206. // re-attach pWriteNode and the accesses linked after it back to the original list
  207. pWriteNode->m_pPreviousAccess = pReadBeforeWrite;
  208. pReadBeforeWrite->m_pNextAccess = pWriteNode;
  209. return TRUE;
  210. }
  211. //-----------------------------------------------------------------------------
  212. // CRegisterFile::CRegisterFile
  213. //-----------------------------------------------------------------------------
  214. CRegisterFile::CRegisterFile(UINT NumRegisters,
  215. BOOL bWritable,
  216. UINT NumReadPorts,
  217. BOOL bPreShaderInitialized)
  218. {
  219. m_bInitOk = FALSE;
  220. m_NumRegisters = NumRegisters;
  221. m_bWritable = bWritable;
  222. m_NumReadPorts = NumReadPorts;
  223. for( UINT i = 0; i < NUM_COMPONENTS_IN_REGISTER; i++ )
  224. {
  225. if( m_NumRegisters )
  226. {
  227. m_pAccessHistory[i] = new CAccessHistory[m_NumRegisters];
  228. if( NULL == m_pAccessHistory[i] )
  229. {
  230. OutputDebugString( "Direct3D Shader Validator: Out of memory.\n" );
  231. m_NumRegisters = 0;
  232. return;
  233. }
  234. }
  235. for( UINT j = 0; j < m_NumRegisters; j++ )
  236. {
  237. m_pAccessHistory[i][j].m_bPreShaderInitialized = bPreShaderInitialized;
  238. }
  239. // To get the access history for a component of a register, use:
  240. // m_pAccessHistory[component][register number]
  241. }
  242. }
  243. //-----------------------------------------------------------------------------
  244. // CRegisterFile::~CRegisterFile
  245. //-----------------------------------------------------------------------------
  246. CRegisterFile::~CRegisterFile()
  247. {
  248. for( UINT i = 0; i < NUM_COMPONENTS_IN_REGISTER; i++ )
  249. {
  250. delete [] m_pAccessHistory[i];
  251. }
  252. }
  253. //-----------------------------------------------------------------------------
  254. // CBaseShaderValidator::CBaseShaderValidator
  255. //-----------------------------------------------------------------------------
  256. CBaseShaderValidator::CBaseShaderValidator( const DWORD* pCode, const D3DCAPS8* pCaps, DWORD Flags )
  257. {
  258. m_ReturnCode = E_FAIL; // do this first.
  259. m_bBaseInitOk = FALSE;
  260. m_pLog = new CErrorLog(Flags & SHADER_VALIDATOR_LOG_ERRORS);
  261. if( NULL == m_pLog )
  262. {
  263. OutputDebugString("D3D PixelShader Validator: Out of memory.\n");
  264. return;
  265. }
  266. // ----------------------------------------------------
  267. // Member variable initialization
  268. //
  269. m_pCaps = pCaps;
  270. m_ErrorCount = 0;
  271. m_bSeenAllInstructions = FALSE;
  272. m_SpewInstructionCount = 0;
  273. m_pInstructionList = NULL;
  274. m_pCurrInst = NULL;
  275. m_pCurrToken = pCode; // can be null - vertex shader fixed function
  276. if( m_pCurrToken )
  277. m_Version = *(m_pCurrToken++);
  278. else
  279. m_Version = 0;
  280. m_pLatestSpewLineNumber = NULL;
  281. m_pLatestSpewFileName = NULL;
  282. for( UINT i = 0; i < SHADER_INSTRUCTION_MAX_SRCPARAMS; i++ )
  283. {
  284. m_bSrcParamError[i] = FALSE;
  285. }
  286. m_bBaseInitOk = TRUE;
  287. return;
  288. }
  289. //-----------------------------------------------------------------------------
  290. // CBaseShaderValidator::~CBaseShaderValidator
  291. //-----------------------------------------------------------------------------
  292. CBaseShaderValidator::~CBaseShaderValidator()
  293. {
  294. while( m_pCurrInst ) // Delete the linked list of instructions
  295. {
  296. CBaseInstruction* pDeleteMe = m_pCurrInst;
  297. m_pCurrInst = m_pCurrInst->m_pPrevInst;
  298. delete pDeleteMe;
  299. }
  300. delete m_pLog;
  301. }
  302. //-----------------------------------------------------------------------------
  303. // CBaseShaderValidator::DecodeDstParam
  304. //-----------------------------------------------------------------------------
  305. void CBaseShaderValidator::DecodeDstParam( DSTPARAM* pDstParam, DWORD Token )
  306. {
  307. DXGASSERT(pDstParam);
  308. pDstParam->m_bParamUsed = TRUE;
  309. pDstParam->m_RegNum = Token & D3DSP_REGNUM_MASK;
  310. pDstParam->m_WriteMask = Token & D3DSP_WRITEMASK_ALL;
  311. pDstParam->m_DstMod = (D3DSHADER_PARAM_DSTMOD_TYPE)(Token & D3DSP_DSTMOD_MASK);
  312. pDstParam->m_DstShift = (DSTSHIFT)((Token & D3DSP_DSTSHIFT_MASK) >> D3DSP_DSTSHIFT_SHIFT );
  313. pDstParam->m_RegType = (D3DSHADER_PARAM_REGISTER_TYPE)(Token & D3DSP_REGTYPE_MASK);
  314. }
  315. //-----------------------------------------------------------------------------
  316. // CBaseShaderValidator::DecodeSrcParam
  317. //-----------------------------------------------------------------------------
  318. void CBaseShaderValidator::DecodeSrcParam( SRCPARAM* pSrcParam, DWORD Token )
  319. {
  320. DXGASSERT(pSrcParam);
  321. pSrcParam->m_bParamUsed = TRUE;
  322. pSrcParam->m_RegNum = Token & D3DSP_REGNUM_MASK;
  323. pSrcParam->m_SwizzleShift = Token & D3DSP_SWIZZLE_MASK;
  324. pSrcParam->m_AddressMode = (D3DVS_ADDRESSMODE_TYPE)(Token & D3DVS_ADDRESSMODE_MASK);
  325. pSrcParam->m_RelativeAddrComponent = COMPONENT_MASKS[(Token >> 14) & 0x3];
  326. pSrcParam->m_SrcMod = (D3DSHADER_PARAM_SRCMOD_TYPE)(Token & D3DSP_SRCMOD_MASK);
  327. pSrcParam->m_RegType = (D3DSHADER_PARAM_REGISTER_TYPE)(Token & D3DSP_REGTYPE_MASK);
  328. }
  329. //-----------------------------------------------------------------------------
  330. // CBaseShaderValidator::ValidateShader
  331. //-----------------------------------------------------------------------------
  332. void CBaseShaderValidator::ValidateShader()
  333. {
  334. m_SpewInstructionCount++; // Consider the version token as the first
  335. // statement (1) for spew counting.
  336. if( !InitValidation() ) // i.e. Set up max register counts
  337. {
  338. // Returns false on:
  339. // 1) Unrecognized version token,
  340. // 2) Vertex shader declaration validation with no shader code (fixed function).
  341. // In this case InitValidation() sets m_ReturnCode as appropriate.
  342. return;
  343. }
  344. // Loop through all the instructions
  345. while( *m_pCurrToken != D3DPS_END() )
  346. {
  347. m_pCurrInst = AllocateNewInstruction(m_pCurrInst); // New instruction in linked list
  348. if( NULL == m_pCurrInst )
  349. {
  350. Spew( SPEW_GLOBAL_ERROR, NULL, "Out of memory." );
  351. return;
  352. }
  353. if( NULL == m_pInstructionList )
  354. m_pInstructionList = m_pCurrInst;
  355. if( !DecodeNextInstruction() )
  356. return;
  357. // Skip comments
  358. if( m_pCurrInst->m_Type == D3DSIO_COMMENT )
  359. {
  360. CBaseInstruction* pDeleteMe = m_pCurrInst;
  361. m_pCurrInst = m_pCurrInst->m_pPrevInst;
  362. if( pDeleteMe == m_pInstructionList )
  363. m_pInstructionList = NULL;
  364. delete pDeleteMe;
  365. continue;
  366. }
  367. for( UINT i = 0; i < SHADER_INSTRUCTION_MAX_SRCPARAMS; i++ )
  368. {
  369. m_bSrcParamError[i] = FALSE;
  370. }
  371. // Apply all the per-instruction rules - order the rule checks sensibly.
  372. // Note: Rules only return FALSE if they find an error that is so severe that it is impossible to
  373. // continue validation.
  374. if( !ApplyPerInstructionRules() )
  375. return;
  376. }
  377. m_bSeenAllInstructions = TRUE;
  378. // Apply any rules that also need to run after all instructions seen.
  379. //
  380. // NOTE: It is possible to get here with m_pCurrInst == NULL, if there were no
  381. // instructions. So any rules you add here must be able to account for that
  382. // possiblity.
  383. //
  384. ApplyPostInstructionsRules();
  385. // If no errors, then success!
  386. if( 0 == m_ErrorCount )
  387. m_ReturnCode = D3D_OK;
  388. }
  389. //-----------------------------------------------------------------------------
  390. // CBaseShaderValidator::ParseCommentForAssemblerMessages
  391. //-----------------------------------------------------------------------------
  392. void CBaseShaderValidator::ParseCommentForAssemblerMessages(const DWORD* pComment)
  393. {
  394. if( !pComment )
  395. return;
  396. // There must be at least 2 DWORDS in the comment
  397. if( (((*(pComment++)) & D3DSI_COMMENTSIZE_MASK) >> D3DSI_COMMENTSIZE_SHIFT) < 2 )
  398. return;
  399. switch(*(pComment++))
  400. {
  401. case MAKEFOURCC('F','I','L','E'):
  402. m_pLatestSpewFileName = (const char*)pComment;
  403. break;
  404. case MAKEFOURCC('L','I','N','E'):
  405. m_pLatestSpewLineNumber = pComment;
  406. break;
  407. }
  408. }
  409. //-----------------------------------------------------------------------------
  410. // CBaseShaderValidator::Spew
  411. //-----------------------------------------------------------------------------
  412. void CBaseShaderValidator::Spew( SPEW_TYPE SpewType,
  413. CBaseInstruction* pInst /* can be NULL */,
  414. const char* pszFormat, ... )
  415. {
  416. int Length = 128;
  417. char* pBuffer = NULL;
  418. va_list marker;
  419. if( !m_pLog )
  420. return;
  421. while( pBuffer == NULL )
  422. {
  423. int BytesStored = 0;
  424. int BytesLeft = Length;
  425. char *pIndex = NULL;
  426. char* pErrorLocationText = NULL;
  427. pBuffer = new char[Length];
  428. if( !pBuffer )
  429. {
  430. OutputDebugString("Out of memory.\n");
  431. return;
  432. }
  433. pIndex = pBuffer;
  434. // Code location text
  435. switch( SpewType )
  436. {
  437. case SPEW_INSTRUCTION_ERROR:
  438. case SPEW_INSTRUCTION_WARNING:
  439. if( pInst )
  440. pErrorLocationText = pInst->MakeInstructionLocatorString();
  441. break;
  442. }
  443. if( pErrorLocationText )
  444. {
  445. BytesStored = _snprintf( pIndex, BytesLeft - 1, pErrorLocationText );
  446. if( BytesStored < 0 ) goto OverFlow;
  447. BytesLeft -= BytesStored;
  448. pIndex += BytesStored;
  449. }
  450. // Spew text prefix
  451. switch( SpewType )
  452. {
  453. case SPEW_INSTRUCTION_ERROR:
  454. BytesStored = _snprintf( pIndex, BytesLeft - 1, "(Validation Error) " );
  455. break;
  456. case SPEW_GLOBAL_ERROR:
  457. BytesStored = _snprintf( pIndex, BytesLeft - 1, "(Global Validation Error) " );
  458. break;
  459. case SPEW_INSTRUCTION_WARNING:
  460. BytesStored = _snprintf( pIndex, BytesLeft - 1, "(Validation Warning) " );
  461. break;
  462. case SPEW_GLOBAL_WARNING:
  463. BytesStored = _snprintf( pIndex, BytesLeft - 1, "(Global Validation Warning) " );
  464. break;
  465. }
  466. if( BytesStored < 0 ) goto OverFlow;
  467. BytesLeft -= BytesStored;
  468. pIndex += BytesStored;
  469. // Formatted text
  470. va_start( marker, pszFormat );
  471. BytesStored = _vsnprintf( pIndex, BytesLeft - 1, pszFormat, marker );
  472. va_end( marker );
  473. if( BytesStored < 0 ) goto OverFlow;
  474. BytesLeft -= BytesStored;
  475. pIndex += BytesStored;
  476. m_pLog->AppendText(pBuffer);
  477. delete [] pErrorLocationText;
  478. delete [] pBuffer;
  479. break;
  480. OverFlow:
  481. delete [] pErrorLocationText;
  482. delete [] pBuffer;
  483. pBuffer = NULL;
  484. Length = Length * 2;
  485. }
  486. }
  487. //-----------------------------------------------------------------------------
  488. // CBaseShaderValidator::MakeAffectedComponentsText
  489. //
  490. // Note that the string returned is STATIC.
  491. //-----------------------------------------------------------------------------
  492. char* CBaseShaderValidator::MakeAffectedComponentsText( DWORD ComponentMask,
  493. BOOL bColorLabels,
  494. BOOL bPositionLabels)
  495. {
  496. char* ColorLabels[4] = {"r/", "g/", "b/", "a/"};
  497. char* PositionLabels[4] = {"x/", "y/", "z/", "w/"};
  498. char* NumericLabels[4] = {"0 ", "1 ", "2 ", "3"}; // always used
  499. static char s_AffectedComponents[28]; // enough to hold "*r/x/0 *g/y/1 *b/z/2 *a/w/3"
  500. UINT LabelCount = 0;
  501. s_AffectedComponents[0] = '\0';
  502. for( UINT i = 0; i < 4; i++ )
  503. {
  504. if( COMPONENT_MASKS[i] & ComponentMask )
  505. {
  506. strcat( s_AffectedComponents, "*" );
  507. }
  508. if( bColorLabels )
  509. strcat( s_AffectedComponents, ColorLabels[i] );
  510. if( bPositionLabels )
  511. strcat( s_AffectedComponents, PositionLabels[i] );
  512. strcat( s_AffectedComponents, NumericLabels[i] ); // always used
  513. }
  514. return s_AffectedComponents;
  515. }