1 //
2 // Copyright 2024 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 // These tests verify shadow sampler functions and their options.
7
8 #include "common/gl_enum_utils.h"
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11
12 using namespace angle;
13
14 namespace
15 {
16
17 enum class FunctionType
18 {
19 Texture,
20 TextureBias,
21 TextureOffset,
22 TextureOffsetBias,
23 TextureLod,
24 TextureLodOffset,
25 TextureGrad,
26 TextureGradOffset,
27 TextureProj,
28 TextureProjBias,
29 TextureProjOffset,
30 TextureProjOffsetBias,
31 TextureProjLod,
32 TextureProjLodOffset,
33 TextureProjGrad,
34 TextureProjGradOffset,
35 };
36
FunctionName(FunctionType function)37 const char *FunctionName(FunctionType function)
38 {
39 switch (function)
40 {
41 case FunctionType::Texture:
42 case FunctionType::TextureBias:
43 return "texture";
44 case FunctionType::TextureOffset:
45 case FunctionType::TextureOffsetBias:
46 return "textureOffset";
47 case FunctionType::TextureLod:
48 return "textureLod";
49 case FunctionType::TextureLodOffset:
50 return "textureLodOffset";
51 case FunctionType::TextureGrad:
52 return "textureGrad";
53 case FunctionType::TextureGradOffset:
54 return "textureGradOffset";
55 case FunctionType::TextureProj:
56 case FunctionType::TextureProjBias:
57 return "textureProj";
58 case FunctionType::TextureProjOffset:
59 case FunctionType::TextureProjOffsetBias:
60 return "textureProjOffset";
61 case FunctionType::TextureProjLod:
62 return "textureProjLod";
63 case FunctionType::TextureProjLodOffset:
64 return "textureProjLodOffset";
65 case FunctionType::TextureProjGrad:
66 return "textureProjGrad";
67 case FunctionType::TextureProjGradOffset:
68 return "textureProjGradOffset";
69 }
70 }
71
IsProj(FunctionType function)72 constexpr bool IsProj(FunctionType function)
73 {
74 switch (function)
75 {
76 case FunctionType::TextureProj:
77 case FunctionType::TextureProjBias:
78 case FunctionType::TextureProjOffset:
79 case FunctionType::TextureProjOffsetBias:
80 case FunctionType::TextureProjLod:
81 case FunctionType::TextureProjLodOffset:
82 case FunctionType::TextureProjGrad:
83 case FunctionType::TextureProjGradOffset:
84 return true;
85 default:
86 return false;
87 }
88 }
89
HasBias(FunctionType function)90 constexpr bool HasBias(FunctionType function)
91 {
92 switch (function)
93 {
94 case FunctionType::TextureBias:
95 case FunctionType::TextureOffsetBias:
96 case FunctionType::TextureProjBias:
97 case FunctionType::TextureProjOffsetBias:
98 return true;
99 default:
100 return false;
101 }
102 }
103
HasLOD(FunctionType function)104 constexpr bool HasLOD(FunctionType function)
105 {
106 switch (function)
107 {
108 case FunctionType::TextureLod:
109 case FunctionType::TextureLodOffset:
110 case FunctionType::TextureProjLod:
111 case FunctionType::TextureProjLodOffset:
112 return true;
113 default:
114 return false;
115 }
116 }
117
HasGrad(FunctionType function)118 constexpr bool HasGrad(FunctionType function)
119 {
120 switch (function)
121 {
122 case FunctionType::TextureGrad:
123 case FunctionType::TextureGradOffset:
124 case FunctionType::TextureProjGrad:
125 case FunctionType::TextureProjGradOffset:
126 return true;
127 default:
128 return false;
129 }
130 }
131
HasOffset(FunctionType function)132 constexpr bool HasOffset(FunctionType function)
133 {
134 switch (function)
135 {
136 case FunctionType::TextureOffset:
137 case FunctionType::TextureOffsetBias:
138 case FunctionType::TextureLodOffset:
139 case FunctionType::TextureGradOffset:
140 case FunctionType::TextureProjOffset:
141 case FunctionType::TextureProjOffsetBias:
142 case FunctionType::TextureProjLodOffset:
143 case FunctionType::TextureProjGradOffset:
144 return true;
145 default:
146 return false;
147 }
148 }
149
RequiresExtensionFor2DArray(FunctionType function)150 constexpr bool RequiresExtensionFor2DArray(FunctionType function)
151 {
152 switch (function)
153 {
154 case FunctionType::TextureBias:
155 case FunctionType::TextureOffset:
156 case FunctionType::TextureOffsetBias:
157 case FunctionType::TextureLod:
158 case FunctionType::TextureLodOffset:
159 return true;
160 default:
161 return false;
162 }
163 }
164
RequiresExtensionForCube(FunctionType function)165 constexpr bool RequiresExtensionForCube(FunctionType function)
166 {
167 switch (function)
168 {
169 case FunctionType::TextureLod:
170 return true;
171 default:
172 return false;
173 }
174 }
175
RequiresExtensionForCubeArray(FunctionType function)176 constexpr bool RequiresExtensionForCubeArray(FunctionType function)
177 {
178 switch (function)
179 {
180 case FunctionType::TextureBias:
181 case FunctionType::TextureLod:
182 return true;
183 default:
184 return false;
185 }
186 }
187
Compare(float reference,float sampled,GLenum op)188 bool Compare(float reference, float sampled, GLenum op)
189 {
190 switch (op)
191 {
192 case GL_NEVER:
193 return false;
194 case GL_LESS:
195 return reference < sampled;
196 case GL_EQUAL:
197 return reference == sampled;
198 case GL_LEQUAL:
199 return reference <= sampled;
200 case GL_GREATER:
201 return reference > sampled;
202 case GL_NOTEQUAL:
203 return reference != sampled;
204 case GL_GEQUAL:
205 return reference >= sampled;
206 case GL_ALWAYS:
207 return true;
208 default:
209 UNREACHABLE();
210 return false;
211 }
212 }
213
214 // Variations corresponding to enums above.
215 using ShadowSamplerFunctionVariationsTestParams =
216 std::tuple<angle::PlatformParameters, FunctionType, bool>;
217
operator <<(std::ostream & out,FunctionType function)218 std::ostream &operator<<(std::ostream &out, FunctionType function)
219 {
220 switch (function)
221 {
222 case FunctionType::Texture:
223 out << "Texture";
224 break;
225 case FunctionType::TextureBias:
226 out << "TextureBias";
227 break;
228 case FunctionType::TextureOffset:
229 out << "TextureOffset";
230 break;
231 case FunctionType::TextureOffsetBias:
232 out << "TextureOffsetBias";
233 break;
234 case FunctionType::TextureLod:
235 out << "TextureLod";
236 break;
237 case FunctionType::TextureLodOffset:
238 out << "TextureLodOffset";
239 break;
240 case FunctionType::TextureGrad:
241 out << "TextureGrad";
242 break;
243 case FunctionType::TextureGradOffset:
244 out << "TextureGradOffset";
245 break;
246 case FunctionType::TextureProj:
247 out << "TextureProj";
248 break;
249 case FunctionType::TextureProjBias:
250 out << "TextureProjBias";
251 break;
252 case FunctionType::TextureProjOffset:
253 out << "TextureProjOffset";
254 break;
255 case FunctionType::TextureProjOffsetBias:
256 out << "TextureProjOffsetBias";
257 break;
258 case FunctionType::TextureProjLod:
259 out << "TextureProjLod";
260 break;
261 case FunctionType::TextureProjLodOffset:
262 out << "TextureProjLodOffset";
263 break;
264 case FunctionType::TextureProjGrad:
265 out << "TextureProjGrad";
266 break;
267 case FunctionType::TextureProjGradOffset:
268 out << "TextureProjGradOffset";
269 break;
270 }
271
272 return out;
273 }
274
ParseShadowSamplerFunctionVariationsTestParams(const ShadowSamplerFunctionVariationsTestParams & params,FunctionType * functionOut,bool * mipmappedOut)275 void ParseShadowSamplerFunctionVariationsTestParams(
276 const ShadowSamplerFunctionVariationsTestParams ¶ms,
277 FunctionType *functionOut,
278 bool *mipmappedOut)
279 {
280 *functionOut = std::get<1>(params);
281 *mipmappedOut = std::get<2>(params);
282 }
283
ShadowSamplerFunctionVariationsTestPrint(const::testing::TestParamInfo<ShadowSamplerFunctionVariationsTestParams> & paramsInfo)284 std::string ShadowSamplerFunctionVariationsTestPrint(
285 const ::testing::TestParamInfo<ShadowSamplerFunctionVariationsTestParams> ¶msInfo)
286 {
287 const ShadowSamplerFunctionVariationsTestParams ¶ms = paramsInfo.param;
288 std::ostringstream out;
289
290 out << std::get<0>(params);
291
292 FunctionType function;
293 bool mipmapped;
294 ParseShadowSamplerFunctionVariationsTestParams(params, &function, &mipmapped);
295
296 out << "__" << function << "_" << (mipmapped ? "Mipmapped" : "NonMipmapped");
297 return out.str();
298 }
299
300 class ShadowSamplerFunctionTestBase : public ANGLETest<ShadowSamplerFunctionVariationsTestParams>
301 {
302 protected:
ShadowSamplerFunctionTestBase()303 ShadowSamplerFunctionTestBase()
304 {
305 setWindowWidth(16);
306 setWindowHeight(16);
307 setConfigRedBits(8);
308 setConfigGreenBits(8);
309 setConfigBlueBits(8);
310 setConfigAlphaBits(8);
311 }
312
313 GLuint mPrg = 0;
314 };
315
316 class ShadowSamplerFunctionTexture2DTest : public ShadowSamplerFunctionTestBase
317 {
318 protected:
setupProgram2D(FunctionType function,bool useShadowSampler)319 void setupProgram2D(FunctionType function, bool useShadowSampler)
320 {
321 std::stringstream fragmentSource;
322 fragmentSource << "#version 300 es\n"
323 << "precision mediump float;\n"
324 << "precision mediump sampler2D;\n"
325 << "precision mediump sampler2DShadow;\n"
326 << "uniform float dRef;\n"
327 << "uniform sampler2D" << (useShadowSampler ? "Shadow" : "") << " tex;\n"
328 << "in vec4 v_position;\n"
329 << "out vec4 my_FragColor;\n"
330 << "void main()\n"
331 << "{\n"
332 << " vec2 texcoord = v_position.xy * 0.5 + 0.5;\n"
333 << " float r = " << FunctionName(function) << "(tex, ";
334 if (IsProj(function))
335 {
336 if (useShadowSampler)
337 {
338 fragmentSource << "vec4(texcoord * 2.0, dRef * 2.0, 2.0)";
339 }
340 else
341 {
342 fragmentSource << "vec3(texcoord * 2.0, 2.0)";
343 }
344 }
345 else
346 {
347 if (useShadowSampler)
348 {
349 fragmentSource << "vec3(texcoord, dRef)";
350 }
351 else
352 {
353 fragmentSource << "vec2(texcoord)";
354 }
355 }
356
357 if (HasLOD(function))
358 {
359 fragmentSource << ", 2.0";
360 }
361 else if (HasGrad(function))
362 {
363 fragmentSource << ", vec2(0.17), vec2(0.17)";
364 }
365
366 if (HasOffset(function))
367 {
368 // Does not affect LOD selection, added to try all overloads.
369 fragmentSource << ", ivec2(1, 1)";
370 }
371
372 if (HasBias(function))
373 {
374 fragmentSource << ", 3.0";
375 }
376
377 fragmentSource << ")" << (useShadowSampler ? "" : ".r") << ";\n";
378 if (useShadowSampler)
379 {
380 fragmentSource << " my_FragColor = vec4(0.0, r, 1.0 - r, 1.0);\n";
381 }
382 else
383 {
384 fragmentSource << " my_FragColor = vec4(r, 0.0, 0.0, 1.0);\n";
385 }
386 fragmentSource << "}";
387
388 ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Passthrough(), fragmentSource.str().c_str());
389 glUseProgram(program);
390 mPrg = program;
391 }
392 };
393
394 class ShadowSamplerFunctionTexture2DArrayTest : public ShadowSamplerFunctionTestBase
395 {
396 protected:
setupProgram2DArray(FunctionType function,bool useShadowSampler)397 void setupProgram2DArray(FunctionType function, bool useShadowSampler)
398 {
399 ASSERT_FALSE(IsProj(function));
400 std::stringstream fragmentSource;
401 fragmentSource << "#version 300 es\n"
402 << "#extension GL_EXT_texture_shadow_lod : enable\n"
403 << "precision mediump float;\n"
404 << "precision mediump sampler2DArray;\n"
405 << "precision mediump sampler2DArrayShadow;\n"
406 << "uniform float dRef;\n"
407 << "uniform sampler2DArray" << (useShadowSampler ? "Shadow" : "")
408 << " tex;\n"
409 << "in vec4 v_position;\n"
410 << "out vec4 my_FragColor;\n"
411 << "void main()\n"
412 << "{\n"
413 << " vec2 texcoord = v_position.xy * 0.5 + 0.5;\n"
414 << " float r = " << FunctionName(function) << "(tex, ";
415 if (useShadowSampler)
416 {
417 fragmentSource << "vec4(texcoord, 1.0, dRef)";
418 }
419 else
420 {
421 fragmentSource << "vec3(texcoord, 1.0)";
422 }
423
424 if (HasLOD(function))
425 {
426 fragmentSource << ", 2.0";
427 }
428 else if (HasGrad(function))
429 {
430 fragmentSource << ", vec2(0.17), vec2(0.17)";
431 }
432
433 if (HasOffset(function))
434 {
435 // Does not affect LOD selection, added to try all overloads.
436 fragmentSource << ", ivec2(1, 1)";
437 }
438
439 if (HasBias(function))
440 {
441 fragmentSource << ", 3.0";
442 }
443
444 fragmentSource << ")" << (useShadowSampler ? "" : ".r") << ";\n";
445 if (useShadowSampler)
446 {
447 fragmentSource << " my_FragColor = vec4(0.0, r, 1.0 - r, 1.0);\n";
448 }
449 else
450 {
451 fragmentSource << " my_FragColor = vec4(r, 0.0, 0.0, 1.0);\n";
452 }
453 fragmentSource << "}";
454
455 ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Passthrough(), fragmentSource.str().c_str());
456 glUseProgram(program);
457 mPrg = program;
458 }
459 };
460
461 class ShadowSamplerFunctionTextureCubeTest : public ShadowSamplerFunctionTestBase
462 {
463 protected:
setupProgramCube(FunctionType function,bool useShadowSampler)464 void setupProgramCube(FunctionType function, bool useShadowSampler)
465 {
466 ASSERT_FALSE(IsProj(function));
467 ASSERT_FALSE(HasOffset(function));
468 std::stringstream fragmentSource;
469 fragmentSource << "#version 300 es\n"
470 << "#extension GL_EXT_texture_shadow_lod : enable\n"
471 << "precision mediump float;\n"
472 << "precision mediump samplerCube;\n"
473 << "precision mediump samplerCubeShadow;\n"
474 << "uniform float dRef;\n"
475 << "uniform samplerCube" << (useShadowSampler ? "Shadow" : "") << " tex;\n"
476 << "in vec4 v_position;\n"
477 << "out vec4 my_FragColor;\n"
478 << "void main()\n"
479 << "{\n"
480 << " vec3 texcoord = vec3(1.0, v_position.xy);\n"
481 << " float r = " << FunctionName(function) << "(tex, ";
482 if (useShadowSampler)
483 {
484 fragmentSource << "vec4(texcoord, dRef)";
485 }
486 else
487 {
488 fragmentSource << "vec3(texcoord)";
489 }
490
491 if (HasLOD(function))
492 {
493 fragmentSource << ", 2.0";
494 }
495 else if (HasGrad(function))
496 {
497 fragmentSource << ", vec3(0.0, 0.34, 0.34), vec3(0.0)";
498 }
499
500 if (HasBias(function))
501 {
502 fragmentSource << ", 3.0";
503 }
504
505 fragmentSource << ")" << (useShadowSampler ? "" : ".r") << ";\n";
506 if (useShadowSampler)
507 {
508 fragmentSource << " my_FragColor = vec4(0.0, r, 1.0 - r, 1.0);\n";
509 }
510 else
511 {
512 fragmentSource << " my_FragColor = vec4(r, 0.0, 0.0, 1.0);\n";
513 }
514 fragmentSource << "}";
515
516 ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Passthrough(), fragmentSource.str().c_str());
517 glUseProgram(program);
518 mPrg = program;
519 }
520 };
521
522 class ShadowSamplerFunctionTextureCubeArrayTest : public ShadowSamplerFunctionTestBase
523 {
524 protected:
setupProgramCubeArray(FunctionType function,bool useShadowSampler)525 void setupProgramCubeArray(FunctionType function, bool useShadowSampler)
526 {
527 ASSERT_FALSE(IsProj(function));
528 ASSERT_FALSE(HasOffset(function));
529 ASSERT_FALSE(HasGrad(function));
530 std::stringstream fragmentSource;
531 fragmentSource << "#version 310 es\n"
532 << "#extension GL_EXT_texture_cube_map_array : require\n"
533 << "#extension GL_EXT_texture_shadow_lod : enable\n"
534 << "precision mediump float;\n"
535 << "precision mediump samplerCubeArray;\n"
536 << "precision mediump samplerCubeArrayShadow;\n"
537 << "uniform float dRef;\n"
538 << "uniform samplerCubeArray" << (useShadowSampler ? "Shadow" : "")
539 << " tex;\n"
540 << "in vec4 v_position;\n"
541 << "out vec4 my_FragColor;\n"
542 << "void main()\n"
543 << "{\n"
544 << " vec4 texcoord = vec4(1.0, v_position.xy, 1.0);\n"
545 << " float r = " << FunctionName(function) << "(tex, texcoord";
546 if (useShadowSampler)
547 {
548 fragmentSource << ", dRef";
549 }
550
551 if (HasLOD(function))
552 {
553 fragmentSource << ", 2.0";
554 }
555 else if (HasBias(function))
556 {
557 fragmentSource << ", 3.0";
558 }
559
560 fragmentSource << ")" << (useShadowSampler ? "" : ".r") << ";\n";
561 if (useShadowSampler)
562 {
563 fragmentSource << " my_FragColor = vec4(0.0, r, 1.0 - r, 1.0);\n";
564 }
565 else
566 {
567 fragmentSource << " my_FragColor = vec4(r, 0.0, 0.0, 1.0);\n";
568 }
569 fragmentSource << "}";
570
571 ANGLE_GL_PROGRAM(program, essl31_shaders::vs::Passthrough(), fragmentSource.str().c_str());
572 glUseProgram(program);
573 mPrg = program;
574 }
575 };
576
577 constexpr GLenum kCompareFuncs[] = {
578 GL_LEQUAL, GL_GEQUAL, GL_LESS, GL_GREATER, GL_EQUAL, GL_NOTEQUAL, GL_ALWAYS, GL_NEVER,
579 };
580 constexpr float kRefValues[] = {
581 0.0f, 0.25f, 0.5f, 0.75f, 1.0f,
582 };
583
584 // Test TEXTURE_2D with shadow samplers
TEST_P(ShadowSamplerFunctionTexture2DTest,Test)585 TEST_P(ShadowSamplerFunctionTexture2DTest, Test)
586 {
587 FunctionType function;
588 bool mipmapped;
589 ParseShadowSamplerFunctionVariationsTestParams(GetParam(), &function, &mipmapped);
590
591 GLTexture tex;
592 const std::vector<GLfloat> level0(64, 0.125f);
593 const std::vector<GLfloat> level1(16, 0.5f);
594 const std::vector<GLfloat> level2(4, 0.25f);
595 const std::vector<GLfloat> level3(1, 0.75f);
596
597 glBindTexture(GL_TEXTURE_2D, tex);
598 glTexStorage2D(GL_TEXTURE_2D, 4, GL_DEPTH_COMPONENT32F, 8, 8);
599 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 8, 8, GL_DEPTH_COMPONENT, GL_FLOAT, level0.data());
600 glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 4, 4, GL_DEPTH_COMPONENT, GL_FLOAT, level1.data());
601 glTexSubImage2D(GL_TEXTURE_2D, 2, 0, 0, 2, 2, GL_DEPTH_COMPONENT, GL_FLOAT, level2.data());
602 glTexSubImage2D(GL_TEXTURE_2D, 3, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, level3.data());
603 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
604 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
605 mipmapped ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
606 ASSERT_GL_NO_ERROR();
607
608 float expectedSample;
609 if (mipmapped)
610 {
611 if (HasBias(function))
612 {
613 // Base level 8x8, viewport 8x8, bias 3.0
614 expectedSample = level3[0];
615 }
616 else if (HasLOD(function))
617 {
618 // Explicitly requested level 2
619 expectedSample = level2[0];
620 }
621 else if (HasGrad(function))
622 {
623 // Screen space derivatives of 0.17 for a 8x8 texture should resolve to level 1
624 expectedSample = level1[0];
625 }
626 else // implicit LOD
627 {
628 // Base level 8x8, viewport 8x8, no bias
629 expectedSample = level0[0];
630 }
631 }
632 else
633 {
634 // LOD options must have no effect when the texture is not mipmapped.
635 expectedSample = level0[0];
636 }
637
638 glViewport(0, 0, 8, 8);
639 glClearColor(1.0, 0.0, 1.0, 1.0);
640
641 // First sample the texture directly for easier debugging
642 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_NONE);
643 setupProgram2D(function, false);
644 ASSERT_GL_NO_ERROR();
645
646 glClear(GL_COLOR_BUFFER_BIT);
647 drawQuad(mPrg, essl3_shaders::PositionAttrib(), 0.0f);
648 EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(expectedSample * 255.0, 0, 0, 255), 1);
649
650 // Try shadow samplers
651 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
652 setupProgram2D(function, true);
653 const GLint loc = glGetUniformLocation(mPrg, "dRef");
654 for (const float refValue : kRefValues)
655 {
656 glUniform1f(loc, refValue);
657 for (const GLenum compareFunc : kCompareFuncs)
658 {
659 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, compareFunc);
660 ASSERT_GL_NO_ERROR();
661
662 glClear(GL_COLOR_BUFFER_BIT);
663 drawQuad(mPrg, essl3_shaders::PositionAttrib(), 0.0f);
664 if (Compare(refValue, expectedSample, compareFunc))
665 {
666 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::green)
667 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
668 << ", reference " << refValue << ", expected sample " << expectedSample;
669 }
670 else
671 {
672 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::blue)
673 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
674 << ", reference " << refValue << ", expected sample " << expectedSample;
675 }
676 }
677 }
678 }
679
680 // Test TEXTURE_2D_ARRAY with shadow samplers
TEST_P(ShadowSamplerFunctionTexture2DArrayTest,Test)681 TEST_P(ShadowSamplerFunctionTexture2DArrayTest, Test)
682 {
683 FunctionType function;
684 bool mipmapped;
685 ParseShadowSamplerFunctionVariationsTestParams(GetParam(), &function, &mipmapped);
686
687 if (RequiresExtensionFor2DArray(function))
688 {
689 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_shadow_lod"));
690 }
691
692 GLTexture tex;
693 const std::vector<GLfloat> unused(64, 0.875);
694 const std::vector<GLfloat> level0(64, 0.125f);
695 const std::vector<GLfloat> level1(16, 0.5f);
696 const std::vector<GLfloat> level2(4, 0.25f);
697 const std::vector<GLfloat> level3(1, 0.75f);
698
699 glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
700 glTexStorage3D(GL_TEXTURE_2D_ARRAY, 4, GL_DEPTH_COMPONENT32F, 8, 8, 2);
701 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 8, 8, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
702 unused.data());
703 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 1, 0, 0, 0, 4, 4, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
704 unused.data());
705 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 2, 0, 0, 0, 2, 2, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
706 unused.data());
707 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 3, 0, 0, 0, 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
708 unused.data());
709 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 1, 8, 8, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
710 level0.data());
711 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 1, 0, 0, 1, 4, 4, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
712 level1.data());
713 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 2, 0, 0, 1, 2, 2, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
714 level2.data());
715 glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 3, 0, 0, 1, 1, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT,
716 level3.data());
717 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
718 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER,
719 mipmapped ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
720 ASSERT_GL_NO_ERROR();
721
722 float expectedSample;
723 if (mipmapped)
724 {
725 if (HasBias(function))
726 {
727 // Base level 8x8, viewport 8x8, bias 3.0
728 expectedSample = level3[0];
729 }
730 else if (HasLOD(function))
731 {
732 // Explicitly requested level 2
733 expectedSample = level2[0];
734 }
735 else if (HasGrad(function))
736 {
737 // Screen space derivatives of 0.17 for a 8x8 texture should resolve to level 1
738 expectedSample = level1[0];
739 }
740 else // implicit LOD
741 {
742 // Base level 8x8, viewport 8x8, no bias
743 expectedSample = level0[0];
744 }
745 }
746 else
747 {
748 // LOD options must have no effect when the texture is not mipmapped.
749 expectedSample = level0[0];
750 }
751
752 glViewport(0, 0, 8, 8);
753 glClearColor(1.0, 0.0, 1.0, 1.0);
754
755 // First sample the texture directly for easier debugging
756 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_NONE);
757 setupProgram2DArray(function, false);
758 ASSERT_GL_NO_ERROR();
759
760 glClear(GL_COLOR_BUFFER_BIT);
761 drawQuad(mPrg, essl3_shaders::PositionAttrib(), 0.0f);
762 EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(expectedSample * 255.0, 0, 0, 255), 1);
763
764 // Try shadow samplers
765 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
766 setupProgram2DArray(function, true);
767 const GLint loc = glGetUniformLocation(mPrg, "dRef");
768 for (const float refValue : kRefValues)
769 {
770 glUniform1f(loc, refValue);
771 for (const GLenum compareFunc : kCompareFuncs)
772 {
773 glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_COMPARE_FUNC, compareFunc);
774 ASSERT_GL_NO_ERROR();
775
776 glClear(GL_COLOR_BUFFER_BIT);
777 drawQuad(mPrg, essl3_shaders::PositionAttrib(), 0.0f);
778 if (Compare(refValue, expectedSample, compareFunc))
779 {
780 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::green)
781 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
782 << ", reference " << refValue << ", expected sample " << expectedSample;
783 }
784 else
785 {
786 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::blue)
787 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
788 << ", reference " << refValue << ", expected sample " << expectedSample;
789 }
790 }
791 }
792 }
793
794 // Test TEXTURE_CUBE_MAP with shadow samplers
TEST_P(ShadowSamplerFunctionTextureCubeTest,Test)795 TEST_P(ShadowSamplerFunctionTextureCubeTest, Test)
796 {
797 FunctionType function;
798 bool mipmapped;
799 ParseShadowSamplerFunctionVariationsTestParams(GetParam(), &function, &mipmapped);
800
801 if (RequiresExtensionForCube(function))
802 {
803 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_shadow_lod"));
804 }
805
806 GLTexture tex;
807 const std::vector<GLfloat> level0(64, 0.125f);
808 const std::vector<GLfloat> level1(16, 0.5f);
809 const std::vector<GLfloat> level2(4, 0.25f);
810 const std::vector<GLfloat> level3(1, 0.75f);
811
812 glBindTexture(GL_TEXTURE_CUBE_MAP, tex);
813 glTexStorage2D(GL_TEXTURE_CUBE_MAP, 4, GL_DEPTH_COMPONENT32F, 8, 8);
814 for (const GLenum face : {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,
815 GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,
816 GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z})
817 {
818 glTexSubImage2D(face, 0, 0, 0, 8, 8, GL_DEPTH_COMPONENT, GL_FLOAT, level0.data());
819 glTexSubImage2D(face, 1, 0, 0, 4, 4, GL_DEPTH_COMPONENT, GL_FLOAT, level1.data());
820 glTexSubImage2D(face, 2, 0, 0, 2, 2, GL_DEPTH_COMPONENT, GL_FLOAT, level2.data());
821 glTexSubImage2D(face, 3, 0, 0, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, level3.data());
822 }
823
824 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
825 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER,
826 mipmapped ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
827 ASSERT_GL_NO_ERROR();
828
829 float expectedSample;
830 if (mipmapped)
831 {
832 if (HasBias(function))
833 {
834 // Base level 8x8, viewport 8x8, bias 3.0
835 expectedSample = level3[0];
836 }
837 else if (HasLOD(function))
838 {
839 // Explicitly requested level 2
840 expectedSample = level2[0];
841 }
842 else if (HasGrad(function))
843 {
844 // Cube screen space derivatives should be projected as 0.17
845 // on the +X face and resolved to level 1 for a 8x8 texture
846 expectedSample = level1[0];
847 }
848 else // implicit LOD
849 {
850 // Base level 8x8, viewport 8x8, no bias
851 expectedSample = level0[0];
852 }
853 }
854 else
855 {
856 // LOD options must have no effect when the texture is not mipmapped.
857 expectedSample = level0[0];
858 }
859
860 glViewport(0, 0, 8, 8);
861 glClearColor(1.0, 0.0, 1.0, 1.0);
862
863 // First sample the texture directly for easier debugging
864 // This step is skipped on Apple GPUs when the PreTransformTextureCubeGradDerivatives feature
865 // is disabled for testing because native cubemap sampling with explicit derivatives does not
866 // work on that platform without this feature.
867 // Since the AllowSamplerCompareGradient feature is also disabled in this case, the next steps,
868 // which use shadow samplers, rely on emulation and thus they are not affected.
869 const auto &disabledFeatures = std::get<0>(GetParam()).eglParameters.disabledFeatureOverrides;
870 if (!IsAppleGPU() ||
871 std::find(disabledFeatures.begin(), disabledFeatures.end(),
872 Feature::PreTransformTextureCubeGradDerivatives) == disabledFeatures.end())
873 {
874 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_NONE);
875 setupProgramCube(function, false);
876 ASSERT_GL_NO_ERROR();
877
878 glClear(GL_COLOR_BUFFER_BIT);
879 drawQuad(mPrg, essl3_shaders::PositionAttrib(), 0.0f);
880 EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(expectedSample * 255.0, 0, 0, 255), 1);
881 }
882
883 // Try shadow samplers
884 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_REF_TO_TEXTURE);
885 setupProgramCube(function, true);
886 const GLint loc = glGetUniformLocation(mPrg, "dRef");
887 for (const float refValue : kRefValues)
888 {
889 glUniform1f(loc, refValue);
890 for (const GLenum compareFunc : kCompareFuncs)
891 {
892 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_COMPARE_FUNC, compareFunc);
893 ASSERT_GL_NO_ERROR();
894
895 glClear(GL_COLOR_BUFFER_BIT);
896 drawQuad(mPrg, essl3_shaders::PositionAttrib(), 0.0f);
897 if (Compare(refValue, expectedSample, compareFunc))
898 {
899 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::green)
900 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
901 << ", reference " << refValue << ", expected sample " << expectedSample;
902 }
903 else
904 {
905 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::blue)
906 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
907 << ", reference " << refValue << ", expected sample " << expectedSample;
908 }
909 }
910 }
911 }
912
913 // Test TEXTURE_CUBE_MAP_ARRAY with shadow samplers
TEST_P(ShadowSamplerFunctionTextureCubeArrayTest,Test)914 TEST_P(ShadowSamplerFunctionTextureCubeArrayTest, Test)
915 {
916 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_cube_map_array"));
917
918 FunctionType function;
919 bool mipmapped;
920 ParseShadowSamplerFunctionVariationsTestParams(GetParam(), &function, &mipmapped);
921
922 if (RequiresExtensionForCubeArray(function))
923 {
924 ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_shadow_lod"));
925 }
926
927 GLTexture tex;
928 const std::vector<GLfloat> unused(64, 0.875);
929 const std::vector<GLfloat> level0(64, 0.125f);
930 const std::vector<GLfloat> level1(16, 0.5f);
931 const std::vector<GLfloat> level2(4, 0.25f);
932 const std::vector<GLfloat> level3(1, 0.75f);
933
934 glBindTexture(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, tex);
935 glTexStorage3D(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, 4, GL_DEPTH_COMPONENT32F, 8, 8, 12);
936 for (size_t k = 0; k < 6 * 2; ++k)
937 {
938 glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, 0, 0, 0, k, 8, 8, 1, GL_DEPTH_COMPONENT,
939 GL_FLOAT, k > 5 ? level0.data() : unused.data());
940 glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, 1, 0, 0, k, 4, 4, 1, GL_DEPTH_COMPONENT,
941 GL_FLOAT, k > 5 ? level1.data() : unused.data());
942 glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, 2, 0, 0, k, 2, 2, 1, GL_DEPTH_COMPONENT,
943 GL_FLOAT, k > 5 ? level2.data() : unused.data());
944 glTexSubImage3D(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, 3, 0, 0, k, 1, 1, 1, GL_DEPTH_COMPONENT,
945 GL_FLOAT, k > 5 ? level3.data() : unused.data());
946 }
947
948 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
949 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, GL_TEXTURE_MIN_FILTER,
950 mipmapped ? GL_NEAREST_MIPMAP_NEAREST : GL_NEAREST);
951 ASSERT_GL_NO_ERROR();
952
953 float expectedSample;
954 if (mipmapped)
955 {
956 if (HasBias(function))
957 {
958 // Base level 8x8, viewport 8x8, bias 3.0
959 expectedSample = level3[0];
960 }
961 else if (HasLOD(function))
962 {
963 // Explicitly requested level 2
964 expectedSample = level2[0];
965 }
966 else // implicit LOD
967 {
968 // Base level 8x8, viewport 8x8, no bias
969 expectedSample = level0[0];
970 }
971 }
972 else
973 {
974 // LOD options must have no effect when the texture is not mipmapped.
975 expectedSample = level0[0];
976 }
977
978 glViewport(0, 0, 8, 8);
979 glClearColor(1.0, 0.0, 1.0, 1.0);
980
981 // First sample the texture directly for easier debugging
982 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, GL_TEXTURE_COMPARE_MODE, GL_NONE);
983 setupProgramCubeArray(function, false);
984 ASSERT_GL_NO_ERROR();
985
986 glClear(GL_COLOR_BUFFER_BIT);
987 drawQuad(mPrg, essl31_shaders::PositionAttrib(), 0.0f);
988 EXPECT_PIXEL_COLOR_NEAR(0, 0, GLColor(expectedSample * 255.0, 0, 0, 255), 1);
989
990 // Try shadow samplers
991 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, GL_TEXTURE_COMPARE_MODE,
992 GL_COMPARE_REF_TO_TEXTURE);
993 setupProgramCubeArray(function, true);
994 const GLint loc = glGetUniformLocation(mPrg, "dRef");
995 for (const float refValue : kRefValues)
996 {
997 glUniform1f(loc, refValue);
998 for (const GLenum compareFunc : kCompareFuncs)
999 {
1000 glTexParameteri(GL_TEXTURE_CUBE_MAP_ARRAY_EXT, GL_TEXTURE_COMPARE_FUNC, compareFunc);
1001 ASSERT_GL_NO_ERROR();
1002
1003 glClear(GL_COLOR_BUFFER_BIT);
1004 drawQuad(mPrg, essl31_shaders::PositionAttrib(), 0.0f);
1005 if (Compare(refValue, expectedSample, compareFunc))
1006 {
1007 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::green)
1008 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
1009 << ", reference " << refValue << ", expected sample " << expectedSample;
1010 }
1011 else
1012 {
1013 EXPECT_PIXEL_COLOR_EQ(4, 4, GLColor::blue)
1014 << gl::GLenumToString(gl::GLESEnum::DepthFunction, compareFunc)
1015 << ", reference " << refValue << ", expected sample " << expectedSample;
1016 }
1017 }
1018 }
1019 }
1020
1021 constexpr FunctionType kTexture2DFunctionTypes[] = {
1022 FunctionType::Texture, FunctionType::TextureBias,
1023 FunctionType::TextureOffset, FunctionType::TextureOffsetBias,
1024 FunctionType::TextureLod, FunctionType::TextureLodOffset,
1025 FunctionType::TextureGrad, FunctionType::TextureGradOffset,
1026 FunctionType::TextureProj, FunctionType::TextureProjBias,
1027 FunctionType::TextureProjOffset, FunctionType::TextureProjOffsetBias,
1028 FunctionType::TextureProjLod, FunctionType::TextureProjLodOffset,
1029 FunctionType::TextureProjGrad, FunctionType::TextureProjGradOffset,
1030 };
1031
1032 constexpr FunctionType kTexture2DArrayFunctionTypes[] = {
1033 FunctionType::Texture, FunctionType::TextureBias,
1034 FunctionType::TextureOffset, FunctionType::TextureOffsetBias,
1035 FunctionType::TextureLod, FunctionType::TextureLodOffset,
1036 FunctionType::TextureGrad, FunctionType::TextureGradOffset,
1037 };
1038
1039 constexpr FunctionType kTextureCubeFunctionTypes[] = {
1040 FunctionType::Texture,
1041 FunctionType::TextureBias,
1042 FunctionType::TextureLod,
1043 FunctionType::TextureGrad,
1044 };
1045
1046 constexpr FunctionType kTextureCubeArrayFunctionTypes[] = {
1047 FunctionType::Texture,
1048 FunctionType::TextureBias,
1049 FunctionType::TextureLod,
1050 };
1051
1052 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShadowSamplerFunctionTexture2DTest);
1053 ANGLE_INSTANTIATE_TEST_COMBINE_2(ShadowSamplerFunctionTexture2DTest,
1054 ShadowSamplerFunctionVariationsTestPrint,
1055 testing::ValuesIn(kTexture2DFunctionTypes),
1056 testing::Bool(),
1057 ANGLE_ALL_TEST_PLATFORMS_ES3,
1058 ES3_METAL()
1059 .disable(Feature::AllowSamplerCompareGradient)
1060 .disable(Feature::PreTransformTextureCubeGradDerivatives));
1061
1062 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShadowSamplerFunctionTexture2DArrayTest);
1063 ANGLE_INSTANTIATE_TEST_COMBINE_2(ShadowSamplerFunctionTexture2DArrayTest,
1064 ShadowSamplerFunctionVariationsTestPrint,
1065 testing::ValuesIn(kTexture2DArrayFunctionTypes),
1066 testing::Bool(),
1067 ANGLE_ALL_TEST_PLATFORMS_ES3,
1068 ES3_METAL()
1069 .disable(Feature::AllowSamplerCompareGradient)
1070 .disable(Feature::PreTransformTextureCubeGradDerivatives));
1071
1072 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShadowSamplerFunctionTextureCubeTest);
1073 ANGLE_INSTANTIATE_TEST_COMBINE_2(ShadowSamplerFunctionTextureCubeTest,
1074 ShadowSamplerFunctionVariationsTestPrint,
1075 testing::ValuesIn(kTextureCubeFunctionTypes),
1076 testing::Bool(),
1077 ANGLE_ALL_TEST_PLATFORMS_ES3,
1078 ES3_METAL()
1079 .disable(Feature::AllowSamplerCompareGradient)
1080 .disable(Feature::PreTransformTextureCubeGradDerivatives));
1081
1082 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(ShadowSamplerFunctionTextureCubeArrayTest);
1083 ANGLE_INSTANTIATE_TEST_COMBINE_2(ShadowSamplerFunctionTextureCubeArrayTest,
1084 ShadowSamplerFunctionVariationsTestPrint,
1085 testing::ValuesIn(kTextureCubeArrayFunctionTypes),
1086 testing::Bool(),
1087 ANGLE_ALL_TEST_PLATFORMS_ES31);
1088
1089 } // anonymous namespace
1090