xref: /aosp_15_r20/external/angle/src/tests/gl_tests/ShadowSamplerFunctionsTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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 &params,
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> &paramsInfo)
286 {
287     const ShadowSamplerFunctionVariationsTestParams &params = 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