// // Copyright 2020 The ANGLE Project Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // // ProgramExecutable.cpp: Collects the interfaces common to both Programs and // ProgramPipelines in order to execute/draw with either. #include "libANGLE/ProgramExecutable.h" #include "common/string_utils.h" #include "libANGLE/Context.h" #include "libANGLE/Program.h" #include "libANGLE/Shader.h" namespace gl { namespace { bool IncludeSameArrayElement(const std::set &nameSet, const std::string &name) { std::vector subscripts; std::string baseName = ParseResourceName(name, &subscripts); for (const std::string &nameInSet : nameSet) { std::vector arrayIndices; std::string arrayName = ParseResourceName(nameInSet, &arrayIndices); if (baseName == arrayName && (subscripts.empty() || arrayIndices.empty() || subscripts == arrayIndices)) { return true; } } return false; } // Find the matching varying or field by name. const sh::ShaderVariable *FindOutputVaryingOrField(const ProgramMergedVaryings &varyings, ShaderType stage, const std::string &name) { const sh::ShaderVariable *var = nullptr; for (const ProgramVaryingRef &ref : varyings) { if (ref.frontShaderStage != stage) { continue; } const sh::ShaderVariable *varying = ref.get(stage); if (varying->name == name) { var = varying; break; } GLuint fieldIndex = 0; var = varying->findField(name, &fieldIndex); if (var != nullptr) { break; } } return var; } bool FindUsedOutputLocation(std::vector &outputLocations, unsigned int baseLocation, unsigned int elementCount, const std::vector &reservedLocations, unsigned int variableIndex) { if (baseLocation + elementCount > outputLocations.size()) { elementCount = baseLocation < outputLocations.size() ? static_cast(outputLocations.size() - baseLocation) : 0; } for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++) { const unsigned int location = baseLocation + elementIndex; if (outputLocations[location].used()) { VariableLocation locationInfo(elementIndex, variableIndex); if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) == reservedLocations.end()) { return true; } } } return false; } void AssignOutputLocations(std::vector &outputLocations, unsigned int baseLocation, unsigned int elementCount, const std::vector &reservedLocations, unsigned int variableIndex, sh::ShaderVariable &outputVariable) { if (baseLocation + elementCount > outputLocations.size()) { outputLocations.resize(baseLocation + elementCount); } for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++) { VariableLocation locationInfo(elementIndex, variableIndex); if (std::find(reservedLocations.begin(), reservedLocations.end(), locationInfo) == reservedLocations.end()) { outputVariable.location = baseLocation; const unsigned int location = baseLocation + elementIndex; outputLocations[location] = locationInfo; } } } int GetOutputLocationForLink(const ProgramAliasedBindings &fragmentOutputLocations, const sh::ShaderVariable &outputVariable) { if (outputVariable.location != -1) { return outputVariable.location; } int apiLocation = fragmentOutputLocations.getBinding(outputVariable); if (apiLocation != -1) { return apiLocation; } return -1; } bool IsOutputSecondaryForLink(const ProgramAliasedBindings &fragmentOutputIndexes, const sh::ShaderVariable &outputVariable) { if (outputVariable.index != -1) { ASSERT(outputVariable.index == 0 || outputVariable.index == 1); return (outputVariable.index == 1); } int apiIndex = fragmentOutputIndexes.getBinding(outputVariable); if (apiIndex != -1) { // Index layout qualifier from the shader takes precedence, so the index from the API is // checked only if the index was not set in the shader. This is not specified in the EXT // spec, but is specified in desktop OpenGL specs. return (apiIndex == 1); } // EXT_blend_func_extended: Outputs get index 0 by default. return false; } RangeUI AddUniforms(const ShaderMap &programs, ShaderBitSet activeShaders, std::vector &outputUniforms, const std::function &getRange) { unsigned int startRange = static_cast(outputUniforms.size()); for (ShaderType shaderType : activeShaders) { const ProgramState &programState = programs[shaderType]->getState(); const std::vector &programUniforms = programState.getUniforms(); const RangeUI uniformRange = getRange(programState); outputUniforms.insert(outputUniforms.end(), programUniforms.begin() + uniformRange.low(), programUniforms.begin() + uniformRange.high()); } return RangeUI(startRange, static_cast(outputUniforms.size())); } template void AppendActiveBlocks(ShaderType shaderType, const std::vector &blocksIn, std::vector &blocksOut) { for (const BlockT &block : blocksIn) { if (block.isActive(shaderType)) { blocksOut.push_back(block); } } } } // anonymous namespace ProgramExecutable::ProgramExecutable() : mMaxActiveAttribLocation(0), mAttributesTypeMask(0), mAttributesMask(0), mActiveSamplerRefCounts{}, mCanDrawWith(false), mYUVOutput(false), mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS), mDefaultUniformRange(0, 0), mSamplerUniformRange(0, 0), mImageUniformRange(0, 0), mAtomicCounterUniformRange(0, 0), mFragmentInoutRange(0, 0), mHasDiscard(false), mEnablesPerSampleShading(false), // [GL_EXT_geometry_shader] Table 20.22 mGeometryShaderInputPrimitiveType(PrimitiveMode::Triangles), mGeometryShaderOutputPrimitiveType(PrimitiveMode::TriangleStrip), mGeometryShaderInvocations(1), mGeometryShaderMaxVertices(0), mTessControlShaderVertices(0), mTessGenMode(GL_NONE), mTessGenSpacing(GL_NONE), mTessGenVertexOrder(GL_NONE), mTessGenPointMode(GL_NONE) { reset(true); } ProgramExecutable::ProgramExecutable(const ProgramExecutable &other) : mLinkedShaderStages(other.mLinkedShaderStages), mActiveAttribLocationsMask(other.mActiveAttribLocationsMask), mMaxActiveAttribLocation(other.mMaxActiveAttribLocation), mAttributesTypeMask(other.mAttributesTypeMask), mAttributesMask(other.mAttributesMask), mActiveSamplersMask(other.mActiveSamplersMask), mActiveSamplerRefCounts(other.mActiveSamplerRefCounts), mActiveSamplerTypes(other.mActiveSamplerTypes), mActiveSamplerYUV(other.mActiveSamplerYUV), mActiveSamplerFormats(other.mActiveSamplerFormats), mActiveSamplerShaderBits(other.mActiveSamplerShaderBits), mActiveImagesMask(other.mActiveImagesMask), mActiveImageShaderBits(other.mActiveImageShaderBits), mCanDrawWith(other.mCanDrawWith), mOutputVariables(other.mOutputVariables), mOutputLocations(other.mOutputLocations), mSecondaryOutputLocations(other.mSecondaryOutputLocations), mYUVOutput(other.mYUVOutput), mProgramInputs(other.mProgramInputs), mLinkedTransformFeedbackVaryings(other.mLinkedTransformFeedbackVaryings), mTransformFeedbackStrides(other.mTransformFeedbackStrides), mTransformFeedbackBufferMode(other.mTransformFeedbackBufferMode), mUniforms(other.mUniforms), mDefaultUniformRange(other.mDefaultUniformRange), mSamplerUniformRange(other.mSamplerUniformRange), mImageUniformRange(other.mImageUniformRange), mAtomicCounterUniformRange(other.mAtomicCounterUniformRange), mUniformBlocks(other.mUniformBlocks), mActiveUniformBlockBindings(other.mActiveUniformBlockBindings), mAtomicCounterBuffers(other.mAtomicCounterBuffers), mShaderStorageBlocks(other.mShaderStorageBlocks), mFragmentInoutRange(other.mFragmentInoutRange), mHasDiscard(other.mHasDiscard), mEnablesPerSampleShading(other.mEnablesPerSampleShading), mAdvancedBlendEquations(other.mAdvancedBlendEquations) { reset(true); } ProgramExecutable::~ProgramExecutable() = default; void ProgramExecutable::reset(bool clearInfoLog) { if (clearInfoLog) { resetInfoLog(); } mActiveAttribLocationsMask.reset(); mAttributesTypeMask.reset(); mAttributesMask.reset(); mMaxActiveAttribLocation = 0; mActiveSamplersMask.reset(); mActiveSamplerRefCounts = {}; mActiveSamplerTypes.fill(TextureType::InvalidEnum); mActiveSamplerYUV.reset(); mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum); mActiveImagesMask.reset(); mProgramInputs.clear(); mLinkedTransformFeedbackVaryings.clear(); mTransformFeedbackStrides.clear(); mUniforms.clear(); mUniformBlocks.clear(); mActiveUniformBlockBindings.reset(); mShaderStorageBlocks.clear(); mAtomicCounterBuffers.clear(); mOutputVariables.clear(); mOutputLocations.clear(); mActiveOutputVariablesMask.reset(); mSecondaryOutputLocations.clear(); mYUVOutput = false; mSamplerBindings.clear(); mImageBindings.clear(); mDefaultUniformRange = RangeUI(0, 0); mSamplerUniformRange = RangeUI(0, 0); mImageUniformRange = RangeUI(0, 0); mAtomicCounterUniformRange = RangeUI(0, 0); mFragmentInoutRange = RangeUI(0, 0); mHasDiscard = false; mEnablesPerSampleShading = false; mAdvancedBlendEquations.reset(); mGeometryShaderInputPrimitiveType = PrimitiveMode::Triangles; mGeometryShaderOutputPrimitiveType = PrimitiveMode::TriangleStrip; mGeometryShaderInvocations = 1; mGeometryShaderMaxVertices = 0; mTessControlShaderVertices = 0; mTessGenMode = GL_NONE; mTessGenSpacing = GL_NONE; mTessGenVertexOrder = GL_NONE; mTessGenPointMode = GL_NONE; mOutputVariableTypes.clear(); mDrawBufferTypeMask.reset(); } void ProgramExecutable::load(bool isSeparable, gl::BinaryInputStream *stream) { static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8, "Too many vertex attribs for mask: All bits of mAttributesTypeMask types and " "mask fit into 32 bits each"); mAttributesTypeMask = gl::ComponentTypeMask(stream->readInt()); mAttributesMask = gl::AttributesMask(stream->readInt()); mActiveAttribLocationsMask = gl::AttributesMask(stream->readInt()); mMaxActiveAttribLocation = stream->readInt(); unsigned int fragmentInoutRangeLow = stream->readInt(); unsigned int fragmentInoutRangeHigh = stream->readInt(); mFragmentInoutRange = RangeUI(fragmentInoutRangeLow, fragmentInoutRangeHigh); mHasDiscard = stream->readBool(); mEnablesPerSampleShading = stream->readBool(); static_assert(sizeof(mAdvancedBlendEquations.bits()) == sizeof(uint32_t)); mAdvancedBlendEquations = BlendEquationBitSet(stream->readInt()); mLinkedShaderStages = ShaderBitSet(stream->readInt()); mGeometryShaderInputPrimitiveType = stream->readEnum(); mGeometryShaderOutputPrimitiveType = stream->readEnum(); mGeometryShaderInvocations = stream->readInt(); mGeometryShaderMaxVertices = stream->readInt(); mTessControlShaderVertices = stream->readInt(); mTessGenMode = stream->readInt(); mTessGenSpacing = stream->readInt(); mTessGenVertexOrder = stream->readInt(); mTessGenPointMode = stream->readInt(); size_t attribCount = stream->readInt(); ASSERT(getProgramInputs().empty()); for (size_t attribIndex = 0; attribIndex < attribCount; ++attribIndex) { sh::ShaderVariable attrib; LoadShaderVar(stream, &attrib); attrib.location = stream->readInt(); mProgramInputs.push_back(attrib); } size_t uniformCount = stream->readInt(); ASSERT(getUniforms().empty()); for (size_t uniformIndex = 0; uniformIndex < uniformCount; ++uniformIndex) { LinkedUniform uniform; LoadShaderVar(stream, &uniform); uniform.bufferIndex = stream->readInt(); LoadBlockMemberInfo(stream, &uniform.blockInfo); stream->readIntVector(&uniform.outerArraySizes); uniform.outerArrayOffset = stream->readInt(); uniform.typeInfo = &GetUniformTypeInfo(uniform.type); // Active shader info for (ShaderType shaderType : gl::AllShaderTypes()) { uniform.setActive(shaderType, stream->readBool()); } mUniforms.push_back(uniform); } size_t uniformBlockCount = stream->readInt(); ASSERT(getUniformBlocks().empty()); for (size_t uniformBlockIndex = 0; uniformBlockIndex < uniformBlockCount; ++uniformBlockIndex) { InterfaceBlock uniformBlock; LoadInterfaceBlock(stream, &uniformBlock); mUniformBlocks.push_back(uniformBlock); mActiveUniformBlockBindings.set(uniformBlockIndex, uniformBlock.binding != 0); } size_t shaderStorageBlockCount = stream->readInt(); ASSERT(getShaderStorageBlocks().empty()); for (size_t shaderStorageBlockIndex = 0; shaderStorageBlockIndex < shaderStorageBlockCount; ++shaderStorageBlockIndex) { InterfaceBlock shaderStorageBlock; LoadInterfaceBlock(stream, &shaderStorageBlock); mShaderStorageBlocks.push_back(shaderStorageBlock); } size_t atomicCounterBufferCount = stream->readInt(); ASSERT(getAtomicCounterBuffers().empty()); for (size_t bufferIndex = 0; bufferIndex < atomicCounterBufferCount; ++bufferIndex) { AtomicCounterBuffer atomicCounterBuffer; LoadShaderVariableBuffer(stream, &atomicCounterBuffer); mAtomicCounterBuffers.push_back(atomicCounterBuffer); } size_t transformFeedbackVaryingCount = stream->readInt(); ASSERT(mLinkedTransformFeedbackVaryings.empty()); for (size_t transformFeedbackVaryingIndex = 0; transformFeedbackVaryingIndex < transformFeedbackVaryingCount; ++transformFeedbackVaryingIndex) { sh::ShaderVariable varying; stream->readIntVector(&varying.arraySizes); stream->readInt(&varying.type); stream->readString(&varying.name); GLuint arrayIndex = stream->readInt(); mLinkedTransformFeedbackVaryings.emplace_back(varying, arrayIndex); } mTransformFeedbackBufferMode = stream->readInt(); size_t outputCount = stream->readInt(); ASSERT(getOutputVariables().empty()); for (size_t outputIndex = 0; outputIndex < outputCount; ++outputIndex) { sh::ShaderVariable output; LoadShaderVar(stream, &output); output.location = stream->readInt(); output.index = stream->readInt(); mOutputVariables.push_back(output); } size_t outputVarCount = stream->readInt(); ASSERT(getOutputLocations().empty()); for (size_t outputIndex = 0; outputIndex < outputVarCount; ++outputIndex) { VariableLocation locationData; stream->readInt(&locationData.arrayIndex); stream->readInt(&locationData.index); stream->readBool(&locationData.ignored); mOutputLocations.push_back(locationData); } mActiveOutputVariablesMask = gl::DrawBufferMask(stream->readInt()); size_t outputTypeCount = stream->readInt(); for (size_t outputIndex = 0; outputIndex < outputTypeCount; ++outputIndex) { mOutputVariableTypes.push_back(stream->readInt()); } static_assert(IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t), "All bits of mDrawBufferTypeMask and mActiveOutputVariables types and mask fit " "into 32 bits each"); mDrawBufferTypeMask = gl::ComponentTypeMask(stream->readInt()); stream->readBool(&mYUVOutput); size_t secondaryOutputVarCount = stream->readInt(); ASSERT(getSecondaryOutputLocations().empty()); for (size_t outputIndex = 0; outputIndex < secondaryOutputVarCount; ++outputIndex) { VariableLocation locationData; stream->readInt(&locationData.arrayIndex); stream->readInt(&locationData.index); stream->readBool(&locationData.ignored); mSecondaryOutputLocations.push_back(locationData); } unsigned int defaultUniformRangeLow = stream->readInt(); unsigned int defaultUniformRangeHigh = stream->readInt(); mDefaultUniformRange = RangeUI(defaultUniformRangeLow, defaultUniformRangeHigh); unsigned int samplerRangeLow = stream->readInt(); unsigned int samplerRangeHigh = stream->readInt(); mSamplerUniformRange = RangeUI(samplerRangeLow, samplerRangeHigh); size_t samplerCount = stream->readInt(); for (size_t samplerIndex = 0; samplerIndex < samplerCount; ++samplerIndex) { TextureType textureType = stream->readEnum(); GLenum samplerType = stream->readInt(); SamplerFormat format = stream->readEnum(); size_t bindingCount = stream->readInt(); mSamplerBindings.emplace_back(textureType, samplerType, format, bindingCount); } unsigned int imageRangeLow = stream->readInt(); unsigned int imageRangeHigh = stream->readInt(); mImageUniformRange = RangeUI(imageRangeLow, imageRangeHigh); size_t imageBindingCount = stream->readInt(); for (size_t imageIndex = 0; imageIndex < imageBindingCount; ++imageIndex) { size_t elementCount = stream->readInt(); TextureType textureType = static_cast(stream->readInt()); ImageBinding imageBinding(elementCount, textureType); for (size_t elementIndex = 0; elementIndex < elementCount; ++elementIndex) { imageBinding.boundImageUnits[elementIndex] = stream->readInt(); } mImageBindings.emplace_back(imageBinding); } unsigned int atomicCounterRangeLow = stream->readInt(); unsigned int atomicCounterRangeHigh = stream->readInt(); mAtomicCounterUniformRange = RangeUI(atomicCounterRangeLow, atomicCounterRangeHigh); // These values are currently only used by PPOs, so only load them when the program is marked // separable to save memory. if (isSeparable) { for (ShaderType shaderType : mLinkedShaderStages) { mLinkedOutputVaryings[shaderType].resize(stream->readInt()); for (sh::ShaderVariable &variable : mLinkedOutputVaryings[shaderType]) { LoadShaderVar(stream, &variable); } mLinkedInputVaryings[shaderType].resize(stream->readInt()); for (sh::ShaderVariable &variable : mLinkedInputVaryings[shaderType]) { LoadShaderVar(stream, &variable); } mLinkedUniforms[shaderType].resize(stream->readInt()); for (sh::ShaderVariable &variable : mLinkedUniforms[shaderType]) { LoadShaderVar(stream, &variable); } mLinkedUniformBlocks[shaderType].resize(stream->readInt()); for (sh::InterfaceBlock &shaderStorageBlock : mLinkedUniformBlocks[shaderType]) { LoadShInterfaceBlock(stream, &shaderStorageBlock); } mLinkedShaderVersions[shaderType] = stream->readInt(); } } } void ProgramExecutable::save(bool isSeparable, gl::BinaryOutputStream *stream) const { static_assert(MAX_VERTEX_ATTRIBS * 2 <= sizeof(uint32_t) * 8, "All bits of mAttributesTypeMask types and mask fit into 32 bits each"); stream->writeInt(static_cast(mAttributesTypeMask.to_ulong())); stream->writeInt(static_cast(mAttributesMask.to_ulong())); stream->writeInt(static_cast(mActiveAttribLocationsMask.to_ulong())); stream->writeInt(mMaxActiveAttribLocation); stream->writeInt(mFragmentInoutRange.low()); stream->writeInt(mFragmentInoutRange.high()); stream->writeBool(mHasDiscard); stream->writeBool(mEnablesPerSampleShading); stream->writeInt(mAdvancedBlendEquations.bits()); stream->writeInt(mLinkedShaderStages.bits()); ASSERT(mGeometryShaderInvocations >= 1 && mGeometryShaderMaxVertices >= 0); stream->writeEnum(mGeometryShaderInputPrimitiveType); stream->writeEnum(mGeometryShaderOutputPrimitiveType); stream->writeInt(mGeometryShaderInvocations); stream->writeInt(mGeometryShaderMaxVertices); stream->writeInt(mTessControlShaderVertices); stream->writeInt(mTessGenMode); stream->writeInt(mTessGenSpacing); stream->writeInt(mTessGenVertexOrder); stream->writeInt(mTessGenPointMode); stream->writeInt(getProgramInputs().size()); for (const sh::ShaderVariable &attrib : getProgramInputs()) { WriteShaderVar(stream, attrib); stream->writeInt(attrib.location); } stream->writeInt(getUniforms().size()); for (const LinkedUniform &uniform : getUniforms()) { WriteShaderVar(stream, uniform); stream->writeInt(uniform.bufferIndex); WriteBlockMemberInfo(stream, uniform.blockInfo); stream->writeIntVector(uniform.outerArraySizes); stream->writeInt(uniform.outerArrayOffset); // Active shader info for (ShaderType shaderType : gl::AllShaderTypes()) { stream->writeBool(uniform.isActive(shaderType)); } } stream->writeInt(getUniformBlocks().size()); for (const InterfaceBlock &uniformBlock : getUniformBlocks()) { WriteInterfaceBlock(stream, uniformBlock); } stream->writeInt(getShaderStorageBlocks().size()); for (const InterfaceBlock &shaderStorageBlock : getShaderStorageBlocks()) { WriteInterfaceBlock(stream, shaderStorageBlock); } stream->writeInt(mAtomicCounterBuffers.size()); for (const AtomicCounterBuffer &atomicCounterBuffer : getAtomicCounterBuffers()) { WriteShaderVariableBuffer(stream, atomicCounterBuffer); } stream->writeInt(getLinkedTransformFeedbackVaryings().size()); for (const auto &var : getLinkedTransformFeedbackVaryings()) { stream->writeIntVector(var.arraySizes); stream->writeInt(var.type); stream->writeString(var.name); stream->writeIntOrNegOne(var.arrayIndex); } stream->writeInt(getTransformFeedbackBufferMode()); stream->writeInt(getOutputVariables().size()); for (const sh::ShaderVariable &output : getOutputVariables()) { WriteShaderVar(stream, output); stream->writeInt(output.location); stream->writeInt(output.index); } stream->writeInt(getOutputLocations().size()); for (const auto &outputVar : getOutputLocations()) { stream->writeInt(outputVar.arrayIndex); stream->writeIntOrNegOne(outputVar.index); stream->writeBool(outputVar.ignored); } stream->writeInt(static_cast(mActiveOutputVariablesMask.to_ulong())); stream->writeInt(mOutputVariableTypes.size()); for (const auto &outputVariableType : mOutputVariableTypes) { stream->writeInt(outputVariableType); } static_assert( IMPLEMENTATION_MAX_DRAW_BUFFERS * 2 <= 8 * sizeof(uint32_t), "All bits of mDrawBufferTypeMask and mActiveOutputVariables can be contained in 32 bits"); stream->writeInt(static_cast(mDrawBufferTypeMask.to_ulong())); stream->writeBool(mYUVOutput); stream->writeInt(getSecondaryOutputLocations().size()); for (const auto &outputVar : getSecondaryOutputLocations()) { stream->writeInt(outputVar.arrayIndex); stream->writeIntOrNegOne(outputVar.index); stream->writeBool(outputVar.ignored); } stream->writeInt(getDefaultUniformRange().low()); stream->writeInt(getDefaultUniformRange().high()); stream->writeInt(getSamplerUniformRange().low()); stream->writeInt(getSamplerUniformRange().high()); stream->writeInt(getSamplerBindings().size()); for (const auto &samplerBinding : getSamplerBindings()) { stream->writeEnum(samplerBinding.textureType); stream->writeInt(samplerBinding.samplerType); stream->writeEnum(samplerBinding.format); stream->writeInt(samplerBinding.boundTextureUnits.size()); } stream->writeInt(getImageUniformRange().low()); stream->writeInt(getImageUniformRange().high()); stream->writeInt(getImageBindings().size()); for (const auto &imageBinding : getImageBindings()) { stream->writeInt(imageBinding.boundImageUnits.size()); stream->writeInt(static_cast(imageBinding.textureType)); for (size_t i = 0; i < imageBinding.boundImageUnits.size(); ++i) { stream->writeInt(imageBinding.boundImageUnits[i]); } } stream->writeInt(getAtomicCounterUniformRange().low()); stream->writeInt(getAtomicCounterUniformRange().high()); // These values are currently only used by PPOs, so only save them when the program is marked // separable to save memory. if (isSeparable) { for (ShaderType shaderType : mLinkedShaderStages) { stream->writeInt(mLinkedOutputVaryings[shaderType].size()); for (const sh::ShaderVariable &shaderVariable : mLinkedOutputVaryings[shaderType]) { WriteShaderVar(stream, shaderVariable); } stream->writeInt(mLinkedInputVaryings[shaderType].size()); for (const sh::ShaderVariable &shaderVariable : mLinkedInputVaryings[shaderType]) { WriteShaderVar(stream, shaderVariable); } stream->writeInt(mLinkedUniforms[shaderType].size()); for (const sh::ShaderVariable &shaderVariable : mLinkedUniforms[shaderType]) { WriteShaderVar(stream, shaderVariable); } stream->writeInt(mLinkedUniformBlocks[shaderType].size()); for (const sh::InterfaceBlock &shaderStorageBlock : mLinkedUniformBlocks[shaderType]) { WriteShInterfaceBlock(stream, shaderStorageBlock); } stream->writeInt(mLinkedShaderVersions[shaderType]); } } } int ProgramExecutable::getInfoLogLength() const { return static_cast(mInfoLog.getLength()); } void ProgramExecutable::getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const { return mInfoLog.getLog(bufSize, length, infoLog); } std::string ProgramExecutable::getInfoLogString() const { return mInfoLog.str(); } bool ProgramExecutable::isAttribLocationActive(size_t attribLocation) const { ASSERT(attribLocation < mActiveAttribLocationsMask.size()); return mActiveAttribLocationsMask[attribLocation]; } AttributesMask ProgramExecutable::getAttributesMask() const { return mAttributesMask; } bool ProgramExecutable::hasDefaultUniforms() const { return !getDefaultUniformRange().empty(); } bool ProgramExecutable::hasTextures() const { return !getSamplerBindings().empty(); } bool ProgramExecutable::hasUniformBuffers() const { return !mUniformBlocks.empty(); } bool ProgramExecutable::hasStorageBuffers() const { return !mShaderStorageBlocks.empty(); } bool ProgramExecutable::hasAtomicCounterBuffers() const { return !mAtomicCounterBuffers.empty(); } bool ProgramExecutable::hasImages() const { return !mImageBindings.empty(); } bool ProgramExecutable::usesFramebufferFetch() const { return (mFragmentInoutRange.length() > 0); } GLuint ProgramExecutable::getUniformIndexFromImageIndex(GLuint imageIndex) const { ASSERT(imageIndex < mImageUniformRange.length()); return imageIndex + mImageUniformRange.low(); } GLuint ProgramExecutable::getUniformIndexFromSamplerIndex(GLuint samplerIndex) const { ASSERT(samplerIndex < mSamplerUniformRange.length()); return samplerIndex + mSamplerUniformRange.low(); } void ProgramExecutable::setActive(size_t textureUnit, const SamplerBinding &samplerBinding, const gl::LinkedUniform &samplerUniform) { mActiveSamplersMask.set(textureUnit); mActiveSamplerTypes[textureUnit] = samplerBinding.textureType; mActiveSamplerYUV[textureUnit] = IsSamplerYUVType(samplerBinding.samplerType); mActiveSamplerFormats[textureUnit] = samplerBinding.format; mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders(); } void ProgramExecutable::setInactive(size_t textureUnit) { mActiveSamplersMask.reset(textureUnit); mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum; mActiveSamplerYUV.reset(textureUnit); mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum; mActiveSamplerShaderBits[textureUnit].reset(); } void ProgramExecutable::hasSamplerTypeConflict(size_t textureUnit) { // Conflicts are marked with InvalidEnum mActiveSamplerYUV.reset(textureUnit); mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum; } void ProgramExecutable::hasSamplerFormatConflict(size_t textureUnit) { // Conflicts are marked with InvalidEnum mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum; } void ProgramExecutable::updateActiveSamplers(const ProgramState &programState) { const std::vector &samplerBindings = programState.getSamplerBindings(); for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex) { const SamplerBinding &samplerBinding = samplerBindings[samplerIndex]; for (GLint textureUnit : samplerBinding.boundTextureUnits) { if (++mActiveSamplerRefCounts[textureUnit] == 1) { uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(samplerIndex); setActive(textureUnit, samplerBinding, programState.getUniforms()[uniformIndex]); } else { if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType || mActiveSamplerYUV.test(textureUnit) != IsSamplerYUVType(samplerBinding.samplerType)) { hasSamplerTypeConflict(textureUnit); } if (mActiveSamplerFormats[textureUnit] != samplerBinding.format) { hasSamplerFormatConflict(textureUnit); } } mActiveSamplersMask.set(textureUnit); } } // Invalidate the validation cache. resetCachedValidateSamplersResult(); } void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable) { const std::vector &imageBindings = executable.getImageBindings(); for (uint32_t imageIndex = 0; imageIndex < imageBindings.size(); ++imageIndex) { const gl::ImageBinding &imageBinding = imageBindings.at(imageIndex); uint32_t uniformIndex = executable.getUniformIndexFromImageIndex(imageIndex); const gl::LinkedUniform &imageUniform = executable.getUniforms()[uniformIndex]; const ShaderBitSet shaderBits = imageUniform.activeShaders(); for (GLint imageUnit : imageBinding.boundImageUnits) { mActiveImagesMask.set(imageUnit); mActiveImageShaderBits[imageUnit] |= shaderBits; } } } void ProgramExecutable::setSamplerUniformTextureTypeAndFormat( size_t textureUnitIndex, std::vector &samplerBindings) { bool foundBinding = false; TextureType foundType = TextureType::InvalidEnum; bool foundYUV = false; SamplerFormat foundFormat = SamplerFormat::InvalidEnum; for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex) { const SamplerBinding &binding = samplerBindings[samplerIndex]; // A conflict exists if samplers of different types are sourced by the same texture unit. // We need to check all bound textures to detect this error case. for (GLuint textureUnit : binding.boundTextureUnits) { if (textureUnit != textureUnitIndex) { continue; } if (!foundBinding) { foundBinding = true; foundType = binding.textureType; foundYUV = IsSamplerYUVType(binding.samplerType); foundFormat = binding.format; uint32_t uniformIndex = getUniformIndexFromSamplerIndex(samplerIndex); setActive(textureUnit, binding, mUniforms[uniformIndex]); } else { if (foundType != binding.textureType || foundYUV != IsSamplerYUVType(binding.samplerType)) { hasSamplerTypeConflict(textureUnit); } if (foundFormat != binding.format) { hasSamplerFormatConflict(textureUnit); } } } } } void ProgramExecutable::updateCanDrawWith() { mCanDrawWith = hasLinkedShaderStage(ShaderType::Vertex); } void ProgramExecutable::saveLinkedStateInfo(const Context *context, const ProgramState &state) { for (ShaderType shaderType : getLinkedShaderStages()) { Shader *shader = state.getAttachedShader(shaderType); ASSERT(shader); mLinkedOutputVaryings[shaderType] = shader->getOutputVaryings(context); mLinkedInputVaryings[shaderType] = shader->getInputVaryings(context); mLinkedShaderVersions[shaderType] = shader->getShaderVersion(context); mLinkedUniforms[shaderType] = shader->getUniforms(context); mLinkedUniformBlocks[shaderType] = shader->getUniformBlocks(context); } } bool ProgramExecutable::isYUVOutput() const { return mYUVOutput; } ShaderType ProgramExecutable::getLinkedTransformFeedbackStage() const { return GetLastPreFragmentStage(mLinkedShaderStages); } bool ProgramExecutable::linkMergedVaryings( const Context *context, const ProgramMergedVaryings &mergedVaryings, const std::vector &transformFeedbackVaryingNames, const LinkingVariables &linkingVariables, bool isSeparable, ProgramVaryingPacking *varyingPacking) { ShaderType tfStage = GetLastPreFragmentStage(linkingVariables.isShaderStageUsedBitset); if (!linkValidateTransformFeedback(context, mergedVaryings, tfStage, transformFeedbackVaryingNames)) { return false; } // Map the varyings to the register file // In WebGL, we use a slightly different handling for packing variables. gl::PackMode packMode = PackMode::ANGLE_RELAXED; if (context->getLimitations().noFlexibleVaryingPacking) { // D3D9 pack mode is strictly more strict than WebGL, so takes priority. packMode = PackMode::ANGLE_NON_CONFORMANT_D3D9; } else if (context->isWebGL()) { packMode = PackMode::WEBGL_STRICT; } // Build active shader stage map. ShaderBitSet activeShadersMask; for (ShaderType shaderType : kAllGraphicsShaderTypes) { // - Check for attached shaders to handle the case of a Program linking the currently // attached shaders. // - Check for linked shaders to handle the case of a PPO linking separable programs before // drawing. if (linkingVariables.isShaderStageUsedBitset[shaderType] || getLinkedShaderStages().test(shaderType)) { activeShadersMask[shaderType] = true; } } if (!varyingPacking->collectAndPackUserVaryings(mInfoLog, context->getCaps(), packMode, activeShadersMask, mergedVaryings, transformFeedbackVaryingNames, isSeparable)) { return false; } gatherTransformFeedbackVaryings(mergedVaryings, tfStage, transformFeedbackVaryingNames); updateTransformFeedbackStrides(); return true; } bool ProgramExecutable::linkValidateTransformFeedback( const Context *context, const ProgramMergedVaryings &varyings, ShaderType stage, const std::vector &transformFeedbackVaryingNames) { const Version &version = context->getClientVersion(); // Validate the tf names regardless of the actual program varyings. std::set uniqueNames; for (const std::string &tfVaryingName : transformFeedbackVaryingNames) { if (version < Version(3, 1) && tfVaryingName.find('[') != std::string::npos) { mInfoLog << "Capture of array elements is undefined and not supported."; return false; } if (version >= Version(3, 1)) { if (IncludeSameArrayElement(uniqueNames, tfVaryingName)) { mInfoLog << "Two transform feedback varyings include the same array element (" << tfVaryingName << ")."; return false; } } else { if (uniqueNames.count(tfVaryingName) > 0) { mInfoLog << "Two transform feedback varyings specify the same output variable (" << tfVaryingName << ")."; return false; } } uniqueNames.insert(tfVaryingName); } // From OpneGLES spec. 11.1.2.1: A program will fail to link if: // the count specified by TransformFeedbackVaryings is non-zero, but the // program object has no vertex, tessellation evaluation, or geometry shader if (transformFeedbackVaryingNames.size() > 0 && !gl::ShaderTypeSupportsTransformFeedback(getLinkedTransformFeedbackStage())) { mInfoLog << "Linked transform feedback stage " << getLinkedTransformFeedbackStage() << " does not support transform feedback varying."; return false; } // Validate against program varyings. size_t totalComponents = 0; for (const std::string &tfVaryingName : transformFeedbackVaryingNames) { std::vector subscripts; std::string baseName = ParseResourceName(tfVaryingName, &subscripts); const sh::ShaderVariable *var = FindOutputVaryingOrField(varyings, stage, baseName); if (var == nullptr) { mInfoLog << "Transform feedback varying " << tfVaryingName << " does not exist in the vertex shader."; return false; } // Validate the matching variable. if (var->isStruct()) { mInfoLog << "Struct cannot be captured directly (" << baseName << ")."; return false; } size_t elementCount = 0; size_t componentCount = 0; if (var->isArray()) { if (version < Version(3, 1)) { mInfoLog << "Capture of arrays is undefined and not supported."; return false; } // GLSL ES 3.10 section 4.3.6: A vertex output can't be an array of arrays. ASSERT(!var->isArrayOfArrays()); if (!subscripts.empty() && subscripts[0] >= var->getOutermostArraySize()) { mInfoLog << "Cannot capture outbound array element '" << tfVaryingName << "'."; return false; } elementCount = (subscripts.empty() ? var->getOutermostArraySize() : 1); } else { if (!subscripts.empty()) { mInfoLog << "Varying '" << baseName << "' is not an array to be captured by element."; return false; } elementCount = 1; } const Caps &caps = context->getCaps(); // TODO(jmadill): Investigate implementation limits on D3D11 componentCount = VariableComponentCount(var->type) * elementCount; if (mTransformFeedbackBufferMode == GL_SEPARATE_ATTRIBS && componentCount > static_cast(caps.maxTransformFeedbackSeparateComponents)) { mInfoLog << "Transform feedback varying " << tfVaryingName << " components (" << componentCount << ") exceed the maximum separate components (" << caps.maxTransformFeedbackSeparateComponents << ")."; return false; } totalComponents += componentCount; if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS && totalComponents > static_cast(caps.maxTransformFeedbackInterleavedComponents)) { mInfoLog << "Transform feedback varying total components (" << totalComponents << ") exceed the maximum interleaved components (" << caps.maxTransformFeedbackInterleavedComponents << ")."; return false; } } return true; } void ProgramExecutable::gatherTransformFeedbackVaryings( const ProgramMergedVaryings &varyings, ShaderType stage, const std::vector &transformFeedbackVaryingNames) { // Gather the linked varyings that are used for transform feedback, they should all exist. mLinkedTransformFeedbackVaryings.clear(); for (const std::string &tfVaryingName : transformFeedbackVaryingNames) { std::vector subscripts; std::string baseName = ParseResourceName(tfVaryingName, &subscripts); size_t subscript = GL_INVALID_INDEX; if (!subscripts.empty()) { subscript = subscripts.back(); } for (const ProgramVaryingRef &ref : varyings) { if (ref.frontShaderStage != stage) { continue; } const sh::ShaderVariable *varying = ref.get(stage); if (baseName == varying->name) { mLinkedTransformFeedbackVaryings.emplace_back(*varying, static_cast(subscript)); break; } else if (varying->isStruct()) { GLuint fieldIndex = 0; const auto *field = varying->findField(tfVaryingName, &fieldIndex); if (field != nullptr) { mLinkedTransformFeedbackVaryings.emplace_back(*field, *varying); break; } } } } } void ProgramExecutable::updateTransformFeedbackStrides() { if (mLinkedTransformFeedbackVaryings.empty()) { return; } if (mTransformFeedbackBufferMode == GL_INTERLEAVED_ATTRIBS) { mTransformFeedbackStrides.resize(1); size_t totalSize = 0; for (const TransformFeedbackVarying &varying : mLinkedTransformFeedbackVaryings) { totalSize += varying.size() * VariableExternalSize(varying.type); } mTransformFeedbackStrides[0] = static_cast(totalSize); } else { mTransformFeedbackStrides.resize(mLinkedTransformFeedbackVaryings.size()); for (size_t i = 0; i < mLinkedTransformFeedbackVaryings.size(); i++) { TransformFeedbackVarying &varying = mLinkedTransformFeedbackVaryings[i]; mTransformFeedbackStrides[i] = static_cast(varying.size() * VariableExternalSize(varying.type)); } } } bool ProgramExecutable::validateSamplersImpl(InfoLog *infoLog, const Caps &caps) const { // if any two active samplers in a program are of different types, but refer to the same // texture image unit, and this is the current program, then ValidateProgram will fail, and // DrawArrays and DrawElements will issue the INVALID_OPERATION error. for (size_t textureUnit : mActiveSamplersMask) { if (mActiveSamplerTypes[textureUnit] == TextureType::InvalidEnum) { if (infoLog) { (*infoLog) << "Samplers of conflicting types refer to the same texture " "image unit (" << textureUnit << ")."; } mCachedValidateSamplersResult = false; return false; } if (mActiveSamplerFormats[textureUnit] == SamplerFormat::InvalidEnum) { if (infoLog) { (*infoLog) << "Samplers of conflicting formats refer to the same texture " "image unit (" << textureUnit << ")."; } mCachedValidateSamplersResult = false; return false; } } mCachedValidateSamplersResult = true; return true; } bool ProgramExecutable::linkValidateOutputVariables( const Caps &caps, const Extensions &extensions, const Version &version, GLuint combinedImageUniformsCount, GLuint combinedShaderStorageBlocksCount, const std::vector &outputVariables, int fragmentShaderVersion, const ProgramAliasedBindings &fragmentOutputLocations, const ProgramAliasedBindings &fragmentOutputIndices) { ASSERT(mOutputVariableTypes.empty()); ASSERT(mActiveOutputVariablesMask.none()); ASSERT(mDrawBufferTypeMask.none()); ASSERT(!mYUVOutput); // Gather output variable types for (const sh::ShaderVariable &outputVariable : outputVariables) { if (outputVariable.isBuiltIn() && outputVariable.name != "gl_FragColor" && outputVariable.name != "gl_FragData") { continue; } unsigned int baseLocation = (outputVariable.location == -1 ? 0u : static_cast(outputVariable.location)); // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of // structures, so we may use getBasicTypeElementCount(). unsigned int elementCount = outputVariable.getBasicTypeElementCount(); for (unsigned int elementIndex = 0; elementIndex < elementCount; elementIndex++) { const unsigned int location = baseLocation + elementIndex; if (location >= mOutputVariableTypes.size()) { mOutputVariableTypes.resize(location + 1, GL_NONE); } ASSERT(location < mActiveOutputVariablesMask.size()); mActiveOutputVariablesMask.set(location); mOutputVariableTypes[location] = VariableComponentType(outputVariable.type); ComponentType componentType = GLenumToComponentType(mOutputVariableTypes[location]); SetComponentTypeMask(componentType, location, &mDrawBufferTypeMask); } if (outputVariable.yuv) { ASSERT(outputVariables.size() == 1); mYUVOutput = true; } } if (version >= ES_3_1) { // [OpenGL ES 3.1] Chapter 8.22 Page 203: // A link error will be generated if the sum of the number of active image uniforms used in // all shaders, the number of active shader storage blocks, and the number of active // fragment shader outputs exceeds the implementation-dependent value of // MAX_COMBINED_SHADER_OUTPUT_RESOURCES. if (combinedImageUniformsCount + combinedShaderStorageBlocksCount + mActiveOutputVariablesMask.count() > static_cast(caps.maxCombinedShaderOutputResources)) { mInfoLog << "The sum of the number of active image uniforms, active shader storage blocks " "and active fragment shader outputs exceeds " "MAX_COMBINED_SHADER_OUTPUT_RESOURCES (" << caps.maxCombinedShaderOutputResources << ")"; return false; } } mOutputVariables = outputVariables; if (fragmentShaderVersion == 100) { return true; } // EXT_blend_func_extended doesn't specify anything related to binding specific elements of an // output array in explicit terms. // // Assuming fragData is an output array, you can defend the position that: // P1) you must support binding "fragData" because it's specified // P2) you must support querying "fragData[x]" because it's specified // P3) you must support binding "fragData[0]" because it's a frequently used pattern // // Then you can make the leap of faith: // P4) you must support binding "fragData[x]" because you support "fragData[0]" // P5) you must support binding "fragData[x]" because you support querying "fragData[x]" // // The spec brings in the "world of arrays" when it mentions binding the arrays and the // automatic binding. Thus it must be interpreted that the thing is not undefined, rather you // must infer the only possible interpretation (?). Note again: this need of interpretation // might be completely off of what GL spec logic is. // // The other complexity is that unless you implement this feature, it's hard to understand what // should happen when the client invokes the feature. You cannot add an additional error as it // is not specified. One can ignore it, but obviously it creates the discrepancies... std::vector reservedLocations; // Process any output API bindings for arrays that don't alias to the first element. for (const auto &bindingPair : fragmentOutputLocations) { const std::string &name = bindingPair.first; const ProgramBinding &binding = bindingPair.second; size_t nameLengthWithoutArrayIndex; unsigned int arrayIndex = ParseArrayIndex(name, &nameLengthWithoutArrayIndex); if (arrayIndex == 0 || arrayIndex == GL_INVALID_INDEX) { continue; } for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size(); outputVariableIndex++) { const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex]; // Check that the binding corresponds to an output array and its array index fits. if (outputVariable.isBuiltIn() || !outputVariable.isArray() || !angle::BeginsWith(outputVariable.name, name, nameLengthWithoutArrayIndex) || arrayIndex >= outputVariable.getOutermostArraySize()) { continue; } // Get the API index that corresponds to this exact binding. // This index may differ from the index used for the array's base. std::vector &outputLocations = fragmentOutputIndices.getBindingByName(name) == 1 ? mSecondaryOutputLocations : mOutputLocations; unsigned int location = binding.location; VariableLocation locationInfo(arrayIndex, outputVariableIndex); if (location >= outputLocations.size()) { outputLocations.resize(location + 1); } if (outputLocations[location].used()) { mInfoLog << "Location of variable " << outputVariable.name << " conflicts with another variable."; return false; } outputLocations[location] = locationInfo; // Note the array binding location so that it can be skipped later. reservedLocations.push_back(locationInfo); } } // Reserve locations for output variables whose location is fixed in the shader or through the // API. Otherwise, the remaining unallocated outputs will be processed later. for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size(); outputVariableIndex++) { const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex]; // Don't store outputs for gl_FragDepth, gl_FragColor, etc. if (outputVariable.isBuiltIn()) continue; int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable); if (fixedLocation == -1) { // Here we're only reserving locations for variables whose location is fixed. continue; } unsigned int baseLocation = static_cast(fixedLocation); std::vector &outputLocations = IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable) ? mSecondaryOutputLocations : mOutputLocations; // GLSL ES 3.10 section 4.3.6: Output variables cannot be arrays of arrays or arrays of // structures, so we may use getBasicTypeElementCount(). unsigned int elementCount = outputVariable.getBasicTypeElementCount(); if (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations, outputVariableIndex)) { mInfoLog << "Location of variable " << outputVariable.name << " conflicts with another variable."; return false; } AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations, outputVariableIndex, mOutputVariables[outputVariableIndex]); } // Here we assign locations for the output variables that don't yet have them. Note that we're // not necessarily able to fit the variables optimally, since then we might have to try // different arrangements of output arrays. Now we just assign the locations in the order that // we got the output variables. The spec isn't clear on what kind of algorithm is required for // finding locations for the output variables, so this should be acceptable at least for now. GLuint maxLocation = static_cast(caps.maxDrawBuffers); if (!mSecondaryOutputLocations.empty()) { // EXT_blend_func_extended: Program outputs will be validated against // MAX_DUAL_SOURCE_DRAW_BUFFERS_EXT if there's even one output with index one. maxLocation = caps.maxDualSourceDrawBuffers; } for (unsigned int outputVariableIndex = 0; outputVariableIndex < mOutputVariables.size(); outputVariableIndex++) { const sh::ShaderVariable &outputVariable = mOutputVariables[outputVariableIndex]; // Don't store outputs for gl_FragDepth, gl_FragColor, etc. if (outputVariable.isBuiltIn()) continue; int fixedLocation = GetOutputLocationForLink(fragmentOutputLocations, outputVariable); std::vector &outputLocations = IsOutputSecondaryForLink(fragmentOutputIndices, outputVariable) ? mSecondaryOutputLocations : mOutputLocations; unsigned int baseLocation = 0; unsigned int elementCount = outputVariable.getBasicTypeElementCount(); if (fixedLocation != -1) { // Secondary inputs might have caused the max location to drop below what has already // been explicitly assigned locations. Check for any fixed locations above the max // that should cause linking to fail. baseLocation = static_cast(fixedLocation); } else { // No fixed location, so try to fit the output in unassigned locations. // Try baseLocations starting from 0 one at a time and see if the variable fits. while (FindUsedOutputLocation(outputLocations, baseLocation, elementCount, reservedLocations, outputVariableIndex)) { baseLocation++; } AssignOutputLocations(outputLocations, baseLocation, elementCount, reservedLocations, outputVariableIndex, mOutputVariables[outputVariableIndex]); } // Check for any elements assigned above the max location that are actually used. if (baseLocation + elementCount > maxLocation && (baseLocation >= maxLocation || FindUsedOutputLocation(outputLocations, maxLocation, baseLocation + elementCount - maxLocation, reservedLocations, outputVariableIndex))) { // EXT_blend_func_extended: Linking can fail: // "if the explicit binding assignments do not leave enough space for the linker to // automatically assign a location for a varying out array, which requires multiple // contiguous locations." mInfoLog << "Could not fit output variable into available locations: " << outputVariable.name; return false; } } return true; } bool ProgramExecutable::linkUniforms( const Context *context, const ShaderMap> &shaderUniforms, InfoLog &infoLog, const ProgramAliasedBindings &uniformLocationBindings, GLuint *combinedImageUniformsCountOut, std::vector *unusedUniformsOutOrNull, std::vector *uniformLocationsOutOrNull) { UniformLinker linker(mLinkedShaderStages, shaderUniforms); if (!linker.link(context->getCaps(), infoLog, uniformLocationBindings)) { return false; } linker.getResults(&mUniforms, unusedUniformsOutOrNull, uniformLocationsOutOrNull); linkSamplerAndImageBindings(combinedImageUniformsCountOut); if (!linkAtomicCounterBuffers(context, infoLog)) { return false; } return true; } void ProgramExecutable::linkSamplerAndImageBindings(GLuint *combinedImageUniforms) { ASSERT(combinedImageUniforms); // Iterate over mExecutable->mUniforms from the back, and find the range of subpass inputs, // atomic counters, images and samplers in that order. auto highIter = mUniforms.rbegin(); auto lowIter = highIter; unsigned int high = static_cast(mUniforms.size()); unsigned int low = high; // Note that uniform block uniforms are not yet appended to this list. ASSERT(mUniforms.empty() || highIter->isAtomicCounter() || highIter->isImage() || highIter->isSampler() || highIter->isInDefaultBlock() || highIter->isFragmentInOut); for (; lowIter != mUniforms.rend() && lowIter->isFragmentInOut; ++lowIter) { --low; } mFragmentInoutRange = RangeUI(low, high); highIter = lowIter; high = low; for (; lowIter != mUniforms.rend() && lowIter->isAtomicCounter(); ++lowIter) { --low; } mAtomicCounterUniformRange = RangeUI(low, high); highIter = lowIter; high = low; for (; lowIter != mUniforms.rend() && lowIter->isImage(); ++lowIter) { --low; } mImageUniformRange = RangeUI(low, high); *combinedImageUniforms = 0u; // If uniform is a image type, insert it into the mImageBindings array. for (unsigned int imageIndex : mImageUniformRange) { // ES3.1 (section 7.6.1) and GLSL ES3.1 (section 4.4.5), Uniform*i{v} commands // cannot load values into a uniform defined as an image. if declare without a // binding qualifier, any uniform image variable (include all elements of // unbound image array) should be bound to unit zero. auto &imageUniform = mUniforms[imageIndex]; TextureType textureType = ImageTypeToTextureType(imageUniform.type); const GLuint arraySize = imageUniform.isArray() ? imageUniform.arraySizes[0] : 1u; if (imageUniform.binding == -1) { mImageBindings.emplace_back( ImageBinding(imageUniform.getBasicTypeElementCount(), textureType)); } else { // The arrays of arrays are flattened to arrays, it needs to record the array offset for // the correct binding image unit. mImageBindings.emplace_back( ImageBinding(imageUniform.binding + imageUniform.parentArrayIndex() * arraySize, imageUniform.getBasicTypeElementCount(), textureType)); } *combinedImageUniforms += imageUniform.activeShaderCount() * arraySize; } highIter = lowIter; high = low; for (; lowIter != mUniforms.rend() && lowIter->isSampler(); ++lowIter) { --low; } mSamplerUniformRange = RangeUI(low, high); // If uniform is a sampler type, insert it into the mSamplerBindings array. for (unsigned int samplerIndex : mSamplerUniformRange) { const auto &samplerUniform = mUniforms[samplerIndex]; TextureType textureType = SamplerTypeToTextureType(samplerUniform.type); GLenum samplerType = samplerUniform.typeInfo->type; unsigned int elementCount = samplerUniform.getBasicTypeElementCount(); SamplerFormat format = samplerUniform.typeInfo->samplerFormat; mSamplerBindings.emplace_back(textureType, samplerType, format, elementCount); } // Whatever is left constitutes the default uniforms. mDefaultUniformRange = RangeUI(0, low); } bool ProgramExecutable::linkAtomicCounterBuffers(const Context *context, InfoLog &infoLog) { for (unsigned int index : mAtomicCounterUniformRange) { auto &uniform = mUniforms[index]; uniform.blockInfo.offset = uniform.offset; uniform.blockInfo.arrayStride = (uniform.isArray() ? 4 : 0); uniform.blockInfo.matrixStride = 0; uniform.blockInfo.isRowMajorMatrix = false; bool found = false; for (unsigned int bufferIndex = 0; bufferIndex < getActiveAtomicCounterBufferCount(); ++bufferIndex) { auto &buffer = mAtomicCounterBuffers[bufferIndex]; if (buffer.binding == uniform.binding) { buffer.memberIndexes.push_back(index); uniform.bufferIndex = bufferIndex; found = true; buffer.unionReferencesWith(uniform); break; } } if (!found) { AtomicCounterBuffer atomicCounterBuffer; atomicCounterBuffer.binding = uniform.binding; atomicCounterBuffer.memberIndexes.push_back(index); atomicCounterBuffer.unionReferencesWith(uniform); mAtomicCounterBuffers.push_back(atomicCounterBuffer); uniform.bufferIndex = static_cast(getActiveAtomicCounterBufferCount() - 1); } } // Count each atomic counter buffer to validate against // per-stage and combined gl_Max*AtomicCounterBuffers. GLint combinedShaderACBCount = 0; gl::ShaderMap perShaderACBCount = {}; for (unsigned int bufferIndex = 0; bufferIndex < getActiveAtomicCounterBufferCount(); ++bufferIndex) { AtomicCounterBuffer &acb = mAtomicCounterBuffers[bufferIndex]; const ShaderBitSet shaderStages = acb.activeShaders(); for (gl::ShaderType shaderType : shaderStages) { ++perShaderACBCount[shaderType]; } ++combinedShaderACBCount; } const Caps &caps = context->getCaps(); if (combinedShaderACBCount > caps.maxCombinedAtomicCounterBuffers) { infoLog << " combined AtomicCounterBuffers count exceeds limit"; return false; } for (gl::ShaderType stage : gl::AllShaderTypes()) { if (perShaderACBCount[stage] > caps.maxShaderAtomicCounterBuffers[stage]) { infoLog << GetShaderTypeString(stage) << " shader AtomicCounterBuffers count exceeds limit"; return false; } } return true; } void ProgramExecutable::copyInputsFromProgram(const ProgramState &programState) { mProgramInputs = programState.getProgramInputs(); } void ProgramExecutable::copyShaderBuffersFromProgram(const ProgramState &programState, ShaderType shaderType) { AppendActiveBlocks(shaderType, programState.getUniformBlocks(), mUniformBlocks); AppendActiveBlocks(shaderType, programState.getShaderStorageBlocks(), mShaderStorageBlocks); AppendActiveBlocks(shaderType, programState.getAtomicCounterBuffers(), mAtomicCounterBuffers); } void ProgramExecutable::clearSamplerBindings() { mSamplerBindings.clear(); } void ProgramExecutable::copySamplerBindingsFromProgram(const ProgramState &programState) { const std::vector &bindings = programState.getSamplerBindings(); mSamplerBindings.insert(mSamplerBindings.end(), bindings.begin(), bindings.end()); } void ProgramExecutable::copyImageBindingsFromProgram(const ProgramState &programState) { const std::vector &bindings = programState.getImageBindings(); mImageBindings.insert(mImageBindings.end(), bindings.begin(), bindings.end()); } void ProgramExecutable::copyOutputsFromProgram(const ProgramState &programState) { mOutputVariables = programState.getOutputVariables(); mOutputLocations = programState.getOutputLocations(); mSecondaryOutputLocations = programState.getSecondaryOutputLocations(); } void ProgramExecutable::copyUniformsFromProgramMap(const ShaderMap &programs) { // Merge default uniforms. auto getDefaultRange = [](const ProgramState &state) { return state.getDefaultUniformRange(); }; mDefaultUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getDefaultRange); // Merge sampler uniforms. auto getSamplerRange = [](const ProgramState &state) { return state.getSamplerUniformRange(); }; mSamplerUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getSamplerRange); // Merge image uniforms. auto getImageRange = [](const ProgramState &state) { return state.getImageUniformRange(); }; mImageUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getImageRange); // Merge atomic counter uniforms. auto getAtomicRange = [](const ProgramState &state) { return state.getAtomicCounterUniformRange(); }; mAtomicCounterUniformRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getAtomicRange); // Merge fragment in/out uniforms. auto getInoutRange = [](const ProgramState &state) { return state.getFragmentInoutRange(); }; mFragmentInoutRange = AddUniforms(programs, mLinkedShaderStages, mUniforms, getInoutRange); } } // namespace gl