| // | |
| // 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/SimpleMutex.h" | |
| #include "common/angleutils.h" | |
| #include "common/hash_containers.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; | |
| } | |
| bool isBinaryReady(const Context *context); | |
| ANGLE_INLINE void cacheProgramBinaryIfNecessary(const Context *context) | |
| { | |
| // This function helps ensure the program binary is cached, even if the backend waits for | |
| // post-link tasks without the knowledge of the front-end. | |
| if (!mIsBinaryCached && !mState.mExecutable->mBinaryRetrieveableHint && | |
| mState.mExecutable->mPostLinkSubTasks.empty()) | |
| { | |
| cacheProgramBinaryIfNotAlready(context); | |
| } | |
| } | |
| 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() { mRefCount++; } | |
| ANGLE_INLINE void release(const Context *context) | |
| { | |
| 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(); | |
| } | |
| // Try to resolve linking. Inlined to make sure its overhead is as low as possible. | |
| ANGLE_INLINE void resolveLink(const Context *context) | |
| { | |
| if (ANGLE_UNLIKELY(mLinkingState)) | |
| { | |
| resolveLinkImpl(context); | |
| } | |
| } | |
| // Writes a program's binary to |mBinary|. | |
| angle::Result serialize(const Context *context); | |
| const angle::MemoryBuffer &getSerializedBinary() const { return mBinary; } | |
| 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(); | |
| } | |
| 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 syncExecutableOnSuccessfulLink(); | |
| 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(); | |
| // Block until linking is finished and resolve it. | |
| void resolveLinkImpl(const Context *context); | |
| // Block until post-link tasks are finished. | |
| void waitForPostLinkTasks(const Context *context); | |
| void postResolveLink(const Context *context); | |
| void cacheProgramBinaryIfNotAlready(const Context *context); | |
| void dumpProgramInfo(const Context *context) const; | |
| rx::UniqueSerial mSerial; | |
| ProgramState mState; | |
| rx::ProgramImpl *mProgram; | |
| bool mValidated; | |
| // Flag to indicate that the program can be deleted when no longer in use | |
| bool mDeleteStatus; | |
| // Whether the program binary is implicitly cached yet. This is usually done in | |
| // |resolveLinkImpl|, but may be deferred in the presence of post-link tasks. In that case, | |
| // |waitForPostLinkTasks| would cache the binary. However, if the wait on the tasks is done by | |
| // the backend itself, this caching will not be done. This flag is used to make sure the binary | |
| // is eventually cached at some point in the future. | |
| bool mIsBinaryCached; | |
| bool mLinked; | |
| std::unique_ptr<LinkingState> mLinkingState; | |
| egl::BlobCache::Key mProgramHash; | |
| 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; | |
| // A cache of the program binary, prepared by |serialize()|. OpenGL requires the application to | |
| // query the length of the binary first (requiring a call to |serialize()|), and then get the | |
| // actual binary. This cache ensures the second call does not need to call |serialize()| again. | |
| angle::MemoryBuffer mBinary; | |
| angle::SimpleMutex mHistogramMutex; | |
| }; | |
| } // namespace gl | |
| #endif // LIBANGLE_PROGRAM_H_ |