Commit 4c4c8e72 by Martin Radev Committed by Commit Bot

Add compute program compilation and linking support

Compute shaders can be now compiled and linked to create programs. Some tests are added to verify successful and unsuccessful compute shader linking. The patch also replaces std::array<int, 3> with a custom struct WorkGroupSize. BUG=angleproject:1442 TEST=angle_end2end_tests TEST=angle_unittests Change-Id: I4ab0ac05755d0167a6d2a798f8d7f1516cf54d84 Reviewed-on: https://chromium-review.googlesource.com/366740Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
parent 63e1ec5c
......@@ -49,7 +49,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API.
// It is incremented every time the API changes.
#define ANGLE_SH_VERSION 153
#define ANGLE_SH_VERSION 154
typedef enum {
SH_GLES2_SPEC,
......@@ -501,7 +501,7 @@ COMPILER_EXPORT const std::vector<sh::Varying> *ShGetVaryings(const ShHandle han
COMPILER_EXPORT const std::vector<sh::Attribute> *ShGetAttributes(const ShHandle handle);
COMPILER_EXPORT const std::vector<sh::OutputVariable> *ShGetOutputVariables(const ShHandle handle);
COMPILER_EXPORT const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handle);
COMPILER_EXPORT std::array<int, 3> ShGetComputeShaderLocalGroupSize(const ShHandle handle);
COMPILER_EXPORT sh::WorkGroupSize ShGetComputeShaderLocalGroupSize(const ShHandle handle);
typedef struct
{
......
......@@ -10,9 +10,9 @@
#ifndef GLSLANG_SHADERVARS_H_
#define GLSLANG_SHADERVARS_H_
#include <algorithm>
#include <string>
#include <vector>
#include <algorithm>
// Assume ShaderLang.h is included before ShaderVars.h, for sh::GLenum
// Note: make sure to increment ANGLE_SH_VERSION when changing ShaderVars.h
......@@ -216,6 +216,32 @@ struct COMPILER_EXPORT InterfaceBlock
std::vector<InterfaceBlockField> fields;
};
struct COMPILER_EXPORT WorkGroupSize
{
void fill(int fillValue);
void setLocalSize(int localSizeX, int localSizeY, int localSizeZ);
int &operator[](size_t index);
int operator[](size_t index) const;
size_t size() const;
// Checks whether two work group size declarations match.
// Two work group size declarations are the same if the explicitly specified elements are the
// same or if one of them is specified as one and the other one is not specified
bool isWorkGroupSizeMatching(const WorkGroupSize &right) const;
// Checks whether any of the values are set.
bool isAnyValueSet() const;
// Checks whether all of the values are set.
bool isDeclared() const;
// Checks whether either all of the values are set, or none of them are.
bool isLocalSizeValid() const;
int localSizeQualifiers[3];
};
} // namespace sh
#endif // GLSLANG_SHADERVARS_H_
......@@ -11,6 +11,7 @@
#include <array>
#include "common/debug.h"
#include "GLSLANG/ShaderLang.h"
//
// Precision qualifiers
......@@ -377,8 +378,6 @@ enum TLayoutBlockStorage
EbsStd140
};
using TLocalSize = std::array<int, 3>;
struct TLayoutQualifier
{
int location;
......@@ -386,8 +385,7 @@ struct TLayoutQualifier
TLayoutBlockStorage blockStorage;
// Compute shader layout qualifiers.
// -1 means unspecified.
TLocalSize localSize;
sh::WorkGroupSize localSize;
static TLayoutQualifier create()
{
......@@ -405,19 +403,12 @@ struct TLayoutQualifier
bool isEmpty() const
{
return location == -1 && matrixPacking == EmpUnspecified &&
blockStorage == EbsUnspecified && localSize[0] == -1 && localSize[1] == -1 &&
localSize[2] == -1;
}
bool isGroupSizeSpecified() const
{
return std::any_of(localSize.begin(), localSize.end(),
[](int value) { return value != -1; });
blockStorage == EbsUnspecified && !localSize.isAnyValueSet();
}
bool isCombinationValid() const
{
bool workSizeSpecified = isGroupSizeSpecified();
bool workSizeSpecified = localSize.isAnyValueSet();
bool otherLayoutQualifiersSpecified =
(location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified);
......@@ -425,23 +416,13 @@ struct TLayoutQualifier
return !(workSizeSpecified && otherLayoutQualifiersSpecified);
}
bool isLocalSizeEqual(const TLocalSize &localSizeIn) const
bool isLocalSizeEqual(const sh::WorkGroupSize &localSizeIn) const
{
for (size_t i = 0u; i < localSize.size(); ++i)
{
bool result =
(localSize[i] == localSizeIn[i] || (localSize[i] == 1 && localSizeIn[i] == -1) ||
(localSize[i] == -1 && localSizeIn[i] == 1));
if (!result)
{
return false;
}
}
return true;
return localSize.isWorkGroupSizeMatching(localSizeIn);
}
};
inline const char *getLocalSizeString(size_t dimension)
inline const char *getWorkGroupSizeString(size_t dimension)
{
switch (dimension)
{
......
......@@ -86,7 +86,7 @@ class TCompiler : public TShHandleBase
TInfoSink& getInfoSink() { return infoSink; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
const TLocalSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
const sh::WorkGroupSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
// Clears the results from the previous compilation.
void clearResults();
......@@ -237,7 +237,7 @@ class TCompiler : public TShHandleBase
// compute shader local group size
bool mComputeShaderLocalSizeDeclared;
TLocalSize mComputeShaderLocalSize;
sh::WorkGroupSize mComputeShaderLocalSize;
// name hashing.
ShHashFunction64 hashFunction;
......
......@@ -1044,12 +1044,12 @@ void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
bool TParseContext::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
const TLayoutQualifier &layoutQualifier)
{
const TLocalSize &localSize = layoutQualifier.localSize;
const sh::WorkGroupSize &localSize = layoutQualifier.localSize;
for (size_t i = 0u; i < localSize.size(); ++i)
{
if (localSize[i] != -1)
{
error(location, "invalid layout qualifier:", getLocalSizeString(i),
error(location, "invalid layout qualifier:", getWorkGroupSizeString(i),
"only valid when used with 'in' in a compute shader global layout declaration");
return false;
}
......@@ -1119,9 +1119,9 @@ void TParseContext::handlePragmaDirective(const TSourceLoc &loc,
mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl);
}
TLocalSize TParseContext::getComputeShaderLocalSize() const
sh::WorkGroupSize TParseContext::getComputeShaderLocalSize() const
{
TLocalSize result;
sh::WorkGroupSize result;
for (size_t i = 0u; i < result.size(); ++i)
{
if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1)
......@@ -1895,7 +1895,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
return;
}
if (!layoutQualifier.isGroupSizeSpecified())
if (!layoutQualifier.localSize.isAnyValueSet())
{
error(typeQualifier.line, "No local work group size specified", "layout");
return;
......@@ -1921,7 +1921,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
<< maxComputeWorkGroupSizeValue;
const std::string &errorMessage = errorMessageStream.str();
error(typeQualifier.line, "invalid value:", getLocalSizeString(i),
error(typeQualifier.line, "invalid value:", getWorkGroupSizeString(i),
errorMessage.c_str());
return;
}
......@@ -3052,12 +3052,12 @@ void TParseContext::parseLocalSize(const TString &qualifierType,
const TSourceLoc &intValueLine,
const std::string &intValueString,
size_t index,
TLocalSize *localSize)
sh::WorkGroupSize *localSize)
{
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
if (intValue < 1)
{
std::string errorMessage = std::string(getLocalSizeString(index)) + " must be positive";
std::string errorMessage = std::string(getWorkGroupSizeString(index)) + " must be positive";
error(intValueLine, "out of range:", intValueString.c_str(), errorMessage.c_str());
}
(*localSize)[index] = intValue;
......@@ -3136,7 +3136,7 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif
{
error(rightQualifierLocation,
"Cannot have multiple different work group size specifiers",
getLocalSizeString(i));
getWorkGroupSizeString(i));
}
joinedQualifier.localSize[i] = rightQualifier.localSize[i];
}
......
......@@ -117,7 +117,7 @@ class TParseContext : angle::NonCopyable
void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
TLocalSize getComputeShaderLocalSize() const;
sh::WorkGroupSize getComputeShaderLocalSize() const;
// This method is guaranteed to succeed, even if no variable with 'name' exists.
const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
......@@ -308,7 +308,7 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &intValueLine,
const std::string &intValueString,
size_t index,
TLocalSize *localSize);
sh::WorkGroupSize *localSize);
TLayoutQualifier parseLayoutQualifier(
const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
......@@ -437,7 +437,7 @@ class TParseContext : angle::NonCopyable
// keep track of local group size declared in layout. It should be declared only once.
bool mComputeShaderLocalSizeDeclared;
TLocalSize mComputeShaderLocalSize;
sh::WorkGroupSize mComputeShaderLocalSize;
};
int PaParseStrings(
......
......@@ -358,7 +358,7 @@ const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handl
return GetShaderVariables<sh::InterfaceBlock>(handle);
}
std::array<int, 3> ShGetComputeShaderLocalGroupSize(const ShHandle handle)
sh::WorkGroupSize ShGetComputeShaderLocalGroupSize(const ShHandle handle)
{
ASSERT(handle);
......
......@@ -418,4 +418,71 @@ bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other)
return true;
}
void WorkGroupSize::fill(int fillValue)
{
localSizeQualifiers[0] = fillValue;
localSizeQualifiers[1] = fillValue;
localSizeQualifiers[2] = fillValue;
}
void WorkGroupSize::setLocalSize(int localSizeX, int localSizeY, int localSizeZ)
{
localSizeQualifiers[0] = localSizeX;
localSizeQualifiers[1] = localSizeY;
localSizeQualifiers[2] = localSizeZ;
}
// check that if one of them is less than 1, then all of them are.
// Or if one is positive, then all of them are positive.
bool WorkGroupSize::isLocalSizeValid() const
{
return (
(localSizeQualifiers[0] < 1 && localSizeQualifiers[1] < 1 && localSizeQualifiers[2] < 1) ||
(localSizeQualifiers[0] > 0 && localSizeQualifiers[1] > 0 && localSizeQualifiers[2] > 0));
}
bool WorkGroupSize::isAnyValueSet() const
{
return localSizeQualifiers[0] > 0 || localSizeQualifiers[1] > 0 || localSizeQualifiers[2] > 0;
}
bool WorkGroupSize::isDeclared() const
{
bool localSizeDeclared = localSizeQualifiers[0] > 0;
ASSERT(isLocalSizeValid());
return localSizeDeclared;
}
bool WorkGroupSize::isWorkGroupSizeMatching(const WorkGroupSize &right) const
{
for (size_t i = 0u; i < size(); ++i)
{
bool result = (localSizeQualifiers[i] == right.localSizeQualifiers[i] ||
(localSizeQualifiers[i] == 1 && right.localSizeQualifiers[i] == -1) ||
(localSizeQualifiers[i] == -1 && right.localSizeQualifiers[i] == 1));
if (!result)
{
return false;
}
}
return true;
}
int &WorkGroupSize::operator[](size_t index)
{
ASSERT(index < size());
return localSizeQualifiers[index];
}
int WorkGroupSize::operator[](size_t index) const
{
ASSERT(index < size());
return localSizeQualifiers[index];
}
size_t WorkGroupSize::size() const
{
return 3u;
}
} // namespace sh
......@@ -80,7 +80,7 @@ void TranslatorESSL::translate(TIntermNode *root, int compileOptions)
if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
{
const TLocalSize &localSize = getComputeShaderLocalSize();
const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
<< ", local_size_z=" << localSize[2] << ") in;\n";
}
......
......@@ -167,7 +167,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared())
{
const TLocalSize &localSize = getComputeShaderLocalSize();
const sh::WorkGroupSize &localSize = getComputeShaderLocalSize();
sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
<< ", local_size_z=" << localSize[2] << ") in;\n";
}
......
......@@ -47,7 +47,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mOutputType(mImplementation->getTranslatorOutputType()),
mResources(),
mFragmentCompiler(nullptr),
mVertexCompiler(nullptr)
mVertexCompiler(nullptr),
mComputeCompiler(nullptr)
{
ASSERT(state.getClientMajorVersion() == 2 || state.getClientMajorVersion() == 3);
......@@ -135,6 +136,15 @@ Error Compiler::release()
activeCompilerHandles--;
}
if (mComputeCompiler)
{
ShDestruct(mComputeCompiler);
mComputeCompiler = nullptr;
ASSERT(activeCompilerHandles > 0);
activeCompilerHandles--;
}
if (activeCompilerHandles == 0)
{
ShFinalize();
......@@ -157,7 +167,9 @@ ShHandle Compiler::getCompilerHandle(GLenum type)
case GL_FRAGMENT_SHADER:
compiler = &mFragmentCompiler;
break;
case GL_COMPUTE_SHADER:
compiler = &mComputeCompiler;
break;
default:
UNREACHABLE();
return nullptr;
......
......@@ -42,6 +42,7 @@ class Compiler final : angle::NonCopyable
ShHandle mFragmentCompiler;
ShHandle mVertexCompiler;
ShHandle mComputeCompiler;
};
} // namespace gl
......
......@@ -165,6 +165,7 @@ class ProgramState final : angle::NonCopyable
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;
......@@ -198,8 +199,11 @@ class ProgramState final : angle::NonCopyable
std::string mLabel;
sh::WorkGroupSize mComputeShaderLocalSize;
Shader *mAttachedFragmentShader;
Shader *mAttachedVertexShader;
Shader *mAttachedComputeShader;
std::vector<std::string> mTransformFeedbackVaryingNames;
std::vector<sh::Varying> mTransformFeedbackVaryingVars;
......@@ -377,12 +381,22 @@ class Program final : angle::NonCopyable, public LabeledObject
InfoLog &infoLog,
const Bindings &attributeBindings,
const Shader *vertexShader);
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 Shader *vertexShader, const Shader *fragmentShader) const;
bool validateVertexAndFragmentUniforms(InfoLog &infoLog) const;
bool linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps, const Bindings &uniformBindings);
bool indexUniforms(gl::InfoLog &infoLog, const gl::Caps &caps, const Bindings &uniformBindings);
bool areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock);
bool areMatchingInterfaceBlocks(gl::InfoLog &infoLog,
const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock) const;
static bool linkValidateVariablesBase(InfoLog &infoLog,
const std::string &variableName,
......@@ -406,6 +420,13 @@ class Program final : angle::NonCopyable, public LabeledObject
std::vector<const sh::Varying *> getMergedVaryings() const;
void linkOutputVariables();
bool flattenUniformsAndCheckCapsForShader(const gl::Shader &shader,
GLuint maxUniformComponents,
GLuint maxTextureImageUnits,
const std::string &componentsErrorMessage,
const std::string &samplerErrorMessage,
std::vector<LinkedUniform> &samplerUniforms,
InfoLog &infoLog);
bool flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog);
struct VectorAndSamplerCount
......
......@@ -97,13 +97,10 @@ GLuint ResourceManager::createShader(rx::GLImplFactory *factory,
const gl::Limitations &rendererLimitations,
GLenum type)
{
ASSERT(type == GL_VERTEX_SHADER || type == GL_FRAGMENT_SHADER || type == GL_COMPUTE_SHADER);
GLuint handle = mProgramShaderHandleAllocator.allocate();
if (type == GL_VERTEX_SHADER || type == GL_FRAGMENT_SHADER)
{
mShaderMap[handle] = new Shader(this, factory, rendererLimitations, type, handle);
}
else UNREACHABLE();
mShaderMap[handle] = new Shader(this, factory, rendererLimitations, type, handle);
return handle;
}
......
......@@ -75,6 +75,7 @@ bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y)
ShaderState::ShaderState(GLenum shaderType) : mLabel(), mShaderType(shaderType), mShaderVersion(100)
{
mLocalSize.fill(-1);
}
ShaderState::~ShaderState()
......@@ -306,18 +307,28 @@ void Shader::compile(Compiler *compiler)
mState.mUniforms = GetShaderVariables(ShGetUniforms(compilerHandle));
mState.mInterfaceBlocks = GetShaderVariables(ShGetInterfaceBlocks(compilerHandle));
if (mState.mShaderType == GL_VERTEX_SHADER)
switch (mState.mShaderType)
{
mState.mActiveAttributes = GetActiveShaderVariables(ShGetAttributes(compilerHandle));
}
else
{
ASSERT(mState.mShaderType == GL_FRAGMENT_SHADER);
// TODO(jmadill): Figure out why we only sort in the FS, and if we need to.
std::sort(mState.mVaryings.begin(), mState.mVaryings.end(), CompareShaderVar);
mState.mActiveOutputVariables =
GetActiveShaderVariables(ShGetOutputVariables(compilerHandle));
case GL_COMPUTE_SHADER:
{
mState.mLocalSize = ShGetComputeShaderLocalGroupSize(compilerHandle);
break;
}
case GL_VERTEX_SHADER:
{
mState.mActiveAttributes = GetActiveShaderVariables(ShGetAttributes(compilerHandle));
break;
}
case GL_FRAGMENT_SHADER:
{
// TODO(jmadill): Figure out why we only sort in the FS, and if we need to.
std::sort(mState.mVaryings.begin(), mState.mVaryings.end(), CompareShaderVar);
mState.mActiveOutputVariables =
GetActiveShaderVariables(ShGetOutputVariables(compilerHandle));
break;
}
default:
UNREACHABLE();
}
ASSERT(!mState.mTranslatedSource.empty());
......
......@@ -70,6 +70,8 @@ class ShaderState final : angle::NonCopyable
std::string mTranslatedSource;
std::string mSource;
sh::WorkGroupSize mLocalSize;
std::vector<sh::Varying> mVaryings;
std::vector<sh::Uniform> mUniforms;
std::vector<sh::InterfaceBlock> mInterfaceBlocks;
......@@ -127,6 +129,8 @@ class Shader final : angle::NonCopyable, public LabeledObject
int getSemanticIndex(const std::string &attributeName) const;
const sh::WorkGroupSize &getWorkGroupSize() const { return mState.mLocalSize; }
private:
static void getSourceImpl(const std::string &source, GLsizei bufSize, GLsizei *length, char *buffer);
......
......@@ -121,7 +121,12 @@ const uint8_t *LinkedUniform::getDataPtrToElement(size_t elementIndex) const
}
UniformBlock::UniformBlock()
: isArray(false), arrayElement(0), dataSize(0), vertexStaticUse(false), fragmentStaticUse(false)
: isArray(false),
arrayElement(0),
dataSize(0),
vertexStaticUse(false),
fragmentStaticUse(false),
computeStaticUse(false)
{
}
......@@ -131,7 +136,8 @@ UniformBlock::UniformBlock(const std::string &nameIn, bool isArrayIn, unsigned i
arrayElement(arrayElementIn),
dataSize(0),
vertexStaticUse(false),
fragmentStaticUse(false)
fragmentStaticUse(false),
computeStaticUse(false)
{
}
......
......@@ -63,6 +63,7 @@ struct UniformBlock
bool vertexStaticUse;
bool fragmentStaticUse;
bool computeStaticUse;
std::vector<unsigned int> memberUniformIndexes;
};
......
......@@ -101,53 +101,68 @@ LinkResult ProgramGL::link(const gl::ContextState &data, gl::InfoLog &infoLog)
{
preLink();
// Set the transform feedback state
std::vector<const GLchar *> transformFeedbackVaryings;
for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames())
if (mState.getAttachedComputeShader())
{
transformFeedbackVaryings.push_back(tfVarying.c_str());
}
const ShaderGL *computeShaderGL = GetImplAs<ShaderGL>(mState.getAttachedComputeShader());
if (transformFeedbackVaryings.empty())
{
if (mFunctions->transformFeedbackVaryings)
{
mFunctions->transformFeedbackVaryings(mProgramID, 0, nullptr,
mState.getTransformFeedbackBufferMode());
}
mFunctions->attachShader(mProgramID, computeShaderGL->getShaderID());
// Link and verify
mFunctions->linkProgram(mProgramID);
// Detach the shaders
mFunctions->detachShader(mProgramID, computeShaderGL->getShaderID());
}
else
{
ASSERT(mFunctions->transformFeedbackVaryings);
mFunctions->transformFeedbackVaryings(
mProgramID, static_cast<GLsizei>(transformFeedbackVaryings.size()),
&transformFeedbackVaryings[0], mState.getTransformFeedbackBufferMode());
}
// Set the transform feedback state
std::vector<const GLchar *> transformFeedbackVaryings;
for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames())
{
transformFeedbackVaryings.push_back(tfVarying.c_str());
}
const ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(mState.getAttachedVertexShader());
const ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(mState.getAttachedFragmentShader());
if (transformFeedbackVaryings.empty())
{
if (mFunctions->transformFeedbackVaryings)
{
mFunctions->transformFeedbackVaryings(mProgramID, 0, nullptr,
mState.getTransformFeedbackBufferMode());
}
}
else
{
ASSERT(mFunctions->transformFeedbackVaryings);
mFunctions->transformFeedbackVaryings(
mProgramID, static_cast<GLsizei>(transformFeedbackVaryings.size()),
&transformFeedbackVaryings[0], mState.getTransformFeedbackBufferMode());
}
// Attach the shaders
mFunctions->attachShader(mProgramID, vertexShaderGL->getShaderID());
mFunctions->attachShader(mProgramID, fragmentShaderGL->getShaderID());
const ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(mState.getAttachedVertexShader());
const ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(mState.getAttachedFragmentShader());
// Bind attribute locations to match the GL layer.
for (const sh::Attribute &attribute : mState.getAttributes())
{
if (!attribute.staticUse)
// Attach the shaders
mFunctions->attachShader(mProgramID, vertexShaderGL->getShaderID());
mFunctions->attachShader(mProgramID, fragmentShaderGL->getShaderID());
// Bind attribute locations to match the GL layer.
for (const sh::Attribute &attribute : mState.getAttributes())
{
continue;
}
if (!attribute.staticUse)
{
continue;
}
mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str());
}
mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str());
}
// Link and verify
mFunctions->linkProgram(mProgramID);
// Link and verify
mFunctions->linkProgram(mProgramID);
// Detach the shaders
mFunctions->detachShader(mProgramID, vertexShaderGL->getShaderID());
mFunctions->detachShader(mProgramID, fragmentShaderGL->getShaderID());
// Detach the shaders
mFunctions->detachShader(mProgramID, vertexShaderGL->getShaderID());
mFunctions->detachShader(mProgramID, fragmentShaderGL->getShaderID());
}
// Verify the link
if (!checkLinkStatus(infoLog))
......
......@@ -3309,4 +3309,23 @@ bool ValidateCopySubTextureCHROMIUM(Context *context,
return true;
}
bool ValidateCreateShader(Context *context, GLenum type)
{
switch (type)
{
case GL_VERTEX_SHADER:
case GL_FRAGMENT_SHADER:
break;
case GL_COMPUTE_SHADER:
if (context->getGLVersion().isES31())
{
break;
}
default:
context->handleError(Error(GL_INVALID_ENUM));
return false;
}
return true;
}
} // namespace gl
......@@ -305,6 +305,8 @@ bool ValidateCopySubTextureCHROMIUM(Context *context,
GLboolean unpackPremultiplyAlpha,
GLboolean unpackUnmultiplyAlpha);
bool ValidateCreateShader(Context *context, GLenum type);
} // namespace gl
#endif // LIBANGLE_VALIDATION_ES2_H_
......@@ -748,18 +748,13 @@ GLuint GL_APIENTRY CreateShader(GLenum type)
Context *context = GetValidGlobalContext();
if (context)
{
switch (type)
{
case GL_FRAGMENT_SHADER:
case GL_VERTEX_SHADER:
return context->createShader(type);
default:
context->handleError(Error(GL_INVALID_ENUM));
if (!context->skipValidation() && !ValidateCreateShader(context, type))
{
return 0;
}
return context->createShader(type);
}
return 0;
}
......
......@@ -22,6 +22,7 @@
'<(angle_path)/src/tests/gl_tests/BuiltinVariableTest.cpp',
'<(angle_path)/src/tests/gl_tests/ClearTest.cpp',
'<(angle_path)/src/tests/gl_tests/ColorMaskTest.cpp',
'<(angle_path)/src/tests/gl_tests/ComputeShaderTest.cpp',
'<(angle_path)/src/tests/gl_tests/CopyTexImageTest.cpp',
'<(angle_path)/src/tests/gl_tests/CopyTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp',
......
......@@ -56,7 +56,7 @@ TEST_F(WorkGroupSizeTest, OnlyLocalSizeXSpecified)
compile(shaderString);
const TLocalSize &localSize = mTranslator->getComputeShaderLocalSize();
const sh::WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize();
ASSERT_EQ(5, localSize[0]);
ASSERT_EQ(1, localSize[1]);
ASSERT_EQ(1, localSize[2]);
......@@ -73,7 +73,7 @@ TEST_F(WorkGroupSizeTest, LocalSizeXandZ)
compile(shaderString);
const TLocalSize &localSize = mTranslator->getComputeShaderLocalSize();
const sh::WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize();
ASSERT_EQ(5, localSize[0]);
ASSERT_EQ(1, localSize[1]);
ASSERT_EQ(10, localSize[2]);
......@@ -90,7 +90,7 @@ TEST_F(WorkGroupSizeTest, LocalSizeAll)
compile(shaderString);
const TLocalSize &localSize = mTranslator->getComputeShaderLocalSize();
const sh::WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize();
ASSERT_EQ(5, localSize[0]);
ASSERT_EQ(15, localSize[1]);
ASSERT_EQ(10, localSize[2]);
......
//
// Copyright 2016 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.
//
// ComputeShaderTest:
// Compute shader specific tests.
#include "test_utils/ANGLETest.h"
#include "test_utils/gl_raii.h"
#include <vector>
using namespace angle;
namespace
{
class ComputeShaderTest : public ANGLETest
{
protected:
ComputeShaderTest() {}
};
class ComputeShaderTestES3 : public ANGLETest
{
protected:
ComputeShaderTestES3() {}
};
// link a simple compute program. It should be successful.
TEST_P(ComputeShaderTest, LinkComputeProgram)
{
const std::string csSource =
"#version 310 es\n"
"layout(local_size_x=1) in;\n"
"void main()\n"
"{\n"
"}\n";
ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
EXPECT_GL_NO_ERROR();
}
// link a simple compute program. There is no local size and linking should fail.
TEST_P(ComputeShaderTest, LinkComputeProgramNoLocalSizeLinkError)
{
const std::string csSource =
"#version 310 es\n"
"void main()\n"
"{\n"
"}\n";
GLuint program = CompileComputeProgram(csSource, false);
EXPECT_EQ(0u, program);
glDeleteProgram(program);
EXPECT_GL_NO_ERROR();
}
// link a simple compute program.
// make sure that uniforms and uniform samplers get recorded
TEST_P(ComputeShaderTest, LinkComputeProgramWithUniforms)
{
const std::string csSource =
"#version 310 es\n"
"precision mediump sampler2D;\n"
"layout(local_size_x=1) in;\n"
"uniform int myUniformInt;\n"
"uniform sampler2D myUniformSampler;\n"
"void main()\n"
"{\n"
"int q = myUniformInt;\n"
"texture(myUniformSampler, vec2(0.0));\n"
"}\n";
ANGLE_GL_COMPUTE_PROGRAM(program, csSource);
GLint uniformLoc = glGetUniformLocation(program.get(), "myUniformInt");
EXPECT_NE(-1, uniformLoc);
uniformLoc = glGetUniformLocation(program.get(), "myUniformSampler");
EXPECT_NE(-1, uniformLoc);
EXPECT_GL_NO_ERROR();
}
// Attach both compute and non-compute shaders. A link time error should occur.
// OpenGL ES 3.10, 7.3 Program Objects
TEST_P(ComputeShaderTest, AttachMultipleShaders)
{
const std::string csSource =
"#version 310 es\n"
"layout(local_size_x=1) in;\n"
"void main()\n"
"{\n"
"}\n";
const std::string vsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
"}\n";
const std::string fsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
"}\n";
GLuint program = glCreateProgram();
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
EXPECT_NE(0u, vs);
EXPECT_NE(0u, fs);
EXPECT_NE(0u, cs);
glAttachShader(program, vs);
glDeleteShader(vs);
glAttachShader(program, fs);
glDeleteShader(fs);
glAttachShader(program, cs);
glDeleteShader(cs);
glLinkProgram(program);
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
EXPECT_EQ(0, linkStatus);
EXPECT_GL_NO_ERROR();
}
// Attach a vertex, fragment and compute shader.
// Query for the number of attached shaders and check the count.
TEST_P(ComputeShaderTest, AttachmentCount)
{
const std::string csSource =
"#version 310 es\n"
"layout(local_size_x=1) in;\n"
"void main()\n"
"{\n"
"}\n";
const std::string vsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
"}\n";
const std::string fsSource =
"#version 310 es\n"
"void main()\n"
"{\n"
"}\n";
GLuint program = glCreateProgram();
GLuint vs = CompileShader(GL_VERTEX_SHADER, vsSource);
GLuint fs = CompileShader(GL_FRAGMENT_SHADER, fsSource);
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
EXPECT_NE(0u, vs);
EXPECT_NE(0u, fs);
EXPECT_NE(0u, cs);
glAttachShader(program, vs);
glDeleteShader(vs);
glAttachShader(program, fs);
glDeleteShader(fs);
glAttachShader(program, cs);
glDeleteShader(cs);
GLint numAttachedShaders;
glGetProgramiv(program, GL_ATTACHED_SHADERS, &numAttachedShaders);
EXPECT_EQ(3, numAttachedShaders);
glDeleteProgram(program);
EXPECT_GL_NO_ERROR();
}
// Check that it is not possible to create a compute shader when the context does not support ES
// 3.10
TEST_P(ComputeShaderTestES3, NotSupported)
{
GLuint computeShaderHandle = glCreateShader(GL_COMPUTE_SHADER);
EXPECT_EQ(0u, computeShaderHandle);
EXPECT_GL_ERROR(GL_INVALID_ENUM);
}
ANGLE_INSTANTIATE_TEST(ComputeShaderTest, ES31_OPENGL(), ES31_OPENGLES());
ANGLE_INSTANTIATE_TEST(ComputeShaderTestES3, ES3_OPENGL(), ES3_OPENGLES());
} // namespace
......@@ -555,6 +555,16 @@ PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor)
return PlatformParameters(3, 0, egl_platform::OPENGLES(major, minor));
}
PlatformParameters ES31_OPENGLES()
{
return PlatformParameters(3, 1, egl_platform::OPENGLES());
}
PlatformParameters ES31_OPENGLES(EGLint major, EGLint minor)
{
return PlatformParameters(3, 1, egl_platform::OPENGLES(major, minor));
}
PlatformParameters ES2_OPENGL()
{
return PlatformParameters(2, 0, egl_platform::OPENGL());
......@@ -575,4 +585,14 @@ PlatformParameters ES3_OPENGL(EGLint major, EGLint minor)
return PlatformParameters(3, 0, egl_platform::OPENGL(major, minor));
}
PlatformParameters ES31_OPENGL()
{
return PlatformParameters(3, 1, egl_platform::OPENGL());
}
PlatformParameters ES31_OPENGL(EGLint major, EGLint minor)
{
return PlatformParameters(3, 1, egl_platform::OPENGL(major, minor));
}
} // namespace angle
......@@ -129,11 +129,15 @@ PlatformParameters ES2_OPENGL();
PlatformParameters ES2_OPENGL(EGLint major, EGLint minor);
PlatformParameters ES3_OPENGL();
PlatformParameters ES3_OPENGL(EGLint major, EGLint minor);
PlatformParameters ES31_OPENGL();
PlatformParameters ES31_OPENGL(EGLint major, EGLint minor);
PlatformParameters ES2_OPENGLES();
PlatformParameters ES2_OPENGLES(EGLint major, EGLint minor);
PlatformParameters ES3_OPENGLES();
PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor);
PlatformParameters ES31_OPENGLES();
PlatformParameters ES31_OPENGLES(EGLint major, EGLint minor);
} // namespace angle
......
......@@ -55,13 +55,22 @@ class GLProgram
{
}
GLProgram(const std::string &computeShader) : mHandle(0), mComputeShader(computeShader) {}
~GLProgram() { glDeleteProgram(mHandle); }
GLuint get()
{
if (mHandle == 0)
{
mHandle = CompileProgram(mVertexShader, mFragmentShader);
if (!mComputeShader.empty())
{
mHandle = CompileComputeProgram(mComputeShader);
}
else
{
mHandle = CompileProgram(mVertexShader, mFragmentShader);
}
}
return mHandle;
}
......@@ -70,12 +79,17 @@ class GLProgram
GLuint mHandle;
const std::string mVertexShader;
const std::string mFragmentShader;
const std::string mComputeShader;
};
#define ANGLE_GL_PROGRAM(name, vertex, fragment) \
GLProgram name(vertex, fragment); \
ASSERT_NE(0u, name.get());
#define ANGLE_GL_COMPUTE_PROGRAM(name, compute) \
GLProgram name(compute); \
ASSERT_NE(0u, name.get());
} // namespace angle
#endif // ANGLE_TESTS_GL_RAII_H_
......@@ -77,6 +77,40 @@ GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
return CompileShader(type, source);
}
GLuint CheckLinkStatusAndReturnProgram(GLuint program, bool outputErrorMessages)
{
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == 0)
{
if (outputErrorMessages)
{
GLint infoLogLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
// Info log length includes the null terminator, so 1 means that the info log is an
// empty string.
if (infoLogLength > 1)
{
std::vector<GLchar> infoLog(infoLogLength);
glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr,
&infoLog[0]);
std::cerr << "program link failed: " << &infoLog[0];
}
else
{
std::cerr << "program link failed. <Empty log message>";
}
}
glDeleteProgram(program);
return 0;
}
return program;
}
GLuint CompileProgramWithTransformFeedback(
const std::string &vsSource,
const std::string &fsSource,
......@@ -117,33 +151,7 @@ GLuint CompileProgramWithTransformFeedback(
glLinkProgram(program);
GLint linkStatus;
glGetProgramiv(program, GL_LINK_STATUS, &linkStatus);
if (linkStatus == 0)
{
GLint infoLogLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
// Info log length includes the null terminator, so 1 means that the info log is an empty
// string.
if (infoLogLength > 1)
{
std::vector<GLchar> infoLog(infoLogLength);
glGetProgramInfoLog(program, static_cast<GLsizei>(infoLog.size()), nullptr, &infoLog[0]);
std::cerr << "program link failed: " << &infoLog[0];
}
else
{
std::cerr << "program link failed. <Empty log message>";
}
glDeleteProgram(program);
return 0;
}
return program;
return CheckLinkStatusAndReturnProgram(program, true);
}
GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource)
......@@ -163,3 +171,21 @@ GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsP
return CompileProgram(vsSource, fsSource);
}
GLuint CompileComputeProgram(const std::string &csSource, bool outputErrorMessages)
{
GLuint program = glCreateProgram();
GLuint cs = CompileShader(GL_COMPUTE_SHADER, csSource);
if (cs == 0)
{
glDeleteProgram(program);
return 0;
}
glAttachShader(program, cs);
glLinkProgram(program);
return CheckLinkStatusAndReturnProgram(program, outputErrorMessages);
}
......@@ -7,6 +7,7 @@
#ifndef SAMPLE_UTIL_SHADER_UTILS_H
#define SAMPLE_UTIL_SHADER_UTILS_H
#include <GLES3/gl31.h>
#include <GLES3/gl3.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
......@@ -28,5 +29,5 @@ GLuint CompileProgramWithTransformFeedback(
GLenum bufferMode);
GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource);
GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath);
GLuint CompileComputeProgram(const std::string &csSource, bool outputErrorMessages = true);
#endif // SAMPLE_UTIL_SHADER_UTILS_H
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment