bool TParseContext::samplerErrorCheck(int line, const TPublicType& pType, const char* reason) { if (pType.type == EbtStruct) { if (containsSampler(*pType.userDef)) { error(line, reason, getBasicString(pType.type), "(structure contains a sampler)"); return true; } return false; } else if (IsSampler(pType.type)) { error(line, reason, getBasicString(pType.type), ""); return true; } return false; }
unsigned int UniformHLSL::declareUniformAndAssignRegister(const TType &type, const TString &name) { unsigned int registerIndex = (IsSampler(type.getBasicType()) ? mSamplerRegister : mUniformRegister); const Uniform *uniform = findUniformByName(name); ASSERT(uniform); mUniformRegisterMap[uniform->name] = registerIndex; unsigned int registerCount = HLSLVariableRegisterCount(*uniform, mOutputType); if (gl::IsSamplerType(uniform->type)) { mSamplerRegister += registerCount; } else { mUniformRegister += registerCount; } return registerIndex; }
TString UniformHLSL::uniformsHeader(ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms) { TString uniforms; for (ReferencedSymbols::const_iterator uniformIt = referencedUniforms.begin(); uniformIt != referencedUniforms.end(); uniformIt++) { const TIntermSymbol &uniform = *uniformIt->second; const TType &type = uniform.getType(); const TString &name = uniform.getSymbol(); unsigned int registerIndex = declareUniformAndAssignRegister(type, name); if (outputType == SH_HLSL11_OUTPUT && IsSampler(type.getBasicType())) // Also declare the texture { uniforms += "uniform " + SamplerString(type) + " sampler_" + DecorateUniform(name, type) + ArrayString(type) + " : register(s" + str(registerIndex) + ");\n"; uniforms += "uniform " + TextureString(type) + " texture_" + DecorateUniform(name, type) + ArrayString(type) + " : register(t" + str(registerIndex) + ");\n"; } else { const TStructure *structure = type.getStruct(); // If this is a nameless struct, we need to use its full definition, rather than its (empty) name. // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for // nameless structs in ES, as nameless structs cannot be used anywhere that layout qualifiers are // permitted. const TString &typeName = ((structure && !structure->name().empty()) ? QualifiedStructNameString(*structure, false, false) : TypeString(type)); const TString ®isterString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")"; uniforms += "uniform " + typeName + " " + DecorateUniform(name, type) + ArrayString(type) + " : " + registerString + ";\n"; } } return (uniforms.empty() ? "" : ("// Uniforms\n\n" + uniforms)); }
TGraphSymbol* TDependencyGraph::getOrCreateSymbol(TIntermSymbol* intermSymbol) { TSymbolIdMap::const_iterator iter = mSymbolIdMap.find(intermSymbol->getId()); TGraphSymbol* symbol = NULL; if (iter != mSymbolIdMap.end()) { TSymbolIdPair pair = *iter; symbol = pair.second; } else { symbol = new TGraphSymbol(intermSymbol); mAllNodes.push_back(symbol); TSymbolIdPair pair(intermSymbol->getId(), symbol); mSymbolIdMap.insert(pair); // We save all sampler symbols in a collection, so we can start graph traversals from them quickly. if (IsSampler(intermSymbol->getBasicType())) mSamplerSymbols.push_back(symbol); } return symbol; }
bool ValidateUniform(gl::Context *context, GLenum uniformType, GLint location, GLsizei count) { // Check for ES3 uniform entry points if (UniformComponentType(uniformType) == GL_UNSIGNED_INT && context->getClientVersion() < 3) { return gl::error(GL_INVALID_OPERATION, false); } LinkedUniform *uniform = NULL; if (!ValidateUniformCommonBase(context, uniformType, location, count, &uniform)) { return false; } GLenum targetBoolType = UniformBoolVectorType(uniformType); bool samplerUniformCheck = (IsSampler(uniform->type) && uniformType == GL_INT); if (!samplerUniformCheck && uniformType != uniform->type && targetBoolType != uniform->type) { return gl::error(GL_INVALID_OPERATION, false); } return true; }
bool ValidateLimitations::visitBinary(Visit, TIntermBinary* node) { // Check if loop index is modified in the loop body. validateOperation(node, node->getLeft()); // Check indexing. switch (node->getOp()) { case EOpIndexDirect: validateIndexing(node); break; case EOpIndexIndirect: #if defined(__APPLE__) // Loop unrolling is a work-around for a Mac Cg compiler bug where it // crashes when a sampler array's index is also the loop index. // Once Apple fixes this bug, we should remove the code in this CL. // See http://codereview.appspot.com/4331048/. if ((node->getLeft() != NULL) && (node->getRight() != NULL) && (node->getLeft()->getAsSymbolNode())) { TIntermSymbol* symbol = node->getLeft()->getAsSymbolNode(); if (IsSampler(symbol->getBasicType()) && symbol->isArray()) { ValidateLoopIndexExpr validate(mLoopStack); node->getRight()->traverse(&validate); if (validate.usesFloatLoopIndex()) { error(node->getLine(), "sampler array index is float loop index", "for"); } } } #endif validateIndexing(node); break; default: break; } return true; }
// // Make sure there is enough data provided to the constructor to build // something of the type of the constructor. Also returns the type of // the constructor. // // Returns true if there was an error in construction. // bool TParseContext::constructorErrorCheck(int line, TIntermNode* node, TFunction& function, TOperator op, TType* type) { *type = function.getReturnType(); bool constructingMatrix = false; switch(op) { case EOpConstructMat2: case EOpConstructMat3: case EOpConstructMat4: constructingMatrix = true; break; default: break; } // // Note: It's okay to have too many components available, but not okay to have unused // arguments. 'full' will go to true when enough args have been seen. If we loop // again, there is an extra argument, so 'overfull' will become true. // int size = 0; bool constType = true; bool full = false; bool overFull = false; bool matrixInMatrix = false; bool arrayArg = false; for (int i = 0; i < function.getParamCount(); ++i) { const TParameter& param = function.getParam(i); size += param.type->getObjectSize(); if (constructingMatrix && param.type->isMatrix()) matrixInMatrix = true; if (full) overFull = true; if (op != EOpConstructStruct && !type->isArray() && size >= type->getObjectSize()) full = true; if (param.type->getQualifier() != EvqConst) constType = false; if (param.type->isArray()) arrayArg = true; } if (constType) type->setQualifier(EvqConst); if (type->isArray() && type->getArraySize() != function.getParamCount()) { error(line, "array constructor needs one argument per array element", "constructor", ""); return true; } if (arrayArg && op != EOpConstructStruct) { error(line, "constructing from a non-dereferenced array", "constructor", ""); return true; } if (matrixInMatrix && !type->isArray()) { if (function.getParamCount() != 1) { error(line, "constructing matrix from matrix can only take one argument", "constructor", ""); return true; } } if (overFull) { error(line, "too many arguments", "constructor", ""); return true; } if (op == EOpConstructStruct && !type->isArray() && int(type->getStruct()->size()) != function.getParamCount()) { error(line, "Number of constructor parameters does not match the number of structure fields", "constructor", ""); return true; } if (!type->isMatrix() || !matrixInMatrix) { if ((op != EOpConstructStruct && size != 1 && size < type->getObjectSize()) || (op == EOpConstructStruct && size < type->getObjectSize())) { error(line, "not enough data provided for construction", "constructor", ""); return true; } } TIntermTyped *typed = node ? node->getAsTyped() : 0; if (typed == 0) { error(line, "constructor argument does not have a type", "constructor", ""); return true; } if (op != EOpConstructStruct && IsSampler(typed->getBasicType())) { error(line, "cannot convert a sampler", "constructor", ""); return true; } if (typed->getBasicType() == EbtVoid) { error(line, "cannot convert a void", "constructor", ""); return true; } return false; }
// TODO(jmadill): This is not complete. void TOutputVulkanGLSL::writeLayoutQualifier(TIntermTyped *variable) { const TType &type = variable->getType(); bool needsCustomLayout = (type.getQualifier() == EvqAttribute || type.getQualifier() == EvqFragmentOut || type.getQualifier() == EvqVertexIn || IsVarying(type.getQualifier()) || IsSampler(type.getBasicType()) || type.isInterfaceBlock()); if (!NeedsToWriteLayoutQualifier(type) && !needsCustomLayout) { return; } TInfoSinkBase &out = objSink(); const TLayoutQualifier &layoutQualifier = type.getLayoutQualifier(); // This isn't super clean, but it gets the job done. // See corresponding code in GlslangWrapper.cpp. TIntermSymbol *symbol = variable->getAsSymbolNode(); ASSERT(symbol); ImmutableString name = symbol->getName(); const char *blockStorage = nullptr; const char *matrixPacking = nullptr; // For interface blocks, use the block name instead. When the layout qualifier is being // replaced in the backend, that would be the name that's available. if (type.isInterfaceBlock()) { const TInterfaceBlock *interfaceBlock = type.getInterfaceBlock(); name = interfaceBlock->name(); TLayoutBlockStorage storage = interfaceBlock->blockStorage(); // Make sure block storage format is specified. if (storage != EbsStd430) { // Change interface block layout qualifiers to std140 for any layout that is not // explicitly set to std430. This is to comply with GL_KHR_vulkan_glsl where shared and // packed are not allowed (and std140 could be used instead) and unspecified layouts can // assume either std140 or std430 (and we choose std140 as std430 is not yet universally // supported). storage = EbsStd140; } blockStorage = getBlockStorageString(storage); } // Specify matrix packing if necessary. if (layoutQualifier.matrixPacking != EmpUnspecified) { matrixPacking = getMatrixPackingString(layoutQualifier.matrixPacking); } if (needsCustomLayout) { out << "@@ LAYOUT-" << name << "("; } else { out << "layout("; } // Output the list of qualifiers already known at this stage, i.e. everything other than // `location` and `set`/`binding`. std::string otherQualifiers = getCommonLayoutQualifiers(variable); const char *separator = ""; if (blockStorage) { out << separator << blockStorage; separator = ", "; } if (matrixPacking) { out << separator << matrixPacking; separator = ", "; } if (!otherQualifiers.empty()) { out << separator << otherQualifiers; } out << ") "; if (needsCustomLayout) { out << "@@"; } }
void UniformHLSL::uniformsHeader(TInfoSinkBase &out, ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms) { if (!referencedUniforms.empty()) { out << "// Uniforms\n\n"; } // In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is // written. They are grouped based on the combination of the HLSL texture type and // HLSL sampler type, enumerated in HLSLTextureSamplerGroup. TVector<TVector<const TIntermSymbol *>> groupedSamplerUniforms(HLSL_TEXTURE_MAX + 1); TMap<const TIntermSymbol *, TString> samplerInStructSymbolsToAPINames; for (auto &uniformIt : referencedUniforms) { // Output regular uniforms. Group sampler uniforms by type. const TIntermSymbol &uniform = *uniformIt.second; const TType &type = uniform.getType(); const TName &name = uniform.getName(); if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType())) { HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType()); groupedSamplerUniforms[group].push_back(&uniform); } else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType())) { unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr); outputHLSL4_0_FL9_3Sampler(out, type, name, registerIndex); } else { if (type.isStructureContainingSamplers()) { TVector<TIntermSymbol *> samplerSymbols; TMap<TIntermSymbol *, TString> symbolsToAPINames; unsigned int arrayOfStructsSize = type.isArray() ? type.getArraySize() : 0u; type.createSamplerSymbols("angle_" + name.getString(), name.getString(), arrayOfStructsSize, &samplerSymbols, &symbolsToAPINames); for (TIntermSymbol *sampler : samplerSymbols) { const TType &samplerType = sampler->getType(); // Will use angle_ prefix instead of regular prefix. sampler->setInternal(true); const TName &samplerName = sampler->getName(); if (outputType == SH_HLSL_4_1_OUTPUT) { HLSLTextureSamplerGroup group = TextureGroup(samplerType.getBasicType()); groupedSamplerUniforms[group].push_back(sampler); samplerInStructSymbolsToAPINames[sampler] = symbolsToAPINames[sampler]; } else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT) { unsigned int registerIndex = assignSamplerInStructUniformRegister( samplerType, symbolsToAPINames[sampler], nullptr); outputHLSL4_0_FL9_3Sampler(out, samplerType, samplerName, registerIndex); } else { ASSERT(outputType == SH_HLSL_3_0_OUTPUT); unsigned int registerIndex = assignSamplerInStructUniformRegister( samplerType, symbolsToAPINames[sampler], nullptr); outputUniform(out, samplerType, samplerName, registerIndex); } } } unsigned int registerIndex = assignUniformRegister(type, name.getString(), nullptr); outputUniform(out, type, name, registerIndex); } } if (outputType == SH_HLSL_4_1_OUTPUT) { unsigned int groupTextureRegisterIndex = 0; // TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case. ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D); for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId) { outputHLSLSamplerUniformGroup( out, HLSLTextureSamplerGroup(groupId), groupedSamplerUniforms[groupId], samplerInStructSymbolsToAPINames, &groupTextureRegisterIndex); } } }
void TStructure::createSamplerSymbols(const TString &structName, const TString &structAPIName, const unsigned int arrayOfStructsSize, TVector<TIntermSymbol *> *outputSymbols, TMap<TIntermSymbol *, TString> *outputSymbolsToAPINames) const { for (auto &field : *mFields) { const TType *fieldType = field->type(); if (IsSampler(fieldType->getBasicType())) { if (arrayOfStructsSize > 0u) { for (unsigned int arrayIndex = 0u; arrayIndex < arrayOfStructsSize; ++arrayIndex) { TStringStream name; name << structName << "_" << arrayIndex << "_" << field->name(); TIntermSymbol *symbol = new TIntermSymbol(0, name.str(), *fieldType); outputSymbols->push_back(symbol); if (outputSymbolsToAPINames) { TStringStream apiName; apiName << structAPIName << "[" << arrayIndex << "]." << field->name(); (*outputSymbolsToAPINames)[symbol] = apiName.str(); } } } else { TString symbolName = structName + "_" + field->name(); TIntermSymbol *symbol = new TIntermSymbol(0, symbolName, *fieldType); outputSymbols->push_back(symbol); if (outputSymbolsToAPINames) { TString apiName = structAPIName + "." + field->name(); (*outputSymbolsToAPINames)[symbol] = apiName; } } } else if (fieldType->isStructureContainingSamplers()) { unsigned int nestedArrayOfStructsSize = fieldType->isArray() ? fieldType->getArraySize() : 0u; if (arrayOfStructsSize > 0) { for (unsigned int arrayIndex = 0u; arrayIndex < arrayOfStructsSize; ++arrayIndex) { TStringStream fieldName; fieldName << structName << "_" << arrayIndex << "_" << field->name(); TStringStream fieldAPIName; if (outputSymbolsToAPINames) { fieldAPIName << structAPIName << "[" << arrayIndex << "]." << field->name(); } fieldType->createSamplerSymbols(fieldName.str(), fieldAPIName.str(), nestedArrayOfStructsSize, outputSymbols, outputSymbolsToAPINames); } } else { fieldType->createSamplerSymbols( structName + "_" + field->name(), structAPIName + "." + field->name(), nestedArrayOfStructsSize, outputSymbols, outputSymbolsToAPINames); } } } }
void UniformHLSL::uniformsHeader(TInfoSinkBase &out, ShShaderOutput outputType, const ReferencedSymbols &referencedUniforms) { if (!referencedUniforms.empty()) { out << "// Uniforms\n\n"; } // In the case of HLSL 4, sampler uniforms need to be grouped by type before the code is // written. They are grouped based on the combination of the HLSL texture type and // HLSL sampler type, enumerated in HLSLTextureSamplerGroup. TVector<TVector<const TIntermSymbol *>> groupedSamplerUniforms; groupedSamplerUniforms.resize(HLSL_TEXTURE_MAX + 1); for (auto &uniformIt : referencedUniforms) { // Output regular uniforms. Group sampler uniforms by type. const TIntermSymbol &uniform = *uniformIt.second; const TType &type = uniform.getType(); const TString &name = uniform.getSymbol(); if (outputType == SH_HLSL_4_1_OUTPUT && IsSampler(type.getBasicType())) { HLSLTextureSamplerGroup group = TextureGroup(type.getBasicType()); groupedSamplerUniforms[group].push_back(&uniform); } else if (outputType == SH_HLSL_4_0_FL9_3_OUTPUT && IsSampler(type.getBasicType())) { unsigned int registerIndex = declareUniformAndAssignRegister(type, name); out << "uniform " << SamplerString(type.getBasicType()) << " sampler_" << DecorateUniform(name, type) << ArrayString(type) << " : register(s" << str(registerIndex) << ");\n"; out << "uniform " << TextureString(type.getBasicType()) << " texture_" << DecorateUniform(name, type) << ArrayString(type) << " : register(t" << str(registerIndex) << ");\n"; } else { unsigned int registerIndex = declareUniformAndAssignRegister(type, name); const TStructure *structure = type.getStruct(); // If this is a nameless struct, we need to use its full definition, rather than its (empty) name. // TypeString() will invoke defineNameless in this case; qualifier prefixes are unnecessary for // nameless structs in ES, as nameless structs cannot be used anywhere that layout qualifiers are // permitted. const TString &typeName = ((structure && !structure->name().empty()) ? QualifiedStructNameString(*structure, false, false) : TypeString(type)); const TString ®isterString = TString("register(") + UniformRegisterPrefix(type) + str(registerIndex) + ")"; out << "uniform " << typeName << " " << DecorateUniform(name, type) << ArrayString(type) << " : " << registerString << ";\n"; } } if (outputType == SH_HLSL_4_1_OUTPUT) { unsigned int groupTextureRegisterIndex = 0; // TEXTURE_2D is special, index offset is assumed to be 0 and omitted in that case. ASSERT(HLSL_TEXTURE_MIN == HLSL_TEXTURE_2D); for (int groupId = HLSL_TEXTURE_MIN; groupId < HLSL_TEXTURE_MAX; ++groupId) { outputHLSLSamplerUniformGroup(out, HLSLTextureSamplerGroup(groupId), groupedSamplerUniforms[groupId], &groupTextureRegisterIndex); } } }