Vulkan: SPIR-V Gen: Assignment between mismatching SPIR-V types

In general, GLSL qualifiers translate to SPIR-V decorations on SPIR-V
variables.  In the case of blocks (struct or interface block),
OpMemberDecorate is used, which due to its specification in SPIR-V, can
only apply decorations to direct members of a block.  This makes it
impossible for example to decorate a nested member of a block through
its variable id.

As such, some decorations such as RowMajor and Invariant apply to
members of a block given its _type_ id.  Unfortunately SPIR-V requires
ArrayStride to also be applied to a type directly, rather than a member
of a block.  This implies that some types, such as structs used in
uniform/buffer interface block, or decorated with invariant or
row_major, as well as arrays (of any type) used inside and outside
interface blocks to produce different SPIR-V types from the same GLSL
type.  The SpirvTypeSpec data previously introduced specialize these
types.

It's necessary to "cast" between these types when needed.  The
translator handles casts at load/store boundary:

- Upon load, the value is cast to the type with the default
  SpirvTypeSpec.
- Upon store, the value is cast from the default to the store target
  SpirvTypeSpec.
- All intermediate results use the default SpirvTypeSpec.

Bug: angleproject:4889
Change-Id: I6fa28e518ec6b517ff163f44b6892859eb4b10fd
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3026145
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
This commit is contained in:
Shahbaz Youssefi 2021-07-13 14:10:41 -04:00 коммит произвёл Angle LUCI CQ
Родитель 5e579a1879
Коммит 9c6fb52c74
4 изменённых файлов: 515 добавлений и 76 удалений

Просмотреть файл

