//----------------------------------------------------------------------- GLSLESProgramManagerCommon::~GLSLESProgramManagerCommon(void) { #if !OGRE_NO_GLES2_GLSL_OPTIMISER if(mGLSLOptimiserContext) { glslopt_cleanup(mGLSLOptimiserContext); mGLSLOptimiserContext = NULL; } #endif }
//----------------------------------------------------------------------- GLSLESLinkProgramManager::~GLSLESLinkProgramManager(void) { // iterate through map container and delete link programs for (LinkProgramIterator currentProgram = mLinkPrograms.begin(); currentProgram != mLinkPrograms.end(); ++currentProgram) { OGRE_DELETE currentProgram->second; } #ifdef OGRE_USE_GLES2_GLSL_OPTIMISER if(mGLSLOptimiserContext) { glslopt_cleanup(mGLSLOptimiserContext); mGLSLOptimiserContext = NULL; } #endif }
extern "C" void compileShader() { // Copy the AS3 string to the C heap (must be free'd later) char *src = NULL; AS3_MallocString(src, src); bool gles = false; AS3_CopyScalarToVar(gles, gles); glslopt_ctx* gContext = glslopt_initialize(gles); int mode; AS3_GetScalarFromVar(mode, mode); const glslopt_shader_type type = mode == 0 ? kGlslOptShaderVertex : kGlslOptShaderFragment; glslopt_shader* shader = glslopt_optimize(gContext, type, src, 0); const char* optimizedShader = glslopt_get_output(shader); AS3_DeclareVar(outputstr, String); AS3_CopyCStringToVar(outputstr, optimizedShader, strlen(optimizedShader)); glslopt_cleanup(gContext); //optimize shader and/or turn sampler instructions into easily //replaceable aliases (for generating sampler variations at runtime) inline_as3( "import com.adobe.AGALOptimiser.translator.transformations.Utils;\n" "var shader:Object = null;\n" "try { shader = JSON.parse(outputstr) } catch(e:*) { }\n" "if(shader != null && shader[\"agalasm\"] != null) {\n" " if(optimize)\n" " shader = Utils.optimizeShader(shader, mode == 0)\n" " shader[\"agalasm\"] = Utils.processSamplers(shader[\"agalasm\"]);\n" " outputstr = JSON.stringify(shader, null, 1)\n" "}\n" ); AS3_ReturnAS3Var(outputstr); }
extern "C" void compileShader() { // Copy the AS3 string to the C heap (must be free'd later) char *src = NULL; AS3_MallocString(src, src); bool gles = false; AS3_CopyScalarToVar(gles, gles); glslopt_ctx* gContext = glslopt_initialize(gles); int mode; AS3_GetScalarFromVar(mode, mode); const glslopt_shader_type type = mode == 0 ? kGlslOptShaderVertex : kGlslOptShaderFragment; glslopt_shader* shader = glslopt_optimize(gContext, type, src, 0); const char* optimizedShader = glslopt_get_output(shader); AS3_DeclareVar(outputstr, String); AS3_CopyCStringToVar(outputstr, optimizedShader, strlen(optimizedShader)); glslopt_cleanup(gContext); inline_as3( "import com.adobe.AGALOptimiser.translator.transformations.Utils;\n" "if(optimize) {\n" " var shader:Object = JSON.parse(outputstr)\n" " if(shader[\"agalasm\"] != null) {\n" " shader = Utils.optimizeShader(shader, mode == 0)\n" " outputstr = JSON.stringify(shader)\n" " }\n" "}\n" ); AS3_ReturnAS3Var(outputstr); }
int main (int argc, const char** argv) { if (argc < 2) { printf ("USAGE: glsloptimizer testfolder\n"); return 1; } bool hasOpenGL = InitializeOpenGL (); glslopt_ctx* ctx[2] = { glslopt_initialize(true), glslopt_initialize(false), }; std::string baseFolder = argv[1]; clock_t time0 = clock(); static const char* kTypeName[2] = { "vertex", "fragment" }; size_t tests = 0; size_t errors = 0; for (int type = 0; type < 2; ++type) { std::string testFolder = baseFolder + "/" + kTypeName[type]; static const char* kAPIName[2] = { "OpenGL ES 2.0", "OpenGL" }; static const char* kApiIn [2] = {"-inES.txt", "-in.txt"}; static const char* kApiIR [2] = {"-irES.txt", "-ir.txt"}; static const char* kApiOut[2] = {"-outES.txt", "-out.txt"}; for (int api = 0; api < 2; ++api) { printf ("\n** running %s tests for %s...\n", kTypeName[type], kAPIName[api]); StringVector inputFiles = GetFiles (testFolder, kApiIn[api]); size_t n = inputFiles.size(); for (size_t i = 0; i < n; ++i) { std::string inname = inputFiles[i]; //if (inname != "ast-in.txt") // continue; std::string hirname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiIR[api]; std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api]; bool ok = TestFile (ctx[api], type==0, inname, testFolder + "/" + inname, testFolder + "/" + hirname, testFolder + "/" + outname, api==0, hasOpenGL); if (!ok) { ++errors; } ++tests; } } } clock_t time1 = clock(); float timeDelta = float(time1-time0)/CLOCKS_PER_SEC; if (errors != 0) printf ("\n**** %i tests (%.2fsec), %i !!!FAILED!!!\n", tests, timeDelta, errors); else printf ("\n**** %i tests (%.2fsec) succeeded\n", tests, timeDelta); // 3.25s // with builtin call linking, 3.84s for (int i = 0; i < 2; ++i) glslopt_cleanup (ctx[i]); return errors ? 1 : 0; }
bool compileGLSLShader(bx::CommandLine& _cmdLine, uint32_t _gles, const std::string& _code, bx::WriterI* _writer) { char ch = tolower(_cmdLine.findOption('\0', "type")[0]); const glslopt_shader_type type = ch == 'f' ? kGlslOptShaderFragment : (ch == 'c' ? kGlslOptShaderCompute : kGlslOptShaderVertex); glslopt_target target = kGlslTargetOpenGL; switch (_gles) { case BX_MAKEFOURCC('M', 'T', 'L', 0): target = kGlslTargetMetal; break; case 2: target = kGlslTargetOpenGLES20; break; case 3: target = kGlslTargetOpenGLES30; break; default: target = kGlslTargetOpenGL; break; } glslopt_ctx* ctx = glslopt_initialize(target); glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0); if (!glslopt_get_status(shader) ) { const char* log = glslopt_get_log(shader); int32_t source = 0; int32_t line = 0; int32_t column = 0; int32_t start = 0; int32_t end = INT32_MAX; if (3 == sscanf(log, "%u:%u(%u):", &source, &line, &column) && 0 != line) { start = bx::uint32_imax(1, line-10); end = start + 20; } printCode(_code.c_str(), line, start, end); fprintf(stderr, "Error: %s\n", log); glslopt_cleanup(ctx); return false; } const char* optimizedShader = glslopt_get_output(shader); // Trim all directives. while ('#' == *optimizedShader) { optimizedShader = bx::strnl(optimizedShader); } if (0 != _gles) { char* code = const_cast<char*>(optimizedShader); strreplace(code, "gl_FragDepthEXT", "gl_FragDepth"); strreplace(code, "texture2DLodEXT", "texture2DLod"); strreplace(code, "texture2DProjLodEXT", "texture2DProjLod"); strreplace(code, "textureCubeLodEXT", "textureCubeLod"); strreplace(code, "texture2DGradEXT", "texture2DGrad"); strreplace(code, "texture2DProjGradEXT", "texture2DProjGrad"); strreplace(code, "textureCubeGradEXT", "textureCubeGrad"); strreplace(code, "shadow2DEXT", "shadow2D"); strreplace(code, "shadow2DProjEXT", "shadow2DProj"); } UniformArray uniforms; { const char* parse = optimizedShader; while (NULL != parse && *parse != '\0') { parse = bx::strws(parse); const char* eol = strchr(parse, ';'); if (NULL != eol) { const char* qualifier = parse; parse = bx::strws(bx::strword(parse) ); if (0 == strncmp(qualifier, "attribute", 9) || 0 == strncmp(qualifier, "varying", 7) ) { // skip attributes and varyings. parse = eol + 1; continue; } if (0 != strncmp(qualifier, "uniform", 7) ) { // end if there is no uniform keyword. parse = NULL; continue; } const char* precision = NULL; const char* typen = parse; if (0 == strncmp(typen, "lowp", 4) || 0 == strncmp(typen, "mediump", 7) || 0 == strncmp(typen, "highp", 5) ) { precision = typen; typen = parse = bx::strws(bx::strword(parse) ); } BX_UNUSED(precision); char uniformType[256]; parse = bx::strword(parse); if (0 == strncmp(typen, "sampler", 7) ) { strcpy(uniformType, "int"); } else { bx::strlcpy(uniformType, typen, parse-typen+1); } const char* name = parse = bx::strws(parse); char uniformName[256]; uint8_t num = 1; const char* array = bx::strnstr(name, "[", eol-parse); if (NULL != array) { bx::strlcpy(uniformName, name, array-name+1); char arraySize[32]; const char* end = bx::strnstr(array, "]", eol-array); bx::strlcpy(arraySize, array+1, end-array); num = atoi(arraySize); } else { bx::strlcpy(uniformName, name, eol-name+1); } Uniform un; un.type = nameToUniformTypeEnum(uniformType); if (UniformType::Count != un.type) { BX_TRACE("name: %s (type %d, num %d)", uniformName, un.type, num); un.name = uniformName; un.num = num; un.regIndex = 0; un.regCount = num; uniforms.push_back(un); } parse = eol + 1; } } } uint16_t count = (uint16_t)uniforms.size(); bx::write(_writer, count); for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it) { const Uniform& un = *it; uint8_t nameSize = (uint8_t)un.name.size(); bx::write(_writer, nameSize); bx::write(_writer, un.name.c_str(), nameSize); uint8_t uniformType = un.type; bx::write(_writer, uniformType); bx::write(_writer, un.num); bx::write(_writer, un.regIndex); bx::write(_writer, un.regCount); BX_TRACE("%s, %s, %d, %d, %d" , un.name.c_str() , getUniformTypeName(un.type) , un.num , un.regIndex , un.regCount ); } uint32_t shaderSize = (uint32_t)strlen(optimizedShader); bx::write(_writer, shaderSize); bx::write(_writer, optimizedShader, shaderSize); uint8_t nul = 0; bx::write(_writer, nul); glslopt_cleanup(ctx); return true; }
static bool compile(const Options& _options, uint32_t _version, const std::string& _code, bx::WriterI* _writer) { char ch = _options.shaderType; const glslopt_shader_type type = ch == 'f' ? kGlslOptShaderFragment : (ch == 'c' ? kGlslOptShaderCompute : kGlslOptShaderVertex); glslopt_target target = kGlslTargetOpenGL; switch (_version) { case BX_MAKEFOURCC('M', 'T', 'L', 0): target = kGlslTargetMetal; break; case 2: target = kGlslTargetOpenGLES20; break; case 3: target = kGlslTargetOpenGLES30; break; default: target = kGlslTargetOpenGL; break; } glslopt_ctx* ctx = glslopt_initialize(target); glslopt_shader* shader = glslopt_optimize(ctx, type, _code.c_str(), 0); if (!glslopt_get_status(shader) ) { const char* log = glslopt_get_log(shader); int32_t source = 0; int32_t line = 0; int32_t column = 0; int32_t start = 0; int32_t end = INT32_MAX; bool found = false || 3 == sscanf(log, "%u:%u(%u):", &source, &line, &column) || 2 == sscanf(log, "(%u,%u):", &line, &column) ; if (found && 0 != line) { start = bx::uint32_imax(1, line-10); end = start + 20; } printCode(_code.c_str(), line, start, end, column); fprintf(stderr, "Error: %s\n", log); glslopt_shader_delete(shader); glslopt_cleanup(ctx); return false; } const char* optimizedShader = glslopt_get_output(shader); // Trim all directives. while ('#' == *optimizedShader) { optimizedShader = bx::strnl(optimizedShader); } { char* code = const_cast<char*>(optimizedShader); strReplace(code, "gl_FragDepthEXT", "gl_FragDepth"); strReplace(code, "texture2DLodARB", "texture2DLod"); strReplace(code, "texture2DLodEXT", "texture2DLod"); strReplace(code, "texture2DGradARB", "texture2DGrad"); strReplace(code, "texture2DGradEXT", "texture2DGrad"); strReplace(code, "textureCubeLodARB", "textureCubeLod"); strReplace(code, "textureCubeLodEXT", "textureCubeLod"); strReplace(code, "textureCubeGradARB", "textureCubeGrad"); strReplace(code, "textureCubeGradEXT", "textureCubeGrad"); strReplace(code, "texture2DProjLodARB", "texture2DProjLod"); strReplace(code, "texture2DProjLodEXT", "texture2DProjLod"); strReplace(code, "texture2DProjGradARB", "texture2DProjGrad"); strReplace(code, "texture2DProjGradEXT", "texture2DProjGrad"); strReplace(code, "shadow2DARB", "shadow2D"); strReplace(code, "shadow2DEXT", "shadow2D"); strReplace(code, "shadow2DProjARB", "shadow2DProj"); strReplace(code, "shadow2DProjEXT", "shadow2DProj"); } UniformArray uniforms; if (target != kGlslTargetMetal) { const char* parse = optimizedShader; while (NULL != parse && *parse != '\0') { parse = bx::strws(parse); const char* eol = bx::strFind(parse, ';'); if (NULL != eol) { const char* qualifier = parse; parse = bx::strws(bx::strSkipWord(parse) ); if (0 == bx::strCmp(qualifier, "attribute", 9) || 0 == bx::strCmp(qualifier, "varying", 7) || 0 == bx::strCmp(qualifier, "in", 2) || 0 == bx::strCmp(qualifier, "out", 3) ) { // skip attributes and varyings. parse = eol + 1; continue; } if (0 == bx::strCmp(parse, "tmpvar", 6) ) { // skip temporaries parse = eol + 1; continue; } if (0 != bx::strCmp(qualifier, "uniform", 7) ) { // end if there is no uniform keyword. parse = NULL; continue; } const char* precision = NULL; const char* typen = parse; if (0 == bx::strCmp(typen, "lowp", 4) || 0 == bx::strCmp(typen, "mediump", 7) || 0 == bx::strCmp(typen, "highp", 5) ) { precision = typen; typen = parse = bx::strws(bx::strSkipWord(parse) ); } BX_UNUSED(precision); char uniformType[256]; parse = bx::strSkipWord(parse); if (0 == bx::strCmp(typen, "sampler", 7) ) { bx::strCopy(uniformType, BX_COUNTOF(uniformType), "int"); } else { bx::strCopy(uniformType, int32_t(parse-typen+1), typen); } const char* name = parse = bx::strws(parse); char uniformName[256]; uint8_t num = 1; const char* array = bx::strFind(bx::StringView(name, int32_t(eol-parse) ), "["); if (NULL != array) { bx::strCopy(uniformName, int32_t(array-name+1), name); char arraySize[32]; const char* end = bx::strFind(bx::StringView(array, int32_t(eol-array) ), "]"); bx::strCopy(arraySize, int32_t(end-array), array+1); num = uint8_t(atoi(arraySize) ); } else { bx::strCopy(uniformName, int32_t(eol-name+1), name); } Uniform un; un.type = nameToUniformTypeEnum(uniformType); if (UniformType::Count != un.type) { BX_TRACE("name: %s (type %d, num %d)", uniformName, un.type, num); un.name = uniformName; un.num = num; un.regIndex = 0; un.regCount = num; uniforms.push_back(un); } parse = eol + 1; } } } else { const char* parse = bx::strFind(optimizedShader, "struct xlatMtlShaderUniform {"); const char* end = parse; if (NULL != parse) { parse += bx::strLen("struct xlatMtlShaderUniform {"); end = bx::strFind(parse, "};"); } while ( parse < end && *parse != '\0') { parse = bx::strws(parse); const char* eol = bx::strFind(parse, ';'); if (NULL != eol) { const char* typen = parse; char uniformType[256]; parse = bx::strSkipWord(parse); bx::strCopy(uniformType, int32_t(parse-typen+1), typen); const char* name = parse = bx::strws(parse); char uniformName[256]; uint8_t num = 1; const char* array = bx::strFind(bx::StringView(name, int32_t(eol-parse) ), "["); if (NULL != array) { bx::strCopy(uniformName, int32_t(array-name+1), name); char arraySize[32]; const char* arrayEnd = bx::strFind(bx::StringView(array, int32_t(eol-array) ), "]"); bx::strCopy(arraySize, int32_t(arrayEnd-array), array+1); num = uint8_t(atoi(arraySize) ); } else { bx::strCopy(uniformName, int32_t(eol-name+1), name); } Uniform un; un.type = nameToUniformTypeEnum(uniformType); if (UniformType::Count != un.type) { BX_TRACE("name: %s (type %d, num %d)", uniformName, un.type, num); un.name = uniformName; un.num = num; un.regIndex = 0; un.regCount = num; uniforms.push_back(un); } parse = eol + 1; } } } uint16_t count = (uint16_t)uniforms.size(); bx::write(_writer, count); for (UniformArray::const_iterator it = uniforms.begin(); it != uniforms.end(); ++it) { const Uniform& un = *it; uint8_t nameSize = (uint8_t)un.name.size(); bx::write(_writer, nameSize); bx::write(_writer, un.name.c_str(), nameSize); uint8_t uniformType = uint8_t(un.type); bx::write(_writer, uniformType); bx::write(_writer, un.num); bx::write(_writer, un.regIndex); bx::write(_writer, un.regCount); BX_TRACE("%s, %s, %d, %d, %d" , un.name.c_str() , getUniformTypeName(un.type) , un.num , un.regIndex , un.regCount ); } uint32_t shaderSize = (uint32_t)bx::strLen(optimizedShader); bx::write(_writer, shaderSize); bx::write(_writer, optimizedShader, shaderSize); uint8_t nul = 0; bx::write(_writer, nul); if (_options.disasm ) { std::string disasmfp = _options.outputFilePath + ".disasm"; writeFile(disasmfp.c_str(), optimizedShader, shaderSize); } glslopt_shader_delete(shader); glslopt_cleanup(ctx); return true; }
//------------------------------------------------------------------------------------------------------- void OpenGLGraphicSystem::OnFrameworkExit( float time, BohgeEngine::Framework& framework ) { glslopt_cleanup( m_pGlslopt_ctx ); m_pGlslopt_ctx = NULL; }
int main (int argc, const char** argv) { #ifndef __S3E__ if (argc < 2) { printf ("USAGE: glsloptimizer testfolder\n"); return 1; } #endif bool hasOpenGL = InitializeOpenGL (); bool hasMetal = InitializeMetal (); glslopt_ctx* ctx[3] = { glslopt_initialize(kGlslTargetOpenGLES20), glslopt_initialize(kGlslTargetOpenGLES30), glslopt_initialize(kGlslTargetOpenGL), }; glslopt_ctx* ctxMetal = glslopt_initialize(kGlslTargetMetal); #ifndef __S3E__ std::string baseFolder = argv[1]; #else std::string baseFolder = "." ; #endif clock_t time0 = clock(); // 2.39s // ralloc fix 256 initial: 1.35s static const char* kTypeName[2] = { "vertex", "fragment" }; size_t tests = 0; size_t errors = 0; int type = 0 ; // vertex std::string testFolder = baseFolder + "/" + kTypeName[type]; static const char* kAPIName[3] = { "OpenGL ES 2.0", "OpenGL ES 3.0", "OpenGL" }; static const char* kApiIn [3] = {"-inES.txt", "-inES3.txt", "-in.txt"}; static const char* kApiOut[3] = {"-outES.txt", "-outES3.txt", "-out.txt"}; static const char* kApiOutMetal[3] = {"-outESMetal.txt", "-outES3Metal.txt", "-outMetal.txt"}; int api= 0 ; // OpenGL ES 2.0 printf ("\n** running %s tests for %s...\n", kTypeName[type], kAPIName[api]); std::string inname = "ogre-inES.txt"; std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api]; std::string outnameMetal = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOutMetal[api]; const bool useMetal = (api == 1); #ifdef __S3E__ // with s3e we wont perform GLES check hasOpenGL = false ; #endif bool ok = TestFile (ctx[api], type==0, inname, testFolder + "/" + inname, testFolder + "/" + outname, api<=1, hasOpenGL, false); if (!ok) { ++errors; } // for __S3E__ we normally wont test Metal if (useMetal) { ok = TestFile (ctxMetal, type==0, inname, testFolder + "/" + inname, testFolder + "/" + outnameMetal, api==0, false, hasMetal); if (!ok) { ++errors; } } ++tests; clock_t time1 = clock(); float timeDelta = float(time1-time0)/CLOCKS_PER_SEC; if (errors != 0) printf ("\n**** %i tests (%.2fsec), %i !!!FAILED!!!\n", (int)tests, timeDelta, (int)errors); else printf ("\n**** %i tests (%.2fsec) succeeded\n", (int)tests, timeDelta); // 3.25s // with builtin call linking, 3.84s for (int i = 0; i < 2; ++i) glslopt_cleanup (ctx[i]); glslopt_cleanup (ctxMetal); CleanupGL(); return errors ? 1 : 0; }
// ------------------------------------------------------------------------------------------ //! Generates the HLSL shader Byte code. //! @param ShaderByteCodeOutputStream Output for the shader code. //! @param ShaderParsedData Parsed shader data, produced by @ref HLSLParser //! @param ShaderType Type of the shader to generate code. //! @param ShaderEntryName Name of the shader entry point. //! @param ShaderTargetPlatform Target platform for the GLSL code. void GLSLCompiler::GenerateAndCompileShader(UniquePtr<OutputStream> const& ShaderByteCodeOutputStream , HLSLScope const* const ShaderParsedData, IGraphicsShaderType const ShaderType, String const& ShaderEntryName , DHLSLShaderCrossCompilerTarget const ShaderTargetPlatform) { StringBuilder GLSLGeneratorBuilder; GLSLGenerator().GenerateShader(GLSLGeneratorBuilder, ShaderParsedData, ShaderEntryName, ShaderTargetPlatform, ShaderType); // Now we need just preprocess generated code to reduce loading time. StringBuilder GLSLPreprocessedBuilder; GLSLPreprocessedBuilder.Append(GLSLInsertations::GLSLCopyright); GLSLPreprocessedBuilder.Append(GLSLInsertations::GLSLPreambule[ShaderTargetPlatform - DHLSL_CC_TARGET_GLSL430]); // We use MCPP preprocessor because it is faster than our one. //! @todo Implement temporary files access here. String const MCPPSourceFilePath = "a.tmp";// Path::GetTemporaryFileName(); String const MCPPOutputFilePath = "b.tmp";// Path::GetTemporaryFileName(); CStr const MCPPArguments[] = { nullptr, "-P", MCPPSourceFilePath.CStr(), MCPPOutputFilePath.CStr() }; // Saving output to some temporary file to make is accessible from MCPP FileOutputStream MCPPSourceFile(/*MCPPSourceFilePath*/L"a.tmp"); MCPPSourceFile.Write(GLSLGeneratorBuilder.CStr(), GLSLGeneratorBuilder.GetLength(), sizeof(Char)); MCPPSourceFile.Close(); { // MCPP is not thread-safe. // CriticalSection static MCPPCriticalSection; // ScopedCriticalSection MCPPLock(MCPPCriticalSection); int const MCPPResult = mcpp_lib_main(GD_ARRAY_LENGTH(MCPPArguments), const_cast<char**>(&MCPPArguments[0])); GD_DEBUG_ASSERT(MCPPResult == 0, "Failed to preprocess shader source."); } // Reading MCPP result. { FileInputStream MCPPOutputFile(/*MCPPOutputFilePath*/L"b.tmp"); SizeTp const GLSLPreprocessedBuilderPos = GLSLPreprocessedBuilder.GetLength(); GLSLPreprocessedBuilder.Resize(GLSLPreprocessedBuilder.GetLength() + (UInt32) MCPPOutputFile.GetLength()); MCPPOutputFile.Read(GLSLPreprocessedBuilder.CStr() + GLSLPreprocessedBuilderPos, MCPPOutputFile.GetLength(), 1); MCPPOutputFile.Close(); GLSLPreprocessedBuilder.Append('\0'); GD_DEBUG_ASSERT(::remove(MCPPSourceFilePath.CStr()) == 0, "Failed to remove mcpp source file."); GD_DEBUG_ASSERT(::remove(MCPPOutputFilePath.CStr()) == 0, "Failed to remove mcpp output file."); } // Trying to optimize the GLSL code.. StringBuilder GLSLOptimizerBuilder; /**/if (ShaderTargetPlatform != DHLSL_CC_TARGET_GLSL430 // glsl_optimizer supports only desktop GLSL 140 version. We are using 430... && (ShaderType != IGRAPHICS_SHADER_TYPE_VERTEX && ShaderType != IGRAPHICS_SHADER_TYPE_PIXEL)) // glsl_optimizer supports only vertex and pixel shaders... { // Optimizing is supported for this shader type and target. glslopt_target static const IGraphicsGLSLangTargetsTable[] = { /* DHLSL_CC_TARGET_GLSL430 */ kGlslTargetOpenGL, /* DHLSL_CC_TARGET_GLSLES3 */ kGlslTargetOpenGLES20, /* DHLSL_CC_TARGET_GLSLES2 */ kGlslTargetOpenGLES30, }; glslopt_shader_type static const IGraphicsGLSLangShaderTypesTable[] = { /* IGRAPHICS_SHADER_TYPE_VERTEX */ kGlslOptShaderVertex, /* IGRAPHICS_SHADER_TYPE_PIXEL */ kGlslOptShaderFragment, }; auto const OptimizerContext = glslopt_initialize(IGraphicsGLSLangTargetsTable[ShaderTargetPlatform]); auto const OptimizedShader = glslopt_optimize(OptimizerContext, IGraphicsGLSLangShaderTypesTable[ShaderType - DHLSL_CC_TARGET_GLSL430] , GLSLPreprocessedBuilder.CStr(), kGlslOptionSkipPreprocessor); if (!glslopt_get_status(OptimizedShader)) { // Shader was not optimized.. // This does not mean that our shader is incorrect, just MESA-based optimizer may not parse #if 0 // our code correctly. CStr const ShaderOptimizationLog = glslopt_get_log(OptimizedShader); throw GLSLCompilerErrorException("shader optimization failed with following log: \n%s", ShaderOptimizationLog); #endif // if 0 GLSLOptimizerBuilder = Move(GLSLPreprocessedBuilder); } else { // Shader was optimized.. GLSLOptimizerBuilder.Append(glslopt_get_output(OptimizedShader)); } glslopt_shader_delete(OptimizedShader); glslopt_cleanup(OptimizerContext); } else { // Shader was not optimized - using default version. GLSLOptimizerBuilder = Move(GLSLPreprocessedBuilder); } // No we need to validate our shader.. TBuiltInResource static const GLSLangDefaultResources{ INT_MAX }; EShLanguage static const IGraphicsGLSLangShaderTypesTable[IGRAPHICS_SHADER_TYPES_COUNT] = { /* IGRAPHICS_SHADER_TYPE_VERTEX */ EShLangVertex, /* IGRAPHICS_SHADER_TYPE_PIXEL */ EShLangFragment, /* IGRAPHICS_SHADER_TYPE_GEOMETRY */ EShLangGeometry, /* IGRAPHICS_SHADER_TYPE_HULL */ EShLangTessControl, /* IGRAPHICS_SHADER_TYPE_DOMAIN */ EShLangTessEvaluation, /* IGRAPHICS_SHADER_TYPE_COMPUTE */ EShLangCompute, /* IGRAPHICS_SHADER_TYPE_UNKNOWN */ EShLangCount, }; glslang::InitializeProcess(); // Trying to compile our shader.. char const* const GLSLOptimizerBuilderPtr = GLSLOptimizerBuilder.CStr(); glslang::TShader GLSLangShader(IGraphicsGLSLangShaderTypesTable[ShaderType]); GLSLangShader.setStrings(&GLSLOptimizerBuilderPtr, 1); if (!GLSLangShader.parse(&GLSLangDefaultResources, 100, true, EShMsgDefault)) { // Failed to compile our shader. throw GLSLCompilerErrorException("GLSLang compilation returned errors: \n%s\nGenerated code:\n%s", GLSLangShader.getInfoLog(), GLSLOptimizerBuilderPtr); } else { #if 0 Debug::Log(GLSLangShader.getInfoLog()); #endif // if 0 } // Trying to link our shader program.. glslang::TProgram GLSLangProgram; GLSLangProgram.addShader(&GLSLangShader); if (!GLSLangProgram.link(EShMsgDefault)) { throw GLSLCompilerErrorException("GLSLang linking returned errors: \n%s", GLSLangProgram.getInfoLog()); } else { #if 0 Debug::Log(GLSLangProgram.getInfoLog()); #endif // if 0 } glslang::FinalizeProcess(); ShaderByteCodeOutputStream->Write(GLSLOptimizerBuilder.CStr(), GLSLOptimizerBuilder.GetLength(), sizeof(Char)); }
bool GLSLTool::Assemble( Engine &engine, const r::Material &material, const Shader::Ref &shader, const r::MaterialInputMappings &mapping, const Shader::TexCoordMapping &tcMapping, r::Shader::Pass pass, AssembleFlags flags, std::ostream &out ) { std::stringstream ss; const bool GLES = (flags & kAssemble_GLES) ? true : false; if (!(flags&(kAssemble_VertexShader|kAssemble_PixelShader))) flags |= kAssemble_VertexShader; bool vertexShader = (flags & kAssemble_VertexShader) ? true : false; if (!GLES) ss << "#version 120\r\n"; if (vertexShader) { ss << "#define VERTEX\r\n"; } else { ss << "#define FRAGMENT\r\n"; ss << "#define MATERIAL\r\n"; } switch (shader->precisionMode) { case Shader::kPrecision_Low: ss << "#define PFLOAT FIXED\r\n"; ss << "#define PFLOAT2 FIXED2\r\n"; ss << "#define PFLOAT3 FIXED3\r\n"; ss << "#define PFLOAT4 FIXED4\r\n"; ss << "#define PFLOAT4X4 FIXED4X4\r\n"; break; case Shader::kPrecision_Medium: ss << "#define PFLOAT HALF\r\n"; ss << "#define PFLOAT2 HALF2\r\n"; ss << "#define PFLOAT3 HALF3\r\n"; ss << "#define PFLOAT4 HALF4\r\n"; ss << "#define PFLOAT4X4 HALF4X4\r\n"; break; case Shader::kPrecision_High: ss << "#define PFLOAT FLOAT\r\n"; ss << "#define PFLOAT2 FLOAT2\r\n"; ss << "#define PFLOAT3 FLOAT3\r\n"; ss << "#define PFLOAT4 FLOAT4\r\n"; ss << "#define PFLOAT4X4 FLOAT4X4\r\n"; break; } if (pass != r::Shader::kPass_Preview) { if (material.skinMode == r::Material::kSkinMode_Sprite) ss << "#define SKIN_SPRITE\r\n"; if (material.skinMode == r::Material::kSkinMode_Billboard) ss << "#define SKIN_BILLBOARD\r\n"; } if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Color) > 0) ss << "#define SHADER_COLOR\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_VertexColor) > 0) ss << "#define SHADER_VERTEX_COLOR\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Vertex) > 0) ss << "#define SHADER_POSITION\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_MV) > 0) ss << "#define SHADER_MV\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_PRJ) > 0) ss << "#define SHADER_PRJ\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_MVP) > 0) ss << "#define SHADER_MVP\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_InverseMV) > 0) ss << "#define SHADER_INVERSE_MV\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_InverseMVP) > 0) ss << "#define SHADER_INVERSE_MVP\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_InversePRJ) > 0) ss << "#define SHADER_INVERSE_PRJ\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_PFXVars) > 0) ss << "#define PFX_VARS\r\n"; if (shader->MaterialSourceUsage(pass, Shader::kMaterialSource_EyeVertex) > 0) ss << "#define SHADER_EYE_VERTEX\r\n"; int numTextures = 0; for (int i = 0; i < r::kMaxTextures; ++i) { if (mapping.textures[i][0] == r::kInvalidMapping) break; // no more texture bindings switch (mapping.textures[i][0]) { case r::kMaterialTextureSource_Texture: ss << "#define T" << i << "TYPE sampler2D" << "\r\n"; break; default: break; } switch(shader->SamplerPrecisionMode(i)) { case Shader::kPrecision_Low: ss << "#define T" << i << "PRECISION lowp" << "\r\n"; break; case Shader::kPrecision_Medium: ss << "#define T" << i << "PRECISION mediump" << "\r\n"; break; case Shader::kPrecision_High: ss << "#define T" << i << "PRECISION highp" << "\r\n"; break; } ++numTextures; } if (numTextures > 0) ss << "#define TEXTURES " << numTextures << "\r\n"; const Shader::IntSet &tcUsage = shader->AttributeUsage(pass, Shader::kMaterialSource_TexCoord); int numTexCoords = (int)tcUsage.size(); if (numTexCoords > 0) { // Shaders files can read from r::kMaxTexture unique texture coordinate slots. // The set of read texcoord registers may be sparse. Additionally the engine only supports // 2 UV channels in most model data. We do some work here to map the set of read texcoord // registers onto the smallest possible set: // // A tcMod may take an input UV channel and modify it, meaning that texture channel is unique, // however there are a lot of cases where the tcMod is identity and therefore a texture coordinate // register in a shader can be mapped to a commmon register. int numTCMods = 0; for (int i = 0; i < r::kMaterialTextureSource_MaxIndices; ++i) { if (mapping.tcMods[i] == r::kInvalidMapping) break; ++numTCMods; } // numTCMods is the number of unique texture coordinates read by the pixel shader. // (which have to be generated by the vertex shader). ss << "#define TEXCOORDS " << numTCMods << "\r\n"; RAD_VERIFY(tcUsage.size() == tcMapping.size()); if (vertexShader) { Shader::IntSet tcInputs; bool genReflect = false; bool genProject = false; for (int i = 0; i < r::kMaterialTextureSource_MaxIndices; ++i) { if (mapping.tcMods[i] == r::kInvalidMapping) break; int tcIndex = (int)mapping.tcMods[i]; tcInputs.insert(material.TCUVIndex(tcIndex)); int tcMod = material.TCModFlags(tcIndex); if (tcMod & r::Material::kTCModFlag_Rotate) ss << "#define TEXCOORD" << i << "_ROTATE\r\n"; if (tcMod & r::Material::kTCModFlag_Scale) ss << "#define TEXCOORD" << i << "_SCALE\r\n"; if (tcMod & r::Material::kTCModFlag_Shift) ss << "#define TEXCOORD" << i << "_SHIFT\r\n"; if (tcMod & r::Material::kTCModFlag_Scroll) ss << "#define TEXCOORD" << i << "_SCROLL\r\n"; if (tcMod & r::Material::kTCModFlag_Turb) ss << "#define TEXCOORD" << i << "_TURB\r\n"; int tcGen = material.TCGen(tcIndex); if (tcGen == r::Material::kTCGen_EnvMap) { if (!genReflect) { genReflect = true; ss << "#define GENREFLECT\r\n"; } ss << "#define TEXCOORD" << i << "_GENREFLECT\r\n"; } else if (tcGen == r::Material::kTCGen_Projected) { if (!genProject) { genProject = true; ss << "#define GENPROJECT\r\n"; } ss << "#define TEXCOORD" << i << "_GENPROJECT\r\n"; } } ss << "#define TCINPUTS " << tcInputs.size() << "\r\n"; for (int i = 0; i < r::kMaterialTextureSource_MaxIndices; ++i) { if (mapping.tcMods[i] == r::kInvalidMapping) break; int tcIndex = (int)mapping.tcMods[i]; int uvIndex = material.TCUVIndex(tcIndex); int ofs = 0; for (Shader::IntSet::const_iterator it2 = tcInputs.begin(); it2 != tcInputs.end(); ++it2) { if (uvIndex == *it2) break; ++ofs; } RAD_VERIFY(ofs < (int)tcInputs.size()); ss << "#define TEXCOORD" << i << " tc" << ofs << "\r\n"; } } else { // fragment shader inputs used generated tc's, which may have expanded from the // the vertex shader inputs. int ofs = 0; for (Shader::IntSet::const_iterator it = tcUsage.begin(); it != tcUsage.end(); ++it, ++ofs) { ss << "#define TEXCOORD" << ofs << " tc" << tcMapping[ofs].first << "\r\n"; } } } int numColors = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Color); if (numColors > 0) ss << "#define COLORS " << numColors << "\r\n"; int numSpecularColors = std::max( shader->MaterialSourceUsage(pass, Shader::kMaterialSource_SpecularColor), shader->MaterialSourceUsage(pass, Shader::kMaterialSource_SpecularExponent) ); if (numSpecularColors > 0) ss << "#define SHADER_SPECULAR_COLORS " << numSpecularColors << "\r\n"; int numLightPos = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightPos); int numLightVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightVec); int numLightHalfVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightHalfVec); int numLightVertex = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightVertex); int numLightTanVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightTanVec); int numLightTanHalfVec = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightTanHalfVec); int numLightDiffuseColor = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightDiffuseColor); int numLightSpecularColor = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_LightSpecularColor); int numShaderNormals = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Normal); int numShaderTangents = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Tangent); int numShaderBitangents = shader->MaterialSourceUsage(pass, Shader::kMaterialSource_Bitangent); int numNormals = numShaderNormals; int numTangents = numShaderTangents; int numBitangents = numShaderBitangents; bool needTangents = vertexShader && (numLightTanVec || numLightTanHalfVec); if (needTangents) { numNormals = std::max(1, numShaderNormals); numTangents = std::max(1, numShaderTangents); numBitangents = std::max(1, numShaderBitangents); ss << "#define TANGENT_FRAME" << "\r\n"; } if (numNormals > 0) ss << "#define NORMALS " << numNormals << "\r\n"; if (numTangents > 0) ss << "#define TANGENTS " << numTangents << "\r\n"; if (numBitangents > 0) ss << "#define BITANGENTS " << numBitangents << "\r\n"; if (numShaderNormals > 0) ss << "#define NUM_SHADER_NORMALS " << numShaderNormals << "\r\n"; if (numShaderTangents > 0) ss << "#define NUM_SHADER_TANGENTS " << numShaderTangents << "\r\n"; if (numShaderBitangents > 0) ss << "#define NUM_SHADER_BITANGENTS " << numShaderBitangents << "\r\n"; int numLights = std::max( numLightPos, std::max( numLightVec, std::max( numLightHalfVec, std::max( numLightVertex, std::max( numLightTanVec, std::max( numLightTanHalfVec, std::max(numLightDiffuseColor, numLightSpecularColor) ) ) ) ) ) ); if (numLights > 0) { ss << "#define LIGHTS " << numLights << "\r\n"; if (numLightPos > 0) ss << "#define SHADER_LIGHT_POS " << numLightPos << "\r\n"; if (numLightVec > 0) ss << "#define SHADER_LIGHT_VEC " << numLightVec << "\r\n"; if (numLightHalfVec > 0) ss << "#define SHADER_LIGHT_HALFVEC " << numLightHalfVec << "\r\n"; if (numLightVertex > 0) ss << "#define SHADER_LIGHT_VERTEXPOS " << numLightVertex << "\r\n"; if (numLightTanVec > 0) ss << "#define SHADER_LIGHT_TANVEC " << numLightTanVec << "\r\n"; if (numLightTanHalfVec > 0) ss << "#define SHADER_LIGHT_TANHALFVEC " << numLightTanHalfVec << "\r\n"; if (numLightDiffuseColor > 0) ss << "#define SHADER_LIGHT_DIFFUSE_COLOR " << numLightDiffuseColor << "\r\n"; if (numLightSpecularColor > 0) ss << "#define SHADER_LIGHT_SPECULAR_COLOR " << numLightSpecularColor << "\r\n"; } if (GLES) { ss << "#define _GLES\r\n"; ss << "#define MOBILE\r\n"; } if (!Inject(engine, "@r:/Source/Shaders/Nodes/GLSL.c", ss)) return false; if (!Inject(engine, "@r:/Source/Shaders/Nodes/Common.c", ss)) return false; if (!Inject(engine, "@r:/Source/Shaders/Nodes/Shader.c", ss)) return false; std::stringstream ex; if (!ExpandIncludes(*this, ss, ex)) return false; if (flags & kAssemble_Optimize) { String in(ex.str()); glslopt_ctx *glslopt = glslopt_initialize(GLES); glslopt_shader *opt_shader = glslopt_optimize( glslopt, vertexShader ? kGlslOptShaderVertex : kGlslOptShaderFragment, in.c_str, 0 ); char szPass[32]; sprintf(szPass, "_pass%d", (int)pass); { engine.sys->files->CreateDirectory("@r:/Temp/Shaders/Logs"); String path(CStr("@r:/Temp/Shaders/Logs/")); path += shader->name; path += szPass; path += "_unoptimized"; if (vertexShader) { path += ".vert.glsl"; } else { path += ".frag.glsl"; } tools::shader_utils::SaveText(engine, path.c_str, in.c_str); } if (!glslopt_get_status(opt_shader)) { COut(C_Error) << "Error optimizing shader: " << std::endl << in << std::endl << glslopt_get_log(opt_shader) << std::endl; engine.sys->files->CreateDirectory("@r:/Temp/Shaders/Logs"); String path; for (int i = 0;; ++i) { path.PrintfASCII("@r:/Temp/Shaders/Logs/%s_error_%d.log", shader->name.get(), i); if (!engine.sys->files->FileExists(path.c_str)) { SaveText(engine, path.c_str, in.c_str); break; } } glslopt_shader_delete(opt_shader); glslopt_cleanup(glslopt); return false; } std::stringstream z; if (GLES) { switch (shader->precisionMode) { case Shader::kPrecision_Low: z << "precision lowp float;\r\n"; break; case Shader::kPrecision_Medium: z << "precision mediump float;\r\n"; break; case Shader::kPrecision_High: z << "precision highp float;\r\n"; break; } } z << glslopt_get_output(opt_shader); glslopt_shader_delete(opt_shader); glslopt_cleanup(glslopt); Copy(z, out); { engine.sys->files->CreateDirectory("@r:/Temp/Shaders/Logs"); String path(CStr("@r:/Temp/Shaders/Logs/")); path += shader->name; path += szPass; path += "_optimized"; if (vertexShader) { path += ".vert.glsl"; } else { path += ".frag.glsl"; } tools::shader_utils::SaveText(engine, path.c_str, z.str().c_str()); } } else { Copy(ex, out); } return true; }
static void term() { glslopt_cleanup(gContext); }