// ------------------------------------------------------------------------------------------ //! 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)); }
// ------------------------------------------------------------------------------------------ //! @todo Document and cleanup me. void GLSLGenerator::GenerateShaderEntryPoint(HLSLFunction const* const EntryPoint, StringBuilder& Builder) { StringBuilder FakeEntryAssigment; StringBuilder FakeEntryInvocation; StringBuilder FakeEntryInternals, FakeEntryExternals; int LastUsedSemanticIn = 0, LastUsedSemanticOut = 0; for (auto const& Argument : EntryPoint->Arguments) { CStr const ArgumentAccessType = Argument->AccsessType == GD_HLSL_ARGUMENT_IN ? "in " : "out"; int& LastUsedSemantic = Argument->AccsessType == GD_HLSL_ARGUMENT_IN ? LastUsedSemanticIn : LastUsedSemanticOut; if (Argument->Type->Class == GD_HLSL_TYPE_CLASS_STRUCT) { if (Argument->AccsessType == GD_HLSL_ARGUMENT_IN) { FakeEntryInvocation.AppendFormat("%s(", Argument->Type->Name.CStr(), Argument->Name.CStr()); } else { FakeEntryInvocation.AppendFormat("%s, ", Argument->Name.CStr()); FakeEntryInternals.AppendFormat("\n\t%s %s;", Argument->Type->Name.CStr(), Argument->Name.CStr()); } auto const Struct = static_cast<HLSLStruct const*>(Argument->Type); for (auto const& Definition : Struct->InnerDefinitions) { auto const StructField = static_cast<HLSLVariable const*>(Definition); // auto const StructFieldSemantic = static_cast<HLSLSemantic const*>(StructField->Binding); FakeEntryExternals.AppendFormat("\nlayout(location = %d) %s %s Varying%s%s;", LastUsedSemantic , ArgumentAccessType, StructField->Type->Name.CStr(), Argument->Name.CStr(), StructField->Name.CStr()); if (Argument->AccsessType == GD_HLSL_ARGUMENT_IN) FakeEntryInvocation.AppendFormat("Varying%s%s, ", Argument->Name.CStr(), StructField->Name.CStr()); else FakeEntryAssigment.AppendFormat("\n\tVarying%s%s = %s.%s;", Argument->Name.CStr(), StructField->Name.CStr(), Argument->Name.CStr(), StructField->Name.CStr()); LastUsedSemantic += 1; } if (Argument->AccsessType == GD_HLSL_ARGUMENT_IN) { //! @todo Refactor this awful code. *(FakeEntryInvocation.CStr() + FakeEntryInvocation.GetLength() - 2) = ')'; // Removing trailing comma. *(FakeEntryInvocation.CStr() + FakeEntryInvocation.GetLength() - 1) = ','; // Removing trailing space. FakeEntryInvocation.Append(' '); } } else { auto const ArgumentSemantic = static_cast<HLSLSemantic const*>(Argument->Binding); if (Argument->AccsessType == GD_HLSL_ARGUMENT_OUT) { if (ArgumentSemantic->Semantic == GD_HLSL_SEMANTIC_SV_Position) FakeEntryAssigment.AppendFormat("\n\tgl_Position = %s;", Argument->Name.CStr()); } FakeEntryExternals.AppendFormat("\nlayout(location = %d) %s %s Varying%s;", LastUsedSemantic, ArgumentAccessType, Argument->Type->Name.CStr(), Argument->Name.CStr()); FakeEntryInvocation.AppendFormat("Varying%s, ", Argument->Name.CStr()); LastUsedSemantic += 1; } } //! @todo Refactor this awful code. *(FakeEntryInvocation.CStr() + FakeEntryInvocation.GetLength() - 2) = ')'; // Removing trailing comma. *(FakeEntryInvocation.CStr() + FakeEntryInvocation.GetLength() - 1) = ';'; // Removing trailing space. FakeEntryInvocation = Move(StringBuilder().AppendFormat("\n\t%s(%s", EntryPoint->Name.CStr(), FakeEntryInvocation.CStr())); if (EntryPoint->Type->Class == GD_HLSL_TYPE_CLASS_STRUCT) { FakeEntryInternals.AppendFormat("\n\t%s Ret;", EntryPoint->Type->Name.CStr()); FakeEntryInvocation = Move(StringBuilder().AppendFormat("\n\tRet = %s", FakeEntryInvocation.CStr() + 2)); HLSLStruct const* const ReturnStruct = static_cast<HLSLStruct const*>(EntryPoint->Type); for (auto const& Definition : ReturnStruct->InnerDefinitions) { HLSLVariable const* const ReturnStructField = static_cast<HLSLVariable const*>(Definition); // HLSLSemantic const* const ReturnStructFieldSemantic = static_cast<HLSLSemantic const*>(ReturnStructField->Binding); FakeEntryExternals.AppendFormat("\nlayout(location = %d) out %s VaryingRet%s;", LastUsedSemanticOut, ReturnStructField->Type->Name.CStr(), ReturnStructField->Name.CStr()); FakeEntryAssigment.AppendFormat("\n\tVaryingRet%s = Ret.%s;", ReturnStructField->Name.CStr(), ReturnStructField->Name.CStr()); LastUsedSemanticOut += 1; } } else if (EntryPoint->Semantic != nullptr) { FakeEntryExternals.AppendFormat("\nlayout(location = %d) out %s VaryingRet;", LastUsedSemanticOut, EntryPoint->Name.CStr()); FakeEntryInvocation = Move(StringBuilder().AppendFormat("\n\tVaryingRet = %s", FakeEntryInvocation.CStr() + 2)); } Builder.AppendFormat("\n%s\n\nvoid main()\n{\n%s\n%s\n%s}\n", FakeEntryExternals.CStr() , FakeEntryInternals.CStr(), FakeEntryInvocation.CStr(), FakeEntryAssigment.CStr()); }