#ifdef UNSAFE_BUFFERS_BUILD
# pragma allow_unsafe_buffers
#endif
#include "GLSLANG/ShaderLang.h"
#include "angle_gl.h"
#include "common/angleutils.h"
#include "gtest/gtest.h"
#include "tests/test_utils/compiler_test.h"
using namespace sh;
namespace
{
class UnrollFlattenTest : public testing::Test
{
public:
UnrollFlattenTest() : mInputSpec(SH_GLES2_SPEC) {}
UnrollFlattenTest(ShShaderSpec inputSpec) : mInputSpec(inputSpec) {}
protected:
void compile(const std::string &shaderString)
{
ShCompileOptions compileOptions = {};
std::string infoLog;
bool compilationSuccess =
compileTestShader(GL_FRAGMENT_SHADER, mInputSpec, SH_HLSL_4_1_OUTPUT, shaderString,
compileOptions, &mTranslatedSource, &infoLog);
if (!compilationSuccess)
{
FAIL() << "Shader compilation failed " << infoLog;
}
mCurrentPosition = static_cast<int>(mTranslatedSource.find("cbuffer DriverConstants"));
}
void expect(const char *patterns[], size_t count)
{
const char *badPatterns[] = {UNROLL, FLATTEN};
for (size_t i = 0; i < count; i++)
{
const char *pattern = patterns[i];
auto position = mTranslatedSource.find(pattern, mCurrentPosition);
if (position == std::string::npos)
{
FAIL() << "Couldn't find '" << pattern << "' after expectations '"
<< mExpectationList << "' in translated source:\n"
<< mTranslatedSource;
}
for (size_t j = 0; j < ArraySize(badPatterns); j++)
{
const char *badPattern = badPatterns[j];
if (pattern != badPattern &&
mTranslatedSource.find(badPattern, mCurrentPosition) < position)
{
FAIL() << "Found '" << badPattern << "' before '" << pattern
<< "' after expectations '" << mExpectationList
<< "' in translated source:\n"
<< mTranslatedSource;
}
}
mExpectationList += " - " + std::string(pattern);
mCurrentPosition = static_cast<int>(position) + 1;
}
}
static const char *UNROLL;
static const char *FLATTEN;
private:
ShShaderSpec mInputSpec;
std::string mTranslatedSource;
int mCurrentPosition;
std::string mExpectationList;
};
const char *UnrollFlattenTest::UNROLL = "LOOP";
const char *UnrollFlattenTest::FLATTEN = "FLATTEN";
TEST_F(UnrollFlattenTest, NoGradient)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float f;\n"
"float fun(float a){\n"
" if (a > 1.0) {return f;}\n"
" else {return a + 1.0;}\n"
"}\n"
"float fun2(float a){\n"
" for (int i = 0; i < 10; i++) {\n"
" if (a > 1.0) {break;}\n"
" a = fun(a);\n"
" }\n"
" return a;\n"
"}\n"
"void main() {\n"
" float accum = 0.0;\n"
" if (f < 5.0) {accum = fun2(accum);}\n"
" gl_FragColor = vec4(accum);\n"
"}\n";
compile(shaderString);
const char *expectations[] = {"fun(", "if", "fun2(", "for", "if",
"break", "fun(", "main(", "if", "fun2("};
expect(expectations, ArraySize(expectations));
}
TEST_F(UnrollFlattenTest, GradientNotInDiscont)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float f;\n"
"uniform sampler2D tex;"
"float fun(float a){\n"
" return texture2D(tex, vec2(0.5, f)).x;\n"
"}\n"
"float fun2(float a){\n"
" for (int i = 0; i < 10; i++) {\n"
" if (a > 1.0) {}\n"
" a = fun(a);\n"
" a += texture2D(tex, vec2(a, 0.0)).x;"
" }\n"
" return a;\n"
"}\n"
"void main() {\n"
" float accum = 0.0;\n"
" if (f < 5.0) {accum = fun2(accum);}\n"
" gl_FragColor = vec4(accum);\n"
"}\n";
compile(shaderString);
const char *expectations[] = {"fun(", "texture2D(", "fun2(", "LOOP", "for", "if",
"fun(", "texture2D(", "main(", "FLATTEN", "if", "fun2("};
expect(expectations, ArraySize(expectations));
}
TEST_F(UnrollFlattenTest, GradientInDiscont)
{
const std::string &shaderString =
"precision mediump float;\n"
"uniform float f;\n"
"uniform sampler2D tex;"
"float fun(float a){\n"
" return texture2D(tex, vec2(0.5, f)).x;\n"
"}\n"
"float fun2(float a){\n"
" for (int i = 0; i < 10; i++) {\n"
" if (a > 1.0) {break;}\n"
" a = fun(a);\n"
" a += texture2D(tex, vec2(a, 0.0)).x;"
" }\n"
" return a;\n"
"}\n"
"void main() {\n"
" float accum = 0.0;\n"
" if (f < 5.0) {accum = fun2(accum);}\n"
" gl_FragColor = vec4(accum);\n"
"}\n";
compile(shaderString);
const char *expectations[] = {
"fun(", "texture2D(", "funLod0(", "texture2DLod0(", "fun2(", "LOOP", "for", "if",
"break", "funLod0(", "texture2DLod0", "main(", "FLATTEN", "if", "fun2("};
expect(expectations, ArraySize(expectations));
}
class UnrollFlattenTestES3 : public UnrollFlattenTest
{
public:
UnrollFlattenTestES3() : UnrollFlattenTest(SH_GLES3_SPEC) {}
};
TEST_F(UnrollFlattenTestES3, TextureBuiltin)
{
const std::string &shaderString =
"#version 300 es\n"
"precision mediump float;\n"
"uniform sampler2D tex;"
"out float fragColor;\n"
"void main() {\n"
" float a = 0.0;"
" for (int i = 0; i < 10; i++) {\n"
" if (a > 1.0) {break;}\n"
" a += texture(tex, vec2(a, 0.0)).x;"
" }\n"
" fragColor = a;\n"
"}\n";
compile(shaderString);
const char *expectations[] = {"main(", "LOOP", "Lod0("};
expect(expectations, ArraySize(expectations));
}
}