зеркало из https://github.com/AvaloniaUI/angle.git
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/366740 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Olli Etuaho <oetuaho@nvidia.com> Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
This commit is contained in:
Родитель
63e1ec5c70
Коммит
4c4c8e7252
|
@ -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();
|
||||
mState.mAttachedFragmentShader = shader;
|
||||
mState.mAttachedFragmentShader->addRef();
|
||||
break;
|
||||
}
|
||||
case GL_COMPUTE_SHADER:
|
||||
{
|
||||
if (mState.mAttachedComputeShader)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
shader->release();
|
||||
mState.mAttachedFragmentShader = nullptr;
|
||||
break;
|
||||
}
|
||||
case GL_COMPUTE_SHADER:
|
||||
{
|
||||
if (mState.mAttachedComputeShader != shader)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
ASSERT(mState.mAttachedVertexShader->getType() == GL_VERTEX_SHADER);
|
||||
|
||||
if (mState.mAttachedFragmentShader->getShaderVersion() !=
|
||||
mState.mAttachedVertexShader->getShaderVersion())
|
||||
{
|
||||
mInfoLog << "Fragment shader version does not match vertex shader version.";
|
||||
return Error(GL_NO_ERROR);
|
||||
mInfoLog << "Both a compute and non-compute shaders are attached to the same program.";
|
||||
return NoError();
|
||||
}
|
||||
|
||||
if (!linkAttributes(data, mInfoLog, mAttributeBindings, mState.mAttachedVertexShader))
|
||||
if (mState.mAttachedComputeShader)
|
||||
{
|
||||
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);
|
||||
|
||||
mState.mComputeShaderLocalSize = mState.mAttachedComputeShader->getWorkGroupSize();
|
||||
|
||||
// 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, caps, mUniformBindings))
|
||||
{
|
||||
return NoError();
|
||||
}
|
||||
|
||||
if (!linkUniformBlocks(mInfoLog, caps))
|
||||
{
|
||||
return NoError();
|
||||
}
|
||||
|
||||
rx::LinkResult result = mProgram->link(data, mInfoLog);
|
||||
|
||||
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);
|
||||
|
||||
if (!mState.mAttachedVertexShader || !mState.mAttachedVertexShader->isCompiled())
|
||||
{
|
||||
return NoError();
|
||||
}
|
||||
ASSERT(mState.mAttachedVertexShader->getType() == GL_VERTEX_SHADER);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (!linkVaryings(mInfoLog, mState.mAttachedVertexShader, mState.mAttachedFragmentShader))
|
||||
{
|
||||
return Error(GL_NO_ERROR);
|
||||
}
|
||||
|
||||
if (!linkUniforms(mInfoLog, data.getCaps(), mUniformBindings))
|
||||
{
|
||||
return Error(GL_NO_ERROR);
|
||||
}
|
||||
|
||||
if (!linkUniformBlocks(mInfoLog, data.getCaps()))
|
||||
{
|
||||
return Error(GL_NO_ERROR);
|
||||
}
|
||||
|
||||
const auto &mergedVaryings = getMergedVaryings();
|
||||
|
||||
if (!linkValidateTransformFeedback(mInfoLog, mergedVaryings, data.getCaps()))
|
||||
{
|
||||
return Error(GL_NO_ERROR);
|
||||
}
|
||||
|
||||
linkOutputVariables();
|
||||
|
||||
rx::LinkResult result = mProgram->link(data, mInfoLog);
|
||||
if (result.error.isError() || !result.linkSuccess)
|
||||
{
|
||||
return result.error;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
// Check that interface blocks defined in the vertex and fragment shaders are identical
|
||||
typedef std::map<std::string, const sh::InterfaceBlock*> UniformBlockMap;
|
||||
UniformBlockMap linkedUniformBlocks;
|
||||
|
||||
GLuint vertexBlockCount = 0;
|
||||
for (const sh::InterfaceBlock &vertexInterfaceBlock : vertexInterfaceBlocks)
|
||||
GLuint blockCount = 0;
|
||||
for (const sh::InterfaceBlock &block : intefaceBlocks)
|
||||
{
|
||||
linkedUniformBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock;
|
||||
|
||||
// Note: shared and std140 layouts are always considered active
|
||||
if (vertexInterfaceBlock.staticUse || vertexInterfaceBlock.layout != sh::BLOCKLAYOUT_PACKED)
|
||||
if (block.staticUse || block.layout != sh::BLOCKLAYOUT_PACKED)
|
||||
{
|
||||
if (++vertexBlockCount > caps.maxVertexUniformBlocks)
|
||||
if (++blockCount > maxUniformBlocks)
|
||||
{
|
||||
infoLog << "Vertex shader uniform block count exceed GL_MAX_VERTEX_UNIFORM_BLOCKS ("
|
||||
<< caps.maxVertexUniformBlocks << ")";
|
||||
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;
|
||||
UniformBlockMap linkedUniformBlocks;
|
||||
|
||||
for (const sh::InterfaceBlock &vertexInterfaceBlock : vertexInterfaceBlocks)
|
||||
{
|
||||
linkedUniformBlocks[vertexInterfaceBlock.name] = &vertexInterfaceBlock;
|
||||
}
|
||||
|
||||
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::flattenUniformsAndCheckCapsForShader(const gl::Shader &shader,
|
||||
GLuint maxUniformComponents,
|
||||
GLuint maxTextureImageUnits,
|
||||
const std::string &componentsErrorMessage,
|
||||
const std::string &samplerErrorMessage,
|
||||
std::vector<LinkedUniform> &samplerUniforms,
|
||||
InfoLog &infoLog)
|
||||
{
|
||||
VectorAndSamplerCount vasCount;
|
||||
for (const sh::Uniform &uniform : shader.getUniforms())
|
||||
{
|
||||
if (uniform.staticUse)
|
||||
{
|
||||
vasCount += flattenUniform(uniform, uniform.name, &samplerUniforms);
|
||||
}
|
||||
}
|
||||
|
||||
if (vasCount.vectorCount > maxUniformComponents)
|
||||
{
|
||||
infoLog << componentsErrorMessage << maxUniformComponents << ").";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (vasCount.samplerCount > maxTextureImageUnits)
|
||||
{
|
||||
infoLog << samplerErrorMessage << maxTextureImageUnits << ").";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Program::flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog)
|
||||
{
|
||||
const gl::Shader *vertexShader = mState.getAttachedVertexShader();
|
||||
VectorAndSamplerCount vsCounts;
|
||||
|
||||
std::vector<LinkedUniform> samplerUniforms;
|
||||
|
||||
for (const sh::Uniform &uniform : vertexShader->getUniforms())
|
||||
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))
|
||||
{
|
||||
vsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (vsCounts.vectorCount > caps.maxVertexUniformVectors)
|
||||
else
|
||||
{
|
||||
infoLog << "Vertex shader active uniforms exceed MAX_VERTEX_UNIFORM_VECTORS ("
|
||||
<< caps.maxVertexUniformVectors << ").";
|
||||
return false;
|
||||
}
|
||||
const gl::Shader *vertexShader = mState.getAttachedVertexShader();
|
||||
|
||||
if (vsCounts.samplerCount > caps.maxVertexTextureImageUnits)
|
||||
{
|
||||
infoLog << "Vertex shader sampler count exceeds MAX_VERTEX_TEXTURE_IMAGE_UNITS ("
|
||||
<< caps.maxVertexTextureImageUnits << ").";
|
||||
return false;
|
||||
}
|
||||
|
||||
const gl::Shader *fragmentShader = mState.getAttachedFragmentShader();
|
||||
VectorAndSamplerCount fsCounts;
|
||||
|
||||
for (const sh::Uniform &uniform : fragmentShader->getUniforms())
|
||||
{
|
||||
if (uniform.staticUse)
|
||||
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))
|
||||
{
|
||||
fsCounts += flattenUniform(uniform, uniform.name, &samplerUniforms);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
const gl::Shader *fragmentShader = mState.getAttachedFragmentShader();
|
||||
|
||||
if (fsCounts.vectorCount > caps.maxFragmentUniformVectors)
|
||||
{
|
||||
infoLog << "Fragment shader active uniforms exceed MAX_FRAGMENT_UNIFORM_VECTORS ("
|
||||
<< caps.maxFragmentUniformVectors << ").";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (fsCounts.samplerCount > caps.maxTextureImageUnits)
|
||||
{
|
||||
infoLog << "Fragment shader sampler count exceeds MAX_TEXTURE_IMAGE_UNITS ("
|
||||
<< caps.maxTextureImageUnits << ").";
|
||||
return false;
|
||||
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)
|
||||
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();
|
||||
}
|
||||
|
||||
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,54 +101,69 @@ 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());
|
||||
}
|
||||
|
||||
const ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(mState.getAttachedVertexShader());
|
||||
const ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(mState.getAttachedFragmentShader());
|
||||
|
||||
// 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())
|
||||
{
|
||||
if (!attribute.staticUse)
|
||||
// Set the transform feedback state
|
||||
std::vector<const GLchar *> transformFeedbackVaryings;
|
||||
for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames())
|
||||
{
|
||||
continue;
|
||||
transformFeedbackVaryings.push_back(tfVarying.c_str());
|
||||
}
|
||||
|
||||
mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str());
|
||||
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());
|
||||
}
|
||||
|
||||
const ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(mState.getAttachedVertexShader());
|
||||
const ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(mState.getAttachedFragmentShader());
|
||||
|
||||
// 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())
|
||||
{
|
||||
if (!attribute.staticUse)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
mFunctions->bindAttribLocation(mProgramID, attribute.location, attribute.name.c_str());
|
||||
}
|
||||
|
||||
// Link and verify
|
||||
mFunctions->linkProgram(mProgramID);
|
||||
|
||||
// Detach the shaders
|
||||
mFunctions->detachShader(mProgramID, vertexShaderGL->getShaderID());
|
||||
mFunctions->detachShader(mProgramID, fragmentShaderGL->getShaderID());
|
||||
}
|
||||
|
||||
// Link and verify
|
||||
mFunctions->linkProgram(mProgramID);
|
||||
|
||||
// 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]);
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
//
|
||||
// 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
|
||||
|
|
Загрузка…
Ссылка в новой задаче