Commit 3b0fcf6a by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: SPIR-V Gen: Support type casts in constructors

GLSL basic, vector and matrix constructors can convert between types. This was already done for constants used in constructors. This change implements the cast for non-constant expressions. Bug: angleproject:4889 Change-Id: I0a8c1a6e97ffced0d1652032a41fb87c70be16ca Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2999022Reviewed-by: 's avatarShahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
parent 0fa2e7f9
......@@ -362,6 +362,23 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const TSymbol *bl
spirv::LiteralInteger(0));
break;
case EbtBool:
// TODO: In SPIR-V, it's invalid to have a bool type in an interface block. An AST
// transformation should be written to rewrite the blocks to use a uint type with
// appropriate casts where used. Need to handle:
//
// - Store: cast the rhs of assignment
// - Non-array load: cast the expression
// - Array load (for example to use in a struct constructor): reconstruct the array
// with elements cast.
// - Pass to function as out parameter: Use
// MonomorphizeUnsupportedFunctionsInVulkanGLSL to avoid it, as there's no easy
// way to handle such function calls inside if conditions and such.
//
// It might be simplest to do this for bools in structs as well, to avoid having to
// convert between an old and new struct type if the struct is used both inside and
// outside an interface block.
//
// http://anglebug.com/4889.
spirv::WriteTypeBool(&mSpirvTypeAndConstantDecls, typeId);
break;
default:
......@@ -836,6 +853,41 @@ spirv::IdRef SPIRVBuilder::getFloatConstant(float value)
return getBasicConstantHelper(asUint.u, EbtFloat, &mFloatConstants);
}
spirv::IdRef SPIRVBuilder::getVectorConstantHelper(spirv::IdRef valueId, TBasicType type, int size)
{
if (size == 1)
{
return valueId;
}
SpirvType vecType;
vecType.type = type;
vecType.primarySize = static_cast<uint8_t>(size);
const spirv::IdRef typeId = getSpirvTypeData(vecType, nullptr).id;
const spirv::IdRefList valueIds(size, valueId);
return getCompositeConstant(typeId, valueIds);
}
spirv::IdRef SPIRVBuilder::getUvecConstant(uint32_t value, int size)
{
const spirv::IdRef valueId = getUintConstant(value);
return getVectorConstantHelper(valueId, EbtUInt, size);
}
spirv::IdRef SPIRVBuilder::getIvecConstant(int32_t value, int size)
{
const spirv::IdRef valueId = getIntConstant(value);
return getVectorConstantHelper(valueId, EbtInt, size);
}
spirv::IdRef SPIRVBuilder::getVecConstant(float value, int size)
{
const spirv::IdRef valueId = getFloatConstant(value);
return getVectorConstantHelper(valueId, EbtFloat, size);
}
spirv::IdRef SPIRVBuilder::getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values)
{
SpirvIdAndIdList key{typeId, values};
......
......@@ -323,6 +323,9 @@ class SPIRVBuilder : angle::NonCopyable
spirv::IdRef getUintConstant(uint32_t value);
spirv::IdRef getIntConstant(int32_t value);
spirv::IdRef getFloatConstant(float value);
spirv::IdRef getUvecConstant(uint32_t value, int size);
spirv::IdRef getIvecConstant(int32_t value, int size);
spirv::IdRef getVecConstant(float value, int size);
spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values);
// Helpers to start and end a function.
......@@ -381,6 +384,7 @@ class SPIRVBuilder : angle::NonCopyable
spirv::IdRef getBasicConstantHelper(uint32_t value,
TBasicType type,
angle::HashMap<uint32_t, spirv::IdRef> *constants);
spirv::IdRef getVectorConstantHelper(spirv::IdRef valueId, TBasicType type, int size);
uint32_t nextUnusedBinding();
uint32_t nextUnusedInputLocation(uint32_t consumedCount);
......
......@@ -216,9 +216,12 @@ class OutputSPIRVTraverser : public TIntermTraverser
spirv::IdRef createConstructorVectorFromScalar(const TType &type,
spirv::IdRef typeId,
const spirv::IdRefList &parameters);
spirv::IdRef createConstructorVectorFromNonScalar(TIntermAggregate *node,
spirv::IdRef typeId,
const spirv::IdRefList &parameters);
spirv::IdRef createConstructorVectorFromMatrix(TIntermAggregate *node,
spirv::IdRef typeId,
const spirv::IdRefList &parameters);
spirv::IdRef createConstructorVectorFromScalarsAndVectors(TIntermAggregate *node,
spirv::IdRef typeId,
const spirv::IdRefList &parameters);
spirv::IdRef createConstructorMatrixFromScalar(TIntermAggregate *node,
spirv::IdRef typeId,
const spirv::IdRefList &parameters);
......@@ -244,6 +247,21 @@ class OutputSPIRVTraverser : public TIntermTraverser
spirv::IdRef createFunctionCall(TIntermAggregate *node, spirv::IdRef resultTypeId);
// Cast between types. There are two kinds of casts:
//
// - A constructor can cast between basic types, for example vec4(someInt).
// - Assignments, constructors, function calls etc may copy an array or struct between different
// block storages or invariance (which due to their decorations generate different SPIR-V
// types). For example:
//
// layout(std140) uniform U { invariant Struct s; } u; ... Struct s2 = u.s;
//
// TODO: implement casts due to block storage and invariance differences.
// http://anglebug.com/4889
spirv::IdRef castBasicType(spirv::IdRef value,
const TType &valueType,
TBasicType expectedBasicType);
TCompiler *mCompiler;
ShCompileOptions mCompileOptions;
......@@ -1009,24 +1027,28 @@ spirv::IdRef OutputSPIRVTraverser::createConstructor(TIntermAggregate *node, spi
if (type.isScalar())
{
// TODO: handle casting. http://anglebug.com/4889.
return parameters[0];
return castBasicType(parameters[0], arg0Type, type.getBasicType());
}
if (type.isVector())
{
if (arguments.size() == 1 && arg0Type.isScalar())
{
parameters[0] = castBasicType(parameters[0], arg0Type, type.getBasicType());
return createConstructorVectorFromScalar(node->getType(), typeId, parameters);
}
return createConstructorVectorFromNonScalar(node, typeId, parameters);
if (arguments.size() == 1 && arg0Type.isMatrix())
{
return createConstructorVectorFromMatrix(node, typeId, parameters);
}
return createConstructorVectorFromScalarsAndVectors(node, typeId, parameters);
}
ASSERT(type.isMatrix());
if (arg0Type.isScalar())
{
parameters[0] = castBasicType(parameters[0], arg0Type, type.getBasicType());
return createConstructorMatrixFromScalar(node, typeId, parameters);
}
if (arg0Type.isMatrix())
......@@ -1062,16 +1084,51 @@ spirv::IdRef OutputSPIRVTraverser::createConstructorVectorFromScalar(
return result;
}
spirv::IdRef OutputSPIRVTraverser::createConstructorVectorFromNonScalar(
spirv::IdRef OutputSPIRVTraverser::createConstructorVectorFromMatrix(
TIntermAggregate *node,
spirv::IdRef typeId,
const spirv::IdRefList &parameters)
{
// vecN(v1.zy, v2.x) translates to OpCompositeConstruct %vecN %v1.z %v1.y %v2.x
// vecN(m) translates to OpCompositeConstruct %vecN %m[0][0] %m[0][1] ...
spirv::IdRefList extractedComponents;
extractComponents(node, node->getType().getNominalSize(), parameters, &extractedComponents);
// Construct the vector with the basic type of the argument, and cast it at end if needed.
ASSERT(parameters.size() == 1);
const TType &arg0Type = node->getChildNode(0)->getAsTyped()->getType();
const TBasicType expectedBasicType = node->getType().getBasicType();
spirv::IdRef argumentTypeId = typeId;
TType arg0TypeAsVector(arg0Type);
arg0TypeAsVector.setPrimarySize(static_cast<unsigned char>(node->getType().getNominalSize()));
arg0TypeAsVector.setSecondarySize(1);
if (arg0Type.getBasicType() != expectedBasicType)
{
argumentTypeId = mBuilder.getTypeData(arg0TypeAsVector, EbsUnspecified).id;
}
spirv::IdRef result = mBuilder.getNewId(mBuilder.getDecorations(node->getType()));
spirv::WriteCompositeConstruct(mBuilder.getSpirvCurrentFunctionBlock(), argumentTypeId, result,
extractedComponents);
if (arg0Type.getBasicType() != expectedBasicType)
{
result = castBasicType(result, arg0TypeAsVector, expectedBasicType);
}
return result;
}
spirv::IdRef OutputSPIRVTraverser::createConstructorVectorFromScalarsAndVectors(
TIntermAggregate *node,
spirv::IdRef typeId,
const spirv::IdRefList &parameters)
{
// vecN(v1.zy, v2.x) translates to OpCompositeConstruct %vecN %v1.z %v1.y %v2.x
spirv::IdRefList extractedComponents;
extractComponents(node, node->getType().getNominalSize(), parameters, &extractedComponents);
const spirv::IdRef result = mBuilder.getNewId(mBuilder.getDecorations(node->getType()));
spirv::WriteCompositeConstruct(mBuilder.getSpirvCurrentFunctionBlock(), typeId, result,
extractedComponents);
......@@ -1091,8 +1148,7 @@ spirv::IdRef OutputSPIRVTraverser::createConstructorMatrixFromScalar(
// ...
// %m = OpCompositeConstruct %matNxM %c0 %c1 %c2 ...
const TType &type = node->getType();
// TODO: handle casting. http://anglebug.com/4889.
const TType &type = node->getType();
const spirv::IdRef scalarId = parameters[0];
spirv::IdRef zeroId;
......@@ -1129,8 +1185,11 @@ spirv::IdRef OutputSPIRVTraverser::createConstructorMatrixFromScalar(
columnIds.push_back(mBuilder.getNewId(decorations));
// Place the scalar at the correct index (diagonal of the matrix, i.e. row == col).
componentIds[columnIndex] = scalarId;
if (columnIndex > 0)
if (columnIndex < type.getRows())
{
componentIds[columnIndex] = scalarId;
}
if (columnIndex > 0 && columnIndex <= type.getRows())
{
componentIds[columnIndex - 1] = zeroId;
}
......@@ -1220,8 +1279,6 @@ spirv::IdRef OutputSPIRVTraverser::createConstructorMatrixFromMatrix(
SpirvDecorations decorations = mBuilder.getDecorations(type);
// TODO: handle casting. http://anglebug.com/4889.
ASSERT(parameters.size() == 1);
spirv::IdRefList columnIds;
......@@ -1361,9 +1418,8 @@ void OutputSPIRVTraverser::extractComponents(TIntermAggregate *node,
// more components than necessary) and extracts the first componentCount components.
const TIntermSequence &arguments = *node->getSequence();
SpirvDecorations decorations = mBuilder.getDecorations(node->getType());
// TODO: handle casting. http://anglebug.com/4889.
const SpirvDecorations decorations = mBuilder.getDecorations(node->getType());
const TBasicType expectedBasicType = node->getType().getBasicType();
ASSERT(arguments.size() == parameters.size());
......@@ -1371,22 +1427,34 @@ void OutputSPIRVTraverser::extractComponents(TIntermAggregate *node,
argumentIndex < arguments.size() && extractedComponentsOut->size() < componentCount;
++argumentIndex)
{
const TType &argumentType = arguments[argumentIndex]->getAsTyped()->getType();
TIntermNode *argument = arguments[argumentIndex];
const TType &argumentType = argument->getAsTyped()->getType();
const spirv::IdRef parameterId = parameters[argumentIndex];
if (argumentType.isScalar())
{
// For scalar parameters, there's nothing to do.
extractedComponentsOut->push_back(parameterId);
// For scalar parameters, there's nothing to do other than a potential cast.
const spirv::IdRef castParameterId =
argument->getAsConstantUnion()
? parameterId
: castBasicType(parameterId, argumentType, expectedBasicType);
extractedComponentsOut->push_back(castParameterId);
continue;
}
if (argumentType.isVector())
{
SpirvType componentType = mBuilder.getSpirvType(argumentType, EbsUnspecified);
componentType.type = expectedBasicType;
componentType.primarySize = 1;
const spirv::IdRef componentTypeId =
mBuilder.getSpirvTypeData(componentType, nullptr).id;
// Cast the whole vector parameter in one go.
const spirv::IdRef castParameterId =
argument->getAsConstantUnion()
? parameterId
: castBasicType(parameterId, argumentType, expectedBasicType);
// For vector parameters, take components out of the vector one by one.
for (int componentIndex = 0; componentIndex < argumentType.getNominalSize() &&
extractedComponentsOut->size() < componentCount;
......@@ -1394,7 +1462,7 @@ void OutputSPIRVTraverser::extractComponents(TIntermAggregate *node,
{
const spirv::IdRef componentId = mBuilder.getNewId(decorations);
spirv::WriteCompositeExtract(mBuilder.getSpirvCurrentFunctionBlock(),
componentTypeId, componentId, parameterId,
componentTypeId, componentId, castParameterId,
{spirv::LiteralInteger(componentIndex)});
extractedComponentsOut->push_back(componentId);
......@@ -1410,7 +1478,8 @@ void OutputSPIRVTraverser::extractComponents(TIntermAggregate *node,
const spirv::IdRef componentTypeId = mBuilder.getSpirvTypeData(componentType, nullptr).id;
// For matrix parameters, take components out of the matrix one by one in column-major
// order.
// order. No cast is done here; it would only be required for vector constructors with
// matrix parameters, in which case the resulting vector is cast in the end.
for (int columnIndex = 0; columnIndex < argumentType.getCols() &&
extractedComponentsOut->size() < componentCount;
++columnIndex)
......@@ -1547,20 +1616,17 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
spirv::IdRef paramValue;
SpirvDecorations decorations = mBuilder.getDecorations(paramType);
if (IsOpaqueType(paramType.getBasicType()) || paramQualifier == EvqConst)
if (paramQualifier == EvqConst)
{
// The following parameters are passed as rvalue:
//
// - Opaque uniforms,
// - const parameters,
// |const| parameters are passed as rvalue.
paramValue = accessChainLoad(&param, decorations);
}
else if (IsAccessChainUnindexedLValue(param) &&
(mCompileOptions & SH_GENERATE_SPIRV_WORKAROUNDS) == 0)
(IsOpaqueType(paramType.getBasicType()) ||
(param.accessChain.storageClass == spv::StorageClassFunction &&
(mCompileOptions & SH_GENERATE_SPIRV_WORKAROUNDS) == 0)))
{
// The following parameters are passed directly:
//
// - unindexed lvalues.
// Unindexed lvalues are passed directly.
//
// This optimization is not applied on buggy drivers. http://anglebug.com/6110.
paramValue = param.baseId;
......@@ -3355,6 +3421,122 @@ spirv::IdRef OutputSPIRVTraverser::createImageTextureBuiltIn(TIntermOperator *no
return result;
}
spirv::IdRef OutputSPIRVTraverser::castBasicType(spirv::IdRef value,
const TType &valueType,
TBasicType expectedBasicType)
{
if (valueType.getBasicType() == expectedBasicType)
{
return value;
}
SpirvType valueSpirvType = mBuilder.getSpirvType(valueType, EbsUnspecified);
valueSpirvType.type = expectedBasicType;
const spirv::IdRef castTypeId = mBuilder.getSpirvTypeData(valueSpirvType, nullptr).id;
const spirv::IdRef castValue = mBuilder.getNewId(mBuilder.getDecorations(valueType));
// Write the instruction that casts between types. Different instructions are used based on the
// types being converted.
//
// - int/uint <-> float: OpConvert*To*
// - int <-> uint: OpBitcast
// - bool --> int/uint/float: OpSelect with 0 and 1
// - int/uint --> bool: OPINotEqual 0
// - float --> bool: OpFUnordNotEqual 0
WriteUnaryOp writeUnaryOp = nullptr;
WriteBinaryOp writeBinaryOp = nullptr;
WriteTernaryOp writeTernaryOp = nullptr;
spirv::IdRef zero;
spirv::IdRef one;
switch (valueType.getBasicType())
{
case EbtFloat:
switch (expectedBasicType)
{
case EbtInt:
writeUnaryOp = spirv::WriteConvertFToS;
break;
case EbtUInt:
writeUnaryOp = spirv::WriteConvertFToU;
break;
case EbtBool:
zero = mBuilder.getVecConstant(0, valueType.getNominalSize());
writeBinaryOp = spirv::WriteFUnordNotEqual;
break;
default:
UNREACHABLE();
}
break;
case EbtInt:
case EbtUInt:
switch (expectedBasicType)
{
case EbtFloat:
writeUnaryOp = valueType.getBasicType() == EbtInt ? spirv::WriteConvertSToF
: spirv::WriteConvertUToF;
break;
case EbtInt:
case EbtUInt:
writeUnaryOp = spirv::WriteBitcast;
break;
case EbtBool:
zero = mBuilder.getUvecConstant(0, valueType.getNominalSize());
writeBinaryOp = spirv::WriteINotEqual;
break;
default:
UNREACHABLE();
}
break;
case EbtBool:
writeTernaryOp = spirv::WriteSelect;
switch (expectedBasicType)
{
case EbtFloat:
zero = mBuilder.getVecConstant(0, valueType.getNominalSize());
one = mBuilder.getVecConstant(1, valueType.getNominalSize());
break;
case EbtInt:
zero = mBuilder.getIvecConstant(0, valueType.getNominalSize());
one = mBuilder.getIvecConstant(1, valueType.getNominalSize());
break;
case EbtUInt:
zero = mBuilder.getUvecConstant(0, valueType.getNominalSize());
one = mBuilder.getUvecConstant(1, valueType.getNominalSize());
break;
default:
UNREACHABLE();
}
break;
default:
// TODO: support desktop GLSL. http://anglebug.com/4889.
UNIMPLEMENTED();
}
if (writeUnaryOp)
{
writeUnaryOp(mBuilder.getSpirvCurrentFunctionBlock(), castTypeId, castValue, value);
}
else if (writeBinaryOp)
{
writeBinaryOp(mBuilder.getSpirvCurrentFunctionBlock(), castTypeId, castValue, value, zero);
}
else
{
ASSERT(writeTernaryOp);
writeTernaryOp(mBuilder.getSpirvCurrentFunctionBlock(), castTypeId, castValue, value, one,
zero);
}
return castValue;
}
void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node)
{
// Constants are expected to be folded.
......@@ -4225,7 +4407,10 @@ void OutputSPIRVTraverser::visitFunctionPrototype(TIntermFunctionPrototype *node
// with the Function storage class.
if (paramType.getQualifier() != EvqConst)
{
paramId = mBuilder.getTypePointerId(paramId, spv::StorageClassFunction);
const spv::StorageClass storageClass = IsOpaqueType(paramType.getBasicType())
? spv::StorageClassUniformConstant
: spv::StorageClassFunction;
paramId = mBuilder.getTypePointerId(paramId, storageClass);
}
ids.parameterTypeIds.push_back(paramId);
......
......@@ -3376,7 +3376,9 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FramebufferTest_ES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(FramebufferTest_ES3,
WithEmulatedPrerotation(ES3_VULKAN(), 90),
WithEmulatedPrerotation(ES3_VULKAN(), 180),
WithEmulatedPrerotation(ES3_VULKAN(), 270));
WithEmulatedPrerotation(ES3_VULKAN(), 270),
WithDirectSPIRVGeneration(WithEmulatedPrerotation(ES3_VULKAN(),
90)));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(FramebufferTest_ES31);
ANGLE_INSTANTIATE_TEST_ES31(FramebufferTest_ES31);
......@@ -2585,6 +2585,83 @@ TEST_P(GLSLTest_ES3, AmbiguousFunctionCall2x2)
EXPECT_NE(0u, program);
}
// Test that constructing matrices from non-float types works.
TEST_P(GLSLTest_ES3, ConstructMatrixFromNonFloat)
{
constexpr char kFS[] = R"(#version 300 es
precision highp float;
out vec4 color;
uniform int i;
uniform uint u;
void main()
{
bool b = i > 10;
mat3x2 mi = mat3x2(i);
mat4 mu = mat4(u);
mat2x4 mb = mat2x4(b);
mat3x2 m = mat3x2(ivec2(i), uvec2(u), bvec2(b));
color = vec4(mi[0][0] == float(i) ? 1 : 0,
mu[2][2] == float(u) ? 1 : 0,
mb[1][1] == float(b) ? 1 : 0,
m[0][1] == float(i) && m[1][0] == float(u) && m[2][0] == float(b) ? 1 : 0);
})";
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint iloc = glGetUniformLocation(program, "i");
GLint uloc = glGetUniformLocation(program, "u");
ASSERT_NE(iloc, -1);
ASSERT_NE(uloc, -1);
glUniform1i(iloc, -123);
glUniform1ui(uloc, 456);
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
}
// Test that constructing non-float vectors from matrix types works.
TEST_P(GLSLTest_ES3, ConstructNonFloatVectorFromMatrix)
{
constexpr char kFS[] = R"(#version 300 es
precision highp float;
out vec4 color;
uniform float f;
void main()
{
mat4 m = mat4(f);
ivec3 vi = ivec3(m);
uvec2 vu = uvec2(m);
bvec4 vb = bvec4(m);
color = vec4(vi.x == int(f) ? 1 : 0,
vu.x == uint(f) ? 1 : 0,
vb.x == bool(f) ? 1 : 0,
1);
})";
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), kFS);
glUseProgram(program);
GLint floc = glGetUniformLocation(program, "f");
ASSERT_NE(floc, -1);
glUniform1f(floc, 123);
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::white);
}
// Test that an user-defined function with a large number of float4 parameters doesn't fail due to
// the function name being too long.
TEST_P(GLSLTest_ES3, LargeNumberOfFloat4Parameters)
......
......@@ -9468,7 +9468,8 @@ ANGLE_INSTANTIATE_TEST_ES2(TextureCubeTest);
ANGLE_INSTANTIATE_TEST_ES2(Texture2DTestWithDrawScale);
ANGLE_INSTANTIATE_TEST_ES2(Sampler2DAsFunctionParameterTest);
ANGLE_INSTANTIATE_TEST_ES2(SamplerArrayTest);
ANGLE_INSTANTIATE_TEST_ES2(SamplerArrayAsFunctionParameterTest);
ANGLE_INSTANTIATE_TEST_ES2_AND(SamplerArrayAsFunctionParameterTest,
WithDirectSPIRVGeneration(ES2_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DTestES3, WithAllocateNonZeroMemory(ES3_VULKAN()));
......@@ -9501,7 +9502,8 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DArrayTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DArrayTestES3, WithDirectSPIRVGeneration(ES3_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TextureSizeTextureArrayTest);
ANGLE_INSTANTIATE_TEST_ES3(TextureSizeTextureArrayTest);
ANGLE_INSTANTIATE_TEST_ES3_AND(TextureSizeTextureArrayTest,
WithDirectSPIRVGeneration(ES3_VULKAN()));
ANGLE_INSTANTIATE_TEST_ES2(SamplerInStructTest);
ANGLE_INSTANTIATE_TEST_ES2(SamplerInStructAsFunctionParameterTest);
......@@ -9546,10 +9548,12 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TextureCubeIntegerEdgeTestES3);
ANGLE_INSTANTIATE_TEST_ES3(TextureCubeIntegerEdgeTestES3);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DIntegerProjectiveOffsetTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DIntegerProjectiveOffsetTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DIntegerProjectiveOffsetTestES3,
WithDirectSPIRVGeneration(ES3_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture2DArrayIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture2DArrayIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3_AND(Texture2DArrayIntegerTestES3,
WithDirectSPIRVGeneration(ES3_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Texture3DIntegerTestES3);
ANGLE_INSTANTIATE_TEST_ES3(Texture3DIntegerTestES3);
......
......@@ -168,6 +168,11 @@ struct CombinedPrintToStringParamName
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
#define ANGLE_INSTANTIATE_TEST_ES2_AND(testName, ...) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES2, __VA_ARGS__}; \
INSTANTIATE_TEST_SUITE_P(, testName, ANGLE_INSTANTIATE_TEST_PLATFORMS(testName), \
testing::PrintToStringParamName())
// Instantiate the test once for each GLES3 platform
#define ANGLE_INSTANTIATE_TEST_ES3(testName) \
const PlatformParameters testName##params[] = {ANGLE_ALL_TEST_PLATFORMS_ES3}; \
......
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