Commit 84c11c53 by Jamie Madill Committed by Commit Bot

Vulkan: Implement sampler structs as function args.

Bug: angleproject:2494 Change-Id: Ia8e374846427b7140ab2565ae5b9b18409a76d96 Reviewed-on: https://chromium-review.googlesource.com/1117323 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarOlli Etuaho <oetuaho@nvidia.com>
parent 5c8113d3
......@@ -55,6 +55,12 @@ class ImmutableStringBuilder
}
}
template <typename T>
static constexpr size_t GetHexCharCount()
{
return sizeof(T) * 2;
}
private:
inline static char *AllocateEmptyPoolCharArray(size_t strLength)
{
......
......@@ -219,4 +219,17 @@ bool TFunction::isImageFunction() const
(name() == kImageSizeName || name() == kImageLoadName || name() == kImageStoreName);
}
bool TFunction::hasSamplerInStructParams() const
{
for (size_t paramIndex = 0; paramIndex < mParamCount; ++paramIndex)
{
const TVariable *param = getParam(paramIndex);
if (param->getType().isStructureContainingSamplers())
{
return true;
}
}
return false;
}
} // namespace sh
......@@ -234,6 +234,7 @@ class TFunction : public TSymbol
bool isMain() const;
bool isImageFunction() const;
bool hasSamplerInStructParams() const;
// Note: Only to be used for static built-in functions!
constexpr TFunction(const TSymbolUniqueId &id,
......
......@@ -113,6 +113,8 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
const TSymbol *findUserDefined(const ImmutableString &name) const;
TFunction *findUserDefinedFunction(const ImmutableString &name) const;
const TSymbol *findGlobal(const ImmutableString &name) const;
const TSymbol *findBuiltIn(const ImmutableString &name, int shaderVersion) const;
......@@ -157,8 +159,6 @@ class TSymbolTable : angle::NonCopyable, TSymbolTableBase
class TSymbolTableLevel;
TFunction *findUserDefinedFunction(const ImmutableString &name) const;
void initSamplerDefaultPrecision(TBasicType samplerType);
void initializeBuiltInVariables(sh::GLenum shaderType,
......
......@@ -16,11 +16,14 @@ namespace sh
{
namespace
{
// Maximum string size of a hex unsigned int.
constexpr size_t kHexSize = ImmutableStringBuilder::GetHexCharCount<unsigned int>();
class Traverser final : public TIntermTraverser
{
public:
explicit Traverser(TSymbolTable *symbolTable)
: TIntermTraverser(true, false, false, symbolTable), mRemovedUniformsCount(0)
: TIntermTraverser(true, false, true, symbolTable), mRemovedUniformsCount(0)
{
mSymbolTable->push();
}
......@@ -29,9 +32,12 @@ class Traverser final : public TIntermTraverser
int removedUniformsCount() const { return mRemovedUniformsCount; }
// Each struct sampler declaration is stripped of its samplers. New uniforms are added for each
// stripped struct sampler.
bool visitDeclaration(Visit visit, TIntermDeclaration *decl) override
{
ASSERT(visit == PreVisit);
if (visit != PreVisit)
return true;
if (!mInGlobalScope)
{
......@@ -65,61 +71,139 @@ class Traverser final : public TIntermTraverser
return true;
}
// Each struct sampler reference is replaced with a reference to the new extracted sampler.
bool visitBinary(Visit visit, TIntermBinary *node) override
{
if (visit != PreVisit)
return true;
if (node->getOp() == EOpIndexDirectStruct && node->getType().isSampler())
{
std::string stringBuilder;
ImmutableString newName = GetStructSamplerNameFromTypedNode(node);
const TVariable *samplerReplacement =
static_cast<const TVariable *>(mSymbolTable->findUserDefined(newName));
ASSERT(samplerReplacement);
TIntermTyped *currentNode = node;
while (currentNode->getAsBinaryNode())
{
TIntermBinary *asBinary = currentNode->getAsBinaryNode();
TIntermSymbol *replacement = new TIntermSymbol(samplerReplacement);
switch (asBinary->getOp())
{
case EOpIndexDirect:
{
const int index = asBinary->getRight()->getAsConstantUnion()->getIConst(0);
const std::string strInt = Str(index);
stringBuilder.insert(0, strInt);
stringBuilder.insert(0, "_");
break;
}
case EOpIndexDirectStruct:
{
stringBuilder.insert(0, asBinary->getIndexStructFieldName().data());
stringBuilder.insert(0, "_");
break;
}
queueReplacement(replacement, OriginalNode::IS_DROPPED);
return true;
}
default:
UNREACHABLE();
break;
}
return true;
}
currentNode = asBinary->getLeft();
}
// In we are passing references to structs containing samplers we must new additional
// arguments. For each extracted struct sampler a new argument is added. This chains to nested
// structs.
void visitFunctionPrototype(TIntermFunctionPrototype *node) override
{
const TFunction *function = node->getFunction();
const ImmutableString &variableName = currentNode->getAsSymbolNode()->variable().name();
stringBuilder.insert(0, variableName.data());
if (!function->hasSamplerInStructParams())
{
return;
}
ImmutableString newName(stringBuilder);
const TSymbol *foundFunction = mSymbolTable->findUserDefined(function->name());
if (foundFunction)
{
ASSERT(foundFunction->isFunction());
function = static_cast<const TFunction *>(foundFunction);
}
else
{
TFunction *newFunction = createStructSamplerFunction(function);
mSymbolTable->declareUserDefinedFunction(newFunction, true);
function = newFunction;
}
const TVariable *samplerReplacement =
static_cast<const TVariable *>(mSymbolTable->findUserDefined(newName));
ASSERT(samplerReplacement);
ASSERT(!function->hasSamplerInStructParams());
TIntermFunctionPrototype *newProto = new TIntermFunctionPrototype(function);
queueReplacement(newProto, OriginalNode::IS_DROPPED);
}
TIntermSymbol *replacement = new TIntermSymbol(samplerReplacement);
// We insert a new scope for each function definition so we can track the new parameters.
bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override
{
if (visit == PreVisit)
{
mSymbolTable->push();
}
else
{
ASSERT(visit == PostVisit);
mSymbolTable->pop();
}
return true;
}
queueReplacement(replacement, OriginalNode::IS_DROPPED);
// For function call nodes we pass references to the extracted struct samplers in that scope.
bool visitAggregate(Visit visit, TIntermAggregate *node) override
{
if (visit != PreVisit)
return true;
if (!node->isFunctionCall())
return true;
const TFunction *function = node->getFunction();
if (!function->hasSamplerInStructParams())
return true;
}
ASSERT(node->getOp() == EOpCallFunctionInAST);
TFunction *newFunction = mSymbolTable->findUserDefinedFunction(function->name());
TIntermSequence *newArguments = getStructSamplerArguments(function, node->getSequence());
TIntermAggregate *newCall =
TIntermAggregate::CreateFunctionCall(*newFunction, newArguments);
queueReplacement(newCall, OriginalNode::IS_DROPPED);
return true;
}
private:
// This returns the name of a struct sampler reference. References are always TIntermBinary.
static ImmutableString GetStructSamplerNameFromTypedNode(TIntermTyped *node)
{
std::string stringBuilder;
TIntermTyped *currentNode = node;
while (currentNode->getAsBinaryNode())
{
TIntermBinary *asBinary = currentNode->getAsBinaryNode();
switch (asBinary->getOp())
{
case EOpIndexDirect:
{
const int index = asBinary->getRight()->getAsConstantUnion()->getIConst(0);
const std::string strInt = Str(index);
stringBuilder.insert(0, strInt);
stringBuilder.insert(0, "_");
break;
}
case EOpIndexDirectStruct:
{
stringBuilder.insert(0, asBinary->getIndexStructFieldName().data());
stringBuilder.insert(0, "_");
break;
}
default:
UNREACHABLE();
break;
}
currentNode = asBinary->getLeft();
}
const ImmutableString &variableName = currentNode->getAsSymbolNode()->variable().name();
stringBuilder.insert(0, variableName.data());
return stringBuilder;
}
// Removes all the struct samplers from a struct specifier.
void stripStructSpecifierSamplers(const TStructure *structure, TIntermSequence *newSequence)
{
TFieldList *newFieldList = new TFieldList;
......@@ -155,14 +239,19 @@ class Traverser final : public TIntermTraverser
structDecl->appendDeclarator(newStructRef);
newSequence->push_back(structDecl);
mSymbolTable->declare(newStruct);
}
// Returns true if the type is a struct that was removed because we extracted all the members.
bool isRemovedStructType(const TType &type) const
{
const TStructure *structure = type.getStruct();
return (structure && (mRemovedStructs.count(structure->name()) > 0));
}
// Removes samplers from struct uniforms. For each sampler removed also adds a new globally
// defined sampler uniform.
void extractStructSamplerUniforms(TIntermDeclaration *oldDeclaration,
const TVariable &variable,
const TStructure *structure,
......@@ -189,6 +278,7 @@ class Traverser final : public TIntermTraverser
}
}
// Extracts samplers from a field of a struct. Works with nested structs and arrays.
size_t extractFieldSamplers(const ImmutableString &prefix,
const TField *field,
const TType &containingType,
......@@ -202,7 +292,7 @@ class Traverser final : public TIntermTraverser
const TVector<unsigned int> &arraySizes = *containingType.getArraySizes();
for (unsigned int arrayElement = 0; arrayElement < arraySizes[0]; ++arrayElement)
{
ImmutableStringBuilder stringBuilder(prefix.length() + 10);
ImmutableStringBuilder stringBuilder(prefix.length() + kHexSize + 1);
stringBuilder << prefix << "_";
stringBuilder.appendHex(arrayElement);
nonSamplerCount = extractFieldSamplersImpl(stringBuilder, field, newSequence);
......@@ -214,6 +304,7 @@ class Traverser final : public TIntermTraverser
return extractFieldSamplersImpl(prefix, field, newSequence);
}
// Extracts samplers from a field of a struct. Works with nested structs and arrays.
size_t extractFieldSamplersImpl(const ImmutableString &prefix,
const TField *field,
TIntermSequence *newSequence)
......@@ -249,9 +340,10 @@ class Traverser final : public TIntermTraverser
return nonSamplerCount;
}
// Extracts a sampler from a struct. Declares the new extracted sampler.
void extractSampler(const ImmutableString &newName,
const TType &fieldType,
TIntermSequence *newSequence)
TIntermSequence *newSequence) const
{
TType *newType = new TType(fieldType);
newType->setQualifier(EvqUniform);
......@@ -267,6 +359,263 @@ class Traverser final : public TIntermTraverser
mSymbolTable->declareInternal(newVariable);
}
// Returns the chained name of a sampler uniform field.
static ImmutableString GetFieldName(const ImmutableString &paramName,
const TField *field,
unsigned arrayIndex)
{
ImmutableStringBuilder nameBuilder(paramName.length() + kHexSize + 2 +
field->name().length());
nameBuilder << paramName << "_";
if (arrayIndex < std::numeric_limits<unsigned>::max())
{
nameBuilder.appendHex(arrayIndex);
nameBuilder << "_";
}
nameBuilder << field->name();
return nameBuilder;
}
// A pattern that visits every parameter of a function call. Uses different handlers for struct
// parameters, struct sampler parameters, and non-struct parameters.
class StructSamplerFunctionVisitor : angle::NonCopyable
{
public:
StructSamplerFunctionVisitor() = default;
virtual ~StructSamplerFunctionVisitor() = default;
virtual void traverse(const TFunction *function)
{
size_t paramCount = function->getParamCount();
for (size_t paramIndex = 0; paramIndex < paramCount; ++paramIndex)
{
const TVariable *param = function->getParam(paramIndex);
const TType &paramType = param->getType();
if (paramType.isStructureContainingSamplers())
{
const ImmutableString &baseName = getNameFromIndex(function, paramIndex);
if (traverseStructContainingSamplers(baseName, paramType))
{
visitStructParam(function, paramIndex);
}
}
else
{
visitNonStructParam(function, paramIndex);
}
}
}
virtual ImmutableString getNameFromIndex(const TFunction *function, size_t paramIndex) = 0;
virtual void visitSamplerInStructParam(const ImmutableString &name,
const TField *field) = 0;
virtual void visitStructParam(const TFunction *function, size_t paramIndex) = 0;
virtual void visitNonStructParam(const TFunction *function, size_t paramIndex) = 0;
protected:
// Helper method to get the sampler extracted struct type of a parameter.
TType *getStructSamplerParameterType(TSymbolTable *symbolTable, const TVariable *param)
{
const TStructure *structure = param->getType().getStruct();
const TSymbol *structSymbol = symbolTable->findUserDefined(structure->name());
ASSERT(structSymbol && structSymbol->isStruct());
const TStructure *structVar = static_cast<const TStructure *>(structSymbol);
TType *structType = new TType(structVar, false);
ASSERT(!structType->isStructureContainingSamplers());
return structType;
}
private:
bool traverseStructContainingSamplers(const ImmutableString &baseName,
const TType &structType)
{
bool hasNonSamplerFields = false;
const TStructure *structure = structType.getStruct();
for (const TField *field : structure->fields())
{
if (field->type()->isStructureContainingSamplers() || field->type()->isSampler())
{
if (traverseSamplerInStruct(baseName, structType, field))
{
hasNonSamplerFields = true;
}
}
else
{
hasNonSamplerFields = true;
}
}
return hasNonSamplerFields;
}
bool traverseSamplerInStruct(const ImmutableString &baseName,
const TType &baseType,
const TField *field)
{
bool hasNonSamplerParams = false;
if (baseType.isArray())
{
const TVector<unsigned int> &arraySizes = *baseType.getArraySizes();
ASSERT(arraySizes.size() == 1);
for (unsigned int arrayIndex = 0; arrayIndex < arraySizes[0]; ++arrayIndex)
{
ImmutableString name = GetFieldName(baseName, field, arrayIndex);
if (field->type()->isStructureContainingSamplers())
{
if (traverseStructContainingSamplers(name, *field->type()))
{
hasNonSamplerParams = true;
}
}
else
{
ASSERT(field->type()->isSampler());
visitSamplerInStructParam(name, field);
}
}
}
else if (field->type()->isStructureContainingSamplers())
{
ImmutableString name =
GetFieldName(baseName, field, std::numeric_limits<unsigned>::max());
hasNonSamplerParams = traverseStructContainingSamplers(name, *field->type());
}
else
{
ASSERT(field->type()->isSampler());
ImmutableString name =
GetFieldName(baseName, field, std::numeric_limits<unsigned>::max());
visitSamplerInStructParam(name, field);
}
return hasNonSamplerParams;
}
};
// A visitor that replaces functions with struct sampler references. The struct sampler
// references are expanded to include new fields for the structs.
class CreateStructSamplerFunctionVisitor final : public StructSamplerFunctionVisitor
{
public:
CreateStructSamplerFunctionVisitor(TSymbolTable *symbolTable)
: mSymbolTable(symbolTable), mNewFunction(nullptr)
{
}
ImmutableString getNameFromIndex(const TFunction *function, size_t paramIndex) override
{
const TVariable *param = function->getParam(paramIndex);
return param->name();
}
void traverse(const TFunction *function) override
{
mNewFunction =
new TFunction(mSymbolTable, function->name(), function->symbolType(),
&function->getReturnType(), function->isKnownToNotHaveSideEffects());
StructSamplerFunctionVisitor::traverse(function);
}
void visitSamplerInStructParam(const ImmutableString &name, const TField *field) override
{
TVariable *fieldSampler =
new TVariable(mSymbolTable, name, field->type(), SymbolType::AngleInternal);
mNewFunction->addParameter(fieldSampler);
mSymbolTable->declareInternal(fieldSampler);
}
void visitStructParam(const TFunction *function, size_t paramIndex) override
{
const TVariable *param = function->getParam(paramIndex);
TType *structType = getStructSamplerParameterType(mSymbolTable, param);
TVariable *newParam =
new TVariable(mSymbolTable, param->name(), structType, param->symbolType());
mNewFunction->addParameter(newParam);
}
void visitNonStructParam(const TFunction *function, size_t paramIndex) override
{
const TVariable *param = function->getParam(paramIndex);
mNewFunction->addParameter(param);
}
TFunction *getNewFunction() const { return mNewFunction; }
private:
TSymbolTable *mSymbolTable;
TFunction *mNewFunction;
};
TFunction *createStructSamplerFunction(const TFunction *function) const
{
CreateStructSamplerFunctionVisitor visitor(mSymbolTable);
visitor.traverse(function);
return visitor.getNewFunction();
}
// A visitor that replaces function calls with expanded struct sampler parameters.
class GetSamplerArgumentsVisitor final : public StructSamplerFunctionVisitor
{
public:
GetSamplerArgumentsVisitor(TSymbolTable *symbolTable, const TIntermSequence *arguments)
: mSymbolTable(symbolTable), mArguments(arguments), mNewArguments(new TIntermSequence)
{
}
ImmutableString getNameFromIndex(const TFunction *function, size_t paramIndex) override
{
TIntermTyped *argument = (*mArguments)[paramIndex]->getAsTyped();
return GetStructSamplerNameFromTypedNode(argument);
}
void visitSamplerInStructParam(const ImmutableString &name, const TField *field) override
{
TVariable *argSampler =
new TVariable(mSymbolTable, name, field->type(), SymbolType::AngleInternal);
TIntermSymbol *argSymbol = new TIntermSymbol(argSampler);
mNewArguments->push_back(argSymbol);
}
void visitStructParam(const TFunction *function, size_t paramIndex) override
{
// The prior struct type is left intact. This leaves the tree in an inconsistent state.
// TODO(jmadill): Fix tree structure. http://anglebug.com/2494
TIntermTyped *argument = (*mArguments)[paramIndex]->getAsTyped();
mNewArguments->push_back(argument);
}
void visitNonStructParam(const TFunction *function, size_t paramIndex) override
{
TIntermTyped *argument = (*mArguments)[paramIndex]->getAsTyped();
mNewArguments->push_back(argument);
}
TIntermSequence *getNewArguments() const { return mNewArguments; }
private:
TSymbolTable *mSymbolTable;
const TIntermSequence *mArguments;
TIntermSequence *mNewArguments;
};
TIntermSequence *getStructSamplerArguments(const TFunction *function,
const TIntermSequence *arguments) const
{
GetSamplerArgumentsVisitor visitor(mSymbolTable, arguments);
visitor.traverse(function);
return visitor.getNewArguments();
}
int mRemovedUniformsCount;
std::set<ImmutableString> mRemovedStructs;
};
......
......@@ -267,9 +267,28 @@
1028 WIN LINUX MAC : dEQP-GLES2.functional.fbo.completeness.renderable.texture.depth.srgb8 = FAIL
// Vulkan failures
2494 VULKAN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_in_function_arg_* = SKIP
2494 VULKAN : dEQP-GLES2.functional.shaders.struct.uniform.sampler_in_array_function_arg_* = SKIP
2592 VULKAN : dEQP-GLES2.functional.shaders.builtin_variable.depth_range* = SKIP
2598 VULKAN : dEQP-GLES2.functional.rasterization.primitives.line* = SKIP
2599 VULKAN : dEQP-GLES2.functional.rasterization.limits.points = SKIP
2601 VULKAN : dEQP-GLES2.functional.clipping.* = SKIP
2353 VULKAN : dEQP-GLES2.functional.polygon_offset.* = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.points.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.triangles.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.triangle_fan.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.triangle_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.lines.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.line_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.line_loop.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.indices.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.points.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.triangles.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.triangle_fan.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.triangle_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.lines.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.line_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.line_loop.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.random.* = SKIP
2444 VULKAN : dEQP-GLES2.functional.default_vertex_attrib.* = SKIP
// fixed format vertex data
2405 VULKAN : dEQP-GLES2.functional.vertex_arrays.multiple_attributes.input_types.3_byte2_vec2_byte2_vec2_fixed* = SKIP
......@@ -307,27 +326,6 @@
2405 VULKAN : dEQP-GLES2.functional.vertex_arrays.single_attribute.usages.buffer_0_0_fixed* = SKIP
2405 VULKAN : dEQP-GLES2.functional.vertex_arrays.single_attribute.usages.buffer_0_32_fixed* = SKIP
2405 VULKAN : dEQP-GLES2.functional.vertex_arrays.single_attribute.usages.buffer_0_8_fixed* = SKIP
2598 VULKAN : dEQP-GLES2.functional.rasterization.primitives.line* = SKIP
2599 VULKAN : dEQP-GLES2.functional.rasterization.limits.points = SKIP
2601 VULKAN : dEQP-GLES2.functional.clipping.* = SKIP
2353 VULKAN : dEQP-GLES2.functional.polygon_offset.* = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.points.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.triangles.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.triangle_fan.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.triangle_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.lines.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.line_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_arrays.line_loop.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.indices.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.points.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.triangles.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.triangle_fan.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.triangle_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.lines.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.line_strip.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.draw_elements.line_loop.default_attribute = SKIP
2444 VULKAN : dEQP-GLES2.functional.draw.random.* = SKIP
2444 VULKAN : dEQP-GLES2.functional.default_vertex_attrib.* = SKIP
// Vulkan AMD Windows specific failures
2602 VULKAN WIN AMD : dEQP-GLES2.functional.buffer.write.* = SKIP
......
......@@ -3224,15 +3224,104 @@ TEST_P(GLSLTest_ES3, VaryingStructUsedInFragmentShader)
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
// This test covers passing a struct containing a sampler as a function argument.
TEST_P(GLSLTest, StructsWithSamplersAsFunctionArg)
{
// Shader failed to compile on Android. http://anglebug.com/2114
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
const char kFragmentShader[] = R"(precision mediump float;
struct S { sampler2D samplerMember; };
uniform S uStruct;
uniform vec2 uTexCoord;
vec4 foo(S structVar)
{
return texture2D(structVar.samplerMember, uTexCoord);
}
void main()
{
gl_FragColor = foo(uStruct);
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader);
// Initialize the texture with green.
GLTexture tex;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte texData[] = {0u, 255u, 0u, 255u};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Draw
glUseProgram(program);
GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMember");
ASSERT_NE(-1, samplerMemberLoc);
glUniform1i(samplerMemberLoc, 0);
GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord");
ASSERT_NE(-1, texCoordLoc);
glUniform2f(texCoordLoc, 0.5f, 0.5f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
}
// This test covers passing a struct containing a sampler as a function argument.
TEST_P(GLSLTest, StructsWithSamplersAsFunctionArgWithPrototype)
{
// Shader failed to compile on Android. http://anglebug.com/2114
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
const char kFragmentShader[] = R"(precision mediump float;
struct S { sampler2D samplerMember; };
uniform S uStruct;
uniform vec2 uTexCoord;
vec4 foo(S structVar);
vec4 foo(S structVar)
{
return texture2D(structVar.samplerMember, uTexCoord);
}
void main()
{
gl_FragColor = foo(uStruct);
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader);
// Initialize the texture with green.
GLTexture tex;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte texData[] = {0u, 255u, 0u, 255u};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Draw
glUseProgram(program);
GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMember");
ASSERT_NE(-1, samplerMemberLoc);
glUniform1i(samplerMemberLoc, 0);
GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord");
ASSERT_NE(-1, texCoordLoc);
glUniform2f(texCoordLoc, 0.5f, 0.5f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
}
// This test covers passing an array of structs containing samplers as a function argument.
TEST_P(GLSLTest, ArrayOfStructsWithSamplersAsFunctionArg)
{
// Shader failed to compile on Android. http://anglebug.com/2114
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
// TODO(jmadill): Samplers in structs. http://anglebug.com/2494
ANGLE_SKIP_TEST_IF(IsVulkan());
const std::string &fragmentShader =
"precision mediump float;\n"
"struct S\n"
......@@ -3284,8 +3373,8 @@ TEST_P(GLSLTest, StructWithSamplerArrayAsFunctionArg)
// Shader failed to compile on Android. http://anglebug.com/2114
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
// TODO(jmadill): Samplers in structs. http://anglebug.com/2494
ANGLE_SKIP_TEST_IF(IsVulkan());
// TODO(jmadill): Fix on Android if possible. http://anglebug.com/2703
ANGLE_SKIP_TEST_IF(IsAndroid() && IsVulkan());
const std::string &fragmentShader =
"precision mediump float;\n"
......@@ -3332,6 +3421,108 @@ TEST_P(GLSLTest, StructWithSamplerArrayAsFunctionArg)
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
}
// This test covers passing nested structs containing a sampler as a function argument.
TEST_P(GLSLTest, NestedStructsWithSamplersAsFunctionArg)
{
// Shader failed to compile on Android. http://anglebug.com/2114
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
const char kFragmentShader[] = R"(precision mediump float;
struct S { sampler2D samplerMember; };
struct T { S nest; };
uniform T uStruct;
uniform vec2 uTexCoord;
vec4 foo2(S structVar)
{
return texture2D(structVar.samplerMember, uTexCoord);
}
vec4 foo(T structVar)
{
return foo2(structVar.nest);
}
void main()
{
gl_FragColor = foo(uStruct);
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader);
// Initialize the texture with green.
GLTexture tex;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte texData[] = {0u, 255u, 0u, 255u};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Draw
glUseProgram(program);
GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.nest.samplerMember");
ASSERT_NE(-1, samplerMemberLoc);
glUniform1i(samplerMemberLoc, 0);
GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord");
ASSERT_NE(-1, texCoordLoc);
glUniform2f(texCoordLoc, 0.5f, 0.5f);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
}
// This test covers passing a compound structs containing a sampler as a function argument.
TEST_P(GLSLTest, CompoundStructsWithSamplersAsFunctionArg)
{
// Shader failed to compile on Android. http://anglebug.com/2114
ANGLE_SKIP_TEST_IF(IsAndroid() && IsAdreno() && IsOpenGLES());
const char kFragmentShader[] = R"(precision mediump float;
struct S { sampler2D samplerMember; bool b; };
uniform S uStruct;
uniform vec2 uTexCoord;
vec4 foo(S structVar)
{
if (structVar.b)
return texture2D(structVar.samplerMember, uTexCoord);
else
return vec4(1, 0, 0, 1);
}
void main()
{
gl_FragColor = foo(uStruct);
})";
ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), kFragmentShader);
// Initialize the texture with green.
GLTexture tex;
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, tex);
GLubyte texData[] = {0u, 255u, 0u, 255u};
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
ASSERT_GL_NO_ERROR();
// Draw
glUseProgram(program);
GLint samplerMemberLoc = glGetUniformLocation(program, "uStruct.samplerMember");
ASSERT_NE(-1, samplerMemberLoc);
glUniform1i(samplerMemberLoc, 0);
GLint texCoordLoc = glGetUniformLocation(program, "uTexCoord");
ASSERT_NE(-1, texCoordLoc);
glUniform2f(texCoordLoc, 0.5f, 0.5f);
GLint bLoc = glGetUniformLocation(program, "uStruct.b");
ASSERT_NE(-1, bLoc);
glUniform1i(bLoc, 1);
drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f);
ASSERT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(1, 1, GLColor::green);
}
// Test that a global variable declared after main() works. This is a regression test for an issue
// in global variable initialization.
TEST_P(WebGLGLSLTest, GlobalVariableDeclaredAfterMain)
......
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