diff options
Diffstat (limited to 'utils/remoteshadercompile/remoteshadercompile.cpp')
| -rw-r--r-- | utils/remoteshadercompile/remoteshadercompile.cpp | 316 |
1 files changed, 316 insertions, 0 deletions
diff --git a/utils/remoteshadercompile/remoteshadercompile.cpp b/utils/remoteshadercompile/remoteshadercompile.cpp new file mode 100644 index 0000000..81438a1 --- /dev/null +++ b/utils/remoteshadercompile/remoteshadercompile.cpp @@ -0,0 +1,316 @@ +//========= 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; +} |