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
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();
typeId = getNewId();
......@@ -929,11 +929,6 @@ void SPIRVBuilder::addCapability(spv::Capability capability)
mCapabilities.insert(capability);
}
void SPIRVBuilder::addExecutionMode(spv::ExecutionMode executionMode)
{
mExecutionModes.insert(executionMode);
}
void SPIRVBuilder::setEntryPointId(spirv::IdRef id)
{
ASSERT(!mEntryPointId.valid());
......@@ -1292,15 +1287,14 @@ spirv::Blob SPIRVBuilder::getSpirv()
//
// 5 for header +
// a number of capabilities +
// a number of execution modes +
// size of already generated instructions.
//
// The actual size is larger due to other metadata instructions such as extensions,
// OpExtInstImport, OpEntryPoint etc.
result.reserve(5 + mCapabilities.size() * 2 + mExecutionModes.size() * 3 + mSpirvDebug.size() +
mSpirvDecorations.size() + mSpirvTypeAndConstantDecls.size() +
mSpirvTypePointerDecls.size() + mSpirvFunctionTypeDecls.size() +
mSpirvVariableDecls.size() + mSpirvFunctions.size());
// OpExtInstImport, OpEntryPoint, OpExecutionMode etc.
result.reserve(5 + mCapabilities.size() * 2 + mSpirvDebug.size() + mSpirvDecorations.size() +
mSpirvTypeAndConstantDecls.size() + mSpirvTypePointerDecls.size() +
mSpirvFunctionTypeDecls.size() + mSpirvVariableDecls.size() +
mSpirvFunctions.size());
// Generate any necessary id before writing the id bound in header.
const spirv::IdRef extInstImportId = getNewId();
......@@ -1338,11 +1332,7 @@ spirv::Blob SPIRVBuilder::getSpirv()
mEntryPointInterfaceList);
// - OpExecutionMode instructions
for (spv::ExecutionMode executionMode : mExecutionModes)
{
spirv::WriteExecutionMode(&result, mEntryPointId, executionMode, {});
}
result.insert(result.end(), mSpirvExecutionModes.begin(), mSpirvExecutionModes.end());
generateExecutionModes(&result);
// - OpSource instruction.
//
......@@ -1364,4 +1354,23 @@ spirv::Blob SPIRVBuilder::getSpirv()
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
......@@ -211,8 +211,13 @@ struct SpirvConditional
class SPIRVBuilder : angle::NonCopyable
{
public:
SPIRVBuilder(gl::ShaderType shaderType, ShHashFunction64 hashFunction, NameMap &nameMap)
: mShaderType(shaderType),
SPIRVBuilder(TCompiler *compiler,
ShCompileOptions compileOptions,
ShHashFunction64 hashFunction,
NameMap &nameMap)
: mCompiler(compiler),
mCompileOptions(compileOptions),
mShaderType(gl::FromGLenum<gl::ShaderType>(compiler->getShaderType())),
mNextAvailableId(1),
mHashFunction(hashFunction),
mNameMap(nameMap),
......@@ -228,7 +233,6 @@ class SPIRVBuilder : angle::NonCopyable
spirv::IdRef getTypePointerId(spirv::IdRef typeId, spv::StorageClass storageClass);
spirv::IdRef getFunctionTypeId(spirv::IdRef returnTypeId, const spirv::IdRefList &paramTypeIds);
spirv::Blob *getSpirvExecutionModes() { return &mSpirvExecutionModes; }
spirv::Blob *getSpirvDebug() { return &mSpirvDebug; }
spirv::Blob *getSpirvDecorations() { return &mSpirvDecorations; }
spirv::Blob *getSpirvTypeAndConstantDecls() { return &mSpirvTypeAndConstantDecls; }
......@@ -255,7 +259,6 @@ class SPIRVBuilder : angle::NonCopyable
SpirvConditional *getCurrentConditional() { return &mConditionalStack.back(); }
void addCapability(spv::Capability capability);
void addExecutionMode(spv::ExecutionMode executionMode);
void setEntryPointId(spirv::IdRef id);
void addEntryPointInterfaceVariableId(spirv::IdRef id);
void writePerVertexBuiltIns(const TType &type, spirv::IdRef typeId);
......@@ -321,17 +324,16 @@ class SPIRVBuilder : angle::NonCopyable
uint32_t nextUnusedInputLocation(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;
// Capabilities the shader is using. Accumulated as the instructions are generated. The Shader
// capability is unconditionally generated, so it's not tracked.
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
// generated. Used for the OpEntryPoint instruction.
spirv::IdRefList mEntryPointInterfaceList;
......@@ -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
// is obtained by stitching the sections together. This puts the instructions in the order
// required by the spec.
spirv::Blob mSpirvExecutionModes;
spirv::Blob mSpirvDebug;
spirv::Blob mSpirvDecorations;
spirv::Blob mSpirvTypeAndConstantDecls;
......
......@@ -1598,64 +1598,4 @@ bool TCompiler::isVaryingDefined(const char *varyingName)
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
......@@ -350,14 +350,6 @@ class TCompiler : public TShHandleBase
TCompiler *ConstructCompiler(sh::GLenum type, ShShaderSpec spec, ShShaderOutput output);
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
#endif // COMPILER_TRANSLATOR_COMPILER_H_
......@@ -1474,4 +1474,64 @@ bool NeedsToWriteLayoutQualifier(const TType &type)
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
......@@ -9,6 +9,7 @@
#include <set>
#include "compiler/translator/ExtensionBehavior.h"
#include "compiler/translator/HashNames.h"
#include "compiler/translator/InfoSink.h"
#include "compiler/translator/tree_util/IntermTraverse.h"
......@@ -132,6 +133,14 @@ void WriteTessEvaluationShaderLayoutQualifiers(TInfoSinkBase &out,
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
#endif // COMPILER_TRANSLATOR_OUTPUTGLSLBASE_H_
......@@ -224,34 +224,4 @@ bool TFunction::isAtomicCounterFunction() const
{
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
......@@ -234,8 +234,6 @@ class TFunction : public TSymbol
bool isMain() const;
bool isImageFunction() const;
bool isAtomicCounterFunction() const;
bool hasSamplerInStructOrArrayParams() const;
bool hasSamplerInStructOrArrayOfArrayParams() const;
// Note: Only to be used for static built-in functions!
constexpr TFunction(const TSymbolUniqueId &id,
......
......@@ -1341,8 +1341,10 @@ bool TranslatorVulkan::translate(TIntermBlock *root,
#if defined(ANGLE_ENABLE_DIRECT_SPIRV_GENERATION)
constexpr ShCompileOptions kUnsupportedTransformations = SH_ADD_BRESENHAM_LINE_RASTER_EMULATION;
if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 0 && getShaderType() == GL_VERTEX_SHADER &&
(compileOptions & kUnsupportedTransformations) == 0)
if ((compileOptions & SH_GENERATE_SPIRV_DIRECTLY) != 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
// SPIR-V generation treat them mostly like usual I/O blocks.
......
......@@ -406,6 +406,9 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterArrayOfArray)
// Intel's Windows OpenGL driver crashes in this test. http://anglebug.com/3791
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
layout(local_size_x=1, local_size_y=1, local_size_z=1) in;
layout(binding = 0) uniform atomic_uint ac[7][5][3];
......@@ -590,6 +593,7 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterMemoryBarrier)
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtomicCounterBufferTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(AtomicCounterBufferTest31);
ANGLE_INSTANTIATE_TEST_ES3_AND_ES31(AtomicCounterBufferTest);
ANGLE_INSTANTIATE_TEST_ES31(AtomicCounterBufferTest31);
ANGLE_INSTANTIATE_TEST_ES31_AND(AtomicCounterBufferTest31,
WithDirectSPIRVGeneration(ES31_VULKAN()));
} // namespace
......@@ -4169,6 +4169,9 @@ TEST_P(ComputeShaderTest, DispatchBlitStencilDispatch)
// http://anglebug.com/5533
ANGLE_SKIP_TEST_IF(IsQualcomm() && IsOpenGLES());
// http://anglebug.com/5072
ANGLE_SKIP_TEST_IF(IsIntel() && IsLinux() && IsOpenGL());
constexpr GLsizei kSize = 1;
constexpr char kCS[] = R"(#version 310 es
......
......@@ -300,7 +300,7 @@ inline PlatformParameters WithEmulatedVAOs(const PlatformParameters &params)
inline PlatformParameters WithDirectSPIRVGeneration(const PlatformParameters &params)
{
PlatformParameters directSPIRVGeneration = params;
directSPIRVGeneration.eglParameters.directSPIRVGeneration = true;
directSPIRVGeneration.eglParameters.directSPIRVGeneration = EGL_TRUE;
return directSPIRVGeneration;
}
} // 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