1 //
2 // Copyright 2020 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 // EXT_clip_cull_distance_test.cpp:
7 // Test for EXT_clip_cull_distance
8 //
9
10 #include "tests/test_utils/ShaderExtensionTest.h"
11
12 namespace
13 {
14 const char EXTPragma[] = "#extension GL_EXT_clip_cull_distance : require\n";
15
16 // Shader using gl_ClipDistance and gl_CullDistance
17 const char VertexShaderCompileSucceeds1[] =
18 R"(
19 uniform vec4 uPlane;
20
21 in vec4 aPosition;
22
23 void main()
24 {
25 gl_Position = aPosition;
26 gl_ClipDistance[1] = dot(aPosition, uPlane);
27 gl_CullDistance[1] = dot(aPosition, uPlane);
28 })";
29
30 // Shader redeclares gl_ClipDistance and gl_CullDistance
31 const char VertexShaderCompileSucceeds2[] =
32 R"(
33 uniform vec4 uPlane;
34
35 in vec4 aPosition;
36
37 out highp float gl_ClipDistance[4];
38 out highp float gl_CullDistance[4];
39
40 void main()
41 {
42 gl_Position = aPosition;
43 gl_ClipDistance[gl_MaxClipDistances - 6 + 1] = dot(aPosition, uPlane);
44 gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)] = dot(aPosition, uPlane);
45 gl_CullDistance[gl_MaxCullDistances - 6 + 1] = dot(aPosition, uPlane);
46 gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)] = dot(aPosition, uPlane);
47 })";
48
49 #if defined(ANGLE_ENABLE_VULKAN)
50 // Shader using gl_ClipDistance and gl_CullDistance
51 // But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
52 const char VertexShaderCompileFails1[] =
53 R"(
54 uniform vec4 uPlane;
55
56 in vec4 aPosition;
57
58 void main()
59 {
60 gl_Position = aPosition;
61 gl_ClipDistance[5] = dot(aPosition, uPlane);
62 gl_CullDistance[4] = dot(aPosition, uPlane);
63 })";
64
65 // Shader redeclares gl_ClipDistance and gl_CullDistance
66 // But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
67 const char VertexShaderCompileFails2[] =
68 R"(
69 uniform vec4 uPlane;
70
71 in vec4 aPosition;
72
73 out highp float gl_ClipDistance[5];
74 out highp float gl_CullDistance[4];
75
76 void main()
77 {
78 gl_Position = aPosition;
79 gl_ClipDistance[gl_MaxClipDistances - 6 + 1] = dot(aPosition, uPlane);
80 gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)] = dot(aPosition, uPlane);
81 gl_CullDistance[gl_MaxCullDistances - 6 + 1] = dot(aPosition, uPlane);
82 gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)] = dot(aPosition, uPlane);
83 })";
84
85 // Shader redeclares gl_ClipDistance
86 // But, the array size is greater than gl_MaxClipDistances
87 const char VertexShaderCompileFails3[] =
88 R"(
89 uniform vec4 uPlane;
90
91 in vec4 aPosition;
92
93 out highp float gl_ClipDistance[gl_MaxClipDistances + 1];
94
95 void main()
96 {
97 gl_Position = aPosition;
98 gl_ClipDistance[gl_MaxClipDistances - 6 + 1] = dot(aPosition, uPlane);
99 gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)] = dot(aPosition, uPlane);
100 })";
101
102 // Access gl_CullDistance with integral constant index
103 // But, the index is greater than gl_MaxCullDistances
104 const char VertexShaderCompileFails4[] =
105 R"(
106 uniform vec4 uPlane;
107
108 in vec4 aPosition;
109
110 void main()
111 {
112 gl_Position = aPosition;
113 gl_CullDistance[gl_MaxCullDistances] = dot(aPosition, uPlane);
114 })";
115
116 const char VertexShaderCompileFails5[] =
117 R"(
118 uniform vec4 uPlane;
119
120 attribute vec4 aPosition;
121
122 void main()
123 {
124 gl_Position = aPosition;
125 gl_CullDistance[0] = 0.0;
126 })";
127
128 const char VertexShaderCompileFailes6[] =
129 R"(
130 uniform vec4 uPlane;
131
132 attribute vec4 aPosition;
133
134 varying float gl_ClipDistance[1];
135
136 void main()
137 {
138 gl_Position = aPosition;
139 gl_ClipDistance[0] = 0.0;
140 })";
141 #endif
142
143 // Shader using gl_ClipDistance and gl_CullDistance
144 const char FragmentShaderCompileSucceeds1[] =
145 R"(
146 out highp vec4 fragColor;
147
148 void main()
149 {
150 fragColor = vec4(gl_ClipDistance[0], gl_CullDistance[0], 0, 1);
151 })";
152
153 // Shader redeclares gl_ClipDistance and gl_CullDistance
154 const char FragmentShaderCompileSucceeds2[] =
155 R"(
156 in highp float gl_ClipDistance[4];
157 in highp float gl_CullDistance[4];
158
159 in highp vec4 aPosition;
160
161 out highp vec4 fragColor;
162
163 void main()
164 {
165 fragColor.x = gl_ClipDistance[gl_MaxClipDistances - 6 + 1];
166 fragColor.y = gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)];
167 fragColor.z = gl_CullDistance[gl_MaxCullDistances - 6 + 1];
168 fragColor.w = gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)];
169 })";
170
171 #if defined(ANGLE_ENABLE_VULKAN)
172 // Shader using gl_ClipDistance and gl_CullDistance
173 // But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
174 const char FragmentShaderCompileFails1[] =
175 R"(
176 out highp vec4 fragColor;
177
178 void main()
179 {
180 fragColor = vec4(gl_ClipDistance[4], gl_CullDistance[5], 0, 1);
181 })";
182
183 // Shader redeclares gl_ClipDistance and gl_CullDistance
184 // But, the sum of the sizes is greater than gl_MaxCombinedClipAndCullDistances
185 const char FragmentShaderCompileFails2[] =
186 R"(
187 in highp float gl_ClipDistance[5];
188 in highp float gl_CullDistance[4];
189
190 in highp vec4 aPosition;
191
192 out highp vec4 fragColor;
193
194 void main()
195 {
196 fragColor.x = gl_ClipDistance[gl_MaxClipDistances - 6 + 1];
197 fragColor.y = gl_ClipDistance[gl_MaxClipDistances - int(aPosition.x)];
198 fragColor.z = gl_CullDistance[gl_MaxCullDistances - 6 + 1];
199 fragColor.w = gl_CullDistance[gl_MaxCullDistances - int(aPosition.x)];
200 })";
201
202 // In fragment shader, writing to gl_ClipDistance should be denied.
203 const char FragmentShaderCompileFails3[] =
204 R"(
205 out highp vec4 fragColor;
206
207 void main()
208 {
209 gl_ClipDistance[0] = 0.0f;
210 fragColor = vec4(1, gl_ClipDistance[0], 0, 1);
211 })";
212
213 // In fragment shader, writing to gl_CullDistance should be denied even if redeclaring it with the
214 // array size
215 const char FragmentShaderCompileFails4[] =
216 R"(
217 out highp vec4 fragColor;
218
219 in highp float gl_CullDistance[1];
220
221 void main()
222 {
223 gl_CullDistance[0] = 0.0f;
224 fragColor = vec4(1, gl_CullDistance[0], 0, 1);
225 })";
226
227 // Accessing to gl_Clip/CullDistance with non-const index should be denied if the size of
228 // gl_Clip/CullDistance is not decided.
229 const char FragmentShaderCompileFails5[] =
230 R"(
231 out highp vec4 fragColor;
232
233 void main()
234 {
235 medium float color[3];
236 for(int i = 0 ; i < 3 ; i++)
237 {
238 color[i] = gl_CullDistance[i];
239 }
240 fragColor = vec4(color[0], color[1], color[2], 1.0f);
241 })";
242 #endif
243
244 class EXTClipCullDistanceTest : public sh::ShaderExtensionTest
245 {
246 public:
InitializeCompiler(ShShaderOutput shaderOutputType,GLenum shaderType)247 void InitializeCompiler(ShShaderOutput shaderOutputType, GLenum shaderType)
248 {
249 DestroyCompiler();
250
251 mCompiler = sh::ConstructCompiler(shaderType, testing::get<0>(GetParam()), shaderOutputType,
252 &mResources);
253 ASSERT_TRUE(mCompiler != nullptr) << "Compiler could not be constructed.";
254 }
255
TestShaderCompile(const char * pragma)256 testing::AssertionResult TestShaderCompile(const char *pragma)
257 {
258 const char *shaderStrings[] = {testing::get<1>(GetParam()), pragma,
259 testing::get<2>(GetParam())};
260 bool success = sh::Compile(mCompiler, shaderStrings, 3, SH_VARIABLES | SH_OBJECT_CODE);
261 if (success)
262 {
263 return ::testing::AssertionSuccess() << "Compilation success";
264 }
265 return ::testing::AssertionFailure() << sh::GetInfoLog(mCompiler);
266 }
267
SetExtensionEnable(bool enable)268 void SetExtensionEnable(bool enable)
269 {
270 // GL_APPLE_clip_distance is implicitly enabled when GL_EXT_clip_cull_distance is enabled
271 #if defined(ANGLE_ENABLE_VULKAN)
272 mResources.APPLE_clip_distance = enable;
273 #endif
274 mResources.EXT_clip_cull_distance = enable;
275 }
276 };
277
278 class EXTClipCullDistanceForVertexShaderTest : public EXTClipCullDistanceTest
279 {
280 public:
InitializeCompiler()281 void InitializeCompiler() { InitializeCompiler(SH_GLSL_450_CORE_OUTPUT); }
InitializeCompiler(ShShaderOutput shaderOutputType)282 void InitializeCompiler(ShShaderOutput shaderOutputType)
283 {
284 EXTClipCullDistanceTest::InitializeCompiler(shaderOutputType, GL_VERTEX_SHADER);
285 }
286 };
287
288 class EXTClipCullDistanceForFragmentShaderTest : public EXTClipCullDistanceTest
289 {
290 public:
InitializeCompiler()291 void InitializeCompiler() { InitializeCompiler(SH_GLSL_450_CORE_OUTPUT); }
InitializeCompiler(ShShaderOutput shaderOutputType)292 void InitializeCompiler(ShShaderOutput shaderOutputType)
293 {
294 EXTClipCullDistanceTest::InitializeCompiler(shaderOutputType, GL_FRAGMENT_SHADER);
295 }
296 };
297
298 // Extension flag is required to compile properly. Expect failure when it is
299 // not present.
TEST_P(EXTClipCullDistanceForVertexShaderTest,CompileFailsWithoutExtension)300 TEST_P(EXTClipCullDistanceForVertexShaderTest, CompileFailsWithoutExtension)
301 {
302 SetExtensionEnable(false);
303 InitializeCompiler();
304 EXPECT_FALSE(TestShaderCompile(EXTPragma));
305 }
306
307 // Extension directive is required to compile properly. Expect failure when
308 // it is not present.
TEST_P(EXTClipCullDistanceForVertexShaderTest,CompileFailsWithExtensionWithoutPragma)309 TEST_P(EXTClipCullDistanceForVertexShaderTest, CompileFailsWithExtensionWithoutPragma)
310 {
311 SetExtensionEnable(true);
312 InitializeCompiler();
313 EXPECT_FALSE(TestShaderCompile(""));
314 }
315
316 #if defined(ANGLE_ENABLE_VULKAN)
317 // With extension flag and extension directive, compiling using TranslatorVulkan succeeds.
TEST_P(EXTClipCullDistanceForVertexShaderTest,CompileSucceedsVulkan)318 TEST_P(EXTClipCullDistanceForVertexShaderTest, CompileSucceedsVulkan)
319 {
320 SetExtensionEnable(true);
321
322 mResources.MaxClipDistances = 8;
323 mResources.MaxCullDistances = 8;
324 mResources.MaxCombinedClipAndCullDistances = 8;
325
326 InitializeCompiler(SH_SPIRV_VULKAN_OUTPUT);
327 EXPECT_TRUE(TestShaderCompile(EXTPragma));
328 EXPECT_FALSE(TestShaderCompile(""));
329 EXPECT_TRUE(TestShaderCompile(EXTPragma));
330 }
331 #endif
332
333 // Extension flag is required to compile properly. Expect failure when it is
334 // not present.
TEST_P(EXTClipCullDistanceForFragmentShaderTest,CompileFailsWithoutExtension)335 TEST_P(EXTClipCullDistanceForFragmentShaderTest, CompileFailsWithoutExtension)
336 {
337 SetExtensionEnable(false);
338 InitializeCompiler();
339 EXPECT_FALSE(TestShaderCompile(EXTPragma));
340 }
341
342 // Extension directive is required to compile properly. Expect failure when
343 // it is not present.
TEST_P(EXTClipCullDistanceForFragmentShaderTest,CompileFailsWithExtensionWithoutPragma)344 TEST_P(EXTClipCullDistanceForFragmentShaderTest, CompileFailsWithExtensionWithoutPragma)
345 {
346 SetExtensionEnable(true);
347 InitializeCompiler();
348 EXPECT_FALSE(TestShaderCompile(""));
349 }
350
351 #if defined(ANGLE_ENABLE_VULKAN)
352 // With extension flag and extension directive, compiling using TranslatorVulkan succeeds.
353 //
354 // Test is disabled due to translation bug. http://anglebug.com/5747
TEST_P(EXTClipCullDistanceForFragmentShaderTest,DISABLED_CompileSucceedsVulkan)355 TEST_P(EXTClipCullDistanceForFragmentShaderTest, DISABLED_CompileSucceedsVulkan)
356 {
357 SetExtensionEnable(true);
358
359 mResources.MaxClipDistances = 8;
360 mResources.MaxCullDistances = 8;
361 mResources.MaxCombinedClipAndCullDistances = 8;
362
363 InitializeCompiler(SH_SPIRV_VULKAN_OUTPUT);
364 EXPECT_TRUE(TestShaderCompile(EXTPragma));
365 EXPECT_FALSE(TestShaderCompile(""));
366 EXPECT_TRUE(TestShaderCompile(EXTPragma));
367 }
368
369 class EXTClipCullDistanceForVertexShaderCompileFailureTest
370 : public EXTClipCullDistanceForVertexShaderTest
371 {};
372
373 class EXTClipCullDistanceForFragmentShaderCompileFailureTest
374 : public EXTClipCullDistanceForFragmentShaderTest
375 {};
376
TEST_P(EXTClipCullDistanceForVertexShaderCompileFailureTest,CompileFails)377 TEST_P(EXTClipCullDistanceForVertexShaderCompileFailureTest, CompileFails)
378 {
379 SetExtensionEnable(true);
380
381 mResources.MaxClipDistances = 8;
382 mResources.MaxCullDistances = 8;
383 mResources.MaxCombinedClipAndCullDistances = 8;
384
385 InitializeCompiler(SH_SPIRV_VULKAN_OUTPUT);
386 EXPECT_FALSE(TestShaderCompile(EXTPragma));
387 }
388
TEST_P(EXTClipCullDistanceForFragmentShaderCompileFailureTest,CompileFails)389 TEST_P(EXTClipCullDistanceForFragmentShaderCompileFailureTest, CompileFails)
390 {
391 SetExtensionEnable(true);
392
393 mResources.MaxClipDistances = 8;
394 mResources.MaxCullDistances = 8;
395 mResources.MaxCombinedClipAndCullDistances = 8;
396
397 InitializeCompiler(SH_SPIRV_VULKAN_OUTPUT);
398 EXPECT_FALSE(TestShaderCompile(EXTPragma));
399 }
400 #endif
401
402 INSTANTIATE_TEST_SUITE_P(CorrectESSL300Shaders,
403 EXTClipCullDistanceForVertexShaderTest,
404 Combine(Values(SH_GLES3_SPEC),
405 Values(sh::ESSLVersion300),
406 Values(VertexShaderCompileSucceeds1,
407 VertexShaderCompileSucceeds2)));
408
409 INSTANTIATE_TEST_SUITE_P(CorrectESSL300Shaders,
410 EXTClipCullDistanceForFragmentShaderTest,
411 Combine(Values(SH_GLES3_SPEC),
412 Values(sh::ESSLVersion300),
413 Values(FragmentShaderCompileSucceeds1,
414 FragmentShaderCompileSucceeds2)));
415
416 // The corresponding TEST_Ps are defined only when ANGLE_ENABLE_VULKAN is
417 // defined.
418 #if defined(ANGLE_ENABLE_VULKAN)
419 INSTANTIATE_TEST_SUITE_P(IncorrectESSL100Shaders,
420 EXTClipCullDistanceForVertexShaderCompileFailureTest,
421 Combine(Values(SH_GLES2_SPEC),
422 Values(sh::ESSLVersion100),
423 Values(VertexShaderCompileFails5, VertexShaderCompileFailes6)));
424
425 INSTANTIATE_TEST_SUITE_P(IncorrectESSL300Shaders,
426 EXTClipCullDistanceForVertexShaderCompileFailureTest,
427 Combine(Values(SH_GLES3_SPEC),
428 Values(sh::ESSLVersion300),
429 Values(VertexShaderCompileFails1,
430 VertexShaderCompileFails2,
431 VertexShaderCompileFails3,
432 VertexShaderCompileFails4)));
433
434 INSTANTIATE_TEST_SUITE_P(IncorrectESSL300Shaders,
435 EXTClipCullDistanceForFragmentShaderCompileFailureTest,
436 Combine(Values(SH_GLES3_SPEC),
437 Values(sh::ESSLVersion300),
438 Values(FragmentShaderCompileFails1,
439 FragmentShaderCompileFails2,
440 FragmentShaderCompileFails3,
441 FragmentShaderCompileFails4,
442 FragmentShaderCompileFails5)));
443 #endif
444
445 } // anonymous namespace
446