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

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