Commit ee4a5266 by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: SPIR-V Gen: Function calls

This change implements function calls. As a result, transform feedback tests are enabled as well as support for transform feedback emulation (which contains functions and if blocks). `const` and opaque uniform function arguments take intermediate values, while the rest take memory objects. If the argument being passed is an unindexed lvalue, it can be directly given to the function. Otherwise a temporary variable is made which is initialized by the parameter if necessary (in and inout parameters) and later overwrites the parameter if necessary (out and inout parameters). Bug: angleproject:4889 Change-Id: I8976cdd17870c35d5a2daeed3c38de57ada931d9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2930363 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org>
parent c1932fb5
...@@ -78,11 +78,13 @@ SpirvType SPIRVBuilder::getSpirvType(const TType &type, TLayoutBlockStorage bloc ...@@ -78,11 +78,13 @@ SpirvType SPIRVBuilder::getSpirvType(const TType &type, TLayoutBlockStorage bloc
// Calculate the block storage from the interface block automatically. The fields inherit // Calculate the block storage from the interface block automatically. The fields inherit
// from this. Default to std140. // from this. Default to std140.
ASSERT(spirvType.blockStorage == EbsUnspecified); if (spirvType.blockStorage == EbsUnspecified)
spirvType.blockStorage = type.getLayoutQualifier().blockStorage;
if (!IsShaderIoBlock(type.getQualifier()) && spirvType.blockStorage != EbsStd430)
{ {
spirvType.blockStorage = EbsStd140; spirvType.blockStorage = type.getLayoutQualifier().blockStorage;
if (!IsShaderIoBlock(type.getQualifier()) && spirvType.blockStorage != EbsStd430)
{
spirvType.blockStorage = EbsStd140;
}
} }
} }
else if (spirvType.arraySizes.empty()) else if (spirvType.arraySizes.empty())
...@@ -151,7 +153,7 @@ spirv::IdRef SPIRVBuilder::getFunctionTypeId(spirv::IdRef returnTypeId, ...@@ -151,7 +153,7 @@ spirv::IdRef SPIRVBuilder::getFunctionTypeId(spirv::IdRef returnTypeId,
{ {
const spirv::IdRef functionTypeId = getNewId(); const spirv::IdRef functionTypeId = getNewId();
spirv::WriteTypeFunction(&mSpirvTypeAndConstantDecls, functionTypeId, returnTypeId, spirv::WriteTypeFunction(&mSpirvFunctionTypeDecls, functionTypeId, returnTypeId,
paramTypeIds); paramTypeIds);
iter = mFunctionTypeIdMap.insert({key, functionTypeId}).first; iter = mFunctionTypeIdMap.insert({key, functionTypeId}).first;
...@@ -180,11 +182,20 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const char *block ...@@ -180,11 +182,20 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const char *block
const spirv::IdRef subTypeId = getSpirvTypeData(subType, "").id; const spirv::IdRef subTypeId = getSpirvTypeData(subType, "").id;
const unsigned int length = type.arraySizes.back(); const unsigned int length = type.arraySizes.back();
const spirv::IdRef lengthId = getUintConstant(length); typeId = getNewId();
typeId = getNewId(); if (length == 0)
spirv::WriteTypeArray(&mSpirvTypeAndConstantDecls, typeId, subTypeId, lengthId); {
// Storage buffers may include a dynamically-sized array, which is identified by it
// having a length of 0.
spirv::WriteTypeRuntimeArray(&mSpirvTypeAndConstantDecls, typeId, subTypeId);
}
else
{
const spirv::IdRef lengthId = getUintConstant(length);
spirv::WriteTypeArray(&mSpirvTypeAndConstantDecls, typeId, subTypeId, lengthId);
}
} }
else if (type.block != nullptr) else if (type.block != nullptr)
{ {
...@@ -781,13 +792,19 @@ spirv::IdRef SPIRVBuilder::getCompositeConstant(spirv::IdRef typeId, const spirv ...@@ -781,13 +792,19 @@ spirv::IdRef SPIRVBuilder::getCompositeConstant(spirv::IdRef typeId, const spirv
return iter->second; return iter->second;
} }
void SPIRVBuilder::startNewFunction() void SPIRVBuilder::startNewFunction(spirv::IdRef functionId, const char *name)
{ {
ASSERT(mSpirvCurrentFunctionBlocks.empty()); ASSERT(mSpirvCurrentFunctionBlocks.empty());
// Add the first block of the function. // Add the first block of the function.
mSpirvCurrentFunctionBlocks.emplace_back(); mSpirvCurrentFunctionBlocks.emplace_back();
mSpirvCurrentFunctionBlocks.back().labelId = getNewId(); mSpirvCurrentFunctionBlocks.back().labelId = getNewId();
// Output debug information.
if (name)
{
spirv::WriteName(&mSpirvDebug, functionId, name);
}
} }
void SPIRVBuilder::assembleSpirvFunctionBlocks() void SPIRVBuilder::assembleSpirvFunctionBlocks()
...@@ -1060,7 +1077,8 @@ uint32_t SPIRVBuilder::calculateBaseAlignmentAndSize(const SpirvType &type, ...@@ -1060,7 +1077,8 @@ uint32_t SPIRVBuilder::calculateBaseAlignmentAndSize(const SpirvType &type,
uint32_t arraySizeProduct = 1; uint32_t arraySizeProduct = 1;
for (uint32_t arraySize : type.arraySizes) for (uint32_t arraySize : type.arraySizes)
{ {
arraySizeProduct *= arraySize; // For runtime arrays, arraySize will be 0 and should be excluded.
arraySizeProduct *= arraySize > 0 ? arraySize : 1;
} }
*sizeInStorageBlockOut = baseTypeData.sizeInStorageBlock * arraySizeProduct; *sizeInStorageBlockOut = baseTypeData.sizeInStorageBlock * arraySizeProduct;
...@@ -1281,8 +1299,8 @@ spirv::Blob SPIRVBuilder::getSpirv() ...@@ -1281,8 +1299,8 @@ spirv::Blob SPIRVBuilder::getSpirv()
// OpExtInstImport, OpEntryPoint etc. // OpExtInstImport, OpEntryPoint etc.
result.reserve(5 + mCapabilities.size() * 2 + mExecutionModes.size() * 3 + mSpirvDebug.size() + result.reserve(5 + mCapabilities.size() * 2 + mExecutionModes.size() * 3 + mSpirvDebug.size() +
mSpirvDecorations.size() + mSpirvTypeAndConstantDecls.size() + mSpirvDecorations.size() + mSpirvTypeAndConstantDecls.size() +
mSpirvTypePointerDecls.size() + mSpirvVariableDecls.size() + mSpirvTypePointerDecls.size() + mSpirvFunctionTypeDecls.size() +
mSpirvFunctions.size()); mSpirvVariableDecls.size() + mSpirvFunctions.size());
// Generate any necessary id before writing the id bound in header. // Generate any necessary id before writing the id bound in header.
const spirv::IdRef extInstImportId = getNewId(); const spirv::IdRef extInstImportId = getNewId();
...@@ -1338,6 +1356,7 @@ spirv::Blob SPIRVBuilder::getSpirv() ...@@ -1338,6 +1356,7 @@ spirv::Blob SPIRVBuilder::getSpirv()
result.insert(result.end(), mSpirvTypeAndConstantDecls.begin(), result.insert(result.end(), mSpirvTypeAndConstantDecls.begin(),
mSpirvTypeAndConstantDecls.end()); mSpirvTypeAndConstantDecls.end());
result.insert(result.end(), mSpirvTypePointerDecls.begin(), mSpirvTypePointerDecls.end()); result.insert(result.end(), mSpirvTypePointerDecls.begin(), mSpirvTypePointerDecls.end());
result.insert(result.end(), mSpirvFunctionTypeDecls.begin(), mSpirvFunctionTypeDecls.end());
result.insert(result.end(), mSpirvVariableDecls.begin(), mSpirvVariableDecls.end()); result.insert(result.end(), mSpirvVariableDecls.begin(), mSpirvVariableDecls.end());
result.insert(result.end(), mSpirvFunctions.begin(), mSpirvFunctions.end()); result.insert(result.end(), mSpirvFunctions.begin(), mSpirvFunctions.end());
......
...@@ -233,6 +233,7 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -233,6 +233,7 @@ class SPIRVBuilder : angle::NonCopyable
spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; } spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; }
spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; } spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; }
spirv::Blob *getSpirvTypePointerDecls() { return &mSpirvTypePointerDecls; } spirv::Blob *getSpirvTypePointerDecls() { return &mSpirvTypePointerDecls; }
spirv::Blob *getSpirvFunctionTypeDecls() { return &mSpirvFunctionTypeDecls; }
spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; } spirv::Blob *getSpirvVariableDecls() { return &mSpirvVariableDecls; }
spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; } spirv::Blob *getSpirvFunctions() { return &mSpirvFunctions; }
spirv::Blob *getSpirvCurrentFunctionBlock() spirv::Blob *getSpirvCurrentFunctionBlock()
...@@ -272,7 +273,7 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -272,7 +273,7 @@ class SPIRVBuilder : angle::NonCopyable
spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values); spirv::IdRef getCompositeConstant(spirv::IdRef typeId, const spirv::IdRefList &values);
// Helpers to start and end a function. // Helpers to start and end a function.
void startNewFunction(); void startNewFunction(spirv::IdRef functionId, const char *name);
void assembleSpirvFunctionBlocks(); void assembleSpirvFunctionBlocks();
// Helper to declare a variable. Function-local variables must be placed in the first block of // Helper to declare a variable. Function-local variables must be placed in the first block of
...@@ -352,6 +353,7 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -352,6 +353,7 @@ class SPIRVBuilder : angle::NonCopyable
spirv::Blob mSpirvDecorations; spirv::Blob mSpirvDecorations;
spirv::Blob mSpirvTypeAndConstantDecls; spirv::Blob mSpirvTypeAndConstantDecls;
spirv::Blob mSpirvTypePointerDecls; spirv::Blob mSpirvTypePointerDecls;
spirv::Blob mSpirvFunctionTypeDecls;
spirv::Blob mSpirvVariableDecls; spirv::Blob mSpirvVariableDecls;
spirv::Blob mSpirvFunctions; spirv::Blob mSpirvFunctions;
// A list of blocks created for the current function. These are assembled by // A list of blocks created for the current function. These are assembled by
......
...@@ -92,11 +92,6 @@ struct AccessChain ...@@ -92,11 +92,6 @@ struct AccessChain
TLayoutBlockStorage baseBlockStorage; TLayoutBlockStorage baseBlockStorage;
}; };
bool IsAccessChainRValue(const AccessChain &accessChain)
{
return accessChain.storageClass == spv::StorageClassMax;
}
// As each node is traversed, it produces data. When visiting back the parent, this data is used to // As each node is traversed, it produces data. When visiting back the parent, this data is used to
// complete the data of the parent. For example, the children of a function call (i.e. the // complete the data of the parent. For example, the children of a function call (i.e. the
// arguments) each produce a SPIR-V id corresponding to the result of their expression. The // arguments) each produce a SPIR-V id corresponding to the result of their expression. The
...@@ -116,6 +111,17 @@ struct NodeData ...@@ -116,6 +111,17 @@ struct NodeData
AccessChain accessChain; AccessChain accessChain;
}; };
bool IsAccessChainRValue(const AccessChain &accessChain)
{
return accessChain.storageClass == spv::StorageClassMax;
}
bool IsAccessChainUnindexedLValue(const NodeData &data)
{
return !IsAccessChainRValue(data.accessChain) && data.idList.empty() &&
data.accessChain.swizzles.empty() && !data.accessChain.dynamicComponent.valid();
}
// A traverser that generates SPIR-V as it walks the AST. // A traverser that generates SPIR-V as it walks the AST.
class OutputSPIRVTraverser : public TIntermTraverser class OutputSPIRVTraverser : public TIntermTraverser
{ {
...@@ -147,6 +153,10 @@ class OutputSPIRVTraverser : public TIntermTraverser ...@@ -147,6 +153,10 @@ class OutputSPIRVTraverser : public TIntermTraverser
void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override; void visitPreprocessorDirective(TIntermPreprocessorDirective *node) override;
private: private:
spirv::IdRef getSymbolIdAndStorageClass(const TSymbol *symbol,
const TType &type,
spv::StorageClass *storageClass);
// Access chain handling. // Access chain handling.
void accessChainPush(NodeData *data, spirv::IdRef index, spirv::IdRef typeId) const; void accessChainPush(NodeData *data, spirv::IdRef index, spirv::IdRef typeId) const;
void accessChainPushLiteral(NodeData *data, void accessChainPushLiteral(NodeData *data,
...@@ -177,9 +187,7 @@ class OutputSPIRVTraverser : public TIntermTraverser ...@@ -177,9 +187,7 @@ class OutputSPIRVTraverser : public TIntermTraverser
spirv::IdRef createConstant(const TType &type, spirv::IdRef createConstant(const TType &type,
TBasicType expectedBasicType, TBasicType expectedBasicType,
const TConstantUnion *constUnion); const TConstantUnion *constUnion);
spirv::IdRef createConstructor(TIntermAggregate *node, spirv::IdRef createConstructor(TIntermAggregate *node, spirv::IdRef typeId);
spirv::IdRef typeId,
const spirv::IdRefList &parameters);
spirv::IdRef createArrayOrStructConstructor(TIntermAggregate *node, spirv::IdRef createArrayOrStructConstructor(TIntermAggregate *node,
spirv::IdRef typeId, spirv::IdRef typeId,
const spirv::IdRefList &parameters); const spirv::IdRefList &parameters);
...@@ -203,6 +211,8 @@ class OutputSPIRVTraverser : public TIntermTraverser ...@@ -203,6 +211,8 @@ class OutputSPIRVTraverser : public TIntermTraverser
const spirv::IdRefList &parameters, const spirv::IdRefList &parameters,
spirv::IdRefList *extractedComponentsOut); spirv::IdRefList *extractedComponentsOut);
spirv::IdRef createFunctionCall(TIntermAggregate *node, spirv::IdRef resultTypeId);
ANGLE_MAYBE_UNUSED TCompiler *mCompiler; ANGLE_MAYBE_UNUSED TCompiler *mCompiler;
ANGLE_MAYBE_UNUSED ShCompileOptions mCompileOptions; ANGLE_MAYBE_UNUSED ShCompileOptions mCompileOptions;
...@@ -216,6 +226,7 @@ class OutputSPIRVTraverser : public TIntermTraverser ...@@ -216,6 +226,7 @@ class OutputSPIRVTraverser : public TIntermTraverser
// A map of TSymbol to its SPIR-V id. This could be a: // A map of TSymbol to its SPIR-V id. This could be a:
// //
// - TVariable, or // - TVariable, or
// - TFunction, or
// - TInterfaceBlock: because TIntermSymbols referencing a field of an unnamed interface block // - TInterfaceBlock: because TIntermSymbols referencing a field of an unnamed interface block
// don't reference the TVariable that defines the struct, but the TInterfaceBlock itself. // don't reference the TVariable that defines the struct, but the TInterfaceBlock itself.
angle::HashMap<const TSymbol *, spirv::IdRef> mSymbolIdMap; angle::HashMap<const TSymbol *, spirv::IdRef> mSymbolIdMap;
...@@ -229,14 +240,16 @@ spv::StorageClass GetStorageClass(const TType &type) ...@@ -229,14 +240,16 @@ spv::StorageClass GetStorageClass(const TType &type)
return spv::StorageClassUniformConstant; return spv::StorageClassUniformConstant;
} }
const TQualifier qualifier = type.getQualifier();
// Input varying and IO blocks have the Input storage class // Input varying and IO blocks have the Input storage class
if (IsShaderIn(type.getQualifier())) if (IsShaderIn(qualifier))
{ {
return spv::StorageClassInput; return spv::StorageClassInput;
} }
// Output varying and IO blocks have the Input storage class // Output varying and IO blocks have the Input storage class
if (IsShaderOut(type.getQualifier())) if (IsShaderOut(qualifier))
{ {
return spv::StorageClassOutput; return spv::StorageClassOutput;
} }
...@@ -245,25 +258,38 @@ spv::StorageClass GetStorageClass(const TType &type) ...@@ -245,25 +258,38 @@ spv::StorageClass GetStorageClass(const TType &type)
if (type.isInterfaceBlock()) if (type.isInterfaceBlock())
{ {
// I/O blocks must have already been classified as input or output above. // I/O blocks must have already been classified as input or output above.
ASSERT(!IsShaderIoBlock(type.getQualifier())); ASSERT(!IsShaderIoBlock(qualifier));
return spv::StorageClassUniform; return spv::StorageClassUniform;
} }
// Compute shader shared memory has the Workgroup storage class switch (qualifier)
if (type.getQualifier() == EvqShared)
{ {
return spv::StorageClassWorkgroup; case EvqShared:
} // Compute shader shared memory has the Workgroup storage class
return spv::StorageClassWorkgroup;
// All other variables are either Private or Function, based on whether they are global or case EvqGlobal:
// function-local. // Global variables have the Private class.
if (type.getQualifier() == EvqGlobal) return spv::StorageClassPrivate;
{
return spv::StorageClassPrivate; case EvqTemporary:
case EvqIn:
case EvqOut:
case EvqInOut:
// Function-local variables have the Function class
return spv::StorageClassFunction;
case EvqVertexID:
case EvqInstanceID:
return spv::StorageClassInput;
default:
// TODO: http://anglebug.com/4889
UNIMPLEMENTED();
} }
ASSERT(type.getQualifier() == EvqTemporary); UNREACHABLE();
return spv::StorageClassFunction; return spv::StorageClassPrivate;
} }
OutputSPIRVTraverser::OutputSPIRVTraverser(TCompiler *compiler, ShCompileOptions compileOptions) OutputSPIRVTraverser::OutputSPIRVTraverser(TCompiler *compiler, ShCompileOptions compileOptions)
...@@ -280,6 +306,50 @@ OutputSPIRVTraverser::~OutputSPIRVTraverser() ...@@ -280,6 +306,50 @@ OutputSPIRVTraverser::~OutputSPIRVTraverser()
ASSERT(mNodeData.empty()); ASSERT(mNodeData.empty());
} }
spirv::IdRef OutputSPIRVTraverser::getSymbolIdAndStorageClass(const TSymbol *symbol,
const TType &type,
spv::StorageClass *storageClass)
{
*storageClass = GetStorageClass(type);
auto iter = mSymbolIdMap.find(symbol);
if (iter != mSymbolIdMap.end())
{
return iter->second;
}
// This must be an implicitly defined variable, define it now.
const char *name = nullptr;
spv::BuiltIn builtInDecoration = spv::BuiltInMax;
SpirvType spirvType;
switch (type.getQualifier())
{
case EvqVertexID:
name = "gl_VertexIndex";
builtInDecoration = spv::BuiltInVertexIndex;
spirvType.type = EbtInt;
break;
case EvqInstanceID:
name = "gl_InstanceIndex";
builtInDecoration = spv::BuiltInInstanceIndex;
spirvType.type = EbtInt;
break;
default:
// TODO: more built-ins. http://anglebug.com/4889
UNIMPLEMENTED();
}
const spirv::IdRef typeId = mBuilder.getSpirvTypeData(spirvType, "").id;
const spirv::IdRef varId = mBuilder.declareVariable(typeId, *storageClass, nullptr, name);
mBuilder.addEntryPointInterfaceVariableId(varId);
spirv::WriteDecorate(mBuilder.getSpirvDecorations(), varId, spv::DecorationBuiltIn,
{spirv::LiteralInteger(builtInDecoration)});
mSymbolIdMap.insert({symbol, varId});
return varId;
}
void OutputSPIRVTraverser::nodeDataInitLValue(NodeData *data, void OutputSPIRVTraverser::nodeDataInitLValue(NodeData *data,
spirv::IdRef baseId, spirv::IdRef baseId,
spirv::IdRef typeId, spirv::IdRef typeId,
...@@ -729,14 +799,27 @@ spirv::IdRef OutputSPIRVTraverser::createConstant(const TType &type, ...@@ -729,14 +799,27 @@ spirv::IdRef OutputSPIRVTraverser::createConstant(const TType &type,
return componentIds[0]; return componentIds[0];
} }
spirv::IdRef OutputSPIRVTraverser::createConstructor(TIntermAggregate *node, spirv::IdRef OutputSPIRVTraverser::createConstructor(TIntermAggregate *node, spirv::IdRef typeId)
spirv::IdRef typeId,
const spirv::IdRefList &parameters)
{ {
const TType &type = node->getType(); const TType &type = node->getType();
const TIntermSequence &arguments = *node->getSequence(); const TIntermSequence &arguments = *node->getSequence();
const TType &arg0Type = arguments[0]->getAsTyped()->getType(); const TType &arg0Type = arguments[0]->getAsTyped()->getType();
const size_t parameterCount = node->getChildCount();
spirv::IdRefList parameters;
for (size_t paramIndex = 0; paramIndex < parameterCount; ++paramIndex)
{
// Take each constructor argument that is visited and evaluate it as rvalue
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
const spirv::IdRef paramValue = accessChainLoad(&param);
// TODO: handle mismatching types. http://anglebug.com/6000
parameters.push_back(paramValue);
}
// Constructors in GLSL can take various shapes, resulting in different translations to SPIR-V // Constructors in GLSL can take various shapes, resulting in different translations to SPIR-V
// (in each case, if the parameter doesn't match the type being constructed, it must be cast): // (in each case, if the parameter doesn't match the type being constructed, it must be cast):
// //
...@@ -1162,6 +1245,121 @@ void OutputSPIRVTraverser::extractComponents(TIntermAggregate *node, ...@@ -1162,6 +1245,121 @@ void OutputSPIRVTraverser::extractComponents(TIntermAggregate *node,
} }
} }
spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
spirv::IdRef resultTypeId)
{
const TFunction *function = node->getFunction();
ASSERT(function);
ASSERT(mSymbolIdMap.count(function) > 0);
const spirv::IdRef functionId = mSymbolIdMap[function];
// Get the list of parameters passed to the function. The function parameters can only be
// memory variables, or if the function argument is |const|, an rvalue.
//
// For in variables:
//
// - If the parameter is const, pass it directly as rvalue, otherwise
// - If the parameter is an unindexed lvalue, pass it directly, otherwise
// - Write it to a temp variable first and pass that.
//
// For out variables:
//
// - If the parameter is an unindexed lvalue, pass it directly, otherwise
// - Pass a temporary variable. After the function call, copy that variable to the parameter.
//
// For inout variables:
//
// - If the parameter is an unindexed lvalue, pass it directly, otherwise
// - Write the parameter to a temp variable and pass that. After the function call, copy that
// variable back to the parameter.
//
// - For opaque uniforms, pass it directly as lvalue,
//
const size_t parameterCount = node->getChildCount();
spirv::IdRefList parameters;
spirv::IdRefList tempVarIds(parameterCount);
spirv::IdRefList tempVarTypeIds(parameterCount);
for (size_t paramIndex = 0; paramIndex < parameterCount; ++paramIndex)
{
const TType &paramType = function->getParam(paramIndex)->getType();
const TQualifier &paramQualifier = paramType.getQualifier();
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
spirv::IdRef paramValue;
if (IsOpaqueType(paramType.getBasicType()) || paramQualifier == EvqConst ||
IsAccessChainUnindexedLValue(param))
{
// The following parameters are passed directly:
//
// - Opaque uniforms,
// - const parameters,
// - unindexed lvalues.
paramValue = accessChainLoad(&param);
}
else
{
ASSERT(paramQualifier == EvqIn || paramQualifier == EvqOut ||
paramQualifier == EvqInOut);
// Need to create a temp variable and pass that.
tempVarTypeIds[paramIndex] = mBuilder.getTypeData(paramType, EbsUnspecified).id;
tempVarIds[paramIndex] = mBuilder.declareVariable(
tempVarTypeIds[paramIndex], spv::StorageClassFunction, nullptr, "param");
// If it's an in or inout parameter, the temp variable needs to be initialized with the
// value of the parameter first.
//
// TODO: handle mismatching types. http://anglebug.com/6000
if (paramQualifier == EvqIn || paramQualifier == EvqInOut)
{
paramValue = accessChainLoad(&param);
spirv::WriteStore(mBuilder.getSpirvCurrentFunctionBlock(), tempVarIds[paramIndex],
paramValue, nullptr);
}
paramValue = tempVarIds[paramIndex];
}
parameters.push_back(paramValue);
}
// Make the actual function call.
const spirv::IdRef result = mBuilder.getNewId();
spirv::WriteFunctionCall(mBuilder.getSpirvCurrentFunctionBlock(), resultTypeId, result,
functionId, parameters);
// Copy from the out and inout temp variables back to the original parameters.
for (size_t paramIndex = 0; paramIndex < parameterCount; ++paramIndex)
{
if (!tempVarIds[paramIndex].valid())
{
continue;
}
const TQualifier &paramQualifier = function->getParam(paramIndex)->getType().getQualifier();
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
if (paramQualifier == EvqIn)
{
continue;
}
// Copy from the temp variable to the parameter.
//
// TODO: handle mismatching types. http://anglebug.com/6000
NodeData tempVarData;
nodeDataInitLValue(&tempVarData, tempVarIds[paramIndex], tempVarTypeIds[paramIndex],
spv::StorageClassFunction, EbsUnspecified);
const spirv::IdRef tempVarValue = accessChainLoad(&tempVarData);
accessChainStore(&param, tempVarValue);
}
return result;
}
void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node) void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node)
{ {
// Constants are expected to be folded. // Constants are expected to be folded.
...@@ -1195,10 +1393,21 @@ void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node) ...@@ -1195,10 +1393,21 @@ void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node)
} }
} }
spirv::IdRef typeId = mBuilder.getTypeData(type, blockStorage).id; const spirv::IdRef typeId = mBuilder.getTypeData(type, blockStorage).id;
nodeDataInitLValue(&mNodeData.back(), mSymbolIdMap[symbol], typeId, GetStorageClass(type), // If the symbol is a const variable, such as a const function parameter, create an rvalue.
blockStorage); if (type.getQualifier() == EvqConst)
{
ASSERT(mSymbolIdMap.count(symbol) > 0);
nodeDataInitRValue(&mNodeData.back(), mSymbolIdMap[symbol], typeId);
return;
}
// Otherwise create an lvalue.
spv::StorageClass storageClass;
const spirv::IdRef symbolId = getSymbolIdAndStorageClass(symbol, type, &storageClass);
nodeDataInitLValue(&mNodeData.back(), symbolId, typeId, storageClass, blockStorage);
// If a field of a nameless interface block, create an access chain. // If a field of a nameless interface block, create an access chain.
if (interfaceBlock && !type.isInterfaceBlock()) if (interfaceBlock && !type.isInterfaceBlock())
...@@ -1338,20 +1547,24 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node) ...@@ -1338,20 +1547,24 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node)
ASSERT(mNodeData.size() >= 2); ASSERT(mNodeData.size() >= 2);
// Load the result of the right node right away. // Load the result of the right node right away.
const spirv::IdRef rightTypeId = getAccessChainTypeId(&mNodeData.back()); spirv::IdRef typeId = getAccessChainTypeId(&mNodeData.back());
const spirv::IdRef rightValue = accessChainLoad(&mNodeData.back()); spirv::IdRef rightValue = accessChainLoad(&mNodeData.back());
mNodeData.pop_back(); mNodeData.pop_back();
// For EOpIndex* operations, push the right value as an index to the left value's access chain. // For EOpIndex* operations, push the right value as an index to the left value's access chain.
// For the other operations, evaluate the expression. // For the other operations, evaluate the expression.
NodeData &left = mNodeData.back(); NodeData &left = mNodeData.back();
spirv::IdRef typeId;
const TBasicType leftBasicType = node->getLeft()->getType().getBasicType(); const TBasicType leftBasicType = node->getLeft()->getType().getBasicType();
const bool isFloat = leftBasicType == EbtFloat || leftBasicType == EbtDouble; const bool isFloat = leftBasicType == EbtFloat || leftBasicType == EbtDouble;
const bool isUnsigned = leftBasicType == EbtUInt; const bool isUnsigned = leftBasicType == EbtUInt;
const bool isBool = leftBasicType == EbtBool; const bool isBool = leftBasicType == EbtBool;
// Whether the operands need to be swapped in the instruction
bool swapOperands = false;
// Whether the scalar operand needs to be extended to match the other operand which is a vector.
bool extendScalarToVector = true;
using WriteBinaryOp = using WriteBinaryOp =
void (*)(spirv::Blob * blob, spirv::IdResultType idResultType, spirv::IdResult idResult, void (*)(spirv::Blob * blob, spirv::IdResultType idResultType, spirv::IdResult idResult,
spirv::IdRef operand1, spirv::IdRef operand2); spirv::IdRef operand1, spirv::IdRef operand2);
...@@ -1380,7 +1593,74 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node) ...@@ -1380,7 +1593,74 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node)
// 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|.
accessChainStore(&left, rightValue); accessChainStore(&left, rightValue);
nodeDataInitRValue(&left, rightValue, rightTypeId); nodeDataInitRValue(&left, rightValue, typeId);
return true;
case EOpAdd:
case EOpAddAssign:
if (isFloat)
writeBinaryOp = spirv::WriteFAdd;
else
writeBinaryOp = spirv::WriteIAdd;
break;
case EOpSub:
case EOpSubAssign:
if (isFloat)
writeBinaryOp = spirv::WriteFSub;
else
writeBinaryOp = spirv::WriteISub;
break;
case EOpMul:
case EOpMulAssign:
if (isFloat)
writeBinaryOp = spirv::WriteFMul;
else
writeBinaryOp = spirv::WriteIMul;
break;
case EOpDiv:
case EOpDivAssign:
if (isFloat)
writeBinaryOp = spirv::WriteFDiv;
else if (isUnsigned)
writeBinaryOp = spirv::WriteUDiv;
else
writeBinaryOp = spirv::WriteSDiv;
break;
case EOpIMod:
case EOpIModAssign:
if (isFloat)
writeBinaryOp = spirv::WriteFMod;
else if (isUnsigned)
writeBinaryOp = spirv::WriteUMod;
else
writeBinaryOp = spirv::WriteSMod;
break;
case EOpVectorTimesScalar:
case EOpVectorTimesScalarAssign:
if (isFloat)
{
writeBinaryOp = spirv::WriteVectorTimesScalar;
swapOperands = node->getRight()->getType().isVector();
extendScalarToVector = false;
}
else
writeBinaryOp = spirv::WriteIMul;
break;
case EOpVectorTimesMatrix:
case EOpVectorTimesMatrixAssign:
writeBinaryOp = spirv::WriteVectorTimesMatrix;
break;
case EOpMatrixTimesVector:
writeBinaryOp = spirv::WriteMatrixTimesVector;
break;
case EOpMatrixTimesScalar:
case EOpMatrixTimesScalarAssign:
writeBinaryOp = spirv::WriteMatrixTimesScalar;
break;
case EOpMatrixTimesMatrix:
case EOpMatrixTimesMatrixAssign:
writeBinaryOp = spirv::WriteMatrixTimesMatrix;
break; break;
case EOpEqual: case EOpEqual:
...@@ -1445,18 +1725,47 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node) ...@@ -1445,18 +1725,47 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node)
if (writeBinaryOp) if (writeBinaryOp)
{ {
// Load the left value. // Load the left value.
const spirv::IdRef leftValue = accessChainLoad(&left); spirv::IdRef leftValue = accessChainLoad(&left);
ASSERT(!typeId.valid());
typeId = mBuilder.getTypeData(node->getType(), EbsUnspecified).id; typeId = mBuilder.getTypeData(node->getType(), EbsUnspecified).id;
// For vector<op>scalar operations that require it, turn the scalar into a vector of the
// same size.
if (extendScalarToVector)
{
const TType &leftType = node->getLeft()->getType();
const TType &rightType = node->getRight()->getType();
if (leftType.isScalar() && rightType.isVector())
{
leftValue = createConstructorVectorFromScalar(rightType, typeId, {{leftValue}});
}
else if (rightType.isScalar() && leftType.isVector())
{
rightValue = createConstructorVectorFromScalar(leftType, typeId, {{rightValue}});
}
}
if (swapOperands)
{
std::swap(leftValue, rightValue);
}
// Write the operation that combines the left and right values. // Write the operation that combines the left and right values.
spirv::IdRef result = mBuilder.getNewId(); spirv::IdRef result = mBuilder.getNewId();
writeBinaryOp(mBuilder.getSpirvCurrentFunctionBlock(), typeId, result, leftValue, writeBinaryOp(mBuilder.getSpirvCurrentFunctionBlock(), typeId, result, leftValue,
rightValue); rightValue);
// If it's an assignment, store the calculated value.
if (IsAssignment(node->getOp()))
{
accessChainStore(&left, result);
}
// Replace the access chain with an rvalue that's the result. // Replace the access chain with an rvalue that's the result.
nodeDataInitRValue(&left, result, typeId); nodeDataInitRValue(&left, result, typeId);
// TODO: Handle NoContraction decoration. http://anglebug.com/4889
} }
return true; return true;
...@@ -1604,8 +1913,18 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD ...@@ -1604,8 +1913,18 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD
spirv::IdRefList paramTypeIds; spirv::IdRefList paramTypeIds;
for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex) for (size_t paramIndex = 0; paramIndex < function->getParamCount(); ++paramIndex)
{ {
paramTypeIds.push_back( const TType &paramType = function->getParam(paramIndex)->getType();
mBuilder.getTypeData(function->getParam(paramIndex)->getType(), EbsUnspecified).id);
spirv::IdRef paramId = mBuilder.getTypeData(paramType, EbsUnspecified).id;
// const function parameters are intermediate values, while the rest are "variables"
// with the Function storage class.
if (paramType.getQualifier() != EvqConst)
{
paramId = mBuilder.getTypePointerId(paramId, spv::StorageClassFunction);
}
paramTypeIds.push_back(paramId);
} }
const spirv::IdRef functionTypeId = mBuilder.getFunctionTypeId(returnTypeId, paramTypeIds); const spirv::IdRef functionTypeId = mBuilder.getFunctionTypeId(returnTypeId, paramTypeIds);
...@@ -1621,8 +1940,10 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD ...@@ -1621,8 +1940,10 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD
spirv::WriteFunctionParameter(mBuilder.getSpirvFunctions(), paramTypeIds[paramIndex], spirv::WriteFunctionParameter(mBuilder.getSpirvFunctions(), paramTypeIds[paramIndex],
paramId); paramId);
// TODO: Add to TVariable to variableId map so references to this variable can discover // Remember the id of the variable for future look up.
// the ID. http://anglebug.com/4889 const TVariable *paramVariable = function->getParam(paramIndex);
ASSERT(mSymbolIdMap.count(paramVariable) == 0);
mSymbolIdMap[paramVariable] = paramId;
} }
// Remember the ID of main() for the sake of OpEntryPoint. // Remember the ID of main() for the sake of OpEntryPoint.
...@@ -1631,7 +1952,11 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD ...@@ -1631,7 +1952,11 @@ bool OutputSPIRVTraverser::visitFunctionDefinition(Visit visit, TIntermFunctionD
mBuilder.setEntryPointId(functionId); mBuilder.setEntryPointId(functionId);
} }
mBuilder.startNewFunction(); mBuilder.startNewFunction(functionId, mBuilder.hashFunctionName(function).data());
// Remember the id of the function for future look up.
ASSERT(mSymbolIdMap.count(function) == 0);
mSymbolIdMap[function] = functionId;
return true; return true;
} }
...@@ -1690,36 +2015,35 @@ bool OutputSPIRVTraverser::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -1690,36 +2015,35 @@ bool OutputSPIRVTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
} }
// Expect to have accumulated as many parameters as the node requires. // Expect to have accumulated as many parameters as the node requires.
size_t parameterCount = node->getChildCount(); ASSERT(mNodeData.size() > node->getChildCount());
ASSERT(mNodeData.size() > parameterCount);
const spirv::IdRef typeId = mBuilder.getTypeData(node->getType(), EbsUnspecified).id; const spirv::IdRef typeId = mBuilder.getTypeData(node->getType(), EbsUnspecified).id;
spirv::IdRef result;
if (node->isConstructor()) switch (node->getOp())
{ {
// Construct a value out of the accumulated parameters. case EOpConstruct:
spirv::IdRefList parameters; // Construct a value out of the accumulated parameters.
for (size_t paramIndex = 0; paramIndex < parameterCount; ++paramIndex) result = createConstructor(node, typeId);
{ break;
// Take each constructor argument that is visited and evaluate it as rvalue case EOpCallFunctionInAST:
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex]; // Create a call to the function.
result = createFunctionCall(node, typeId);
const spirv::IdRef paramValue = accessChainLoad(&param); break;
default:
// TODO: handle mismatching types. http://anglebug.com/6000 // TODO: Built-in functions. http://anglebug.com/4889
UNIMPLEMENTED();
}
parameters.push_back(paramValue); // Pop the parameters.
} mNodeData.resize(mNodeData.size() - node->getChildCount());
mNodeData.resize(mNodeData.size() - parameterCount);
const spirv::IdRef result = createConstructor(node, typeId, parameters); // If the function has a return value, take the return value as the result.
if (node->getType().getBasicType() != EbtVoid)
{
nodeDataInitRValue(&mNodeData.back(), result, typeId); nodeDataInitRValue(&mNodeData.back(), result, typeId);
return true;
} }
// TODO: http://anglebug.com/4889
UNIMPLEMENTED();
return false; return false;
} }
...@@ -1817,8 +2141,52 @@ bool OutputSPIRVTraverser::visitLoop(Visit visit, TIntermLoop *node) ...@@ -1817,8 +2141,52 @@ bool OutputSPIRVTraverser::visitLoop(Visit visit, TIntermLoop *node)
bool OutputSPIRVTraverser::visitBranch(Visit visit, TIntermBranch *node) bool OutputSPIRVTraverser::visitBranch(Visit visit, TIntermBranch *node)
{ {
// TODO: http://anglebug.com/4889 if (visit == PreVisit)
UNIMPLEMENTED(); {
mNodeData.emplace_back();
return true;
}
// There is only ever one child at most.
ASSERT(visit != InVisit);
switch (node->getFlowOp())
{
case EOpKill:
// TODO: http://anglebug.com/4889
UNIMPLEMENTED();
break;
case EOpBreak:
// TODO: http://anglebug.com/4889
UNIMPLEMENTED();
break;
case EOpContinue:
// TODO: http://anglebug.com/4889
UNIMPLEMENTED();
break;
case EOpReturn:
// Evaluate the expression if any, and return.
if (node->getExpression() != nullptr)
{
ASSERT(mNodeData.size() >= 1);
const spirv::IdRef expressionValue = accessChainLoad(&mNodeData.back());
mNodeData.pop_back();
// TODO: handle mismatching types. http://anglebug.com/6000
spirv::WriteReturnValue(mBuilder.getSpirvCurrentFunctionBlock(), expressionValue);
mBuilder.terminateCurrentFunctionBlock();
}
else
{
spirv::WriteReturn(mBuilder.getSpirvCurrentFunctionBlock());
mBuilder.terminateCurrentFunctionBlock();
}
break;
default:
UNREACHABLE();
}
return true; return true;
} }
......
...@@ -390,9 +390,11 @@ ANGLE_NO_DISCARD bool AddXfbEmulationSupport(TCompiler *compiler, ...@@ -390,9 +390,11 @@ ANGLE_NO_DISCARD bool AddXfbEmulationSupport(TCompiler *compiler,
constexpr uint32_t kMaxXfbBuffers = 4; constexpr uint32_t kMaxXfbBuffers = 4;
const TType *ivec4Type = StaticType::GetBasic<EbtInt, kMaxXfbBuffers>(); const TType *ivec4Type = StaticType::GetBasic<EbtInt, kMaxXfbBuffers>();
TType *stridesType = new TType(*ivec4Type);
stridesType->setQualifier(EvqConst);
// Create the parameter variable. // Create the parameter variable.
TVariable *stridesVar = new TVariable(symbolTable, ImmutableString("strides"), ivec4Type, TVariable *stridesVar = new TVariable(symbolTable, ImmutableString("strides"), stridesType,
SymbolType::AngleInternal); SymbolType::AngleInternal);
TIntermSymbol *stridesSymbol = new TIntermSymbol(stridesVar); TIntermSymbol *stridesSymbol = new TIntermSymbol(stridesVar);
...@@ -479,10 +481,8 @@ ANGLE_NO_DISCARD bool AddXfbEmulationSupport(TCompiler *compiler, ...@@ -479,10 +481,8 @@ ANGLE_NO_DISCARD bool AddXfbEmulationSupport(TCompiler *compiler,
// Create a call to ANGLEGetXfbOffsets too, for the sole purpose of preventing it from being // Create a call to ANGLEGetXfbOffsets too, for the sole purpose of preventing it from being
// culled as unused by glslang. // culled as unused by glslang.
TIntermSequence zero;
zero.push_back(CreateIndexNode(0));
TIntermSequence ivec4Zero; TIntermSequence ivec4Zero;
ivec4Zero.push_back(TIntermAggregate::CreateConstructor(*ivec4Type, &zero)); ivec4Zero.push_back(CreateZeroNode(*ivec4Type));
TIntermAggregate *getOffsetsCall = TIntermAggregate *getOffsetsCall =
TIntermAggregate::CreateFunctionCall(*getOffsetsFunction, &ivec4Zero); TIntermAggregate::CreateFunctionCall(*getOffsetsFunction, &ivec4Zero);
captureXfbBlock->appendStatement(getOffsetsCall); captureXfbBlock->appendStatement(getOffsetsCall);
...@@ -1340,8 +1340,7 @@ bool TranslatorVulkan::translate(TIntermBlock *root, ...@@ -1340,8 +1340,7 @@ bool TranslatorVulkan::translate(TIntermBlock *root,
} }
#if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION) #if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION)
constexpr ShCompileOptions kUnsupportedTransformations = constexpr ShCompileOptions kUnsupportedTransformations = SH_ADD_BRESENHAM_LINE_RASTER_EMULATION;
SH_ADD_VULKAN_XFB_EMULATION_SUPPORT_CODE | SH_ADD_BRESENHAM_LINE_RASTER_EMULATION;
if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 && getShaderType() == GL_VERTEX_SHADER && if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 && getShaderType() == GL_VERTEX_SHADER &&
(compileOptions & kUnsupportedTransformations) == 0) (compileOptions & kUnsupportedTransformations) == 0)
{ {
......
...@@ -1046,6 +1046,7 @@ class SpirvIDDiscoverer final : angle::NonCopyable ...@@ -1046,6 +1046,7 @@ class SpirvIDDiscoverer final : angle::NonCopyable
spirv::IdRef vec4Id() const { return mVec4Id; } spirv::IdRef vec4Id() const { return mVec4Id; }
spirv::IdRef vec4OutTypePointerId() const { return mVec4OutTypePointerId; } spirv::IdRef vec4OutTypePointerId() const { return mVec4OutTypePointerId; }
spirv::IdRef intId() const { return mIntId; } spirv::IdRef intId() const { return mIntId; }
spirv::IdRef ivec4Id() const { return mIvec4Id; }
spirv::IdRef uintId() const { return mUintId; } spirv::IdRef uintId() const { return mUintId; }
spirv::IdRef int0Id() const { return mInt0Id; } spirv::IdRef int0Id() const { return mInt0Id; }
spirv::IdRef floatHalfId() const { return mFloatHalfId; } spirv::IdRef floatHalfId() const { return mFloatHalfId; }
...@@ -1082,11 +1083,12 @@ class SpirvIDDiscoverer final : angle::NonCopyable ...@@ -1082,11 +1083,12 @@ class SpirvIDDiscoverer final : angle::NonCopyable
// and swizzles. // and swizzles.
// //
// - mFloatId: id of OpTypeFloat 32 // - mFloatId: id of OpTypeFloat 32
// - mVec4Id: id of OpTypeVector %mFloatID 4 // - mVec4Id: id of OpTypeVector %mFloatId 4
// - mVec4OutTypePointerId: id of OpTypePointer Output %mVec4ID // - mVec4OutTypePointerId: id of OpTypePointer Output %mVec4Id
// - mIntId: id of OpTypeInt 32 1 // - mIntId: id of OpTypeInt 32 1
// - mIvecId: id of OpTypeVector %mIntId 4
// - mUintId: id of OpTypeInt 32 0 // - mUintId: id of OpTypeInt 32 0
// - mInt0Id: id of OpConstant %mIntID 0 // - mInt0Id: id of OpConstant %mIntId 0
// - mFloatHalfId: id of OpConstant %mFloatId 0.5f // - mFloatHalfId: id of OpConstant %mFloatId 0.5f
// - mOutputPerVertexTypePointerId: id of OpTypePointer Output %mOutputPerVertex.typeId // - mOutputPerVertexTypePointerId: id of OpTypePointer Output %mOutputPerVertex.typeId
// - mOutputPerVertexId: id of OpVariable %mOutputPerVertexTypePointerId Output // - mOutputPerVertexId: id of OpVariable %mOutputPerVertexTypePointerId Output
...@@ -1095,6 +1097,7 @@ class SpirvIDDiscoverer final : angle::NonCopyable ...@@ -1095,6 +1097,7 @@ class SpirvIDDiscoverer final : angle::NonCopyable
spirv::IdRef mVec4Id; spirv::IdRef mVec4Id;
spirv::IdRef mVec4OutTypePointerId; spirv::IdRef mVec4OutTypePointerId;
spirv::IdRef mIntId; spirv::IdRef mIntId;
spirv::IdRef mIvec4Id;
spirv::IdRef mUintId; spirv::IdRef mUintId;
spirv::IdRef mInt0Id; spirv::IdRef mInt0Id;
spirv::IdRef mFloatHalfId; spirv::IdRef mFloatHalfId;
...@@ -1266,12 +1269,17 @@ void SpirvIDDiscoverer::visitTypeVector(spirv::IdResult id, ...@@ -1266,12 +1269,17 @@ void SpirvIDDiscoverer::visitTypeVector(spirv::IdResult id,
spirv::IdRef componentId, spirv::IdRef componentId,
spirv::LiteralInteger componentCount) spirv::LiteralInteger componentCount)
{ {
// Only interested in OpTypeVector %mFloatId 4 // Only interested in OpTypeVector %mFloatId 4 and OpTypeVector %mIntId 4
if (componentId == mFloatId && componentCount == 4) if (componentId == mFloatId && componentCount == 4)
{ {
ASSERT(!mVec4Id.valid()); ASSERT(!mVec4Id.valid());
mVec4Id = id; mVec4Id = id;
} }
if (componentId == mIntId && componentCount == 4)
{
ASSERT(!mIvec4Id.valid());
mIvec4Id = id;
}
} }
SpirvVariableType SpirvIDDiscoverer::visitVariable(spirv::IdResultType typeId, SpirvVariableType SpirvIDDiscoverer::visitVariable(spirv::IdResultType typeId,
...@@ -1350,6 +1358,12 @@ void SpirvIDDiscoverer::writePendingDeclarations(spirv::Blob *blobOut) ...@@ -1350,6 +1358,12 @@ void SpirvIDDiscoverer::writePendingDeclarations(spirv::Blob *blobOut)
spirv::WriteTypeInt(blobOut, mIntId, spirv::LiteralInteger(32), spirv::LiteralInteger(1)); spirv::WriteTypeInt(blobOut, mIntId, spirv::LiteralInteger(32), spirv::LiteralInteger(1));
} }
if (!mIvec4Id.valid())
{
mIvec4Id = SpirvTransformerBase::GetNewId(blobOut);
spirv::WriteTypeVector(blobOut, mIvec4Id, mIntId, spirv::LiteralInteger(4));
}
ASSERT(!mInt0Id.valid()); ASSERT(!mInt0Id.valid());
mInt0Id = SpirvTransformerBase::GetNewId(blobOut); mInt0Id = SpirvTransformerBase::GetNewId(blobOut);
spirv::WriteConstant(blobOut, mIntId, mInt0Id, spirv::LiteralContextDependentNumber(0)); spirv::WriteConstant(blobOut, mIntId, mInt0Id, spirv::LiteralContextDependentNumber(0));
...@@ -2149,6 +2163,13 @@ void SpirvTransformFeedbackCodeGenerator::writePendingDeclarations( ...@@ -2149,6 +2163,13 @@ void SpirvTransformFeedbackCodeGenerator::writePendingDeclarations(
spirv::WriteTypePointer(blobOut, mFloatUniformPointerId, spv::StorageClassUniform, spirv::WriteTypePointer(blobOut, mFloatUniformPointerId, spv::StorageClassUniform,
ids.floatId()); ids.floatId());
if (!mIVec4FuncPointerId.valid())
{
mIVec4FuncPointerId = SpirvTransformerBase::GetNewId(blobOut);
spirv::WriteTypePointer(blobOut, mIVec4FuncPointerId, spv::StorageClassFunction,
ids.ivec4Id());
}
mIntNIds.resize(4); mIntNIds.resize(4);
mIntNIds[0] = ids.int0Id(); mIntNIds[0] = ids.int0Id();
for (int n = 1; n < 4; ++n) for (int n = 1; n < 4; ++n)
...@@ -2282,7 +2303,6 @@ void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackEmulationOutput( ...@@ -2282,7 +2303,6 @@ void SpirvTransformFeedbackCodeGenerator::writeTransformFeedbackEmulationOutput(
// //
// - For the initial offsets calculation: // - For the initial offsets calculation:
// //
// %getOffsetsParam = OpVariable %mIVec4FuncPointerId Function %stridesComposite
// %xfbOffsetsResult = OpFunctionCall %ivec4 %ANGLEGetXfbOffsets %stridesComposite // %xfbOffsetsResult = OpFunctionCall %ivec4 %ANGLEGetXfbOffsets %stridesComposite
// %xfbOffsetsVar = OpVariable %mIVec4FuncPointerId Function // %xfbOffsetsVar = OpVariable %mIVec4FuncPointerId Function
// OpStore %xfbOffsetsVar %xfbOffsetsResult // OpStore %xfbOffsetsVar %xfbOffsetsResult
...@@ -2462,7 +2482,6 @@ void SpirvTransformFeedbackCodeGenerator::getVaryingTypeIds(const SpirvIDDiscove ...@@ -2462,7 +2482,6 @@ void SpirvTransformFeedbackCodeGenerator::getVaryingTypeIds(const SpirvIDDiscove
void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOffsets, void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOffsets,
spirv::Blob *blobOut) spirv::Blob *blobOut)
{ {
const spirv::IdRef xfbGetOffsetsParam(SpirvTransformerBase::GetNewId(blobOut));
const spirv::IdRef xfbOffsetsResult(SpirvTransformerBase::GetNewId(blobOut)); const spirv::IdRef xfbOffsetsResult(SpirvTransformerBase::GetNewId(blobOut));
const spirv::IdRef xfbOffsetsVar(SpirvTransformerBase::GetNewId(blobOut)); const spirv::IdRef xfbOffsetsVar(SpirvTransformerBase::GetNewId(blobOut));
...@@ -2470,17 +2489,13 @@ void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOf ...@@ -2470,17 +2489,13 @@ void SpirvTransformFeedbackCodeGenerator::writeGetOffsetsCall(spirv::IdRef xfbOf
// //
// ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3)); // ivec4 xfbOffsets = ANGLEGetXfbOffsets(ivec4(stride0, stride1, stride2, stride3));
// Create a variable to hold the parameter, initialized with the constant ivec4 containing the
// strides.
spirv::WriteVariable(blobOut, mIVec4FuncPointerId, xfbGetOffsetsParam,
spv::StorageClassFunction, &mBufferStridesCompositeId);
// Create a variable to hold the result. // Create a variable to hold the result.
spirv::WriteVariable(blobOut, mIVec4FuncPointerId, xfbOffsetsVar, spv::StorageClassFunction, spirv::WriteVariable(blobOut, mIVec4FuncPointerId, xfbOffsetsVar, spv::StorageClassFunction,
nullptr); nullptr);
// Call a helper function generated by the translator to calculate the offsets for the current // Call a helper function generated by the translator to calculate the offsets for the current
// vertex. // vertex.
spirv::WriteFunctionCall(blobOut, mIVec4Id, xfbOffsetsResult, mGetXfbOffsetsFuncId, spirv::WriteFunctionCall(blobOut, mIVec4Id, xfbOffsetsResult, mGetXfbOffsetsFuncId,
{xfbGetOffsetsParam}); {mBufferStridesCompositeId});
// Store the results. // Store the results.
spirv::WriteStore(blobOut, xfbOffsetsVar, xfbOffsetsResult, nullptr); spirv::WriteStore(blobOut, xfbOffsetsVar, xfbOffsetsResult, nullptr);
// Load from the variable for use in expressions. // Load from the variable for use in expressions.
......
...@@ -64,8 +64,8 @@ ...@@ -64,8 +64,8 @@
5981 PIXEL4ORXL GLES : Texture3DTestES2.Luminance/* = SKIP 5981 PIXEL4ORXL GLES : Texture3DTestES2.Luminance/* = SKIP
5981 PIXEL4ORXL GLES : Texture3DTestES2.RGBA/* = SKIP 5981 PIXEL4ORXL GLES : Texture3DTestES2.RGBA/* = SKIP
5981 PIXEL4ORXL GLES : TextureBufferTestES31.UseAsUBOThenUpdateThenAsTextureBuffer/* = SKIP 5981 PIXEL4ORXL GLES : TextureBufferTestES31.UseAsUBOThenUpdateThenAsTextureBuffer/* = SKIP
5981 PIXEL4ORXL GLES : TransformFeedbackTestES31.IOBlocksInterleaved/* = SKIP 5981 PIXEL4ORXL GLES : TransformFeedbackTestIOBlocks.Interleaved/* = SKIP
5981 PIXEL4ORXL GLES : TransformFeedbackTestES31.IOBlocksSeparate/* = SKIP 5981 PIXEL4ORXL GLES : TransformFeedbackTestIOBlocks.Separate/* = SKIP
5981 PIXEL4ORXL GLES : VulkanExternalImageTest.ShouldClearOpaqueFdRGBA8/* = SKIP 5981 PIXEL4ORXL GLES : VulkanExternalImageTest.ShouldClearOpaqueFdRGBA8/* = SKIP
5981 PIXEL4ORXL GLES : VulkanExternalImageTest.TextureFormatCompatChromiumFd/* = SKIP 5981 PIXEL4ORXL GLES : VulkanExternalImageTest.TextureFormatCompatChromiumFd/* = SKIP
...@@ -98,4 +98,4 @@ ...@@ -98,4 +98,4 @@
5981 PIXEL4ORXL VULKAN : Texture2DArrayCopy.UnsizedFormats/* = SKIP 5981 PIXEL4ORXL VULKAN : Texture2DArrayCopy.UnsizedFormats/* = SKIP
5981 PIXEL4ORXL VULKAN : Texture3DCopy.UnsignedByteFormats/* = SKIP 5981 PIXEL4ORXL VULKAN : Texture3DCopy.UnsignedByteFormats/* = SKIP
5981 PIXEL4ORXL VULKAN : Texture3DCopy.UnsizedFormats/* = SKIP 5981 PIXEL4ORXL VULKAN : Texture3DCopy.UnsizedFormats/* = SKIP
5981 PIXEL4ORXL VULKAN : TransformFeedbackTestES31.IOBlocksInterleaved/* = SKIP 5981 PIXEL4ORXL VULKAN : TransformFeedbackTestIOBlocks.Interleaved/* = SKIP
...@@ -3052,9 +3052,12 @@ TEST_P(TransformFeedbackTestES32, PrimitivesWrittenAndGenerated) ...@@ -3052,9 +3052,12 @@ TEST_P(TransformFeedbackTestES32, PrimitivesWrittenAndGenerated)
} }
} }
class TransformFeedbackTestIOBlocks : public TransformFeedbackTestES31
{};
// Verify that capture of I/O block fields works, both when the instance name is specified and when // Verify that capture of I/O block fields works, both when the instance name is specified and when
// not. This test uses interleaved components. // not. This test uses interleaved components.
TEST_P(TransformFeedbackTestES31, IOBlocksInterleaved) TEST_P(TransformFeedbackTestIOBlocks, Interleaved)
{ {
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks"));
...@@ -3161,7 +3164,7 @@ void main() ...@@ -3161,7 +3164,7 @@ void main()
} }
// Verify that capture of I/O block fields works. This test uses separate components. // Verify that capture of I/O block fields works. This test uses separate components.
TEST_P(TransformFeedbackTestES31, IOBlocksSeparate) TEST_P(TransformFeedbackTestIOBlocks, Separate)
{ {
ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks")); ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_shader_io_blocks"));
...@@ -3305,7 +3308,11 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackLifetimeTest); ...@@ -3305,7 +3308,11 @@ GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackLifetimeTest);
ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackLifetimeTest); ANGLE_INSTANTIATE_TEST_ES3(TransformFeedbackLifetimeTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTestES31); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTestES31);
ANGLE_INSTANTIATE_TEST_ES31(TransformFeedbackTestES31); ANGLE_INSTANTIATE_TEST_ES31_AND(TransformFeedbackTestES31,
WithDirectSPIRVGeneration(ES31_VULKAN()));
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTestIOBlocks);
ANGLE_INSTANTIATE_TEST_ES31(TransformFeedbackTestIOBlocks);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTestES32); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TransformFeedbackTestES32);
ANGLE_INSTANTIATE_TEST_ES32(TransformFeedbackTestES32); ANGLE_INSTANTIATE_TEST_ES32(TransformFeedbackTestES32);
......
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