blob: 1a7459689d3fa2bc3f0563ad0383e306c43e0cb3 [file] [log] [blame]
// 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.
#include <GLES2/gl2.h>
#include <GLSLANG/ShaderVars.h>
#include <array>
#include <map>
#include <set>
#include <sstream>
#include <string>
#include <vector>
#include "common/angleutils.h"
#include "common/mathutil.h"
#include "common/Optional.h"
#include "libANGLE/angletypes.h"
#include "libANGLE/Constants.h"
#include "libANGLE/Debug.h"
#include "libANGLE/Error.h"
#include "libANGLE/RefCountObject.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;
struct UniformBlock;
struct LinkedUniform;
struct PackedVarying;
extern const char * const g_fakepath;
class InfoLog : angle::NonCopyable
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
StreamHelper(StreamHelper &&rhs)
: mStream(rhs.mStream)
rhs.mStream = nullptr;
StreamHelper &operator=(StreamHelper &&rhs)
std::swap(mStream, rhs.mStream);
return *this;
// Write newline when destroyed on the stack
if (mStream)
(*mStream) << std::endl;
template <typename T>
StreamHelper &operator<<(const T &value)
(*mStream) << value;
return *this;
friend class InfoLog;
StreamHelper(std::stringstream *stream)
: mStream(stream)
std::stringstream *mStream;
template <typename T>
StreamHelper operator<<(const T &value)
StreamHelper helper(&mStream);
helper << value;
return helper;
std::string str() const { return mStream.str(); }
std::stringstream mStream;
// Struct used for correlating uniforms/elements of uniform arrays to handles
struct VariableLocation
VariableLocation(const std::string &name, unsigned int element, unsigned int index);
std::string name;
unsigned int element;
unsigned int index;
// If this is a valid uniform location
bool used;
// 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)
: textureType(textureTypeIn), boundTextureUnits(elementCount, 0)
// Necessary for retrieving active textures from the GL state.
GLenum textureType;
// List of all textures bound to this sampler, of type textureType.
std::vector<GLuint> boundTextureUnits;
// 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)
std::string nameWithArrayIndex() const
std::stringstream fullNameStr;
fullNameStr << name;
if (arrayIndex != GL_INVALID_INDEX)
fullNameStr << "[" << arrayIndex << "]";
return fullNameStr.str();
GLsizei size() const { return (arrayIndex == GL_INVALID_INDEX ? elementCount() : 1); }
GLuint arrayIndex;
class ProgramState final : angle::NonCopyable
const std::string &getLabel();
const Shader *getAttachedVertexShader() const { return mAttachedVertexShader; }
const Shader *getAttachedFragmentShader() const { return mAttachedFragmentShader; }
const Shader *getAttachedComputeShader() const { return mAttachedComputeShader; }
const std::vector<std::string> &getTransformFeedbackVaryingNames() const
return mTransformFeedbackVaryingNames;
GLint getTransformFeedbackBufferMode() const { return mTransformFeedbackBufferMode; }
GLuint getUniformBlockBinding(GLuint uniformBlockIndex) const
return mUniformBlockBindings[uniformBlockIndex];
const UniformBlockBindingMask &getActiveUniformBlockBindingsMask() const
return mActiveUniformBlockBindings;
const std::vector<sh::Attribute> &getAttributes() const { return mAttributes; }
const AttributesMask &getActiveAttribLocationsMask() const
return mActiveAttribLocationsMask;
const std::map<int, VariableLocation> &getOutputLocations() const { return mOutputLocations; }
const std::vector<LinkedUniform> &getUniforms() const { return mUniforms; }
const std::vector<VariableLocation> &getUniformLocations() const { return mUniformLocations; }
const std::vector<UniformBlock> &getUniformBlocks() const { return mUniformBlocks; }
const std::vector<SamplerBinding> &getSamplerBindings() const { return mSamplerBindings; }
GLint getUniformLocation(const std::string &name) const;
GLuint getUniformIndexFromName(const std::string &name) const;
GLuint getUniformIndexFromLocation(GLint location) const;
Optional<GLuint> getSamplerIndex(GLint location) const;
bool isSamplerUniformIndex(GLuint index) const;
GLuint getSamplerIndexFromUniformIndex(GLuint uniformIndex) const;
friend class Program;
std::string mLabel;
sh::WorkGroupSize mComputeShaderLocalSize;
Shader *mAttachedFragmentShader;
Shader *mAttachedVertexShader;
Shader *mAttachedComputeShader;
std::vector<std::string> mTransformFeedbackVaryingNames;
std::vector<TransformFeedbackVarying> mLinkedTransformFeedbackVaryings;
GLenum mTransformFeedbackBufferMode;
UniformBlockBindingMask mActiveUniformBlockBindings;
std::vector<sh::Attribute> mAttributes;
angle::BitSet<MAX_VERTEX_ATTRIBS> mActiveAttribLocationsMask;
// Uniforms are sorted in order:
// 1. Non-sampler uniforms
// 2. Sampler uniforms
// 3. Uniform block uniforms
// This makes sampler validation easier, since we don't need a separate list.
std::vector<LinkedUniform> mUniforms;
std::vector<VariableLocation> mUniformLocations;
std::vector<UniformBlock> mUniformBlocks;
RangeUI mSamplerUniformRange;
// An array of the samplers that are used by the program
std::vector<gl::SamplerBinding> mSamplerBindings;
std::vector<sh::OutputVariable> mOutputVariables;
// TODO(jmadill): use unordered/hash map when available
std::map<int, VariableLocation> mOutputLocations;
bool mBinaryRetrieveableHint;
bool mSeparable;
class Program final : angle::NonCopyable, public LabeledObject
Program(rx::GLImplFactory *factory, ShaderProgramManager *manager, GLuint handle);
void destroy(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; }
void bindAttributeLocation(GLuint index, const char *name);
void bindUniformLocation(GLuint index, const char *name);
// CHROMIUM_path_rendering
BindingInfo getFragmentInputBindingInfo(GLint index) const;
void bindFragmentInputLocation(GLint index, const char *name);
void pathFragmentInputGen(GLint index,
GLenum genMode,
GLint components,
const GLfloat *coeffs);
Error link(const gl::Context *context);
bool isLinked() const;
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;
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<sh::Attribute> &getAttributes() const { return mState.mAttributes; }
GLint getFragDataLocation(const std::string &name) const;
size_t getOutputResourceCount() const;
void getActiveUniform(GLuint index,
GLsizei bufsize,
GLsizei *length,
GLint *size,
GLenum *type,
GLchar *name) const;
GLint getActiveUniformCount() const;
GLint getActiveUniformMaxLength() const;
GLint getActiveUniformi(GLuint index, GLenum pname) const;
bool isValidUniformLocation(GLint location) const;
const LinkedUniform &getUniformByLocation(GLint location) const;
const VariableLocation &getUniformLocation(GLint location) const;
const std::vector<VariableLocation> &getUniformLocations() const;
const LinkedUniform &getUniformByIndex(GLuint index) const;
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);
void 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(GLint location, GLfloat *params) const;
void getUniformiv(GLint location, GLint *params) const;
void getUniformuiv(GLint location, GLuint *params) const;
void getActiveUniformBlockName(GLuint uniformBlockIndex, GLsizei bufSize, GLsizei *length, GLchar *uniformBlockName) const;
GLuint getActiveUniformBlockCount() const;
GLint getActiveUniformBlockMaxLength() const;
GLuint getUniformBlockIndex(const std::string &name) const;
void bindUniformBlock(GLuint uniformBlockIndex, GLuint uniformBlockBinding);
GLuint getUniformBlockBinding(GLuint uniformBlockIndex) const;
const UniformBlock &getUniformBlockByIndex(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);
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<SamplerBinding> &getSamplerBindings() const
return mState.mSamplerBindings;
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;
class Bindings final : angle::NonCopyable
void bindLocation(GLuint index, const std::string &name);
int getBinding(const std::string &name) const;
typedef std::unordered_map<std::string, GLuint>::const_iterator const_iterator;
const_iterator begin() const;
const_iterator end() const;
std::unordered_map<std::string, GLuint> mBindings;
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<std::string, VaryingRef>;
void unlink();
void resetUniformBlockBindings();
bool linkAttributes(const ContextState &data, InfoLog &infoLog);
bool validateUniformBlocksCount(GLuint maxUniformBlocks,
const std::vector<sh::InterfaceBlock> &block,
const std::string &errorMessage,
InfoLog &infoLog) const;
bool validateVertexAndFragmentInterfaceBlocks(
const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks,
const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks,
InfoLog &infoLog) const;
bool linkUniformBlocks(InfoLog &infoLog, const Caps &caps);
bool linkVaryings(InfoLog &infoLog) const;
bool linkUniforms(InfoLog &infoLog, const Caps &caps, const Bindings &uniformLocationBindings);
void linkSamplerBindings();
bool areMatchingInterfaceBlocks(InfoLog &infoLog,
const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock) const;
static bool linkValidateVaryings(InfoLog &infoLog,
const std::string &varyingName,
const sh::Varying &vertexVarying,
const sh::Varying &fragmentVarying,
int shaderVersion);
bool linkValidateBuiltInVaryings(InfoLog &infoLog) const;
bool linkValidateTransformFeedback(const gl::Context *context,
InfoLog &infoLog,
const MergedVaryings &linkedVaryings,
const Caps &caps) const;
void gatherTransformFeedbackVaryings(const MergedVaryings &varyings);
MergedVaryings getMergedVaryings() const;
std::vector<PackedVarying> getPackedVaryings(const MergedVaryings &mergedVaryings) const;
void linkOutputVariables();
void setUniformValuesFromBindingQualifiers();
void gatherInterfaceBlockInfo();
template <typename VarT>
void defineUniformBlockMembers(const std::vector<VarT> &fields,
const std::string &prefix,
int blockIndex);
void defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenum shaderType);
// Both these function update the cached uniform values and return a modified "count"
// so that the uniform update doesn't overflow the uniform.
template <typename T>
GLsizei setUniformInternal(GLint location, GLsizei count, int vectorSize, const T *v);
template <size_t cols, size_t rows, typename T>
GLsizei setMatrixUniformInternal(GLint location,
GLsizei count,
GLboolean transpose,
const T *v);
template <typename T>
void updateSamplerUniform(const VariableLocation &locationInfo,
const uint8_t *destPointer,
GLsizei clampedCount,
const T *v);
template <typename DestT>
void getUniformInternal(GLint location, DestT *dataOut) 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<bool> mCachedValidateSamplersResult;
std::vector<GLenum> mTextureUnitTypesCache;
} // namespace gl