// ------------------------------------------------------------------------------------------ //! 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)); }