1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 // ShCompile_test.cpp
7 // Test the sh::Compile interface with different parameters.
8 //
9
10 #include <clocale>
11 #include "GLSLANG/ShaderLang.h"
12 #include "angle_gl.h"
13 #include "common/angleutils.h"
14 #include "common/platform.h"
15 #include "gtest/gtest.h"
16
17 class ShCompileTest : public testing::Test
18 {
19 public:
ShCompileTest()20 ShCompileTest() {}
21
22 protected:
SetUp()23 void SetUp() override
24 {
25 sh::InitBuiltInResources(&mResources);
26 mCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_WEBGL_SPEC,
27 SH_GLSL_COMPATIBILITY_OUTPUT, &mResources);
28 ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
29 }
30
TearDown()31 void TearDown() override
32 {
33 if (mCompiler)
34 {
35 sh::Destruct(mCompiler);
36 mCompiler = nullptr;
37 }
38 }
39
testCompile(const char ** shaderStrings,int stringCount,bool expectation)40 void testCompile(const char **shaderStrings, int stringCount, bool expectation)
41 {
42 ShCompileOptions options = {};
43 options.objectCode = true;
44 options.initOutputVariables = true;
45
46 bool success = sh::Compile(mCompiler, shaderStrings, stringCount, options);
47 const std::string &compileLog = sh::GetInfoLog(mCompiler);
48 EXPECT_EQ(expectation, success) << compileLog;
49 }
50
51 ShBuiltInResources mResources;
52
53 class [[nodiscard]] ScopedRestoreDefaultLocale : angle::NonCopyable
54 {
55 public:
56 ScopedRestoreDefaultLocale();
57 ~ScopedRestoreDefaultLocale();
58
59 private:
60 std::locale defaultLocale;
61 };
62
63 public:
64 ShHandle mCompiler;
65 };
66
ScopedRestoreDefaultLocale()67 ShCompileTest::ScopedRestoreDefaultLocale::ScopedRestoreDefaultLocale()
68 {
69 defaultLocale = std::locale();
70 }
71
~ScopedRestoreDefaultLocale()72 ShCompileTest::ScopedRestoreDefaultLocale::~ScopedRestoreDefaultLocale()
73 {
74 std::locale::global(defaultLocale);
75 }
76
77 class ShCompileComputeTest : public ShCompileTest
78 {
79 public:
ShCompileComputeTest()80 ShCompileComputeTest() {}
81
82 protected:
SetUp()83 void SetUp() override
84 {
85 sh::InitBuiltInResources(&mResources);
86 mCompiler = sh::ConstructCompiler(GL_COMPUTE_SHADER, SH_WEBGL3_SPEC,
87 SH_GLSL_COMPATIBILITY_OUTPUT, &mResources);
88 ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
89 }
90 };
91
92 // Test calling sh::Compile with compute shader source string.
TEST_F(ShCompileComputeTest,ComputeShaderString)93 TEST_F(ShCompileComputeTest, ComputeShaderString)
94 {
95 constexpr char kComputeShaderString[] =
96 R"(#version 310 es
97 layout(local_size_x=1) in;
98 void main()
99 {
100 })";
101
102 const char *shaderStrings[] = {kComputeShaderString};
103
104 testCompile(shaderStrings, 1, true);
105 }
106
107 // Test calling sh::Compile with more than one shader source string.
TEST_F(ShCompileTest,MultipleShaderStrings)108 TEST_F(ShCompileTest, MultipleShaderStrings)
109 {
110 const std::string &shaderString1 =
111 "precision mediump float;\n"
112 "void main() {\n";
113 const std::string &shaderString2 =
114 " gl_FragColor = vec4(0.0);\n"
115 "}";
116
117 const char *shaderStrings[] = {shaderString1.c_str(), shaderString2.c_str()};
118
119 testCompile(shaderStrings, 2, true);
120 }
121
122 // Test calling sh::Compile with a tokens split into different shader source strings.
TEST_F(ShCompileTest,TokensSplitInShaderStrings)123 TEST_F(ShCompileTest, TokensSplitInShaderStrings)
124 {
125 const std::string &shaderString1 =
126 "precision mediump float;\n"
127 "void ma";
128 const std::string &shaderString2 =
129 "in() {\n"
130 "#i";
131 const std::string &shaderString3 =
132 "f 1\n"
133 " gl_FragColor = vec4(0.0);\n"
134 "#endif\n"
135 "}";
136
137 const char *shaderStrings[] = {shaderString1.c_str(), shaderString2.c_str(),
138 shaderString3.c_str()};
139
140 testCompile(shaderStrings, 3, true);
141 }
142
143 // Parsing floats in shaders can run afoul of locale settings.
144 // Eg. in de_DE, `strtof("1.5")` will yield `1.0f`. (It's expecting "1.5")
TEST_F(ShCompileTest,DecimalSepLocale)145 TEST_F(ShCompileTest, DecimalSepLocale)
146 {
147 // Locale names are platform dependent, add platform-specific names of locales to be tested here
148 const std::string availableLocales[] = {
149 "de_DE",
150 "de-DE",
151 "de_DE.UTF-8",
152 "de_DE.ISO8859-1",
153 "de_DE.ISO8859-15",
154 "de_DE@euro",
155 "de_DE.88591",
156 "de_DE.88591.en",
157 "de_DE.iso88591",
158 "de_DE.ISO-8859-1",
159 "de_DE.ISO_8859-1",
160 "de_DE.iso885915",
161 "de_DE.ISO-8859-15",
162 "de_DE.ISO_8859-15",
163 "de_DE.8859-15",
164 "de_DE.8859-15@euro",
165 #if !defined(_WIN32)
166 // TODO(https://crbug.com/972372): Add this test back on Windows once the
167 // CRT no longer throws on code page sections ('ISO-8859-15@euro') that
168 // are >= 16 characters long.
169 "de_DE.ISO-8859-15@euro",
170 #endif
171 "de_DE.UTF-8@euro",
172 "de_DE.utf8",
173 "German_germany",
174 "German_Germany",
175 "German_Germany.1252",
176 "German_Germany.UTF-8",
177 "German",
178 // One ubuntu tester doesn't have a german locale, but da_DK.utf8 has similar float
179 // representation
180 "da_DK.utf8"
181 };
182
183 const auto localeExists = [](const std::string name) {
184 return bool(setlocale(LC_ALL, name.c_str()));
185 };
186
187 const char kSource[] = R"(
188 void main()
189 {
190 gl_FragColor = vec4(1.5);
191 })";
192 const char *parts[] = {kSource};
193
194 // Ensure the locale is reset after the test runs.
195 ScopedRestoreDefaultLocale restoreLocale;
196
197 for (const std::string &locale : availableLocales)
198 {
199 // If the locale doesn't exist on the testing platform, the locale constructor will fail,
200 // throwing an exception
201 // We use setlocale() (through localeExists) to test whether a locale
202 // exists before calling the locale constructor
203 if (localeExists(locale))
204 {
205 std::locale localizedLoc(locale);
206
207 ShCompileOptions compileOptions = {};
208 compileOptions.objectCode = true;
209
210 // std::locale::global() must be used instead of setlocale() to affect new streams'
211 // default locale
212 std::locale::global(std::locale::classic());
213 sh::Compile(mCompiler, parts, 1, compileOptions);
214 std::string referenceOut = sh::GetObjectCode(mCompiler);
215 EXPECT_NE(referenceOut.find("1.5"), std::string::npos)
216 << "float formatted incorrectly with classic locale";
217
218 sh::ClearResults(mCompiler);
219
220 std::locale::global(localizedLoc);
221 sh::Compile(mCompiler, parts, 1, compileOptions);
222 std::string localizedOut = sh::GetObjectCode(mCompiler);
223 EXPECT_NE(localizedOut.find("1.5"), std::string::npos)
224 << "float formatted incorrectly with locale (" << localizedLoc.name() << ") set";
225
226 ASSERT_EQ(referenceOut, localizedOut)
227 << "different output with locale (" << localizedLoc.name() << ") set";
228 }
229 }
230 }
231