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.
496 lines
13 KiB
496 lines
13 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
// |
|
// mpivrad.cpp |
|
// |
|
|
|
#include <windows.h> |
|
#include <conio.h> |
|
#include "vrad.h" |
|
#include "physdll.h" |
|
#include "lightmap.h" |
|
#include "tier1/strtools.h" |
|
#include "radial.h" |
|
#include "utlbuffer.h" |
|
#include "pacifier.h" |
|
#include "messbuf.h" |
|
#include "bsplib.h" |
|
#include "consolewnd.h" |
|
#include "vismat.h" |
|
#include "vmpi_filesystem.h" |
|
#include "vmpi_dispatch.h" |
|
#include "utllinkedlist.h" |
|
#include "vmpi.h" |
|
#include "mpi_stats.h" |
|
#include "vmpi_distribute_work.h" |
|
#include "vmpi_tools_shared.h" |
|
|
|
|
|
|
|
|
|
CUtlVector<char> g_LightResultsFilename; |
|
|
|
|
|
extern int total_transfer; |
|
extern int max_transfer; |
|
|
|
extern void BuildVisLeafs(int); |
|
extern void BuildPatchLights( int facenum ); |
|
|
|
|
|
// Handle VRAD packets. |
|
bool VRAD_DispatchFn( MessageBuffer *pBuf, int iSource, int iPacketID ) |
|
{ |
|
switch( pBuf->data[1] ) |
|
{ |
|
case VMPI_SUBPACKETID_PLIGHTDATA_RESULTS: |
|
{ |
|
const char *pFilename = &pBuf->data[2]; |
|
g_LightResultsFilename.CopyArray( pFilename, strlen( pFilename ) + 1 ); |
|
return true; |
|
} |
|
|
|
default: |
|
return false; |
|
} |
|
} |
|
CDispatchReg g_VRADDispatchReg( VMPI_VRAD_PACKET_ID, VRAD_DispatchFn ); // register to handle the messages we want |
|
CDispatchReg g_DistributeWorkReg( VMPI_DISTRIBUTEWORK_PACKETID, DistributeWorkDispatch ); |
|
|
|
|
|
|
|
void VRAD_SetupMPI( int &argc, char **&argv ) |
|
{ |
|
CmdLib_AtCleanup( VMPI_Stats_Term ); |
|
|
|
// |
|
// Preliminary check -mpi flag |
|
// |
|
if ( !VMPI_FindArg( argc, argv, "-mpi", "" ) && !VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Worker ), "" ) ) |
|
return; |
|
|
|
// Force local mode? |
|
VMPIRunMode mode; |
|
if ( VMPI_FindArg( argc, argv, VMPI_GetParamString( mpi_Local ), "" ) ) |
|
mode = VMPI_RUN_LOCAL; |
|
else |
|
mode = VMPI_RUN_NETWORKED; |
|
|
|
VMPI_Stats_InstallSpewHook(); |
|
|
|
// |
|
// Extract mpi specific arguments |
|
// |
|
Msg( "Initializing VMPI...\n" ); |
|
if ( !VMPI_Init( |
|
argc, |
|
argv, |
|
"dependency_info_vrad.txt", |
|
HandleMPIDisconnect, |
|
mode |
|
) ) |
|
{ |
|
Error( "MPI_Init failed." ); |
|
} |
|
|
|
StatsDB_InitStatsDatabase( argc, argv, "dbinfo_vrad.txt" ); |
|
} |
|
|
|
|
|
//----------------------------------------- |
|
// |
|
// Run BuildFaceLights across all available processing nodes |
|
// and collect the results. |
|
// |
|
|
|
CCycleCount g_CPUTime; |
|
|
|
|
|
template<class T> void WriteValues( MessageBuffer *pmb, T const *pSrc, int nNumValues) |
|
{ |
|
pmb->write(pSrc, sizeof( pSrc[0]) * nNumValues ); |
|
} |
|
|
|
template<class T> int ReadValues( MessageBuffer *pmb, T *pDest, int nNumValues) |
|
{ |
|
return pmb->read( pDest, sizeof( pDest[0]) * nNumValues ); |
|
} |
|
|
|
|
|
//-------------------------------------------------- |
|
// Serialize face data |
|
void SerializeFace( MessageBuffer * pmb, int facenum ) |
|
{ |
|
int i, n; |
|
|
|
dface_t * f = &g_pFaces[facenum]; |
|
facelight_t * fl = &facelight[facenum]; |
|
|
|
pmb->write(f, sizeof(dface_t)); |
|
pmb->write(fl, sizeof(facelight_t)); |
|
|
|
WriteValues( pmb, fl->sample, fl->numsamples); |
|
|
|
// |
|
// Write the light information |
|
// |
|
for (i=0; i<MAXLIGHTMAPS; ++i) { |
|
for (n=0; n<NUM_BUMP_VECTS+1; ++n) { |
|
if (fl->light[i][n]) |
|
{ |
|
WriteValues( pmb, fl->light[i][n], fl->numsamples); |
|
} |
|
} |
|
} |
|
|
|
if (fl->luxel) |
|
WriteValues( pmb, fl->luxel, fl->numluxels); |
|
|
|
if (fl->luxelNormals) |
|
WriteValues( pmb, fl->luxelNormals, fl->numluxels); |
|
} |
|
|
|
//-------------------------------------------------- |
|
// UnSerialize face data |
|
// |
|
void UnSerializeFace( MessageBuffer * pmb, int facenum, int iSource ) |
|
{ |
|
int i, n; |
|
|
|
dface_t * f = &g_pFaces[facenum]; |
|
facelight_t * fl = &facelight[facenum]; |
|
|
|
if (pmb->read(f, sizeof(dface_t)) < 0) |
|
Error("UnSerializeFace - invalid dface_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() ); |
|
|
|
if (pmb->read(fl, sizeof(facelight_t)) < 0) |
|
Error("UnSerializeFace - invalid facelight_t from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() ); |
|
|
|
fl->sample = (sample_t *) calloc(fl->numsamples, sizeof(sample_t)); |
|
if (pmb->read(fl->sample, sizeof(sample_t) * fl->numsamples) < 0) |
|
Error("UnSerializeFace - invalid sample_t from %s (mb len: %d, offset: %d, fl->numsamples: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset(), fl->numsamples ); |
|
|
|
// |
|
// Read the light information |
|
// |
|
for (i=0; i<MAXLIGHTMAPS; ++i) { |
|
for (n=0; n<NUM_BUMP_VECTS+1; ++n) { |
|
if (fl->light[i][n]) |
|
{ |
|
fl->light[i][n] = (LightingValue_t *) calloc( fl->numsamples, sizeof(LightingValue_t ) ); |
|
if ( ReadValues( pmb, fl->light[i][n], fl->numsamples) < 0) |
|
Error("UnSerializeFace - invalid fl->light from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() ); |
|
} |
|
} |
|
} |
|
|
|
if (fl->luxel) { |
|
fl->luxel = (Vector *) calloc(fl->numluxels, sizeof(Vector)); |
|
if (ReadValues( pmb, fl->luxel, fl->numluxels) < 0) |
|
Error("UnSerializeFace - invalid fl->luxel from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() ); |
|
} |
|
|
|
if (fl->luxelNormals) { |
|
fl->luxelNormals = (Vector *) calloc(fl->numluxels, sizeof( Vector )); |
|
if ( ReadValues( pmb, fl->luxelNormals, fl->numluxels) < 0) |
|
Error("UnSerializeFace - invalid fl->luxelNormals from %s (mb len: %d, offset: %d)", VMPI_GetMachineName( iSource ), pmb->getLen(), pmb->getOffset() ); |
|
} |
|
|
|
} |
|
|
|
|
|
void MPI_ReceiveFaceResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker ) |
|
{ |
|
UnSerializeFace( pBuf, iWorkUnit, iWorker ); |
|
} |
|
|
|
|
|
void MPI_ProcessFaces( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf ) |
|
{ |
|
// Do BuildFacelights on the face. |
|
CTimeAdder adder( &g_CPUTime ); |
|
|
|
BuildFacelights( iThread, iWorkUnit ); |
|
|
|
// Send the results. |
|
if ( pBuf ) |
|
{ |
|
SerializeFace( pBuf, iWorkUnit ); |
|
} |
|
} |
|
|
|
|
|
void RunMPIBuildFacelights() |
|
{ |
|
g_CPUTime.Init(); |
|
|
|
Msg( "%-20s ", "BuildFaceLights:" ); |
|
if ( g_bMPIMaster ) |
|
{ |
|
StartPacifier(""); |
|
} |
|
|
|
VMPI_SetCurrentStage( "RunMPIBuildFaceLights" ); |
|
double elapsed = DistributeWork( |
|
numfaces, |
|
VMPI_DISTRIBUTEWORK_PACKETID, |
|
MPI_ProcessFaces, |
|
MPI_ReceiveFaceResults ); |
|
|
|
if ( g_bMPIMaster ) |
|
{ |
|
EndPacifier(false); |
|
Msg( " (%d)\n", (int)elapsed ); |
|
} |
|
|
|
if ( g_bMPIMaster ) |
|
{ |
|
// |
|
// BuildPatchLights is normally called from BuildFacelights(), |
|
// but in MPI mode we have the master do the calculation |
|
// We might be able to speed this up by doing while the master |
|
// is idling in the above loop. Wouldn't want to slow down the |
|
// handing out of work - maybe another thread? |
|
// |
|
for ( int i=0; i < numfaces; ++i ) |
|
{ |
|
BuildPatchLights(i); |
|
} |
|
} |
|
else |
|
{ |
|
if ( g_iVMPIVerboseLevel >= 1 ) |
|
Msg( "\n\n%.1f%% CPU utilization during BuildFaceLights\n\n", ( g_CPUTime.GetSeconds() * 100 / elapsed ) ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------- |
|
// |
|
// Run BuildVisLeafs across all available processing nodes |
|
// and collect the results. |
|
// |
|
|
|
// This function is called when the master receives results back from a worker. |
|
void MPI_ReceiveVisLeafsResults( uint64 iWorkUnit, MessageBuffer *pBuf, int iWorker ) |
|
{ |
|
int patchesInCluster = 0; |
|
|
|
pBuf->read(&patchesInCluster, sizeof(patchesInCluster)); |
|
|
|
for ( int k=0; k < patchesInCluster; ++k ) |
|
{ |
|
int patchnum = 0; |
|
pBuf->read(&patchnum, sizeof(patchnum)); |
|
|
|
CPatch * patch = &g_Patches[patchnum]; |
|
int numtransfers; |
|
pBuf->read( &numtransfers, sizeof(numtransfers) ); |
|
patch->numtransfers = numtransfers; |
|
if (numtransfers) |
|
{ |
|
patch->transfers = new transfer_t[numtransfers]; |
|
pBuf->read(patch->transfers, numtransfers * sizeof(transfer_t)); |
|
} |
|
|
|
total_transfer += numtransfers; |
|
if (max_transfer < numtransfers) |
|
max_transfer = numtransfers; |
|
} |
|
} |
|
|
|
|
|
// Temporary variables used during callbacks. If we're going to be threadsafe, these |
|
// should go in a structure and get passed around. |
|
class CVMPIVisLeafsData |
|
{ |
|
public: |
|
MessageBuffer *m_pVisLeafsMB; |
|
int m_nPatchesInCluster; |
|
transfer_t *m_pBuildVisLeafsTransfers; |
|
}; |
|
|
|
CVMPIVisLeafsData g_VMPIVisLeafsData[MAX_TOOL_THREADS+1]; |
|
|
|
|
|
|
|
// This is called by BuildVisLeafs_Cluster every time it finishes a patch. |
|
// The results are appended to g_VisLeafsMB and sent back to the master when all clusters are done. |
|
void MPI_AddPatchData( int iThread, int patchnum, CPatch *patch ) |
|
{ |
|
CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread]; |
|
if ( pData->m_pVisLeafsMB ) |
|
{ |
|
// Add in results for this patch |
|
++pData->m_nPatchesInCluster; |
|
pData->m_pVisLeafsMB->write(&patchnum, sizeof(patchnum)); |
|
pData->m_pVisLeafsMB->write(&patch->numtransfers, sizeof(patch->numtransfers)); |
|
pData->m_pVisLeafsMB->write( patch->transfers, patch->numtransfers * sizeof(transfer_t) ); |
|
} |
|
} |
|
|
|
|
|
// This handles a work unit sent by the master. Each work unit here is a |
|
// list of clusters. |
|
void MPI_ProcessVisLeafs( int iThread, uint64 iWorkUnit, MessageBuffer *pBuf ) |
|
{ |
|
CTimeAdder adder( &g_CPUTime ); |
|
|
|
CVMPIVisLeafsData *pData = &g_VMPIVisLeafsData[iThread]; |
|
int iCluster = iWorkUnit; |
|
|
|
// Start this cluster. |
|
pData->m_nPatchesInCluster = 0; |
|
pData->m_pVisLeafsMB = pBuf; |
|
|
|
// Write a temp value in there. We overwrite it later. |
|
int iSavePos = 0; |
|
if ( pBuf ) |
|
{ |
|
iSavePos = pBuf->getLen(); |
|
pBuf->write( &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) ); |
|
} |
|
|
|
// Collect the results in MPI_AddPatchData. |
|
BuildVisLeafs_Cluster( iThread, pData->m_pBuildVisLeafsTransfers, iCluster, MPI_AddPatchData ); |
|
|
|
// Now send the results back.. |
|
if ( pBuf ) |
|
{ |
|
pBuf->update( iSavePos, &pData->m_nPatchesInCluster, sizeof(pData->m_nPatchesInCluster) ); |
|
pData->m_pVisLeafsMB = NULL; |
|
} |
|
} |
|
|
|
|
|
void RunMPIBuildVisLeafs() |
|
{ |
|
g_CPUTime.Init(); |
|
|
|
Msg( "%-20s ", "BuildVisLeafs :" ); |
|
if ( g_bMPIMaster ) |
|
{ |
|
StartPacifier(""); |
|
} |
|
|
|
memset( g_VMPIVisLeafsData, 0, sizeof( g_VMPIVisLeafsData ) ); |
|
if ( !g_bMPIMaster || VMPI_GetActiveWorkUnitDistributor() == k_eWorkUnitDistributor_SDK ) |
|
{ |
|
// Allocate space for the transfers for each thread. |
|
for ( int i=0; i < numthreads; i++ ) |
|
{ |
|
g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers = BuildVisLeafs_Start(); |
|
} |
|
} |
|
|
|
// |
|
// Slaves ask for work via GetMPIBuildVisLeafWork() |
|
// Results are returned in BuildVisRow() |
|
// |
|
VMPI_SetCurrentStage( "RunMPIBuildVisLeafs" ); |
|
|
|
double elapsed = DistributeWork( |
|
dvis->numclusters, |
|
VMPI_DISTRIBUTEWORK_PACKETID, |
|
MPI_ProcessVisLeafs, |
|
MPI_ReceiveVisLeafsResults ); |
|
|
|
// Free the transfers from each thread. |
|
for ( int i=0; i < numthreads; i++ ) |
|
{ |
|
if ( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers ) |
|
BuildVisLeafs_End( g_VMPIVisLeafsData[i].m_pBuildVisLeafsTransfers ); |
|
} |
|
|
|
if ( g_bMPIMaster ) |
|
{ |
|
EndPacifier(false); |
|
Msg( " (%d)\n", (int)elapsed ); |
|
} |
|
else |
|
{ |
|
if ( g_iVMPIVerboseLevel >= 1 ) |
|
Msg( "%.1f%% CPU utilization during PortalFlow\n", (g_CPUTime.GetSeconds() * 100.0f / elapsed) / numthreads ); |
|
} |
|
} |
|
|
|
void VMPI_DistributeLightData() |
|
{ |
|
if ( !g_bUseMPI ) |
|
return; |
|
|
|
if ( g_bMPIMaster ) |
|
{ |
|
const char *pVirtualFilename = "--plightdata--"; |
|
|
|
CUtlBuffer lightFaceData; |
|
|
|
// write out the light data |
|
lightFaceData.EnsureCapacity( pdlightdata->Count() + (numfaces * (MAXLIGHTMAPS+sizeof(int))) ); |
|
Q_memcpy( lightFaceData.PeekPut(), pdlightdata->Base(), pdlightdata->Count() ); |
|
lightFaceData.SeekPut( CUtlBuffer::SEEK_HEAD, pdlightdata->Count() ); |
|
|
|
// write out the relevant face info into the stream |
|
for ( int i = 0; i < numfaces; i++ ) |
|
{ |
|
for ( int j = 0; j < MAXLIGHTMAPS; j++ ) |
|
{ |
|
lightFaceData.PutChar(g_pFaces[i].styles[j]); |
|
} |
|
lightFaceData.PutInt(g_pFaces[i].lightofs); |
|
} |
|
VMPI_FileSystem_CreateVirtualFile( pVirtualFilename, lightFaceData.Base(), lightFaceData.TellMaxPut() ); |
|
|
|
char cPacketID[2] = { VMPI_VRAD_PACKET_ID, VMPI_SUBPACKETID_PLIGHTDATA_RESULTS }; |
|
VMPI_Send2Chunks( cPacketID, sizeof( cPacketID ), pVirtualFilename, strlen( pVirtualFilename ) + 1, VMPI_PERSISTENT ); |
|
} |
|
else |
|
{ |
|
VMPI_SetCurrentStage( "VMPI_DistributeLightData" ); |
|
|
|
// Wait until we've received the filename from the master. |
|
while ( g_LightResultsFilename.Count() == 0 ) |
|
{ |
|
VMPI_DispatchNextMessage(); |
|
} |
|
|
|
// Open |
|
FileHandle_t fp = g_pFileSystem->Open( g_LightResultsFilename.Base(), "rb", VMPI_VIRTUAL_FILES_PATH_ID ); |
|
if ( !fp ) |
|
Error( "Can't open '%s' to read lighting info.", g_LightResultsFilename.Base() ); |
|
|
|
int size = g_pFileSystem->Size( fp ); |
|
int faceSize = (numfaces*(MAXLIGHTMAPS+sizeof(int))); |
|
|
|
if ( size > faceSize ) |
|
{ |
|
int lightSize = size - faceSize; |
|
CUtlBuffer faceData; |
|
pdlightdata->EnsureCount( lightSize ); |
|
faceData.EnsureCapacity( faceSize ); |
|
|
|
g_pFileSystem->Read( pdlightdata->Base(), lightSize, fp ); |
|
g_pFileSystem->Read( faceData.Base(), faceSize, fp ); |
|
g_pFileSystem->Close( fp ); |
|
|
|
faceData.SeekPut( CUtlBuffer::SEEK_HEAD, faceSize ); |
|
|
|
// write out the face data |
|
for ( int i = 0; i < numfaces; i++ ) |
|
{ |
|
for ( int j = 0; j < MAXLIGHTMAPS; j++ ) |
|
{ |
|
g_pFaces[i].styles[j] = faceData.GetChar(); |
|
} |
|
g_pFaces[i].lightofs = faceData.GetInt(); |
|
} |
|
} |
|
} |
|
} |
|
|
|
|
|
|