@ -1541,9 +1541,6 @@ void SPIRVBuilder::writeInterfaceVariableDecorations(const TType &type, spirv::I
const bool needsBlendIndex = const bool needsBlendIndex =
type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0; type.getQualifier() == EvqFragmentOut && layoutQualifier.index >= 0;
// TODO: handle row-major matrixes. http://anglebug.com/4889.
// TODO: handle invariant (spv::DecorationInvariant).
// If the resource declaration requires set & binding, add the DescriptorSet and Binding // If the resource declaration requires set & binding, add the DescriptorSet and Binding
// decorations. // decorations.
if (needsSetBinding) if (needsSetBinding)

Просмотреть файл

@ -194,8 +194,8 @@ class OutputSPIRVTraverser : public TIntermTraverser
uint8_t componentCount) const; uint8_t componentCount) const;
void accessChainPushDynamicComponent(NodeData *data, spirv::IdRef index, spirv::IdRef typeId); void accessChainPushDynamicComponent(NodeData *data, spirv::IdRef index, spirv::IdRef typeId);
spirv::IdRef accessChainCollapse(NodeData *data); spirv::IdRef accessChainCollapse(NodeData *data);
spirv::IdRef accessChainLoad(NodeData *data, const SpirvDecorations &decorations); spirv::IdRef accessChainLoad(NodeData *data, const TType &valueType);
void accessChainStore(NodeData *data, spirv::IdRef value); void accessChainStore(NodeData *data, spirv::IdRef value, const TType &valueType);
// Access chain helpers. // Access chain helpers.
void makeAccessChainIdList(NodeData *data, spirv::IdRefList *idsOut); void makeAccessChainIdList(NodeData *data, spirv::IdRefList *idsOut);
@ -261,16 +261,18 @@ class OutputSPIRVTraverser : public TIntermTraverser
// //
// - A constructor can cast between basic types, for example vec4(someInt). // - A constructor can cast between basic types, for example vec4(someInt).
// - Assignments, constructors, function calls etc may copy an array or struct between different // - 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 // block storages, invariance etc (which due to their decorations generate different SPIR-V
// types). For example: // types). For example:
// //
// layout(std140) uniform U { invariant Struct s; } u; ... Struct s2 = u.s; // 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, spirv::IdRef castBasicType(spirv::IdRef value,
const TType &valueType, const TType &valueType,
TBasicType expectedBasicType); TBasicType expectedBasicType);
spirv::IdRef cast(spirv::IdRef value,
const TType &valueType,
const SpirvTypeSpec &valueTypeSpec,
const SpirvTypeSpec &expectedTypeSpec);
// Helper to reduce vector == and != with OpAll and OpAny respectively. If multiple ids are // Helper to reduce vector == and != with OpAll and OpAny respectively. If multiple ids are
// given, either OpLogicalAnd or OpLogicalOr is used (if two operands) or a bool vector is // given, either OpLogicalAnd or OpLogicalOr is used (if two operands) or a bool vector is
@ -686,9 +688,10 @@ spirv::IdRef OutputSPIRVTraverser::accessChainCollapse(NodeData *data)
return accessChain.accessChainId; return accessChain.accessChainId;
} }
spirv::IdRef OutputSPIRVTraverser::accessChainLoad(NodeData *data, spirv::IdRef OutputSPIRVTraverser::accessChainLoad(NodeData *data, const TType &valueType)
const SpirvDecorations &decorations)
{ {
const SpirvDecorations &decorations = mBuilder.getDecorations(valueType);
// Loading through the access chain can generate different instructions based on whether it's an // Loading through the access chain can generate different instructions based on whether it's an
// rvalue, the indices are literal, there's a swizzle etc. // rvalue, the indices are literal, there's a swizzle etc.
// //
@ -788,10 +791,15 @@ spirv::IdRef OutputSPIRVTraverser::accessChainLoad(NodeData *data,
loadResult = result; loadResult = result;
} }
return loadResult; // Upon loading values, cast them to the default SPIR-V variant.
const spirv::IdRef castResult = cast(loadResult, valueType, accessChain.typeSpec, {});
return castResult;
} }
void OutputSPIRVTraverser::accessChainStore(NodeData *data, spirv::IdRef value) void OutputSPIRVTraverser::accessChainStore(NodeData *data,
spirv::IdRef value,
const TType &valueType)
{ {
// Storing through the access chain can generate different instructions based on whether the // Storing through the access chain can generate different instructions based on whether the
// there's a swizzle. // there's a swizzle.
@ -853,8 +861,12 @@ void OutputSPIRVTraverser::accessChainStore(NodeData *data, spirv::IdRef value)
value = result; value = result;
} }
// Store through the access chain. // Store through the access chain. The values are always cast to the default SPIR-V type
spirv::WriteStore(mBuilder.getSpirvCurrentFunctionBlock(), accessChainId, value, nullptr); // variant when loaded from memory and operated on as such. When storing, we need to cast the
// result to the variant specified by the access chain.
const spirv::IdRef castValue = cast(value, valueType, {}, accessChain.typeSpec);
spirv::WriteStore(mBuilder.getSpirvCurrentFunctionBlock(), accessChainId, castValue, nullptr);
} }
void OutputSPIRVTraverser::makeAccessChainIdList(NodeData *data, spirv::IdRefList *idsOut) void OutputSPIRVTraverser::makeAccessChainIdList(NodeData *data, spirv::IdRefList *idsOut)
@ -1478,11 +1490,8 @@ spirv::IdRefList OutputSPIRVTraverser::loadAllParams(TIntermOperator *node)
// Take each parameter that is visited and evaluate it as rvalue // Take each parameter that is visited and evaluate it as rvalue
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex]; NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
const spirv::IdRef paramValue = accessChainLoad( const spirv::IdRef paramValue =
&param, accessChainLoad(&param, node->getChildNode(paramIndex)->getAsTyped()->getType());
mBuilder.getDecorations(node->getChildNode(paramIndex)->getAsTyped()->getType()));
// TODO: handle mismatching types. http://anglebug.com/6000
parameters.push_back(paramValue); parameters.push_back(paramValue);
} }
@ -1593,8 +1602,7 @@ void OutputSPIRVTraverser::startShortCircuit(TIntermBinary *node)
// Load |left| and replace the access chain with an rvalue that's the result. // Load |left| and replace the access chain with an rvalue that's the result.
const spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back()); const spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back());
const spirv::IdRef left = const spirv::IdRef left = accessChainLoad(&mNodeData.back(), node->getLeft()->getType());
accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(node->getLeft()->getType()));
nodeDataInitRValue(&mNodeData.back(), left, typeId); nodeDataInitRValue(&mNodeData.back(), left, typeId);
// Keep the id of the block |left| was evaluated in. // Keep the id of the block |left| was evaluated in.
@ -1619,8 +1627,7 @@ void OutputSPIRVTraverser::startShortCircuit(TIntermBinary *node)
spirv::IdRef OutputSPIRVTraverser::endShortCircuit(TIntermBinary *node, spirv::IdRef *typeId) spirv::IdRef OutputSPIRVTraverser::endShortCircuit(TIntermBinary *node, spirv::IdRef *typeId)
{ {
// Load the right hand side. // Load the right hand side.
const spirv::IdRef right = const spirv::IdRef right = accessChainLoad(&mNodeData.back(), node->getRight()->getType());
accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(node->getRight()->getType()));
mNodeData.pop_back(); mNodeData.pop_back();
// Get the id of the block |right| is evaluated in. // Get the id of the block |right| is evaluated in.
@ -1695,12 +1702,11 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex]; NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
spirv::IdRef paramValue; spirv::IdRef paramValue;
SpirvDecorations decorations = mBuilder.getDecorations(paramType);
if (paramQualifier == EvqConst) if (paramQualifier == EvqConst)
{ {
// |const| parameters are passed as rvalue. // |const| parameters are passed as rvalue.
paramValue = accessChainLoad(&param, decorations); paramValue = accessChainLoad(&param, paramType);
} }
else if (IsOpaqueType(paramType.getBasicType())) else if (IsOpaqueType(paramType.getBasicType()))
{ {
@ -1725,15 +1731,13 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
tempVarTypeIds[paramIndex] = mBuilder.getTypeData(paramType, {}).id; tempVarTypeIds[paramIndex] = mBuilder.getTypeData(paramType, {}).id;
tempVarIds[paramIndex] = tempVarIds[paramIndex] =
mBuilder.declareVariable(tempVarTypeIds[paramIndex], spv::StorageClassFunction, mBuilder.declareVariable(tempVarTypeIds[paramIndex], spv::StorageClassFunction,
decorations, nullptr, "param"); mBuilder.getDecorations(paramType), nullptr, "param");
// If it's an in or inout parameter, the temp variable needs to be initialized with the // If it's an in or inout parameter, the temp variable needs to be initialized with the
// value of the parameter first. // value of the parameter first.
//
// TODO: handle mismatching types. http://anglebug.com/6000
if (paramQualifier == EvqIn || paramQualifier == EvqInOut) if (paramQualifier == EvqIn || paramQualifier == EvqInOut)
{ {
paramValue = accessChainLoad(&param, decorations); paramValue = accessChainLoad(&param, paramType);
spirv::WriteStore(mBuilder.getSpirvCurrentFunctionBlock(), tempVarIds[paramIndex], spirv::WriteStore(mBuilder.getSpirvCurrentFunctionBlock(), tempVarIds[paramIndex],
paramValue, nullptr); paramValue, nullptr);
} }
@ -1767,14 +1771,11 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
} }
// Copy from the temp variable to the parameter. // Copy from the temp variable to the parameter.
//
// TODO: handle mismatching types. http://anglebug.com/6000
NodeData tempVarData; NodeData tempVarData;
nodeDataInitLValue(&tempVarData, tempVarIds[paramIndex], tempVarTypeIds[paramIndex], nodeDataInitLValue(&tempVarData, tempVarIds[paramIndex], tempVarTypeIds[paramIndex],
spv::StorageClassFunction, {}); spv::StorageClassFunction, {});
const spirv::IdRef tempVarValue = const spirv::IdRef tempVarValue = accessChainLoad(&tempVarData, paramType);
accessChainLoad(&tempVarData, mBuilder.getDecorations(paramType)); accessChainStore(&param, tempVarValue, function->getParam(paramIndex)->getType());
accessChainStore(&param, tempVarValue);
} }
return result; return result;
@ -1888,7 +1889,7 @@ spirv::IdRef OutputSPIRVTraverser::visitOperator(TIntermOperator *node, spirv::I
break; break;
case EOpPositive: case EOpPositive:
// This is a noop. // This is a noop.
return accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(firstOperandType)); return accessChainLoad(&mNodeData.back(), firstOperandType);
case EOpLogicalNot: case EOpLogicalNot:
case EOpNotComponentWise: case EOpNotComponentWise:
@ -2571,7 +2572,7 @@ spirv::IdRef OutputSPIRVTraverser::visitOperator(TIntermOperator *node, spirv::I
{ {
ASSERT(mNodeData.size() >= 2); ASSERT(mNodeData.size() >= 2);
ASSERT(parameters.size() == 2); ASSERT(parameters.size() == 2);
accessChainStore(&mNodeData[mNodeData.size() - 2], result); accessChainStore(&mNodeData[mNodeData.size() - 2], result, firstOperandType);
} }
return result; return result;
@ -2610,8 +2611,7 @@ spirv::IdRef OutputSPIRVTraverser::createIncrementDecrement(TIntermOperator *nod
} }
// Load the operand. // Load the operand.
spirv::IdRef value = spirv::IdRef value = accessChainLoad(&mNodeData.back(), operandType);
accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(operand->getType()));
spirv::IdRef result = mBuilder.getNewId(mBuilder.getDecorations(operandType)); spirv::IdRef result = mBuilder.getNewId(mBuilder.getDecorations(operandType));
const spirv::IdRef one = isFloat ? mBuilder.getFloatConstant(1) : mBuilder.getIntConstant(1); const spirv::IdRef one = isFloat ? mBuilder.getFloatConstant(1) : mBuilder.getIntConstant(1);
@ -2619,7 +2619,7 @@ spirv::IdRef OutputSPIRVTraverser::createIncrementDecrement(TIntermOperator *nod
writeBinaryOp(mBuilder.getSpirvCurrentFunctionBlock(), resultTypeId, result, value, one); writeBinaryOp(mBuilder.getSpirvCurrentFunctionBlock(), resultTypeId, result, value, one);
// The result is always written back. // The result is always written back.
accessChainStore(&mNodeData.back(), result); accessChainStore(&mNodeData.back(), result, operandType);
// Initialize the access chain with either the result or the value based on whether pre or // Initialize the access chain with either the result or the value based on whether pre or
// post increment/decrement was used. The result is always an rvalue. // post increment/decrement was used. The result is always an rvalue.
@ -2747,10 +2747,9 @@ spirv::IdRef OutputSPIRVTraverser::createAtomicBuiltIn(TIntermOperator *node,
pointerId = accessChainCollapse(&mNodeData[mNodeData.size() - parameterCount]); pointerId = accessChainCollapse(&mNodeData[mNodeData.size() - parameterCount]);
for (size_t paramIndex = 1; paramIndex < parameterCount; ++paramIndex) for (size_t paramIndex = 1; paramIndex < parameterCount; ++paramIndex)
{ {
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex]; NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
const spirv::IdRef parameter = accessChainLoad( const spirv::IdRef parameter =
&param, accessChainLoad(&param, node->getChildNode(paramIndex)->getAsTyped()->getType());
mBuilder.getDecorations(node->getChildNode(paramIndex)->getAsTyped()->getType()));
// imageAtomic* built-ins have a few additional parameters right after the image. These are // imageAtomic* built-ins have a few additional parameters right after the image. These are
// kept separately for use with OpImageTexelPointer. // kept separately for use with OpImageTexelPointer.
@ -3736,6 +3735,111 @@ spirv::IdRef OutputSPIRVTraverser::castBasicType(spirv::IdRef value,
return castValue; return castValue;
} }
spirv::IdRef OutputSPIRVTraverser::cast(spirv::IdRef value,
const TType &valueType,
const SpirvTypeSpec &valueTypeSpec,
const SpirvTypeSpec &expectedTypeSpec)
{
// If there's no difference in type specialization, there's nothing to cast.
if (valueTypeSpec.blockStorage == expectedTypeSpec.blockStorage &&
valueTypeSpec.isInvariantBlock == expectedTypeSpec.isInvariantBlock &&
valueTypeSpec.isRowMajorQualifiedBlock == expectedTypeSpec.isRowMajorQualifiedBlock &&
valueTypeSpec.isRowMajorQualifiedArray == expectedTypeSpec.isRowMajorQualifiedArray)
{
return value;
}
// At this point, a value is loaded with the |valueType| GLSL type which is of a SPIR-V type
// specialized by |valueTypeSpec|. However, it's being assigned (for example through operator=,
// used in a constructor or passed as a function argument) where the same GLSL type is expected
// but with different SPIR-V type specialization (|expectedTypeSpec|). SPIR-V 1.4 has
// OpCopyLogical that does exactly that, but we generate SPIR-V 1.0 at the moment.
//
// The following code recursively copies the array elements or struct fields and then constructs
// the final result with the expected SPIR-V type.
// Interface blocks cannot be copied or passed as parameters in GLSL.
ASSERT(!valueType.isInterfaceBlock());
spirv::IdRefList constituents;
if (valueType.isArray())
{
// Find the SPIR-V type specialization for the element type.
SpirvTypeSpec valueElementTypeSpec = valueTypeSpec;
SpirvTypeSpec expectedElementTypeSpec = expectedTypeSpec;
const bool isElementBlock = valueType.getStruct() != nullptr;
const bool isElementArray = valueType.isArrayOfArrays();
valueElementTypeSpec.onArrayElementSelection(isElementBlock, isElementArray);
expectedElementTypeSpec.onArrayElementSelection(isElementBlock, isElementArray);
// Get the element type id.
TType elementType(valueType);
elementType.toArrayElementType();
const spirv::IdRef elementTypeId =
mBuilder.getTypeData(elementType, valueElementTypeSpec).id;
const SpirvDecorations elementDecorations = mBuilder.getDecorations(elementType);
// Extract each element of the array and cast it to the expected type.
for (unsigned int elementIndex = 0; elementIndex < valueType.getOutermostArraySize();
++elementIndex)
{
const spirv::IdRef elementId = mBuilder.getNewId(elementDecorations);
spirv::WriteCompositeExtract(mBuilder.getSpirvCurrentFunctionBlock(), elementTypeId,
elementId, value, {spirv::LiteralInteger(elementIndex)});
constituents.push_back(
cast(elementId, elementType, valueElementTypeSpec, expectedElementTypeSpec));
}
}
else if (valueType.getStruct() != nullptr)
{
uint32_t fieldIndex = 0;
// Extract each field of the struct and cast it to the expected type.
for (const TField *field : valueType.getStruct()->fields())
{
const TType &fieldType = *field->type();
// Find the SPIR-V type specialization for the field type.
SpirvTypeSpec valueFieldTypeSpec = valueTypeSpec;
SpirvTypeSpec expectedFieldTypeSpec = expectedTypeSpec;
valueFieldTypeSpec.onBlockFieldSelection(fieldType);
expectedFieldTypeSpec.onBlockFieldSelection(fieldType);
// Get the field type id.
const spirv::IdRef fieldTypeId = mBuilder.getTypeData(fieldType, valueFieldTypeSpec).id;
// Extract the field.
const spirv::IdRef fieldId = mBuilder.getNewId(mBuilder.getDecorations(fieldType));
spirv::WriteCompositeExtract(mBuilder.getSpirvCurrentFunctionBlock(), fieldTypeId,
fieldId, value, {spirv::LiteralInteger(fieldIndex++)});
constituents.push_back(
cast(fieldId, fieldType, valueFieldTypeSpec, expectedFieldTypeSpec));
}
}
else
{
// TODO: support bool in interface blocks. http://anglebug.com/4889.
UNREACHABLE();
}
// Construct the value with the expected type from its cast constituents.
const spirv::IdRef expectedTypeId = mBuilder.getTypeData(valueType, expectedTypeSpec).id;
const spirv::IdRef expectedId = mBuilder.getNewId(mBuilder.getDecorations(valueType));
spirv::WriteCompositeConstruct(mBuilder.getSpirvCurrentFunctionBlock(), expectedTypeId,
expectedId, constituents);
return expectedId;
}
spirv::IdRef OutputSPIRVTraverser::reduceBoolVector(TOperator op, spirv::IdRef OutputSPIRVTraverser::reduceBoolVector(TOperator op,
const spirv::IdRefList &valueIds, const spirv::IdRefList &valueIds,
spirv::IdRef typeId, spirv::IdRef typeId,
@ -4161,8 +4265,8 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node)
case EOpIndexIndirect: case EOpIndexIndirect:
{ {
// Load the index. // Load the index.
const spirv::IdRef rightValue = accessChainLoad( const spirv::IdRef rightValue =
&mNodeData.back(), mBuilder.getDecorations(node->getRight()->getType())); accessChainLoad(&mNodeData.back(), node->getRight()->getType());
mNodeData.pop_back(); mNodeData.pop_back();
if (!node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector()) if (!node->getLeft()->getType().isArray() && node->getLeft()->getType().isVector())
@ -4179,14 +4283,13 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node)
case EOpAssign: case EOpAssign:
{ {
// Load the right hand side of assignment. // Load the right hand side of assignment.
const spirv::IdRef rightValue = accessChainLoad( const spirv::IdRef rightValue =
&mNodeData.back(), mBuilder.getDecorations(node->getRight()->getType())); accessChainLoad(&mNodeData.back(), node->getRight()->getType());
mNodeData.pop_back(); mNodeData.pop_back();
// Store into the access chain. Since the result of the (a = b) expression is b, change // Store into the access chain. Since the result of the (a = b) expression is b, change
// the access chain to an unindexed rvalue which is |rightValue|. // the access chain to an unindexed rvalue which is |rightValue|.
// TODO: handle mismatching types. http://anglebug.com/4889. accessChainStore(&mNodeData.back(), rightValue, node->getLeft()->getType());
accessChainStore(&mNodeData.back(), rightValue);
nodeDataInitRValue(&mNodeData.back(), rightValue, resultTypeId); nodeDataInitRValue(&mNodeData.back(), rightValue, resultTypeId);
break; break;
} }
@ -4287,9 +4390,9 @@ bool OutputSPIRVTraverser::visitTernary(Visit visit, TIntermTernary *node)
if (lastChildIndex == 0) if (lastChildIndex == 0)
{ {
spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back()); spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back());
spirv::IdRef conditionValue = accessChainLoad( spirv::IdRef conditionValue =
&mNodeData.back(), mBuilder.getDecorations(node->getCondition()->getType())); accessChainLoad(&mNodeData.back(), node->getCondition()->getType());
// If OpSelect can be used, keep the condition for later usage. // If OpSelect can be used, keep the condition for later usage.
if (canUseOpSelect) if (canUseOpSelect)
@ -4326,9 +4429,8 @@ bool OutputSPIRVTraverser::visitTernary(Visit visit, TIntermTernary *node)
// Load the result of the true or false part, and keep it for the end. It's either used in // Load the result of the true or false part, and keep it for the end. It's either used in
// OpSelect or OpPhi. // OpSelect or OpPhi.
// TODO: handle mismatching types. http://anglebug.com/4889.
const spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back()); const spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back());
const spirv::IdRef value = accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(type)); const spirv::IdRef value = accessChainLoad(&mNodeData.back(), type);
mNodeData.pop_back(); mNodeData.pop_back();
mNodeData.back().idList.push_back(value); mNodeData.back().idList.push_back(value);
@ -4388,8 +4490,8 @@ bool OutputSPIRVTraverser::visitIfElse(Visit visit, TIntermIfElse *node)
// If the condition was just visited, evaluate it and create the branch instructions. // If the condition was just visited, evaluate it and create the branch instructions.
if (lastChildIndex == 0) if (lastChildIndex == 0)
{ {
const spirv::IdRef conditionValue = accessChainLoad( const spirv::IdRef conditionValue =
&mNodeData.back(), mBuilder.getDecorations(node->getCondition()->getType())); accessChainLoad(&mNodeData.back(), node->getCondition()->getType());
// Create a conditional with maximum 3 blocks, one for the true block (if any), one for the // Create a conditional with maximum 3 blocks, one for the true block (if any), one for the
// else block (if any), and one for the merge block. getChildCount() works here as it // else block (if any), and one for the merge block. getChildCount() works here as it
@ -4510,7 +4612,7 @@ bool OutputSPIRVTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
ASSERT(getLastTraversedChildIndex(visit) == 0); ASSERT(getLastTraversedChildIndex(visit) == 0);
const spirv::IdRef conditionValue = const spirv::IdRef conditionValue =
accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(node->getInit()->getType())); accessChainLoad(&mNodeData.back(), node->getInit()->getType());
// First, need to find out how many blocks are there in the switch. // First, need to find out how many blocks are there in the switch.
const TIntermSequence &statements = *node->getStatementList()->getSequence(); const TIntermSequence &statements = *node->getStatementList()->getSequence();
@ -5024,12 +5126,9 @@ bool OutputSPIRVTraverser::visitDeclaration(Visit visit, TIntermDeclaration *nod
else else
{ {
// Otherwise generate code to load from right hand side expression. // Otherwise generate code to load from right hand side expression.
initializerId = initializerId = accessChainLoad(&mNodeData.back(), symbol->getType());
accessChainLoad(&mNodeData.back(), mBuilder.getDecorations(initializer->getType()));
} }
// TODO: handle mismatching types. http://anglebug.com/4889.
// Clean up the initializer data. // Clean up the initializer data.
mNodeData.pop_back(); mNodeData.pop_back();
} }
@ -5297,8 +5396,8 @@ bool OutputSPIRVTraverser::visitLoop(Visit visit, TIntermLoop *node)
node->getCondition()->traverse(this); node->getCondition()->traverse(this);
// Generate the branch at the end of the %cond block. // Generate the branch at the end of the %cond block.
const spirv::IdRef conditionValue = accessChainLoad( const spirv::IdRef conditionValue =
&mNodeData.back(), mBuilder.getDecorations(node->getCondition()->getType())); accessChainLoad(&mNodeData.back(), node->getCondition()->getType());
mBuilder.writeLoopConditionEnd(conditionValue, bodyBlock, mergeBlock); mBuilder.writeLoopConditionEnd(conditionValue, bodyBlock, mergeBlock);
mNodeData.pop_back(); mNodeData.pop_back();
@ -5340,8 +5439,8 @@ bool OutputSPIRVTraverser::visitLoop(Visit visit, TIntermLoop *node)
node->getCondition()->traverse(this); node->getCondition()->traverse(this);
// Generate the branch at the end of the %cond block. // Generate the branch at the end of the %cond block.
const spirv::IdRef conditionValue = accessChainLoad( const spirv::IdRef conditionValue =
&mNodeData.back(), mBuilder.getDecorations(node->getCondition()->getType())); accessChainLoad(&mNodeData.back(), node->getCondition()->getType());
mBuilder.writeLoopConditionEnd(conditionValue, headerBlock, mergeBlock); mBuilder.writeLoopConditionEnd(conditionValue, headerBlock, mergeBlock);
mNodeData.pop_back(); mNodeData.pop_back();
@ -5388,12 +5487,10 @@ bool OutputSPIRVTraverser::visitBranch(Visit visit, TIntermBranch *node)
{ {
ASSERT(mNodeData.size() >= 1); ASSERT(mNodeData.size() >= 1);
const spirv::IdRef expressionValue = accessChainLoad( const spirv::IdRef expressionValue =
&mNodeData.back(), mBuilder.getDecorations(node->getExpression()->getType())); accessChainLoad(&mNodeData.back(), node->getExpression()->getType());
mNodeData.pop_back(); mNodeData.pop_back();
// TODO: handle mismatching types. http://anglebug.com/6000
spirv::WriteReturnValue(mBuilder.getSpirvCurrentFunctionBlock(), expressionValue); spirv::WriteReturnValue(mBuilder.getSpirvCurrentFunctionBlock(), expressionValue);
mBuilder.terminateCurrentFunctionBlock(); mBuilder.terminateCurrentFunctionBlock();
} }

Просмотреть файл

@ -22,10 +22,6 @@
4889 VULKAN : GLSLTest.MoreNestedCompoundStructsWithSamplersAsFunctionArg/ES2_Vulkan_DirectSPIRVGen = SKIP 4889 VULKAN : GLSLTest.MoreNestedCompoundStructsWithSamplersAsFunctionArg/ES2_Vulkan_DirectSPIRVGen = SKIP
4889 VULKAN : GLSLTest.NestedCompoundStructsWithSamplersAsFunctionArg/ES2_Vulkan_DirectSPIRVGen = SKIP 4889 VULKAN : GLSLTest.NestedCompoundStructsWithSamplersAsFunctionArg/ES2_Vulkan_DirectSPIRVGen = SKIP
4889 VULKAN : GLSLTest.SamplerInStructMemberIndexing/ES2_Vulkan_DirectSPIRVGen = SKIP 4889 VULKAN : GLSLTest.SamplerInStructMemberIndexing/ES2_Vulkan_DirectSPIRVGen = SKIP
// SPIR-V generation doesn't yet handle copy between different SPIR-V types generated from the same GLSL type
4889 VULKAN : GLSLTest_ES31.MixedRowAndColumnMajorMatrices/ES3_1_Vulkan_DirectSPIRVGen = SKIP
4889 VULKAN : GLSLTest_ES31.BasicTypeArrayAndArrayOfSampler/ES3_1_Vulkan_DirectSPIRVGen = SKIP
4889 VULKAN : GLSLTest_ES31.StructBothInvariantAndNot/ES3_1_Vulkan_DirectSPIRVGen = SKIP
// Windows // Windows
3786 WIN NVIDIA D3D11 : BufferDataOverflowTest.VertexBufferIntegerOverflow/* = SKIP 3786 WIN NVIDIA D3D11 : BufferDataOverflowTest.VertexBufferIntegerOverflow/* = SKIP
@ -41,6 +37,8 @@
6065 LINUX INTEL VULKAN : SimpleStateChangeTestES31.DrawThenUpdateUBOThenDrawThenDrawIndexed/* = SKIP 6065 LINUX INTEL VULKAN : SimpleStateChangeTestES31.DrawThenUpdateUBOThenDrawThenDrawIndexed/* = SKIP
// Fails in older mesa // Fails in older mesa
6109 LINUX INTEL : GLSLTestLoops.*While*/* = SKIP 6109 LINUX INTEL : GLSLTestLoops.*While*/* = SKIP
// Crashes when compiling the shader
6173 LINUX INTEL OPENGL : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP
// Nvidia // Nvidia
6115 NVIDIA OPENGL : GLSLTestLoops.DoWhileContinue/* = SKIP 6115 NVIDIA OPENGL : GLSLTestLoops.DoWhileContinue/* = SKIP
@ -51,6 +49,9 @@
6115 NVIDIA GLES : GLSLTestLoops.DoWhileUnconditionalContinue/* = SKIP 6115 NVIDIA GLES : GLSLTestLoops.DoWhileUnconditionalContinue/* = SKIP
6115 NVIDIA GLES : GLSLTestLoops.DoWhileContinueInSwitch/* = SKIP 6115 NVIDIA GLES : GLSLTestLoops.DoWhileContinueInSwitch/* = SKIP
6115 NVIDIA GLES : GLSLTestLoops.WhileBreak/* = SKIP 6115 NVIDIA GLES : GLSLTestLoops.WhileBreak/* = SKIP
// Generates invalid errors when compiling the shader in the GL backend
6172 NVIDIA OpenGL : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP
6172 NVIDIA GLES : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP
// Intel Vulkan // Intel Vulkan
@ -80,6 +81,8 @@
6091 WIN D3D11 : GLSLTest_ES3.InitGlobalComplexConstant/* = SKIP 6091 WIN D3D11 : GLSLTest_ES3.InitGlobalComplexConstant/* = SKIP
6122 WIN D3D11 : GLSLTestLoops.*ContinueInSwitch/* = SKIP 6122 WIN D3D11 : GLSLTestLoops.*ContinueInSwitch/* = SKIP
6150 WIN D3D11 : GLSLTest_ES31.StructAndArrayEqualOperator/* = SKIP 6150 WIN D3D11 : GLSLTest_ES31.StructAndArrayEqualOperator/* = SKIP
// Fails on assertion in translation to D3D
3841 WIN D3D11 : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP
// Android // Android
6095 ANDROID GLES : GLSLTest_ES3.InitGlobalComplexConstant/ES3_OpenGLES = SKIP 6095 ANDROID GLES : GLSLTest_ES3.InitGlobalComplexConstant/ES3_OpenGLES = SKIP
@ -181,9 +184,14 @@
6167 PIXEL4ORXL GLES : DepthStencilTestES3.FramebufferClearThenStencilTestStateThenStencilAttached/* = SKIP 6167 PIXEL4ORXL GLES : DepthStencilTestES3.FramebufferClearThenStencilTestStateThenStencilAttached/* = SKIP
6167 PIXEL4ORXL GLES : DepthStencilTestES3.StencilTestStateThenFramebufferClearThenStencilAttached/* = SKIP 6167 PIXEL4ORXL GLES : DepthStencilTestES3.StencilTestStateThenFramebufferClearThenStencilAttached/* = SKIP
// Internal driver failures on both GLES and Vulkan
3839 NEXUS5X : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP
3839 PIXEL4ORXL : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP
// AMD windows failures. The GPU and driver on our bots are very old and buggy. // AMD windows failures. The GPU and driver on our bots are very old and buggy.
// http://crbug.com/1224996 // http://crbug.com/1224996
4889 WIN AMD VULKAN : GLSLTest.StructWithInitializer/ES2_Vulkan_DirectSPIRVGen = SKIP 4889 WIN AMD VULKAN : GLSLTest.StructWithInitializer/ES2_Vulkan_DirectSPIRVGen = SKIP
4889 WIN AMD VULKAN : GLSLTest.StructWithUniformInitializer/ES2_Vulkan_DirectSPIRVGen = SKIP 4889 WIN AMD VULKAN : GLSLTest.StructWithUniformInitializer/ES2_Vulkan_DirectSPIRVGen = SKIP
4889 WIN AMD VULKAN : GLSLTest_ES3.ValidIndexClampES300/ES3_Vulkan_DirectSPIRVGen = SKIP 4889 WIN AMD VULKAN : GLSLTest_ES3.ValidIndexClampES300/ES3_Vulkan_DirectSPIRVGen = SKIP
4889 WIN AMD VULKAN : GLSLTest_ES3.InitGlobalComplexConstant/ES3_Vulkan_DirectSPIRVGen = SKIP 4889 WIN AMD VULKAN : GLSLTest_ES3.InitGlobalComplexConstant/ES3_Vulkan_DirectSPIRVGen = SKIP
4889 WIN AMD VULKAN : GLSLTest_ES31.TypesUsedInDifferentBlockStorages/* = SKIP

Просмотреть файл

@ -8604,7 +8604,7 @@ void InitBuffer(GLuint program,
const char *name, const char *name,
GLuint buffer, GLuint buffer,
uint32_t bindingIndex, uint32_t bindingIndex,
float data[], const float data[],
uint32_t dataSize, uint32_t dataSize,
bool isUniform) bool isUniform)
{ {
@ -10315,6 +10315,343 @@ void main()
EXPECT_TRUE(VerifyBuffer(ssbo, data, size)); EXPECT_TRUE(VerifyBuffer(ssbo, data, size));
} }
// Verify that types used differently (in different block storages, differently qualified etc) work
// when copied around.
TEST_P(GLSLTest_ES31, TypesUsedInDifferentBlockStorages)
{
constexpr char kCS[] = R"(#version 310 es
precision highp float;
layout(local_size_x=1) in;
struct Inner
{
mat3x2 m;
float f[3];
uvec2 u[2][4];
ivec3 i;
mat2x3 m2[3][2];
};
struct Outer
{
Inner i[2];
};
layout(std140, column_major) uniform Ubo140c
{
mat2 m;
layout(row_major) Outer o;
} ubo140cIn;
layout(std430, row_major, binding = 0) buffer Ubo430r
{
mat2 m;
layout(column_major) Outer o;
} ubo430rIn;
layout(std140, column_major, binding = 1) buffer Ssbo140c
{
layout(row_major) mat2 m[2];
Outer o;
layout(row_major) Inner i;
} ssbo140cOut;
layout(std430, row_major, binding = 2) buffer Ssbo430r
{
layout(column_major) mat2 m[2];
Outer o;
layout(column_major) Inner i;
} ssbo430rOut;
void writeArgToStd140(uvec2 u[2][4], int innerIndex)
{
ssbo140cOut.o.i[innerIndex].u = u;
}
void writeBlockArgToStd140(Inner i, int innerIndex)
{
ssbo140cOut.o.i[innerIndex] = i;
}
mat2x3[3][2] readFromStd140(int innerIndex)
{
return ubo140cIn.o.i[0].m2;
}
Inner readBlockFromStd430(int innerIndex)
{
return ubo430rIn.o.i[innerIndex];
}
void copyFromStd140(out Inner i)
{
i = ubo140cIn.o.i[1];
}
void main(){
// Directly copy from one layout to another.
ssbo140cOut.m[0] = ubo140cIn.m;
ssbo140cOut.m[1] = ubo430rIn.m;
ssbo140cOut.o.i[0].m = ubo140cIn.o.i[0].m;
ssbo140cOut.o.i[0].f = ubo140cIn.o.i[0].f;
ssbo140cOut.o.i[0].i = ubo140cIn.o.i[0].i;
// Read from block and pass to function.
writeArgToStd140(ubo140cIn.o.i[0].u, 0);
writeBlockArgToStd140(ubo430rIn.o.i[0], 1);
// Have function return value read from block.
ssbo140cOut.o.i[0].m2 = readFromStd140(0);
// Have function fill in value as out parameter.
copyFromStd140(ssbo140cOut.i);
// Initialize local variable.
mat2 mStd140 = ubo140cIn.m;
// Copy to variable, through multiple assignments.
mat2 mStd430, temp;
mStd430 = temp = ubo430rIn.m;
// Copy from local variable
ssbo430rOut.m[0] = mStd140;
ssbo430rOut.m[1] = mStd430;
// Construct from struct.
Inner iStd140 = ubo140cIn.o.i[1];
Outer oStd140 = Outer(Inner[2](iStd140, ubo430rIn.o.i[1]));
// Copy struct from local variable.
ssbo430rOut.o = oStd140;
// Construct from arrays
Inner iStd430 = Inner(ubo430rIn.o.i[1].m,
ubo430rIn.o.i[1].f,
ubo430rIn.o.i[1].u,
ubo430rIn.o.i[1].i,
ubo430rIn.o.i[1].m2);
ssbo430rOut.i = iStd430;
})";
ANGLE_GL_COMPUTE_PROGRAM(program, kCS);
EXPECT_GL_NO_ERROR();
// Test data, laid out with padding (0) based on std140/std430 rules.
// clang-format off
const std::vector<float> ubo140cData = {
// m (mat2, column-major)
1, 2, 0, 0, 3, 4, 0, 0,
// o.i[0].m (mat3x2, row-major)
5, 7, 9, 0, 6, 8, 10, 0,
// o.i[0].f (float[3])
12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0,
// o.i[0].u (uvec2[2][4])
15, 16, 0, 0, 17, 18, 0, 0, 19, 20, 0, 0, 21, 22, 0, 0,
23, 24, 0, 0, 25, 26, 0, 0, 27, 28, 0, 0, 29, 30, 0, 0,
// o.i[0].i (ivec3)
31, 32, 33, 0,
// o.i[0].m2 (mat2x3[3][2], row-major)
34, 37, 0, 0, 35, 38, 0, 0, 36, 39, 0, 0,
40, 43, 0, 0, 41, 44, 0, 0, 42, 45, 0, 0,
46, 49, 0, 0, 47, 50, 0, 0, 48, 51, 0, 0,
52, 55, 0, 0, 53, 56, 0, 0, 54, 57, 0, 0,
58, 61, 0, 0, 59, 62, 0, 0, 60, 63, 0, 0,
64, 67, 0, 0, 65, 68, 0, 0, 66, 69, 0, 0,
// o.i[1].m (mat3x2, row-major)
70, 72, 74, 0, 71, 73, 75, 0,
// o.i[1].f (float[3])
77, 0, 0, 0, 78, 0, 0, 0, 79, 0, 0, 0,
// o.i[1].u (uvec2[2][4])
80, 81, 0, 0, 82, 83, 0, 0, 84, 85, 0, 0, 86, 87, 0, 0,
88, 89, 0, 0, 90, 91, 0, 0, 92, 93, 0, 0, 94, 95, 0, 0,
// o.i[1].i (ivec3)
96, 97, 98, 0,
// o.i[1].m2 (mat2x3[3][2], row-major)
99, 102, 0, 0, 100, 103, 0, 0, 101, 104, 0, 0,
105, 108, 0, 0, 106, 109, 0, 0, 107, 110, 0, 0,
111, 114, 0, 0, 112, 115, 0, 0, 113, 116, 0, 0,
117, 120, 0, 0, 118, 121, 0, 0, 119, 122, 0, 0,
123, 126, 0, 0, 124, 127, 0, 0, 125, 128, 0, 0,
129, 132, 0, 0, 130, 133, 0, 0, 131, 134, 0, 0,
};
const std::vector<float> ubo430rData = {
// m (mat2, row-major)
135, 137, 136, 138,
// o.i[0].m (mat3x2, column-major)
139, 140, 141, 142, 143, 144,
// o.i[0].f (float[3])
146, 147, 148, 0,
// o.i[0].u (uvec2[2][4])
149, 150, 151, 152, 153, 154, 155, 156,
157, 158, 159, 160, 161, 162, 163, 164, 0, 0,
// o.i[0].i (ivec3)
165, 166, 167, 0,
// o.i[0].m2 (mat2x3[3][2], column-major)
168, 169, 170, 0, 171, 172, 173, 0,
174, 175, 176, 0, 177, 178, 179, 0,
180, 181, 182, 0, 183, 184, 185, 0,
186, 187, 188, 0, 189, 190, 191, 0,
192, 193, 194, 0, 195, 196, 197, 0,
198, 199, 200, 0, 201, 202, 203, 0,
// o.i[1].m (mat3x2, column-major)
204, 205, 206, 207, 208, 209,
// o.i[1].f (float[3])
211, 212, 213, 0,
// o.i[1].u (uvec2[2][4])
214, 215, 216, 217, 218, 219, 220, 221,
222, 223, 224, 225, 226, 227, 228, 229, 0, 0,
// o.i[1].i (ivec3)
230, 231, 232, 0,
// o.i[1].m2 (mat2x3[3][2], column-major)
233, 234, 235, 0, 236, 237, 238, 0,
239, 240, 241, 0, 242, 243, 244, 0,
245, 246, 247, 0, 248, 249, 250, 0,
251, 252, 253, 0, 254, 255, 256, 0,
257, 258, 259, 0, 260, 261, 262, 0,
263, 264, 265, 0, 266, 267, 268, 0,
};
const std::vector<float> ssbo140cExpect = {
// m (mat2[2], row-major), m[0] copied from ubo140cIn.m, m[1] from ubo430rIn.m
1, 3, 0, 0, 2, 4, 0, 0,
135, 137, 0, 0, 136, 138, 0, 0,
// o.i[0].m (mat3x2, column-major), copied from ubo140cIn.o.i[0].m
5, 6, 0, 0, 7, 8, 0, 0, 9, 10, 0, 0,
// o.i[0].f (float[3]), copied from ubo140cIn.o.i[0].f
12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0,
// o.i[0].u (uvec2[2][4]), copied from ubo140cIn.o.i[0].u
15, 16, 0, 0, 17, 18, 0, 0, 19, 20, 0, 0, 21, 22, 0, 0,
23, 24, 0, 0, 25, 26, 0, 0, 27, 28, 0, 0, 29, 30, 0, 0,
// o.i[0].i (ivec3), copied from ubo140cIn.o.i[0].i
31, 32, 33, 0,
// o.i[0].m2 (mat2x3[3][2], column-major), copied from ubo140cIn.o.i[0].m2
34, 35, 36, 0, 37, 38, 39, 0,
40, 41, 42, 0, 43, 44, 45, 0,
46, 47, 48, 0, 49, 50, 51, 0,
52, 53, 54, 0, 55, 56, 57, 0,
58, 59, 60, 0, 61, 62, 63, 0,
64, 65, 66, 0, 67, 68, 69, 0,
// o.i[1].m (mat3x2, column-major), copied from ubo430rIn.o.i[0].m
139, 140, 0, 0, 141, 142, 0, 0, 143, 144, 0, 0,
// o.i[1].f (float[3]), copied from ubo430rIn.o.i[0].f
146, 0, 0, 0, 147, 0, 0, 0, 148, 0, 0, 0,
// o.i[1].u (uvec2[2][4]), copied from ubo430rIn.o.i[0].u
149, 150, 0, 0, 151, 152, 0, 0, 153, 154, 0, 0, 155, 156, 0, 0,
157, 158, 0, 0, 159, 160, 0, 0, 161, 162, 0, 0, 163, 164, 0, 0,
// o.i[1].i (ivec3), copied from ubo430rIn.o.i[0].i
165, 166, 167, 0,
// o.i[1].m2 (mat2x3[3][2], column-major), copied from ubo430rIn.o.i[0].m2
168, 169, 170, 0, 171, 172, 173, 0,
174, 175, 176, 0, 177, 178, 179, 0,
180, 181, 182, 0, 183, 184, 185, 0,
186, 187, 188, 0, 189, 190, 191, 0,
192, 193, 194, 0, 195, 196, 197, 0,
198, 199, 200, 0, 201, 202, 203, 0,
// i.m (mat3x2, row-major), copied from ubo140cIn.o.i[1].m
70, 72, 74, 0, 71, 73, 75, 0,
// i.f (float[3]), copied from ubo140cIn.o.i[1].f
77, 0, 0, 0, 78, 0, 0, 0, 79, 0, 0, 0,
// i.u (uvec2[2][4]), copied from ubo430rIn.o.i[1].u
80, 81, 0, 0, 82, 83, 0, 0, 84, 85, 0, 0, 86, 87, 0, 0,
88, 89, 0, 0, 90, 91, 0, 0, 92, 93, 0, 0, 94, 95, 0, 0,
// i.i (ivec3), copied from ubo140cIn.o.i[1].i
96, 97, 98, 0,
// i.m2 (mat2x3[3][2], row-major), copied from ubo140cIn.o.i[1].m2
99, 102, 0, 0, 100, 103, 0, 0, 101, 104, 0, 0,
105, 108, 0, 0, 106, 109, 0, 0, 107, 110, 0, 0,
111, 114, 0, 0, 112, 115, 0, 0, 113, 116, 0, 0,
117, 120, 0, 0, 118, 121, 0, 0, 119, 122, 0, 0,
123, 126, 0, 0, 124, 127, 0, 0, 125, 128, 0, 0,
129, 132, 0, 0, 130, 133, 0, 0, 131, 134, 0, 0,
};
const std::vector<float> ssbo430rExpect = {
// m (mat2[2], column-major), m[0] copied from ubo140cIn.m, m[1] from ubo430rIn.m
1, 2, 3, 4,
135, 136, 137, 138,
// o.i[0].m (mat3x2, row-major), copied from ubo140cIn.o.i[1].m
70, 72, 74, 0, 71, 73, 75, 0,
// o.i[0].f (float[3]), copied from ubo140cIn.o.i[1].f
77, 78, 79, 0,
// o.i[0].u (uvec2[2][4]), copied from ubo140cIn.o.i[1].u
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90, 91, 92, 93, 94, 95,
// o.i[0].i (ivec3), copied from ubo140cIn.o.i[1].i
96, 97, 98, 0,
// o.i[0].m2 (mat2x3[3][2], row-major), copied from ubo140cIn.o.i[1].m2
99, 102, 100, 103, 101, 104,
105, 108, 106, 109, 107, 110,
111, 114, 112, 115, 113, 116,
117, 120, 118, 121, 119, 122,
123, 126, 124, 127, 125, 128,
129, 132, 130, 133, 131, 134,
// o.i[1].m (mat3x2, row-major), copied from ubo430rIn.o.i[1].m
204, 206, 208, 0, 205, 207, 209, 0,
// o.i[1].f (float[3]), copied from ubo430rIn.o.i[1].f
211, 212, 213, 0,
// o.i[1].u (uvec2[2][4]), copied from ubo430rIn.o.i[1].u
214, 215, 216, 217, 218, 219, 220, 221,
222, 223, 224, 225, 226, 227, 228, 229,
// o.i[1].i (ivec3), copied from ubo430rIn.o.i[1].i
230, 231, 232, 0,
// o.i[1].m2 (mat2x3[3][2], row-major), copied from ubo430rIn.o.i[1].m2
233, 236, 234, 237, 235, 238,
239, 242, 240, 243, 241, 244,
245, 248, 246, 249, 247, 250,
251, 254, 252, 255, 253, 256,
257, 260, 258, 261, 259, 262,
263, 266, 264, 267, 265, 268,
// i.m (mat3x2, column-major), copied from ubo430rIn.o.i[1].m
204, 205, 206, 207, 208, 209,
// i.f (float[3]), copied from ubo430rIn.o.i[1].f
211, 212, 213, 0,
// i.u (uvec2[2][4]), copied from ubo430rIn.o.i[1].u
214, 215, 216, 217, 218, 219, 220, 221,
222, 223, 224, 225, 226, 227, 228, 229, 0, 0,
// i.i (ivec3), copied from ubo430rIn.o.i[1].i
230, 231, 232, 0,
// i.m2 (mat2x3[3][2], column-major), copied from ubo430rIn.o.i[1].m2
233, 234, 235, 0, 236, 237, 238, 0,
239, 240, 241, 0, 242, 243, 244, 0,
245, 246, 247, 0, 248, 249, 250, 0,
251, 252, 253, 0, 254, 255, 256, 0,
257, 258, 259, 0, 260, 261, 262, 0,
263, 264, 265, 0, 266, 267, 268, 0,
};
const std::vector<float> zeros(std::max(ssbo140cExpect.size(), ssbo430rExpect.size()), 0);
// clang-format on
GLBuffer uboStd140ColMajor, uboStd430RowMajor;
GLBuffer ssboStd140ColMajor, ssboStd430RowMajor;
InitBuffer(program, "Ubo140c", uboStd140ColMajor, 0, ubo140cData.data(),
static_cast<uint32_t>(ubo140cData.size()), true);
InitBuffer(program, "Ubo430r", uboStd430RowMajor, 0, ubo430rData.data(),
static_cast<uint32_t>(ubo430rData.size()), false);
InitBuffer(program, "Ssbo140c", ssboStd140ColMajor, 1, zeros.data(),
static_cast<uint32_t>(ssbo140cExpect.size()), false);
InitBuffer(program, "Ssbo430r", ssboStd430RowMajor, 2, zeros.data(),
static_cast<uint32_t>(ssbo430rExpect.size()), false);
EXPECT_GL_NO_ERROR();
glUseProgram(program);
glDispatchCompute(1, 1, 1);
EXPECT_GL_NO_ERROR();
EXPECT_TRUE(VerifyBuffer(ssboStd140ColMajor, ssbo140cExpect.data(),
static_cast<uint32_t>(ssbo140cExpect.size())));
EXPECT_TRUE(VerifyBuffer(ssboStd430RowMajor, ssbo430rExpect.data(),
static_cast<uint32_t>(ssbo430rExpect.size())));
}
// Test that the precise keyword is not reserved before ES3.1. // Test that the precise keyword is not reserved before ES3.1.
TEST_P(GLSLTest_ES3, PreciseNotReserved) TEST_P(GLSLTest_ES3, PreciseNotReserved)
{ {
@ -11947,10 +12284,10 @@ ANGLE_INSTANTIATE_TEST_ES3_AND(GLSLTest_ES3, WithDirectSPIRVGeneration(ES3_VULKA
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTestLoops); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTestLoops);
ANGLE_INSTANTIATE_TEST_ES3_AND(GLSLTestLoops, WithDirectSPIRVGeneration(ES3_VULKAN())); ANGLE_INSTANTIATE_TEST_ES3_AND(GLSLTestLoops, WithDirectSPIRVGeneration(ES3_VULKAN()));
ANGLE_INSTANTIATE_TEST_ES2(WebGLGLSLTest); ANGLE_INSTANTIATE_TEST_ES2_AND(WebGLGLSLTest, WithDirectSPIRVGeneration(ES2_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2GLSLTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2GLSLTest);
ANGLE_INSTANTIATE_TEST_ES3(WebGL2GLSLTest); ANGLE_INSTANTIATE_TEST_ES3_AND(WebGL2GLSLTest, WithDirectSPIRVGeneration(ES3_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTest_ES31); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTest_ES31);
ANGLE_INSTANTIATE_TEST_ES31_AND(GLSLTest_ES31, WithDirectSPIRVGeneration(ES31_VULKAN())); ANGLE_INSTANTIATE_TEST_ES31_AND(GLSLTest_ES31, WithDirectSPIRVGeneration(ES31_VULKAN()));