/*---------------------------------------------------------------------------* Copyright (C) Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. *---------------------------------------------------------------------------*/ // ------------------------------------------------------------------ // gshCompile.cpp // ------------------------------------------------------------------ #include #include #include #include "gshCompile.h" typedef DWORD64 uint64; #define GSH_DUMP_FILENAME_SIZE 256 #define GSH_FULL_FILE_NAME_SIZE 512 static char vs_name_buffer[GSH_FULL_FILE_NAME_SIZE]; static char ps_name_buffer[GSH_FULL_FILE_NAME_SIZE]; static char gs_name_buffer[GSH_FULL_FILE_NAME_SIZE]; static char cs_name_buffer[GSH_FULL_FILE_NAME_SIZE]; // // structure describing optimization flags // static struct oflagdefS { const char *name; const char *help; uint64 flagbits; } oflagdef[] = { OFLAGDEF_CONTENTS }; static void Help(char** argv) { int i; printf("\n"); printf("gshCompile\n"); printf("\n"); printf("Usage: gshCompile -v vs_file -p ps_file -g gs_file -o output_file -force_uniformblock\n"); printf(" gshCompile -c cs_file -o output_file -force_uniformblock\n"); printf(" gshCompile -version\n\n"); printf("-v : Vertex Shader file\n"); printf("-p : Pixel Shader file\n"); printf("-g : Geometry Shader file\n"); printf("-c : Compute Shader file\n"); printf("-so : Stream Out Varings file\n"); printf("-o : Output file name\n"); printf("-oa : Existing output file name to append shaders.\n"); printf("-oh : Output header file name to write shaders as code.\n"); printf("-force_uniformblock : Force uniform block usage\n"); printf("-no_limit_array_syms : Create symbols all elements of uniform arrays (not just the first 2 and last 1)\n"); printf("-endianbugfix : Performs 8-in-32 byte swap on the output texture\n"); printf(" data. This is a bug workaround for the prototype\n"); printf(" hardware.\n"); printf("-align : Adds alignment padding blocks between the header\n"); printf(" and the image. The amount of padding depends \n"); printf(" upon hardware requirements.\n"); printf("-h : Show this help message\n"); printf("-d : Dump Debug Information to a file\n"); printf(" Dump files:\n"); printf(" _vs.dmp\n"); printf(" _gs.dmp\n"); printf(" _ps.dmp\n"); printf("-istats : Dump shader instruction statistics to a file\n"); printf(" File names: _vs.stat, _ps.stat, etc.\n"); printf("-btstats : Dump build time statistics to a file\n"); printf("-btstats+ : Append build time statistics to a file\n"); printf(" File name: .bt\n"); printf("-nospark : Omit Spark debug information. Without this option a\n"); printf(" 16 byte hash is appended to each shader, and a debug\n"); printf(" information file is written to directory\n"); printf(" $(LOCALAPPDATA)\\Temp\\Nintendo\\Cafe\\ShaderSymbols\n"); printf(" or the directory specified via -sparkdir option.\n"); printf("-sparkdir path : Override the default Spark debug information\n"); printf(" directory.\n"); printf("-t essl : Select experimental OpenGL ES mode.\n"); printf("-O : Perform additional optimizations:\n"); printf(" is a comma separated list of optimizations to enable:\n"); for (i = 0; oflagdef[i].name; i++) { if (oflagdef[i].help) { printf(" %-12s : %s\n", oflagdef[i].name, oflagdef[i].help); } } printf(" Any of the optimizations may be disabled by prefixing them with ! or no-\n"); printf(" For example:\n"); printf(" -Oall,no-fastmath\n"); printf(" will enable all optimizations except fastmath.\n"); printf(" Just plain -O is equivalent to -Oall.\n"); printf("\n"); } void LoadDLLs(HMODULE *hShaderUtilDLL, GSH2Func *fpGSH2, HMODULE *hGfdDLL, GFDFunc *fpGFD) { *hShaderUtilDLL = LoadLibrary(LIB_DLL_SHADERUTILS); if ( !*hShaderUtilDLL ) { printf("Failed to load DLL %ws. Exiting.", LIB_DLL_SHADERUTILS); FreeLibrary(*hShaderUtilDLL); exit(EC_LOADINGDLLFAILED); } *hGfdDLL = LoadLibrary(LIB_DLL_GFD); if ( !*hGfdDLL ) { printf("Failed to load DLL %ws. Exiting.", LIB_DLL_GFD); FreeLibrary(*hGfdDLL); exit(EC_LOADINGDLLFAILED); } // Get function fpGSH2->Initialize = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2Initialize")); fpGSH2->Destroy = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2Destroy")); fpGSH2->CompileProgram3 = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2CompileProgram3")); fpGSH2->DestroyGX2Program3 = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2DestroyGX2Program3")); fpGSH2->CalcFetchShaderSizeEx = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2CalcFetchShaderSizeEx")); fpGSH2->InitFetchShaderEx = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2InitFetchShaderEx")); fpGSH2->GetVertexShaderGPRs = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetVertexShaderGPRs")); fpGSH2->GetGeometryShaderGPRs = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetGeometryShaderGPRs")); fpGSH2->GetPixelShaderGPRs = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetPixelShaderGPRs")); fpGSH2->GetVertexShaderStackEntries = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetVertexShaderStackEntries")); fpGSH2->GetGeometryShaderStackEntries = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetGeometryShaderStackEntries")); fpGSH2->GetPixelShaderStackEntries = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetPixelShaderStackEntries")); fpGSH2->GetABIVersion = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2GetABIVersion")); fpGSH2->PrintBuildTimeStats = reinterpret_cast(GetProcAddress(*hShaderUtilDLL, "GSH2PrintBuildTimeStats")); fpGFD->WriteFileShaderAsCode = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDWriteFileShaderAsCode")); fpGFD->WriteFileShader = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDWriteFileShader")); fpGFD->AppendWriteFileShader = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDAppendWriteFileShader")); fpGFD->WriteFileShaderAsCode2 = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDWriteFileShaderAsCode2")); fpGFD->WriteFileShader2 = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDWriteFileShader2")); fpGFD->AppendWriteFileShader2 = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDAppendWriteFileShader2")); fpGFD->WriteFileShaderAsCodeWithSource = reinterpret_cast(GetProcAddress(*hGfdDLL, "GFDWriteFileShaderAsCodeWithSource")); } void FreeDLLs(HMODULE *hShaderUtilDLL, HMODULE *hGfdDLL) { FreeLibrary(*hShaderUtilDLL); FreeLibrary(*hGfdDLL); } bool DeleteShaderSource(char** ppShaders) { if (*ppShaders) { delete[] *ppShaders; *ppShaders = NULL; } return true; } #define INCLUDE_SUPPORT #ifdef INCLUDE_SUPPORT /* * append a string to another, reallocating memory if necessary * origData is the original source * appendData is the new string to append * *pCurLen is the current length of the string * *pMaxLen is the maximum allocated space for the string */ static char * appendBuffer(char *origData, char *appendData, size_t *pMaxLen, size_t *pCurLen) { size_t maxLen = *pMaxLen; size_t curLen = *pCurLen; size_t appendLen = strlen(appendData); size_t newLen = curLen + appendLen; char *newData = origData; if (newLen > maxLen) { maxLen = newLen + BUFSIZ; newData = (char *)realloc(origData, maxLen+1); /* remember space for trailing null!! */ if (!newData) { fprintf(stderr, "Ran out of memory while reading data!\n"); exit(2); } *pMaxLen = maxLen; } #if 0 // strangely, this is slower than strcpy memcpy(newData + curLen, appendData, appendLen); #else strcpy(newData+curLen, appendData); #endif *pCurLen = newLen; return newData; } /* * structure to hold a file processing state * used for printing error messages */ typedef struct fileState { FILE *fp; const char *fname; int linenum; } FileState; // forward declaration static char *processInclude(FileState *curfs, char *strbuf, char *includeline, size_t *pMaxSize, size_t *pCurSize); // // append an error message to the string // static char *appendError(FileState *fs, char *msg, char *strbuf, size_t *pMaxSize, size_t *pCurSize) { char temp[32]; strbuf = appendBuffer(strbuf, "#error \"", pMaxSize, pCurSize); strbuf = appendBuffer(strbuf, fs->fname ? fs->fname : "(unknown)", pMaxSize, pCurSize); strbuf = appendBuffer(strbuf, ": ", pMaxSize, pCurSize); sprintf_s(temp, sizeof(temp), "%d", fs->linenum); strbuf = appendBuffer(strbuf, temp, pMaxSize, pCurSize); strbuf = appendBuffer(strbuf, ": ", pMaxSize, pCurSize); strbuf = appendBuffer(strbuf, msg, pMaxSize, pCurSize); strbuf = appendBuffer(strbuf, "\"\n", pMaxSize, pCurSize); return strbuf; } // // append a whole file to a string // strbuf is a pointer to the string to be appended to // pCurSize points to the current size // pMaxSize points to the maximum size currently allocated to the buffer // static char * getFile(FileState *fs, char *strbuf, size_t *pMaxSize, size_t *pCurSize) { char linebuf[BUFSIZ]; char *ptr; FILE *fp = fs->fp; // check for byte order mark indicating UTF-8 // if found, just strip it off { int c1, c2, c3; c1 = fgetc(fp); c2 = fgetc(fp); c3 = fgetc(fp); if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { // byte order mark; ignore these and keep going } else { // rewind and start again fseek(fp, 0, SEEK_SET); } } // now process lines from the file for(;;) { char *word; ptr = fgets(linebuf, sizeof(linebuf), fp); word = NULL; if (!ptr) break; // check for #include lines while (*ptr == ' ' || *ptr=='\t') ptr++; if (*ptr == '#') { ptr++; while (*ptr == ' ' || *ptr == '\t') ptr++; word = ptr; } if (word && !strncmp(word, "include", 7) && isspace(word[7])) { strbuf = processInclude(fs, strbuf, ptr+8, pMaxSize, pCurSize); } else { strbuf = appendBuffer(strbuf, linebuf, pMaxSize, pCurSize); } while (*ptr) { if (*ptr == '\n') fs->linenum++; ptr++; } } // make sure to end with a newline strbuf = appendBuffer(strbuf, "\n", pMaxSize, pCurSize); return strbuf; } // don't make this too big, or the preprocessor won't report errors from #includes inside nested // #ifs #define MAX_INCLUDE_DEPTH 24 static char *processInclude(FileState *oldfs, char *strbuf, char *includeline, size_t *pMaxSize, size_t *pCurSize) { char *fname; char *fullfname; char *ptr; char *dirsuffix; FILE *fp; FileState fs; size_t bufsiz; static int includeDepth = 0; // check for #include nested too far if (includeDepth >= MAX_INCLUDE_DEPTH) { strbuf = appendError(oldfs, "#includes nested too deeply", strbuf, pMaxSize, pCurSize); //fprintf(stderr, "#includes nest too deeply\n"); return strbuf; } // find the file name fname = includeline; while (isspace(*fname)) fname++; if (*fname != '"') { strbuf = appendError(oldfs, "expected quote after #include", strbuf, pMaxSize, pCurSize); return strbuf; } fname++; ptr = fname; while (*ptr && *ptr != '"') ptr++; if (!*ptr) { strbuf = appendError(oldfs, "missing quote at end of #include", strbuf, pMaxSize, pCurSize); return strbuf; } *ptr = 0; // at this point "fname" contains the file name // we need to prepend the directory of the originating file bufsiz = strlen(fname) + strlen(oldfs->fname) + 1; fullfname = (char *)calloc(strlen(fname) + strlen(oldfs->fname) + 1, 1); strcpy_s(fullfname, bufsiz, oldfs->fname); // find the part (if any) of the full file name that contains a directory dirsuffix = NULL; for (ptr = fullfname; *ptr; ptr++) { if (*ptr == '/' || *ptr == '\\') dirsuffix = ptr; } if (dirsuffix) { *++dirsuffix = 0; } else { *fullfname = 0; } // now append the specific file name so it will be relative to the file // that included this one strcat_s(fullfname, bufsiz, fname); // try to open it //fprintf(stderr, "include of %s (old file name=%s, new=%s)\n", fullfname, oldfs->fname, fname); fopen_s(&fp, fullfname, "rb"); if (!fp) { // this could be inside a #if, so we can't throw an error immediately // instead, insert a #error into the text char *temp; size_t tempsiz = strlen(fname) + 80; free(fullfname); temp = (char *)calloc(1, tempsiz); if (!temp) { temp = "Out of memory"; } else { sprintf_s(temp, tempsiz, "unable to open file %s", fname); } strbuf = appendError(oldfs, temp, strbuf, pMaxSize, pCurSize); free(temp); return strbuf; } fs.fname = fullfname; fs.linenum = 1; fs.fp = fp; ++includeDepth; strbuf = getFile(&fs, strbuf, pMaxSize, pCurSize); --includeDepth; fclose(fp); return strbuf; } bool AppendShaderSource(char* pFilename, char** ppSource) { FILE *fp = NULL; FileState fs; size_t totalLen = 0; size_t curLen = 0; size_t fileLen; fopen_s(&fp, pFilename, "rb"); if (!fp) { return false; } // Get file size fseek(fp, 0, SEEK_END); fileLen = ftell(fp); fseek(fp, 0, SEEK_SET); if (!(*ppSource)) { curLen = 0; totalLen = fileLen + 1; // for a trailing newline *ppSource = (char *)malloc(totalLen+1); // add space for a trailing 0 if (!(*ppSource)) { return false; } *ppSource[0] = '\0'; } else { curLen = strlen(*ppSource); totalLen = curLen; // this is, unfortunatley, just a guess, but it's a safe one } fs.fp = fp; fs.linenum = 1; fs.fname = pFilename; *ppSource = getFile(&fs, *ppSource, &totalLen, &curLen); fclose(fp); return true; } #else bool AppendShaderSource(char* pFilename, char** ppSource) { FILE *fp = NULL; u32 totalLen = 0; u32 offset = 0; u32 fileLen = 0; fopen_s(&fp, pFilename, "rb"); if (!fp) { return false; } // Get file size fseek(fp, 0, SEEK_END); fileLen = ftell(fp); fseek(fp, 0, SEEK_SET); // check for byte order mark indicating UTF-8 // if found, just strip it off { int c1, c2, c3; c1 = fgetc(fp); c2 = fgetc(fp); c3 = fgetc(fp); if (c1 == 0xEF && c2 == 0xBB && c3 == 0xBF) { // byte order mark; ignore these, reduce // the file size accordingly fileLen -= 3; } else { // rewind and start again fseek(fp, 0, SEEK_SET); } } if (!(*ppSource)) { offset = 0; totalLen = fileLen + 1; *ppSource = new char[totalLen]; if (!(*ppSource)) { return false; } *ppSource[0] = '\0'; } else { offset = (u32)(strlen(*ppSource) + 1); // + 1 for appending "\n" totalLen = offset + fileLen + 1; // + 1 for appending "\0" char* pTemp = new char[totalLen]; if (!pTemp) { return false; } assert(totalLen >= 2); strcpy_s(pTemp, totalLen, (*ppSource)); strcat_s(pTemp, totalLen, "\n"); delete [] (*ppSource); *ppSource = pTemp; } fread(((*ppSource) + offset), fileLen, 1, fp); (*ppSource)[totalLen - 1] = '\0'; fclose(fp); return true; } #endif void DeleteSOVaryings(char** pSOVaryings, u32 numVaryings) { if (pSOVaryings != NULL) { for (u32 idx = 0; idx < numVaryings; idx++) { delete[] (pSOVaryings[idx]); } delete [] (pSOVaryings); } } #define MIN_NUM_VARYINGS 50 char** GetStreamOutVaryings(char* pFilename, u32* pNumSOVaryings) { FILE *fp = NULL; u32 totalLen = 0; u32 offset = 0; u32 fileLen = 0; char varying[256]; char** pSOVaryings = NULL; char** pTemp; u32 varyingsSize; u32 numVaryings = 0; fopen_s(&fp, pFilename, "r"); if (!fp) { return NULL; } *pNumSOVaryings = 0; pSOVaryings = new char*[MIN_NUM_VARYINGS]; varyingsSize = MIN_NUM_VARYINGS; if (pSOVaryings != NULL) { while (fgets(varying, 256, fp) != NULL) { size_t varyingLen; if (numVaryings >= varyingsSize) { varyingsSize += MIN_NUM_VARYINGS; pTemp = new char*[varyingsSize]; if (pTemp == NULL) { DeleteSOVaryings(pSOVaryings, numVaryings); return NULL; } memcpy(pTemp, pSOVaryings, (numVaryings * sizeof(char *))); delete [] (pSOVaryings); pSOVaryings = pTemp; } varyingLen = strlen(varying) + 1; varying[varyingLen - 2] = '\0'; pSOVaryings[numVaryings] = new char[varyingLen]; if (pSOVaryings[numVaryings] == NULL) { DeleteSOVaryings(pSOVaryings, numVaryings); return NULL; } strcpy_s(pSOVaryings[numVaryings], varyingLen, varying); numVaryings++; } } *pNumSOVaryings = numVaryings; fclose(fp); return pSOVaryings; } bool Destroy(ShaderSourceInfo* pConfig) { DeleteShaderSource(&pConfig->vs_source); DeleteShaderSource(&pConfig->ps_source); DeleteShaderSource(&pConfig->gs_source); DeleteSOVaryings(pConfig->so_varyings, pConfig->numSOVaryings); return true; } static bool matchword(const char *word, const char *str) { // see if word appears first in str while (*word && *word == *str) { word++, str++; } if (*word) return false; if (*str && *str != ',') return false; return true; } // // process a string and add/subtract optimization flags from origFlags, // returning the new set of flags // static uint64 parseOptFlags(uint64 mask, const char *str) { bool invert, found; int i; if (!str) return mask; while (*str) { invert = false; if (!strncmp(str, "no-", 3)) { str += 3; invert = true; } else if (str[0] == '!') { str++; invert = true; } found = false; for (i = 0; oflagdef[i].name; i++) { if (matchword(oflagdef[i].name, str)) { found = true; break; } } if (!found) { fprintf(stderr, "Unknown optimization option: %s\n", str); return mask; } if (oflagdef[i].flagbits == 0) { mask = 0; /* turn off everything */ } else if (invert) { mask &= ~oflagdef[i].flagbits; } else { mask |= oflagdef[i].flagbits; } str += strlen(oflagdef[i].name); while (*str == ',') str++; } return mask; } void PrintVersion(u32 abiVersion) { printf("gshCompile compiled on %s\n", __DATE__); if (abiVersion) { printf("shaderUtils version: 0x%08lX\n", (unsigned long)abiVersion); } else { printf("shaderUtils version unknown\n"); } } bool Initialize(s32 argc, char** argv, ShaderSourceInfo* pConfig, u32 abiVersion) { s32 count = 0; memset(pConfig, 0, sizeof(*pConfig)); // initalize defaults pConfig->pOutFilename = DEFAULT_OUTFILENAME; pConfig->gpu = DEFAULT_GPU; pConfig->lang = DEFAULT_SHADINGLANGUAGE; pConfig->forceUniformBlock = DEFAULT_FORCEUNIFORMBLOCK; pConfig->outfilealign = DEFAULT_ALIGNMODE; pConfig->append = DEFAULT_APPENDMODE; pConfig->ascode = DEFAULT_ASCODEMODE; pConfig->endianbugfix = DEFAULT_ENDIANBUGFIX; pConfig->noSparkInfo = DEFAULT_NOSPARKINFO; if (argc == 1) { Help(argv); exit(EC_SUCCESS); } for (count = 1; count < argc; count++) { if (!strcmp(argv[count], "-gpu")) { if ((count + 1) < argc) { switch(atoi(argv[count + 1])) { case 1: pConfig->gpu = GPU_VERSION_1; break; case 2: pConfig->gpu = GPU_VERSION_GPU7; break; default: printf("Error: Unsupported GPU version\n"); exit(EC_UNSUPPORTEDGPU); break; } count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -v source vertex shader else if (!strcmp(argv[count], "-v")) { if ((count + 1) < argc) { if (!AppendShaderSource(argv[count + 1], &pConfig->vs_source)) { printf("Problem opening file \"%s\".\n", argv[count + 1]); exit(EC_INPUTFILEERROR); } pConfig->vs_source_filename = _fullpath( vs_name_buffer, argv[count + 1], GSH_FULL_FILE_NAME_SIZE ); if( !pConfig->vs_source_filename ) pConfig->vs_source_filename = argv[count + 1]; count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -p source pixel shader else if (!strcmp(argv[count], "-p")) { if ((count + 1) < argc) { if (!AppendShaderSource(argv[count + 1], &pConfig->ps_source)) { printf("Problem opening file \"%s\".\n", argv[count + 1]); exit(EC_INPUTFILEERROR); } pConfig->ps_source_filename = _fullpath( ps_name_buffer, argv[count + 1], GSH_FULL_FILE_NAME_SIZE ); if( !pConfig->ps_source_filename ) pConfig->ps_source_filename = argv[count + 1]; count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -g source geometry shader else if (!strcmp(argv[count], "-g")) { if ((count + 1) < argc) { if (!AppendShaderSource(argv[count + 1], &pConfig->gs_source)) { printf("Problem opening file \"%s\".\n", argv[count + 1]); exit(EC_INPUTFILEERROR); } pConfig->gs_source_filename = _fullpath( gs_name_buffer, argv[count + 1], GSH_FULL_FILE_NAME_SIZE ); if( !pConfig->gs_source_filename ) pConfig->gs_source_filename = argv[count + 1]; count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -c compute shader else if (!strcmp(argv[count], "-c")) { if ((count + 1) < argc) { if (!AppendShaderSource(argv[count + 1], &pConfig->cs_source)) { printf("Problem opening file \"%s\".\n", argv[count + 1]); exit(EC_INPUTFILEERROR); } pConfig->cs_source_filename = _fullpath( cs_name_buffer, argv[count + 1], GSH_FULL_FILE_NAME_SIZE ); if( !pConfig->cs_source_filename ) pConfig->cs_source_filename = argv[count + 1]; count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -so stream out varyings else if (!strcmp(argv[count], "-so")) { if ((count + 1) < argc) { pConfig->so_varyings = GetStreamOutVaryings(argv[count + 1], &pConfig->numSOVaryings); if (pConfig->so_varyings == NULL) { printf("Problem opening file \"%s\".\n", argv[count + 1]); exit(EC_INPUTFILEERROR); } count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -o destination filename else if (!strcmp(argv[count], "-o")) { if ((count + 1) < argc) { pConfig->pOutFilename = argv[count + 1]; count++; } } // -oa destination filename else if (!strcmp(argv[count], "-oa")) { pConfig->append = true; if ((count + 1) < argc) { pConfig->pOutFilename = argv[count + 1]; count++; } } // -oh destination filename else if (!strcmp(argv[count], "-oh")) { pConfig->ascode = true; if ((count + 1) < argc) { pConfig->pOutFilename = argv[count + 1]; count++; } } // -? or -help print help else if (!strcmp(argv[count], "-?") || !strcmp(argv[count], "-help")) { Help(argv); exit(EC_SUCCESS); } // -force_uniformblock else if (!strcmp(argv[count], "-force_uniformblock")) { pConfig->forceUniformBlock = true; } else if (!strncmp(argv[count], "-O", 2)) { if (argv[count][2]) pConfig->optimizeFlags = parseOptFlags(pConfig->optimizeFlags, argv[count]+2); else pConfig->optimizeFlags = GSH2_OPTFLAGS_ALL; } // -t (language) else if (!strcmp(argv[count], "-t")) { if ((count + 1) < argc) { if (!strcmp(argv[count + 1], "essl")) { pConfig->lang = SHADERLANG_ESSL; } else if (!strcmp(argv[count + 1], "glsl")) { pConfig->lang = SHADERLANG_GLSL; } else { printf("Unsupported shader language type specified\n"); exit(EC_UNSUPPORTEDSHADERTYPE); } count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -align (file alignment) else if (!strcmp(argv[count], "-align")) { pConfig->outfilealign = true; } // -endianbugfix (endian bug hw workaround) else if (!strcmp(argv[count], "-endianbugfix")) { pConfig->endianbugfix = true; } // -gx2 - ignored but here for backward compatibility // so users don't get an error else if (!strcmp(argv[count], "-gx2")) { continue; } // -d (dump shaders) else if (!strcmp(argv[count], "-d")) { pConfig->dumpShaders = true; } // -nospark: leave out Spark debug info else if (!strcmp(argv[count], "-nospark")) { pConfig->noSparkInfo = true; } // -sparkdir: directory to write the Spark debug info else if (!strcmp(argv[count], "-sparkdir")) { if ((count + 1) < argc) { pConfig->pSparkDir = argv[count + 1]; count++; } else { printf("Missing parameter argument %s \n", argv[count]); exit(EC_BADPARAM); } } // -istats: write out instruction stats else if (!strcmp(argv[count], "-istats")) { pConfig->istats = 1; } // -btstats: write out instruction stats else if (!strcmp(argv[count], "-btstats")) { pConfig->btstats = 1; } // -btstats+: write out instruction stats else if (!strcmp(argv[count], "-btstats+")) { pConfig->btstats = 2; } // -no_limit_array_syms: write out all symbols for each uniform array else if (!strcmp(argv[count], "-no_limit_array_syms")) { pConfig->noArrayLimit = 1; } // -version: print version of shaderUtils (and gshCompile??) else if (!strcmp(argv[count], "-version")) { PrintVersion(abiVersion); exit(EC_SUCCESS); } // command line parameter not recognized else { printf("Unrecognized command line parameter \"%s\". Exiting.", argv[count]); exit(EC_BADPARAM); } } return true; } void DumpShaderData(const char* pFileName, const char* pShaderDump) { FILE* fp = NULL; if (pShaderDump != NULL) { fopen_s(&fp, pFileName, "w"); assert(fp); if (fp != NULL) { fputs(pShaderDump, fp); fflush(fp); fclose(fp); } } } void DumpShaderStats(const char *pFileName, GSH2ShaderStats *pStats) { FILE *fp = NULL; if (pStats != NULL) { fopen_s(&fp, pFileName, "w"); assert(fp); if (fp != NULL) { fprintf(fp, "Hardware Usage Statistics\n\n"); fprintf(fp, "Total HW instructions used: %5d\n", pStats->uNumInst); fprintf(fp, " ALU instructions used: %5d\n", pStats->uNumALUInst); fprintf(fp, " Tfetch instructions used: %5d\n", pStats->uNumTfetchInst); fprintf(fp, " Vfetch instructions used: %5d\n", pStats->uNumVfetchInst); fprintf(fp, " Control flow instructions: %5d\n", pStats->uNumCflowInst); fprintf(fp, "GPR pool size: %5d\n", pStats->uGPRPoolSize); fprintf(fp, "Num HW GPRs used: %5d\n", pStats->uNumTempReg); fprintf(fp, "Num HW clause temps used: %5d\n", pStats->uNumClauseTempReg); fprintf(fp, "External (API) ALU consts: %5d\n", pStats->uNumALUConstReg); fclose(fp); } } } static GX2Boolean WriteFileShader(GFDFunc *pgfd, char* pFilename, GFDGPUVersion gpuVer, GFDEndianSwapMode swapMode, GFDAlignMode alignMode, u32 numShader, GFDShaders2 *pShaders) { GFDShaders shader1; if (pgfd->WriteFileShader2) { return (*pgfd->WriteFileShader2)(pFilename, gpuVer, swapMode, alignMode, numShader, pShaders); } if (pShaders->pComputeShader != NULL) { fprintf(stderr, "ERROR: This version of gfd.dll is unable to handle compute shaders\n"); return GX2_FALSE; } if (!pgfd->WriteFileShader) { fprintf(stderr, "ERROR: Unable to find WriteFileShader function in gfd.dll\n"); return GX2_FALSE; } shader1.pGeometryShader = pShaders->pGeometryShader; shader1.pPixelShader = pShaders->pPixelShader; shader1.pVertexShader = pShaders->pVertexShader; return (*pgfd->WriteFileShader)(pFilename, gpuVer, swapMode, alignMode, numShader, &shader1); } static GX2Boolean AppendWriteFileShader(GFDFunc *pgfd, char* pFilename, GFDGPUVersion gpuVer, GFDEndianSwapMode swapMode, GFDAlignMode alignMode, u32 numShader, GFDShaders2 *pShaders) { GFDShaders shader1; if (pgfd->AppendWriteFileShader2) { return (*pgfd->AppendWriteFileShader2)(pFilename, gpuVer, swapMode, alignMode, numShader, pShaders); } if (pShaders->pComputeShader != NULL) { fprintf(stderr, "ERROR: This version of gfd.dll is unable to handle compute shaders\n"); return GX2_FALSE; } if (!pgfd->AppendWriteFileShader) { fprintf(stderr, "ERROR: Unable to find AppendWriteFileShader function in gfd.dll\n"); return GX2_FALSE; } shader1.pGeometryShader = pShaders->pGeometryShader; shader1.pPixelShader = pShaders->pPixelShader; shader1.pVertexShader = pShaders->pVertexShader; return (*pgfd->AppendWriteFileShader)(pFilename, gpuVer, swapMode, alignMode, numShader, &shader1); } static GX2Boolean WriteFileShaderAsCode(GFDFunc *pgfd, char* pFilename, GFDEndianSwapMode swapMode, GFDShaders2 *pShaders) { GFDShaders shader1; if (pgfd->WriteFileShaderAsCode2) { return (*pgfd->WriteFileShaderAsCode2)(pFilename, swapMode, pShaders); } if (pShaders->pComputeShader != NULL) { fprintf(stderr, "ERROR: This version of gfd.dll is unable to handle compute shaders\n"); return GX2_FALSE; } if (!pgfd->WriteFileShaderAsCode) { fprintf(stderr, "ERROR: Unable to find WriteFileShaderAsCode function in gfd.dll\n"); return GX2_FALSE; } shader1.pGeometryShader = pShaders->pGeometryShader; shader1.pPixelShader = pShaders->pPixelShader; shader1.pVertexShader = pShaders->pVertexShader; return (*pgfd->WriteFileShaderAsCode)(pFilename, swapMode, &shader1); } s32 main(int argc, char** argv) { HMODULE hShaderUtilDLL; HMODULE hGfdDLL; GSH2Func fpGSH2; GFDFunc fpGFD; GFDShaders2 shaders; GSH2Setup libSetup; GSH2CompileSetup3 compileSetup; GSH2Handle hShaderUtils = NULL; GSH2CompileOutput3 compileOutput; ShaderSourceInfo sourceInfo; s32 retCode = EC_SUCCESS; u32 abiVersion = 0; // Load DLLs LoadDLLs(&hShaderUtilDLL, &fpGSH2, &hGfdDLL, &fpGFD); if (fpGSH2.GetABIVersion) abiVersion = (*fpGSH2.GetABIVersion)(); memset(&sourceInfo, 0, sizeof(sourceInfo)); memset(&compileOutput, 0, sizeof(compileOutput)); memset(&shaders, 0, sizeof(shaders)); memset(&libSetup, 0, sizeof(libSetup)); memset(&compileSetup, 0, sizeof(compileSetup)); compileSetup.abi_version = GSH2_ABI_VERSION_CURRENT; compileOutput.abi_version = GSH2_ABI_VERSION_CURRENT; compileOutput.gx2Program.abi_version = GSH2_ABI_VERSION_CURRENT; // get parameters from command line Initialize(argc, argv, &sourceInfo, abiVersion); libSetup.gpu = sourceInfo.gpu; // Initialize shaderUtils lib if (!(hShaderUtils = fpGSH2.Initialize(&libSetup))) { FreeDLLs(&hShaderUtilDLL, &hGfdDLL); exit(EC_GSH2INITFAILED); } // add optimizations from environment variables char *env = getenv("GSHCOMPILE_OPTIMIZE"); if (env != NULL) { sourceInfo.optimizeFlags = parseOptFlags(sourceInfo.optimizeFlags, env); } // Compile shaders compileSetup.vs_source = (const char*)sourceInfo.vs_source; compileSetup.ps_source = (const char*)sourceInfo.ps_source; compileSetup.gs_source = (const char*)sourceInfo.gs_source; compileSetup.so_varyings = (const char**)sourceInfo.so_varyings; compileSetup.cs_source = (const char*)sourceInfo.cs_source; compileSetup.numSOVaryings = sourceInfo.numSOVaryings; compileSetup.lang = sourceInfo.lang; compileSetup.options.dumpShaders = (sourceInfo.dumpShaders) ? 1 : 0; compileSetup.options.forceUniformBlock = (sourceInfo.forceUniformBlock) ? 1 : 0; compileSetup.options.optimize = (sourceInfo.optimizeFlags != 0) ? 1 : 0; compileSetup.options.skipSparkDebug = (sourceInfo.noSparkInfo) ? 1 : 0; compileSetup.options.optFlags = 1; compileSetup.optimizeFlags = sourceInfo.optimizeFlags; // limit number of symbols generated for arrays (default, can be overridden with -no_limit_array_syms) if (sourceInfo.noArrayLimit == 0) GSH2_OPTFLAG_SETBIT(compileSetup.optimizeFlags, GSH2_OPTFLAG_LIMITARRAYSYMS); compileSetup.spark_output_dir = (const char*)sourceInfo.pSparkDir; if (sourceInfo.istats) compileSetup.options.getStats = 1; if (sourceInfo.btstats) { compileSetup.options.getBuildTimeStats = 1; compileSetup.bt_stats = new GSH2BuildTimeStats; compileSetup.bt_stats->elapsedCount = GSH2_BT_NUMITEMS; } if (sourceInfo.cs_source) { if (compileSetup.vs_source || compileSetup.ps_source || compileSetup.gs_source) { fprintf(stderr, "Error: compute shader must be compiled on its own.\n"); exit(1); } compileSetup.cs_source_filename = (const char *)sourceInfo.cs_source_filename; if (compileSetup.options.getStats) compileSetup.cs_stats = new GSH2ShaderStats; } else { compileSetup.vs_source_filename = (const char*)sourceInfo.vs_source_filename; compileSetup.ps_source_filename = (const char*)sourceInfo.ps_source_filename; compileSetup.gs_source_filename = (const char*)sourceInfo.gs_source_filename; compileSetup.cs_source_filename = NULL; if (compileSetup.options.getStats) { if (compileSetup.vs_source) compileSetup.vs_stats = new GSH2ShaderStats; if (compileSetup.ps_source) compileSetup.ps_stats = new GSH2ShaderStats; if (compileSetup.gs_source) compileSetup.gs_stats = new GSH2ShaderStats; } } if (fpGSH2.CompileProgram3(hShaderUtils, &compileSetup, &compileOutput)) { shaders.abiVersion = GFD_DLL_ABI_VERSION; if (sourceInfo.vs_source) { shaders.pVertexShader = &compileOutput.gx2Program.vs; } if (sourceInfo.ps_source) { shaders.pPixelShader = &compileOutput.gx2Program.ps; } if (sourceInfo.gs_source) { shaders.pGeometryShader = &compileOutput.gx2Program.gs; } if (sourceInfo.cs_source) { shaders.pComputeShader = &compileOutput.gx2Program.cs; } if (sourceInfo.dumpShaders) { char baseName[GSH_DUMP_FILENAME_SIZE]; char fileName[GSH_DUMP_FILENAME_SIZE]; char* pDot; strncpy_s(baseName, GSH_DUMP_FILENAME_SIZE, sourceInfo.pOutFilename, strlen(sourceInfo.pOutFilename)); // search for the last "." in the output name and replace it with a NULL char to terminate // the string. pDot = strrchr(baseName, '.'); if (pDot != NULL) { *pDot = '\0'; } // Dump the shader debug data sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_vs.dmp", baseName); DumpShaderData(fileName, compileOutput.pVSDump); sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_gs.dmp", baseName); DumpShaderData(fileName, compileOutput.pGSDump); sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_ps.dmp", baseName); DumpShaderData(fileName, compileOutput.pPSDump); sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_cs.dmp", baseName); DumpShaderData(fileName, compileOutput.pCSDump); } if (sourceInfo.btstats) { char baseName[GSH_DUMP_FILENAME_SIZE]; char fileName[GSH_DUMP_FILENAME_SIZE]; char* pDot; GX2Boolean ok = GX2_FALSE; GX2Boolean append = (sourceInfo.btstats == 2) ? GX2_TRUE : GX2_FALSE; strncpy_s(baseName, GSH_DUMP_FILENAME_SIZE, sourceInfo.pOutFilename, strlen(sourceInfo.pOutFilename)); // search for the last "." in the output name and replace it with a NULL char to terminate // the string. pDot = strrchr(baseName, '.'); if (pDot != NULL) { *pDot = '\0'; } // Dump the shader build time data sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s.bt", baseName); if (fpGSH2.PrintBuildTimeStats) { ok = fpGSH2.PrintBuildTimeStats(fileName, compileSetup.bt_stats, append); } else { fprintf(stderr, "Unable to find GSH2PrintBuildTimeStats function in shaderUtils.dll\n"); } if (!ok) { fprintf(stderr, "PrintBuildTimeStats failed\n"); } } if (sourceInfo.istats) { char baseName[GSH_DUMP_FILENAME_SIZE]; char fileName[GSH_DUMP_FILENAME_SIZE]; char* pDot; strncpy_s(baseName, GSH_DUMP_FILENAME_SIZE, sourceInfo.pOutFilename, strlen(sourceInfo.pOutFilename)); // search for the last "." in the output name and replace it with a NULL char to terminate // the string. pDot = strrchr(baseName, '.'); if (pDot != NULL) { *pDot = '\0'; } // Dump the shader debug data sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_vs.stat", baseName); DumpShaderStats(fileName, compileSetup.vs_stats); sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_gs.stat", baseName); DumpShaderStats(fileName, compileSetup.gs_stats); sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_ps.stat", baseName); DumpShaderStats(fileName, compileSetup.ps_stats); sprintf_s(fileName, GSH_DUMP_FILENAME_SIZE, "%s_cs.stat", baseName); DumpShaderStats(fileName, compileSetup.cs_stats); } if(sourceInfo.ascode) { // Write gx2 shaders to file as code if(!WriteFileShaderAsCode(&fpGFD, sourceInfo.pOutFilename, (sourceInfo.endianbugfix) ? GFD_ENDIAN_SWAP_MODE_8_IN_32 : GFD_ENDIAN_SWAP_MODE_DEFAULT, &shaders)) { retCode = EC_OUTPUTFILEERROR; } } else { if(!sourceInfo.append) { // Write gx2 shaders to file if (!WriteFileShader(&fpGFD, sourceInfo.pOutFilename, (sourceInfo.gpu == GPU_VERSION_1) ? GFD_GPU_VERSION_1 : GFD_GPU_VERSION_GPU7, (sourceInfo.endianbugfix) ? GFD_ENDIAN_SWAP_MODE_8_IN_32 : GFD_ENDIAN_SWAP_MODE_DEFAULT, (sourceInfo.outfilealign) ? GFD_ALIGN_MODE_ENABLE : GFD_ALIGN_MODE_DISABLE, 1, &shaders)) { retCode = EC_OUTPUTFILEERROR; } } else { // Append write gx2 shaders to existing file if (!AppendWriteFileShader(&fpGFD, sourceInfo.pOutFilename, (sourceInfo.gpu == GPU_VERSION_1) ? GFD_GPU_VERSION_1 : GFD_GPU_VERSION_GPU7, (sourceInfo.endianbugfix) ? GFD_ENDIAN_SWAP_MODE_8_IN_32 : GFD_ENDIAN_SWAP_MODE_DEFAULT, (sourceInfo.outfilealign) ? GFD_ALIGN_MODE_ENABLE : GFD_ALIGN_MODE_DISABLE, 1, &shaders)) { retCode = EC_OUTPUTFILEERROR; } } } } else { retCode = EC_COMPILEFAILED; } // Print warnings and errors if they exist in the info log. if (compileOutput.pInfoLog) { // printf("%s\n", compileOutput.pInfoLog); fprintf(stderr, "%s\n", compileOutput.pInfoLog); } // Cleanup fpGSH2.DestroyGX2Program3(hShaderUtils, &compileOutput.gx2Program); fpGSH2.Destroy(hShaderUtils); Destroy(&sourceInfo); FreeDLLs(&hShaderUtilDLL, &hGfdDLL); return retCode; }