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; ...@@ -49,7 +49,7 @@ typedef unsigned int GLenum;
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 153 #define ANGLE_SH_VERSION 154
typedef enum { typedef enum {
SH_GLES2_SPEC, SH_GLES2_SPEC,
...@@ -501,7 +501,7 @@ COMPILER_EXPORT const std::vector<sh::Varying> *ShGetVaryings(const ShHandle han ...@@ -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::Attribute> *ShGetAttributes(const ShHandle handle);
COMPILER_EXPORT const std::vector<sh::OutputVariable> *ShGetOutputVariables(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 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 typedef struct
{ {
......
...@@ -10,9 +10,9 @@ ...@@ -10,9 +10,9 @@
#ifndef GLSLANG_SHADERVARS_H_ #ifndef GLSLANG_SHADERVARS_H_
#define GLSLANG_SHADERVARS_H_ #define GLSLANG_SHADERVARS_H_
#include <algorithm>
#include <string> #include <string>
#include <vector> #include <vector>
#include <algorithm>
// Assume ShaderLang.h is included before ShaderVars.h, for sh::GLenum // Assume ShaderLang.h is included before ShaderVars.h, for sh::GLenum
// Note: make sure to increment ANGLE_SH_VERSION when changing ShaderVars.h // Note: make sure to increment ANGLE_SH_VERSION when changing ShaderVars.h
...@@ -216,6 +216,32 @@ struct COMPILER_EXPORT InterfaceBlock ...@@ -216,6 +216,32 @@ struct COMPILER_EXPORT InterfaceBlock
std::vector<InterfaceBlockField> fields; 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 } // namespace sh
#endif // GLSLANG_SHADERVARS_H_ #endif // GLSLANG_SHADERVARS_H_
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <array> #include <array>
#include "common/debug.h" #include "common/debug.h"
#include "GLSLANG/ShaderLang.h"
// //
// Precision qualifiers // Precision qualifiers
...@@ -377,8 +378,6 @@ enum TLayoutBlockStorage ...@@ -377,8 +378,6 @@ enum TLayoutBlockStorage
EbsStd140 EbsStd140
}; };
using TLocalSize = std::array<int, 3>;
struct TLayoutQualifier struct TLayoutQualifier
{ {
int location; int location;
...@@ -386,8 +385,7 @@ struct TLayoutQualifier ...@@ -386,8 +385,7 @@ struct TLayoutQualifier
TLayoutBlockStorage blockStorage; TLayoutBlockStorage blockStorage;
// Compute shader layout qualifiers. // Compute shader layout qualifiers.
// -1 means unspecified. sh::WorkGroupSize localSize;
TLocalSize localSize;
static TLayoutQualifier create() static TLayoutQualifier create()
{ {
...@@ -405,19 +403,12 @@ struct TLayoutQualifier ...@@ -405,19 +403,12 @@ struct TLayoutQualifier
bool isEmpty() const bool isEmpty() const
{ {
return location == -1 && matrixPacking == EmpUnspecified && return location == -1 && matrixPacking == EmpUnspecified &&
blockStorage == EbsUnspecified && localSize[0] == -1 && localSize[1] == -1 && blockStorage == EbsUnspecified && !localSize.isAnyValueSet();
localSize[2] == -1;
}
bool isGroupSizeSpecified() const
{
return std::any_of(localSize.begin(), localSize.end(),
[](int value) { return value != -1; });
} }
bool isCombinationValid() const bool isCombinationValid() const
{ {
bool workSizeSpecified = isGroupSizeSpecified(); bool workSizeSpecified = localSize.isAnyValueSet();
bool otherLayoutQualifiersSpecified = bool otherLayoutQualifiersSpecified =
(location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified); (location != -1 || matrixPacking != EmpUnspecified || blockStorage != EbsUnspecified);
...@@ -425,23 +416,13 @@ struct TLayoutQualifier ...@@ -425,23 +416,13 @@ struct TLayoutQualifier
return !(workSizeSpecified && otherLayoutQualifiersSpecified); 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) return localSize.isWorkGroupSizeMatching(localSizeIn);
{
bool result =
(localSize[i] == localSizeIn[i] || (localSize[i] == 1 && localSizeIn[i] == -1) ||
(localSize[i] == -1 && localSizeIn[i] == 1));
if (!result)
{
return false;
}
}
return true;
} }
}; };
inline const char *getLocalSizeString(size_t dimension) inline const char *getWorkGroupSizeString(size_t dimension)
{ {
switch (dimension) switch (dimension)
{ {
......
...@@ -86,7 +86,7 @@ class TCompiler : public TShHandleBase ...@@ -86,7 +86,7 @@ class TCompiler : public TShHandleBase
TInfoSink& getInfoSink() { return infoSink; } TInfoSink& getInfoSink() { return infoSink; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; } bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; }
const TLocalSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; } const sh::WorkGroupSize &getComputeShaderLocalSize() { return mComputeShaderLocalSize; }
// Clears the results from the previous compilation. // Clears the results from the previous compilation.
void clearResults(); void clearResults();
...@@ -237,7 +237,7 @@ class TCompiler : public TShHandleBase ...@@ -237,7 +237,7 @@ class TCompiler : public TShHandleBase
// compute shader local group size // compute shader local group size
bool mComputeShaderLocalSizeDeclared; bool mComputeShaderLocalSizeDeclared;
TLocalSize mComputeShaderLocalSize; sh::WorkGroupSize mComputeShaderLocalSize;
// name hashing. // name hashing.
ShHashFunction64 hashFunction; ShHashFunction64 hashFunction;
......
...@@ -1044,12 +1044,12 @@ void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location, ...@@ -1044,12 +1044,12 @@ void TParseContext::checkLayoutQualifierSupported(const TSourceLoc &location,
bool TParseContext::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location, bool TParseContext::checkWorkGroupSizeIsNotSpecified(const TSourceLoc &location,
const TLayoutQualifier &layoutQualifier) const TLayoutQualifier &layoutQualifier)
{ {
const TLocalSize &localSize = layoutQualifier.localSize; const sh::WorkGroupSize &localSize = layoutQualifier.localSize;
for (size_t i = 0u; i < localSize.size(); ++i) for (size_t i = 0u; i < localSize.size(); ++i)
{ {
if (localSize[i] != -1) 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"); "only valid when used with 'in' in a compute shader global layout declaration");
return false; return false;
} }
...@@ -1119,9 +1119,9 @@ void TParseContext::handlePragmaDirective(const TSourceLoc &loc, ...@@ -1119,9 +1119,9 @@ void TParseContext::handlePragmaDirective(const TSourceLoc &loc,
mDirectiveHandler.handlePragma(srcLoc, name, value, stdgl); 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) for (size_t i = 0u; i < result.size(); ++i)
{ {
if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1) if (mComputeShaderLocalSizeDeclared && mComputeShaderLocalSize[i] == -1)
...@@ -1895,7 +1895,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) ...@@ -1895,7 +1895,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
return; return;
} }
if (!layoutQualifier.isGroupSizeSpecified()) if (!layoutQualifier.localSize.isAnyValueSet())
{ {
error(typeQualifier.line, "No local work group size specified", "layout"); error(typeQualifier.line, "No local work group size specified", "layout");
return; return;
...@@ -1921,7 +1921,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier) ...@@ -1921,7 +1921,7 @@ void TParseContext::parseGlobalLayoutQualifier(const TPublicType &typeQualifier)
<< maxComputeWorkGroupSizeValue; << maxComputeWorkGroupSizeValue;
const std::string &errorMessage = errorMessageStream.str(); const std::string &errorMessage = errorMessageStream.str();
error(typeQualifier.line, "invalid value:", getLocalSizeString(i), error(typeQualifier.line, "invalid value:", getWorkGroupSizeString(i),
errorMessage.c_str()); errorMessage.c_str());
return; return;
} }
...@@ -3052,12 +3052,12 @@ void TParseContext::parseLocalSize(const TString &qualifierType, ...@@ -3052,12 +3052,12 @@ void TParseContext::parseLocalSize(const TString &qualifierType,
const TSourceLoc &intValueLine, const TSourceLoc &intValueLine,
const std::string &intValueString, const std::string &intValueString,
size_t index, size_t index,
TLocalSize *localSize) sh::WorkGroupSize *localSize)
{ {
checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310); checkLayoutQualifierSupported(qualifierTypeLine, qualifierType, 310);
if (intValue < 1) 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()); error(intValueLine, "out of range:", intValueString.c_str(), errorMessage.c_str());
} }
(*localSize)[index] = intValue; (*localSize)[index] = intValue;
...@@ -3136,7 +3136,7 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif ...@@ -3136,7 +3136,7 @@ TLayoutQualifier TParseContext::joinLayoutQualifiers(TLayoutQualifier leftQualif
{ {
error(rightQualifierLocation, error(rightQualifierLocation,
"Cannot have multiple different work group size specifiers", "Cannot have multiple different work group size specifiers",
getLocalSizeString(i)); getWorkGroupSizeString(i));
} }
joinedQualifier.localSize[i] = rightQualifier.localSize[i]; joinedQualifier.localSize[i] = rightQualifier.localSize[i];
} }
......
...@@ -117,7 +117,7 @@ class TParseContext : angle::NonCopyable ...@@ -117,7 +117,7 @@ class TParseContext : angle::NonCopyable
void decrSwitchNestingLevel() { --mSwitchNestingLevel; } void decrSwitchNestingLevel() { --mSwitchNestingLevel; }
bool isComputeShaderLocalSizeDeclared() const { return mComputeShaderLocalSizeDeclared; } 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. // 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); const TVariable *getNamedVariable(const TSourceLoc &location, const TString *name, const TSymbol *symbol);
...@@ -308,7 +308,7 @@ class TParseContext : angle::NonCopyable ...@@ -308,7 +308,7 @@ class TParseContext : angle::NonCopyable
const TSourceLoc &intValueLine, const TSourceLoc &intValueLine,
const std::string &intValueString, const std::string &intValueString,
size_t index, size_t index,
TLocalSize *localSize); sh::WorkGroupSize *localSize);
TLayoutQualifier parseLayoutQualifier( TLayoutQualifier parseLayoutQualifier(
const TString &qualifierType, const TSourceLoc &qualifierTypeLine); const TString &qualifierType, const TSourceLoc &qualifierTypeLine);
TLayoutQualifier parseLayoutQualifier(const TString &qualifierType, TLayoutQualifier parseLayoutQualifier(const TString &qualifierType,
...@@ -437,7 +437,7 @@ class TParseContext : angle::NonCopyable ...@@ -437,7 +437,7 @@ class TParseContext : angle::NonCopyable
// keep track of local group size declared in layout. It should be declared only once. // keep track of local group size declared in layout. It should be declared only once.
bool mComputeShaderLocalSizeDeclared; bool mComputeShaderLocalSizeDeclared;
TLocalSize mComputeShaderLocalSize; sh::WorkGroupSize mComputeShaderLocalSize;
}; };
int PaParseStrings( int PaParseStrings(
......
...@@ -358,7 +358,7 @@ const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handl ...@@ -358,7 +358,7 @@ const std::vector<sh::InterfaceBlock> *ShGetInterfaceBlocks(const ShHandle handl
return GetShaderVariables<sh::InterfaceBlock>(handle); return GetShaderVariables<sh::InterfaceBlock>(handle);
} }
std::array<int, 3> ShGetComputeShaderLocalGroupSize(const ShHandle handle) sh::WorkGroupSize ShGetComputeShaderLocalGroupSize(const ShHandle handle)
{ {
ASSERT(handle); ASSERT(handle);
......
...@@ -418,4 +418,71 @@ bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other) ...@@ -418,4 +418,71 @@ bool InterfaceBlock::isSameInterfaceBlockAtLinkTime(const InterfaceBlock &other)
return true; 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 } // namespace sh
...@@ -80,7 +80,7 @@ void TranslatorESSL::translate(TIntermNode *root, int compileOptions) ...@@ -80,7 +80,7 @@ void TranslatorESSL::translate(TIntermNode *root, int compileOptions)
if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared()) 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] sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
<< ", local_size_z=" << localSize[2] << ") in;\n"; << ", local_size_z=" << localSize[2] << ") in;\n";
} }
......
...@@ -167,7 +167,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions) ...@@ -167,7 +167,7 @@ void TranslatorGLSL::translate(TIntermNode *root, int compileOptions)
if (getShaderType() == GL_COMPUTE_SHADER && isComputeShaderLocalSizeDeclared()) 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] sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
<< ", local_size_z=" << localSize[2] << ") in;\n"; << ", local_size_z=" << localSize[2] << ") in;\n";
} }
......
...@@ -47,7 +47,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state) ...@@ -47,7 +47,8 @@ Compiler::Compiler(rx::GLImplFactory *implFactory, const ContextState &state)
mOutputType(mImplementation->getTranslatorOutputType()), mOutputType(mImplementation->getTranslatorOutputType()),
mResources(), mResources(),
mFragmentCompiler(nullptr), mFragmentCompiler(nullptr),
mVertexCompiler(nullptr) mVertexCompiler(nullptr),
mComputeCompiler(nullptr)
{ {
ASSERT(state.getClientMajorVersion() == 2 || state.getClientMajorVersion() == 3); ASSERT(state.getClientMajorVersion() == 2 || state.getClientMajorVersion() == 3);
...@@ -135,6 +136,15 @@ Error Compiler::release() ...@@ -135,6 +136,15 @@ Error Compiler::release()
activeCompilerHandles--; activeCompilerHandles--;
} }
if (mComputeCompiler)
{
ShDestruct(mComputeCompiler);
mComputeCompiler = nullptr;
ASSERT(activeCompilerHandles > 0);
activeCompilerHandles--;
}
if (activeCompilerHandles == 0) if (activeCompilerHandles == 0)
{ {
ShFinalize(); ShFinalize();
...@@ -157,7 +167,9 @@ ShHandle Compiler::getCompilerHandle(GLenum type) ...@@ -157,7 +167,9 @@ ShHandle Compiler::getCompilerHandle(GLenum type)
case GL_FRAGMENT_SHADER: case GL_FRAGMENT_SHADER:
compiler = &mFragmentCompiler; compiler = &mFragmentCompiler;
break; break;
case GL_COMPUTE_SHADER:
compiler = &mComputeCompiler;
break;
default: default:
UNREACHABLE(); UNREACHABLE();
return nullptr; return nullptr;
......
...@@ -42,6 +42,7 @@ class Compiler final : angle::NonCopyable ...@@ -42,6 +42,7 @@ class Compiler final : angle::NonCopyable
ShHandle mFragmentCompiler; ShHandle mFragmentCompiler;
ShHandle mVertexCompiler; ShHandle mVertexCompiler;
ShHandle mComputeCompiler;
}; };
} // namespace gl } // namespace gl
......
...@@ -165,6 +165,7 @@ class ProgramState final : angle::NonCopyable ...@@ -165,6 +165,7 @@ class ProgramState final : angle::NonCopyable
const Shader *getAttachedVertexShader() const { return mAttachedVertexShader; } const Shader *getAttachedVertexShader() const { return mAttachedVertexShader; }
const Shader *getAttachedFragmentShader() const { return mAttachedFragmentShader; } const Shader *getAttachedFragmentShader() const { return mAttachedFragmentShader; }
const Shader *getAttachedComputeShader() const { return mAttachedComputeShader; }
const std::vector<std::string> &getTransformFeedbackVaryingNames() const const std::vector<std::string> &getTransformFeedbackVaryingNames() const
{ {
return mTransformFeedbackVaryingNames; return mTransformFeedbackVaryingNames;
...@@ -198,8 +199,11 @@ class ProgramState final : angle::NonCopyable ...@@ -198,8 +199,11 @@ class ProgramState final : angle::NonCopyable
std::string mLabel; std::string mLabel;
sh::WorkGroupSize mComputeShaderLocalSize;
Shader *mAttachedFragmentShader; Shader *mAttachedFragmentShader;
Shader *mAttachedVertexShader; Shader *mAttachedVertexShader;
Shader *mAttachedComputeShader;
std::vector<std::string> mTransformFeedbackVaryingNames; std::vector<std::string> mTransformFeedbackVaryingNames;
std::vector<sh::Varying> mTransformFeedbackVaryingVars; std::vector<sh::Varying> mTransformFeedbackVaryingVars;
...@@ -377,12 +381,22 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -377,12 +381,22 @@ class Program final : angle::NonCopyable, public LabeledObject
InfoLog &infoLog, InfoLog &infoLog,
const Bindings &attributeBindings, const Bindings &attributeBindings,
const Shader *vertexShader); 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 linkUniformBlocks(InfoLog &infoLog, const Caps &caps);
bool linkVaryings(InfoLog &infoLog, const Shader *vertexShader, const Shader *fragmentShader) const; 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 linkUniforms(gl::InfoLog &infoLog, const gl::Caps &caps, const Bindings &uniformBindings);
bool indexUniforms(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, bool areMatchingInterfaceBlocks(gl::InfoLog &infoLog,
const sh::InterfaceBlock &fragmentInterfaceBlock); const sh::InterfaceBlock &vertexInterfaceBlock,
const sh::InterfaceBlock &fragmentInterfaceBlock) const;
static bool linkValidateVariablesBase(InfoLog &infoLog, static bool linkValidateVariablesBase(InfoLog &infoLog,
const std::string &variableName, const std::string &variableName,
...@@ -406,6 +420,13 @@ class Program final : angle::NonCopyable, public LabeledObject ...@@ -406,6 +420,13 @@ class Program final : angle::NonCopyable, public LabeledObject
std::vector<const sh::Varying *> getMergedVaryings() const; std::vector<const sh::Varying *> getMergedVaryings() const;
void linkOutputVariables(); 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); bool flattenUniformsAndCheckCaps(const Caps &caps, InfoLog &infoLog);
struct VectorAndSamplerCount struct VectorAndSamplerCount
......
...@@ -97,13 +97,10 @@ GLuint ResourceManager::createShader(rx::GLImplFactory *factory, ...@@ -97,13 +97,10 @@ GLuint ResourceManager::createShader(rx::GLImplFactory *factory,
const gl::Limitations &rendererLimitations, const gl::Limitations &rendererLimitations,
GLenum type) GLenum type)
{ {
ASSERT(type == GL_VERTEX_SHADER || type == GL_FRAGMENT_SHADER || type == GL_COMPUTE_SHADER);
GLuint handle = mProgramShaderHandleAllocator.allocate(); GLuint handle = mProgramShaderHandleAllocator.allocate();
if (type == GL_VERTEX_SHADER || type == GL_FRAGMENT_SHADER) mShaderMap[handle] = new Shader(this, factory, rendererLimitations, type, handle);
{
mShaderMap[handle] = new Shader(this, factory, rendererLimitations, type, handle);
}
else UNREACHABLE();
return handle; return handle;
} }
......
...@@ -75,6 +75,7 @@ bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y) ...@@ -75,6 +75,7 @@ bool CompareShaderVar(const sh::ShaderVariable &x, const sh::ShaderVariable &y)
ShaderState::ShaderState(GLenum shaderType) : mLabel(), mShaderType(shaderType), mShaderVersion(100) ShaderState::ShaderState(GLenum shaderType) : mLabel(), mShaderType(shaderType), mShaderVersion(100)
{ {
mLocalSize.fill(-1);
} }
ShaderState::~ShaderState() ShaderState::~ShaderState()
...@@ -306,18 +307,28 @@ void Shader::compile(Compiler *compiler) ...@@ -306,18 +307,28 @@ void Shader::compile(Compiler *compiler)
mState.mUniforms = GetShaderVariables(ShGetUniforms(compilerHandle)); mState.mUniforms = GetShaderVariables(ShGetUniforms(compilerHandle));
mState.mInterfaceBlocks = GetShaderVariables(ShGetInterfaceBlocks(compilerHandle)); mState.mInterfaceBlocks = GetShaderVariables(ShGetInterfaceBlocks(compilerHandle));
if (mState.mShaderType == GL_VERTEX_SHADER) switch (mState.mShaderType)
{ {
mState.mActiveAttributes = GetActiveShaderVariables(ShGetAttributes(compilerHandle)); case GL_COMPUTE_SHADER:
} {
else mState.mLocalSize = ShGetComputeShaderLocalGroupSize(compilerHandle);
{ break;
ASSERT(mState.mShaderType == GL_FRAGMENT_SHADER); }
case GL_VERTEX_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.mActiveAttributes = GetActiveShaderVariables(ShGetAttributes(compilerHandle));
mState.mActiveOutputVariables = break;
GetActiveShaderVariables(ShGetOutputVariables(compilerHandle)); }
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()); ASSERT(!mState.mTranslatedSource.empty());
......
...@@ -70,6 +70,8 @@ class ShaderState final : angle::NonCopyable ...@@ -70,6 +70,8 @@ class ShaderState final : angle::NonCopyable
std::string mTranslatedSource; std::string mTranslatedSource;
std::string mSource; std::string mSource;
sh::WorkGroupSize mLocalSize;
std::vector<sh::Varying> mVaryings; std::vector<sh::Varying> mVaryings;
std::vector<sh::Uniform> mUniforms; std::vector<sh::Uniform> mUniforms;
std::vector<sh::InterfaceBlock> mInterfaceBlocks; std::vector<sh::InterfaceBlock> mInterfaceBlocks;
...@@ -127,6 +129,8 @@ class Shader final : angle::NonCopyable, public LabeledObject ...@@ -127,6 +129,8 @@ class Shader final : angle::NonCopyable, public LabeledObject
int getSemanticIndex(const std::string &attributeName) const; int getSemanticIndex(const std::string &attributeName) const;
const sh::WorkGroupSize &getWorkGroupSize() const { return mState.mLocalSize; }
private: private:
static void getSourceImpl(const std::string &source, GLsizei bufSize, GLsizei *length, char *buffer); 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 ...@@ -121,7 +121,12 @@ const uint8_t *LinkedUniform::getDataPtrToElement(size_t elementIndex) const
} }
UniformBlock::UniformBlock() 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 ...@@ -131,7 +136,8 @@ UniformBlock::UniformBlock(const std::string &nameIn, bool isArrayIn, unsigned i
arrayElement(arrayElementIn), arrayElement(arrayElementIn),
dataSize(0), dataSize(0),
vertexStaticUse(false), vertexStaticUse(false),
fragmentStaticUse(false) fragmentStaticUse(false),
computeStaticUse(false)
{ {
} }
......
...@@ -63,6 +63,7 @@ struct UniformBlock ...@@ -63,6 +63,7 @@ struct UniformBlock
bool vertexStaticUse; bool vertexStaticUse;
bool fragmentStaticUse; bool fragmentStaticUse;
bool computeStaticUse;
std::vector<unsigned int> memberUniformIndexes; std::vector<unsigned int> memberUniformIndexes;
}; };
......
...@@ -101,53 +101,68 @@ LinkResult ProgramGL::link(const gl::ContextState &data, gl::InfoLog &infoLog) ...@@ -101,53 +101,68 @@ LinkResult ProgramGL::link(const gl::ContextState &data, gl::InfoLog &infoLog)
{ {
preLink(); preLink();
// Set the transform feedback state if (mState.getAttachedComputeShader())
std::vector<const GLchar *> transformFeedbackVaryings;
for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames())
{ {
transformFeedbackVaryings.push_back(tfVarying.c_str()); const ShaderGL *computeShaderGL = GetImplAs<ShaderGL>(mState.getAttachedComputeShader());
}
if (transformFeedbackVaryings.empty()) mFunctions->attachShader(mProgramID, computeShaderGL->getShaderID());
{
if (mFunctions->transformFeedbackVaryings) // Link and verify
{ mFunctions->linkProgram(mProgramID);
mFunctions->transformFeedbackVaryings(mProgramID, 0, nullptr,
mState.getTransformFeedbackBufferMode()); // Detach the shaders
} mFunctions->detachShader(mProgramID, computeShaderGL->getShaderID());
} }
else else
{ {
ASSERT(mFunctions->transformFeedbackVaryings); // Set the transform feedback state
mFunctions->transformFeedbackVaryings( std::vector<const GLchar *> transformFeedbackVaryings;
mProgramID, static_cast<GLsizei>(transformFeedbackVaryings.size()), for (const auto &tfVarying : mState.getTransformFeedbackVaryingNames())
&transformFeedbackVaryings[0], mState.getTransformFeedbackBufferMode()); {
} transformFeedbackVaryings.push_back(tfVarying.c_str());
}
const ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(mState.getAttachedVertexShader()); if (transformFeedbackVaryings.empty())
const ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(mState.getAttachedFragmentShader()); {
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 const ShaderGL *vertexShaderGL = GetImplAs<ShaderGL>(mState.getAttachedVertexShader());
mFunctions->attachShader(mProgramID, vertexShaderGL->getShaderID()); const ShaderGL *fragmentShaderGL = GetImplAs<ShaderGL>(mState.getAttachedFragmentShader());
mFunctions->attachShader(mProgramID, fragmentShaderGL->getShaderID());
// Bind attribute locations to match the GL layer. // Attach the shaders
for (const sh::Attribute &attribute : mState.getAttributes()) mFunctions->attachShader(mProgramID, vertexShaderGL->getShaderID());
{ mFunctions->attachShader(mProgramID, fragmentShaderGL->getShaderID());
if (!attribute.staticUse)
// 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 // Link and verify
mFunctions->linkProgram(mProgramID); mFunctions->linkProgram(mProgramID);
// Detach the shaders // Detach the shaders
mFunctions->detachShader(mProgramID, vertexShaderGL->getShaderID()); mFunctions->detachShader(mProgramID, vertexShaderGL->getShaderID());
mFunctions->detachShader(mProgramID, fragmentShaderGL->getShaderID()); mFunctions->detachShader(mProgramID, fragmentShaderGL->getShaderID());
}
// Verify the link // Verify the link
if (!checkLinkStatus(infoLog)) if (!checkLinkStatus(infoLog))
......
...@@ -3309,4 +3309,23 @@ bool ValidateCopySubTextureCHROMIUM(Context *context, ...@@ -3309,4 +3309,23 @@ bool ValidateCopySubTextureCHROMIUM(Context *context,
return true; 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 } // namespace gl
...@@ -305,6 +305,8 @@ bool ValidateCopySubTextureCHROMIUM(Context *context, ...@@ -305,6 +305,8 @@ bool ValidateCopySubTextureCHROMIUM(Context *context,
GLboolean unpackPremultiplyAlpha, GLboolean unpackPremultiplyAlpha,
GLboolean unpackUnmultiplyAlpha); GLboolean unpackUnmultiplyAlpha);
bool ValidateCreateShader(Context *context, GLenum type);
} // namespace gl } // namespace gl
#endif // LIBANGLE_VALIDATION_ES2_H_ #endif // LIBANGLE_VALIDATION_ES2_H_
...@@ -748,18 +748,13 @@ GLuint GL_APIENTRY CreateShader(GLenum type) ...@@ -748,18 +748,13 @@ GLuint GL_APIENTRY CreateShader(GLenum type)
Context *context = GetValidGlobalContext(); Context *context = GetValidGlobalContext();
if (context) if (context)
{ {
switch (type)
{
case GL_FRAGMENT_SHADER:
case GL_VERTEX_SHADER:
return context->createShader(type);
default: if (!context->skipValidation() && !ValidateCreateShader(context, type))
context->handleError(Error(GL_INVALID_ENUM)); {
return 0; return 0;
} }
return context->createShader(type);
} }
return 0; return 0;
} }
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
'<(angle_path)/src/tests/gl_tests/BuiltinVariableTest.cpp', '<(angle_path)/src/tests/gl_tests/BuiltinVariableTest.cpp',
'<(angle_path)/src/tests/gl_tests/ClearTest.cpp', '<(angle_path)/src/tests/gl_tests/ClearTest.cpp',
'<(angle_path)/src/tests/gl_tests/ColorMaskTest.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/CopyTexImageTest.cpp',
'<(angle_path)/src/tests/gl_tests/CopyTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/CopyTextureTest.cpp',
'<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp', '<(angle_path)/src/tests/gl_tests/CubeMapTextureTest.cpp',
......
...@@ -56,7 +56,7 @@ TEST_F(WorkGroupSizeTest, OnlyLocalSizeXSpecified) ...@@ -56,7 +56,7 @@ TEST_F(WorkGroupSizeTest, OnlyLocalSizeXSpecified)
compile(shaderString); compile(shaderString);
const TLocalSize &localSize = mTranslator->getComputeShaderLocalSize(); const sh::WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize();
ASSERT_EQ(5, localSize[0]); ASSERT_EQ(5, localSize[0]);
ASSERT_EQ(1, localSize[1]); ASSERT_EQ(1, localSize[1]);
ASSERT_EQ(1, localSize[2]); ASSERT_EQ(1, localSize[2]);
...@@ -73,7 +73,7 @@ TEST_F(WorkGroupSizeTest, LocalSizeXandZ) ...@@ -73,7 +73,7 @@ TEST_F(WorkGroupSizeTest, LocalSizeXandZ)
compile(shaderString); compile(shaderString);
const TLocalSize &localSize = mTranslator->getComputeShaderLocalSize(); const sh::WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize();
ASSERT_EQ(5, localSize[0]); ASSERT_EQ(5, localSize[0]);
ASSERT_EQ(1, localSize[1]); ASSERT_EQ(1, localSize[1]);
ASSERT_EQ(10, localSize[2]); ASSERT_EQ(10, localSize[2]);
...@@ -90,7 +90,7 @@ TEST_F(WorkGroupSizeTest, LocalSizeAll) ...@@ -90,7 +90,7 @@ TEST_F(WorkGroupSizeTest, LocalSizeAll)
compile(shaderString); compile(shaderString);
const TLocalSize &localSize = mTranslator->getComputeShaderLocalSize(); const sh::WorkGroupSize &localSize = mTranslator->getComputeShaderLocalSize();
ASSERT_EQ(5, localSize[0]); ASSERT_EQ(5, localSize[0]);
ASSERT_EQ(15, localSize[1]); ASSERT_EQ(15, localSize[1]);
ASSERT_EQ(10, localSize[2]); 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) ...@@ -555,6 +555,16 @@ PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor)
return PlatformParameters(3, 0, egl_platform::OPENGLES(major, 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() PlatformParameters ES2_OPENGL()
{ {
return PlatformParameters(2, 0, egl_platform::OPENGL()); return PlatformParameters(2, 0, egl_platform::OPENGL());
...@@ -575,4 +585,14 @@ PlatformParameters ES3_OPENGL(EGLint major, EGLint minor) ...@@ -575,4 +585,14 @@ PlatformParameters ES3_OPENGL(EGLint major, EGLint minor)
return PlatformParameters(3, 0, egl_platform::OPENGL(major, 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 } // namespace angle
...@@ -129,11 +129,15 @@ PlatformParameters ES2_OPENGL(); ...@@ -129,11 +129,15 @@ PlatformParameters ES2_OPENGL();
PlatformParameters ES2_OPENGL(EGLint major, EGLint minor); PlatformParameters ES2_OPENGL(EGLint major, EGLint minor);
PlatformParameters ES3_OPENGL(); PlatformParameters ES3_OPENGL();
PlatformParameters ES3_OPENGL(EGLint major, EGLint minor); PlatformParameters ES3_OPENGL(EGLint major, EGLint minor);
PlatformParameters ES31_OPENGL();
PlatformParameters ES31_OPENGL(EGLint major, EGLint minor);
PlatformParameters ES2_OPENGLES(); PlatformParameters ES2_OPENGLES();
PlatformParameters ES2_OPENGLES(EGLint major, EGLint minor); PlatformParameters ES2_OPENGLES(EGLint major, EGLint minor);
PlatformParameters ES3_OPENGLES(); PlatformParameters ES3_OPENGLES();
PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor); PlatformParameters ES3_OPENGLES(EGLint major, EGLint minor);
PlatformParameters ES31_OPENGLES();
PlatformParameters ES31_OPENGLES(EGLint major, EGLint minor);
} // namespace angle } // namespace angle
......
...@@ -55,13 +55,22 @@ class GLProgram ...@@ -55,13 +55,22 @@ class GLProgram
{ {
} }
GLProgram(const std::string &computeShader) : mHandle(0), mComputeShader(computeShader) {}
~GLProgram() { glDeleteProgram(mHandle); } ~GLProgram() { glDeleteProgram(mHandle); }
GLuint get() GLuint get()
{ {
if (mHandle == 0) if (mHandle == 0)
{ {
mHandle = CompileProgram(mVertexShader, mFragmentShader); if (!mComputeShader.empty())
{
mHandle = CompileComputeProgram(mComputeShader);
}
else
{
mHandle = CompileProgram(mVertexShader, mFragmentShader);
}
} }
return mHandle; return mHandle;
} }
...@@ -70,12 +79,17 @@ class GLProgram ...@@ -70,12 +79,17 @@ class GLProgram
GLuint mHandle; GLuint mHandle;
const std::string mVertexShader; const std::string mVertexShader;
const std::string mFragmentShader; const std::string mFragmentShader;
const std::string mComputeShader;
}; };
#define ANGLE_GL_PROGRAM(name, vertex, fragment) \ #define ANGLE_GL_PROGRAM(name, vertex, fragment) \
GLProgram name(vertex, fragment); \ GLProgram name(vertex, fragment); \
ASSERT_NE(0u, name.get()); ASSERT_NE(0u, name.get());
#define ANGLE_GL_COMPUTE_PROGRAM(name, compute) \
GLProgram name(compute); \
ASSERT_NE(0u, name.get());
} // namespace angle } // namespace angle
#endif // ANGLE_TESTS_GL_RAII_H_ #endif // ANGLE_TESTS_GL_RAII_H_
...@@ -77,6 +77,40 @@ GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath) ...@@ -77,6 +77,40 @@ GLuint CompileShaderFromFile(GLenum type, const std::string &sourcePath)
return CompileShader(type, source); 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( GLuint CompileProgramWithTransformFeedback(
const std::string &vsSource, const std::string &vsSource,
const std::string &fsSource, const std::string &fsSource,
...@@ -117,33 +151,7 @@ GLuint CompileProgramWithTransformFeedback( ...@@ -117,33 +151,7 @@ GLuint CompileProgramWithTransformFeedback(
glLinkProgram(program); glLinkProgram(program);
GLint linkStatus; return CheckLinkStatusAndReturnProgram(program, true);
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;
} }
GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource) GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource)
...@@ -163,3 +171,21 @@ GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsP ...@@ -163,3 +171,21 @@ GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsP
return CompileProgram(vsSource, fsSource); 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 @@ ...@@ -7,6 +7,7 @@
#ifndef SAMPLE_UTIL_SHADER_UTILS_H #ifndef SAMPLE_UTIL_SHADER_UTILS_H
#define SAMPLE_UTIL_SHADER_UTILS_H #define SAMPLE_UTIL_SHADER_UTILS_H
#include <GLES3/gl31.h>
#include <GLES3/gl3.h> #include <GLES3/gl3.h>
#include <GLES2/gl2.h> #include <GLES2/gl2.h>
#include <GLES2/gl2ext.h> #include <GLES2/gl2ext.h>
...@@ -28,5 +29,5 @@ GLuint CompileProgramWithTransformFeedback( ...@@ -28,5 +29,5 @@ GLuint CompileProgramWithTransformFeedback(
GLenum bufferMode); GLenum bufferMode);
GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource); GLuint CompileProgram(const std::string &vsSource, const std::string &fsSource);
GLuint CompileProgramFromFiles(const std::string &vsPath, const std::string &fsPath); 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 #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