blob: d52f1613d9e0a6dbd3da997f89576efadaacfa45 [file] [log] [blame]
//
// Copyright 2017 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// HLSLOutput_test.cpp:
// Tests for HLSL output.
//
#include <regex>
#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "gtest/gtest.h"
#include "tests/test_utils/compiler_test.h"
using namespace sh;
class HLSLOutputTest : public MatchOutputCodeTest
{
public:
HLSLOutputTest() : MatchOutputCodeTest(GL_FRAGMENT_SHADER, 0, SH_HLSL_4_1_OUTPUT) {}
};
class HLSL30VertexOutputTest : public MatchOutputCodeTest
{
public:
HLSL30VertexOutputTest() : MatchOutputCodeTest(GL_VERTEX_SHADER, 0, SH_HLSL_3_0_OUTPUT) {}
};
// Test that having dynamic indexing of a vector inside the right hand side of logical or doesn't
// trigger asserts in HLSL output.
TEST_F(HLSLOutputTest, DynamicIndexingOfVectorOnRightSideOfLogicalOr)
{
const std::string &shaderString =
"#version 300 es\n"
"precision highp float;\n"
"out vec4 my_FragColor;\n"
"uniform int u1;\n"
"void main() {\n"
" bvec4 v = bvec4(true, true, true, false);\n"
" my_FragColor = vec4(v[u1 + 1] || v[u1]);\n"
"}\n";
compile(shaderString);
}
// Test that rewriting else blocks in a function that returns a struct doesn't use the struct name
// without a prefix.
TEST_F(HLSL30VertexOutputTest, RewriteElseBlockReturningStruct)
{
const std::string &shaderString =
"struct foo\n"
"{\n"
" float member;\n"
"};\n"
"uniform bool b;\n"
"foo getFoo()\n"
"{\n"
" if (b)\n"
" {\n"
" return foo(0.0);\n"
" }\n"
" else\n"
" {\n"
" return foo(1.0);\n"
" }\n"
"}\n"
"void main()\n"
"{\n"
" gl_Position = vec4(getFoo().member);\n"
"}\n";
compile(shaderString);
EXPECT_TRUE(foundInCode("_foo"));
EXPECT_FALSE(foundInCode("(foo)"));
EXPECT_FALSE(foundInCode(" foo"));
}
// Test that having an array constructor as a statement doesn't trigger an assert in HLSL output.
// This test has a constant array constructor statement.
TEST_F(HLSLOutputTest, ConstArrayConstructorStatement)
{
const std::string &shaderString =
R"(#version 300 es
void main()
{
int[1](0);
})";
compile(shaderString);
}
// Test that having an array constructor as a statement doesn't trigger an assert in HLSL output.
TEST_F(HLSLOutputTest, ArrayConstructorStatement)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 outColor;
void main()
{
outColor = vec4(0.0, 0.0, 0.0, 1.0);
float[1](outColor[1]++);
})";
compile(shaderString);
}
// Test an array of arrays constructor as a statement.
TEST_F(HLSLOutputTest, ArrayOfArraysStatement)
{
const std::string &shaderString =
R"(#version 310 es
precision mediump float;
out vec4 outColor;
void main()
{
outColor = vec4(0.0, 0.0, 0.0, 1.0);
float[2][2](float[2](outColor[1]++, 0.0), float[2](1.0, 2.0));
})";
compile(shaderString);
}
// Test dynamic indexing of a vector. This makes sure that helper functions added for dynamic
// indexing have correct data that subsequent traversal steps rely on.
TEST_F(HLSLOutputTest, VectorDynamicIndexing)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
out vec4 outColor;
uniform int i;
void main()
{
vec4 foo = vec4(0.0, 0.0, 0.0, 1.0);
foo[i] = foo[i + 1];
outColor = foo;
})";
compile(shaderString);
}
// Test returning an array from a user-defined function. This makes sure that function symbols are
// changed consistently when the user-defined function is changed to have an array out parameter.
TEST_F(HLSLOutputTest, ArrayReturnValue)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
uniform float u;
out vec4 outColor;
float[2] getArray(float f)
{
return float[2](f, f + 1.0);
}
void main()
{
float[2] arr = getArray(u);
outColor = vec4(arr[0], arr[1], 0.0, 1.0);
})";
compile(shaderString);
}
// Test that writing parameters without a name doesn't assert.
TEST_F(HLSLOutputTest, ParameterWithNoName)
{
const std::string &shaderString =
R"(precision mediump float;
uniform vec4 v;
vec4 s(vec4)
{
return v;
}
void main()
{
gl_FragColor = s(v);
})";
compile(shaderString);
}
// Test that array dimensions are written out correctly.
TEST_F(HLSLOutputTest, Array)
{
const std::string &shaderString =
R"(#version 300 es
precision mediump float;
uniform float uf;
out vec4 my_FragColor;
void main()
{
my_FragColor = vec4(0.0, 0.0, 0.0, 1.0);
float arr[2];
for (int i = 0; i < 2; ++i) {
arr[i] = uf * 2.0;
my_FragColor.x += arr[i];
}
})";
compile(shaderString);
EXPECT_TRUE(foundInCodeRegex(std::regex("_arr(\\d)*\\[2\\]")));
}
// Test that initializing array with previously declared array will not be overwritten
TEST_F(HLSLOutputTest, SameNameArray)
{
const std::string &shaderString =
R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
void main()
{
float arr[2] = float[2](1.0, 1.0);
{
float arr[2] = arr;
my_FragColor = vec4(0.0, arr[0], 0.0, arr[1]);
}
})";
compile(shaderString);
// There should be two different arr defined, e.g. _arr1000 and _arr1001
// Use Workaround for now.
// Once the build team fixes libc++ we could use the following one line solution instead.
// EXPECT_TRUE(foundInCodeRegex(std::regex("_arr(\\d*)\\[2\\](.|\\r|\\n)*_arr(?!\\1)\\d*\\[2\\]")));
std::smatch m;
EXPECT_TRUE(foundInCodeRegex(std::regex("_arr(\\d)*\\[2\\]"), &m));
EXPECT_TRUE(m.size() == 2);
EXPECT_TRUE(m[0].str() != m[1].str());
}
// Test that passing a non-struct member of a std140 structure to a function won't trigger the
// struct mapping.
TEST_F(HLSLOutputTest, NonStructMemberAsFunctionArgument)
{
constexpr char shaderString[] = R"(#version 300 es
precision highp float;
out vec4 my_FragColor;
struct InstancingData
{
vec4 data;
};
layout(std140) uniform InstanceBlock
{
InstancingData instances[8];
};
void main()
{
int index = int(gl_FragCoord.x);
float result = dot(instances[index].data, vec4(1.0, 1.0, 1.0, 1.0));
my_FragColor = vec4(result, 0.0, 0.0, 1.0);
})";
compile(shaderString);
EXPECT_FALSE(foundInCode("map_instances"));
}