| // |
| // Copyright 2002 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. |
| // |
| |
| // Program.h: Defines the gl::Program class. Implements GL program objects |
| // and related functionality. [OpenGL ES 2.0.24] section 2.10.3 page 28. |
| |
| #ifndef LIBANGLE_PROGRAM_H_ |
| #define LIBANGLE_PROGRAM_H_ |
| |
| #include <GLES2/gl2.h> |
| #include <GLSLANG/ShaderVars.h> |
| |
| #include <array> |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #include "common/Optional.h" |
| #include "common/angleutils.h" |
| #include "common/mathutil.h" |
| #include "common/utilities.h" |
| |
| #include "libANGLE/Constants.h" |
| #include "libANGLE/Debug.h" |
| #include "libANGLE/Error.h" |
| #include "libANGLE/InfoLog.h" |
| #include "libANGLE/ProgramExecutable.h" |
| #include "libANGLE/ProgramLinkedResources.h" |
| #include "libANGLE/RefCountObject.h" |
| #include "libANGLE/Shader.h" |
| #include "libANGLE/Uniform.h" |
| #include "libANGLE/angletypes.h" |
| |
| namespace rx |
| { |
| class GLImplFactory; |
| class ProgramImpl; |
| class LinkSubTask; |
| struct TranslatedAttribute; |
| } // namespace rx |
| |
| namespace gl |
| { |
| class Buffer; |
| class BinaryInputStream; |
| class BinaryOutputStream; |
| struct Caps; |
| class Context; |
| struct Extensions; |
| class Framebuffer; |
| class ProgramExecutable; |
| class ShaderProgramManager; |
| class State; |
| struct UnusedUniform; |
| struct Version; |
| |
| extern const char *const g_fakepath; |
| |
| enum class LinkMismatchError |
| { |
| // Shared |
| NO_MISMATCH, |
| TYPE_MISMATCH, |
| ARRAYNESS_MISMATCH, |
| ARRAY_SIZE_MISMATCH, |
| PRECISION_MISMATCH, |
| STRUCT_NAME_MISMATCH, |
| FIELD_NUMBER_MISMATCH, |
| FIELD_NAME_MISMATCH, |
| |
| // Varying specific |
| INTERPOLATION_TYPE_MISMATCH, |
| INVARIANCE_MISMATCH, |
| |
| // Uniform specific |
| BINDING_MISMATCH, |
| LOCATION_MISMATCH, |
| OFFSET_MISMATCH, |
| INSTANCE_NAME_MISMATCH, |
| FORMAT_MISMATCH, |
| |
| // Interface block specific |
| LAYOUT_QUALIFIER_MISMATCH, |
| MATRIX_PACKING_MISMATCH, |
| |
| // I/O block specific |
| FIELD_LOCATION_MISMATCH, |
| FIELD_STRUCT_NAME_MISMATCH, |
| }; |
| |
| void LogLinkMismatch(InfoLog &infoLog, |
| const std::string &variableName, |
| const char *variableType, |
| LinkMismatchError linkError, |
| const std::string &mismatchedStructOrBlockFieldName, |
| ShaderType shaderType1, |
| ShaderType shaderType2); |
| |
| bool IsActiveInterfaceBlock(const sh::InterfaceBlock &interfaceBlock); |
| |
| // Struct used for correlating uniforms/elements of uniform arrays to handles |
| ANGLE_ENABLE_STRUCT_PADDING_WARNINGS |
| struct VariableLocation |
| { |
| static constexpr unsigned int kUnused = GL_INVALID_INDEX; |
| |
| VariableLocation(); |
| VariableLocation(unsigned int arrayIndex, unsigned int index); |
| |
| // If used is false, it means this location is only used to fill an empty space in an array, |
| // and there is no corresponding uniform variable for this location. It can also mean the |
| // uniform was optimized out by the implementation. |
| bool used() const { return (index != kUnused); } |
| void markUnused() { index = kUnused; } |
| void markIgnored() { ignored = true; } |
| |
| bool operator==(const VariableLocation &other) const |
| { |
| return arrayIndex == other.arrayIndex && index == other.index; |
| } |
| |
| // "index" is an index of the variable. The variable contains the indices for other than the |
| // innermost GLSL arrays. |
| uint32_t index; |
| |
| // "arrayIndex" stores the index of the innermost GLSL array. It's zero for non-arrays. |
| uint32_t arrayIndex : 31; |
| // If this location was bound to an unreferenced uniform. Setting data on this uniform is a |
| // no-op. |
| uint32_t ignored : 1; |
| }; |
| ANGLE_DISABLE_STRUCT_PADDING_WARNINGS |
| |
| // Information about a variable binding. |
| // Currently used by CHROMIUM_path_rendering |
| struct BindingInfo |
| { |
| // The type of binding, for example GL_FLOAT_VEC3. |
| // This can be GL_NONE if the variable is optimized away. |
| GLenum type; |
| |
| // This is the name of the variable in |
| // the translated shader program. Note that |
| // this can be empty in the case where the |
| // variable has been optimized away. |
| std::string name; |
| |
| // True if the binding is valid, otherwise false. |
| bool valid; |
| }; |
| |
| struct ProgramBinding |
| { |
| ProgramBinding() : location(GL_INVALID_INDEX), aliased(false) {} |
| ProgramBinding(GLuint index) : location(index), aliased(false) {} |
| |
| GLuint location; |
| // Whether another binding was set that may potentially alias this. |
| bool aliased; |
| }; |
| |
| class ProgramBindings final : angle::NonCopyable |
| { |
| public: |
| ProgramBindings(); |
| ~ProgramBindings(); |
| |
| void bindLocation(GLuint index, const std::string &name); |
| int getBindingByName(const std::string &name) const; |
| template <typename T> |
| int getBinding(const T &variable) const; |
| |
| using const_iterator = angle::HashMap<std::string, GLuint>::const_iterator; |
| const_iterator begin() const; |
| const_iterator end() const; |
| |
| std::map<std::string, GLuint> getStableIterationMap() const; |
| |
| private: |
| angle::HashMap<std::string, GLuint> mBindings; |
| }; |
| |
| // Uniforms and Fragment Outputs require special treatment due to array notation (e.g., "[0]") |
| class ProgramAliasedBindings final : angle::NonCopyable |
| { |
| public: |
| ProgramAliasedBindings(); |
| ~ProgramAliasedBindings(); |
| |
| void bindLocation(GLuint index, const std::string &name); |
| int getBindingByName(const std::string &name) const; |
| int getBindingByLocation(GLuint location) const; |
| template <typename T> |
| int getBinding(const T &variable) const; |
| |
| using const_iterator = angle::HashMap<std::string, ProgramBinding>::const_iterator; |
| const_iterator begin() const; |
| const_iterator end() const; |
| |
| std::map<std::string, ProgramBinding> getStableIterationMap() const; |
| |
| private: |
| angle::HashMap<std::string, ProgramBinding> mBindings; |
| }; |
| |
| class ProgramState final : angle::NonCopyable |
| { |
| public: |
| ProgramState(rx::GLImplFactory *factory); |
| ~ProgramState(); |
| |
| const std::string &getLabel(); |
| |
| SharedCompiledShaderState getAttachedShader(ShaderType shaderType) const; |
| const ShaderMap<SharedCompiledShaderState> &getAttachedShaders() const |
| { |
| return mAttachedShaders; |
| } |
| const std::vector<std::string> &getTransformFeedbackVaryingNames() const |
| { |
| return mTransformFeedbackVaryingNames; |
| } |
| GLint getTransformFeedbackBufferMode() const { return mTransformFeedbackBufferMode; } |
| |
| bool hasAnyAttachedShader() const; |
| |
| const ProgramBindings &getAttributeBindings() const { return mAttributeBindings; } |
| const ProgramAliasedBindings &getUniformLocationBindings() const |
| { |
| return mUniformLocationBindings; |
| } |
| const ProgramAliasedBindings &getFragmentOutputLocations() const |
| { |
| return mFragmentOutputLocations; |
| } |
| const ProgramAliasedBindings &getFragmentOutputIndexes() const |
| { |
| return mFragmentOutputIndexes; |
| } |
| |
| const ProgramExecutable &getExecutable() const |
| { |
| ASSERT(mExecutable); |
| return *mExecutable; |
| } |
| ProgramExecutable &getExecutable() |
| { |
| ASSERT(mExecutable); |
| return *mExecutable; |
| } |
| |
| const SharedProgramExecutable &getSharedExecutable() const |
| { |
| ASSERT(mExecutable); |
| return mExecutable; |
| } |
| |
| const std::string &getLabel() const { return mLabel; } |
| |
| bool hasBinaryRetrieveableHint() const { return mBinaryRetrieveableHint; } |
| |
| bool isSeparable() const { return mSeparable; } |
| |
| ShaderType getAttachedTransformFeedbackStage() const; |
| |
| private: |
| friend class MemoryProgramCache; |
| friend class Program; |
| |
| void updateActiveSamplers(); |
| void updateProgramInterfaceInputs(); |
| void updateProgramInterfaceOutputs(); |
| |
| // Scans the sampler bindings for type conflicts with sampler 'textureUnitIndex'. |
| void setSamplerUniformTextureTypeAndFormat(size_t textureUnitIndex); |
| |
| std::string mLabel; |
| |
| ShaderMap<SharedCompileJob> mShaderCompileJobs; |
| ShaderMap<SharedCompiledShaderState> mAttachedShaders; |
| |
| std::vector<std::string> mTransformFeedbackVaryingNames; |
| GLenum mTransformFeedbackBufferMode; |
| |
| bool mBinaryRetrieveableHint; |
| bool mSeparable; |
| |
| ProgramBindings mAttributeBindings; |
| |
| // Note that this has nothing to do with binding layout qualifiers that can be set for some |
| // uniforms in GLES3.1+. It is used to pre-set the location of uniforms. |
| ProgramAliasedBindings mUniformLocationBindings; |
| |
| // EXT_blend_func_extended |
| ProgramAliasedBindings mFragmentOutputLocations; |
| ProgramAliasedBindings mFragmentOutputIndexes; |
| |
| InfoLog mInfoLog; |
| |
| // The result of the link. State that is not the link output should remain in ProgramState, |
| // while the link output should be placed in ProgramExecutable. |
| // |
| // This is a shared_ptr because it can be "installed" in the context as part of the rendering |
| // context. Similarly, it can be installed in a program pipeline. Once the executable is |
| // installed, the actual Program should not be referenced; it may have been unsuccessfully |
| // relinked and its executable in an unusable state. |
| SharedProgramExecutable mExecutable; |
| }; |
| |
| struct ProgramVaryingRef |
| { |
| const sh::ShaderVariable *get(ShaderType stage) const |
| { |
| ASSERT(stage == frontShaderStage || stage == backShaderStage); |
| const sh::ShaderVariable *ref = stage == frontShaderStage ? frontShader : backShader; |
| ASSERT(ref); |
| return ref; |
| } |
| |
| const sh::ShaderVariable *frontShader = nullptr; |
| const sh::ShaderVariable *backShader = nullptr; |
| ShaderType frontShaderStage = ShaderType::InvalidEnum; |
| ShaderType backShaderStage = ShaderType::InvalidEnum; |
| }; |
| |
| using ProgramMergedVaryings = std::vector<ProgramVaryingRef>; |
| |
| class Program final : public LabeledObject, public angle::Subject |
| { |
| public: |
| Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, ShaderProgramID handle); |
| void onDestroy(const Context *context); |
| |
| ShaderProgramID id() const; |
| |
| angle::Result setLabel(const Context *context, const std::string &label) override; |
| const std::string &getLabel() const override; |
| |
| ANGLE_INLINE rx::ProgramImpl *getImplementation() const |
| { |
| ASSERT(!mLinkingState); |
| return mProgram; |
| } |
| |
| void attachShader(const Context *context, Shader *shader); |
| void detachShader(const Context *context, Shader *shader); |
| int getAttachedShadersCount() const; |
| |
| Shader *getAttachedShader(ShaderType shaderType) const; |
| |
| void bindAttributeLocation(const Context *context, GLuint index, const char *name); |
| void bindUniformLocation(const Context *context, UniformLocation location, const char *name); |
| |
| // EXT_blend_func_extended |
| void bindFragmentOutputLocation(const Context *context, GLuint index, const char *name); |
| void bindFragmentOutputIndex(const Context *context, GLuint index, const char *name); |
| |
| // KHR_parallel_shader_compile |
| // Try to link the program asynchronously. As a result, background threads may be launched to |
| // execute the linking tasks concurrently. |
| angle::Result link(const Context *context, angle::JobResultExpectancy resultExpectancy); |
| |
| // Peek whether there is any running linking tasks. |
| bool isLinking() const; |
| bool hasLinkingState() const { return mLinkingState != nullptr; } |
| |
| bool isLinked() const |
| { |
| ASSERT(!mLinkingState); |
| return mLinked; |
| } |
| |
| angle::Result setBinary(const Context *context, |
| GLenum binaryFormat, |
| const void *binary, |
| GLsizei length); |
| angle::Result getBinary(Context *context, |
| GLenum *binaryFormat, |
| void *binary, |
| GLsizei bufSize, |
| GLsizei *length); |
| GLint getBinaryLength(Context *context); |
| void setBinaryRetrievableHint(bool retrievable); |
| bool getBinaryRetrievableHint() const; |
| |
| angle::Result loadBinary(const Context *context, |
| const void *binary, |
| GLsizei length, |
| egl::CacheGetResult *resultOut); |
| |
| InfoLog &getInfoLog() { return mState.mInfoLog; } |
| int getInfoLogLength() const; |
| void getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const; |
| |
| void setSeparable(const Context *context, bool separable); |
| bool isSeparable() const { return mState.mSeparable; } |
| |
| void getAttachedShaders(GLsizei maxCount, GLsizei *count, ShaderProgramID *shaders) const; |
| |
| void bindUniformBlock(UniformBlockIndex uniformBlockIndex, GLuint uniformBlockBinding); |
| |
| void setTransformFeedbackVaryings(const Context *context, |
| GLsizei count, |
| const GLchar *const *varyings, |
| GLenum bufferMode); |
| GLenum getTransformFeedbackBufferMode() const { return mState.mTransformFeedbackBufferMode; } |
| |
| ANGLE_INLINE void addRef() |
| { |
| ASSERT(!mLinkingState); |
| mRefCount++; |
| } |
| |
| ANGLE_INLINE void release(const Context *context) |
| { |
| ASSERT(!mLinkingState); |
| mRefCount--; |
| |
| if (mRefCount == 0 && mDeleteStatus) |
| { |
| deleteSelf(context); |
| } |
| } |
| |
| unsigned int getRefCount() const; |
| bool isInUse() const { return getRefCount() != 0; } |
| void flagForDeletion(); |
| bool isFlaggedForDeletion() const; |
| |
| void validate(const Caps &caps); |
| bool isValidated() const; |
| |
| const ProgramState &getState() const { return mState; } |
| |
| const ProgramBindings &getAttributeBindings() const { return mState.getAttributeBindings(); } |
| const ProgramAliasedBindings &getUniformLocationBindings() const |
| { |
| return mState.getUniformLocationBindings(); |
| } |
| const ProgramAliasedBindings &getFragmentOutputLocations() const |
| { |
| return mState.getFragmentOutputLocations(); |
| } |
| const ProgramAliasedBindings &getFragmentOutputIndexes() const |
| { |
| return mState.getFragmentOutputIndexes(); |
| } |
| |
| bool needsSync() |
| { |
| return !mOptionalLinkTasks.empty() || mState.getExecutable().hasAnyDirtyBit(); |
| } |
| angle::Result syncState(const Context *context); |
| |
| // Try to resolve linking. Inlined to make sure its overhead is as low as possible. |
| void resolveLink(const Context *context) |
| { |
| if (mLinkingState) |
| { |
| resolveLinkImpl(context); |
| } |
| } |
| |
| // Writes a program's binary to the output memory buffer. |
| angle::Result serialize(const Context *context, angle::MemoryBuffer *binaryOut); |
| |
| rx::UniqueSerial serial() const { return mSerial; } |
| |
| const ProgramExecutable &getExecutable() const { return mState.getExecutable(); } |
| ProgramExecutable &getExecutable() { return mState.getExecutable(); } |
| const SharedProgramExecutable &getSharedExecutable() const |
| { |
| return mState.getSharedExecutable(); |
| } |
| |
| void onUniformBufferStateChange(size_t uniformBufferIndex) |
| { |
| if (uniformBufferIndex >= mUniformBlockBindingMasks.size()) |
| { |
| mUniformBlockBindingMasks.resize(uniformBufferIndex + 1, UniformBlockBindingMask()); |
| } |
| getExecutable().mDirtyBits |= mUniformBlockBindingMasks[uniformBufferIndex]; |
| } |
| |
| void onPPOUniformBufferStateChange(ShaderType shaderType, |
| size_t uniformBufferIndex, |
| ProgramExecutable *ppoExecutable, |
| const ProgramPipelineUniformBlockIndexMap &blockMap); |
| |
| private: |
| class MainLinkLoadTask; |
| class MainLoadTask; |
| class MainLinkTask; |
| class MainLinkLoadEvent; |
| |
| friend class ProgramPipeline; |
| friend class MainLinkLoadTask; |
| friend class MainLoadTask; |
| friend class MainLinkTask; |
| |
| struct LinkingState; |
| ~Program() override; |
| |
| // Loads program state according to the specified binary blob. Returns true on success. |
| bool deserialize(const Context *context, BinaryInputStream &stream); |
| |
| void unlink(); |
| void setupExecutableForLink(const Context *context); |
| void deleteSelf(const Context *context); |
| |
| angle::Result linkJobImpl(const Caps &caps, |
| const Limitations &limitations, |
| const Version &clientVersion, |
| bool isWebGL, |
| LinkingVariables *linkingVariables, |
| ProgramLinkedResources *resources, |
| ProgramMergedVaryings *mergedVaryingsOut); |
| |
| void makeNewExecutable(const Context *context); |
| |
| bool linkValidateShaders(); |
| void linkShaders(); |
| bool linkAttributes(const Caps &caps, const Limitations &limitations, bool webglCompatibility); |
| bool linkVaryings(); |
| |
| bool linkUniforms(const Caps &caps, |
| const Version &clientVersion, |
| std::vector<UnusedUniform> *unusedUniformsOutOrNull, |
| GLuint *combinedImageUniformsOut); |
| |
| void updateLinkedShaderStages(); |
| |
| void initInterfaceBlockBindings(); |
| |
| // Block until linking is finished and resolve it. |
| void resolveLinkImpl(const gl::Context *context); |
| // Block until optional link tasks are finished. |
| void waitForOptionalLinkTasks(const gl::Context *context); |
| void onLinkInputChange(const gl::Context *context) |
| { |
| // The link tasks work on link input. If link input changes, they must be finished first. |
| waitForOptionalLinkTasks(context); |
| } |
| |
| void postResolveLink(const gl::Context *context); |
| void cacheProgramBinary(const gl::Context *context); |
| |
| void dumpProgramInfo(const Context *context) const; |
| |
| rx::UniqueSerial mSerial; |
| ProgramState mState; |
| rx::ProgramImpl *mProgram; |
| |
| bool mValidated; |
| bool mDeleteStatus; // Flag to indicate that the program can be deleted when no longer in use |
| |
| bool mLinked; |
| std::unique_ptr<LinkingState> mLinkingState; |
| |
| egl::BlobCache::Key mProgramHash; |
| |
| // Optional link tasks that may still be running after a link has succeeded. These tasks are |
| // not waited on in |resolveLink| as they are optimization passes. Instead, they are waited on |
| // when the program is first used. |
| std::vector<std::shared_ptr<rx::LinkSubTask>> mOptionalLinkTasks; |
| std::vector<std::shared_ptr<angle::WaitableEvent>> mOptionalLinkTaskWaitableEvents; |
| |
| unsigned int mRefCount; |
| |
| ShaderProgramManager *mResourceManager; |
| const ShaderProgramID mHandle; |
| |
| // ProgramState::mAttachedShaders holds a reference to shaders' compiled state, which is all the |
| // program and the backends require after link. The actual shaders linked to the program are |
| // stored here to support shader attach/detach and link without providing access to them in the |
| // backends. |
| ShaderMap<Shader *> mAttachedShaders; |
| |
| // To simplify dirty bits handling, instead of tracking dirtiness of both uniform block index |
| // and uniform binding index, we only track which uniform block index is dirty. And then when |
| // buffer index is dirty, we look at which uniform blocks are bound to this buffer binding index |
| // and set all of these uniform blocks dirty. This variable tracks all the uniform blocks bound |
| // to the given binding index in the form of bitmask so that we can quickly convert them to the |
| // dirty bits. |
| static constexpr size_t kFastUniformBlockBindingLimit = 8; |
| angle::FastVector<UniformBlockBindingMask, kFastUniformBlockBindingLimit> |
| mUniformBlockBindingMasks; |
| |
| std::mutex mHistogramMutex; |
| }; |
| } // namespace gl |
| |
| #endif // LIBANGLE_PROGRAM_H_ |