blob: fdc450892287d0b97aaccc9db6369e4c817c460d [file] [log] [blame]
//
// 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_