|
|
//============ Copyright (c) Valve Corporation, All rights reserved. ============
//
// Functionality to handle collation of shader debugging metadata
// (i.e. shader PDBs) sent from shadercompile workers to the master.
//
//===============================================================================
#include "shadercompile_ps3_helpers.h"
#include <windows.h>
#include "utlsymbollarge.h"
// TOC file is a list of all files sent from worker to master and their locations
// in the giant 1 GB pack files
char g_PS3DebugTOCFilename[ MAX_PATH ]; HANDLE g_hPS3DebugTOCFile = INVALID_HANDLE_VALUE;
// Keep this in the range of a 32-bit integer, preferably a signed one for simplicity
const size_t MAX_PS3_DEBUG_INFO_PACK_FILE_SIZE = 1024 * 1024 * 1024;
// Max # of pack files to generate. If we're exceeding this number, something is probably wrong.
const int MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT = 64;
// Names of each of the pack files: ps3shaderdebug_packN for N = 0 to MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT-1
char g_PS3DebugInfoPackFilenames[ MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT ][ MAX_PATH ];
// Handle to the currently open pack file
HANDLE g_hPS3DebugInfoCurrentPackFile = INVALID_HANDLE_VALUE; // Current size of the pack files
size_t g_nPS3DebugInfoPackFileSizes[ MAX_PS3_DEBUG_INFO_PACK_FILE_COUNT ]; // Index into the previous arrays for the current pack file we are operating on (valid during build-up phase)
int g_nCurrentPS3DebugInfoFile;
// Defined in shadercompile.cpp
extern const char *g_pShaderPath;
// The set of all filenames we have encountered so far
CUtlSymbolTableLarge_CI g_PS3DebugInfoFileSet;
int g_nDuplicatePS3DebugFileCount = 0; int g_nTotalPS3DebugFileCount = 0;
//-----------------------------------------------------------------------------
// An entry in the Table-Of-Contents file which is built during compilation.
//
// Each entry corresponds to one tiny PS3 debug metadata file generated
// on a worker machine during Cg shader compile and sent back to the host.
//-----------------------------------------------------------------------------
struct PS3DebugFileTOCEntry_t { // Length of destination filename, including NULL terminator (data follows immediately after this structure)
int32 m_nFilenameLength; // Which of the debug info files this entry was stored in
int32 m_nFileIndex; // Offset in ps3shaderdebug_pack%d.bin (where %d is m_nFileIndex)
int32 m_nFileOffset; // Length of the debug file (usually on the order of kilobytes, so 32-bits is fine)
int32 m_nFileSize;
// Filename immediately follows:
// char m_Filename[m_nFilenameLength]
};
// Appends a single tiny (few KB) file received from the worker to the end fo the current giant pack file.
// These will be expanded later after shader compilation is done.
static void WritePS3DebugInfo( const char *pFullPath, byte *pFileData, DWORD nFileSize ) { if ( g_nPS3DebugInfoPackFileSizes[ g_nCurrentPS3DebugInfoFile ] + nFileSize > MAX_PS3_DEBUG_INFO_PACK_FILE_SIZE ) { // These files should be on the order of a few kilobytes, but let's just make sure nothing insane happens here
Assert( nFileSize < MAX_PS3_DEBUG_INFO_PACK_FILE_SIZE ); CloseHandle( g_hPS3DebugInfoCurrentPackFile ); g_hPS3DebugInfoCurrentPackFile = INVALID_HANDLE_VALUE; ++ g_nCurrentPS3DebugInfoFile; } if ( g_hPS3DebugInfoCurrentPackFile == INVALID_HANDLE_VALUE ) { char filename[ MAX_PATH ]; Q_snprintf( filename, MAX_PATH, "ps3shaderdebug_pack%02d.bin", g_nCurrentPS3DebugInfoFile ); Q_ComposeFileName( g_pShaderPath, filename, g_PS3DebugInfoPackFilenames[ g_nCurrentPS3DebugInfoFile ], MAX_PATH );
g_hPS3DebugInfoCurrentPackFile = CreateFile( g_PS3DebugInfoPackFilenames[ g_nCurrentPS3DebugInfoFile ], GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); }
if ( g_hPS3DebugInfoCurrentPackFile == INVALID_HANDLE_VALUE ) { Error( "Could not write to PS3 debug info file. Make sure you have enough disk space, these things can be huuuge." ); return; }
DWORD nBytesWritten = 0; WriteFile( g_hPS3DebugInfoCurrentPackFile, pFileData, nFileSize, &nBytesWritten, NULL ); if ( nBytesWritten != nFileSize ) { Error( "Error writing to PS3 debug info file. Make sure you have enough disk space, these things can be huuuge." ); return; }
PS3DebugFileTOCEntry_t tocEntry; tocEntry.m_nFilenameLength = Q_strlen( pFullPath ) + 1; tocEntry.m_nFileIndex = g_nCurrentPS3DebugInfoFile; tocEntry.m_nFileOffset = g_nPS3DebugInfoPackFileSizes[ g_nCurrentPS3DebugInfoFile ]; tocEntry.m_nFileSize = nFileSize;
g_nPS3DebugInfoPackFileSizes[ g_nCurrentPS3DebugInfoFile ] += nBytesWritten;
WriteFile( g_hPS3DebugTOCFile, &tocEntry, sizeof( tocEntry ), &nBytesWritten, NULL ); if ( nBytesWritten != sizeof ( tocEntry ) ) { Error( "Error writing to PS3 debug TOC file. Make sure you have enough disk space, these things can be huuuge." ); return; }
WriteFile( g_hPS3DebugTOCFile, pFullPath, tocEntry.m_nFilenameLength, &nBytesWritten, NULL ); if ( nBytesWritten != ( DWORD )tocEntry.m_nFilenameLength ) { Error( "Error writing to PS3 debug TOC file. Make sure you have enough disk space, these things can be huuuge." ); return; } }
bool PS3ShaderDebugInfoDispatch( MessageBuffer *pBuf, int nSource, int nPacketID ) { // Received packet from worker containing list of files generated by PS3 CG compiler
PS3ShaderDebugInfoPacket_t *pPacket = ( PS3ShaderDebugInfoPacket_t * )pBuf->data; const char *pFilename = ( char * )pBuf->data + sizeof( PS3ShaderDebugInfoPacket_t );
++g_nTotalPS3DebugFileCount;
if ( g_PS3DebugInfoFileSet.Find( pFilename ) == UTL_INVAL_SYMBOL_LARGE ) { g_PS3DebugInfoFileSet.AddString( pFilename );
// Re-create the file locally (beneath g_pShaderPath \cgc-capture), exactly as it was on the worker machine
// by writing it into a giant bin file which will later be decompressed
if ( Q_strnicmp( pFilename, "cgc-capture", 11 ) == 0 ) { char fullPath[MAX_PATH]; Q_ComposeFileName( g_pShaderPath, pFilename, fullPath, MAX_PATH ); WritePS3DebugInfo( fullPath, ( byte * )pBuf->data + sizeof( PS3ShaderDebugInfoPacket_t ) + pPacket->m_nFileNameLength, pPacket->m_nFileDataLength ); } } else { ++ g_nDuplicatePS3DebugFileCount; }
return true; }
void InitializePS3ShaderDebugPackFiles() { // Create files for debug information being returned from workers
Q_ComposeFileName( g_pShaderPath, "ps3shaderdebug_toc.bin", g_PS3DebugTOCFilename, MAX_PATH ); g_hPS3DebugTOCFile = CreateFile( g_PS3DebugTOCFilename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); }
static void DisplayFileUnpackProgress( unsigned int nTotalSize, unsigned int nLast, unsigned int nCurrent ) { int nLastProgress = ( int )( 100.0f * ( double )nLast / ( double )nTotalSize ); int nCurrentProgress = ( int )( 100.0f * ( double )nCurrent / ( double )nTotalSize ); for ( int i = nLastProgress + 1; i <= nCurrentProgress; ++ i ) { if ( i % 10 == 0 ) { Msg( "%d", ( i / 10 ) ); } else if ( i % 2 == 0 ) { Msg( "." ); } } }
// Expand the giant ps3shaderdebug_toc.bin and ps3shaderdebug_packN.bin files into a directory tree of little files.
// 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)
void ExpandPS3DebugInfo() { if ( g_hPS3DebugTOCFile != INVALID_HANDLE_VALUE ) { Msg( "Unpacking giant shader debug info files into sub-directory tree.\n" ); Msg( "0" );
// Close the last files we were working on
CloseHandle( g_hPS3DebugTOCFile ); if ( g_hPS3DebugInfoCurrentPackFile != INVALID_HANDLE_VALUE ) { CloseHandle( g_hPS3DebugInfoCurrentPackFile ); }
g_hPS3DebugTOCFile = CreateFile( g_PS3DebugTOCFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); DWORD nTocFileSizeHigh; DWORD nTocFileSize = GetFileSize( g_hPS3DebugTOCFile, &nTocFileSizeHigh ); if ( nTocFileSizeHigh != 0 ) { Error( "PS3 debug info TOC File is greater than 4 GB. This is probably not a good thing." ); return; }
// A set of directories we have already created, so we know not to re-create them
CUtlSymbolTableLarge_CI createdPS3DebugInfoDirectories;
// Scratch space big enough to store any single sub-file, usually on the order of kilobytes
CUtlVector< byte > scratchSpace;
g_hPS3DebugInfoCurrentPackFile = INVALID_HANDLE_VALUE;
DWORD nCurrentTocEntryOffset = 0; DWORD nCurrentDebugInfoOffset = 0; int nCurrentDebugInfoFile = -1; while ( nCurrentTocEntryOffset < nTocFileSize ) { // There must be at least enough room for a TOC entry plus some string data afterwards
Assert( nCurrentTocEntryOffset + sizeof( PS3DebugFileTOCEntry_t ) < nTocFileSize );
DWORD nBytesRead = 0; PS3DebugFileTOCEntry_t tocEntry; ReadFile( g_hPS3DebugTOCFile, &tocEntry, sizeof( PS3DebugFileTOCEntry_t ), &nBytesRead, NULL ); Assert( nBytesRead == sizeof( PS3DebugFileTOCEntry_t ) );
char fileNameBuffer[ MAX_PATH ]; Assert( tocEntry.m_nFilenameLength < MAX_PATH ); ReadFile( g_hPS3DebugTOCFile, fileNameBuffer, MIN( tocEntry.m_nFilenameLength, MAX_PATH ), &nBytesRead, NULL ); Assert( nBytesRead == (DWORD)tocEntry.m_nFilenameLength );
// Create any necessary directories recursively
char dirToCreate[MAX_PATH]; Q_ExtractFilePath( fileNameBuffer, dirToCreate, MAX_PATH ); Q_StripTrailingSlash( dirToCreate );
if ( ( UtlSymLargeId_t )createdPS3DebugInfoDirectories.Find( dirToCreate ) == UTL_INVAL_SYMBOL_LARGE ) { const char *pNextDir = strchr( fileNameBuffer, '\\' ); while ( pNextDir != NULL ) { size_t nCharsToCopy = pNextDir - fileNameBuffer; memcpy( dirToCreate, fileNameBuffer, nCharsToCopy ); dirToCreate[nCharsToCopy] = '\0'; if ( ( UtlSymLargeId_t )createdPS3DebugInfoDirectories.Find( dirToCreate ) == UTL_INVAL_SYMBOL_LARGE ) { CreateDirectory( dirToCreate, NULL ); createdPS3DebugInfoDirectories.AddString( dirToCreate ); } pNextDir = strchr( pNextDir + 1, '\\' ); } }
// Read the data out of the corresponding debug info file
if ( nCurrentDebugInfoFile != tocEntry.m_nFileIndex ) { if ( g_hPS3DebugInfoCurrentPackFile != INVALID_HANDLE_VALUE ) { Assert( nCurrentDebugInfoOffset == g_nPS3DebugInfoPackFileSizes[ nCurrentDebugInfoFile ] ); CloseHandle( g_hPS3DebugInfoCurrentPackFile ); DeleteFile( g_PS3DebugInfoPackFilenames[ nCurrentDebugInfoFile ] ); } nCurrentDebugInfoOffset = 0; nCurrentDebugInfoFile = tocEntry.m_nFileIndex; g_hPS3DebugInfoCurrentPackFile = CreateFile( g_PS3DebugInfoPackFilenames[ nCurrentDebugInfoFile ], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); }
Assert( nCurrentDebugInfoOffset == (DWORD)tocEntry.m_nFileOffset ); scratchSpace.EnsureCount( tocEntry.m_nFileSize ); ReadFile( g_hPS3DebugInfoCurrentPackFile, scratchSpace.Base(), tocEntry.m_nFileSize, &nBytesRead, NULL ); Assert( nBytesRead == (DWORD)tocEntry.m_nFileSize ); nCurrentDebugInfoOffset += nBytesRead;
DWORD nBytesWritten = 0; HANDLE hNewFile = CreateFile( fileNameBuffer, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL ); if ( hNewFile == INVALID_HANDLE_VALUE ) { Error( "Unable to create PS3 shader debug info file: %s. Ensure you have enoug disk space.\n", fileNameBuffer ); } WriteFile( hNewFile, scratchSpace.Base(), tocEntry.m_nFileSize, &nBytesWritten, NULL ); Assert( nBytesWritten == (DWORD)tocEntry.m_nFileSize ); CloseHandle( hNewFile );
DWORD nEntrySize = sizeof( PS3DebugFileTOCEntry_t ) + tocEntry.m_nFilenameLength; DisplayFileUnpackProgress( nTocFileSize, nCurrentTocEntryOffset, nCurrentTocEntryOffset + nEntrySize ); nCurrentTocEntryOffset += nEntrySize; }
if ( g_hPS3DebugInfoCurrentPackFile != INVALID_HANDLE_VALUE ) { CloseHandle( g_hPS3DebugInfoCurrentPackFile ); DeleteFile( g_PS3DebugInfoPackFilenames[ nCurrentDebugInfoFile ] ); }
CloseHandle( g_hPS3DebugTOCFile ); DeleteFile( g_PS3DebugTOCFilename ); } Msg( "\nTotal shader debug files returned: %d, duplicates: %d\n", g_nTotalPS3DebugFileCount, g_nDuplicatePS3DebugFileCount ); }
static void SendFileContentsToMaster( const char *pFilename ) { HANDLE fileHandle = CreateFile( pFilename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if ( fileHandle != INVALID_HANDLE_VALUE ) { DWORD fileSize = GetFileSize( fileHandle, NULL ); byte *pFileData = new byte[fileSize]; DWORD bytesRead; ReadFile( fileHandle, pFileData, fileSize, &bytesRead, NULL ); if ( bytesRead == fileSize ) { PS3ShaderDebugInfoPacket_t filePacket; filePacket.m_PacketID = PS3_SHADER_DEBUG_INFO_PACKETID; filePacket.m_nFileNameLength = Q_strlen( pFilename ) + 1; filePacket.m_nFileDataLength = bytesRead; CUtlBuffer myBuffer; myBuffer.Put( &filePacket, sizeof( filePacket ) ); myBuffer.Put( pFilename, filePacket.m_nFileNameLength ); myBuffer.Put( pFileData, filePacket.m_nFileDataLength ); VMPI_SendData( myBuffer.Base(), sizeof( filePacket ) + filePacket.m_nFileNameLength + filePacket.m_nFileDataLength, VMPI_MASTER_ID ); }
delete[] pFileData; CloseHandle( fileHandle ); unlink( pFilename ); }
fopen( pFilename, "r" ); }
template< typename TFunctor > static void ForEachFileRecursive( const char *pStartingPath, TFunctor callbackFunction ) { WIN32_FIND_DATA findFileData; char searchPath[MAX_PATH]; Q_strncpy( searchPath, pStartingPath, MAX_PATH ); Q_ComposeFileName( pStartingPath, "*.*", searchPath, MAX_PATH ); HANDLE findHandle = FindFirstFile( searchPath, &findFileData ); bool bKeepSearching = ( findHandle != INVALID_HANDLE_VALUE ); while ( bKeepSearching ) { if ( Q_stricmp( findFileData.cFileName, "." ) != 0 && Q_stricmp( findFileData.cFileName, ".." ) != 0 ) { char fullFilePath[MAX_PATH]; Q_ComposeFileName( pStartingPath, findFileData.cFileName, fullFilePath, MAX_PATH ); printf( "Found file: %s\n\n", fullFilePath ); if ( ( findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) != 0 ) { ForEachFileRecursive( fullFilePath, callbackFunction ); } else { callbackFunction( fullFilePath ); } }
bKeepSearching = !!FindNextFile( findHandle, &findFileData ); } FindClose( findHandle ); }
void SendSubDirectoryToMaster( const char *pStartingPath ) { ForEachFileRecursive( pStartingPath, SendFileContentsToMaster ); }
static bool SendShaderCompileLogContentsToMaster( const char *pFilename ) { FILE *pFile = fopen( pFilename, "r" ); if ( !pFile ) return false; const uint nPacketBufSize = 8192; char packetBuf[nPacketBufSize]; PS3ShaderCompileLogPacket_t &filePacket = *reinterpret_cast< PS3ShaderCompileLogPacket_t * >( &packetBuf ); filePacket.m_PacketID = PS3_SHADER_COMPILE_LOG_PACKETID; filePacket.m_nPacketSize = 0; uint nCurBufSize = sizeof( filePacket ); while ( !feof( pFile ) ) { const uint nMaxLineSize = 512;
char szLine[nMaxLineSize]; if ( !fgets( szLine, nMaxLineSize, pFile ) ) break;
int nCurLineSize = V_strlen( szLine );
V_memcpy( packetBuf + nCurBufSize, szLine, nCurLineSize ); nCurBufSize += nCurLineSize; Assert( nCurBufSize <= nPacketBufSize );
if ( ( nPacketBufSize - nCurBufSize ) < nMaxLineSize ) { filePacket.m_nPacketSize = nCurBufSize;
VMPI_SendData( &filePacket, nCurBufSize, VMPI_MASTER_ID ); nCurBufSize = sizeof( filePacket ); } }
if ( nCurBufSize > sizeof( filePacket ) ) { filePacket.m_nPacketSize = nCurBufSize; VMPI_SendData( &filePacket, nCurBufSize, VMPI_MASTER_ID ); }
fclose( pFile );
return true; }
void PS3SendShaderCompileLogContentsToMaster() { char szLogFilename[MAX_PATH]; if ( GetEnvironmentVariableA( "PS3COMPILELOG", szLogFilename, sizeof( szLogFilename ) ) ) { HANDLE hMutex = CreateMutex( NULL, FALSE, "PS3COMPILELOGMUTEX" ); if ( ( hMutex ) && ( WaitForSingleObject( hMutex, 10000 ) == WAIT_OBJECT_0 ) ) { SendShaderCompileLogContentsToMaster( szLogFilename );
_unlink( szLogFilename );
ReleaseMutex( hMutex ); } } }
bool PS3ShaderCompileLogDispatch( MessageBuffer *pBuf, int nSource, int nPacketID ) { if ( pBuf->getLen() < sizeof( PS3ShaderCompileLogPacket_t ) ) return false;
PS3ShaderCompileLogPacket_t *pPacket = ( PS3ShaderCompileLogPacket_t * )pBuf->data; const uint8 *pData = ( const uint8 * )pBuf->data + sizeof( PS3ShaderCompileLogPacket_t ); int nDataSize = pPacket->m_nPacketSize - sizeof( PS3ShaderCompileLogPacket_t );
if ( ( pPacket->m_nPacketSize >= sizeof( PS3ShaderCompileLogPacket_t ) ) && ( pBuf->getLen() >= pPacket->m_nPacketSize ) ) { FILE *pFile = fopen( "ps3compilelog.txt", "ab" ); if ( pFile ) { fwrite( pData, nDataSize, 1, pFile ); fclose( pFile ); } }
return true; }
|