Counter Strike : Global Offensive Source Code
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.

455 lines
16 KiB

  1. //============ Copyright (c) Valve Corporation, All rights reserved. ============
  2. //
  3. // Functionality to handle collation of shader debugging metadata
  4. // (i.e. shader PDBs) sent from shadercompile workers to the master.
  5. //
  6. //===============================================================================
  7. #include "shadercompile_ps3_helpers.h"
  8. #include <windows.h>
  9. #include "utlsymbollarge.h"
  10. // TOC file is a list of all files sent from worker to master and their locations
  11. // in the giant 1 GB pack files
  12. char g_PS3DebugTOCFilename[ MAX_PATH ];
  13. HANDLE g_hPS3DebugTOCFile = INVALID_HANDLE_VALUE;
  14. // Keep this in the range of a 32-bit integer, preferably a signed one for simplicity
  15. const size_t MAX_PS3_DEBUG_INFO_PACK_FILE_SIZE = 1024 * 1024 * 1024;
  16. // Max # of pack files to generate. If we're exceeding this number, something is probably wrong.
  17. const int MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT = 64;
  18. // Names of each of the pack files: ps3shaderdebug_packN for N = 0 to MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT-1
  19. char g_PS3DebugInfoPackFilenames[ MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT ][ MAX_PATH ];
  20. // Handle to the currently open pack file
  21. HANDLE g_hPS3DebugInfoCurrentPackFile = INVALID_HANDLE_VALUE;
  22. // Current size of the pack files
  23. size_t g_nPS3DebugInfoPackFileSizes[ MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT ];
  24. // Index into the previous arrays for the current pack file we are operating on (valid during build-up phase)
  25. int g_nCurrentPS3DebugInfoFile;
  26. // Defined in shadercompile.cpp
  27. extern const char *g_pShaderPath;
  28. // The set of all filenames we have encountered so far
  29. CUtlSymbolTableLarge_CI g_PS3DebugInfoFileSet;
  30. int g_nDuplicatePS3DebugFileCount = 0;
  31. int g_nTotalPS3DebugFileCount = 0;
  32. //-----------------------------------------------------------------------------
  33. // An entry in the Table-Of-Contents file which is built during compilation.
  34. //
  35. // Each entry corresponds to one tiny PS3 debug metadata file generated
  36. // on a worker machine during Cg shader compile and sent back to the host.
  37. //-----------------------------------------------------------------------------
  38. struct PS3DebugFileTOCEntry_t
  39. {
  40. // Length of destination filename, including NULL terminator (data follows immediately after this structure)
  41. int32 m_nFilenameLength;
  42. // Which of the debug info files this entry was stored in
  43. int32 m_nFileIndex;
  44. // Offset in ps3shaderdebug_pack%d.bin (where %d is m_nFileIndex)
  45. int32 m_nFileOffset;
  46. // Length of the debug file (usually on the order of kilobytes, so 32-bits is fine)
  47. int32 m_nFileSize;
  48. // Filename immediately follows:
  49. // char m_Filename[m_nFilenameLength]
  50. };
  51. // Appends a single tiny (few KB) file received from the worker to the end fo the current giant pack file.
  52. // These will be expanded later after shader compilation is done.
  53. static void WritePS3DebugInfo( const char *pFullPath, byte *pFileData, DWORD nFileSize )
  54. {
  55. if ( g_nPS3DebugInfoPackFileSizes[ g_nCurrentPS3DebugInfoFile ] + nFileSize > MAX_PS3_DEBUG_INFO_PACK_FILE_SIZE )
  56. {
  57. // These files should be on the order of a few kilobytes, but let's just make sure nothing insane happens here
  58. Assert( nFileSize < MAX_PS3_DEBUG_INFO_PACK_FILE_SIZE );
  59. CloseHandle( g_hPS3DebugInfoCurrentPackFile );
  60. g_hPS3DebugInfoCurrentPackFile = INVALID_HANDLE_VALUE;
  61. ++ g_nCurrentPS3DebugInfoFile;
  62. }
  63. if ( g_hPS3DebugInfoCurrentPackFile == INVALID_HANDLE_VALUE )
  64. {
  65. char filename[ MAX_PATH ];
  66. Q_snprintf( filename, MAX_PATH, "ps3shaderdebug_pack%02d.bin", g_nCurrentPS3DebugInfoFile );
  67. Q_ComposeFileName( g_pShaderPath, filename, g_PS3DebugInfoPackFilenames[ g_nCurrentPS3DebugInfoFile ], MAX_PATH );
  68. g_hPS3DebugInfoCurrentPackFile = CreateFile( g_PS3DebugInfoPackFilenames[ g_nCurrentPS3DebugInfoFile ], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  69. }
  70. if ( g_hPS3DebugInfoCurrentPackFile == INVALID_HANDLE_VALUE )
  71. {
  72. Error( "Could not write to PS3 debug info file. Make sure you have enough disk space, these things can be huuuge." );
  73. return;
  74. }
  75. DWORD nBytesWritten = 0;
  76. WriteFile( g_hPS3DebugInfoCurrentPackFile, pFileData, nFileSize, &nBytesWritten, NULL );
  77. if ( nBytesWritten != nFileSize )
  78. {
  79. Error( "Error writing to PS3 debug info file. Make sure you have enough disk space, these things can be huuuge." );
  80. return;
  81. }
  82. PS3DebugFileTOCEntry_t tocEntry;
  83. tocEntry.m_nFilenameLength = Q_strlen( pFullPath ) + 1;
  84. tocEntry.m_nFileIndex = g_nCurrentPS3DebugInfoFile;
  85. tocEntry.m_nFileOffset = g_nPS3DebugInfoPackFileSizes[ g_nCurrentPS3DebugInfoFile ];
  86. tocEntry.m_nFileSize = nFileSize;
  87. g_nPS3DebugInfoPackFileSizes[ g_nCurrentPS3DebugInfoFile ] += nBytesWritten;
  88. WriteFile( g_hPS3DebugTOCFile, &tocEntry, sizeof( tocEntry ), &nBytesWritten, NULL );
  89. if ( nBytesWritten != sizeof ( tocEntry ) )
  90. {
  91. Error( "Error writing to PS3 debug TOC file. Make sure you have enough disk space, these things can be huuuge." );
  92. return;
  93. }
  94. WriteFile( g_hPS3DebugTOCFile, pFullPath, tocEntry.m_nFilenameLength, &nBytesWritten, NULL );
  95. if ( nBytesWritten != ( DWORD )tocEntry.m_nFilenameLength )
  96. {
  97. Error( "Error writing to PS3 debug TOC file. Make sure you have enough disk space, these things can be huuuge." );
  98. return;
  99. }
  100. }
  101. bool PS3ShaderDebugInfoDispatch( MessageBuffer *pBuf, int nSource, int nPacketID )
  102. {
  103. // Received packet from worker containing list of files generated by PS3 CG compiler
  104. PS3ShaderDebugInfoPacket_t *pPacket = ( PS3ShaderDebugInfoPacket_t * )pBuf->data;
  105. const char *pFilename = ( char * )pBuf->data + sizeof( PS3ShaderDebugInfoPacket_t );
  106. ++g_nTotalPS3DebugFileCount;
  107. if ( g_PS3DebugInfoFileSet.Find( pFilename ) == UTL_INVAL_SYMBOL_LARGE )
  108. {
  109. g_PS3DebugInfoFileSet.AddString( pFilename );
  110. // Re-create the file locally (beneath g_pShaderPath \cgc-capture), exactly as it was on the worker machine
  111. // by writing it into a giant bin file which will later be decompressed
  112. if ( Q_strnicmp( pFilename, "cgc-capture", 11 ) == 0 )
  113. {
  114. char fullPath[MAX_PATH];
  115. Q_ComposeFileName( g_pShaderPath, pFilename, fullPath, MAX_PATH );
  116. WritePS3DebugInfo( fullPath, ( byte * )pBuf->data + sizeof( PS3ShaderDebugInfoPacket_t ) + pPacket->m_nFileNameLength, pPacket->m_nFileDataLength );
  117. }
  118. }
  119. else
  120. {
  121. ++ g_nDuplicatePS3DebugFileCount;
  122. }
  123. return true;
  124. }
  125. void InitializePS3ShaderDebugPackFiles()
  126. {
  127. // Create files for debug information being returned from workers
  128. Q_ComposeFileName( g_pShaderPath, "ps3shaderdebug_toc.bin", g_PS3DebugTOCFilename, MAX_PATH );
  129. g_hPS3DebugTOCFile = CreateFile( g_PS3DebugTOCFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  130. }
  131. static void DisplayFileUnpackProgress( unsigned int nTotalSize, unsigned int nLast, unsigned int nCurrent )
  132. {
  133. int nLastProgress = ( int )( 100.0f * ( double )nLast / ( double )nTotalSize );
  134. int nCurrentProgress = ( int )( 100.0f * ( double )nCurrent / ( double )nTotalSize );
  135. for ( int i = nLastProgress + 1; i <= nCurrentProgress; ++ i )
  136. {
  137. if ( i % 10 == 0 )
  138. {
  139. Msg( "%d", ( i / 10 ) );
  140. }
  141. else if ( i % 2 == 0 )
  142. {
  143. Msg( "." );
  144. }
  145. }
  146. }
  147. // Expand the giant ps3shaderdebug_toc.bin and ps3shaderdebug_packN.bin files into a directory tree of little files.
  148. // Writing out these files takes too long to do in-line with the shader compile (the master gets bogged down with file IO requests otherwise)
  149. void ExpandPS3DebugInfo()
  150. {
  151. if ( g_hPS3DebugTOCFile != INVALID_HANDLE_VALUE )
  152. {
  153. Msg( "Unpacking giant shader debug info files into sub-directory tree.\n" );
  154. Msg( "0" );
  155. // Close the last files we were working on
  156. CloseHandle( g_hPS3DebugTOCFile );
  157. if ( g_hPS3DebugInfoCurrentPackFile != INVALID_HANDLE_VALUE )
  158. {
  159. CloseHandle( g_hPS3DebugInfoCurrentPackFile );
  160. }
  161. g_hPS3DebugTOCFile = CreateFile( g_PS3DebugTOCFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  162. DWORD nTocFileSizeHigh;
  163. DWORD nTocFileSize = GetFileSize( g_hPS3DebugTOCFile, &nTocFileSizeHigh );
  164. if ( nTocFileSizeHigh != 0 )
  165. {
  166. Error( "PS3 debug info TOC File is greater than 4 GB. This is probably not a good thing." );
  167. return;
  168. }
  169. // A set of directories we have already created, so we know not to re-create them
  170. CUtlSymbolTableLarge_CI createdPS3DebugInfoDirectories;
  171. // Scratch space big enough to store any single sub-file, usually on the order of kilobytes
  172. CUtlVector< byte > scratchSpace;
  173. g_hPS3DebugInfoCurrentPackFile = INVALID_HANDLE_VALUE;
  174. DWORD nCurrentTocEntryOffset = 0;
  175. DWORD nCurrentDebugInfoOffset = 0;
  176. int nCurrentDebugInfoFile = -1;
  177. while ( nCurrentTocEntryOffset < nTocFileSize )
  178. {
  179. // There must be at least enough room for a TOC entry plus some string data afterwards
  180. Assert( nCurrentTocEntryOffset + sizeof( PS3DebugFileTOCEntry_t ) < nTocFileSize );
  181. DWORD nBytesRead = 0;
  182. PS3DebugFileTOCEntry_t tocEntry;
  183. ReadFile( g_hPS3DebugTOCFile, &tocEntry, sizeof( PS3DebugFileTOCEntry_t ), &nBytesRead, NULL );
  184. Assert( nBytesRead == sizeof( PS3DebugFileTOCEntry_t ) );
  185. char fileNameBuffer[ MAX_PATH ];
  186. Assert( tocEntry.m_nFilenameLength < MAX_PATH );
  187. ReadFile( g_hPS3DebugTOCFile, fileNameBuffer, MIN( tocEntry.m_nFilenameLength, MAX_PATH ), &nBytesRead, NULL );
  188. Assert( nBytesRead == (DWORD)tocEntry.m_nFilenameLength );
  189. // Create any necessary directories recursively
  190. char dirToCreate[MAX_PATH];
  191. Q_ExtractFilePath( fileNameBuffer, dirToCreate, MAX_PATH );
  192. Q_StripTrailingSlash( dirToCreate );
  193. if ( ( UtlSymLargeId_t )createdPS3DebugInfoDirectories.Find( dirToCreate ) == UTL_INVAL_SYMBOL_LARGE )
  194. {
  195. const char *pNextDir = strchr( fileNameBuffer, '\\' );
  196. while ( pNextDir != NULL )
  197. {
  198. size_t nCharsToCopy = pNextDir - fileNameBuffer;
  199. memcpy( dirToCreate, fileNameBuffer, nCharsToCopy );
  200. dirToCreate[nCharsToCopy] = '\0';
  201. if ( ( UtlSymLargeId_t )createdPS3DebugInfoDirectories.Find( dirToCreate ) == UTL_INVAL_SYMBOL_LARGE )
  202. {
  203. CreateDirectory( dirToCreate, NULL );
  204. createdPS3DebugInfoDirectories.AddString( dirToCreate );
  205. }
  206. pNextDir = strchr( pNextDir + 1, '\\' );
  207. }
  208. }
  209. // Read the data out of the corresponding debug info file
  210. if ( nCurrentDebugInfoFile != tocEntry.m_nFileIndex )
  211. {
  212. if ( g_hPS3DebugInfoCurrentPackFile != INVALID_HANDLE_VALUE )
  213. {
  214. Assert( nCurrentDebugInfoOffset == g_nPS3DebugInfoPackFileSizes[ nCurrentDebugInfoFile ] );
  215. CloseHandle( g_hPS3DebugInfoCurrentPackFile );
  216. DeleteFile( g_PS3DebugInfoPackFilenames[ nCurrentDebugInfoFile ] );
  217. }
  218. nCurrentDebugInfoOffset = 0;
  219. nCurrentDebugInfoFile = tocEntry.m_nFileIndex;
  220. g_hPS3DebugInfoCurrentPackFile = CreateFile( g_PS3DebugInfoPackFilenames[ nCurrentDebugInfoFile ], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  221. }
  222. Assert( nCurrentDebugInfoOffset == (DWORD)tocEntry.m_nFileOffset );
  223. scratchSpace.EnsureCount( tocEntry.m_nFileSize );
  224. ReadFile( g_hPS3DebugInfoCurrentPackFile, scratchSpace.Base(), tocEntry.m_nFileSize, &nBytesRead, NULL );
  225. Assert( nBytesRead == (DWORD)tocEntry.m_nFileSize );
  226. nCurrentDebugInfoOffset += nBytesRead;
  227. DWORD nBytesWritten = 0;
  228. HANDLE hNewFile = CreateFile( fileNameBuffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL );
  229. if ( hNewFile == INVALID_HANDLE_VALUE )
  230. {
  231. Error( "Unable to create PS3 shader debug info file: %s. Ensure you have enoug disk space.\n", fileNameBuffer );
  232. }
  233. WriteFile( hNewFile, scratchSpace.Base(), tocEntry.m_nFileSize, &nBytesWritten, NULL );
  234. Assert( nBytesWritten == (DWORD)tocEntry.m_nFileSize );
  235. CloseHandle( hNewFile );
  236. DWORD nEntrySize = sizeof( PS3DebugFileTOCEntry_t ) + tocEntry.m_nFilenameLength;
  237. DisplayFileUnpackProgress( nTocFileSize, nCurrentTocEntryOffset, nCurrentTocEntryOffset + nEntrySize );
  238. nCurrentTocEntryOffset += nEntrySize;
  239. }
  240. if ( g_hPS3DebugInfoCurrentPackFile != INVALID_HANDLE_VALUE )
  241. {
  242. CloseHandle( g_hPS3DebugInfoCurrentPackFile );
  243. DeleteFile( g_PS3DebugInfoPackFilenames[ nCurrentDebugInfoFile ] );
  244. }
  245. CloseHandle( g_hPS3DebugTOCFile );
  246. DeleteFile( g_PS3DebugTOCFilename );
  247. }
  248. Msg( "\nTotal shader debug files returned: %d, duplicates: %d\n", g_nTotalPS3DebugFileCount, g_nDuplicatePS3DebugFileCount );
  249. }
  250. static void SendFileContentsToMaster( const char *pFilename )
  251. {
  252. HANDLE fileHandle = CreateFile( pFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL );
  253. if ( fileHandle != INVALID_HANDLE_VALUE )
  254. {
  255. DWORD fileSize = GetFileSize( fileHandle, NULL );
  256. byte *pFileData = new byte[fileSize];
  257. DWORD bytesRead;
  258. ReadFile( fileHandle, pFileData, fileSize, &bytesRead, NULL );
  259. if ( bytesRead == fileSize )
  260. {
  261. PS3ShaderDebugInfoPacket_t filePacket;
  262. filePacket.m_PacketID = PS3_SHADER_DEBUG_INFO_PACKETID;
  263. filePacket.m_nFileNameLength = Q_strlen( pFilename ) + 1;
  264. filePacket.m_nFileDataLength = bytesRead;
  265. CUtlBuffer myBuffer;
  266. myBuffer.Put( &filePacket, sizeof( filePacket ) );
  267. myBuffer.Put( pFilename, filePacket.m_nFileNameLength );
  268. myBuffer.Put( pFileData, filePacket.m_nFileDataLength );
  269. VMPI_SendData( myBuffer.Base(), sizeof( filePacket ) + filePacket.m_nFileNameLength + filePacket.m_nFileDataLength, VMPI_MASTER_ID );
  270. }
  271. delete[] pFileData;
  272. CloseHandle( fileHandle );
  273. unlink( pFilename );
  274. }
  275. fopen( pFilename, "r" );
  276. }
  277. template< typename TFunctor >
  278. static void ForEachFileRecursive( const char *pStartingPath, TFunctor callbackFunction )
  279. {
  280. WIN32_FIND_DATA findFileData;
  281. char searchPath[MAX_PATH];
  282. Q_strncpy( searchPath, pStartingPath, MAX_PATH );
  283. Q_ComposeFileName( pStartingPath, "*.*", searchPath, MAX_PATH );
  284. HANDLE findHandle = FindFirstFile( searchPath, &findFileData );
  285. bool bKeepSearching = ( findHandle != INVALID_HANDLE_VALUE );
  286. while ( bKeepSearching )
  287. {
  288. if ( Q_stricmp( findFileData.cFileName, "." ) != 0 && Q_stricmp( findFileData.cFileName, ".." ) != 0 )
  289. {
  290. char fullFilePath[MAX_PATH];
  291. Q_ComposeFileName( pStartingPath, findFileData.cFileName, fullFilePath, MAX_PATH );
  292. printf( "Found file: %s\n\n", fullFilePath );
  293. if ( ( findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 )
  294. {
  295. ForEachFileRecursive( fullFilePath, callbackFunction );
  296. }
  297. else
  298. {
  299. callbackFunction( fullFilePath );
  300. }
  301. }
  302. bKeepSearching = !!FindNextFile( findHandle, &findFileData );
  303. }
  304. FindClose( findHandle );
  305. }
  306. void SendSubDirectoryToMaster( const char *pStartingPath )
  307. {
  308. ForEachFileRecursive( pStartingPath, SendFileContentsToMaster );
  309. }
  310. static bool SendShaderCompileLogContentsToMaster( const char *pFilename )
  311. {
  312. FILE *pFile = fopen( pFilename, "r" );
  313. if ( !pFile )
  314. return false;
  315. const uint nPacketBufSize = 8192;
  316. char packetBuf[nPacketBufSize];
  317. PS3ShaderCompileLogPacket_t &filePacket = *reinterpret_cast< PS3ShaderCompileLogPacket_t * >( &packetBuf );
  318. filePacket.m_PacketID = PS3_SHADER_COMPILE_LOG_PACKETID;
  319. filePacket.m_nPacketSize = 0;
  320. uint nCurBufSize = sizeof( filePacket );
  321. while ( !feof( pFile ) )
  322. {
  323. const uint nMaxLineSize = 512;
  324. char szLine[nMaxLineSize];
  325. if ( !fgets( szLine, nMaxLineSize, pFile ) )
  326. break;
  327. int nCurLineSize = V_strlen( szLine );
  328. V_memcpy( packetBuf + nCurBufSize, szLine, nCurLineSize );
  329. nCurBufSize += nCurLineSize;
  330. Assert( nCurBufSize <= nPacketBufSize );
  331. if ( ( nPacketBufSize - nCurBufSize ) < nMaxLineSize )
  332. {
  333. filePacket.m_nPacketSize = nCurBufSize;
  334. VMPI_SendData( &filePacket, nCurBufSize, VMPI_MASTER_ID );
  335. nCurBufSize = sizeof( filePacket );
  336. }
  337. }
  338. if ( nCurBufSize > sizeof( filePacket ) )
  339. {
  340. filePacket.m_nPacketSize = nCurBufSize;
  341. VMPI_SendData( &filePacket, nCurBufSize, VMPI_MASTER_ID );
  342. }
  343. fclose( pFile );
  344. return true;
  345. }
  346. void PS3SendShaderCompileLogContentsToMaster()
  347. {
  348. char szLogFilename[MAX_PATH];
  349. if ( GetEnvironmentVariableA( "PS3COMPILELOG", szLogFilename, sizeof( szLogFilename ) ) )
  350. {
  351. HANDLE hMutex = CreateMutex( NULL, FALSE, "PS3COMPILELOGMUTEX" );
  352. if ( ( hMutex ) && ( WaitForSingleObject( hMutex, 10000 ) == WAIT_OBJECT_0 ) )
  353. {
  354. SendShaderCompileLogContentsToMaster( szLogFilename );
  355. _unlink( szLogFilename );
  356. ReleaseMutex( hMutex );
  357. }
  358. }
  359. }
  360. bool PS3ShaderCompileLogDispatch( MessageBuffer *pBuf, int nSource, int nPacketID )
  361. {
  362. if ( pBuf->getLen() < sizeof( PS3ShaderCompileLogPacket_t ) )
  363. return false;
  364. PS3ShaderCompileLogPacket_t *pPacket = ( PS3ShaderCompileLogPacket_t * )pBuf->data;
  365. const uint8 *pData = ( const uint8 * )pBuf->data + sizeof( PS3ShaderCompileLogPacket_t );
  366. int nDataSize = pPacket->m_nPacketSize - sizeof( PS3ShaderCompileLogPacket_t );
  367. if ( ( pPacket->m_nPacketSize >= sizeof( PS3ShaderCompileLogPacket_t ) ) && ( pBuf->getLen() >= pPacket->m_nPacketSize ) )
  368. {
  369. FILE *pFile = fopen( "ps3compilelog.txt", "ab" );
  370. if ( pFile )
  371. {
  372. fwrite( pData, nDataSize, 1, pFile );
  373. fclose( pFile );
  374. }
  375. }
  376. return true;
  377. }