|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Defines the entry point for the console application
//
//===============================================================================
//#include <sys/stat.h>
//#include <time.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <conio.h >
#include "tier1/utlvector.h"
#include "tier0/icommandline.h"
#include "tier2/tier2.h"
#include "tier2/fileutils.h"
#include "../../dx9sdk/include/d3d9.h"
#include "../../dx9sdk/include/d3dx9.h"
#define DEFAULT_PORT "20000"
#define DEFAULT_SEND_BUF_LEN 40000
#define DEFAULT_RECV_BUF_LEN 40000
char g_pPathBase[MAX_PATH]; bool g_bPrintDisassembly;
// This guy just spins and compiles when a command comes in from the game
void ServerThread( void * ) { WSADATA wsaData; if( WSAStartup( 0x101, &wsaData ) != 0 ) return;
struct addrinfo *result = NULL, hints;
ZeroMemory( &hints, sizeof(hints) ); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; hints.ai_flags = AI_PASSIVE;
// Resolve the server address and port
int nResult = getaddrinfo( NULL, DEFAULT_PORT, &hints, &result ); if ( nResult != 0 ) { printf( "getaddrinfo failed: %d\n", nResult ); WSACleanup(); return; }
// Create a SOCKET for connecting to server
SOCKET ListenSocket = socket( result->ai_family, result->ai_socktype, result->ai_protocol ); if (ListenSocket == INVALID_SOCKET) { printf("socket failed: %ld\n", WSAGetLastError()); freeaddrinfo(result); WSACleanup(); return; }
// Setup the TCP listening socket
nResult = bind( ListenSocket, result->ai_addr, (int)result->ai_addrlen ); if (nResult == SOCKET_ERROR) { printf("bind failed: %d\n", WSAGetLastError()); freeaddrinfo(result); closesocket(ListenSocket); WSACleanup(); return; }
freeaddrinfo(result);
nResult = listen( ListenSocket, SOMAXCONN ); if ( nResult == SOCKET_ERROR ) { printf( "listen failed: %d\n", WSAGetLastError() ); closesocket( ListenSocket ); WSACleanup(); return; }
printf( "Waiting for initial connection...\n" );
// Accept a client socket
SOCKET ClientSocket = accept( ListenSocket, NULL, NULL ); if ( ClientSocket == INVALID_SOCKET ) { printf( "accept failed: %d\n", WSAGetLastError() ); closesocket( ListenSocket ); WSACleanup(); return; }
// First connection
printf( "Game connected\n" );
char pRecbuf[DEFAULT_RECV_BUF_LEN]; // Text in command file from game
uint32 pSendbuf[DEFAULT_SEND_BUF_LEN]; // Error string or binary shader blob in reply
while ( true ) { nResult = recv( ClientSocket, pRecbuf, DEFAULT_RECV_BUF_LEN, 0 );
if ( nResult > 0 ) { char *pShaderFilename = strtok ( pRecbuf, "\n"); char pFullFilename[MAX_PATH];
// If we took in a path on the commandline, we concatenate the incoming filenames with it
if ( V_strlen( g_pPathBase ) > 0 ) { V_strncpy( pFullFilename, g_pPathBase, MAX_PATH ); // base path
V_strncat( pFullFilename, pShaderFilename, MAX_PATH ); pShaderFilename = pFullFilename; } char *pShaderModel = strtok ( NULL, "\n"); int nSendBufLen = 0;
// Only try to compile if we have a recognized profile
if ( !stricmp( pShaderModel, "vs_2_0" ) || !stricmp( pShaderModel, "ps_2_0" ) || !stricmp( pShaderModel, "ps_2_b" ) ) { char *pNumMacros = strtok ( NULL, "\n"); int nNumMacros = atoi( pNumMacros );
// Read macros from the command file
CUtlVector<D3DXMACRO> macros; D3DXMACRO macro; for ( int i=0; i<nNumMacros-1; i++ ) // The last one is the (null) one, so don't bother reading it
{ // Allocate and populate strings
macro.Name = strtok( NULL, "\n"); macro.Definition = strtok( NULL, "\n"); macros.AddToTail( macro ); }
// Null macro at the end
macro.Name = NULL; macro.Definition = NULL; macros.AddToTail( macro );
LPD3DXBUFFER pShader, pErrorMessages;
// This is the shader compiler we use for pre-ps30 shaders.
// This utility needs to change if we want to do ps30 shaders (see logic in vertexshaderdx8.cpp)
HRESULT hr = D3DXCompileShaderFromFile( pShaderFilename, macros.Base(), NULL /* LPD3DXINCLUDE */, "main", pShaderModel, 0, &pShader, &pErrorMessages, NULL /* LPD3DXCONSTANTTABLE *ppConstantTable */ ); if ( hr != D3D_OK ) { pSendbuf[0] = 0; printf( "Compilation error in %s\n", pShaderFilename );
if ( pErrorMessages ) { memcpy( pSendbuf+1, pErrorMessages->GetBufferPointer(), pErrorMessages->GetBufferSize() ); // Null-terminated string
printf("%s\n", (const char*)(pSendbuf + 1 )); nSendBufLen = pErrorMessages->GetBufferSize() + 4; // account for uint32 at front of the buffer
} else { ((uint8*)(pSendbuf+1))[0] = '?'; ((uint8*)(pSendbuf+1))[1] = '\0'; nSendBufLen = 2 + 4; // account for uint32 at front of the buffer
} } else // Success!
{ // printf( "Compiled %s\n", pShaderFilename );
pSendbuf[0] = pShader->GetBufferSize(); memcpy( pSendbuf+1, pShader->GetBufferPointer(), pShader->GetBufferSize() ); nSendBufLen = pShader->GetBufferSize() + 4; // account for uint32 at front of the buffer
if ( g_bPrintDisassembly ) { printf( "Filename: %s\n", pShaderFilename ); printf( "Shader model: %s\n", pShaderModel ); printf( "Macros: " ); for ( int i = 0; i < nNumMacros - 1; i++ ) printf( " %s\n", macros[i].Name ); LPD3DXBUFFER pDisassembly = NULL; D3DXDisassembleShader( (const DWORD*)pShader->GetBufferPointer(), FALSE, "", &pDisassembly ); if ( pDisassembly ) { printf( "Disassembled shader:\n"); puts( (const char*)pDisassembly->GetBufferPointer() ); printf("\n");
pDisassembly->Release(); } } }
if (pErrorMessages) { pErrorMessages->Release(); }
if (pShader) { pShader->Release(); }
// Purge the macro buffer
macros.RemoveMultipleFromTail( nNumMacros ); } else // Not a supported shader profile
{ pSendbuf[0] = 0; char *pCharSendbuff = (char *) (pSendbuf+1); V_snprintf( pCharSendbuff, DEFAULT_SEND_BUF_LEN, "Unsupported shader profile: %s\n", pShaderModel ); nSendBufLen = strlen( pCharSendbuff ) + 4; // account for uint32 at front of the buffer
}
// Send the compiled shader back to the game
int nSendResult = send( ClientSocket, (const char *)pSendbuf, nSendBufLen, 0 ); if ( nSendResult == SOCKET_ERROR ) { printf( "send failed: %d\n", WSAGetLastError() ); closesocket( ClientSocket ); WSACleanup(); return; } } else // We had a game talking to us but it went away
{ printf( "Game went away, waiting for new connection...\n" );
// Block again waiting to accept a connection
ClientSocket = accept( ListenSocket, NULL, NULL );
printf( "Game connected\n" );
if ( ClientSocket == INVALID_SOCKET ) { printf( "accept failed: %d\n", WSAGetLastError() ); Assert( 0 ); closesocket( ListenSocket ); WSACleanup(); return; } } }
nResult = shutdown( ClientSocket, SD_SEND ); if ( nResult == SOCKET_ERROR ) { printf("shutdown failed: %d\n", WSAGetLastError()); closesocket( ClientSocket ); WSACleanup(); return; }
// cleanup
closesocket( ClientSocket ); WSACleanup(); }
void CheckPath( char *pPath ) { int len = V_strlen( pPath );
// If we don't have a path separator at the end of the path, put one there
if ( ( pPath[len-1] != '\\' ) && ( pPath[len-1] != '/' ) ) { V_strncat( pPath, CORRECT_PATH_SEPARATOR_S, MAX_PATH ); } }
int main(int argc, char* argv[]) { if ( argc < 2 ) { printf( "============================================================\n" ); printf( " Please provide full path to shader directory. For example:\n" ); printf( " U:\\piston\\staging\\src\\materialsystem\\stdshaders\\ \n"); printf( "============================================================\n" ); printf( " remoteshadercompiler will now exit!!! \n" ); printf( "============================================================\n" ); return 0; }
printf( "========================================================\n"); printf( "Remote shader compiler is running. Press ESCAPE to exit\n" ); printf( "========================================================\n");
// If we have a path specified on the commandline, we expect
// that the remote machine is going to send base filenames only
// and that we'll want to strcat this path onto the filename from the worker.
//
// For example, if you have your shader source on your Windows machine, you can use something like this:
//
// U:\piston\staging\src\materialsystem\stdshaders\ //
strcpy( g_pPathBase, argv[1] );
if ( argc == 3 ) { g_bPrintDisassembly = true; }
CheckPath( g_pPathBase );
// Kick off compile server thread
_beginthread( ServerThread, 0, NULL );
// Spin until escape
while( _getch() != 27 ) ;
return 0; }
|