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_
...@@ -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