Commit 6746e5dd by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: SPIR-V Gen: Basic support for compute shaders

This change enables direct SPIR-V generation for compute shaders and enables a few tests. A handful of built-in functions are translated to support said tests. Bug: angleproject:4889 Change-Id: I8fd6dc50ff31559a738ba680a993fb197e29fcf9 Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2939330 Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org> Reviewed-by: 's avatarJamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarTim Van Patten <timvp@google.com>
parent 1ab6d214
...@@ -180,7 +180,7 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const char *block ...@@ -180,7 +180,7 @@ SpirvTypeData SPIRVBuilder::declareType(const SpirvType &type, const char *block
subType.blockStorage = EbsUnspecified; subType.blockStorage = EbsUnspecified;
} }
const spirv::IdRef subTypeId = getSpirvTypeData(subType, "").id; const spirv::IdRef subTypeId = getSpirvTypeData(subType, blockName).id;
const unsigned int length = type.arraySizes.back(); const unsigned int length = type.arraySizes.back();
typeId = getNewId(); typeId = getNewId();
...@@ -929,11 +929,6 @@ void SPIRVBuilder::addCapability(spv::Capability capability) ...@@ -929,11 +929,6 @@ void SPIRVBuilder::addCapability(spv::Capability capability)
mCapabilities.insert(capability); mCapabilities.insert(capability);
} }
void SPIRVBuilder::addExecutionMode(spv::ExecutionMode executionMode)
{
mExecutionModes.insert(executionMode);
}
void SPIRVBuilder::setEntryPointId(spirv::IdRef id) void SPIRVBuilder::setEntryPointId(spirv::IdRef id)
{ {
ASSERT(!mEntryPointId.valid()); ASSERT(!mEntryPointId.valid());
...@@ -1292,15 +1287,14 @@ spirv::Blob SPIRVBuilder::getSpirv() ...@@ -1292,15 +1287,14 @@ spirv::Blob SPIRVBuilder::getSpirv()
// //
// 5 for header + // 5 for header +
// a number of capabilities + // a number of capabilities +
// a number of execution modes +
// size of already generated instructions. // size of already generated instructions.
// //
// The actual size is larger due to other metadata instructions such as extensions, // The actual size is larger due to other metadata instructions such as extensions,
// OpExtInstImport, OpEntryPoint etc. // OpExtInstImport, OpEntryPoint, OpExecutionMode etc.
result.reserve(5 + mCapabilities.size() * 2 + mExecutionModes.size() * 3 + mSpirvDebug.size() + result.reserve(5 + mCapabilities.size() * 2 + mSpirvDebug.size() + mSpirvDecorations.size() +
mSpirvDecorations.size() + mSpirvTypeAndConstantDecls.size() + mSpirvTypeAndConstantDecls.size() + mSpirvTypePointerDecls.size() +
mSpirvTypePointerDecls.size() + mSpirvFunctionTypeDecls.size() + mSpirvFunctionTypeDecls.size() + mSpirvVariableDecls.size() +
mSpirvVariableDecls.size() + mSpirvFunctions.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,11 +1332,7 @@ spirv::Blob SPIRVBuilder::getSpirv() ...@@ -1338,11 +1332,7 @@ spirv::Blob SPIRVBuilder::getSpirv()
mEntryPointInterfaceList); mEntryPointInterfaceList);
// - OpExecutionMode instructions // - OpExecutionMode instructions
for (spv::ExecutionMode executionMode : mExecutionModes) generateExecutionModes(&result);
{
spirv::WriteExecutionMode(&result, mEntryPointId, executionMode, {});
}
result.insert(result.end(), mSpirvExecutionModes.begin(), mSpirvExecutionModes.end());
// - OpSource instruction. // - OpSource instruction.
// //
...@@ -1364,4 +1354,23 @@ spirv::Blob SPIRVBuilder::getSpirv() ...@@ -1364,4 +1354,23 @@ spirv::Blob SPIRVBuilder::getSpirv()
return result; return result;
} }
void SPIRVBuilder::generateExecutionModes(spirv::Blob *blob)
{
switch (mShaderType)
{
case gl::ShaderType::Compute:
{
const sh::WorkGroupSize &localSize = mCompiler->getComputeShaderLocalSize();
spirv::WriteExecutionMode(
blob, mEntryPointId, spv::ExecutionModeLocalSize,
{spirv::LiteralInteger(localSize[0]), spirv::LiteralInteger(localSize[1]),
spirv::LiteralInteger(localSize[2])});
break;
}
default:
// TODO: other shader types. http://anglebug.com/4889
break;
}
}
} // namespace sh } // namespace sh
...@@ -211,8 +211,13 @@ struct SpirvConditional ...@@ -211,8 +211,13 @@ struct SpirvConditional
class SPIRVBuilder : angle::NonCopyable class SPIRVBuilder : angle::NonCopyable
{ {
public: public:
SPIRVBuilder(gl::ShaderType shaderType, ShHashFunction64 hashFunction, NameMap &nameMap) SPIRVBuilder(TCompiler *compiler,
: mShaderType(shaderType), ShCompileOptions compileOptions,
ShHashFunction64 hashFunction,
NameMap &nameMap)
: mCompiler(compiler),
mCompileOptions(compileOptions),
mShaderType(gl::FromGLenum<gl::ShaderType>(compiler->getShaderType())),
mNextAvailableId(1), mNextAvailableId(1),
mHashFunction(hashFunction), mHashFunction(hashFunction),
mNameMap(nameMap), mNameMap(nameMap),
...@@ -228,7 +233,6 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -228,7 +233,6 @@ class SPIRVBuilder : angle::NonCopyable
spirv::IdRef getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass); spirv::IdRef getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass);
spirv::IdRef getFunctionTypeId(spirv::IdRef returnTypeId, const spirv::IdRefList &paramTypeIds); spirv::IdRef getFunctionTypeId(spirv::IdRef returnTypeId, const spirv::IdRefList &paramTypeIds);
spirv::Blob *getSpirvExecutionModes() { return &mSpirvExecutionModes; }
spirv::Blob *getSpirvDebug() { return &mSpirvDebug; } spirv::Blob *getSpirvDebug() { return &mSpirvDebug; }
spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; } spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; }
spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; } spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; }
...@@ -255,7 +259,6 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -255,7 +259,6 @@ class SPIRVBuilder : angle::NonCopyable
SpirvConditional *getCurrentConditional() { return &mConditionalStack.back(); } SpirvConditional *getCurrentConditional() { return &mConditionalStack.back(); }
void addCapability(spv::Capability capability); void addCapability(spv::Capability capability);
void addExecutionMode(spv::ExecutionMode executionMode);
void setEntryPointId(spirv::IdRef id); void setEntryPointId(spirv::IdRef id);
void addEntryPointInterfaceVariableId(spirv::IdRef id); void addEntryPointInterfaceVariableId(spirv::IdRef id);
void writePerVertexBuiltIns(const TType &type, spirv::IdRef typeId); void writePerVertexBuiltIns(const TType &type, spirv::IdRef typeId);
...@@ -321,17 +324,16 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -321,17 +324,16 @@ class SPIRVBuilder : angle::NonCopyable
uint32_t nextUnusedInputLocation(uint32_t consumedCount); uint32_t nextUnusedInputLocation(uint32_t consumedCount);
uint32_t nextUnusedOutputLocation(uint32_t consumedCount); uint32_t nextUnusedOutputLocation(uint32_t consumedCount);
void generateExecutionModes(spirv::Blob *blob);
ANGLE_MAYBE_UNUSED TCompiler *mCompiler;
ANGLE_MAYBE_UNUSED ShCompileOptions mCompileOptions;
gl::ShaderType mShaderType; gl::ShaderType mShaderType;
// Capabilities the shader is using. Accumulated as the instructions are generated. The Shader // Capabilities the shader is using. Accumulated as the instructions are generated. The Shader
// capability is unconditionally generated, so it's not tracked. // capability is unconditionally generated, so it's not tracked.
std::set<spv::Capability> mCapabilities; std::set<spv::Capability> mCapabilities;
// Execution modes the shader is enabling. Accumulated as the instructions are generated.
// Execution mode instructions that require a parameter are written to mSpirvExecutionModes as
// instructions; they are always generated once so don't benefit from being in a std::set.
std::set<spv::ExecutionMode> mExecutionModes;
// The list of interface variables and the id of main() populated as the instructions are // The list of interface variables and the id of main() populated as the instructions are
// generated. Used for the OpEntryPoint instruction. // generated. Used for the OpEntryPoint instruction.
spirv::IdRefList mEntryPointInterfaceList; spirv::IdRefList mEntryPointInterfaceList;
...@@ -348,7 +350,6 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -348,7 +350,6 @@ class SPIRVBuilder : angle::NonCopyable
// Various sections of SPIR-V. Each section grows as SPIR-V is generated, and the final result // Various sections of SPIR-V. Each section grows as SPIR-V is generated, and the final result
// is obtained by stitching the sections together. This puts the instructions in the order // is obtained by stitching the sections together. This puts the instructions in the order
// required by the spec. // required by the spec.
spirv::Blob mSpirvExecutionModes;
spirv::Blob mSpirvDebug; spirv::Blob mSpirvDebug;
spirv::Blob mSpirvDecorations; spirv::Blob mSpirvDecorations;
spirv::Blob mSpirvTypeAndConstantDecls; spirv::Blob mSpirvTypeAndConstantDecls;
......
...@@ -1598,64 +1598,4 @@ bool TCompiler::isVaryingDefined(const char *varyingName) ...@@ -1598,64 +1598,4 @@ bool TCompiler::isVaryingDefined(const char *varyingName)
return false; return false;
} }
void EmitEarlyFragmentTestsGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isEarlyFragmentTestsSpecified() || compiler.isEarlyFragmentTestsOptimized())
{
sink << "layout (early_fragment_tests) in;\n";
}
}
void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isComputeShaderLocalSizeDeclared())
{
const sh::WorkGroupSize &localSize = compiler.getComputeShaderLocalSize();
sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
<< ", local_size_z=" << localSize[2] << ") in;\n";
}
}
void EmitMultiviewGLSL(const TCompiler &compiler,
const ShCompileOptions &compileOptions,
const TExtension extension,
const TBehavior behavior,
TInfoSinkBase &sink)
{
ASSERT(behavior != EBhUndefined);
if (behavior == EBhDisable)
return;
const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER);
if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) != 0)
{
// Emit ARB_shader_viewport_layer_array/NV_viewport_array2 in a vertex shader if the
// SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the
// OVR_multiview(2) extension is requested.
if (isVertexShader && (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0)
{
sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n"
<< "#extension GL_ARB_shader_viewport_layer_array : require\n"
<< "#elif defined(GL_NV_viewport_array2)\n"
<< "#extension GL_NV_viewport_array2 : require\n"
<< "#endif\n";
}
}
else
{
sink << "#extension GL_OVR_multiview";
if (extension == TExtension::OVR_multiview2)
{
sink << "2";
}
sink << " : " << GetBehaviorString(behavior) << "\n";
const auto &numViews = compiler.getNumViews();
if (isVertexShader && numViews != -1)
{
sink << "layout(num_views=" << numViews << ") in;\n";
}
}
}
} // namespace sh } // namespace sh
...@@ -350,14 +350,6 @@ class TCompiler : public TShHandleBase ...@@ -350,14 +350,6 @@ class TCompiler : public TShHandleBase
TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output); TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
void DeleteCompiler(TCompiler *); void DeleteCompiler(TCompiler *);
void EmitEarlyFragmentTestsGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitWorkGroupSizeGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitMultiviewGLSL(const TCompiler &,
const ShCompileOptions &,
const TExtension,
const TBehavior,
TInfoSinkBase &sink);
} // namespace sh } // namespace sh
#endif // COMPILER_TRANSLATOR_COMPILER_H_ #endif // COMPILER_TRANSLATOR_COMPILER_H_
...@@ -1474,4 +1474,64 @@ bool NeedsToWriteLayoutQualifier(const TType &type) ...@@ -1474,4 +1474,64 @@ bool NeedsToWriteLayoutQualifier(const TType &type)
return false; return false;
} }
void EmitEarlyFragmentTestsGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isEarlyFragmentTestsSpecified() || compiler.isEarlyFragmentTestsOptimized())
{
sink << "layout (early_fragment_tests) in;\n";
}
}
void EmitWorkGroupSizeGLSL(const TCompiler &compiler, TInfoSinkBase &sink)
{
if (compiler.isComputeShaderLocalSizeDeclared())
{
const sh::WorkGroupSize &localSize = compiler.getComputeShaderLocalSize();
sink << "layout (local_size_x=" << localSize[0] << ", local_size_y=" << localSize[1]
<< ", local_size_z=" << localSize[2] << ") in;\n";
}
}
void EmitMultiviewGLSL(const TCompiler &compiler,
const ShCompileOptions &compileOptions,
const TExtension extension,
const TBehavior behavior,
TInfoSinkBase &sink)
{
ASSERT(behavior != EBhUndefined);
if (behavior == EBhDisable)
return;
const bool isVertexShader = (compiler.getShaderType() == GL_VERTEX_SHADER);
if ((compileOptions & SH_INITIALIZE_BUILTINS_FOR_INSTANCED_MULTIVIEW) != 0)
{
// Emit ARB_shader_viewport_layer_array/NV_viewport_array2 in a vertex shader if the
// SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER option is set and the
// OVR_multiview(2) extension is requested.
if (isVertexShader && (compileOptions & SH_SELECT_VIEW_IN_NV_GLSL_VERTEX_SHADER) != 0)
{
sink << "#if defined(GL_ARB_shader_viewport_layer_array)\n"
<< "#extension GL_ARB_shader_viewport_layer_array : require\n"
<< "#elif defined(GL_NV_viewport_array2)\n"
<< "#extension GL_NV_viewport_array2 : require\n"
<< "#endif\n";
}
}
else
{
sink << "#extension GL_OVR_multiview";
if (extension == TExtension::OVR_multiview2)
{
sink << "2";
}
sink << " : " << GetBehaviorString(behavior) << "\n";
const auto &numViews = compiler.getNumViews();
if (isVertexShader && numViews != -1)
{
sink << "layout(num_views=" << numViews << ") in;\n";
}
}
}
} // namespace sh } // namespace sh
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
#include <set> #include <set>
#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/HashNames.h" #include "compiler/translator/HashNames.h"
#include "compiler/translator/InfoSink.h" #include "compiler/translator/InfoSink.h"
#include "compiler/translator/tree_util/IntermTraverse.h" #include "compiler/translator/tree_util/IntermTraverse.h"
...@@ -132,6 +133,14 @@ void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out, ...@@ -132,6 +133,14 @@ void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out,
bool NeedsToWriteLayoutQualifier(const TType &type); bool NeedsToWriteLayoutQualifier(const TType &type);
void EmitEarlyFragmentTestsGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitWorkGroupSizeGLSL(const TCompiler &, TInfoSinkBase &sink);
void EmitMultiviewGLSL(const TCompiler &,
const ShCompileOptions &,
const TExtension,
const TBehavior,
TInfoSinkBase &sink);
} // namespace sh } // namespace sh
#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_ #endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
...@@ -212,6 +212,7 @@ class OutputSPIRVTraverser : public TIntermTraverser ...@@ -212,6 +212,7 @@ class OutputSPIRVTraverser : public TIntermTraverser
spirv::IdRefList *extractedComponentsOut); spirv::IdRefList *extractedComponentsOut);
spirv::IdRef createFunctionCall(TIntermAggregate *node, spirv::IdRef resultTypeId); spirv::IdRef createFunctionCall(TIntermAggregate *node, spirv::IdRef resultTypeId);
spirv::IdRef createAtomicBuiltIn(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;
...@@ -281,6 +282,11 @@ spv::StorageClass GetStorageClass(const TType &type) ...@@ -281,6 +282,11 @@ spv::StorageClass GetStorageClass(const TType &type)
case EvqVertexID: case EvqVertexID:
case EvqInstanceID: case EvqInstanceID:
case EvqNumWorkGroups:
case EvqWorkGroupID:
case EvqLocalInvocationID:
case EvqGlobalInvocationID:
case EvqLocalInvocationIndex:
return spv::StorageClassInput; return spv::StorageClassInput;
default: default:
...@@ -296,9 +302,7 @@ OutputSPIRVTraverser::OutputSPIRVTraverser(TCompiler *compiler, ShCompileOptions ...@@ -296,9 +302,7 @@ OutputSPIRVTraverser::OutputSPIRVTraverser(TCompiler *compiler, ShCompileOptions
: TIntermTraverser(true, true, true, &compiler->getSymbolTable()), : TIntermTraverser(true, true, true, &compiler->getSymbolTable()),
mCompiler(compiler), mCompiler(compiler),
mCompileOptions(compileOptions), mCompileOptions(compileOptions),
mBuilder(gl::FromGLenum<gl::ShaderType>(compiler->getShaderType()), mBuilder(compiler, compileOptions, compiler->getHashFunction(), compiler->getNameMap())
compiler->getHashFunction(),
compiler->getNameMap())
{} {}
OutputSPIRVTraverser::~OutputSPIRVTraverser() OutputSPIRVTraverser::~OutputSPIRVTraverser()
...@@ -334,6 +338,35 @@ spirv::IdRef OutputSPIRVTraverser::getSymbolIdAndStorageClass(const TSymbol *sym ...@@ -334,6 +338,35 @@ spirv::IdRef OutputSPIRVTraverser::getSymbolIdAndStorageClass(const TSymbol *sym
builtInDecoration = spv::BuiltInInstanceIndex; builtInDecoration = spv::BuiltInInstanceIndex;
spirvType.type = EbtInt; spirvType.type = EbtInt;
break; break;
case EvqNumWorkGroups:
name = "gl_NumWorkGroups";
builtInDecoration = spv::BuiltInNumWorkgroups;
spirvType.type = EbtUInt;
spirvType.primarySize = 3;
break;
case EvqWorkGroupID:
name = "gl_WorkGroupID";
builtInDecoration = spv::BuiltInWorkgroupId;
spirvType.type = EbtUInt;
spirvType.primarySize = 3;
break;
case EvqLocalInvocationID:
name = "gl_LocalInvocationID";
builtInDecoration = spv::BuiltInLocalInvocationId;
spirvType.type = EbtUInt;
spirvType.primarySize = 3;
break;
case EvqGlobalInvocationID:
name = "gl_GlobalInvocationID";
builtInDecoration = spv::BuiltInGlobalInvocationId;
spirvType.type = EbtUInt;
spirvType.primarySize = 3;
break;
case EvqLocalInvocationIndex:
name = "gl_LocalInvocationIndex";
builtInDecoration = spv::BuiltInLocalInvocationIndex;
spirvType.type = EbtUInt;
break;
default: default:
// TODO: more built-ins. http://anglebug.com/4889 // TODO: more built-ins. http://anglebug.com/4889
UNIMPLEMENTED(); UNIMPLEMENTED();
...@@ -1360,6 +1393,99 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node, ...@@ -1360,6 +1393,99 @@ spirv::IdRef OutputSPIRVTraverser::createFunctionCall(TIntermAggregate *node,
return result; return result;
} }
spirv::IdRef OutputSPIRVTraverser::createAtomicBuiltIn(TIntermAggregate *node,
spirv::IdRef resultTypeId)
{
// Most atomic instructions are in the form of:
//
// %result = OpAtomicX %pointer Scope MemorySemantics %value
//
// OpAtomicCompareSwap is exceptionally different (note that compare and value are in different
// order than in GLSL):
//
// %result = OpAtomicCompareExchange %pointer
// Scope MemorySemantics MemorySemantics
// %value %comparator
//
// TODO: Turn image atomic functions into ops. Saves generating many built-in variations, and
// lets this function handle both. http://anglebug.com/4889
// In all cases, the first parameter is the pointer, and the rest are rvalues.
const size_t parameterCount = node->getChildCount();
spirv::IdRef pointerId;
spirv::IdRefList parameters;
ASSERT(parameterCount >= 2);
pointerId = accessChainCollapse(&mNodeData[mNodeData.size() - parameterCount]);
for (size_t paramIndex = 1; paramIndex < parameterCount; ++paramIndex)
{
NodeData &param = mNodeData[mNodeData.size() - parameterCount + paramIndex];
parameters.push_back(accessChainLoad(&param));
}
// The scope of the operation is always Device as we don't enable the Vulkan memory model
// extension.
const spirv::IdScope scopeId = mBuilder.getUintConstant(spv::ScopeDevice);
// The memory semantics is always relaxed as we don't enable the Vulkan memory model extension.
const spirv::IdMemorySemantics semanticsId =
mBuilder.getUintConstant(spv::MemorySemanticsMaskNone);
using WriteAtomicOp =
void (*)(spirv::Blob * blob, spirv::IdResultType idResultType, spirv::IdResult idResult,
spirv::IdRef pointer, spirv::IdScope scope, spirv::IdMemorySemantics semantics,
spirv::IdRef value);
WriteAtomicOp writeAtomicOp = nullptr;
const spirv::IdRef result = mBuilder.getNewId();
const bool isUnsigned =
node->getChildNode(0)->getAsTyped()->getType().getBasicType() == EbtUInt;
switch (node->getOp())
{
case EOpAtomicAdd:
writeAtomicOp = spirv::WriteAtomicIAdd;
break;
case EOpAtomicMin:
writeAtomicOp = isUnsigned ? spirv::WriteAtomicUMin : spirv::WriteAtomicSMin;
break;
case EOpAtomicMax:
writeAtomicOp = isUnsigned ? spirv::WriteAtomicUMax : spirv::WriteAtomicSMax;
break;
case EOpAtomicAnd:
writeAtomicOp = spirv::WriteAtomicAnd;
break;
case EOpAtomicOr:
writeAtomicOp = spirv::WriteAtomicOr;
break;
case EOpAtomicXor:
writeAtomicOp = spirv::WriteAtomicXor;
break;
case EOpAtomicExchange:
writeAtomicOp = spirv::WriteAtomicExchange;
break;
case EOpAtomicCompSwap:
// Generate this special instruction right here and early out. Note again that the
// value and compare parameters of OpAtomicCompareExchange are in the opposite order
// from GLSL.
ASSERT(parameters.size() == 2);
spirv::WriteAtomicCompareExchange(mBuilder.getSpirvCurrentFunctionBlock(), resultTypeId,
result, pointerId, scopeId, semanticsId, semanticsId,
parameters[1], parameters[0]);
return result;
default:
UNREACHABLE();
}
// Write the instruction.
ASSERT(parameters.size() == 1);
writeAtomicOp(mBuilder.getSpirvCurrentFunctionBlock(), resultTypeId, result, pointerId, scopeId,
semanticsId, parameters[0]);
return result;
}
void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node) void OutputSPIRVTraverser::visitSymbol(TIntermSymbol *node)
{ {
// Constants are expected to be folded. // Constants are expected to be folded.
...@@ -1717,6 +1843,31 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node) ...@@ -1717,6 +1843,31 @@ bool OutputSPIRVTraverser::visitBinary(Visit visit, TIntermBinary *node)
else else
writeBinaryOp = spirv::WriteSGreaterThanEqual; writeBinaryOp = spirv::WriteSGreaterThanEqual;
break; break;
case EOpBitShiftLeft:
case EOpBitShiftLeftAssign:
writeBinaryOp = spirv::WriteShiftLeftLogical;
break;
case EOpBitShiftRight:
case EOpBitShiftRightAssign:
if (isUnsigned)
writeBinaryOp = spirv::WriteShiftRightLogical;
else
writeBinaryOp = spirv::WriteShiftRightArithmetic;
break;
case EOpBitwiseAnd:
case EOpBitwiseAndAssign:
writeBinaryOp = spirv::WriteBitwiseAnd;
break;
case EOpBitwiseXor:
case EOpBitwiseXorAssign:
writeBinaryOp = spirv::WriteBitwiseXor;
break;
case EOpBitwiseOr:
case EOpBitwiseOrAssign:
writeBinaryOp = spirv::WriteBitwiseOr;
break;
default: default:
UNIMPLEMENTED(); UNIMPLEMENTED();
break; break;
...@@ -2030,8 +2181,18 @@ bool OutputSPIRVTraverser::visitAggregate(Visit visit, TIntermAggregate *node) ...@@ -2030,8 +2181,18 @@ bool OutputSPIRVTraverser::visitAggregate(Visit visit, TIntermAggregate *node)
// Create a call to the function. // Create a call to the function.
result = createFunctionCall(node, typeId); result = createFunctionCall(node, typeId);
break; break;
case EOpAtomicAdd:
case EOpAtomicMin:
case EOpAtomicMax:
case EOpAtomicAnd:
case EOpAtomicOr:
case EOpAtomicXor:
case EOpAtomicExchange:
case EOpAtomicCompSwap:
result = createAtomicBuiltIn(node, typeId);
break;
default: default:
// TODO: Built-in functions. http://anglebug.com/4889 // TODO: More built-in functions. http://anglebug.com/4889
UNIMPLEMENTED(); UNIMPLEMENTED();
} }
...@@ -2089,7 +2250,19 @@ bool OutputSPIRVTraverser::visitDeclaration(Visit visit, TIntermDeclaration *nod ...@@ -2089,7 +2250,19 @@ bool OutputSPIRVTraverser::visitDeclaration(Visit visit, TIntermDeclaration *nod
const spirv::IdRef variableId = const spirv::IdRef variableId =
mBuilder.declareVariable(typeId, storageClass, nullptr, mBuilder.hashName(variable).data()); mBuilder.declareVariable(typeId, storageClass, nullptr, mBuilder.hashName(variable).data());
if (IsShaderIn(type.getQualifier()) || IsShaderOut(type.getQualifier())) const bool isShaderInOut = IsShaderIn(type.getQualifier()) || IsShaderOut(type.getQualifier());
const bool isInterfaceBlock = type.getBasicType() == EbtInterfaceBlock;
// Add decorations, which apply to the element type of arrays, if array.
spirv::IdRef nonArrayTypeId = typeId;
if (type.isArray() && (isShaderInOut || isInterfaceBlock))
{
SpirvType elementType = mBuilder.getSpirvType(type, EbsUnspecified);
elementType.arraySizes = {};
nonArrayTypeId = mBuilder.getSpirvTypeData(elementType, "").id;
}
if (isShaderInOut)
{ {
// Add in and out variables to the list of interface variables. // Add in and out variables to the list of interface variables.
mBuilder.addEntryPointInterfaceVariableId(variableId); mBuilder.addEntryPointInterfaceVariableId(variableId);
...@@ -2099,19 +2272,20 @@ bool OutputSPIRVTraverser::visitDeclaration(Visit visit, TIntermDeclaration *nod ...@@ -2099,19 +2272,20 @@ bool OutputSPIRVTraverser::visitDeclaration(Visit visit, TIntermDeclaration *nod
// For gl_PerVertex in particular, write the necessary BuiltIn decorations // For gl_PerVertex in particular, write the necessary BuiltIn decorations
if (type.getQualifier() == EvqPerVertexIn || type.getQualifier() == EvqPerVertexOut) if (type.getQualifier() == EvqPerVertexIn || type.getQualifier() == EvqPerVertexOut)
{ {
mBuilder.writePerVertexBuiltIns(type, typeId); mBuilder.writePerVertexBuiltIns(type, nonArrayTypeId);
} }
// I/O blocks are decorated with Block // I/O blocks are decorated with Block
spirv::WriteDecorate(mBuilder.getSpirvDecorations(), typeId, spv::DecorationBlock, {}); spirv::WriteDecorate(mBuilder.getSpirvDecorations(), nonArrayTypeId,
spv::DecorationBlock, {});
} }
} }
else if (type.getBasicType() == EbtInterfaceBlock) else if (isInterfaceBlock)
{ {
// For uniform and buffer variables, add Block and BufferBlock decorations respectively. // For uniform and buffer variables, add Block and BufferBlock decorations respectively.
const spv::Decoration decoration = const spv::Decoration decoration =
type.getQualifier() == EvqUniform ? spv::DecorationBlock : spv::DecorationBufferBlock; type.getQualifier() == EvqUniform ? spv::DecorationBlock : spv::DecorationBufferBlock;
spirv::WriteDecorate(mBuilder.getSpirvDecorations(), typeId, decoration, {}); spirv::WriteDecorate(mBuilder.getSpirvDecorations(), nonArrayTypeId, decoration, {});
} }
// Write DescriptorSet, Binding, Location etc decorations if necessary. // Write DescriptorSet, Binding, Location etc decorations if necessary.
......
...@@ -224,34 +224,4 @@ bool TFunction::isAtomicCounterFunction() const ...@@ -224,34 +224,4 @@ bool TFunction::isAtomicCounterFunction() const
{ {
return SymbolType() == SymbolType::BuiltIn && name().beginsWith(kAtomicCounterName); return SymbolType() == SymbolType::BuiltIn && name().beginsWith(kAtomicCounterName);
} }
bool TFunction::hasSamplerInStructOrArrayParams() const
{
for (size_t paramIndex = 0; paramIndex < mParamCount; ++paramIndex)
{
const TVariable *param = getParam(paramIndex);
if (param->getType().isStructureContainingSamplers() ||
(param->getType().isArray() && param->getType().isSampler()))
{
return true;
}
}
return false;
}
bool TFunction::hasSamplerInStructOrArrayOfArrayParams() const
{
for (size_t paramIndex = 0; paramIndex < mParamCount; ++paramIndex)
{
const TVariable *param = getParam(paramIndex);
if (param->getType().isStructureContainingSamplers() ||
(param->getType().isArrayOfArrays() && param->getType().isSampler()))
{
return true;
}
}
return false;
}
} // namespace sh } // namespace sh
...@@ -234,8 +234,6 @@ class TFunction : public TSymbol ...@@ -234,8 +234,6 @@ class TFunction : public TSymbol
bool isMain() const; bool isMain() const;
bool isImageFunction() const; bool isImageFunction() const;
bool isAtomicCounterFunction() const; bool isAtomicCounterFunction() const;
bool hasSamplerInStructOrArrayParams() const;
bool hasSamplerInStructOrArrayOfArrayParams() const;
// Note: Only to be used for static built-in functions! // Note: Only to be used for static built-in functions!
constexpr TFunction(const TSymbolUniqueId &id, constexpr TFunction(const TSymbolUniqueId &id,
......
...@@ -1341,8 +1341,10 @@ bool TranslatorVulkan::translate(TIntermBlock *root, ...@@ -1341,8 +1341,10 @@ bool TranslatorVulkan::translate(TIntermBlock *root,
#if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION) #if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION)
constexpr ShCompileOptions kUnsupportedTransformations = SH_ADD_BRESENHAM_LINE_RASTER_EMULATION; constexpr ShCompileOptions kUnsupportedTransformations = SH_ADD_BRESENHAM_LINE_RASTER_EMULATION;
if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 && getShaderType() == GL_VERTEX_SHADER && if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 &&
(compileOptions & kUnsupportedTransformations) == 0) ((getShaderType() == GL_VERTEX_SHADER &&
(compileOptions & kUnsupportedTransformations) == 0) ||
getShaderType() == GL_COMPUTE_SHADER))
{ {
// Declare the implicitly defined gl_PerVertex I/O blocks if not already. This will help // Declare the implicitly defined gl_PerVertex I/O blocks if not already. This will help
// SPIR-V generation treat them mostly like usual I/O blocks. // SPIR-V generation treat them mostly like usual I/O blocks.
......
...@@ -406,6 +406,9 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterArrayOfArray) ...@@ -406,6 +406,9 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterArrayOfArray)
// Intel's Windows OpenGL driver crashes in this test. http://anglebug.com/3791 // Intel's Windows OpenGL driver crashes in this test. http://anglebug.com/3791
ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows()); ANGLE_SKIP_TEST_IF(IsOpenGL() && IsIntel() && IsWindows());
// TODO: support loops in direct SPIR-V generation path. http://anglebug.com/4889
ANGLE_SKIP_TEST_IF(GetParam().eglParameters.directSPIRVGeneration == EGL_TRUE);
constexpr char kCS[] = R"(#version 310 es constexpr char kCS[] = R"(#version 310 es
layout(local_size_x=1, local_size_y=1, local_size_z=1) in; layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding = 0) uniform atomic_uint ac[7][5][3]; layout(binding = 0) uniform atomic_uint ac[7][5][3];
...@@ -590,6 +593,7 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterMemoryBarrier) ...@@ -590,6 +593,7 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterMemoryBarrier)
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtomicCounterBufferTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtomicCounterBufferTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtomicCounterBufferTest31); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtomicCounterBufferTest31);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(AtomicCounterBufferTest); ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(AtomicCounterBufferTest);
ANGLE_INSTANTIATE_TEST_ES31(AtomicCounterBufferTest31); ANGLE_INSTANTIATE_TEST_ES31_AND(AtomicCounterBufferTest31,
WithDirectSPIRVGeneration(ES31_VULKAN()));
} // namespace } // namespace
...@@ -4169,6 +4169,9 @@ TEST_P(ComputeShaderTest, DispatchBlitStencilDispatch) ...@@ -4169,6 +4169,9 @@ TEST_P(ComputeShaderTest, DispatchBlitStencilDispatch)
// http://anglebug.com/5533 // http://anglebug.com/5533
ANGLE_SKIP_TEST_IF(IsQualcomm() && IsOpenGLES()); ANGLE_SKIP_TEST_IF(IsQualcomm() && IsOpenGLES());
// http://anglebug.com/5072
ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL());
constexpr GLsizei kSize = 1; constexpr GLsizei kSize = 1;
constexpr char kCS[] = R"(#version 310 es constexpr char kCS[] = R"(#version 310 es
......
...@@ -300,7 +300,7 @@ inline PlatformParameters WithEmulatedVAOs(const PlatformParameters &params) ...@@ -300,7 +300,7 @@ inline PlatformParameters WithEmulatedVAOs(const PlatformParameters &params)
inline PlatformParameters WithDirectSPIRVGeneration(const PlatformParameters &params) inline PlatformParameters WithDirectSPIRVGeneration(const PlatformParameters &params)
{ {
PlatformParameters directSPIRVGeneration = params; PlatformParameters directSPIRVGeneration = params;
directSPIRVGeneration.eglParameters.directSPIRVGeneration = true; directSPIRVGeneration.eglParameters.directSPIRVGeneration = EGL_TRUE;
return directSPIRVGeneration; return directSPIRVGeneration;
} }
} // namespace angle } // namespace angle
......
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