| // |
| // 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 "libANGLE/Context.h" |
| #include "libANGLE/Program.h" |
| #include "libANGLE/ProgramPipeline.h" |
| #include "libANGLE/Shader.h" |
| |
| namespace gl |
| { |
| |
| ProgramExecutable::ProgramExecutable() |
| : mMaxActiveAttribLocation(0), |
| mAttributesTypeMask(0), |
| mAttributesMask(0), |
| mActiveSamplersMask(0), |
| mActiveSamplerRefCounts{}, |
| mActiveImagesMask(0), |
| mCanDrawWith(false), |
| mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS), |
| mDefaultUniformRange(0, 0), |
| mSamplerUniformRange(0, 0), |
| mImageUniformRange(0, 0), |
| mPipelineHasGraphicsUniformBuffers(false), |
| mPipelineHasComputeUniformBuffers(false), |
| mPipelineHasGraphicsStorageBuffers(false), |
| mPipelineHasComputeStorageBuffers(false), |
| mPipelineHasGraphicsAtomicCounterBuffers(false), |
| mPipelineHasComputeAtomicCounterBuffers(false), |
| mPipelineHasGraphicsDefaultUniforms(false), |
| mPipelineHasComputeDefaultUniforms(false), |
| mPipelineHasGraphicsTextures(false), |
| mPipelineHasComputeTextures(false), |
| mPipelineHasGraphicsImages(false), |
| mPipelineHasComputeImages(false), |
| mIsCompute(false) |
| { |
| reset(); |
| } |
| |
| ProgramExecutable::ProgramExecutable(const ProgramExecutable &other) |
| : mLinkedGraphicsShaderStages(other.mLinkedGraphicsShaderStages), |
| mLinkedComputeShaderStages(other.mLinkedComputeShaderStages), |
| mActiveAttribLocationsMask(other.mActiveAttribLocationsMask), |
| mMaxActiveAttribLocation(other.mMaxActiveAttribLocation), |
| mAttributesTypeMask(other.mAttributesTypeMask), |
| mAttributesMask(other.mAttributesMask), |
| mActiveSamplersMask(other.mActiveSamplersMask), |
| mActiveSamplerRefCounts(other.mActiveSamplerRefCounts), |
| mActiveSamplerTypes(other.mActiveSamplerTypes), |
| mActiveSamplerFormats(other.mActiveSamplerFormats), |
| mActiveSamplerShaderBits(other.mActiveSamplerShaderBits), |
| mActiveImagesMask(other.mActiveImagesMask), |
| mActiveImageShaderBits(other.mActiveImageShaderBits), |
| mCanDrawWith(other.mCanDrawWith), |
| mOutputVariables(other.mOutputVariables), |
| mOutputLocations(other.mOutputLocations), |
| mProgramInputs(other.mProgramInputs), |
| mLinkedTransformFeedbackVaryings(other.mLinkedTransformFeedbackVaryings), |
| mTransformFeedbackStrides(other.mTransformFeedbackStrides), |
| mTransformFeedbackBufferMode(other.mTransformFeedbackBufferMode), |
| mUniforms(other.mUniforms), |
| mDefaultUniformRange(other.mDefaultUniformRange), |
| mSamplerUniformRange(other.mSamplerUniformRange), |
| mUniformBlocks(other.mUniformBlocks), |
| mAtomicCounterBuffers(other.mAtomicCounterBuffers), |
| mImageUniformRange(other.mImageUniformRange), |
| mComputeShaderStorageBlocks(other.mComputeShaderStorageBlocks), |
| mGraphicsShaderStorageBlocks(other.mGraphicsShaderStorageBlocks), |
| mPipelineHasGraphicsUniformBuffers(other.mPipelineHasGraphicsUniformBuffers), |
| mPipelineHasComputeUniformBuffers(other.mPipelineHasComputeUniformBuffers), |
| mPipelineHasGraphicsStorageBuffers(other.mPipelineHasGraphicsStorageBuffers), |
| mPipelineHasComputeStorageBuffers(other.mPipelineHasComputeStorageBuffers), |
| mPipelineHasGraphicsAtomicCounterBuffers(other.mPipelineHasGraphicsAtomicCounterBuffers), |
| mPipelineHasComputeAtomicCounterBuffers(other.mPipelineHasComputeAtomicCounterBuffers), |
| mPipelineHasGraphicsDefaultUniforms(other.mPipelineHasGraphicsDefaultUniforms), |
| mPipelineHasComputeDefaultUniforms(other.mPipelineHasComputeDefaultUniforms), |
| mPipelineHasGraphicsTextures(other.mPipelineHasGraphicsTextures), |
| mPipelineHasComputeTextures(other.mPipelineHasComputeTextures), |
| mPipelineHasGraphicsImages(other.mPipelineHasGraphicsImages), |
| mPipelineHasComputeImages(other.mPipelineHasComputeImages), |
| mIsCompute(other.mIsCompute) |
| { |
| reset(); |
| } |
| |
| ProgramExecutable::~ProgramExecutable() = default; |
| |
| void ProgramExecutable::reset() |
| { |
| resetInfoLog(); |
| mActiveAttribLocationsMask.reset(); |
| mAttributesTypeMask.reset(); |
| mAttributesMask.reset(); |
| mMaxActiveAttribLocation = 0; |
| |
| mActiveSamplersMask.reset(); |
| mActiveSamplerRefCounts = {}; |
| mActiveSamplerTypes.fill(TextureType::InvalidEnum); |
| mActiveSamplerFormats.fill(SamplerFormat::InvalidEnum); |
| |
| mActiveImagesMask.reset(); |
| |
| mProgramInputs.clear(); |
| mLinkedTransformFeedbackVaryings.clear(); |
| mUniforms.clear(); |
| mUniformBlocks.clear(); |
| mComputeShaderStorageBlocks.clear(); |
| mGraphicsShaderStorageBlocks.clear(); |
| mAtomicCounterBuffers.clear(); |
| mOutputVariables.clear(); |
| mOutputLocations.clear(); |
| mSamplerBindings.clear(); |
| mComputeImageBindings.clear(); |
| mGraphicsImageBindings.clear(); |
| |
| mPipelineHasGraphicsUniformBuffers = false; |
| mPipelineHasComputeUniformBuffers = false; |
| mPipelineHasGraphicsStorageBuffers = false; |
| mPipelineHasComputeStorageBuffers = false; |
| mPipelineHasGraphicsAtomicCounterBuffers = false; |
| mPipelineHasComputeAtomicCounterBuffers = false; |
| mPipelineHasGraphicsDefaultUniforms = false; |
| mPipelineHasComputeDefaultUniforms = false; |
| mPipelineHasGraphicsTextures = false; |
| mPipelineHasComputeTextures = false; |
| } |
| |
| void ProgramExecutable::load(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<uint32_t>()); |
| mAttributesMask = gl::AttributesMask(stream->readInt<uint32_t>()); |
| mActiveAttribLocationsMask = gl::AttributesMask(stream->readInt<uint32_t>()); |
| mMaxActiveAttribLocation = stream->readInt<unsigned int>(); |
| |
| mLinkedGraphicsShaderStages = ShaderBitSet(stream->readInt<uint8_t>()); |
| mLinkedComputeShaderStages = ShaderBitSet(stream->readInt<uint8_t>()); |
| mIsCompute = stream->readBool(); |
| |
| mPipelineHasGraphicsUniformBuffers = stream->readBool(); |
| mPipelineHasComputeUniformBuffers = stream->readBool(); |
| mPipelineHasGraphicsStorageBuffers = stream->readBool(); |
| mPipelineHasComputeStorageBuffers = stream->readBool(); |
| mPipelineHasGraphicsAtomicCounterBuffers = stream->readBool(); |
| mPipelineHasComputeAtomicCounterBuffers = stream->readBool(); |
| mPipelineHasGraphicsDefaultUniforms = stream->readBool(); |
| mPipelineHasComputeDefaultUniforms = stream->readBool(); |
| mPipelineHasGraphicsTextures = stream->readBool(); |
| mPipelineHasComputeTextures = stream->readBool(); |
| } |
| |
| void ProgramExecutable::save(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<uint32_t>(mAttributesTypeMask.to_ulong())); |
| stream->writeInt(static_cast<uint32_t>(mAttributesMask.to_ulong())); |
| stream->writeInt(static_cast<uint32_t>(mActiveAttribLocationsMask.to_ulong())); |
| stream->writeInt(mMaxActiveAttribLocation); |
| |
| stream->writeInt(mLinkedGraphicsShaderStages.bits()); |
| stream->writeInt(mLinkedComputeShaderStages.bits()); |
| stream->writeBool(mIsCompute); |
| |
| stream->writeBool(mPipelineHasGraphicsUniformBuffers); |
| stream->writeBool(mPipelineHasComputeUniformBuffers); |
| stream->writeBool(mPipelineHasGraphicsStorageBuffers); |
| stream->writeBool(mPipelineHasComputeStorageBuffers); |
| stream->writeBool(mPipelineHasGraphicsAtomicCounterBuffers); |
| stream->writeBool(mPipelineHasComputeAtomicCounterBuffers); |
| stream->writeBool(mPipelineHasGraphicsDefaultUniforms); |
| stream->writeBool(mPipelineHasComputeDefaultUniforms); |
| stream->writeBool(mPipelineHasGraphicsTextures); |
| stream->writeBool(mPipelineHasComputeTextures); |
| } |
| |
| int ProgramExecutable::getInfoLogLength() const |
| { |
| return static_cast<int>(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 |
| { |
| // TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow. |
| // ASSERT(!mLinkingState); |
| ASSERT(attribLocation < mActiveAttribLocationsMask.size()); |
| return mActiveAttribLocationsMask[attribLocation]; |
| } |
| |
| AttributesMask ProgramExecutable::getAttributesMask() const |
| { |
| // TODO(timvp): http://anglebug.com/3570: Enable this assert here somehow. |
| // ASSERT(!mLinkingState); |
| return mAttributesMask; |
| } |
| |
| bool ProgramExecutable::hasDefaultUniforms() const |
| { |
| return !getDefaultUniformRange().empty() || |
| (isCompute() ? mPipelineHasComputeDefaultUniforms : mPipelineHasGraphicsDefaultUniforms); |
| } |
| |
| bool ProgramExecutable::hasTextures() const |
| { |
| return !getSamplerBindings().empty() || |
| (isCompute() ? mPipelineHasComputeTextures : mPipelineHasGraphicsTextures); |
| } |
| |
| // TODO: http://anglebug.com/3570: Remove mHas*UniformBuffers once PPO's have valid data in |
| // mUniformBlocks |
| bool ProgramExecutable::hasUniformBuffers() const |
| { |
| return !getUniformBlocks().empty() || |
| (isCompute() ? mPipelineHasComputeUniformBuffers : mPipelineHasGraphicsUniformBuffers); |
| } |
| |
| bool ProgramExecutable::hasStorageBuffers() const |
| { |
| return (isCompute() ? hasComputeStorageBuffers() : hasGraphicsStorageBuffers()); |
| } |
| |
| bool ProgramExecutable::hasGraphicsStorageBuffers() const |
| { |
| return !mGraphicsShaderStorageBlocks.empty() || mPipelineHasGraphicsStorageBuffers; |
| } |
| |
| bool ProgramExecutable::hasComputeStorageBuffers() const |
| { |
| return !mComputeShaderStorageBlocks.empty() || mPipelineHasComputeStorageBuffers; |
| } |
| |
| bool ProgramExecutable::hasAtomicCounterBuffers() const |
| { |
| return !getAtomicCounterBuffers().empty() || |
| (isCompute() ? mPipelineHasComputeAtomicCounterBuffers |
| : mPipelineHasGraphicsAtomicCounterBuffers); |
| } |
| |
| bool ProgramExecutable::hasImages() const |
| { |
| return (isCompute() ? hasComputeImages() : hasGraphicsImages()); |
| } |
| |
| bool ProgramExecutable::hasGraphicsImages() const |
| { |
| return !mGraphicsImageBindings.empty() || mPipelineHasGraphicsImages; |
| } |
| |
| bool ProgramExecutable::hasComputeImages() const |
| { |
| return !mComputeImageBindings.empty() || mPipelineHasComputeImages; |
| } |
| |
| GLuint ProgramExecutable::getUniformIndexFromImageIndex(GLuint imageIndex) const |
| { |
| ASSERT(imageIndex < mImageUniformRange.length()); |
| return imageIndex + mImageUniformRange.low(); |
| } |
| |
| void ProgramExecutable::updateActiveSamplers(const ProgramState &programState) |
| { |
| const std::vector<SamplerBinding> &samplerBindings = programState.getSamplerBindings(); |
| |
| for (uint32_t samplerIndex = 0; samplerIndex < samplerBindings.size(); ++samplerIndex) |
| { |
| const SamplerBinding &samplerBinding = samplerBindings[samplerIndex]; |
| uint32_t uniformIndex = programState.getUniformIndexFromSamplerIndex(samplerIndex); |
| const gl::LinkedUniform &samplerUniform = programState.getUniforms()[uniformIndex]; |
| |
| for (GLint textureUnit : samplerBinding.boundTextureUnits) |
| { |
| if (++mActiveSamplerRefCounts[textureUnit] == 1) |
| { |
| mActiveSamplerTypes[textureUnit] = samplerBinding.textureType; |
| mActiveSamplerFormats[textureUnit] = samplerBinding.format; |
| mActiveSamplerShaderBits[textureUnit] = samplerUniform.activeShaders(); |
| } |
| else |
| { |
| if (mActiveSamplerTypes[textureUnit] != samplerBinding.textureType) |
| { |
| mActiveSamplerTypes[textureUnit] = TextureType::InvalidEnum; |
| } |
| if (mActiveSamplerFormats[textureUnit] != samplerBinding.format) |
| { |
| mActiveSamplerFormats[textureUnit] = SamplerFormat::InvalidEnum; |
| } |
| } |
| mActiveSamplersMask.set(textureUnit); |
| } |
| } |
| } |
| |
| void ProgramExecutable::updateActiveImages(const ProgramExecutable &executable) |
| { |
| const std::vector<ImageBinding> *imageBindings = 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); |
| if (isCompute()) |
| { |
| mActiveImageShaderBits[imageUnit].set(gl::ShaderType::Compute); |
| } |
| else |
| { |
| mActiveImageShaderBits[imageUnit] = shaderBits; |
| } |
| } |
| } |
| } |
| |
| void ProgramExecutable::setSamplerUniformTextureTypeAndFormat( |
| size_t textureUnitIndex, |
| std::vector<SamplerBinding> &samplerBindings) |
| { |
| bool foundBinding = false; |
| TextureType foundType = TextureType::InvalidEnum; |
| SamplerFormat foundFormat = SamplerFormat::InvalidEnum; |
| |
| for (const SamplerBinding &binding : samplerBindings) |
| { |
| // 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) |
| { |
| if (!foundBinding) |
| { |
| foundBinding = true; |
| foundType = binding.textureType; |
| foundFormat = binding.format; |
| } |
| else |
| { |
| if (foundType != binding.textureType) |
| { |
| foundType = TextureType::InvalidEnum; |
| } |
| if (foundFormat != binding.format) |
| { |
| foundFormat = SamplerFormat::InvalidEnum; |
| } |
| } |
| } |
| } |
| } |
| |
| mActiveSamplerTypes[textureUnitIndex] = foundType; |
| mActiveSamplerFormats[textureUnitIndex] = foundFormat; |
| } |
| |
| bool ProgramExecutable::linkValidateGlobalNames( |
| InfoLog &infoLog, |
| const ShaderMap<const ProgramState *> &programStates) const |
| { |
| std::unordered_map<std::string, const sh::ShaderVariable *> uniformMap; |
| using BlockAndFieldPair = std::pair<const sh::InterfaceBlock *, const sh::ShaderVariable *>; |
| std::unordered_map<std::string, std::vector<BlockAndFieldPair>> uniformBlockFieldMap; |
| |
| for (ShaderType shaderType : kAllGraphicsShaderTypes) |
| { |
| const ProgramState *programState = programStates[shaderType]; |
| if (!programState) |
| { |
| continue; |
| } |
| Shader *shader = programState->getAttachedShader(shaderType); |
| if (!shader) |
| { |
| continue; |
| } |
| |
| // Build a map of Uniforms |
| const std::vector<sh::ShaderVariable> uniforms = shader->getUniforms(); |
| for (const auto &uniform : uniforms) |
| { |
| uniformMap[uniform.name] = &uniform; |
| } |
| |
| // Build a map of Uniform Blocks |
| // This will also detect any field name conflicts between Uniform Blocks without instance |
| // names |
| const std::vector<sh::InterfaceBlock> &uniformBlocks = shader->getUniformBlocks(); |
| for (const auto &uniformBlock : uniformBlocks) |
| { |
| // Only uniform blocks without an instance name can create a conflict with their field |
| // names |
| if (!uniformBlock.instanceName.empty()) |
| { |
| continue; |
| } |
| |
| for (const auto &field : uniformBlock.fields) |
| { |
| if (!uniformBlockFieldMap.count(field.name)) |
| { |
| // First time we've seen this uniform block field name, so add the |
| // (Uniform Block, Field) pair immediately since there can't be a conflict yet |
| BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field); |
| std::vector<BlockAndFieldPair> newUniformBlockList; |
| newUniformBlockList.push_back(blockAndFieldPair); |
| uniformBlockFieldMap[field.name] = newUniformBlockList; |
| continue; |
| } |
| |
| // We've seen this name before. |
| // We need to check each of the uniform blocks that contain a field with this name |
| // to see if there's a conflict or not. |
| std::vector<BlockAndFieldPair> prevBlockFieldPairs = |
| uniformBlockFieldMap[field.name]; |
| for (const auto &prevBlockFieldPair : prevBlockFieldPairs) |
| { |
| const sh::InterfaceBlock *prevUniformBlock = prevBlockFieldPair.first; |
| const sh::ShaderVariable *prevUniformBlockField = prevBlockFieldPair.second; |
| |
| if (uniformBlock.isSameInterfaceBlockAtLinkTime(*prevUniformBlock)) |
| { |
| // The same uniform block should, by definition, contain the same field name |
| continue; |
| } |
| |
| // The uniform blocks don't match, so check if the necessary field properties |
| // also match |
| if ((field.name == prevUniformBlockField->name) && |
| (field.type == prevUniformBlockField->type) && |
| (field.precision == prevUniformBlockField->precision)) |
| { |
| infoLog << "Name conflicts between uniform block field names: " |
| << field.name; |
| return false; |
| } |
| } |
| |
| // No conflict, so record this pair |
| BlockAndFieldPair blockAndFieldPair(&uniformBlock, &field); |
| uniformBlockFieldMap[field.name].push_back(blockAndFieldPair); |
| } |
| } |
| } |
| |
| // Validate no uniform names conflict with attribute names |
| const ProgramState *programState = programStates[ShaderType::Vertex]; |
| if (programState) |
| { |
| Shader *vertexShader = programState->getAttachedShader(ShaderType::Vertex); |
| if (vertexShader) |
| { |
| for (const auto &attrib : vertexShader->getActiveAttributes()) |
| { |
| if (uniformMap.count(attrib.name)) |
| { |
| infoLog << "Name conflicts between a uniform and an attribute: " << attrib.name; |
| return false; |
| } |
| } |
| } |
| } |
| |
| // Validate no Uniform Block fields conflict with other Uniforms |
| for (const auto &uniformBlockField : uniformBlockFieldMap) |
| { |
| const std::string &fieldName = uniformBlockField.first; |
| if (uniformMap.count(fieldName)) |
| { |
| infoLog << "Name conflicts between a uniform and a uniform block field: " << fieldName; |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| void ProgramExecutable::updateCanDrawWith() |
| { |
| mCanDrawWith = |
| (hasLinkedShaderStage(ShaderType::Vertex) && hasLinkedShaderStage(ShaderType::Fragment)); |
| } |
| |
| void ProgramExecutable::saveLinkedStateInfo(const ProgramState &state) |
| { |
| for (ShaderType shaderType : getLinkedShaderStages()) |
| { |
| Shader *shader = state.getAttachedShader(shaderType); |
| ASSERT(shader); |
| mLinkedOutputVaryings[shaderType] = shader->getOutputVaryings(); |
| mLinkedInputVaryings[shaderType] = shader->getInputVaryings(); |
| mLinkedShaderVersions[shaderType] = shader->getShaderVersion(); |
| } |
| } |
| |
| } // namespace gl |