// This must handle names like "foo.bar[0]". bool ShaderValidator::FindUniformByMappedName(const std::string& mappedName, std::string* const out_userName, bool* const out_isArray) const { const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle); for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) { const sh::ShaderVariable* found; if (!itr->findInfoByMappedName(mappedName, &found, out_userName)) continue; *out_isArray = found->isArray(); return true; } const std::vector<sh::InterfaceBlock>& interfaces = *ShGetInterfaceBlocks(mHandle); for (const auto& interface : interfaces) { for (const auto& field : interface.fields) { const sh::ShaderVariable* found; if (!field.findInfoByMappedName(mappedName, &found, out_userName)) continue; *out_isArray = found->isArray(); return true; } } return false; }
static bool getSymbolInfo(ShHandle compiler, ANGLEShaderSymbolType symbolType, Vector<ANGLEShaderSymbol>& symbols) { switch (symbolType) { case SHADER_SYMBOL_TYPE_UNIFORM: { auto uniforms = ShGetUniforms(compiler); if (!uniforms) return false; for (const auto& uniform : *uniforms) getSymbolInfo(uniform, symbolType, symbols); break; } case SHADER_SYMBOL_TYPE_VARYING: { auto varyings = ShGetVaryings(compiler); if (!varyings) return false; for (const auto& varying : *varyings) getSymbolInfo(varying, symbolType, symbols); break; } case SHADER_SYMBOL_TYPE_ATTRIBUTE: { auto attributes = ShGetAttributes(compiler); if (!attributes) return false; for (const auto& attribute : *attributes) getSymbolInfo(attribute, symbolType, symbols); break; } default: ASSERT_NOT_REACHED(); return false; } return true; }
// This must handle names like "foo.bar[0]". bool ShaderValidator::FindUniformByMappedName(const std::string& mappedName, std::string* const out_userName, bool* const out_isArray) const { const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle); for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) { const sh::ShaderVariable* found; if (!itr->findInfoByMappedName(mappedName, &found, out_userName)) continue; *out_isArray = found->isArray(); return true; } const size_t dotPos = mappedName.find("."); const std::vector<sh::InterfaceBlock>& interfaces = *ShGetInterfaceBlocks(mHandle); for (const auto& interface : interfaces) { std::string mappedFieldName; const bool hasInstanceName = !interface.instanceName.empty(); // If the InterfaceBlock has an instanceName, all variables defined // within the block are qualified with the block name, as opposed // to being placed in the global scope. if (hasInstanceName) { // If mappedName has no block name prefix, skip if (std::string::npos == dotPos) continue; // If mappedName has a block name prefix that doesn't match, skip const std::string mappedInterfaceBlockName = mappedName.substr(0, dotPos); if (interface.mappedName != mappedInterfaceBlockName) continue; mappedFieldName = mappedName.substr(dotPos + 1); } else { mappedFieldName = mappedName; } for (const auto& field : interface.fields) { const sh::ShaderVariable* found; if (!field.findInfoByMappedName(mappedFieldName, &found, out_userName)) continue; if (hasInstanceName) { // Prepend the user name of the interface that matched *out_userName = interface.name + "." + *out_userName; } *out_isArray = found->isArray(); return true; } } return false; }
size_t ShaderValidator::CalcNumSamplerUniforms() const { size_t accum = 0; const std::vector<sh::Uniform>& uniforms = *ShGetUniforms(mHandle); for (auto itr = uniforms.begin(); itr != uniforms.end(); ++itr) { GLenum type = itr->type; if (type == LOCAL_GL_SAMPLER_2D || type == LOCAL_GL_SAMPLER_CUBE) { accum += itr->arraySize; } } return accum; }
static void PrintActiveVariables(ShHandle compiler) { const std::vector<sh::Uniform> *uniforms = ShGetUniforms(compiler); const std::vector<sh::Varying> *varyings = ShGetVaryings(compiler); const std::vector<sh::Attribute> *attributes = ShGetAttributes(compiler); for (size_t varCategory = 0; varCategory < 3; ++varCategory) { size_t numVars = 0; std::string varCategoryName; if (varCategory == 0) { numVars = uniforms->size(); varCategoryName = "uniform"; } else if (varCategory == 1) { numVars = varyings->size(); varCategoryName = "varying"; } else { numVars = attributes->size(); varCategoryName = "attribute"; } for (size_t i = 0; i < numVars; ++i) { const sh::ShaderVariable *var; if (varCategory == 0) var = &((*uniforms)[i]); else if (varCategory == 1) var = &((*varyings)[i]); else var = &((*attributes)[i]); PrintVariable(varCategoryName, i, *var); } printf("\n"); } }
bool ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const { if (!prev) { nsPrintfCString error("Passed in NULL prev ShaderValidator."); *out_log = error; return false; } { const std::vector<sh::Uniform>* vertPtr = ShGetUniforms(prev->mHandle); const std::vector<sh::Uniform>* fragPtr = ShGetUniforms(mHandle); if (!vertPtr || !fragPtr) { nsPrintfCString error("Could not create uniform list."); *out_log = error; return false; } for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) { for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) { if (itrVert->name != itrFrag->name) continue; if (!itrVert->isSameUniformAtLinkTime(*itrFrag)) { nsPrintfCString error("Uniform `%s`is not linkable between" " attached shaders.", itrFrag->name.c_str()); *out_log = error; return false; } break; } } } { const std::vector<sh::Varying>* vertPtr = ShGetVaryings(prev->mHandle); const std::vector<sh::Varying>* fragPtr = ShGetVaryings(mHandle); if (!vertPtr || !fragPtr) { nsPrintfCString error("Could not create varying list."); *out_log = error; return false; } nsTArray<ShVariableInfo> staticUseVaryingList; for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) { const ShVariableInfo varInfo = { itrFrag->type, (int)itrFrag->elementCount() }; static const char prefix[] = "gl_"; if (StartsWith(itrFrag->name, prefix)) { if (itrFrag->staticUse) staticUseVaryingList.AppendElement(varInfo); continue; } bool definedInVertShader = false; bool staticVertUse = false; for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) { if (itrVert->name != itrFrag->name) continue; if (!itrVert->isSameVaryingAtLinkTime(*itrFrag)) { nsPrintfCString error("Varying `%s`is not linkable between" " attached shaders.", itrFrag->name.c_str()); *out_log = error; return false; } definedInVertShader = true; staticVertUse = itrVert->staticUse; break; } if (!definedInVertShader && itrFrag->staticUse) { nsPrintfCString error("Varying `%s` has static-use in the frag" " shader, but is undeclared in the vert" " shader.", itrFrag->name.c_str()); *out_log = error; return false; } if (staticVertUse && itrFrag->staticUse) staticUseVaryingList.AppendElement(varInfo); } if (!ShCheckVariablesWithinPackingLimits(mMaxVaryingVectors, staticUseVaryingList.Elements(), staticUseVaryingList.Length())) { *out_log = "Statically used varyings do not fit within packing limits. (see" " GLSL ES Specification 1.0.17, p111)"; return false; } } return true; }
bool ShaderValidator::CanLinkTo(const ShaderValidator* prev, nsCString* const out_log) const { if (!prev) { nsPrintfCString error("Passed in NULL prev ShaderValidator."); *out_log = error; return false; } const auto shaderVersion = ShGetShaderVersion(mHandle); if (ShGetShaderVersion(prev->mHandle) != shaderVersion) { nsPrintfCString error("Vertex shader version %d does not match" " fragment shader version %d.", ShGetShaderVersion(prev->mHandle), ShGetShaderVersion(mHandle)); *out_log = error; return false; } { const std::vector<sh::Uniform>* vertPtr = ShGetUniforms(prev->mHandle); const std::vector<sh::Uniform>* fragPtr = ShGetUniforms(mHandle); if (!vertPtr || !fragPtr) { nsPrintfCString error("Could not create uniform list."); *out_log = error; return false; } for (auto itrFrag = fragPtr->begin(); itrFrag != fragPtr->end(); ++itrFrag) { for (auto itrVert = vertPtr->begin(); itrVert != vertPtr->end(); ++itrVert) { if (itrVert->name != itrFrag->name) continue; if (!itrVert->isSameUniformAtLinkTime(*itrFrag)) { nsPrintfCString error("Uniform `%s` is not linkable between" " attached shaders.", itrFrag->name.c_str()); *out_log = error; return false; } break; } } } { const auto vertVars = sh::GetInterfaceBlocks(prev->mHandle); const auto fragVars = sh::GetInterfaceBlocks(mHandle); if (!vertVars || !fragVars) { nsPrintfCString error("Could not create uniform block list."); *out_log = error; return false; } for (const auto& fragVar : *fragVars) { for (const auto& vertVar : *vertVars) { if (vertVar.name != fragVar.name) continue; if (!vertVar.isSameInterfaceBlockAtLinkTime(fragVar)) { nsPrintfCString error("Interface block `%s` is not linkable between" " attached shaders.", fragVar.name.c_str()); *out_log = error; return false; } break; } } } const auto& vertVaryings = ShGetVaryings(prev->mHandle); const auto& fragVaryings = ShGetVaryings(mHandle); if (!vertVaryings || !fragVaryings) { nsPrintfCString error("Could not create varying list."); *out_log = error; return false; } { std::vector<sh::ShaderVariable> staticUseVaryingList; for (const auto& fragVarying : *fragVaryings) { static const char prefix[] = "gl_"; if (StartsWith(fragVarying.name, prefix)) { if (fragVarying.staticUse) { staticUseVaryingList.push_back(fragVarying); } continue; } bool definedInVertShader = false; bool staticVertUse = false; for (const auto& vertVarying : *vertVaryings) { if (vertVarying.name != fragVarying.name) continue; if (!vertVarying.isSameVaryingAtLinkTime(fragVarying, shaderVersion)) { nsPrintfCString error("Varying `%s`is not linkable between" " attached shaders.", fragVarying.name.c_str()); *out_log = error; return false; } definedInVertShader = true; staticVertUse = vertVarying.staticUse; break; } if (!definedInVertShader && fragVarying.staticUse) { nsPrintfCString error("Varying `%s` has static-use in the frag" " shader, but is undeclared in the vert" " shader.", fragVarying.name.c_str()); *out_log = error; return false; } if (staticVertUse && fragVarying.staticUse) { staticUseVaryingList.push_back(fragVarying); } } if (!ShCheckVariablesWithinPackingLimits(mMaxVaryingVectors, staticUseVaryingList)) { *out_log = "Statically used varyings do not fit within packing limits. (see" " GLSL ES Specification 1.0.17, p111)"; return false; } } if (shaderVersion == 100) { // Enforce ESSL1 invariant linking rules. bool isInvariant_Position = false; bool isInvariant_PointSize = false; bool isInvariant_FragCoord = false; bool isInvariant_PointCoord = false; for (const auto& varying : *vertVaryings) { if (varying.name == "gl_Position") { isInvariant_Position = varying.isInvariant; } else if (varying.name == "gl_PointSize") { isInvariant_PointSize = varying.isInvariant; } } for (const auto& varying : *fragVaryings) { if (varying.name == "gl_FragCoord") { isInvariant_FragCoord = varying.isInvariant; } else if (varying.name == "gl_PointCoord") { isInvariant_PointCoord = varying.isInvariant; } } //// const auto fnCanBuiltInsLink = [](bool vertIsInvariant, bool fragIsInvariant) { if (vertIsInvariant) return true; return !fragIsInvariant; }; if (!fnCanBuiltInsLink(isInvariant_Position, isInvariant_FragCoord)) { *out_log = "gl_Position must be invariant if gl_FragCoord is. (see GLSL ES" " Specification 1.0.17, p39)"; return false; } if (!fnCanBuiltInsLink(isInvariant_PointSize, isInvariant_PointCoord)) { *out_log = "gl_PointSize must be invariant if gl_PointCoord is. (see GLSL ES" " Specification 1.0.17, p39)"; return false; } } return true; }
void ShaderD3D::compileToHLSL(ShHandle compiler, const std::string &source) { int compileOptions = (SH_OBJECT_CODE | SH_VARIABLES); // D3D11 Feature Level 9_3 and below do not support non-constant loop indexers in fragment // shaders. // Shader compilation will fail. To provide a better error message we can instruct the // compiler to pre-validate. if (mRenderer->getRendererLimitations().shadersRequireIndexedLoopValidation) { compileOptions |= SH_VALIDATE_LOOP_INDEXING; } std::string sourcePath; #if !defined (ANGLE_ENABLE_WINDOWS_STORE) if (gl::DebugAnnotationsActive()) { sourcePath = getTempPath(); writeFile(sourcePath.c_str(), source.c_str(), source.length()); compileOptions |= SH_LINE_DIRECTIVES; } #endif int result; if (sourcePath.empty()) { const char* sourceStrings[] = { source.c_str(), }; result = ShCompile(compiler, sourceStrings, ArraySize(sourceStrings), compileOptions); } else { const char* sourceStrings[] = { sourcePath.c_str(), source.c_str(), }; result = ShCompile(compiler, sourceStrings, ArraySize(sourceStrings), compileOptions | SH_SOURCE_PATH); } mShaderVersion = ShGetShaderVersion(compiler); if (result) { mTranslatedSource = ShGetObjectCode(compiler); #ifdef _DEBUG // Prefix hlsl shader with commented out glsl shader // Useful in diagnostics tools like pix which capture the hlsl shaders std::ostringstream hlslStream; hlslStream << "// GLSL\n"; hlslStream << "//\n"; size_t curPos = 0; while (curPos != std::string::npos) { size_t nextLine = source.find("\n", curPos); size_t len = (nextLine == std::string::npos) ? std::string::npos : (nextLine - curPos + 1); hlslStream << "// " << source.substr(curPos, len); curPos = (nextLine == std::string::npos) ? std::string::npos : (nextLine + 1); } hlslStream << "\n\n"; hlslStream << mTranslatedSource; mTranslatedSource = hlslStream.str(); #endif mUniforms = *GetShaderVariables(ShGetUniforms(compiler)); for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++) { const sh::Uniform &uniform = mUniforms[uniformIndex]; if (uniform.staticUse && !uniform.isBuiltIn()) { unsigned int index = static_cast<unsigned int>(-1); bool getUniformRegisterResult = ShGetUniformRegister(compiler, uniform.name, &index); UNUSED_ASSERTION_VARIABLE(getUniformRegisterResult); ASSERT(getUniformRegisterResult); mUniformRegisterMap[uniform.name] = index; } } mInterfaceBlocks = *GetShaderVariables(ShGetInterfaceBlocks(compiler)); for (size_t blockIndex = 0; blockIndex < mInterfaceBlocks.size(); blockIndex++) { const sh::InterfaceBlock &interfaceBlock = mInterfaceBlocks[blockIndex]; if (interfaceBlock.staticUse) { unsigned int index = static_cast<unsigned int>(-1); bool blockRegisterResult = ShGetInterfaceBlockRegister(compiler, interfaceBlock.name, &index); UNUSED_ASSERTION_VARIABLE(blockRegisterResult); ASSERT(blockRegisterResult); mInterfaceBlockRegisterMap[interfaceBlock.name] = index; } } } else { mInfoLog = ShGetInfoLog(compiler); TRACE("\n%s", mInfoLog.c_str()); } }
void Shader::compile(Compiler *compiler) { mData.mTranslatedSource.clear(); mInfoLog.clear(); mData.mShaderVersion = 100; mData.mVaryings.clear(); mData.mUniforms.clear(); mData.mInterfaceBlocks.clear(); mData.mActiveAttributes.clear(); mData.mActiveOutputVariables.clear(); ShHandle compilerHandle = compiler->getCompilerHandle(mData.mShaderType); std::stringstream sourceStream; std::string sourcePath; int additionalOptions = mImplementation->prepareSourceAndReturnOptions(&sourceStream, &sourcePath); int compileOptions = (SH_OBJECT_CODE | SH_VARIABLES | additionalOptions); // Some targets (eg D3D11 Feature Level 9_3 and below) do not support non-constant loop indexes // in fragment shaders. Shader compilation will fail. To provide a better error message we can // instruct the compiler to pre-validate. if (mRendererLimitations.shadersRequireIndexedLoopValidation) { compileOptions |= SH_VALIDATE_LOOP_INDEXING; } std::string sourceString = sourceStream.str(); std::vector<const char *> sourceCStrings; if (!sourcePath.empty()) { sourceCStrings.push_back(sourcePath.c_str()); } sourceCStrings.push_back(sourceString.c_str()); bool result = ShCompile(compilerHandle, &sourceCStrings[0], sourceCStrings.size(), compileOptions); if (!result) { mInfoLog = ShGetInfoLog(compilerHandle); TRACE("\n%s", mInfoLog.c_str()); mCompiled = false; return; } mData.mTranslatedSource = ShGetObjectCode(compilerHandle); #ifndef NDEBUG // Prefix translated shader with commented out un-translated shader. // Useful in diagnostics tools which capture the shader source. std::ostringstream shaderStream; shaderStream << "// GLSL\n"; shaderStream << "//\n"; size_t curPos = 0; while (curPos != std::string::npos) { size_t nextLine = mData.mSource.find("\n", curPos); size_t len = (nextLine == std::string::npos) ? std::string::npos : (nextLine - curPos + 1); shaderStream << "// " << mData.mSource.substr(curPos, len); curPos = (nextLine == std::string::npos) ? std::string::npos : (nextLine + 1); } shaderStream << "\n\n"; shaderStream << mData.mTranslatedSource; mData.mTranslatedSource = shaderStream.str(); #endif // Gather the shader information mData.mShaderVersion = ShGetShaderVersion(compilerHandle); mData.mVaryings = GetShaderVariables(ShGetVaryings(compilerHandle)); mData.mUniforms = GetShaderVariables(ShGetUniforms(compilerHandle)); mData.mInterfaceBlocks = GetShaderVariables(ShGetInterfaceBlocks(compilerHandle)); if (mData.mShaderType == GL_VERTEX_SHADER) { mData.mActiveAttributes = GetActiveShaderVariables(ShGetAttributes(compilerHandle)); } else { ASSERT(mData.mShaderType == GL_FRAGMENT_SHADER); // TODO(jmadill): Figure out why we only sort in the FS, and if we need to. std::sort(mData.mVaryings.begin(), mData.mVaryings.end(), CompareShaderVar); mData.mActiveOutputVariables = GetActiveShaderVariables(ShGetOutputVariables(compilerHandle)); } ASSERT(!mData.mTranslatedSource.empty()); mCompiled = mImplementation->postTranslateCompile(compiler, &mInfoLog); }
void ShaderD3D::compileToHLSL(void *compiler, const std::string &source) { // ensure the compiler is loaded initializeCompiler(); int compileOptions = (SH_OBJECT_CODE | SH_VARIABLES); std::string sourcePath; #if !defined (ANGLE_ENABLE_WINDOWS_STORE) if (gl::perfActive()) { sourcePath = getTempPath(); writeFile(sourcePath.c_str(), source.c_str(), source.length()); compileOptions |= SH_LINE_DIRECTIVES; } #endif int result; if (sourcePath.empty()) { const char* sourceStrings[] = { source.c_str(), }; result = ShCompile(compiler, sourceStrings, ArraySize(sourceStrings), compileOptions); } else { const char* sourceStrings[] = { sourcePath.c_str(), source.c_str(), }; result = ShCompile(compiler, sourceStrings, ArraySize(sourceStrings), compileOptions | SH_SOURCE_PATH); } mShaderVersion = ShGetShaderVersion(compiler); if (mShaderVersion == 300 && mRenderer->getCurrentClientVersion() < 3) { mInfoLog = "GLSL ES 3.00 is not supported by OpenGL ES 2.0 contexts"; TRACE("\n%s", mInfoLog.c_str()); } else if (result) { mHlsl = ShGetObjectCode(compiler); #ifdef _DEBUG // Prefix hlsl shader with commented out glsl shader // Useful in diagnostics tools like pix which capture the hlsl shaders std::ostringstream hlslStream; hlslStream << "// GLSL\n"; hlslStream << "//\n"; size_t curPos = 0; while (curPos != std::string::npos) { size_t nextLine = source.find("\n", curPos); size_t len = (nextLine == std::string::npos) ? std::string::npos : (nextLine - curPos + 1); hlslStream << "// " << source.substr(curPos, len); curPos = (nextLine == std::string::npos) ? std::string::npos : (nextLine + 1); } hlslStream << "\n\n"; hlslStream << mHlsl; mHlsl = hlslStream.str(); #endif mUniforms = *GetShaderVariables(ShGetUniforms(compiler)); for (size_t uniformIndex = 0; uniformIndex < mUniforms.size(); uniformIndex++) { const sh::Uniform &uniform = mUniforms[uniformIndex]; if (uniform.staticUse) { unsigned int index = -1; bool result = ShGetUniformRegister(compiler, uniform.name, &index); UNUSED_ASSERTION_VARIABLE(result); ASSERT(result); mUniformRegisterMap[uniform.name] = index; } } mInterfaceBlocks = *GetShaderVariables(ShGetInterfaceBlocks(compiler)); for (size_t blockIndex = 0; blockIndex < mInterfaceBlocks.size(); blockIndex++) { const sh::InterfaceBlock &interfaceBlock = mInterfaceBlocks[blockIndex]; if (interfaceBlock.staticUse) { unsigned int index = -1; bool result = ShGetInterfaceBlockRegister(compiler, interfaceBlock.name, &index); UNUSED_ASSERTION_VARIABLE(result); ASSERT(result); mInterfaceBlockRegisterMap[interfaceBlock.name] = index; } } } else { mInfoLog = ShGetInfoLog(compiler); TRACE("\n%s", mInfoLog.c_str()); } }