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
......
......@@ -237,9 +237,11 @@ ProgramState::ProgramState()
: mLabel(),
mAttachedFragmentShader(nullptr),
mAttachedVertexShader(nullptr),
mAttachedComputeShader(nullptr),
mTransformFeedbackBufferMode(GL_INTERLEAVED_ATTRIBS),
mBinaryRetrieveableHint(false)
{
mComputeShaderLocalSize.fill(1);
}
ProgramState::~ProgramState()
......@@ -253,6 +255,11 @@ ProgramState::~ProgramState()
{
mAttachedFragmentShader->release();
}
if (mAttachedComputeShader != nullptr)
{
mAttachedComputeShader->release();
}
}
const std::string &ProgramState::getLabel()
......@@ -372,61 +379,96 @@ const std::string &Program::getLabel() const
bool Program::attachShader(Shader *shader)
{
if (shader->getType() == GL_VERTEX_SHADER)
switch (shader->getType())
{
if (mState.mAttachedVertexShader)
case GL_VERTEX_SHADER:
{
return false;
}
if (mState.mAttachedVertexShader)
{
return false;
}
mState.mAttachedVertexShader = shader;
mState.mAttachedVertexShader->addRef();
}
else if (shader->getType() == GL_FRAGMENT_SHADER)
{
if (mState.mAttachedFragmentShader)
mState.mAttachedVertexShader = shader;
mState.mAttachedVertexShader->addRef();
break;
}
case GL_FRAGMENT_SHADER:
{
return false;
if (mState.mAttachedFragmentShader)
{
return false;
}
mState.mAttachedFragmentShader = shader;
mState.mAttachedFragmentShader->addRef();
break;
}
case GL_COMPUTE_SHADER:
{
if (mState.mAttachedComputeShader)
{
return false;
}
mState.mAttachedFragmentShader = shader;
mState.mAttachedFragmentShader->addRef();
mState.mAttachedComputeShader = shader;
mState.mAttachedComputeShader->addRef();
break;
}
default:
UNREACHABLE();
}
else UNREACHABLE();
return true;
}
bool Program::detachShader(Shader *shader)
{
if (shader->getType() == GL_VERTEX_SHADER)
switch (shader->getType())
{
if (mState.mAttachedVertexShader != shader)
case GL_VERTEX_SHADER:
{
return false;
}
if (mState.mAttachedVertexShader != shader)
{
return false;
}
shader->release();
mState.mAttachedVertexShader = nullptr;
}
else if (shader->getType() == GL_FRAGMENT_SHADER)
{
if (mState.mAttachedFragmentShader != shader)
shader->release();
mState.mAttachedVertexShader = nullptr;
break;
}
case GL_FRAGMENT_SHADER:
{
return false;
if (mState.mAttachedFragmentShader != shader)
{
return false;
}
shader->release();
mState.mAttachedFragmentShader = nullptr;
break;
}
case GL_COMPUTE_SHADER:
{
if (mState.mAttachedComputeShader != shader)
{
return false;
}
shader->release();
mState.mAttachedFragmentShader = nullptr;
shader->release();
mState.mAttachedComputeShader = nullptr;
break;
}
default:
UNREACHABLE();
}
else UNREACHABLE();
return true;
}
int Program::getAttachedShadersCount() const
{
return (mState.mAttachedVertexShader ? 1 : 0) + (mState.mAttachedFragmentShader ? 1 : 0);
return (mState.mAttachedVertexShader ? 1 : 0) + (mState.mAttachedFragmentShader ? 1 : 0) +
(mState.mAttachedComputeShader ? 1 : 0);
}
void Program::bindAttributeLocation(GLuint index, const char *name)
......@@ -516,9 +558,9 @@ void Program::pathFragmentInputGen(GLint index,
mProgram->setPathFragmentInputGen(binding.name, genMode, components, coeffs);
}
// Links the HLSL code of the vertex and pixel shader by matching up their varyings,
// compiling them into binaries, determining the attribute mappings, and collecting
// a list of uniforms
// The attached shaders are checked for linking errors by matching up their variables.
// Uniform, input and output variables get collected.
// The code gets compiled into binaries.
Error Program::link(const ContextState &data)
{
unlink(false);
......@@ -526,65 +568,119 @@ Error Program::link(const ContextState &data)
mInfoLog.reset();
resetUniformBlockBindings();
if (!mState.mAttachedFragmentShader || !mState.mAttachedFragmentShader->isCompiled())
{
return Error(GL_NO_ERROR);
}
ASSERT(mState.mAttachedFragmentShader->getType() == GL_FRAGMENT_SHADER);
const Caps &caps = data.getCaps();
if (!mState.mAttachedVertexShader || !mState.mAttachedVertexShader->isCompiled())
bool isComputeShaderAttached = (mState.mAttachedComputeShader != nullptr);
bool nonComputeShadersAttached =
(mState.mAttachedVertexShader != nullptr || mState.mAttachedFragmentShader != nullptr);
// Check whether we both have a compute and non-compute shaders attached.
// If there are of both types attached, then linking should fail.
// OpenGL ES 3.10, 7.3 Program Objects, under LinkProgram
if (isComputeShaderAttached == true && nonComputeShadersAttached == true)
{
return Error(GL_NO_ERROR);
mInfoLog << "Both a compute and non-compute shaders are attached to the same program.";
return NoError();
}
ASSERT(mState.mAttachedVertexShader->getType() == GL_VERTEX_SHADER);
if (mState.mAttachedFragmentShader->getShaderVersion() !=
mState.mAttachedVertexShader->getShaderVersion())
if (mState.mAttachedComputeShader)
{
mInfoLog << "Fragment shader version does not match vertex shader version.";
return Error(GL_NO_ERROR);
}
if (!mState.mAttachedComputeShader->isCompiled())
{
mInfoLog << "Attached compute shader is not compiled.";
return NoError();
}
ASSERT(mState.mAttachedComputeShader->getType() == GL_COMPUTE_SHADER);
if (!linkAttributes(data, mInfoLog, mAttributeBindings, mState.mAttachedVertexShader))
{
return Error(GL_NO_ERROR);
}
mState.mComputeShaderLocalSize = mState.mAttachedComputeShader->getWorkGroupSize();
if (!linkVaryings(mInfoLog, mState.mAttachedVertexShader, mState.mAttachedFragmentShader))
{
return Error(GL_NO_ERROR);
}
// GLSL ES 3.10, 4.4.1.1 Compute Shader Inputs
// If the work group size is not specified, a link time error should occur.
if (!mState.mComputeShaderLocalSize.isDeclared())
{
mInfoLog << "Work group size is not specified.";
return NoError();
}
if (!linkUniforms(mInfoLog, data.getCaps(), mUniformBindings))
{
return Error(GL_NO_ERROR);
}
if (!linkUniforms(mInfoLog, caps, mUniformBindings))
{
return NoError();
}
if (!linkUniformBlocks(mInfoLog, data.getCaps()))
{
return Error(GL_NO_ERROR);
}
if (!linkUniformBlocks(mInfoLog, caps))
{
return NoError();
}
const auto &mergedVaryings = getMergedVaryings();
rx::LinkResult result = mProgram->link(data, mInfoLog);
if (!linkValidateTransformFeedback(mInfoLog, mergedVaryings, data.getCaps()))
{
return Error(GL_NO_ERROR);
if (result.error.isError() || !result.linkSuccess)
{
return result.error;
}
}
else
{
if (!mState.mAttachedFragmentShader || !mState.mAttachedFragmentShader->isCompiled())
{
return NoError();
}
ASSERT(mState.mAttachedFragmentShader->getType() == GL_FRAGMENT_SHADER);
linkOutputVariables();
if (!mState.mAttachedVertexShader || !mState.mAttachedVertexShader->isCompiled())
{
return NoError();
}
ASSERT(mState.mAttachedVertexShader->getType() == GL_VERTEX_SHADER);
rx::LinkResult result = mProgram->link(data, mInfoLog);
if (result.error.isError() || !result.linkSuccess)
{
return result.error;
if (mState.mAttachedFragmentShader->getShaderVersion() !=
mState.mAttachedVertexShader->getShaderVersion())
{
mInfoLog << "Fragment shader version does not match vertex shader version.";
return NoError();
}
if (!linkAttributes(data, mInfoLog, mAttributeBindings, mState.mAttachedVertexShader))
{
return NoError();
}
if (!linkVaryings(mInfoLog, mState.mAttachedVertexShader, mState.mAttachedFragmentShader))
{
return NoError();
}
if (!linkUniforms(mInfoLog, caps, mUniformBindings))
{
return NoError();
}
if (!linkUniformBlocks(mInfoLog, caps))
{
return NoError();
}
const auto &mergedVaryings = getMergedVaryings();
if (!linkValidateTransformFeedback(mInfoLog, mergedVaryings, caps))
{
return NoError();
}
linkOutputVariables();
rx::LinkResult result = mProgram->link(data, mInfoLog);
if (result.error.isError() || !result.linkSuccess)
{
return result.error;
}
gatherTransformFeedbackVaryings(mergedVaryings);
}
gatherTransformFeedbackVaryings(mergedVaryings);
gatherInterfaceBlockInfo();
mLinked = true;
return gl::Error(GL_NO_ERROR);
return NoError();
}
// Returns the program object to an unlinked state, before re-linking, or at destruction
......@@ -603,6 +699,12 @@ void Program::unlink(bool destroy)
mState.mAttachedVertexShader->release();
mState.mAttachedVertexShader = nullptr;
}
if (mState.mAttachedComputeShader)
{
mState.mAttachedComputeShader->release();
mState.mAttachedComputeShader = nullptr;
}
}
mState.mAttributes.clear();
......@@ -612,6 +714,7 @@ void Program::unlink(bool destroy)
mState.mUniformLocations.clear();
mState.mUniformBlocks.clear();
mState.mOutputVariables.clear();
mState.mComputeShaderLocalSize.fill(1);
mValidated = false;
......@@ -655,6 +758,10 @@ Error Program::loadBinary(GLenum binaryFormat, const void *binary, GLsizei lengt
return Error(GL_NO_ERROR);
}
mState.mComputeShaderLocalSize[0] = stream.readInt<int>();
mState.mComputeShaderLocalSize[1] = stream.readInt<int>();
mState.mComputeShaderLocalSize[2] = stream.readInt<int>();
static_assert(MAX_VERTEX_ATTRIBS <= sizeof(unsigned long) * 8,
"Too many vertex attribs for mask");
mState.mActiveAttribLocationsMask = stream.readInt<unsigned long>();
......@@ -776,6 +883,10 @@ Error Program::saveBinary(GLenum *binaryFormat, void *binary, GLsizei bufSize, G
stream.writeInt(ANGLE_MINOR_VERSION);
stream.writeBytes(reinterpret_cast<const unsigned char*>(ANGLE_COMMIT_HASH), ANGLE_COMMIT_HASH_SIZE);
stream.writeInt(mState.mComputeShaderLocalSize[0]);
stream.writeInt(mState.mComputeShaderLocalSize[1]);
stream.writeInt(mState.mComputeShaderLocalSize[2]);
stream.writeInt(mState.mActiveAttribLocationsMask.to_ulong());
stream.writeInt(mState.mAttributes.size());
......@@ -947,6 +1058,15 @@ void Program::getAttachedShaders(GLsizei maxCount, GLsizei *count, GLuint *shade
{
int total = 0;
if (mState.mAttachedComputeShader)
{
if (total < maxCount)
{
shaders[total] = mState.mAttachedComputeShader->getHandle();
total++;
}
}
if (mState.mAttachedVertexShader)
{
if (total < maxCount)
......@@ -1772,17 +1892,14 @@ bool Program::linkVaryings(InfoLog &infoLog,
return true;
}
bool Program::linkUniforms(gl::InfoLog &infoLog,
const gl::Caps &caps,
const Bindings &uniformBindings)
bool Program::validateVertexAndFragmentUniforms(InfoLog &infoLog) const
{
// Check that uniforms defined in the vertex and fragment shaders are identical
std::map<std::string, LinkedUniform> linkedUniforms;
const std::vector<sh::Uniform> &vertexUniforms = mState.mAttachedVertexShader->getUniforms();
const std::vector<sh::Uniform> &fragmentUniforms =
mState.mAttachedFragmentShader->getUniforms();
// Check that uniforms defined in the vertex and fragment shaders are identical
std::map<std::string, LinkedUniform> linkedUniforms;
for (const sh::Uniform &vertexUniform : vertexUniforms)
{
linkedUniforms[vertexUniform.name] = LinkedUniform(vertexUniform);
......@@ -1801,6 +1918,21 @@ bool Program::linkUniforms(gl::InfoLog &infoLog,
}
}
}
return true;
}
bool Program::linkUniforms(gl::InfoLog &infoLog,
const gl::Caps &caps,
const Bindings &uniformBindings)
{
if (mState.mAttachedVertexShader && mState.mAttachedFragmentShader)
{
ASSERT(mState.mAttachedComputeShader == nullptr);
if (!validateVertexAndFragmentUniforms(infoLog))
{
return false;
}
}
// Flatten the uniforms list (nested fields) into a simple list (no nesting).
// Also check the maximum uniform vector and sampler counts.
......@@ -1910,7 +2042,10 @@ bool Program::indexUniforms(gl::InfoLog &infoLog,
return true;
}
bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog, const std::string &uniformName, const sh::InterfaceBlockField &vertexUniform, const sh::InterfaceBlockField &fragmentUniform)
bool Program::linkValidateInterfaceBlockFields(InfoLog &infoLog,
const std::string &uniformName,
const sh::InterfaceBlockField &vertexUniform,
const sh::InterfaceBlockField &fragmentUniform)
{
// We don't validate precision on UBO fields. See resolution of Khronos bug 10287.
if (!linkValidateVariablesBase(infoLog, uniformName, vertexUniform, fragmentUniform, false))
......@@ -2034,36 +2169,40 @@ bool Program::linkAttributes(const ContextState &data,
return true;
}
bool Program::linkUniformBlocks(InfoLog &infoLog, const Caps &caps)
bool Program::validateUniformBlocksCount(GLuint maxUniformBlocks,
const std::vector<sh::InterfaceBlock> &intefaceBlocks,
const std::string &errorMessage,
InfoLog &infoLog) const
{
const Shader &vertexShader = *mState.mAttachedVertexShader;
const Shader &fragmentShader = *mState.mAttachedFragmentShader;
const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks = vertexShader.getInterfaceBlocks();
const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks = fragmentShader.getInterfaceBlocks();
GLuint blockCount = 0;
for (const sh::InterfaceBlock &block : intefaceBlocks)
{
if (block.staticUse || block.layout != sh::BLOCKLAYOUT_PACKED)
{
if (++blockCount > maxUniformBlocks)
{
infoLog << errorMessage << maxUniformBlocks << ")";
return false;
}
}
}
return true;
}
bool Program::validateVertexAndFragmentInterfaceBlocks(
const std::vector<sh::InterfaceBlock> &vertexInterfaceBlocks,
const std::vector<sh::InterfaceBlock> &fragmentInterfaceBlocks,
InfoLog &infoLog) const
{
// Check that interface blocks defined in the vertex and fragment shaders are identical
typedef std::map<std::string, const sh::InterfaceBlock*> UniformBlockMap;
typedef std::map<std::string, const sh::InterfaceBlock *> UniformBlockMap;
UniformBlockMap linkedUniformBlocks;
GLuint vertexBlockCount = 0;
for (const sh::InterfaceBlock &vertexInterfaceBlock : vertexInterfaceBlocks)
{
linkedUniformBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock;
// Note: shared and std140 layouts are always considered active
if (vertexInterfaceBlock.staticUse || vertexInterfaceBlock.layout != sh::BLOCKLAYOUT_PACKED)
{
if (++vertexBlockCount > caps.maxVertexUniformBlocks)
{
infoLog << "Vertex shader uniform block count exceed GL_MAX_VERTEX_UNIFORM_BLOCKS ("
<< caps.maxVertexUniformBlocks << ")";
return false;
}
}
}
GLuint fragmentBlockCount = 0;
for (const sh::InterfaceBlock &fragmentInterfaceBlock : fragmentInterfaceBlocks)
{
auto entry = linkedUniformBlocks.find(fragmentInterfaceBlock.name);
......@@ -2075,26 +2214,59 @@ bool Program::linkUniformBlocks(InfoLog &infoLog, const Caps &caps)
return false;
}
}
}
return true;
}
// Note: shared and std140 layouts are always considered active
if (fragmentInterfaceBlock.staticUse ||
fragmentInterfaceBlock.layout != sh::BLOCKLAYOUT_PACKED)
bool Program::linkUniformBlocks(InfoLog &infoLog, const Caps &caps)
{
if (mState.mAttachedComputeShader)
{
const Shader &computeShader = *mState.mAttachedComputeShader;
const auto &computeInterfaceBlocks = computeShader.getInterfaceBlocks();
if (!validateUniformBlocksCount(
caps.maxComputeUniformBlocks, computeInterfaceBlocks,
"Compute shader uniform block count exceeds GL_MAX_COMPUTE_UNIFORM_BLOCKS (",
infoLog))
{
if (++fragmentBlockCount > caps.maxFragmentUniformBlocks)
{
infoLog
<< "Fragment shader uniform block count exceed GL_MAX_FRAGMENT_UNIFORM_BLOCKS ("
<< caps.maxFragmentUniformBlocks << ")";
return false;
}
return false;
}
return true;
}
const Shader &vertexShader = *mState.mAttachedVertexShader;
const Shader &fragmentShader = *mState.mAttachedFragmentShader;
const auto &vertexInterfaceBlocks = vertexShader.getInterfaceBlocks();
const auto &fragmentInterfaceBlocks = fragmentShader.getInterfaceBlocks();
if (!validateUniformBlocksCount(
caps.maxVertexUniformBlocks, vertexInterfaceBlocks,
"Vertex shader uniform block count exceeds GL_MAX_VERTEX_UNIFORM_BLOCKS (", infoLog))
{
return false;
}
if (!validateUniformBlocksCount(
caps.maxFragmentUniformBlocks, fragmentInterfaceBlocks,
"Fragment shader uniform block count exceeds GL_MAX_FRAGMENT_UNIFORM_BLOCKS (",
infoLog))
{
return false;
}
if (!validateVertexAndFragmentInterfaceBlocks(vertexInterfaceBlocks, fragmentInterfaceBlocks,
infoLog))
{
return false;
}
return true;
}
bool Program::areMatchingInterfaceBlocks(gl::InfoLog &infoLog, const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock)
bool Program::areMatchingInterfaceBlocks(gl::InfoLog &infoLog,
const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock) const
{
const char* blockName = vertexInterfaceBlock.name.c_str();
// validate blocks for the same member types
......@@ -2385,58 +2557,79 @@ void Program::linkOutputVariables()
}
}
bool Program::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
bool Program::flattenUniformsAndCheckCapsForShader(const gl::Shader &shader,
GLuint maxUniformComponents,
GLuint maxTextureImageUnits,
const std::string &componentsErrorMessage,
const std::string &samplerErrorMessage,
std::vector<LinkedUniform> &samplerUniforms,
InfoLog &infoLog)
{
const gl::Shader *vertexShader = mState.getAttachedVertexShader();
VectorAndSamplerCount vsCounts;
std::vector<LinkedUniform> samplerUniforms;
for (const sh::Uniform &uniform : vertexShader->getUniforms())
VectorAndSamplerCount vasCount;
for (const sh::Uniform &uniform : shader.getUniforms())
{
if (uniform.staticUse)
{
vsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
vasCount += flattenUniform(uniform, uniform.name, &samplerUniforms);
}
}
if (vsCounts.vectorCount > caps.maxVertexUniformVectors)
if (vasCount.vectorCount > maxUniformComponents)
{
infoLog << "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS ("
<< caps.maxVertexUniformVectors << ").";
infoLog << componentsErrorMessage << maxUniformComponents << ").";
return false;
}
if (vsCounts.samplerCount > caps.maxVertexTextureImageUnits)
if (vasCount.samplerCount > maxTextureImageUnits)
{
infoLog << "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS ("
<< caps.maxVertexTextureImageUnits << ").";
infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
return false;
}
const gl::Shader *fragmentShader = mState.getAttachedFragmentShader();
VectorAndSamplerCount fsCounts;
return true;
}
for (const sh::Uniform &uniform : fragmentShader->getUniforms())
bool Program::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
{
std::vector<LinkedUniform> samplerUniforms;
if (mState.mAttachedComputeShader)
{
if (uniform.staticUse)
const gl::Shader *computeShader = mState.getAttachedComputeShader();
// TODO (mradev): check whether we need finer-grained component counting
if (!flattenUniformsAndCheckCapsForShader(
*computeShader, caps.maxComputeUniformComponents / 4,
caps.maxComputeTextureImageUnits,
"Compute shader active uniforms exceed MAX_COMPUTE_UNIFORM_COMPONENTS (",
"Compute shader sampler count exceeds MAX_COMPUTE_TEXTURE_IMAGE_UNITS (",
samplerUniforms, infoLog))
{
fsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
return false;
}
}
if (fsCounts.vectorCount > caps.maxFragmentUniformVectors)
else
{
infoLog << "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS ("
<< caps.maxFragmentUniformVectors << ").";
return false;
}
const gl::Shader *vertexShader = mState.getAttachedVertexShader();
if (fsCounts.samplerCount > caps.maxTextureImageUnits)
{
infoLog << "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS ("
<< caps.maxTextureImageUnits << ").";
return false;
if (!flattenUniformsAndCheckCapsForShader(
*vertexShader, caps.maxVertexUniformVectors, caps.maxVertexTextureImageUnits,
"Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS (",
"Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS (",
samplerUniforms, infoLog))
{
return false;
}
const gl::Shader *fragmentShader = mState.getAttachedFragmentShader();
if (!flattenUniformsAndCheckCapsForShader(
*fragmentShader, caps.maxFragmentUniformVectors, caps.maxTextureImageUnits,
"Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS (",
"Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS (", samplerUniforms,
infoLog))
{
return false;
}
}
mSamplerUniformRange.start = static_cast<unsigned int>(mState.mUniforms.size());
......@@ -2506,14 +2699,39 @@ Program::VectorAndSamplerCount Program::flattenUniform(const sh::ShaderVariable
void Program::gatherInterfaceBlockInfo()
{
ASSERT(mState.mUniformBlocks.empty());
if (mState.mAttachedComputeShader)
{
const gl::Shader *computeShader = mState.getAttachedComputeShader();
for (const sh::InterfaceBlock &computeBlock : computeShader->getInterfaceBlocks())
{
// Only 'packed' blocks are allowed to be considered inactive.
if (!computeBlock.staticUse && computeBlock.layout == sh::BLOCKLAYOUT_PACKED)
continue;
for (gl::UniformBlock &block : mState.mUniformBlocks)
{
if (block.name == computeBlock.name)
{
block.computeStaticUse = computeBlock.staticUse;
}
}
defineUniformBlock(computeBlock, GL_COMPUTE_SHADER);
}
return;
}
std::set<std::string> visitedList;
const gl::Shader *vertexShader = mState.getAttachedVertexShader();
ASSERT(mState.mUniformBlocks.empty());
for (const sh::InterfaceBlock &vertexBlock : vertexShader->getInterfaceBlocks())
{
// Only 'packed' blocks are allowed to be considered inacive.
// Only 'packed' blocks are allowed to be considered inactive.
if (!vertexBlock.staticUse && vertexBlock.layout == sh::BLOCKLAYOUT_PACKED)
continue;
......@@ -2528,7 +2746,7 @@ void Program::gatherInterfaceBlockInfo()
for (const sh::InterfaceBlock &fragmentBlock : fragmentShader->getInterfaceBlocks())
{
// Only 'packed' blocks are allowed to be considered inacive.
// Only 'packed' blocks are allowed to be considered inactive.
if (!fragmentBlock.staticUse && fragmentBlock.layout == sh::BLOCKLAYOUT_PACKED)
continue;
......@@ -2618,14 +2836,25 @@ void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenu
UniformBlock block(interfaceBlock.name, true, arrayElement);
block.memberUniformIndexes = blockUniformIndexes;
if (shaderType == GL_VERTEX_SHADER)
switch (shaderType)
{
block.vertexStaticUse = interfaceBlock.staticUse;
}
else
{
ASSERT(shaderType == GL_FRAGMENT_SHADER);
block.fragmentStaticUse = interfaceBlock.staticUse;
case GL_VERTEX_SHADER:
{
block.vertexStaticUse = interfaceBlock.staticUse;
break;
}
case GL_FRAGMENT_SHADER:
{
block.fragmentStaticUse = interfaceBlock.staticUse;
break;
}
case GL_COMPUTE_SHADER:
{
block.computeStaticUse = interfaceBlock.staticUse;
break;
}
default:
UNREACHABLE();
}
// TODO(jmadill): Determine if we can ever have an inactive array element block.
......@@ -2645,14 +2874,25 @@ void Program::defineUniformBlock(const sh::InterfaceBlock &interfaceBlock, GLenu
UniformBlock block(interfaceBlock.name, false, 0);
block.memberUniformIndexes = blockUniformIndexes;
if (shaderType == GL_VERTEX_SHADER)
{
block.vertexStaticUse = interfaceBlock.staticUse;
}
else
switch (shaderType)
{
ASSERT(shaderType == GL_FRAGMENT_SHADER);
block.fragmentStaticUse = interfaceBlock.staticUse;
case GL_VERTEX_SHADER:
{
block.vertexStaticUse = interfaceBlock.staticUse;
break;
}
case GL_FRAGMENT_SHADER:
{
block.fragmentStaticUse = interfaceBlock.staticUse;
break;
}
case GL_COMPUTE_SHADER:
{
block.computeStaticUse = interfaceBlock.staticUse;
break;
}
default:
UNREACHABLE();
}
block.dataSize = static_cast<unsigned int>(blockSize);
......
......@@ -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