// // Copyright (c) 2002-2014 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 #include #include #include #include #include #include #include #include "common/angleutils.h" #include "common/mathutil.h" #include "common/Optional.h" #include "libANGLE/Constants.h" #include "libANGLE/Debug.h" #include "libANGLE/Error.h" #include "libANGLE/RefCountObject.h" #include "libANGLE/Uniform.h" #include "libANGLE/angletypes.h" namespace rx { class GLImplFactory; class ProgramImpl; struct TranslatedAttribute; } namespace gl { struct Caps; class Context; class ContextState; class Shader; class ShaderProgramManager; class State; class InfoLog; class Buffer; class Framebuffer; extern const char * const g_fakepath; class InfoLog : angle::NonCopyable { public: InfoLog(); ~InfoLog(); size_t getLength() const; void getLog(GLsizei bufSize, GLsizei *length, char *infoLog) const; void appendSanitized(const char *message); void reset(); // This helper class ensures we append a newline after writing a line. class StreamHelper : angle::NonCopyable { public: StreamHelper(StreamHelper &&rhs) : mStream(rhs.mStream) { rhs.mStream = nullptr; } StreamHelper &operator=(StreamHelper &&rhs) { std::swap(mStream, rhs.mStream); return *this; } ~StreamHelper() { // Write newline when destroyed on the stack if (mStream) { (*mStream) << std::endl; } } template StreamHelper &operator<<(const T &value) { (*mStream) << value; return *this; } private: friend class InfoLog; StreamHelper(std::stringstream *stream) : mStream(stream) { ASSERT(stream); } std::stringstream *mStream; }; template StreamHelper operator<<(const T &value) { ensureInitialized(); StreamHelper helper(mLazyStream.get()); helper << value; return helper; } std::string str() const { return mLazyStream ? mLazyStream->str() : ""; } private: void ensureInitialized() { if (!mLazyStream) { mLazyStream.reset(new std::stringstream()); } } std::unique_ptr mLazyStream; }; // Struct used for correlating uniforms/elements of uniform arrays to handles 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; } // "arrayIndex" stores the index of the innermost GLSL array. It's zero for non-arrays. unsigned int arrayIndex; // "index" is an index of the variable. The variable contains the indices for other than the // innermost GLSL arrays. unsigned int index; // If this location was bound to an unreferenced uniform. Setting data on this uniform is a // no-op. bool ignored; }; // 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; }; // This small structure encapsulates binding sampler uniforms to active GL textures. struct SamplerBinding { SamplerBinding(GLenum textureTypeIn, size_t elementCount, bool unreferenced); SamplerBinding(const SamplerBinding &other); ~SamplerBinding(); // Necessary for retrieving active textures from the GL state. GLenum textureType; // List of all textures bound to this sampler, of type textureType. std::vector boundTextureUnits; // A note if this sampler is an unreferenced uniform. bool unreferenced; }; // A varying with tranform feedback enabled. If it's an array, either the whole array or one of its // elements specified by 'arrayIndex' can set to be enabled. struct TransformFeedbackVarying : public sh::Varying { TransformFeedbackVarying(const sh::Varying &varyingIn, GLuint index) : sh::Varying(varyingIn), arrayIndex(index) { ASSERT(!isArrayOfArrays()); } std::string nameWithArrayIndex() const { std::stringstream fullNameStr; fullNameStr << name; if (arrayIndex != GL_INVALID_INDEX) { fullNameStr << "[" << arrayIndex << "]"; } return fullNameStr.str(); } GLsizei size() const { return (isArray() && arrayIndex == GL_INVALID_INDEX ? getOutermostArraySize() : 1); } GLuint arrayIndex; }; struct ImageBinding { ImageBinding(size_t count); ImageBinding(GLuint imageUnit, size_t count); ImageBinding(const ImageBinding &other); ~ImageBinding(); std::vector boundImageUnits; }; using ShaderStagesMask = angle::BitSet; class ProgramState final : angle::NonCopyable { public: ProgramState(); ~ProgramState(); const std::string &getLabel(); Shader *getAttachedVertexShader() const { return mAttachedVertexShader; } Shader *getAttachedFragmentShader() const { return mAttachedFragmentShader; } Shader *getAttachedComputeShader() const { return mAttachedComputeShader; } Shader *getAttachedGeometryShader() const { return mAttachedGeometryShader; } const std::vector &getTransformFeedbackVaryingNames() const { return mTransformFeedbackVaryingNames; } GLint getTransformFeedbackBufferMode() const { return mTransformFeedbackBufferMode; } GLuint getUniformBlockBinding(GLuint uniformBlockIndex) const { ASSERT(uniformBlockIndex < mUniformBlocks.size()); return mUniformBlocks[uniformBlockIndex].binding; } GLuint getShaderStorageBlockBinding(GLuint blockIndex) const { ASSERT(blockIndex < mShaderStorageBlocks.size()); return mShaderStorageBlocks[blockIndex].binding; } const UniformBlockBindingMask &getActiveUniformBlockBindingsMask() const { return mActiveUniformBlockBindings; } const std::vector &getAttributes() const { return mAttributes; } const AttributesMask &getActiveAttribLocationsMask() const { return mActiveAttribLocationsMask; } unsigned int getMaxActiveAttribLocation() const { return mMaxActiveAttribLocation; } DrawBufferMask getActiveOutputVariables() const { return mActiveOutputVariables; } const std::vector &getOutputVariables() const { return mOutputVariables; } const std::vector &getOutputLocations() const { return mOutputLocations; } const std::vector &getUniforms() const { return mUniforms; } const std::vector &getUniformLocations() const { return mUniformLocations; } const std::vector &getUniformBlocks() const { return mUniformBlocks; } const std::vector &getShaderStorageBlocks() const { return mShaderStorageBlocks; } const std::vector &getBufferVariables() const { return mBufferVariables; } const std::vector &getSamplerBindings() const { return mSamplerBindings; } const std::vector &getImageBindings() const { return mImageBindings; } const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mComputeShaderLocalSize; } const RangeUI &getSamplerUniformRange() const { return mSamplerUniformRange; } const RangeUI &getImageUniformRange() const { return mImageUniformRange; } const RangeUI &getAtomicCounterUniformRange() const { return mAtomicCounterUniformRange; } const std::vector &getLinkedTransformFeedbackVaryings() const { return mLinkedTransformFeedbackVaryings; } const std::vector &getAtomicCounterBuffers() const { return mAtomicCounterBuffers; } GLuint getUniformIndexFromName(const std::string &name) const; GLuint getUniformIndexFromLocation(GLint location) const; Optional getSamplerIndex(GLint location) const; bool isSamplerUniformIndex(GLuint index) const; GLuint getSamplerIndexFromUniformIndex(GLuint uniformIndex) const; GLuint getAttributeLocation(const std::string &name) const; GLuint getBufferVariableIndexFromName(const std::string &name) const; int getNumViews() const { return mNumViews; } bool usesMultiview() const { return mNumViews != -1; } const ShaderStagesMask &getLinkedShaderStages() const { return mLinkedShaderStages; } private: friend class MemoryProgramCache; friend class Program; std::string mLabel; sh::WorkGroupSize mComputeShaderLocalSize; Shader *mAttachedFragmentShader; Shader *mAttachedVertexShader; Shader *mAttachedComputeShader; Shader *mAttachedGeometryShader; std::vector mTransformFeedbackVaryingNames; std::vector mLinkedTransformFeedbackVaryings; GLenum mTransformFeedbackBufferMode; // For faster iteration on the blocks currently being bound. UniformBlockBindingMask mActiveUniformBlockBindings; std::vector mAttributes; angle::BitSet mActiveAttribLocationsMask; unsigned int mMaxActiveAttribLocation; // Uniforms are sorted in order: // 1. Non-opaque uniforms // 2. Sampler uniforms // 3. Image uniforms // 4. Atomic counter uniforms // 5. Uniform block uniforms // This makes opaque uniform validation easier, since we don't need a separate list. // For generating the entries and naming them we follow the spec: GLES 3.1 November 2016 section // 7.3.1.1 Naming Active Resources. There's a separate entry for each struct member and each // inner array of an array of arrays. Names and mapped names of uniforms that are arrays include // [0] in the end. This makes implementation of queries simpler. std::vector mUniforms; std::vector mUniformLocations; std::vector mUniformBlocks; std::vector mBufferVariables; std::vector mShaderStorageBlocks; std::vector mAtomicCounterBuffers; RangeUI mSamplerUniformRange; RangeUI mImageUniformRange; RangeUI mAtomicCounterUniformRange; // An array of the samplers that are used by the program std::vector mSamplerBindings; // An array of the images that are used by the program std::vector mImageBindings; // Names and mapped names of output variables that are arrays include [0] in the end, similarly // to uniforms. std::vector mOutputVariables; std::vector mOutputLocations; DrawBufferMask mActiveOutputVariables; // Fragment output variable base types: FLOAT, INT, or UINT. Ordered by location. std::vector mOutputVariableTypes; bool mBinaryRetrieveableHint; bool mSeparable; ShaderStagesMask mLinkedShaderStages; // ANGLE_multiview. int mNumViews; }; class Program final : angle::NonCopyable, public LabeledObject { public: Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, GLuint handle); void onDestroy(const Context *context); GLuint id() const { return mHandle; } void setLabel(const std::string &label) override; const std::string &getLabel() const override; rx::ProgramImpl *getImplementation() const { return mProgram; } void attachShader(Shader *shader); void detachShader(const Context *context, Shader *shader); int getAttachedShadersCount() const; const Shader *getAttachedVertexShader() const { return mState.mAttachedVertexShader; } const Shader *getAttachedFragmentShader() const { return mState.mAttachedFragmentShader; } const Shader *getAttachedComputeShader() const { return mState.mAttachedComputeShader; } const Shader *getAttachedGeometryShader() const { return mState.mAttachedGeometryShader; } void bindAttributeLocation(GLuint index, const char *name); void bindUniformLocation(GLuint index, const char *name); // CHROMIUM_path_rendering BindingInfo getFragmentInputBindingInfo(const Context *context, GLint index) const; void bindFragmentInputLocation(GLint index, const char *name); void pathFragmentInputGen(const Context *context, GLint index, GLenum genMode, GLint components, const GLfloat *coeffs); Error link(const gl::Context *context); bool isLinked() const; bool hasLinkedVertexShader() const { return mState.mLinkedShaderStages[SHADER_VERTEX]; } bool hasLinkedFragmentShader() const { return mState.mLinkedShaderStages[SHADER_FRAGMENT]; } bool hasLinkedComputeShader() const { return mState.mLinkedShaderStages[SHADER_COMPUTE]; } Error loadBinary(const Context *context, GLenum binaryFormat, const void *binary, GLsizei length); Error saveBinary(const Context *context, GLenum *binaryFormat, void *binary, GLsizei bufSize, GLsizei *length) const; GLint getBinaryLength(const Context *context) const; void setBinaryRetrievableHint(bool retrievable); bool getBinaryRetrievableHint() const; void setSeparable(bool separable); bool isSeparable() const; int getInfoLogLength() const; void getInfoLog(GLsizei bufSize, GLsizei *length, char *infoLog) const; void getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shaders) const; GLuint getAttributeLocation(const std::string &name) const; bool isAttribLocationActive(size_t attribLocation) const; void getActiveAttribute(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) const; GLint getActiveAttributeCount() const; GLint getActiveAttributeMaxLength() const; const std::vector &getAttributes() const { return mState.mAttributes; } GLint getFragDataLocation(const std::string &name) const; size_t getOutputResourceCount() const; const std::vector &getOutputVariableTypes() const { return mState.mOutputVariableTypes; } DrawBufferMask getActiveOutputVariables() const { return mState.mActiveOutputVariables; } void getActiveUniform(GLuint index, GLsizei bufsize, GLsizei *length, GLint *size, GLenum *type, GLchar *name) const; GLint getActiveUniformCount() const; size_t getActiveBufferVariableCount() const; GLint getActiveUniformMaxLength() const; bool isValidUniformLocation(GLint location) const; const LinkedUniform &getUniformByLocation(GLint location) const; const VariableLocation &getUniformLocation(GLint location) const; const std::vector &getUniformLocations() const; const LinkedUniform &getUniformByIndex(GLuint index) const; const BufferVariable &getBufferVariableByIndex(GLuint index) const; enum SetUniformResult { SamplerChanged, NoSamplerChange, }; GLint getUniformLocation(const std::string &name) const; GLuint getUniformIndex(const std::string &name) const; void setUniform1fv(GLint location, GLsizei count, const GLfloat *v); void setUniform2fv(GLint location, GLsizei count, const GLfloat *v); void setUniform3fv(GLint location, GLsizei count, const GLfloat *v); void setUniform4fv(GLint location, GLsizei count, const GLfloat *v); SetUniformResult setUniform1iv(GLint location, GLsizei count, const GLint *v); void setUniform2iv(GLint location, GLsizei count, const GLint *v); void setUniform3iv(GLint location, GLsizei count, const GLint *v); void setUniform4iv(GLint location, GLsizei count, const GLint *v); void setUniform1uiv(GLint location, GLsizei count, const GLuint *v); void setUniform2uiv(GLint location, GLsizei count, const GLuint *v); void setUniform3uiv(GLint location, GLsizei count, const GLuint *v); void setUniform4uiv(GLint location, GLsizei count, const GLuint *v); void setUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix2x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix3x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix2x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix4x2fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix3x4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void setUniformMatrix4x3fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat *value); void getUniformfv(const Context *context, GLint location, GLfloat *params) const; void getUniformiv(const Context *context, GLint location, GLint *params) const; void getUniformuiv(const Context *context, GLint location, GLuint *params) const; void getActiveUniformBlockName(const GLuint blockIndex, GLsizei bufSize, GLsizei *length, GLchar *blockName) const; void getActiveShaderStorageBlockName(const GLuint blockIndex, GLsizei bufSize, GLsizei *length, GLchar *blockName) const; GLuint getActiveUniformBlockCount() const; GLuint getActiveAtomicCounterBufferCount() const; GLuint getActiveShaderStorageBlockCount() const; GLint getActiveUniformBlockMaxLength() const; GLuint getUniformBlockIndex(const std::string &name) const; GLuint getShaderStorageBlockIndex(const std::string &name) const; void bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding); GLuint getUniformBlockBinding(GLuint uniformBlockIndex) const; GLuint getShaderStorageBlockBinding(GLuint shaderStorageBlockIndex) const; const InterfaceBlock &getUniformBlockByIndex(GLuint index) const; const InterfaceBlock &getShaderStorageBlockByIndex(GLuint index) const; void setTransformFeedbackVaryings(GLsizei count, const GLchar *const *varyings, GLenum bufferMode); void getTransformFeedbackVarying(GLuint index, GLsizei bufSize, GLsizei *length, GLsizei *size, GLenum *type, GLchar *name) const; GLsizei getTransformFeedbackVaryingCount() const; GLsizei getTransformFeedbackVaryingMaxLength() const; GLenum getTransformFeedbackBufferMode() const; static bool linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform, bool webglCompatibility); void addRef(); void release(const Context *context); unsigned int getRefCount() const; void flagForDeletion(); bool isFlaggedForDeletion() const; void validate(const Caps &caps); bool validateSamplers(InfoLog *infoLog, const Caps &caps); bool isValidated() const; bool samplesFromTexture(const gl::State &state, GLuint textureID) const; const AttributesMask &getActiveAttribLocationsMask() const { return mState.mActiveAttribLocationsMask; } const std::vector &getSamplerBindings() const { return mState.mSamplerBindings; } const std::vector &getImageBindings() const { return mState.mImageBindings; } const sh::WorkGroupSize &getComputeShaderLocalSize() const { return mState.mComputeShaderLocalSize; } const ProgramState &getState() const { return mState; } static bool linkValidateVariablesBase(InfoLog &infoLog, const std::string &variableName, const sh::ShaderVariable &vertexVariable, const sh::ShaderVariable &fragmentVariable, bool validatePrecision); GLuint getInputResourceIndex(const GLchar *name) const; GLuint getOutputResourceIndex(const GLchar *name) const; void getInputResourceName(GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) const; void getOutputResourceName(GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) const; void getUniformResourceName(GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) const; void getBufferVariableResourceName(GLuint index, GLsizei bufSize, GLsizei *length, GLchar *name) const; const sh::Attribute &getInputResource(GLuint index) const; const sh::OutputVariable &getOutputResource(GLuint index) const; class Bindings final : angle::NonCopyable { public: Bindings(); ~Bindings(); void bindLocation(GLuint index, const std::string &name); int getBinding(const std::string &name) const; typedef std::unordered_map::const_iterator const_iterator; const_iterator begin() const; const_iterator end() const; private: std::unordered_map mBindings; }; const Bindings &getAttributeBindings() const { return mAttributeBindings; } const Bindings &getUniformLocationBindings() const { return mUniformLocationBindings; } const Bindings &getFragmentInputBindings() const { return mFragmentInputBindings; } int getNumViews() const { return mState.getNumViews(); } bool usesMultiview() const { return mState.usesMultiview(); } struct VaryingRef { const sh::Varying *get() const { return vertex ? vertex : fragment; } const sh::Varying *vertex = nullptr; const sh::Varying *fragment = nullptr; }; using MergedVaryings = std::map; private: ~Program() override; void unlink(); bool linkAttributes(const Context *context, InfoLog &infoLog); bool validateVertexAndFragmentInterfaceBlocks( const std::vector &vertexInterfaceBlocks, const std::vector &fragmentInterfaceBlocks, InfoLog &infoLog, bool webglCompatibility) const; bool linkInterfaceBlocks(const Context *context, InfoLog &infoLog); bool linkVaryings(const Context *context, InfoLog &infoLog) const; bool linkUniforms(const Context *context, InfoLog &infoLog, const Bindings &uniformLocationBindings); void linkSamplerAndImageBindings(); bool linkAtomicCounterBuffers(); void updateLinkedShaderStages(); bool areMatchingInterfaceBlocks(InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock, const sh::InterfaceBlock &fragmentInterfaceBlock, bool webglCompatibility) const; static bool linkValidateVaryings(InfoLog &infoLog, const std::string &varyingName, const sh::Varying &vertexVarying, const sh::Varying &fragmentVarying, int shaderVersion); bool linkValidateBuiltInVaryings(const Context *context, InfoLog &infoLog) const; bool linkValidateTransformFeedback(const gl::Context *context, InfoLog &infoLog, const MergedVaryings &linkedVaryings, const Caps &caps) const; bool linkValidateGlobalNames(const Context *context, InfoLog &infoLog) const; void gatherTransformFeedbackVaryings(const MergedVaryings &varyings); MergedVaryings getMergedVaryings(const Context *context) const; void linkOutputVariables(const Context *context); void setUniformValuesFromBindingQualifiers(); void gatherAtomicCounterBuffers(); void initInterfaceBlockBindings(); // Both these function update the cached uniform values and return a modified "count" // so that the uniform update doesn't overflow the uniform. template GLsizei clampUniformCount(const VariableLocation &locationInfo, GLsizei count, int vectorSize, const T *v); template GLsizei clampMatrixUniformCount(GLint location, GLsizei count, GLboolean transpose, const T *v); void updateSamplerUniform(const VariableLocation &locationInfo, GLsizei clampedCount, const GLint *v); template void getUniformInternal(const Context *context, DestT *dataOut, GLint location, GLenum nativeType, int components) const; template void getResourceName(GLuint index, const std::vector &resources, GLsizei bufSize, GLsizei *length, GLchar *name) const; ProgramState mState; rx::ProgramImpl *mProgram; bool mValidated; Bindings 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. Bindings mUniformLocationBindings; // CHROMIUM_path_rendering Bindings mFragmentInputBindings; bool mLinked; bool mDeleteStatus; // Flag to indicate that the program can be deleted when no longer in use unsigned int mRefCount; ShaderProgramManager *mResourceManager; const GLuint mHandle; InfoLog mInfoLog; // Cache for sampler validation Optional mCachedValidateSamplersResult; std::vector mTextureUnitTypesCache; }; } // namespace gl #endif // LIBANGLE_PROGRAM_H_