void GLSLESProgramManagerCommon::optimiseShaderSource(GLSLESGpuProgram* gpuProgram) { if(!gpuProgram->getGLSLProgram()->getIsOptimised()) { GpuProgramType gpuType = gpuProgram->getType(); const glslopt_shader_type shaderType = (gpuType == GPT_VERTEX_PROGRAM) ? kGlslOptShaderVertex : kGlslOptShaderFragment; String shaderSource = gpuProgram->getGLSLProgram()->getSource(); glslopt_shader* shader = glslopt_optimize(mGLSLOptimiserContext, shaderType, shaderSource.c_str(), 0); std::stringstream os; if(glslopt_get_status(shader)) { // Write the current version (this forces the driver to fulfill the glsl es standard) // TODO: Need to insert the current or compatibility version. This is not future-proof os << "#version 100" << std::endl; // Default precision declaration is required in fragment and vertex shaders. os << "precision highp float;" << std::endl; os << "precision highp int;" << std::endl; String source = const_cast<char *>(glslopt_get_output(shader)); // We want to make sure that there are precision qualifiers so strip out the first line // which is the version string. We don't want duplicates since we are adding our own. size_t pos = source.find_first_of("\n"); os << source.substr(pos+1, source.length()-1); gpuProgram->getGLSLProgram()->setOptimisedSource(os.str()); gpuProgram->getGLSLProgram()->setIsOptimised(true); } else { LogManager::getSingleton().logMessage("Error from GLSL Optimiser, disabling optimisation for program: " + gpuProgram->getName()); gpuProgram->getGLSLProgram()->setParameter("use_optimiser", "false"); // LogManager::getSingleton().logMessage(String(glslopt_get_log(shader))); // LogManager::getSingleton().logMessage("Original Shader"); // LogManager::getSingleton().logMessage(gpuProgram->getGLSLProgram()->getSource()); // LogManager::getSingleton().logMessage("Optimized Shader"); // LogManager::getSingleton().logMessage(os.str()); } glslopt_shader_delete(shader); } }
void GLSLESProgramManagerCommon::optimiseShaderSource(GLSLESGpuProgram* gpuProgram) { if(!gpuProgram->getGLSLProgram()->getIsOptimised()) { GpuProgramType gpuType = gpuProgram->getType(); const glslopt_shader_type shaderType = (gpuType == GPT_VERTEX_PROGRAM) ? kGlslOptShaderVertex : kGlslOptShaderFragment; String shaderSource = gpuProgram->getGLSLProgram()->getSource(); glslopt_shader* shader = glslopt_optimize(mGLSLOptimiserContext, shaderType, shaderSource.c_str(), 0); std::stringstream os; if(glslopt_get_status(shader)) { // Write the current version (this forces the driver to fulfill the glsl es standard) // TODO: Need to insert the current or compatibility version. This is not future-proof os << "#version 100" << std::endl; // Default precision declaration is required in fragment and vertex shaders. os << "precision mediump float;" << std::endl; os << "precision highp int;" << std::endl; os << glslopt_get_output(shader); gpuProgram->getGLSLProgram()->setSource(os.str()); gpuProgram->getGLSLProgram()->setIsOptimised(true); } else { LogManager::getSingleton().logMessage("Error from GLSL Optimiser, disabling optimisation for program: " + gpuProgram->getName()); gpuProgram->getGLSLProgram()->setParameter("use_optimiser", "false"); // LogManager::getSingleton().logMessage(String(glslopt_get_log(shader))); // LogManager::getSingleton().logMessage("Original Shader"); // LogManager::getSingleton().logMessage(gpuProgram->getGLSLProgram()->getSource()); // LogManager::getSingleton().logMessage("Optimized Shader"); // LogManager::getSingleton().logMessage(os.str()); } glslopt_shader_delete(shader); } }
void OpenGLGraphicSystem::ProcessShader( BohgeEngine::ShaderProperty::ShaderCode sc, eastl::string& code ) { Utility::RemoveTargetString( code, "precision highp float;" ); Utility::RemoveTargetString( code, "precision mediump float;" ); Utility::RemoveTargetString( code, "precision lowp float;" ); Utility::RemoveTargetString( code, "highp " ); Utility::RemoveTargetString( code, "mediump " ); Utility::RemoveTargetString( code, "lowp " ); code.insert(0, "#define MAXJOINTS 128\n" ); code.insert(0, "#define MAXARRAYSIZE 256\n" ); code.insert(0, "#define _WINDOWS_\n" ); glslopt_shader_type type; switch( sc ) { case ShaderProperty::SC_VERTEX: type = kGlslOptShaderVertex; break; case ShaderProperty::SC_FRAGMENT: type = kGlslOptShaderFragment; break; default:ASSERT(false); } glslopt_shader* shader = glslopt_optimize( m_pGlslopt_ctx, type, code.c_str(), 0 ); if ( glslopt_get_status(shader) ) { code = glslopt_get_output( shader ); } else { const char* log = glslopt_get_log( shader ); DEBUGLOG("Can't optimize shader, caz %s\n", log ); } if ( ShaderProperty::SC_VERTEX == sc ) { size_t mainbegin = _FindMainFunction(code); _FixAttributeIndexingType( code, mainbegin ); } glslopt_shader_delete( shader ); }
static bool TestFile (glslopt_ctx* ctx, bool vertex, const std::string& testName, const std::string& inputPath, const std::string& hirPath, const std::string& outputPath, bool gles, bool doCheckGLSL) { std::string input; if (!ReadStringFromFile (inputPath.c_str(), input)) { printf ("\n %s: failed to read input file\n", testName.c_str()); return false; } if (doCheckGLSL) { if (!CheckGLSL (vertex, gles, testName, "input", input.c_str())) return false; } if (gles) { if (vertex) MassageVertexForGLES (input); else MassageFragmentForGLES (input); } bool res = true; glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment; glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0); bool optimizeOk = glslopt_get_status(shader); if (optimizeOk) { std::string textHir = glslopt_get_raw_output (shader); std::string textOpt = glslopt_get_output (shader); std::string outputHir; ReadStringFromFile (hirPath.c_str(), outputHir); std::string outputOpt; ReadStringFromFile (outputPath.c_str(), outputOpt); if (textHir != outputHir) { // write output FILE* f = fopen (hirPath.c_str(), "wb"); if (!f) { printf ("\n %s: can't write to IR file!\n", testName.c_str()); } else { fwrite (textHir.c_str(), 1, textHir.size(), f); fclose (f); } printf ("\n %s: does not match raw output\n", testName.c_str()); res = false; } if (textOpt != outputOpt) { // write output FILE* f = fopen (outputPath.c_str(), "wb"); if (!f) { printf ("\n %s: can't write to optimized file!\n", testName.c_str()); } else { fwrite (textOpt.c_str(), 1, textOpt.size(), f); fclose (f); } printf ("\n %s: does not match optimized output\n", testName.c_str()); res = false; } if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "raw", textHir.c_str())) res = false; if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "optimized", textOpt.c_str())) res = false; } else { printf ("\n %s: optimize error: %s\n", testName.c_str(), glslopt_get_log(shader)); res = false; } glslopt_shader_delete (shader); return res; }
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; }
static bool TestFile (glslopt_ctx* ctx, bool vertex, const std::string& testName, const std::string& inputPath, const std::string& outputPath, bool gles, bool doCheckGLSL, bool doCheckMetal) { std::string input; if (!ReadStringFromFile (inputPath.c_str(), input)) { printf ("\n %s: failed to read input file\n", testName.c_str()); return false; } if (doCheckGLSL) { if (!CheckGLSL (vertex, gles, testName, "input", input.c_str())) return false; } if (gles) { if (vertex) MassageVertexForGLES (input); else MassageFragmentForGLES (input); } bool res = true; glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment; glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0); bool optimizeOk = glslopt_get_status(shader); if (optimizeOk) { std::string textHir = glslopt_get_raw_output (shader); std::string textOpt = glslopt_get_output (shader); // append stats char buffer[1000]; int statsAlu, statsTex, statsFlow; glslopt_shader_get_stats (shader, &statsAlu, &statsTex, &statsFlow); sprintf(buffer, "\n// stats: %i alu %i tex %i flow\n", statsAlu, statsTex, statsFlow); textOpt += buffer; // append inputs const int inputCount = glslopt_shader_get_input_count (shader); if (inputCount > 0) { sprintf(buffer, "// inputs: %i\n", inputCount); textOpt += buffer; } for (int i = 0; i < inputCount; ++i) { const char* parName; glslopt_basic_type parType; glslopt_precision parPrec; int parVecSize, parMatSize, parArrSize, location; glslopt_shader_get_input_desc(shader, i, &parName, &parType, &parPrec, &parVecSize, &parMatSize, &parArrSize, &location); if (location >= 0) sprintf(buffer, "// #%i: %s (%s %s) %ix%i [%i] loc %i\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize, location); else sprintf(buffer, "// #%i: %s (%s %s) %ix%i [%i]\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize); textOpt += buffer; } // append uniforms const int uniformCount = glslopt_shader_get_uniform_count (shader); const int uniformSize = glslopt_shader_get_uniform_total_size (shader); if (uniformCount > 0) { sprintf(buffer, "// uniforms: %i (total size: %i)\n", uniformCount, uniformSize); textOpt += buffer; } for (int i = 0; i < uniformCount; ++i) { const char* parName; glslopt_basic_type parType; glslopt_precision parPrec; int parVecSize, parMatSize, parArrSize, location; glslopt_shader_get_uniform_desc(shader, i, &parName, &parType, &parPrec, &parVecSize, &parMatSize, &parArrSize, &location); if (location >= 0) sprintf(buffer, "// #%i: %s (%s %s) %ix%i [%i] loc %i\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize, location); else sprintf(buffer, "// #%i: %s (%s %s) %ix%i [%i]\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize); textOpt += buffer; } // append textures const int textureCount = glslopt_shader_get_texture_count (shader); if (textureCount > 0) { sprintf(buffer, "// textures: %i\n", textureCount); textOpt += buffer; } for (int i = 0; i < textureCount; ++i) { const char* parName; glslopt_basic_type parType; glslopt_precision parPrec; int parVecSize, parMatSize, parArrSize, location; glslopt_shader_get_texture_desc(shader, i, &parName, &parType, &parPrec, &parVecSize, &parMatSize, &parArrSize, &location); if (location >= 0) sprintf(buffer, "// #%i: %s (%s %s) %ix%i [%i] loc %i\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize, location); else sprintf(buffer, "// #%i: %s (%s %s) %ix%i [%i]\n", i, parName, kGlslPrecNames[parPrec], kGlslTypeNames[parType], parVecSize, parMatSize, parArrSize); textOpt += buffer; } std::string outputOpt; ReadStringFromFile (outputPath.c_str(), outputOpt); if (res && doCheckMetal && !CheckMetal (vertex, gles, testName, "metal", textOpt.c_str())) res = false; if (textOpt != outputOpt) { // write output FILE* f = fopen (outputPath.c_str(), "wb"); if (!f) { printf ("\n %s: can't write to optimized file!\n", testName.c_str()); } else { fwrite (textOpt.c_str(), 1, textOpt.size(), f); fclose (f); } printf ("\n %s: does not match optimized output\n", testName.c_str()); res = false; } if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "raw", textHir.c_str())) res = false; if (res && doCheckGLSL && !CheckGLSL (vertex, gles, testName, "optimized", textOpt.c_str())) res = false; } else { printf ("\n %s: optimize error: %s\n", testName.c_str(), glslopt_get_log(shader)); res = false; } glslopt_shader_delete (shader); return res; }
// ------------------------------------------------------------------------------------------ //! 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; }