Commit 153240b2 by Shahbaz Youssefi Committed by Angle LUCI CQ

Vulkan: SPIR-V Gen: Support loops

Loops are similar to if-else in that they generate a number of blocks where the first block specifies divergence (OpLoopMerge) and the merge block. Differently from if-else, there is a block where the condition is evaluated and a block which `continue;` leads to (this last block is the only one allowed to back-jump to the beginning of the loop). Bug: angleproject:4889 Change-Id: Ic59f4bf3e05fbf93cb5af85acd3bc4b0da8412af Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/2957809 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 8cf6d8cc
...@@ -26,7 +26,7 @@ ...@@ -26,7 +26,7 @@
// Version number for shader translation API. // Version number for shader translation API.
// It is incremented every time the API changes. // It is incremented every time the API changes.
#define ANGLE_SH_VERSION 260 #define ANGLE_SH_VERSION 261
enum ShShaderSpec enum ShShaderSpec
{ {
...@@ -341,6 +341,9 @@ const ShCompileOptions SH_INIT_FRAGMENT_OUTPUT_VARIABLES = UINT64_C(1) << 57; ...@@ -341,6 +341,9 @@ const ShCompileOptions SH_INIT_FRAGMENT_OUTPUT_VARIABLES = UINT64_C(1) << 57;
// non-dcheck-enabled builds to avoid increasing ANGLE's binary size while both generators coexist. // non-dcheck-enabled builds to avoid increasing ANGLE's binary size while both generators coexist.
const ShCompileOptions SH_GENERATE_SPIRV_DIRECTLY = UINT64_C(1) << 58; const ShCompileOptions SH_GENERATE_SPIRV_DIRECTLY = UINT64_C(1) << 58;
// Generate workarounds in SPIR-V for buggy code.
const ShCompileOptions SH_GENERATE_SPIRV_WORKAROUNDS = UINT64_C(1) << 59;
// The 64 bits hash function. The first parameter is the input string; the // The 64 bits hash function. The first parameter is the input string; the
// second parameter is the string length. // second parameter is the string length.
using ShHashFunction64 = khronos_uint64_t (*)(const char *, size_t); using ShHashFunction64 = khronos_uint64_t (*)(const char *, size_t);
......
...@@ -506,6 +506,12 @@ struct FeaturesVk : FeatureSetBase ...@@ -506,6 +506,12 @@ struct FeaturesVk : FeatureSetBase
"Direct translation to SPIR-V.", &members, "Direct translation to SPIR-V.", &members,
"http://anglebug.com/4889"}; "http://anglebug.com/4889"};
// Whether SPIR-V should be generated with workarounds for buggy drivers.
Feature directSPIRVGenerationWorkarounds = {
"directSPIRVGenerationWorkarounds", FeatureCategory::VulkanWorkarounds,
"Work around driver bugs when translating to SPIR-V.", &members,
"http://anglebug.com/6110"};
// Whether we should use driver uniforms over specialization constants for some shader // Whether we should use driver uniforms over specialization constants for some shader
// modifications like yflip and rotation. // modifications like yflip and rotation.
Feature forceDriverUniformOverSpecConst = { Feature forceDriverUniformOverSpecConst = {
......
...@@ -996,6 +996,19 @@ void SPIRVBuilder::endConditional() ...@@ -996,6 +996,19 @@ void SPIRVBuilder::endConditional()
mConditionalStack.pop_back(); mConditionalStack.pop_back();
} }
bool SPIRVBuilder::isInLoop() const
{
for (const SpirvConditional &conditional : mConditionalStack)
{
if (conditional.isContinuable)
{
return true;
}
}
return false;
}
uint32_t SPIRVBuilder::nextUnusedBinding() uint32_t SPIRVBuilder::nextUnusedBinding()
{ {
return mNextUnusedBinding++; return mNextUnusedBinding++;
...@@ -1162,6 +1175,84 @@ void SPIRVBuilder::writeBranchConditionalBlockEnd() ...@@ -1162,6 +1175,84 @@ void SPIRVBuilder::writeBranchConditionalBlockEnd()
nextConditionalBlock(); nextConditionalBlock();
} }
void SPIRVBuilder::writeLoopHeader(spirv::IdRef branchToBlock,
spirv::IdRef continueBlock,
spirv::IdRef mergeBlock)
{
// First, jump to the header block:
//
// OpBranch %header
//
const spirv::IdRef headerBlock = mConditionalStack.back().blockIds[0];
spirv::WriteBranch(getSpirvCurrentFunctionBlock(), headerBlock);
terminateCurrentFunctionBlock();
// Start the header block.
nextConditionalBlock();
// Generate the following:
//
// OpLoopMerge %mergeBlock %continueBlock None
// OpBranch %branchToBlock (%cond or if do-while, %body)
//
spirv::WriteLoopMerge(getSpirvCurrentFunctionBlock(), mergeBlock, continueBlock,
spv::LoopControlMaskNone);
spirv::WriteBranch(getSpirvCurrentFunctionBlock(), branchToBlock);
terminateCurrentFunctionBlock();
// Start the next block, which is either %cond or %body.
nextConditionalBlock();
}
void SPIRVBuilder::writeLoopConditionEnd(spirv::IdRef conditionValue,
spirv::IdRef branchToBlock,
spirv::IdRef mergeBlock)
{
// Generate the following:
//
// OpBranchConditional %conditionValue %branchToBlock %mergeBlock
//
// %branchToBlock is either %body or if do-while, %header
//
spirv::WriteBranchConditional(getSpirvCurrentFunctionBlock(), conditionValue, branchToBlock,
mergeBlock, {});
terminateCurrentFunctionBlock();
// Start the next block, which is either %continue or %body.
nextConditionalBlock();
}
void SPIRVBuilder::writeLoopContinueEnd(spirv::IdRef headerBlock)
{
// Generate the following:
//
// OpBranch %headerBlock
//
spirv::WriteBranch(getSpirvCurrentFunctionBlock(), headerBlock);
terminateCurrentFunctionBlock();
// Start the next block, which is %body.
nextConditionalBlock();
}
void SPIRVBuilder::writeLoopBodyEnd(spirv::IdRef continueBlock)
{
// Generate the following:
//
// OpBranch %continueBlock
//
// This is only done if the block isn't already terminated in another way, such as with an
// unconditional continue/etc at the end of the loop.
if (!isCurrentFunctionBlockTerminated())
{
spirv::WriteBranch(getSpirvCurrentFunctionBlock(), continueBlock);
terminateCurrentFunctionBlock();
}
// Start the next block, which is %merge or if while, %continue.
nextConditionalBlock();
}
// This function is nearly identical to getTypeData(), except for row-major matrices. For the // This function is nearly identical to getTypeData(), except for row-major matrices. For the
// purposes of base alignment and size calculations, it swaps the primary and secondary sizes such // purposes of base alignment and size calculations, it swaps the primary and secondary sizes such
// that the look up always assumes column-major matrices. Row-major matrices are only applicable to // that the look up always assumes column-major matrices. Row-major matrices are only applicable to
......
...@@ -305,6 +305,14 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -305,6 +305,14 @@ class SPIRVBuilder : angle::NonCopyable
spirv::IdRef falseBlock, spirv::IdRef falseBlock,
spirv::IdRef mergeBlock); spirv::IdRef mergeBlock);
void writeBranchConditionalBlockEnd(); void writeBranchConditionalBlockEnd();
void writeLoopHeader(spirv::IdRef branchToBlock,
spirv::IdRef continueBlock,
spirv::IdRef mergeBlock);
void writeLoopConditionEnd(spirv::IdRef conditionValue,
spirv::IdRef branchToBlock,
spirv::IdRef mergeBlock);
void writeLoopContinueEnd(spirv::IdRef headerBlock);
void writeLoopBodyEnd(spirv::IdRef continueBlock);
spirv::IdRef getBoolConstant(bool value); spirv::IdRef getBoolConstant(bool value);
spirv::IdRef getUintConstant(uint32_t value); spirv::IdRef getUintConstant(uint32_t value);
...@@ -330,6 +338,7 @@ class SPIRVBuilder : angle::NonCopyable ...@@ -330,6 +338,7 @@ class SPIRVBuilder : angle::NonCopyable
void startConditional(size_t blockCount, bool isContinuable, bool isBreakable); void startConditional(size_t blockCount, bool isContinuable, bool isBreakable);
void nextConditionalBlock(); void nextConditionalBlock();
void endConditional(); void endConditional();
bool isInLoop() const;
// TODO: remove name hashing once translation through glslang is removed. That is necessary to // TODO: remove name hashing once translation through glslang is removed. That is necessary to
// avoid name collision between ANGLE's internal symbols and user-defined ones when compiling // avoid name collision between ANGLE's internal symbols and user-defined ones when compiling
......
...@@ -218,9 +218,11 @@ const TFunction *MonomorphizeFunction(TSymbolTable *symbolTable, ...@@ -218,9 +218,11 @@ const TFunction *MonomorphizeFunction(TSymbolTable *symbolTable,
if (asBinary->getOp() == EOpIndexIndirect) if (asBinary->getOp() == EOpIndexIndirect)
{ {
TIntermTyped *index = asBinary->getRight(); TIntermTyped *index = asBinary->getRight();
TType *indexType = new TType(index->getType());
indexType->setQualifier(EvqIn);
TVariable *param = new TVariable(symbolTable, kEmptyImmutableString, TVariable *param = new TVariable(symbolTable, kEmptyImmutableString, indexType,
&index->getType(), SymbolType::AngleInternal); SymbolType::AngleInternal);
substituteFunction->addParameter(param); substituteFunction->addParameter(param);
// The argument now uses the function parameters as indices. // The argument now uses the function parameters as indices.
......
...@@ -2511,6 +2511,14 @@ void RendererVk::initFeatures(DisplayVk *displayVk, ...@@ -2511,6 +2511,14 @@ void RendererVk::initFeatures(DisplayVk *displayVk,
// improves 7%. // improves 7%.
ANGLE_FEATURE_CONDITION(&mFeatures, preferSubmitAtFBOBoundary, isARM); ANGLE_FEATURE_CONDITION(&mFeatures, preferSubmitAtFBOBoundary, isARM);
// When generating SPIR-V, the following workarounds are applied on buggy drivers:
//
// - AMD/Windows: Function parameters are passed in temporary variables even if they are already
// variables.
//
// http://anglebug.com/6110
ANGLE_FEATURE_CONDITION(&mFeatures, directSPIRVGenerationWorkarounds, IsWindows() && isAMD);
angle::PlatformMethods *platform = ANGLEPlatformCurrent(); angle::PlatformMethods *platform = ANGLEPlatformCurrent();
platform->overrideFeaturesVk(platform, &mFeatures); platform->overrideFeaturesVk(platform, &mFeatures);
......
...@@ -104,6 +104,11 @@ std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *conte ...@@ -104,6 +104,11 @@ std::shared_ptr<WaitableCompileEvent> ShaderVk::compile(const gl::Context *conte
if (contextVk->getFeatures().directSPIRVGeneration.enabled) if (contextVk->getFeatures().directSPIRVGeneration.enabled)
{ {
compileOptions |= SH_GENERATE_SPIRV_DIRECTLY; compileOptions |= SH_GENERATE_SPIRV_DIRECTLY;
if (contextVk->getFeatures().directSPIRVGenerationWorkarounds.enabled)
{
compileOptions |= SH_GENERATE_SPIRV_WORKAROUNDS;
}
} }
return compileImpl(context, compilerInstance, mState.getSource(), compileOptions | options); return compileImpl(context, compilerInstance, mState.getSource(), compileOptions | options);
......
...@@ -22,6 +22,9 @@ ...@@ -22,6 +22,9 @@
// Linux // Linux
6065 LINUX INTEL VULKAN : SimpleStateChangeTestES31.DrawThenUpdateUBOThenDrawThenDrawIndexed/* = SKIP 6065 LINUX INTEL VULKAN : SimpleStateChangeTestES31.DrawThenUpdateUBOThenDrawThenDrawIndexed/* = SKIP
// Fails in older mesa
6109 LINUX INTEL : GLSLTestLoops.BasicDoWhile/ES3_* = SKIP
6109 LINUX INTEL : GLSLTestLoops.BasicWhile/ES3_* = SKIP
// Intel Vulkan // Intel Vulkan
......
...@@ -406,9 +406,6 @@ TEST_P(AtomicCounterBufferTest31, AtomicCounterArrayOfArray) ...@@ -406,9 +406,6 @@ 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];
......
...@@ -11074,6 +11074,106 @@ void main() ...@@ -11074,6 +11074,106 @@ void main()
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green); EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
} }
class GLSLTestLoops : public GLSLTest
{
protected:
void runTest(const char *fs)
{
ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Simple(), fs);
drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
EXPECT_GL_NO_ERROR();
EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
}
};
// Test basic for loops
TEST_P(GLSLTestLoops, BasicFor)
{
constexpr char kFS[] = R"(#version 300 es
precision mediump float;
out vec4 color;
void main()
{
int result = 0;
for (int i = 0; i < 10; ++i)
for (int j = 0; j < 8; ++j)
{
for (int k = 0; k < 2; ++k, ++j) ++result;
for (int k = 0; k < 3; ++k) ++result;
for (int k = 0; k < 0; ++k) ++result;
}
color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
})";
runTest(kFS);
}
// Test basic while loops
TEST_P(GLSLTestLoops, BasicWhile)
{
constexpr char kFS[] = R"(#version 300 es
precision mediump float;
out vec4 color;
void main()
{
int result = 0;
int i = 0;
while (i < 10)
{
int j = 0;
while (j < 8)
{
int k = 0;
while (k < 2) { ++result; ++k; ++j; }
while (k < 5) { ++result; ++k; }
while (k < 4) { ++result; }
++j;
}
++i;
}
color = result == 150 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
})";
runTest(kFS);
}
// Test basic do-while loops
TEST_P(GLSLTestLoops, BasicDoWhile)
{
constexpr char kFS[] = R"(#version 300 es
precision mediump float;
out vec4 color;
void main()
{
int result = 0;
int i = 0;
do
{
int j = 0;
do
{
int k = 0;
do { ++result; ++k; ++j; } while (k < 2);
do { ++result; ++k; } while (k < 5);
do { ++result; } while (k < 3);
++j;
} while (j < 8);
++i;
} while (i < 10);
color = result == 180 ? vec4(0, 1, 0, 1) : vec4(1, 0, 0, 1);
})";
runTest(kFS);
}
} // anonymous namespace } // anonymous namespace
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTest); ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTest);
...@@ -11083,6 +11183,9 @@ ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTestNoValidation); ...@@ -11083,6 +11183,9 @@ ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(GLSLTestNoValidation);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTest_ES3); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTest_ES3);
ANGLE_INSTANTIATE_TEST_ES3(GLSLTest_ES3); ANGLE_INSTANTIATE_TEST_ES3(GLSLTest_ES3);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(GLSLTestLoops);
ANGLE_INSTANTIATE_TEST_ES3_AND(GLSLTestLoops, WithDirectSPIRVGeneration(ES3_VULKAN()));
ANGLE_INSTANTIATE_TEST_ES2(WebGLGLSLTest); ANGLE_INSTANTIATE_TEST_ES2(WebGLGLSLTest);
GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2GLSLTest); GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(WebGL2GLSLTest);
......
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