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