Vulkan: SPIR-V Gen: Handle scalar(const) produced by index clamp

Normally scalar(const) is folded into a constant.  The index clamp
transformation may produce such a code where the index looks dynamic at
first (for example `false ? uniform_value : constant`), but becomes
constant after folding.

This change makes SPIR-V generation robust in that case.  A potential
future change could avoid the clamp entirely by making FoldExpressions
adjust the op of the EOpIndexIndirect node whose index is being replaced
with a constant with EOpIndexDirect (and apply the clamp on the
argument).

Bug: chromium:1260651
Change-Id: I552b7527d821d1cb52e0e53212cc481285674861
Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/3226311
Commit-Queue: Shahbaz Youssefi <syoussefi@chromium.org>
Reviewed-by: Tim Van Patten <timvp@google.com>
This commit is contained in:
Shahbaz Youssefi 2021-10-18 15:31:27 -04:00 коммит произвёл Angle LUCI CQ
Родитель ca127443c8
Коммит 066fb91b41
2 изменённых файлов: 49 добавлений и 5 удалений

Просмотреть файл

@ -1280,6 +1280,8 @@ spirv::IdRef OutputSPIRVTraverser::createComplexConstant(const TType &type,
spirv::IdRef typeId,
const spirv::IdRefList &parameters)
{
ASSERT(!type.isScalar());
if (type.isMatrix() && !type.isArray())
{
// Matrices are constructed from their columns.
@ -1309,9 +1311,9 @@ spirv::IdRef OutputSPIRVTraverser::createConstructor(TIntermAggregate *node, spi
const TIntermSequence &arguments = *node->getSequence();
const TType &arg0Type = arguments[0]->getAsTyped()->getType();
// In some cases, constructors with constant value are not folded. If the constructor is a null
// value, use OpConstantNull to avoid creating a bunch of instructions. Otherwise, the constant
// is created below.
// In some cases, constructors-with-constant values are not folded. If the constructor is a
// null value, use OpConstantNull to avoid creating a bunch of instructions. Otherwise, the
// constant is created below.
if (node->isConstantNullValue())
{
return mBuilder.getNullConstant(typeId);
@ -1353,10 +1355,22 @@ spirv::IdRef OutputSPIRVTraverser::createConstructor(TIntermAggregate *node, spi
// Additionally, array and structs are constructed by OpCompositeConstruct followed by ids of
// each parameter which must enumerate every individual element / field.
// In some cases, constructors with constant value are not folded. That is handled here.
// In some cases, constructors-with-constant values are not folded such as for large constants.
// Some transformations may also produce constructors-with-constants instead of constants even
// for basic types. These are handled here.
if (node->hasConstantValue())
{
return createComplexConstant(node->getType(), typeId, parameters);
if (!type.isScalar())
{
return createComplexConstant(node->getType(), typeId, parameters);
}
// If a transformation creates scalar(constant), return the constant as-is.
// visitConstantUnion has already cast it to the right type.
if (arguments[0]->getAsConstantUnion() != nullptr)
{
return parameters[0];
}
}
if (type.isArray() || type.getStruct() != nullptr)

Просмотреть файл

@ -14222,6 +14222,36 @@ void main() {
EXPECT_NE(compileResult, 0);
}
// Regression test for a bug in SPIR-V output where a transformation creates float(constant) without
// folding it into a TIntermConstantUnion. This transformation is clamping non-constant indices in
// WebGL. The |false ? i : 5| as index caused the transformation to consider this a non-constant
// index.
TEST_P(WebGL2GLSLTest, IndexClampConstantIndexBug)
{
constexpr char kFS[] = R"(#version 300 es
precision highp float;
layout(location=0) out float f;
uniform int i;
void main()
{
float data[10];
f = data[false ? i : 5];
})";
GLuint shader = glCreateShader(GL_FRAGMENT_SHADER);
const char *sourceArray[1] = {kFS};
GLint lengths[1] = {static_cast<GLint>(sizeof(kFS) - 1)};
glShaderSource(shader, 1, sourceArray, lengths);
glCompileShader(shader);
GLint compileResult;
glGetShaderiv(shader, GL_COMPILE_STATUS, &compileResult);
EXPECT_NE(compileResult, 0);
}
} // anonymous namespace
ANGLE_INSTANTIATE_TEST_ES2_AND_ES3_AND(GLSLTest, WithGlslang(ES2_VULKAN()));