LinkResult ProgramGL::link(const gl::Data &data, gl::InfoLog &infoLog, gl::Shader *fragmentShader, gl::Shader *vertexShader, const std::vector<std::string> &transformFeedbackVaryings, GLenum transformFeedbackBufferMode, int *registers, std::vector<gl::LinkedVarying> *linkedVaryings, std::map<int, gl::VariableLocation> *outputVariables) { // Reset the program state, delete the current program if one exists reset(); ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(vertexShader); ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(fragmentShader); // Generate a new program, make sure one doesn't already exist ASSERT(mProgramID == 0); mProgramID = mFunctions->createProgram(); // Attach the shaders mFunctions->attachShader(mProgramID, vertexShaderGL->getShaderID()); mFunctions->attachShader(mProgramID, fragmentShaderGL->getShaderID()); // TODO: bind attribute locations? // Link and verify mFunctions->linkProgram(mProgramID); GLint linkStatus = GL_FALSE; mFunctions->getProgramiv(mProgramID, GL_LINK_STATUS, &linkStatus); ASSERT(linkStatus == GL_TRUE); if (linkStatus == GL_FALSE) { // Linking failed, put the error into the info log GLint infoLogLength = 0; mFunctions->getProgramiv(mProgramID, GL_INFO_LOG_LENGTH, &infoLogLength); std::vector<char> buf(infoLogLength); mFunctions->getProgramInfoLog(mProgramID, infoLogLength, nullptr, &buf[0]); mFunctions->deleteProgram(mProgramID); mProgramID = 0; infoLog.append(&buf[0]); TRACE("\n%s", &buf[0]); // TODO, return GL_OUT_OF_MEMORY or just fail the link? This is an unexpected case return LinkResult(false, gl::Error(GL_NO_ERROR)); } // Query the uniform information // TODO: A lot of this logic should be done at the gl::Program level GLint activeUniformMaxLength = 0; mFunctions->getProgramiv(mProgramID, GL_ACTIVE_UNIFORM_MAX_LENGTH, &activeUniformMaxLength); std::vector<GLchar> uniformNameBuffer(activeUniformMaxLength); GLint uniformCount = 0; mFunctions->getProgramiv(mProgramID, GL_ACTIVE_UNIFORMS, &uniformCount); for (GLint i = 0; i < uniformCount; i++) { GLsizei uniformNameLength = 0; GLint uniformSize = 0; GLenum uniformType = GL_NONE; mFunctions->getActiveUniform(mProgramID, i, uniformNameBuffer.size(), &uniformNameLength, &uniformSize, &uniformType, &uniformNameBuffer[0]); std::string uniformName = gl::ParseUniformName(std::string(&uniformNameBuffer[0], uniformNameLength), nullptr); for (size_t arrayIndex = 0; arrayIndex < static_cast<size_t>(uniformSize); arrayIndex++) { std::string locationName = uniformName; if (uniformSize > 1) { locationName += "[" + Str(arrayIndex) + "]"; } GLint location = mFunctions->getUniformLocation(mProgramID, locationName.c_str()); if (location >= 0) { // Make sure the uniform index array is large enough if (static_cast<size_t>(location) >= mUniformIndex.size()) { mUniformIndex.resize(location + 1); } mUniformIndex[location] = gl::VariableLocation(uniformName, arrayIndex, static_cast<unsigned int>(mUniforms.size())); } } // ANGLE uses 0 to identify an non-array uniform. unsigned int arraySize = (uniformSize > 1) ? static_cast<unsigned int>(uniformSize) : 0; // TODO: determine uniform precision mUniforms.push_back(new gl::LinkedUniform(uniformType, GL_NONE, uniformName, arraySize, -1, sh::BlockMemberInfo::getDefaultBlockInfo())); } // Query the attribute information GLint activeAttributeMaxLength = 0; mFunctions->getProgramiv(mProgramID, GL_ACTIVE_ATTRIBUTE_MAX_LENGTH, &activeAttributeMaxLength); std::vector<GLchar> attributeNameBuffer(activeAttributeMaxLength); GLint attributeCount = 0; mFunctions->getProgramiv(mProgramID, GL_ACTIVE_ATTRIBUTES, &attributeCount); for (GLint i = 0; i < attributeCount; i++) { GLsizei attributeNameLength = 0; GLint attributeSize = 0; GLenum attributeType = GL_NONE; mFunctions->getActiveAttrib(mProgramID, i, attributeNameBuffer.size(), &attributeNameLength, &attributeSize, &attributeType, &attributeNameBuffer[0]); std::string attributeName(&attributeNameBuffer[0], attributeNameLength); // TODO: determine attribute precision setShaderAttribute(static_cast<size_t>(i), attributeType, GL_NONE, attributeName, attributeSize, i); } return LinkResult(true, gl::Error(GL_NO_ERROR)); }
gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string &hlsl, const std::string &profile, const std::vector<CompileConfig> &configs, const D3D_SHADER_MACRO *overrideMacros, ID3DBlob **outCompiledBlob, std::string *outDebugInfo) const { #if !defined(ANGLE_ENABLE_WINDOWS_STORE) ASSERT(mD3DCompilerModule); #endif ASSERT(mD3DCompileFunc); #if !defined(ANGLE_ENABLE_WINDOWS_STORE) if (gl::perfActive()) { std::string sourcePath = getTempPath(); std::string sourceText = FormatString("#line 2 \"%s\"\n\n%s", sourcePath.c_str(), hlsl.c_str()); writeFile(sourcePath.c_str(), sourceText.c_str(), sourceText.size()); } #endif const D3D_SHADER_MACRO *macros = overrideMacros ? overrideMacros : NULL; for (size_t i = 0; i < configs.size(); ++i) { ID3DBlob *errorMessage = NULL; ID3DBlob *binary = NULL; HRESULT result = mD3DCompileFunc(hlsl.c_str(), hlsl.length(), gl::g_fakepath, macros, NULL, "main", profile.c_str(), configs[i].flags, 0, &binary, &errorMessage); if (errorMessage) { std::string message = reinterpret_cast<const char*>(errorMessage->GetBufferPointer()); SafeRelease(errorMessage); infoLog.appendSanitized(message.c_str()); TRACE("\n%s", hlsl.c_str()); TRACE("\n%s", message.c_str()); if (message.find("error X3531:") != std::string::npos) // "can't unroll loops marked with loop attribute" { macros = NULL; // Disable [loop] and [flatten] // Retry without changing compiler flags i--; continue; } } if (SUCCEEDED(result)) { *outCompiledBlob = binary; #if ANGLE_SHADER_DEBUG_INFO == ANGLE_ENABLED (*outDebugInfo) += "// COMPILER INPUT HLSL BEGIN\n\n" + hlsl + "\n// COMPILER INPUT HLSL END\n"; (*outDebugInfo) += "\n\n// ASSEMBLY BEGIN\n\n"; (*outDebugInfo) += "// Compiler configuration: " + configs[i].name + "\n// Flags:\n"; for (size_t fIx = 0; fIx < ArraySize(CompilerFlagInfos); ++fIx) { const char *flagName = GetCompilerFlagName(configs[i].flags, fIx); if (flagName != nullptr) { (*outDebugInfo) += std::string("// ") + flagName + "\n"; } } (*outDebugInfo) += "// Macros:\n"; if (macros == nullptr) { (*outDebugInfo) += "// - : -\n"; } else { for (const D3D_SHADER_MACRO *mIt = macros; mIt->Name != nullptr; ++mIt) { (*outDebugInfo) += std::string("// ") + mIt->Name + " : " + mIt->Definition + "\n"; } } (*outDebugInfo) += "\n" + disassembleBinary(binary) + "\n// ASSEMBLY END\n"; #endif return gl::Error(GL_NO_ERROR); } else { if (result == E_OUTOFMEMORY) { *outCompiledBlob = NULL; return gl::Error(GL_OUT_OF_MEMORY, "HLSL compiler had an unexpected failure, result: 0x%X.", result); } infoLog.append("Warning: D3D shader compilation failed with %s flags.", configs[i].name.c_str()); if (i + 1 < configs.size()) { infoLog.append(" Retrying with %s.\n", configs[i + 1].name.c_str()); } } } // None of the configurations succeeded in compiling this shader but the compiler is still intact *outCompiledBlob = NULL; return gl::Error(GL_NO_ERROR); }
gl::Error HLSLCompiler::compileToBinary(gl::InfoLog &infoLog, const std::string &hlsl, const std::string &profile, const std::vector<CompileConfig> &configs, const D3D_SHADER_MACRO *overrideMacros, ID3DBlob **outCompiledBlob, std::string *outDebugInfo) { ASSERT(mInitialized); #if !defined(ANGLE_ENABLE_WINDOWS_STORE) ASSERT(mD3DCompilerModule); #endif ASSERT(mD3DCompileFunc); #if !defined(ANGLE_ENABLE_WINDOWS_STORE) if (gl::DebugAnnotationsActive()) { std::string sourcePath = getTempPath(); std::string sourceText = FormatString("#line 2 \"%s\"\n\n%s", sourcePath.c_str(), hlsl.c_str()); writeFile(sourcePath.c_str(), sourceText.c_str(), sourceText.size()); } #endif const D3D_SHADER_MACRO *macros = overrideMacros ? overrideMacros : nullptr; for (size_t i = 0; i < configs.size(); ++i) { ID3DBlob *errorMessage = nullptr; ID3DBlob *binary = nullptr; HRESULT result = S_OK; { TRACE_EVENT0("gpu.angle", "D3DCompile"); SCOPED_ANGLE_HISTOGRAM_TIMER("GPU.ANGLE.D3DCompileMS"); result = mD3DCompileFunc(hlsl.c_str(), hlsl.length(), gl::g_fakepath, macros, nullptr, "main", profile.c_str(), configs[i].flags, 0, &binary, &errorMessage); } if (errorMessage) { std::string message = reinterpret_cast<const char*>(errorMessage->GetBufferPointer()); SafeRelease(errorMessage); infoLog.appendSanitized(message.c_str()); TRACE("\n%s", hlsl.c_str()); TRACE("\n%s", message.c_str()); if ((message.find("error X3531:") != std::string::npos || // "can't unroll loops marked with loop attribute" message.find("error X4014:") != std::string::npos) && // "cannot have gradient operations inside loops with divergent flow control", // even though it is counter-intuitive to disable unrolling for this error, // some very long shaders have trouble deciding which loops to unroll and // turning off forced unrolls allows them to compile properly. macros != nullptr) { macros = nullptr; // Disable [loop] and [flatten] // Retry without changing compiler flags i--; continue; } } if (SUCCEEDED(result)) { *outCompiledBlob = binary; (*outDebugInfo) += "// COMPILER INPUT HLSL BEGIN\n\n" + hlsl + "\n// COMPILER INPUT HLSL END\n"; #if ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED (*outDebugInfo) += "\n\n// ASSEMBLY BEGIN\n\n"; (*outDebugInfo) += "// Compiler configuration: " + configs[i].name + "\n// Flags:\n"; for (size_t fIx = 0; fIx < ArraySize(CompilerFlagInfos); ++fIx) { if (IsCompilerFlagSet(configs[i].flags, CompilerFlagInfos[fIx].mFlag)) { (*outDebugInfo) += std::string("// ") + CompilerFlagInfos[fIx].mName + "\n"; } } (*outDebugInfo) += "// Macros:\n"; if (macros == nullptr) { (*outDebugInfo) += "// - : -\n"; } else { for (const D3D_SHADER_MACRO *mIt = macros; mIt->Name != nullptr; ++mIt) { (*outDebugInfo) += std::string("// ") + mIt->Name + " : " + mIt->Definition + "\n"; } } std::string disassembly; ANGLE_TRY(disassembleBinary(binary, &disassembly)); (*outDebugInfo) += "\n" + disassembly + "\n// ASSEMBLY END\n"; #endif // ANGLE_APPEND_ASSEMBLY_TO_SHADER_DEBUG_INFO == ANGLE_ENABLED return gl::NoError(); } if (result == E_OUTOFMEMORY) { *outCompiledBlob = nullptr; return gl::Error(GL_OUT_OF_MEMORY, "HLSL compiler had an unexpected failure, result: 0x%X.", result); } infoLog << "Warning: D3D shader compilation failed with " << configs[i].name << " flags. (" << profile << ")"; if (i + 1 < configs.size()) { infoLog << " Retrying with " << configs[i + 1].name; } } // None of the configurations succeeded in compiling this shader but the compiler is still intact *outCompiledBlob = nullptr; return gl::NoError(); }