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 = SH_OBJECT_CODE | SH_VARIABLES | SH_INIT_OUTPUT_VARIABLES;
43 bool success = sh::Compile(mCompiler, shaderStrings, stringCount, options);
44 const std::string &compileLog = sh::GetInfoLog(mCompiler);
45 EXPECT_EQ(expectation, success) << compileLog;
46 }
47
48 ShBuiltInResources mResources;
49
50 class ANGLE_NO_DISCARD ScopedRestoreDefaultLocale : angle::NonCopyable
51 {
52 public:
53 ScopedRestoreDefaultLocale();
54 ~ScopedRestoreDefaultLocale();
55
56 private:
57 std::locale defaultLocale;
58 };
59
60 public:
61 ShHandle mCompiler;
62 };
63
ScopedRestoreDefaultLocale()64 ShCompileTest::ScopedRestoreDefaultLocale::ScopedRestoreDefaultLocale()
65 {
66 defaultLocale = std::locale();
67 }
68
~ScopedRestoreDefaultLocale()69 ShCompileTest::ScopedRestoreDefaultLocale::~ScopedRestoreDefaultLocale()
70 {
71 std::locale::global(defaultLocale);
72 }
73
74 class ShCompileComputeTest : public ShCompileTest
75 {
76 public:
ShCompileComputeTest()77 ShCompileComputeTest() {}
78
79 protected:
SetUp()80 void SetUp() override
81 {
82 sh::InitBuiltInResources(&mResources);
83 mCompiler = sh::ConstructCompiler(GL_COMPUTE_SHADER, SH_WEBGL3_SPEC,
84 SH_GLSL_COMPATIBILITY_OUTPUT, &mResources);
85 ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
86 }
87 };
88
89 // Test calling sh::Compile with compute shader source string.
TEST_F(ShCompileComputeTest,ComputeShaderString)90 TEST_F(ShCompileComputeTest, ComputeShaderString)
91 {
92 constexpr char kComputeShaderString[] =
93 R"(#version 310 es
94 layout(local_size_x=1) in;
95 void main()
96 {
97 })";
98
99 const char *shaderStrings[] = {kComputeShaderString};
100
101 testCompile(shaderStrings, 1, true);
102 }
103
104 // Test calling sh::Compile with more than one shader source string.
TEST_F(ShCompileTest,MultipleShaderStrings)105 TEST_F(ShCompileTest, MultipleShaderStrings)
106 {
107 const std::string &shaderString1 =
108 "precision mediump float;\n"
109 "void main() {\n";
110 const std::string &shaderString2 =
111 " gl_FragColor = vec4(0.0);\n"
112 "}";
113
114 const char *shaderStrings[] = {shaderString1.c_str(), shaderString2.c_str()};
115
116 testCompile(shaderStrings, 2, true);
117 }
118
119 // Test calling sh::Compile with a tokens split into different shader source strings.
TEST_F(ShCompileTest,TokensSplitInShaderStrings)120 TEST_F(ShCompileTest, TokensSplitInShaderStrings)
121 {
122 const std::string &shaderString1 =
123 "precision mediump float;\n"
124 "void ma";
125 const std::string &shaderString2 =
126 "in() {\n"
127 "#i";
128 const std::string &shaderString3 =
129 "f 1\n"
130 " gl_FragColor = vec4(0.0);\n"
131 "#endif\n"
132 "}";
133
134 const char *shaderStrings[] = {shaderString1.c_str(), shaderString2.c_str(),
135 shaderString3.c_str()};
136
137 testCompile(shaderStrings, 3, true);
138 }
139
140 // Parsing floats in shaders can run afoul of locale settings.
141 // Eg. in de_DE, `strtof("1.9")` will yield `1.0f`. (It's expecting "1,9")
TEST_F(ShCompileTest,DecimalSepLocale)142 TEST_F(ShCompileTest, DecimalSepLocale)
143 {
144 // Locale names are platform dependent, add platform-specific names of locales to be tested here
145 const std::string availableLocales[] = {
146 "de_DE",
147 "de-DE",
148 "de_DE.UTF-8",
149 "de_DE.ISO8859-1",
150 "de_DE.ISO8859-15",
151 "de_DE@euro",
152 "de_DE.88591",
153 "de_DE.88591.en",
154 "de_DE.iso88591",
155 "de_DE.ISO-8859-1",
156 "de_DE.ISO_8859-1",
157 "de_DE.iso885915",
158 "de_DE.ISO-8859-15",
159 "de_DE.ISO_8859-15",
160 "de_DE.8859-15",
161 "de_DE.8859-15@euro",
162 #if !defined(_WIN32)
163 // TODO(https://crbug.com/972372): Add this test back on Windows once the
164 // CRT no longer throws on code page sections ('ISO-8859-15@euro') that
165 // are >= 16 characters long.
166 "de_DE.ISO-8859-15@euro",
167 #endif
168 "de_DE.UTF-8@euro",
169 "de_DE.utf8",
170 "German_germany",
171 "German_Germany",
172 "German_Germany.1252",
173 "German_Germany.UTF-8",
174 "German",
175 // One ubuntu tester doesn't have a german locale, but da_DK.utf8 has similar float
176 // representation
177 "da_DK.utf8"
178 };
179
180 const auto localeExists = [](const std::string name) {
181 return bool(setlocale(LC_ALL, name.c_str()));
182 };
183
184 const char kSource[] = R"(
185 void main()
186 {
187 gl_FragColor = vec4(1.9);
188 })";
189 const char *parts[] = {kSource};
190
191 // Ensure the locale is reset after the test runs.
192 ScopedRestoreDefaultLocale restoreLocale;
193
194 for (const std::string &locale : availableLocales)
195 {
196 // If the locale doesn't exist on the testing platform, the locale constructor will fail,
197 // throwing an exception
198 // We use setlocale() (through localeExists) to test whether a locale
199 // exists before calling the locale constructor
200 if (localeExists(locale))
201 {
202 std::locale localizedLoc(locale);
203
204 // std::locale::global() must be used instead of setlocale() to affect new streams'
205 // default locale
206 std::locale::global(std::locale::classic());
207 sh::Compile(mCompiler, parts, 1, SH_OBJECT_CODE);
208 std::string referenceOut = sh::GetObjectCode(mCompiler);
209 EXPECT_NE(referenceOut.find("1.9"), std::string::npos)
210 << "float formatted incorrectly with classic locale";
211
212 sh::ClearResults(mCompiler);
213
214 std::locale::global(localizedLoc);
215 sh::Compile(mCompiler, parts, 1, SH_OBJECT_CODE);
216 std::string localizedOut = sh::GetObjectCode(mCompiler);
217 EXPECT_NE(localizedOut.find("1.9"), std::string::npos)
218 << "float formatted incorrectly with locale (" << localizedLoc.name() << ") set";
219
220 ASSERT_EQ(referenceOut, localizedOut)
221 << "different output with locale (" << localizedLoc.name() << ") set";
222 }
223 }
224 }
225
226 // Desktop GLSL support is not enabled on Android
227 #if !defined(ANGLE_PLATFORM_ANDROID)
228
229 // For testing Desktop GL Shaders
230 class ShCompileDesktopGLTest : public ShCompileTest
231 {
232 public:
ShCompileDesktopGLTest()233 ShCompileDesktopGLTest() {}
234
235 protected:
SetUp()236 void SetUp() override
237 {
238 sh::InitBuiltInResources(&mResources);
239 mCompiler = sh::ConstructCompiler(GL_FRAGMENT_SHADER, SH_GL_COMPATIBILITY_SPEC,
240 SH_GLSL_330_CORE_OUTPUT, &mResources);
241 ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
242 }
243 };
244
245 // Test calling sh::Compile with fragment shader source string
TEST_F(ShCompileDesktopGLTest,DesktopGLString)246 TEST_F(ShCompileDesktopGLTest, DesktopGLString)
247 {
248 constexpr char kFragmentShaderString[] =
249 R"(#version 330
250 void main()
251 {
252 })";
253
254 const char *shaderStrings[] = {kFragmentShaderString};
255
256 testCompile(shaderStrings, 1, true);
257 }
258
259 // Test calling sh::Compile with core version
TEST_F(ShCompileDesktopGLTest,FragmentShaderCoreVersion)260 TEST_F(ShCompileDesktopGLTest, FragmentShaderCoreVersion)
261 {
262 constexpr char kFragmentShaderString[] =
263 R"(#version 330 core
264 void main()
265 {
266 })";
267
268 const char *shaderStrings[] = {kFragmentShaderString};
269
270 testCompile(shaderStrings, 1, true);
271 }
272
273 // Implicit conversions in basic operations
TEST_F(ShCompileDesktopGLTest,ImplicitConversionBasicOperation)274 TEST_F(ShCompileDesktopGLTest, ImplicitConversionBasicOperation)
275 {
276 constexpr char kFragmentShaderString[] =
277 R"(#version 330 core
278 void main()
279 {
280 //float a = 1 + 1.5;
281 //float b = 1 - 1.5;
282 //float c = 1 * 1.5;
283 //float d = 1 / 1.5;
284 //float e = 1.5 + 1;
285 //float f = 1.5 - 1;
286 float g = 1.5 * 1;
287 //float h = 1.5 / 1;
288 })";
289
290 const char *shaderStrings[] = {kFragmentShaderString};
291
292 testCompile(shaderStrings, 1, true);
293 }
294
295 // Implicit conversions when assigning
TEST_F(ShCompileDesktopGLTest,ImplicitConversionAssign)296 TEST_F(ShCompileDesktopGLTest, ImplicitConversionAssign)
297 {
298 constexpr char kFragmentShaderString[] =
299 R"(#version 330 core
300 void main()
301 {
302 float a = 1;
303 uint b = 2u;
304 a = b;
305 a += b;
306 a -= b;
307 a *= b;
308 a /= b;
309 })";
310
311 const char *shaderStrings[] = {kFragmentShaderString};
312
313 testCompile(shaderStrings, 1, true);
314 }
315
316 // Implicit conversions for vectors
TEST_F(ShCompileDesktopGLTest,ImplicitConversionVector)317 TEST_F(ShCompileDesktopGLTest, ImplicitConversionVector)
318 {
319 constexpr char kFragmentShaderString[] =
320 R"(#version 330 core
321 void main()
322 {
323 vec3 a;
324 ivec3 b = ivec3(1, 1, 1);
325 a = b;
326 })";
327
328 const char *shaderStrings[] = {kFragmentShaderString};
329
330 testCompile(shaderStrings, 1, true);
331 }
332
333 // Implicit conversions should not convert between ints and uints
TEST_F(ShCompileDesktopGLTest,ImplicitConversionAssignFailed)334 TEST_F(ShCompileDesktopGLTest, ImplicitConversionAssignFailed)
335 {
336 constexpr char kFragmentShaderString[] =
337 R"(#version 330 core
338 void main()
339 {
340 int a = 1;
341 uint b = 2;
342 a = b;
343 })";
344
345 const char *shaderStrings[] = {kFragmentShaderString};
346
347 testCompile(shaderStrings, 1, false);
348 }
349
350 // GL shaders use implicit conversions between types
351 // Testing internal implicit conversions
TEST_F(ShCompileDesktopGLTest,ImplicitConversionFunction)352 TEST_F(ShCompileDesktopGLTest, ImplicitConversionFunction)
353 {
354 constexpr char kFragmentShaderString[] =
355 R"(#version 330 core
356 void main()
357 {
358 float cosTheta = clamp(0.5,0,1);
359 float exp = pow(0.5,2);
360 })";
361
362 const char *shaderStrings[] = {kFragmentShaderString};
363
364 testCompile(shaderStrings, 1, true);
365 }
366
367 #endif // !defined(ANGLE_PLATFORM_ANDROID)
368