xref: /aosp_15_r20/external/angle/src/tests/gl_tests/MipmapTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2015 The ANGLE Project Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 //
6 
7 #include "test_utils/ANGLETest.h"
8 
9 #include "test_utils/gl_raii.h"
10 
11 using namespace angle;
12 
13 namespace
14 {
15 
TexImageCubeMapFaces(GLint level,GLenum internalformat,GLsizei width,GLenum format,GLenum type,void * pixels)16 void TexImageCubeMapFaces(GLint level,
17                           GLenum internalformat,
18                           GLsizei width,
19                           GLenum format,
20                           GLenum type,
21                           void *pixels)
22 {
23     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internalformat, width, width, 0, format,
24                  type, pixels);
25     glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internalformat, width, width, 0, format,
26                  type, pixels);
27     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internalformat, width, width, 0, format,
28                  type, pixels);
29     glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internalformat, width, width, 0, format,
30                  type, pixels);
31     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internalformat, width, width, 0, format,
32                  type, pixels);
33     glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internalformat, width, width, 0, format,
34                  type, pixels);
35 }
36 
37 class BaseMipmapTest : public ANGLETest<>
38 {
39   protected:
clearAndDrawQuad(GLuint program,GLsizei viewportWidth,GLsizei viewportHeight)40     void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight)
41     {
42         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
43         glClear(GL_COLOR_BUFFER_BIT);
44         glViewport(0, 0, viewportWidth, viewportHeight);
45         ASSERT_GL_NO_ERROR();
46 
47         drawQuad(program, "position", 0.0f);
48     }
49 };
50 
51 }  // namespace
52 
53 class MipmapTest : public BaseMipmapTest
54 {
55   protected:
MipmapTest()56     MipmapTest()
57         : m2DProgram(0),
58           mCubeProgram(0),
59           mTexture2D(0),
60           mTextureCube(0),
61           m3DProgram(0),
62           mLevelZeroBlueInitData(),
63           mLevelZeroWhiteInitData(),
64           mLevelOneGreenInitData(),
65           mLevelTwoRedInitData(),
66           mOffscreenFramebuffer(0)
67     {
68         setWindowWidth(128);
69         setWindowHeight(128);
70         setConfigRedBits(8);
71         setConfigGreenBits(8);
72         setConfigBlueBits(8);
73         setConfigAlphaBits(8);
74     }
75 
setUp2DProgram()76     void setUp2DProgram()
77     {
78         // Vertex Shader source
79         constexpr char kVS[] = R"(attribute vec4 position;
80 varying vec2 vTexCoord;
81 
82 void main()
83 {
84     gl_Position = position;
85     vTexCoord   = (position.xy * 0.5) + 0.5;
86 })";
87 
88         // Fragment Shader source
89         constexpr char kFS[] = R"(precision mediump float;
90 uniform sampler2D uTexture;
91 varying vec2 vTexCoord;
92 
93 void main()
94 {
95     gl_FragColor = texture2D(uTexture, vTexCoord);
96 })";
97 
98         m2DProgram = CompileProgram(kVS, kFS);
99         ASSERT_NE(0u, m2DProgram);
100     }
101 
setUpCubeProgram()102     void setUpCubeProgram()
103     {
104         // A simple vertex shader for the texture cube
105         constexpr char kVS[] = R"(attribute vec4 position;
106 varying vec4 vPosition;
107 void main()
108 {
109     gl_Position = position;
110     vPosition = position;
111 })";
112 
113         // A very simple fragment shader to sample from the negative-Y face of a texture cube.
114         constexpr char kFS[] = R"(precision mediump float;
115 uniform samplerCube uTexture;
116 varying vec4 vPosition;
117 
118 void main()
119 {
120     gl_FragColor = textureCube(uTexture, vec3(vPosition.x, -1, vPosition.y));
121 })";
122 
123         mCubeProgram = CompileProgram(kVS, kFS);
124         ASSERT_NE(0u, mCubeProgram);
125     }
126 
setUp3DProgram()127     void setUp3DProgram()
128     {
129         ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
130 
131         // http://anglebug.com/42263501
132         ANGLE_SKIP_TEST_IF((IsPixel2() || IsNexus5X()) && IsOpenGLES());
133 
134         // Vertex Shader source
135         constexpr char kVS[] = R"(attribute vec4 position;
136 varying vec2 vTexCoord;
137 
138 void main()
139 {
140     gl_Position = position;
141     vTexCoord   = (position.xy * 0.5) + 0.5;
142 })";
143 
144         constexpr char kFS[] = R"(#version 100
145 #extension GL_OES_texture_3D : enable
146 precision highp float;
147 uniform highp sampler3D tex;
148 uniform float slice;
149 uniform float lod;
150 varying vec2 vTexCoord;
151 
152 void main()
153 {
154     gl_FragColor = texture3DLod(tex, vec3(vTexCoord, slice), lod);
155 })";
156 
157         m3DProgram = CompileProgram(kVS, kFS);
158         if (m3DProgram == 0)
159         {
160             FAIL() << "shader compilation failed.";
161         }
162 
163         mTexture3DSliceUniformLocation = glGetUniformLocation(m3DProgram, "slice");
164         ASSERT_NE(-1, mTexture3DSliceUniformLocation);
165 
166         mTexture3DLODUniformLocation = glGetUniformLocation(m3DProgram, "lod");
167         ASSERT_NE(-1, mTexture3DLODUniformLocation);
168 
169         glUseProgram(m3DProgram);
170         glUniform1f(mTexture3DLODUniformLocation, 0);
171         glUseProgram(0);
172         ASSERT_GL_NO_ERROR();
173     }
174 
testSetUp()175     void testSetUp() override
176     {
177         // http://anglebug.com/42264262
178         ANGLE_SKIP_TEST_IF(IsOzone());
179 
180         setUp2DProgram();
181 
182         setUpCubeProgram();
183 
184         setUp3DProgram();
185 
186         mLevelZeroBlueInitData =
187             createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255);  // Blue
188         mLevelZeroWhiteInitData =
189             createRGBInitData(getWindowWidth(), getWindowHeight(), 255, 255, 255);  // White
190         mLevelOneGreenInitData =
191             createRGBInitData((getWindowWidth() / 2), (getWindowHeight() / 2), 0, 255, 0);  // Green
192         mLevelTwoRedInitData =
193             createRGBInitData((getWindowWidth() / 4), (getWindowHeight() / 4), 255, 0, 0);  // Red
194 
195         glGenFramebuffers(1, &mOffscreenFramebuffer);
196         glGenTextures(1, &mTexture2D);
197 
198         // Initialize the texture2D to be empty, and don't use mips.
199         glBindTexture(GL_TEXTURE_2D, mTexture2D);
200         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB,
201                      GL_UNSIGNED_BYTE, nullptr);
202         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
203         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
204 
205         ASSERT_EQ(getWindowWidth(), getWindowHeight());
206 
207         // Create a non-mipped texture cube. Set the negative-Y face to be blue.
208         // The other sides of the cube map have been set to white.
209         glGenTextures(1, &mTextureCube);
210         glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
211         TexImageCubeMapFaces(0, GL_RGB, getWindowWidth(), GL_RGB, GL_UNSIGNED_BYTE, nullptr);
212 
213         glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
214                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData.data());
215         glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
216                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData.data());
217         glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
218                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData.data());
219         glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
220                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData.data());
221         glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
222                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData.data());
223         glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
224                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData.data());
225 
226         // Complete the texture cube without mipmaps to start with.
227         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
228         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
229 
230         ASSERT_GL_NO_ERROR();
231     }
232 
testTearDown()233     void testTearDown() override
234     {
235         glDeleteProgram(m2DProgram);
236         glDeleteProgram(mCubeProgram);
237         glDeleteProgram(m3DProgram);
238         glDeleteFramebuffers(1, &mOffscreenFramebuffer);
239         glDeleteTextures(1, &mTexture2D);
240         glDeleteTextures(1, &mTextureCube);
241     }
242 
createRGBInitData(GLint width,GLint height,GLint r,GLint g,GLint b)243     std::vector<GLubyte> createRGBInitData(GLint width, GLint height, GLint r, GLint g, GLint b)
244     {
245         std::vector<GLubyte> data(3 * width * height);
246 
247         for (int i = 0; i < width * height; i += 1)
248         {
249             data[3 * i + 0] = static_cast<GLubyte>(r);
250             data[3 * i + 1] = static_cast<GLubyte>(g);
251             data[3 * i + 2] = static_cast<GLubyte>(b);
252         }
253 
254         return data;
255     }
256 
clearTextureLevel0(GLenum textarget,GLuint texture,GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)257     void clearTextureLevel0(GLenum textarget,
258                             GLuint texture,
259                             GLfloat red,
260                             GLfloat green,
261                             GLfloat blue,
262                             GLfloat alpha)
263     {
264         glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
265         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textarget, texture, 0);
266         ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
267         glClearColor(red, green, blue, alpha);
268         glClear(GL_COLOR_BUFFER_BIT);
269         glBindFramebuffer(GL_FRAMEBUFFER, 0);
270     }
271 
272     GLuint m2DProgram;
273     GLuint mCubeProgram;
274     GLuint mTexture2D;
275     GLuint mTextureCube;
276 
277     GLuint m3DProgram = 0;
278     GLint mTexture3DSliceUniformLocation;
279     GLint mTexture3DLODUniformLocation;
280 
281     std::vector<GLubyte> mLevelZeroBlueInitData;
282     std::vector<GLubyte> mLevelZeroWhiteInitData;
283     std::vector<GLubyte> mLevelOneGreenInitData;
284     std::vector<GLubyte> mLevelTwoRedInitData;
285 
286   private:
287     GLuint mOffscreenFramebuffer;
288 };
289 
290 class MipmapTestES3 : public BaseMipmapTest
291 {
292   protected:
MipmapTestES3()293     MipmapTestES3()
294         : mTexture(0),
295           mArrayProgram(0),
296           mTextureArraySliceUniformLocation(-1),
297           m3DProgram(0),
298           mTexture3DSliceUniformLocation(-1),
299           mTexture3DLODUniformLocation(-1),
300           m2DProgram(0)
301 
302     {
303         setWindowWidth(128);
304         setWindowHeight(128);
305         setConfigRedBits(8);
306         setConfigGreenBits(8);
307         setConfigBlueBits(8);
308         setConfigAlphaBits(8);
309     }
310 
vertexShaderSource()311     const char *vertexShaderSource()
312     {
313         // Don't put "#version ..." on its own line. See [cpp]p1:
314         // "If there are sequences of preprocessing tokens within the list of arguments that
315         //  would otherwise act as preprocessing directives, the behavior is undefined"
316         return
317             R"(#version 300 es
318 precision highp float;
319 in vec4 position;
320 out vec2 texcoord;
321 
322 void main()
323 {
324     gl_Position = vec4(position.xy, 0.0, 1.0);
325     texcoord = (position.xy * 0.5) + 0.5;
326 })";
327     }
328 
setUpArrayProgram()329     void setUpArrayProgram()
330     {
331         constexpr char kFS[] = R"(#version 300 es
332 precision highp float;
333 uniform highp sampler2DArray tex;
334 uniform int slice;
335 in vec2 texcoord;
336 out vec4 out_FragColor;
337 
338 void main()
339 {
340     out_FragColor = texture(tex, vec3(texcoord, float(slice)));
341 })";
342 
343         mArrayProgram = CompileProgram(vertexShaderSource(), kFS);
344         if (mArrayProgram == 0)
345         {
346             FAIL() << "shader compilation failed.";
347         }
348 
349         mTextureArraySliceUniformLocation = glGetUniformLocation(mArrayProgram, "slice");
350         ASSERT_NE(-1, mTextureArraySliceUniformLocation);
351 
352         glUseProgram(mArrayProgram);
353         glUseProgram(0);
354         ASSERT_GL_NO_ERROR();
355     }
356 
setUp3DProgram()357     void setUp3DProgram()
358     {
359         constexpr char kFS[] = R"(#version 300 es
360 precision highp float;
361 uniform highp sampler3D tex;
362 uniform float slice;
363 uniform float lod;
364 in vec2 texcoord;
365 out vec4 out_FragColor;
366 
367 void main()
368 {
369     out_FragColor = textureLod(tex, vec3(texcoord, slice), lod);
370 })";
371 
372         m3DProgram = CompileProgram(vertexShaderSource(), kFS);
373         if (m3DProgram == 0)
374         {
375             FAIL() << "shader compilation failed.";
376         }
377 
378         mTexture3DSliceUniformLocation = glGetUniformLocation(m3DProgram, "slice");
379         ASSERT_NE(-1, mTexture3DSliceUniformLocation);
380 
381         mTexture3DLODUniformLocation = glGetUniformLocation(m3DProgram, "lod");
382         ASSERT_NE(-1, mTexture3DLODUniformLocation);
383 
384         glUseProgram(m3DProgram);
385         glUniform1f(mTexture3DLODUniformLocation, 0);
386         glUseProgram(0);
387         ASSERT_GL_NO_ERROR();
388     }
389 
setUp2DProgram()390     void setUp2DProgram()
391     {
392         constexpr char kFS[] = R"(#version 300 es
393 precision highp float;
394 uniform highp sampler2D tex;
395 in vec2 texcoord;
396 out vec4 out_FragColor;
397 
398 void main()
399 {
400     out_FragColor = texture(tex, texcoord);
401 })";
402 
403         m2DProgram = CompileProgram(vertexShaderSource(), kFS);
404         ASSERT_NE(0u, m2DProgram);
405 
406         ASSERT_GL_NO_ERROR();
407     }
408 
setUpCubeProgram()409     void setUpCubeProgram()
410     {
411         // A very simple fragment shader to sample from the negative-Y face of a texture cube.
412         constexpr char kFS[] = R"(#version 300 es
413 precision mediump float;
414 uniform samplerCube uTexture;
415 in vec2 texcoord;
416 out vec4 out_FragColor;
417 
418 void main()
419 {
420     out_FragColor = texture(uTexture, vec3(texcoord.x, -1, texcoord.y));
421 })";
422 
423         mCubeProgram = CompileProgram(vertexShaderSource(), kFS);
424         ASSERT_NE(0u, mCubeProgram);
425 
426         ASSERT_GL_NO_ERROR();
427     }
428 
testSetUp()429     void testSetUp() override
430     {
431         glGenTextures(1, &mTexture);
432         ASSERT_GL_NO_ERROR();
433 
434         setUpArrayProgram();
435         setUp3DProgram();
436         setUp2DProgram();
437         setUpCubeProgram();
438     }
439 
testTearDown()440     void testTearDown() override
441     {
442         glDeleteTextures(1, &mTexture);
443 
444         glDeleteProgram(mArrayProgram);
445         glDeleteProgram(m3DProgram);
446         glDeleteProgram(m2DProgram);
447         glDeleteProgram(mCubeProgram);
448     }
449 
verifyAllMips(const uint32_t textureWidth,const uint32_t textureHeight,const GLColor & color)450     void verifyAllMips(const uint32_t textureWidth,
451                        const uint32_t textureHeight,
452                        const GLColor &color)
453     {
454         ANGLE_GL_PROGRAM(program, essl3_shaders::vs::Texture2DLod(),
455                          essl3_shaders::fs::Texture2DLod());
456         glUseProgram(program);
457         const GLint textureLoc = glGetUniformLocation(program, essl3_shaders::Texture2DUniform());
458         const GLint lodLoc     = glGetUniformLocation(program, essl3_shaders::LodUniform());
459         ASSERT_NE(-1, textureLoc);
460         ASSERT_NE(-1, lodLoc);
461         glUniform1i(textureLoc, 0);
462 
463         // Verify that every mip is correct.
464         const int w = getWindowWidth() - 1;
465         const int h = getWindowHeight() - 1;
466         for (uint32_t mip = 0; textureWidth >> mip >= 1 || textureHeight >> mip >= 1; ++mip)
467         {
468             glUniform1f(lodLoc, mip);
469             glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
470             glClear(GL_COLOR_BUFFER_BIT);
471             drawQuad(program, essl3_shaders::PositionAttrib(), 0.5f);
472             EXPECT_GL_NO_ERROR();
473             EXPECT_PIXEL_COLOR_EQ(0, 0, color) << "Failed on mip " << mip;
474             EXPECT_PIXEL_COLOR_EQ(w, 0, color) << "Failed on mip " << mip;
475             EXPECT_PIXEL_COLOR_EQ(0, h, color) << "Failed on mip " << mip;
476             EXPECT_PIXEL_COLOR_EQ(w, h, color) << "Failed on mip " << mip;
477         }
478     }
479 
480     GLuint mTexture;
481 
482     GLuint mArrayProgram;
483     GLint mTextureArraySliceUniformLocation;
484 
485     GLuint m3DProgram;
486     GLint mTexture3DSliceUniformLocation;
487     GLint mTexture3DLODUniformLocation;
488 
489     GLuint m2DProgram;
490 
491     GLuint mCubeProgram;
492 };
493 
494 class MipmapTestES31 : public BaseMipmapTest
495 {
496   protected:
MipmapTestES31()497     MipmapTestES31()
498 
499     {
500         setWindowWidth(128);
501         setWindowHeight(128);
502         setConfigRedBits(8);
503         setConfigGreenBits(8);
504         setConfigBlueBits(8);
505         setConfigAlphaBits(8);
506     }
507 };
508 
509 // Test generating mipmaps with base level and max level set. Ported from part of the
510 // conformance2/textures/misc/tex-mipmap-levels WebGL2 test.
TEST_P(MipmapTestES3,GenerateMipmapPartialLevels)511 TEST_P(MipmapTestES3, GenerateMipmapPartialLevels)
512 {
513     // TODO(anglebug.com/40096747): Failing on ARM-based Apple DTKs.
514     ANGLE_SKIP_TEST_IF(IsMac() && IsARM64() && IsDesktopOpenGL());
515 
516     const std::vector<GLColor> kRedData(64, GLColor::red);
517     const std::vector<GLColor> kGreenData(16, GLColor::green);
518     const std::vector<GLColor> kBlueData(4, GLColor::blue);
519 
520     // Initialize mips 2 to 4
521     GLTexture texture;
522     glBindTexture(GL_TEXTURE_2D, texture);
523     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 8, 8, 0, GL_RGBA, GL_UNSIGNED_BYTE, kRedData.data());
524     glTexImage2D(GL_TEXTURE_2D, 3, GL_RGBA, 4, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, kGreenData.data());
525     glTexImage2D(GL_TEXTURE_2D, 4, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, kBlueData.data());
526 
527     // Set base and max levels
528     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
529     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
530 
531     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
532     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
533 
534     // Verify the data
535     clearAndDrawQuad(m2DProgram, 2, 2);
536     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
537 
538     // Test that generateMipmap works with partial levels.
539     glGenerateMipmap(GL_TEXTURE_2D);
540     clearAndDrawQuad(m2DProgram, 2, 2);
541     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
542 }
543 
544 // This test generates mipmaps for a 1x1 texture, which should be a no-op.
TEST_P(MipmapTestES3,GenerateMipmap1x1Texture)545 TEST_P(MipmapTestES3, GenerateMipmap1x1Texture)
546 {
547     constexpr uint32_t kTextureSize = 1;
548 
549     const std::vector<GLColor> kInitialColor(kTextureSize * kTextureSize,
550                                              GLColor(35, 81, 184, 211));
551 
552     // Create the texture.
553     glBindTexture(GL_TEXTURE_2D, mTexture);
554     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
555                  GL_UNSIGNED_BYTE, kInitialColor.data());
556 
557     // Then generate the mips.
558     glGenerateMipmap(GL_TEXTURE_2D);
559     ASSERT_GL_NO_ERROR();
560 
561     // Verify that every mip is correct.
562     verifyAllMips(kTextureSize, kTextureSize, kInitialColor[0]);
563 }
564 
565 // This test generates mipmaps for a large texture and ensures all mips are generated.
TEST_P(MipmapTestES3,GenerateMipmapLargeTexture)566 TEST_P(MipmapTestES3, GenerateMipmapLargeTexture)
567 {
568     constexpr uint32_t kTextureSize = 4096;
569 
570     const std::vector<GLColor> kInitialColor(kTextureSize * kTextureSize,
571                                              GLColor(35, 81, 184, 211));
572 
573     // Create the texture.
574     glBindTexture(GL_TEXTURE_2D, mTexture);
575     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
576                  GL_UNSIGNED_BYTE, kInitialColor.data());
577 
578     // Then generate the mips.
579     glGenerateMipmap(GL_TEXTURE_2D);
580     ASSERT_GL_NO_ERROR();
581 
582     // Enable mipmaps.
583     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
584 
585     // Verify that every mip is correct.
586     verifyAllMips(kTextureSize, kTextureSize, kInitialColor[0]);
587 }
588 
589 // This test generates mipmaps for a large npot texture and ensures all mips are generated.
TEST_P(MipmapTestES3,GenerateMipmapLargeNPOTTexture)590 TEST_P(MipmapTestES3, GenerateMipmapLargeNPOTTexture)
591 {
592     constexpr uint32_t kTextureWidth  = 3840;
593     constexpr uint32_t kTextureHeight = 2160;
594 
595     const std::vector<GLColor> kInitialColor(kTextureWidth * kTextureHeight,
596                                              GLColor(35, 81, 184, 211));
597 
598     // Create the texture.
599     glBindTexture(GL_TEXTURE_2D, mTexture);
600     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, GL_RGBA,
601                  GL_UNSIGNED_BYTE, kInitialColor.data());
602 
603     // Then generate the mips.
604     glGenerateMipmap(GL_TEXTURE_2D);
605     ASSERT_GL_NO_ERROR();
606 
607     // Enable mipmaps.
608     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
609 
610     // Verify that every mip is correct.
611     verifyAllMips(kTextureWidth, kTextureHeight, kInitialColor[0]);
612 }
613 
614 // This test generates mipmaps for an elongated npot texture with the maximum number of mips and
615 // ensures all mips are generated.
TEST_P(MipmapTestES3,GenerateMipmapLongNPOTTexture)616 TEST_P(MipmapTestES3, GenerateMipmapLongNPOTTexture)
617 {
618     // Imprecisions in the result.  http://anglebug.com/42263409
619     ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL());
620 
621     GLint maxTextureWidth = 32767;
622     glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureWidth);
623 
624     constexpr uint32_t kTextureHeight = 43;
625     const uint32_t kTextureWidth      = maxTextureWidth - 1;  // -1 to make the width NPOT
626 
627     const std::vector<GLColor> kInitialColor(kTextureWidth * kTextureHeight,
628                                              GLColor(35, 81, 184, 211));
629 
630     // Create the texture.
631     glBindTexture(GL_TEXTURE_2D, mTexture);
632     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureWidth, kTextureHeight, 0, GL_RGBA,
633                  GL_UNSIGNED_BYTE, kInitialColor.data());
634 
635     // Then generate the mips.
636     glGenerateMipmap(GL_TEXTURE_2D);
637     ASSERT_GL_NO_ERROR();
638 
639     // Enable mipmaps.
640     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
641 
642     // Verify that every mip is correct.
643     verifyAllMips(kTextureWidth, kTextureHeight, kInitialColor[0]);
644 }
645 
646 // This test generates (and uses) mipmaps on a texture using init data. D3D11 will use a
647 // non-renderable TextureStorage for this. The test then disables mips, renders to level zero of the
648 // texture, and reenables mips before using the texture again. To do this, D3D11 has to convert the
649 // TextureStorage into a renderable one. This test ensures that the conversion works correctly. In
650 // particular, on D3D11 Feature Level 9_3 it ensures that both the zero LOD workaround texture AND
651 // the 'normal' texture are copied during conversion.
TEST_P(MipmapTest,GenerateMipmapFromInitDataThenRender)652 TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender)
653 {
654     // http://anglebug.com/42264262
655     ANGLE_SKIP_TEST_IF(IsOzone());
656 
657     // Pass in initial data so the texture is blue.
658     glBindTexture(GL_TEXTURE_2D, mTexture2D);
659     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB,
660                  GL_UNSIGNED_BYTE, mLevelZeroBlueInitData.data());
661 
662     // Then generate the mips.
663     glGenerateMipmap(GL_TEXTURE_2D);
664     ASSERT_GL_NO_ERROR();
665 
666     // Enable mipmaps.
667     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
668 
669     // Now draw the texture to various different sized areas.
670     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
671     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
672 
673     // Use mip level 1
674     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
675     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
676 
677     // Use mip level 2
678     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
679     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
680 
681     ASSERT_GL_NO_ERROR();
682 
683     // Disable mips. Render a quad using the texture and ensure it's blue.
684     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
685     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
686     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
687 
688     // Clear level 0 of the texture to red.
689     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 1.0f, 0.0f, 0.0f, 1.0f);
690 
691     // Reenable mips, and try rendering different-sized quads.
692     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
693 
694     // Level 0 is now red, so this should render red.
695     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
696     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
697 
698     // Use mip level 1, blue.
699     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
700     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
701 
702     // Use mip level 2, blue.
703     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
704     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
705 }
706 
707 // Test that generating mipmap after the image is already created for a single level works.
TEST_P(MipmapTest,GenerateMipmapAfterSingleLevelDraw)708 TEST_P(MipmapTest, GenerateMipmapAfterSingleLevelDraw)
709 {
710     // http://anglebug.com/42264262
711     ANGLE_SKIP_TEST_IF(IsOzone());
712 
713     uint32_t width  = getWindowWidth();
714     uint32_t height = getWindowHeight();
715 
716     const std::vector<GLColor> kInitData(width * height, GLColor::blue);
717 
718     // Pass in initial data so the texture is blue.
719     glBindTexture(GL_TEXTURE_2D, mTexture2D);
720     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
721                  kInitData.data());
722 
723     // Make sure the texture image is created.
724     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
725     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, kInitData[0]);
726 
727     // Then generate the mips.
728     glGenerateMipmap(GL_TEXTURE_2D);
729     ASSERT_GL_NO_ERROR();
730 
731     // Enable mipmaps.
732     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
733 
734     // Draw and make sure the second mip is blue.
735     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
736     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, kInitData[0]);
737 }
738 
739 // Test that generating mipmaps, then modifying the base level and generating mipmaps again works.
TEST_P(MipmapTest,GenerateMipmapAfterModifyingBaseLevel)740 TEST_P(MipmapTest, GenerateMipmapAfterModifyingBaseLevel)
741 {
742     // http://anglebug.com/42264262
743     ANGLE_SKIP_TEST_IF(IsOzone());
744 
745     uint32_t width  = getWindowWidth();
746     uint32_t height = getWindowHeight();
747 
748     const std::vector<GLColor> kInitData(width * height, GLColor::blue);
749 
750     // Pass in initial data so the texture is blue.
751     glBindTexture(GL_TEXTURE_2D, mTexture2D);
752     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
753                  kInitData.data());
754 
755     // Then generate the mips.
756     glGenerateMipmap(GL_TEXTURE_2D);
757     ASSERT_GL_NO_ERROR();
758 
759     // Enable mipmaps.
760     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
761 
762     // Draw and make sure the second mip is blue.  This is to make sure the texture image is
763     // allocated.
764     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
765     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, kInitData[0]);
766 
767     // Modify mip 0 without redefining it.
768     const std::vector<GLColor> kModifyData(width * height, GLColor::green);
769     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE,
770                     kModifyData.data());
771 
772     // Generate the mips again, which should update all levels to the new (green) color.
773     glGenerateMipmap(GL_TEXTURE_2D);
774     ASSERT_GL_NO_ERROR();
775 
776     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
777     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, kModifyData[0]);
778 }
779 
780 // This test ensures that mips are correctly generated from a rendered image.
781 // In particular, on D3D11 Feature Level 9_3, the clear call will be performed on the zero-level
782 // texture, rather than the mipped one. The test ensures that the zero-level texture is correctly
783 // copied into the mipped texture before the mipmaps are generated.
TEST_P(MipmapTest,GenerateMipmapFromRenderedImage)784 TEST_P(MipmapTest, GenerateMipmapFromRenderedImage)
785 {
786     // http://anglebug.com/42264262
787     ANGLE_SKIP_TEST_IF(IsOzone());
788 
789     glBindTexture(GL_TEXTURE_2D, mTexture2D);
790     // Clear the texture to blue.
791     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f);
792 
793     // Then generate the mips
794     glGenerateMipmap(GL_TEXTURE_2D);
795     ASSERT_GL_NO_ERROR();
796 
797     // Enable mips.
798     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
799 
800     // Now draw the texture to various different sized areas.
801     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
802     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
803 
804     // Use mip level 1
805     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
806     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
807 
808     // Use mip level 2
809     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
810     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
811 }
812 
813 // Test to ensure that rendering to a mipmapped texture works, regardless of whether mipmaps are
814 // enabled or not.
815 // TODO: This test hits a texture rebind bug in the D3D11 renderer. Fix this.
TEST_P(MipmapTest,RenderOntoLevelZeroAfterGenerateMipmap)816 TEST_P(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
817 {
818     // TODO(geofflang): Figure out why this is broken on AMD OpenGL
819     ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL());
820     // http://anglebug.com/42264262
821     ANGLE_SKIP_TEST_IF(IsOzone());
822 
823     glBindTexture(GL_TEXTURE_2D, mTexture2D);
824 
825     // Clear the texture to blue.
826     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f);
827 
828     // Now, draw the texture to a quad that's the same size as the texture. This draws to the
829     // default framebuffer. The quad should be blue.
830     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
831     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
832 
833     // Now go back to the texture, and generate mips on it.
834     glGenerateMipmap(GL_TEXTURE_2D);
835     ASSERT_GL_NO_ERROR();
836 
837     // Now try rendering the textured quad again. Note: we've not told GL to use the generated mips.
838     // The quad should be blue.
839     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
840     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
841 
842     // Now tell GL to use the generated mips.
843     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
844     EXPECT_GL_NO_ERROR();
845 
846     // Now render the textured quad again. It should be still be blue.
847     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
848     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
849 
850     // Now render the textured quad to an area smaller than the texture (i.e. to force
851     // minification). This should be blue.
852     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
853     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
854 
855     // Now clear the texture to green. This just clears the top level. The lower mips should remain
856     // blue.
857     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 1.0f, 0.0f, 1.0f);
858 
859     // Render a textured quad equal in size to the texture. This should be green, since we just
860     // cleared level 0.
861     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
862     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green);
863 
864     // Render a small textured quad. This forces minification, so should render blue (the color of
865     // levels 1+).
866     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
867     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
868 
869     // Disable mipmaps again
870     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
871     ASSERT_GL_NO_ERROR();
872 
873     // Render a textured quad equal in size to the texture. This should be green, the color of level
874     // 0 in the texture.
875     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
876     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green);
877 
878     // Render a small textured quad. This would force minification if mips were enabled, but they're
879     // not. Therefore, this should be green.
880     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
881     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green);
882 }
883 
884 // This test defines a valid mipchain manually, with an extra level that's unused on the first few
885 // draws. Later on, it redefines the whole mipchain but this time, uses the last mip that was
886 // already uploaded before. The test expects that mip to be usable.
TEST_P(MipmapTest,DefineValidExtraLevelAndUseItLater)887 TEST_P(MipmapTest, DefineValidExtraLevelAndUseItLater)
888 {
889     // http://anglebug.com/42264262
890     ANGLE_SKIP_TEST_IF(IsOzone());
891 
892     glBindTexture(GL_TEXTURE_2D, mTexture2D);
893 
894     GLubyte *levels[] = {mLevelZeroBlueInitData.data(), mLevelOneGreenInitData.data(),
895                          mLevelTwoRedInitData.data()};
896 
897     int maxLevel = 1 + static_cast<int>(floor(log2(std::max(getWindowWidth(), getWindowHeight()))));
898 
899     for (int i = 0; i < maxLevel; i++)
900     {
901         glTexImage2D(GL_TEXTURE_2D, i, GL_RGB, getWindowWidth() >> i, getWindowHeight() >> i, 0,
902                      GL_RGB, GL_UNSIGNED_BYTE, levels[i % 3]);
903     }
904 
905     // Define an extra level that won't be used for now
906     std::vector<GLubyte> magentaExtraLevelData =
907         createRGBInitData(getWindowWidth() * 2, getWindowHeight() * 2, 255, 0, 255);
908     glTexImage2D(GL_TEXTURE_2D, maxLevel, GL_RGB, 1, 1, 0, GL_RGB, GL_UNSIGNED_BYTE,
909                  magentaExtraLevelData.data());
910 
911     ASSERT_GL_NO_ERROR();
912 
913     // Enable mipmaps.
914     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
915 
916     // Draw a full-sized quad using mip 0, and check it's blue.
917     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
918     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
919 
920     // Draw a full-sized quad using mip 1, and check it's green.
921     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
922     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green);
923 
924     // Draw a full-sized quad using mip 2, and check it's red.
925     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
926     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
927 
928     // Draw a full-sized quad using the last mip, and check it's green.
929     clearAndDrawQuad(m2DProgram, 1, 1);
930     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
931 
932     // Now redefine everything above level 8 to be a mipcomplete chain again.
933     std::vector<GLubyte> levelDoubleSizeYellowInitData =
934         createRGBInitData(getWindowWidth() * 2, getWindowHeight() * 2, 255, 255, 0);
935 
936     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth() * 2, getWindowHeight() * 2, 0, GL_RGB,
937                  GL_UNSIGNED_BYTE, levelDoubleSizeYellowInitData.data());  // 256
938 
939     for (int i = 0; i < maxLevel - 1; i++)
940     {
941         glTexImage2D(GL_TEXTURE_2D, i + 1, GL_RGB, getWindowWidth() >> i, getWindowHeight() >> i, 0,
942                      GL_RGB, GL_UNSIGNED_BYTE, levels[i % 3]);
943     }
944 
945     // At this point we have a valid mip chain, the last level being magenta if we draw 1x1 pixel.
946     clearAndDrawQuad(m2DProgram, 1, 1);
947     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::magenta);
948 
949     // Draw a full-sized quad using mip 0, and check it's yellow.
950     clearAndDrawQuad(m2DProgram, getWindowWidth() * 2, getWindowHeight() * 2);
951     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::yellow);
952 
953     // Draw a full-sized quad using mip 1, and check it's blue.
954     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
955     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
956 
957     // Draw a full-sized quad using mip 2, and check it's green.
958     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
959     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green);
960 }
961 
962 // Regression test for a bug that cause mipmaps to only generate using the top left corner as input.
TEST_P(MipmapTest,MipMapGenerationD3D9Bug)963 TEST_P(MipmapTest, MipMapGenerationD3D9Bug)
964 {
965     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_EXT_texture_storage") ||
966                        !IsGLExtensionEnabled("GL_OES_rgb8_rgba8") ||
967                        !IsGLExtensionEnabled("GL_ANGLE_texture_usage"));
968 
969     // http://anglebug.com/42264262
970     ANGLE_SKIP_TEST_IF(IsOzone());
971 
972     const GLColor mip0Color[4] = {
973         GLColor::red,
974         GLColor::green,
975         GLColor::red,
976         GLColor::green,
977     };
978     const GLColor mip1Color = GLColor(127, 127, 0, 255);
979 
980     GLTexture texture;
981     glBindTexture(GL_TEXTURE_2D, texture);
982     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_USAGE_ANGLE, GL_FRAMEBUFFER_ATTACHMENT_ANGLE);
983     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
984     glTexStorage2DEXT(GL_TEXTURE_2D, 2, GL_RGBA8_OES, 2, 2);
985     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, mip0Color);
986     glGenerateMipmap(GL_TEXTURE_2D);
987 
988     // Only draw to a 1 pixel viewport so the lower mip is used
989     clearAndDrawQuad(m2DProgram, 1, 1);
990     EXPECT_PIXEL_COLOR_NEAR(0, 0, mip1Color, 1.0);
991 }
992 
993 // This test ensures that the level-zero workaround for TextureCubes (on D3D11 Feature Level 9_3)
994 // works as expected. It tests enabling/disabling mipmaps, generating mipmaps, and rendering to
995 // level zero.
TEST_P(MipmapTest,TextureCubeGeneralLevelZero)996 TEST_P(MipmapTest, TextureCubeGeneralLevelZero)
997 {
998     // http://anglebug.com/42261821
999     ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
1000     // http://anglebug.com/42261524
1001     ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan());
1002     // http://issuetracker.google.com/159666631
1003     ANGLE_SKIP_TEST_IF(isSwiftshader());
1004     // http://anglebug.com/42264262
1005     ANGLE_SKIP_TEST_IF(IsOzone());
1006 
1007     glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
1008 
1009     // Draw. Since the negative-Y face's is blue, this should be blue.
1010     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1011     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1012 
1013     // Generate mipmaps, and render. This should be blue.
1014     glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
1015     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1016     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1017     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1018 
1019     // Draw using a smaller viewport (to force a lower LOD of the texture). This should still be
1020     // blue.
1021     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1022     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1023 
1024     // Now clear the negative-Y face of the cube to red.
1025     clearTextureLevel0(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mTextureCube, 1.0f, 0.0f, 0.0f, 1.0f);
1026 
1027     // Draw using a full-size viewport. This should be red.
1028     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1029     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1030 
1031     // Draw using a quarter-size viewport, to force a lower LOD. This should be *BLUE*, since we
1032     // only cleared level zero of the negative-Y face to red, and left its mipmaps blue.
1033     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1034     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1035 
1036     // Disable mipmaps again, and draw a to a quarter-size viewport.
1037     // Since this should use level zero of the texture, this should be *RED*.
1038     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1039     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1040     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1041 }
1042 
1043 // This test ensures that rendering to level-zero of a TextureCube works as expected.
TEST_P(MipmapTest,TextureCubeRenderToLevelZero)1044 TEST_P(MipmapTest, TextureCubeRenderToLevelZero)
1045 {
1046     // http://anglebug.com/42261821
1047     ANGLE_SKIP_TEST_IF(IsFuchsia() && IsIntel() && IsVulkan());
1048     // http://anglebug.com/42261524
1049     ANGLE_SKIP_TEST_IF(IsWindows() && IsIntel() && IsVulkan());
1050     // http://anglebug.com/42264262
1051     ANGLE_SKIP_TEST_IF(IsOzone());
1052 
1053     glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
1054 
1055     // Draw. Since the negative-Y face's is blue, this should be blue.
1056     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1057     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
1058 
1059     // Now clear the negative-Y face of the cube to red.
1060     clearTextureLevel0(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mTextureCube, 1.0f, 0.0f, 0.0f, 1.0f);
1061 
1062     // Draw using a full-size viewport. This should be red.
1063     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1064     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1065 
1066     // Draw a to a quarter-size viewport. This should also be red.
1067     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1068     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1069 }
1070 
1071 // Creates a mipmapped 3D texture with two layers, and calls ANGLE's GenerateMipmap.
1072 // Then tests if the mipmaps are rendered correctly for all two layers.
1073 // This is the same as MipmapTestES3.MipmapsForTexture3D but for GL_OES_texture_3D extension on
1074 // GLES 2.0 instead.
TEST_P(MipmapTest,MipmapsForTexture3DOES)1075 TEST_P(MipmapTest, MipmapsForTexture3DOES)
1076 {
1077     ANGLE_SKIP_TEST_IF(!IsGLExtensionEnabled("GL_OES_texture_3D"));
1078 
1079     // http://anglebug.com/42263501
1080     ANGLE_SKIP_TEST_IF((IsPixel2() || IsNexus5X()) && IsOpenGLES());
1081     // http://anglebug.com/42264262
1082     ANGLE_SKIP_TEST_IF(IsOzone());
1083 
1084     int px = getWindowWidth() / 2;
1085     int py = getWindowHeight() / 2;
1086 
1087     GLTexture texture;
1088     glBindTexture(GL_TEXTURE_3D, texture);
1089 
1090     glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 16, 16, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1091     glTexImage3DOES(GL_TEXTURE_3D, 1, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1092     glTexImage3DOES(GL_TEXTURE_3D, 2, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1093     glTexImage3DOES(GL_TEXTURE_3D, 3, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1094     glTexImage3DOES(GL_TEXTURE_3D, 4, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1095 
1096     // Fill the first layer with red
1097     std::vector<GLColor> pixelsRed(16 * 16, GLColor::red);
1098     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1099                        pixelsRed.data());
1100 
1101     // Fill the second layer with green
1102     std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green);
1103     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1104                        pixelsGreen.data());
1105 
1106     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1107     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1108 
1109     EXPECT_GL_NO_ERROR();
1110 
1111     glGenerateMipmap(GL_TEXTURE_3D);
1112 
1113     EXPECT_GL_NO_ERROR();
1114 
1115     glUseProgram(m3DProgram);
1116 
1117     EXPECT_GL_NO_ERROR();
1118 
1119     // Mipmap level 0
1120     // Draw the first slice
1121     glUniform1f(mTexture3DLODUniformLocation, 0.);
1122     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
1123     drawQuad(m3DProgram, "position", 0.5f);
1124     EXPECT_GL_NO_ERROR();
1125     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1126 
1127     // Draw the second slice
1128     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
1129     drawQuad(m3DProgram, "position", 0.5f);
1130     EXPECT_GL_NO_ERROR();
1131     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1132 
1133     // Regenerate mipmap of same color texture
1134     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1135                        pixelsRed.data());
1136 
1137     glGenerateMipmap(GL_TEXTURE_3D);
1138 
1139     EXPECT_GL_NO_ERROR();
1140 
1141     // Mipmap level 1 8*8*1
1142     glUniform1f(mTexture3DLODUniformLocation, 1.);
1143     drawQuad(m3DProgram, "position", 0.5f);
1144     EXPECT_GL_NO_ERROR();
1145     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1146 
1147     // Mipmap level 2 4*4*1
1148     glUniform1f(mTexture3DLODUniformLocation, 2.);
1149     drawQuad(m3DProgram, "position", 0.5f);
1150     EXPECT_GL_NO_ERROR();
1151     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1152 
1153     // Mipmap level 3 2*2*1
1154     glUniform1f(mTexture3DLODUniformLocation, 3.);
1155     drawQuad(m3DProgram, "position", 0.5f);
1156     EXPECT_GL_NO_ERROR();
1157     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1158 
1159     // Mipmap level 4 1*1*1
1160     glUniform1f(mTexture3DLODUniformLocation, 4.);
1161     drawQuad(m3DProgram, "position", 0.5f);
1162     EXPECT_GL_NO_ERROR();
1163     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1164 }
1165 
1166 // This test verifies 3D texture mipmap generation uses box filter on Metal back-end.
1167 class Mipmap3DBoxFilterTest : public MipmapTest
1168 {};
1169 
TEST_P(Mipmap3DBoxFilterTest,GenMipmapsForTexture3DOES)1170 TEST_P(Mipmap3DBoxFilterTest, GenMipmapsForTexture3DOES)
1171 {
1172     int px = getWindowWidth() / 2;
1173     int py = getWindowHeight() / 2;
1174 
1175     GLTexture texture;
1176     glBindTexture(GL_TEXTURE_3D, texture);
1177 
1178     glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 32, 32, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1179     glTexImage3DOES(GL_TEXTURE_3D, 1, GL_RGBA, 16, 16, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1180     glTexImage3DOES(GL_TEXTURE_3D, 2, GL_RGBA, 8, 8, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1181     glTexImage3DOES(GL_TEXTURE_3D, 3, GL_RGBA, 4, 4, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1182     glTexImage3DOES(GL_TEXTURE_3D, 4, GL_RGBA, 2, 2, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1183     glTexImage3DOES(GL_TEXTURE_3D, 5, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1184 
1185     // Fill the first layer with red
1186     std::vector<GLColor> pixelsRed(32 * 32, GLColor::red);
1187     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 0, 32, 32, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1188                        pixelsRed.data());
1189 
1190     // Fill the second layer with green
1191     std::vector<GLColor> pixelsGreen(32 * 32, GLColor::green);
1192     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 1, 32, 32, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1193                        pixelsGreen.data());
1194 
1195     // Fill the 3rd layer with blue
1196     std::vector<GLColor> pixelsBlue(32 * 32, GLColor::blue);
1197     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 2, 32, 32, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1198                        pixelsBlue.data());
1199 
1200     // Fill the 4th layer with yellow
1201     std::vector<GLColor> pixelsYellow(32 * 32, GLColor::yellow);
1202     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 3, 32, 32, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1203                        pixelsYellow.data());
1204 
1205     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1206     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1207 
1208     EXPECT_GL_NO_ERROR();
1209 
1210     glGenerateMipmap(GL_TEXTURE_3D);
1211 
1212     EXPECT_GL_NO_ERROR();
1213 
1214     glUseProgram(m3DProgram);
1215 
1216     EXPECT_GL_NO_ERROR();
1217 
1218     // Mipmap level 0
1219     // Draw the first slice
1220     glUniform1f(mTexture3DLODUniformLocation, 0.);
1221     glUniform1f(mTexture3DSliceUniformLocation, 0.125f);
1222     drawQuad(m3DProgram, "position", 0.5f);
1223     EXPECT_GL_NO_ERROR();
1224     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1225 
1226     // Draw the second slice
1227     glUniform1f(mTexture3DSliceUniformLocation, 0.375f);
1228     drawQuad(m3DProgram, "position", 0.5f);
1229     EXPECT_GL_NO_ERROR();
1230     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1231 
1232     // Draw the 3rd slice
1233     glUniform1f(mTexture3DSliceUniformLocation, 0.625f);
1234     drawQuad(m3DProgram, "position", 0.5f);
1235     EXPECT_GL_NO_ERROR();
1236     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::blue);
1237 
1238     // Draw the 4th slice
1239     glUniform1f(mTexture3DSliceUniformLocation, 0.875f);
1240     drawQuad(m3DProgram, "position", 0.5f);
1241     EXPECT_GL_NO_ERROR();
1242     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::yellow);
1243 
1244     // Mipmap level 1
1245     // The second mipmap should have two slice.
1246     glUniform1f(mTexture3DLODUniformLocation, 1.);
1247     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
1248     drawQuad(m3DProgram, "position", 0.5f);
1249     EXPECT_GL_NO_ERROR();
1250     EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0);
1251 
1252     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
1253     drawQuad(m3DProgram, "position", 0.5f);
1254     EXPECT_GL_NO_ERROR();
1255     EXPECT_PIXEL_NEAR(px, py, 127, 127, 127, 255, 1.0);
1256 
1257     // Mipmap level 2
1258     // The 3rd mipmap should only have one slice.
1259     glUniform1f(mTexture3DLODUniformLocation, 2.);
1260     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
1261     drawQuad(m3DProgram, "position", 0.5f);
1262     EXPECT_GL_NO_ERROR();
1263     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1264 
1265     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
1266     drawQuad(m3DProgram, "position", 0.5f);
1267     EXPECT_GL_NO_ERROR();
1268     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1269 
1270     // Mipmap level 3
1271     glUniform1f(mTexture3DLODUniformLocation, 3.);
1272     drawQuad(m3DProgram, "position", 0.5f);
1273     EXPECT_GL_NO_ERROR();
1274     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1275 
1276     // Mipmap level 4
1277     glUniform1f(mTexture3DLODUniformLocation, 4.);
1278     drawQuad(m3DProgram, "position", 0.5f);
1279     EXPECT_GL_NO_ERROR();
1280     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1281 
1282     // Mipmap level 5
1283     glUniform1f(mTexture3DLODUniformLocation, 5.);
1284     drawQuad(m3DProgram, "position", 0.5f);
1285     EXPECT_GL_NO_ERROR();
1286     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1287 }
1288 
1289 // Test that non-power of two texture also has mipmap generated using box filter
TEST_P(Mipmap3DBoxFilterTest,GenMipmapsForTexture3DOESNpot)1290 TEST_P(Mipmap3DBoxFilterTest, GenMipmapsForTexture3DOESNpot)
1291 {
1292     int px = getWindowWidth() / 2;
1293     int py = getWindowHeight() / 2;
1294 
1295     GLTexture texture;
1296     glBindTexture(GL_TEXTURE_3D, texture);
1297 
1298     glTexImage3DOES(GL_TEXTURE_3D, 0, GL_RGBA, 30, 30, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1299     glTexImage3DOES(GL_TEXTURE_3D, 1, GL_RGBA, 15, 15, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1300     glTexImage3DOES(GL_TEXTURE_3D, 2, GL_RGBA, 7, 7, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1301     glTexImage3DOES(GL_TEXTURE_3D, 3, GL_RGBA, 3, 3, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1302     glTexImage3DOES(GL_TEXTURE_3D, 4, GL_RGBA, 1, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1303 
1304     // Fill the first layer with red
1305     std::vector<GLColor> pixelsRed(30 * 30, GLColor::red);
1306     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 0, 30, 30, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1307                        pixelsRed.data());
1308 
1309     // Fill the second layer with green
1310     std::vector<GLColor> pixelsGreen(30 * 30, GLColor::green);
1311     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 1, 30, 30, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1312                        pixelsGreen.data());
1313 
1314     // Fill the 3rd layer with blue
1315     std::vector<GLColor> pixelsBlue(30 * 30, GLColor::blue);
1316     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 2, 30, 30, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1317                        pixelsBlue.data());
1318 
1319     // Fill the 4th layer with yellow
1320     std::vector<GLColor> pixelsYellow(30 * 30, GLColor::yellow);
1321     glTexSubImage3DOES(GL_TEXTURE_3D, 0, 0, 0, 3, 30, 30, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1322                        pixelsYellow.data());
1323 
1324     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1325     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1326 
1327     EXPECT_GL_NO_ERROR();
1328 
1329     glGenerateMipmap(GL_TEXTURE_3D);
1330 
1331     EXPECT_GL_NO_ERROR();
1332 
1333     glUseProgram(m3DProgram);
1334 
1335     EXPECT_GL_NO_ERROR();
1336 
1337     // Mipmap level 0
1338     // Draw the first slice
1339     glUniform1f(mTexture3DLODUniformLocation, 0.);
1340     glUniform1f(mTexture3DSliceUniformLocation, 0.125f);
1341     drawQuad(m3DProgram, "position", 0.5f);
1342     EXPECT_GL_NO_ERROR();
1343     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1344 
1345     // Draw the second slice
1346     glUniform1f(mTexture3DSliceUniformLocation, 0.375f);
1347     drawQuad(m3DProgram, "position", 0.5f);
1348     EXPECT_GL_NO_ERROR();
1349     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1350 
1351     // Draw the 3rd slice
1352     glUniform1f(mTexture3DSliceUniformLocation, 0.625f);
1353     drawQuad(m3DProgram, "position", 0.5f);
1354     EXPECT_GL_NO_ERROR();
1355     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::blue);
1356 
1357     // Draw the 4th slice
1358     glUniform1f(mTexture3DSliceUniformLocation, 0.875f);
1359     drawQuad(m3DProgram, "position", 0.5f);
1360     EXPECT_GL_NO_ERROR();
1361     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::yellow);
1362 
1363     // Mipmap level 1
1364     // The second mipmap should have two slice.
1365     glUniform1f(mTexture3DLODUniformLocation, 1.);
1366     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
1367     drawQuad(m3DProgram, "position", 0.5f);
1368     EXPECT_GL_NO_ERROR();
1369     EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0);
1370 
1371     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
1372     drawQuad(m3DProgram, "position", 0.5f);
1373     EXPECT_GL_NO_ERROR();
1374     EXPECT_PIXEL_NEAR(px, py, 127, 127, 127, 255, 1.0);
1375 
1376     // Mipmap level 2
1377     // The 3rd mipmap should only have one slice.
1378     glUniform1f(mTexture3DLODUniformLocation, 2.);
1379     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
1380     drawQuad(m3DProgram, "position", 0.5f);
1381     EXPECT_GL_NO_ERROR();
1382     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1383 
1384     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
1385     drawQuad(m3DProgram, "position", 0.5f);
1386     EXPECT_GL_NO_ERROR();
1387     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1388 
1389     // Mipmap level 3
1390     glUniform1f(mTexture3DLODUniformLocation, 3.);
1391     drawQuad(m3DProgram, "position", 0.5f);
1392     EXPECT_GL_NO_ERROR();
1393     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1394 
1395     // Mipmap level 4
1396     glUniform1f(mTexture3DLODUniformLocation, 4.);
1397     drawQuad(m3DProgram, "position", 0.5f);
1398     EXPECT_GL_NO_ERROR();
1399     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1400 
1401     // Mipmap level 5
1402     glUniform1f(mTexture3DLODUniformLocation, 5.);
1403     drawQuad(m3DProgram, "position", 0.5f);
1404     EXPECT_GL_NO_ERROR();
1405     EXPECT_PIXEL_NEAR(px, py, 127, 127, 64, 255, 1.0);
1406 }
1407 
1408 // Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap.
1409 // Then tests if the mipmaps are rendered correctly for all three layers.
TEST_P(MipmapTestES3,MipmapsForTextureArray)1410 TEST_P(MipmapTestES3, MipmapsForTextureArray)
1411 {
1412     int px = getWindowWidth() / 2;
1413     int py = getWindowHeight() / 2;
1414 
1415     glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture);
1416 
1417     glTexStorage3D(GL_TEXTURE_2D_ARRAY, 5, GL_RGBA8, 16, 16, 3);
1418 
1419     // Fill the first layer with red
1420     std::vector<GLColor> pixelsRed(16 * 16, GLColor::red);
1421     glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1422                     pixelsRed.data());
1423 
1424     // Fill the second layer with green
1425     std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green);
1426     glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1427                     pixelsGreen.data());
1428 
1429     // Fill the third layer with blue
1430     std::vector<GLColor> pixelsBlue(16 * 16, GLColor::blue);
1431     glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 2, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1432                     pixelsBlue.data());
1433 
1434     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1435     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1436 
1437     EXPECT_GL_NO_ERROR();
1438 
1439     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
1440 
1441     EXPECT_GL_NO_ERROR();
1442 
1443     glUseProgram(mArrayProgram);
1444 
1445     EXPECT_GL_NO_ERROR();
1446 
1447     // Draw the first slice
1448     glUniform1i(mTextureArraySliceUniformLocation, 0);
1449     drawQuad(mArrayProgram, "position", 0.5f);
1450     EXPECT_GL_NO_ERROR();
1451     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1452 
1453     // Draw the second slice
1454     glUniform1i(mTextureArraySliceUniformLocation, 1);
1455     drawQuad(mArrayProgram, "position", 0.5f);
1456     EXPECT_GL_NO_ERROR();
1457     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1458 
1459     // Draw the third slice
1460     glUniform1i(mTextureArraySliceUniformLocation, 2);
1461     drawQuad(mArrayProgram, "position", 0.5f);
1462     EXPECT_GL_NO_ERROR();
1463     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::blue);
1464 }
1465 
1466 // Create a mipmapped 2D array texture with more layers than width / height, and call
1467 // GenerateMipmap.
TEST_P(MipmapTestES3,MipmapForDeepTextureArray)1468 TEST_P(MipmapTestES3, MipmapForDeepTextureArray)
1469 {
1470     int px = getWindowWidth() / 2;
1471     int py = getWindowHeight() / 2;
1472 
1473     glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture);
1474 
1475     // Fill the whole texture with red.
1476     std::vector<GLColor> pixelsRed(2 * 2 * 4, GLColor::red);
1477     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1478                  pixelsRed.data());
1479 
1480     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1481     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1482 
1483     EXPECT_GL_NO_ERROR();
1484 
1485     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
1486 
1487     EXPECT_GL_NO_ERROR();
1488 
1489     glUseProgram(mArrayProgram);
1490 
1491     EXPECT_GL_NO_ERROR();
1492 
1493     // Draw the first slice
1494     glUniform1i(mTextureArraySliceUniformLocation, 0);
1495     drawQuad(mArrayProgram, "position", 0.5f);
1496     EXPECT_GL_NO_ERROR();
1497     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1498 
1499     // Draw the fourth slice
1500     glUniform1i(mTextureArraySliceUniformLocation, 3);
1501     drawQuad(mArrayProgram, "position", 0.5f);
1502     EXPECT_GL_NO_ERROR();
1503     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1504 }
1505 
1506 // Creates a mipmapped 3D texture with two layers, and calls ANGLE's GenerateMipmap.
1507 // Then tests if the mipmaps are rendered correctly for all two layers.
TEST_P(MipmapTestES3,MipmapsForTexture3D)1508 TEST_P(MipmapTestES3, MipmapsForTexture3D)
1509 {
1510     int px = getWindowWidth() / 2;
1511     int py = getWindowHeight() / 2;
1512 
1513     glBindTexture(GL_TEXTURE_3D, mTexture);
1514 
1515     glTexStorage3D(GL_TEXTURE_3D, 5, GL_RGBA8, 16, 16, 2);
1516 
1517     // Fill the first layer with red
1518     std::vector<GLColor> pixelsRed(16 * 16, GLColor::red);
1519     glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1520                     pixelsRed.data());
1521 
1522     // Fill the second layer with green
1523     std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green);
1524     glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1525                     pixelsGreen.data());
1526 
1527     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1528     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1529 
1530     EXPECT_GL_NO_ERROR();
1531 
1532     glGenerateMipmap(GL_TEXTURE_3D);
1533 
1534     EXPECT_GL_NO_ERROR();
1535 
1536     glUseProgram(m3DProgram);
1537 
1538     EXPECT_GL_NO_ERROR();
1539 
1540     // Mipmap level 0
1541     // Draw the first slice
1542     glUniform1f(mTexture3DLODUniformLocation, 0.);
1543     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
1544     drawQuad(m3DProgram, "position", 0.5f);
1545     EXPECT_GL_NO_ERROR();
1546     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1547 
1548     // Draw the second slice
1549     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
1550     drawQuad(m3DProgram, "position", 0.5f);
1551     EXPECT_GL_NO_ERROR();
1552     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1553 
1554     // Regenerate mipmap of same color texture
1555     glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
1556                     pixelsRed.data());
1557 
1558     glGenerateMipmap(GL_TEXTURE_3D);
1559 
1560     EXPECT_GL_NO_ERROR();
1561 
1562     // Mipmap level 1 8*8*1
1563     glUniform1f(mTexture3DLODUniformLocation, 1.);
1564     drawQuad(m3DProgram, "position", 0.5f);
1565     EXPECT_GL_NO_ERROR();
1566     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1567 
1568     // Mipmap level 2 4*4*1
1569     glUniform1f(mTexture3DLODUniformLocation, 2.);
1570     drawQuad(m3DProgram, "position", 0.5f);
1571     EXPECT_GL_NO_ERROR();
1572     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1573 
1574     // Mipmap level 3 2*2*1
1575     glUniform1f(mTexture3DLODUniformLocation, 3.);
1576     drawQuad(m3DProgram, "position", 0.5f);
1577     EXPECT_GL_NO_ERROR();
1578     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1579 
1580     // Mipmap level 4 1*1*1
1581     glUniform1f(mTexture3DLODUniformLocation, 4.);
1582     drawQuad(m3DProgram, "position", 0.5f);
1583     EXPECT_GL_NO_ERROR();
1584     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1585 }
1586 
1587 // Create a 2D array, then immediately redefine it to have fewer layers.  Regression test for a bug
1588 // in the Vulkan backend where the old higher-layer-count data upload was not removed.
TEST_P(MipmapTestES3,TextureArrayRedefineThenGenerateMipmap)1589 TEST_P(MipmapTestES3, TextureArrayRedefineThenGenerateMipmap)
1590 {
1591     int px = getWindowWidth() / 2;
1592     int py = getWindowHeight() / 2;
1593 
1594     glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture);
1595 
1596     // Fill the whole texture with red, then redefine it and fill with green
1597     std::vector<GLColor> pixelsRed(2 * 2 * 4, GLColor::red);
1598     std::vector<GLColor> pixelsGreen(2 * 2 * 2, GLColor::green);
1599     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1600                  pixelsRed.data());
1601     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1602                  pixelsGreen.data());
1603 
1604     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1605     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1606     EXPECT_GL_NO_ERROR();
1607 
1608     // Generate mipmaps
1609     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
1610     EXPECT_GL_NO_ERROR();
1611 
1612     glUseProgram(mArrayProgram);
1613     EXPECT_GL_NO_ERROR();
1614 
1615     // Draw the first slice
1616     glUniform1i(mTextureArraySliceUniformLocation, 0);
1617     drawQuad(mArrayProgram, "position", 0.5f);
1618     EXPECT_GL_NO_ERROR();
1619     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1620 
1621     // Draw the second slice
1622     glUniform1i(mTextureArraySliceUniformLocation, 1);
1623     drawQuad(mArrayProgram, "position", 0.5f);
1624     EXPECT_GL_NO_ERROR();
1625     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1626 }
1627 
1628 // Create a 2D array, use it, then redefine it to have fewer layers.  Regression test for a bug in
1629 // the Vulkan backend where the old higher-layer-count data upload was not removed.
TEST_P(MipmapTestES3,TextureArrayUseThenRedefineThenGenerateMipmap)1630 TEST_P(MipmapTestES3, TextureArrayUseThenRedefineThenGenerateMipmap)
1631 {
1632     int px = getWindowWidth() / 2;
1633     int py = getWindowHeight() / 2;
1634 
1635     glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture);
1636 
1637     // Fill the whole texture with red.
1638     std::vector<GLColor> pixelsRed(2 * 2 * 4, GLColor::red);
1639     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1640                  pixelsRed.data());
1641 
1642     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
1643     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1644     EXPECT_GL_NO_ERROR();
1645 
1646     // Generate mipmap
1647     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
1648     EXPECT_GL_NO_ERROR();
1649 
1650     glUseProgram(mArrayProgram);
1651     EXPECT_GL_NO_ERROR();
1652 
1653     // Draw the first slice
1654     glUniform1i(mTextureArraySliceUniformLocation, 0);
1655     drawQuad(mArrayProgram, "position", 0.5f);
1656     EXPECT_GL_NO_ERROR();
1657     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1658 
1659     // Draw the fourth slice
1660     glUniform1i(mTextureArraySliceUniformLocation, 3);
1661     drawQuad(mArrayProgram, "position", 0.5f);
1662     EXPECT_GL_NO_ERROR();
1663     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
1664 
1665     // Redefine the image and fill with green
1666     std::vector<GLColor> pixelsGreen(2 * 2 * 2, GLColor::green);
1667     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE,
1668                  pixelsGreen.data());
1669 
1670     // Generate mipmap
1671     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
1672     EXPECT_GL_NO_ERROR();
1673 
1674     // Draw the first slice
1675     glUniform1i(mTextureArraySliceUniformLocation, 0);
1676     drawQuad(mArrayProgram, "position", 0.5f);
1677     EXPECT_GL_NO_ERROR();
1678     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1679 
1680     // Draw the second slice
1681     glUniform1i(mTextureArraySliceUniformLocation, 1);
1682     drawQuad(mArrayProgram, "position", 0.5f);
1683     EXPECT_GL_NO_ERROR();
1684     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
1685 }
1686 
1687 // Create a 2D texture with levels 0-2, call GenerateMipmap with base level 1 so that level 0 stays
1688 // the same, and then sample levels 0 and 2.
1689 // GLES 3.0.4 section 3.8.10:
1690 // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from
1691 // the levelbase array, regardless of their previous contents. All other mipmap arrays, including
1692 // the levelbase array, are left unchanged by this computation."
TEST_P(MipmapTestES3,GenerateMipmapBaseLevel)1693 TEST_P(MipmapTestES3, GenerateMipmapBaseLevel)
1694 {
1695     // Observed incorrect rendering on AMD, sampling level 2 returns black.
1696     ANGLE_SKIP_TEST_IF(IsAMD() && IsDesktopOpenGL());
1697 
1698     // TODO(anglebug.com/40096747): Failing on ARM-based Apple DTKs.
1699     ANGLE_SKIP_TEST_IF(IsMac() && IsARM64() && IsDesktopOpenGL());
1700 
1701     glBindTexture(GL_TEXTURE_2D, mTexture);
1702 
1703     ASSERT_EQ(getWindowWidth(), getWindowHeight());
1704 
1705     // Fill level 0 with blue
1706     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue);
1707     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
1708                  GL_UNSIGNED_BYTE, pixelsBlue.data());
1709 
1710     // Fill level 1 with red
1711     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight() / 4, GLColor::red);
1712     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0,
1713                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data());
1714 
1715     // Fill level 2 with green
1716     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 16, GLColor::green);
1717     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth() / 4, getWindowHeight() / 4, 0,
1718                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsGreen.data());
1719 
1720     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1721     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1722     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
1723 
1724     EXPECT_GL_NO_ERROR();
1725 
1726     // The blue level 0 should be untouched by this since base level is 1.
1727     glGenerateMipmap(GL_TEXTURE_2D);
1728 
1729     EXPECT_GL_NO_ERROR();
1730 
1731     // Draw using level 2. It should be set to red by GenerateMipmap.
1732     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1733     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
1734 
1735     // Draw using level 0. It should still be blue.
1736     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1737     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1738     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
1739 }
1740 
1741 // Test that generating mipmaps doesn't discard updates staged to out-of-range mips.
TEST_P(MipmapTestES3,GenerateMipmapPreservesOutOfRangeMips)1742 TEST_P(MipmapTestES3, GenerateMipmapPreservesOutOfRangeMips)
1743 {
1744     // http://anglebug.com/42263372
1745     ANGLE_SKIP_TEST_IF(IsOpenGL() && IsWindows() && (IsAMD() || IsIntel()));
1746 
1747     // http://anglebug.com/42263374
1748     ANGLE_SKIP_TEST_IF(IsOpenGL() && IsLinux() && IsIntel());
1749 
1750     // http://anglebug.com/40096708
1751     ANGLE_SKIP_TEST_IF(IsOpenGLES() && IsNVIDIAShield());
1752 
1753     // TODO(anglebug.com/40096747): Failing on ARM-based Apple DTKs.
1754     ANGLE_SKIP_TEST_IF(IsMac() && IsARM64() && IsDesktopOpenGL());
1755 
1756     constexpr GLint kTextureSize = 16;
1757     const std::vector<GLColor> kLevel0Data(kTextureSize * kTextureSize, GLColor::red);
1758     const std::vector<GLColor> kLevel1Data(kTextureSize * kTextureSize, GLColor::green);
1759     const std::vector<GLColor> kLevel6Data(kTextureSize * kTextureSize, GLColor::blue);
1760 
1761     // Initialize a 16x16 RGBA8 texture with red, green and blue for levels 0, 1 and 6 respectively.
1762     GLTexture tex;
1763     glBindTexture(GL_TEXTURE_2D, tex);
1764     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
1765                  GL_UNSIGNED_BYTE, kLevel0Data.data());
1766     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
1767                  GL_UNSIGNED_BYTE, kLevel1Data.data());
1768     glTexImage2D(GL_TEXTURE_2D, 6, GL_RGBA, kTextureSize, kTextureSize, 0, GL_RGBA,
1769                  GL_UNSIGNED_BYTE, kLevel6Data.data());
1770     ASSERT_GL_NO_ERROR();
1771 
1772     // Set base level to 1, and generate mipmaps.  Levels 1 through 5 will be green.
1773     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
1774     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1775     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1776 
1777     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1778     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, kLevel1Data[0]);
1779 
1780     glGenerateMipmap(GL_TEXTURE_2D);
1781     ASSERT_GL_NO_ERROR();
1782 
1783     // Verify that the mips are all green.
1784     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1785     for (int mip = 0; mip < 5; ++mip)
1786     {
1787         int scale = 1 << mip;
1788         clearAndDrawQuad(m2DProgram, getWindowWidth() / scale, getWindowHeight() / scale);
1789         EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / scale / 2, getWindowHeight() / scale / 2,
1790                               kLevel1Data[0]);
1791     }
1792 
1793     // Verify that level 0 is red.  TODO: setting MAX_LEVEL should be unnecessary, but is needed to
1794     // work around a bug in the Vulkan backend.  http://anglebug.com/40096706
1795     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1796     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
1797     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1798 
1799     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1800     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, kLevel0Data[0]);
1801 
1802     // Verify that level 6 is blue.
1803     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 6);
1804     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 6);
1805 
1806     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1807     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, kLevel6Data[0]);
1808 }
1809 
1810 // Create a cube map with levels 0-2, call GenerateMipmap with base level 1 so that level 0 stays
1811 // the same, and then sample levels 0 and 2.
1812 // GLES 3.0.4 section 3.8.10:
1813 // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from
1814 // the levelbase array, regardless of their previous contents. All other mipmap arrays, including
1815 // the levelbase array, are left unchanged by this computation."
TEST_P(MipmapTestES3,GenerateMipmapCubeBaseLevel)1816 TEST_P(MipmapTestES3, GenerateMipmapCubeBaseLevel)
1817 {
1818     // Observed incorrect rendering on AMD, sampling level 2 returns black.
1819     ANGLE_SKIP_TEST_IF(IsAMD() && IsDesktopOpenGL());
1820 
1821     // TODO(anglebug.com/40096747): Failing on ARM-based Apple DTKs.
1822     ANGLE_SKIP_TEST_IF(IsMac() && IsARM64() && IsDesktopOpenGL());
1823 
1824     ASSERT_EQ(getWindowWidth(), getWindowHeight());
1825 
1826     glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture);
1827     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowWidth(), GLColor::blue);
1828     TexImageCubeMapFaces(0, GL_RGBA8, getWindowWidth(), GL_RGBA, GL_UNSIGNED_BYTE,
1829                          pixelsBlue.data());
1830 
1831     // Fill level 1 with red
1832     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowWidth() / 4, GLColor::red);
1833     TexImageCubeMapFaces(1, GL_RGBA8, getWindowWidth() / 2, GL_RGBA, GL_UNSIGNED_BYTE,
1834                          pixelsRed.data());
1835 
1836     // Fill level 2 with green
1837     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowWidth() / 16, GLColor::green);
1838     TexImageCubeMapFaces(2, GL_RGBA8, getWindowWidth() / 4, GL_RGBA, GL_UNSIGNED_BYTE,
1839                          pixelsGreen.data());
1840 
1841     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1842     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1843     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 1);
1844 
1845     EXPECT_GL_NO_ERROR();
1846 
1847     // The blue level 0 should be untouched by this since base level is 1.
1848     glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
1849 
1850     EXPECT_GL_NO_ERROR();
1851 
1852     // Draw using level 2. It should be set to red by GenerateMipmap.
1853     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1854     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
1855 
1856     // Observed incorrect rendering on NVIDIA, level zero seems to be incorrectly affected by
1857     // GenerateMipmap.
1858     // http://anglebug.com/42262495
1859     ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsOpenGL());
1860 
1861     // Draw using level 0. It should still be blue.
1862     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
1863     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1864     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
1865 }
1866 
1867 // Create a texture with levels 0-2, call GenerateMipmap with max level 1 so that level 2 stays the
1868 // same, and then sample levels 1 and 2.
1869 // GLES 3.0.4 section 3.8.10:
1870 // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from
1871 // the levelbase array, regardless of their previous contents. All other mipmap arrays, including
1872 // the levelbase array, are left unchanged by this computation."
TEST_P(MipmapTestES3,GenerateMipmapMaxLevel)1873 TEST_P(MipmapTestES3, GenerateMipmapMaxLevel)
1874 {
1875     // TODO(anglebug.com/40096747): Failing on ARM-based Apple DTKs.
1876     ANGLE_SKIP_TEST_IF(IsMac() && IsARM64() && IsDesktopOpenGL());
1877 
1878     glBindTexture(GL_TEXTURE_2D, mTexture);
1879 
1880     // Fill level 0 with blue
1881     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue);
1882     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
1883                  GL_UNSIGNED_BYTE, pixelsBlue.data());
1884 
1885     // Fill level 1 with red
1886     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight() / 4, GLColor::red);
1887     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0,
1888                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data());
1889 
1890     // Fill level 2 with green
1891     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 16, GLColor::green);
1892     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth() / 4, getWindowHeight() / 4, 0,
1893                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsGreen.data());
1894 
1895     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1896     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1897     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
1898 
1899     EXPECT_GL_NO_ERROR();
1900 
1901     // The green level 2 should be untouched by this since max level is 1.
1902     glGenerateMipmap(GL_TEXTURE_2D);
1903 
1904     EXPECT_GL_NO_ERROR();
1905 
1906     // Draw using level 1. It should be set to blue by GenerateMipmap.
1907     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
1908     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
1909 
1910     // Draw using level 2. It should still be green.
1911     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
1912     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1913     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green);
1914 }
1915 
1916 // Call GenerateMipmap with out-of-range base level. The spec is interpreted so that an out-of-range
1917 // base level does not have a color-renderable/texture-filterable internal format, so the
1918 // GenerateMipmap call generates INVALID_OPERATION. GLES 3.0.4 section 3.8.10:
1919 // "If the levelbase array was not specified with an unsized internal format from table 3.3 or a
1920 // sized internal format that is both color-renderable and texture-filterable according to table
1921 // 3.13, an INVALID_OPERATION error is generated."
TEST_P(MipmapTestES3,GenerateMipmapBaseLevelOutOfRange)1922 TEST_P(MipmapTestES3, GenerateMipmapBaseLevelOutOfRange)
1923 {
1924     glBindTexture(GL_TEXTURE_2D, mTexture);
1925 
1926     // Fill level 0 with blue
1927     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue);
1928     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
1929                  GL_UNSIGNED_BYTE, pixelsBlue.data());
1930 
1931     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1000);
1932 
1933     EXPECT_GL_NO_ERROR();
1934 
1935     // Expecting the out-of-range base level to be treated as not color-renderable and
1936     // texture-filterable.
1937     glGenerateMipmap(GL_TEXTURE_2D);
1938     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1939 
1940     // Draw using level 0. It should still be blue.
1941     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1942     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1943     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1944     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1945     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
1946 }
1947 
1948 // Call GenerateMipmap with out-of-range base level on an immutable texture. The base level should
1949 // be clamped, so the call doesn't generate an error.
TEST_P(MipmapTestES3,GenerateMipmapBaseLevelOutOfRangeImmutableTexture)1950 TEST_P(MipmapTestES3, GenerateMipmapBaseLevelOutOfRangeImmutableTexture)
1951 {
1952     glBindTexture(GL_TEXTURE_2D, mTexture);
1953 
1954     glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1955     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
1956 
1957     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1000);
1958 
1959     EXPECT_GL_NO_ERROR();
1960 
1961     // This is essentially a no-op, since the texture only has one level.
1962     glGenerateMipmap(GL_TEXTURE_2D);
1963 
1964     EXPECT_GL_NO_ERROR();
1965 
1966     // The only level of the texture should still be green.
1967     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1968     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1969     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1970     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green);
1971 }
1972 
1973 // A native version of the WebGL2 test tex-base-level-bug.html
TEST_P(MipmapTestES3,BaseLevelTextureBug)1974 TEST_P(MipmapTestES3, BaseLevelTextureBug)
1975 {
1976     ANGLE_SKIP_TEST_IF(IsOpenGL() && IsAMD());
1977 
1978     // Regression in 10.12.4 needing workaround -- crbug.com/705865.
1979     // Seems to be passing on AMD GPUs. Definitely not NVIDIA.
1980     // Probably not Intel.
1981     ANGLE_SKIP_TEST_IF(IsMac() && IsNVIDIA());
1982 
1983     // TODO(anglebug.com/40096747): Failing on ARM-based Apple DTKs.
1984     ANGLE_SKIP_TEST_IF(IsMac() && IsARM64() && IsDesktopOpenGL());
1985 
1986     std::vector<GLColor> texDataRed(2u * 2u, GLColor::red);
1987 
1988     glBindTexture(GL_TEXTURE_2D, mTexture);
1989     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, texDataRed.data());
1990     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
1991     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1992     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1993     ASSERT_GL_NO_ERROR();
1994 
1995     drawQuad(m2DProgram, "position", 0.5f);
1996     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
1997 
1998     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
1999     ASSERT_GL_NO_ERROR();
2000 
2001     drawQuad(m2DProgram, "position", 0.5f);
2002     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
2003 }
2004 
TEST_P(MipmapTestES31,MipmapWithMemoryBarrier)2005 TEST_P(MipmapTestES31, MipmapWithMemoryBarrier)
2006 {
2007     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight(), GLColor::red);
2008     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 4, GLColor::green);
2009 
2010     constexpr char kVS[] = R"(#version 300 es
2011 precision highp float;
2012 in vec4 position;
2013 out vec2 texcoord;
2014 
2015 void main()
2016 {
2017     gl_Position = vec4(position.xy, 0.0, 1.0);
2018     texcoord = (position.xy * 0.5) + 0.5;
2019 })";
2020 
2021     constexpr char kFS[] = R"(#version 300 es
2022 precision highp float;
2023 uniform highp sampler2D tex;
2024 in vec2 texcoord;
2025 out vec4 out_FragColor;
2026 
2027 void main()
2028 {
2029     out_FragColor = texture(tex, texcoord);
2030 })";
2031 
2032     ANGLE_GL_PROGRAM(m2DProgram, kVS, kFS);
2033     glUseProgram(m2DProgram);
2034 
2035     // Create a texture with red and enable the mipmap
2036     GLTexture texture;
2037     glBindTexture(GL_TEXTURE_2D, texture);
2038     // Fill level 0 with red
2039     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
2040                  GL_UNSIGNED_BYTE, pixelsRed.data());
2041     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
2042     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2043     EXPECT_GL_NO_ERROR();
2044     glGenerateMipmap(GL_TEXTURE_2D);
2045     ASSERT_GL_NO_ERROR();
2046     // level 2 is red
2047     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
2048     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
2049 
2050     // Clear the level 1 to green
2051     glTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, getWindowWidth() / 2, getWindowHeight() / 2, GL_RGBA,
2052                     GL_UNSIGNED_BYTE, pixelsGreen.data());
2053     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
2054     EXPECT_GL_NO_ERROR();
2055     glGenerateMipmap(GL_TEXTURE_2D);
2056     ASSERT_GL_NO_ERROR();
2057     // Insert a memory barrier, then it will break the graph node submission order.
2058     glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT);
2059     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
2060     // level 0 is red
2061     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
2062     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
2063     // Draw using level 2. It should be set to green by GenerateMipmap.
2064     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
2065     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green);
2066 }
2067 
2068 // Tests respecifying 3D mipmaps.
TEST_P(MipmapTestES3,Generate3DMipmapRespecification)2069 TEST_P(MipmapTestES3, Generate3DMipmapRespecification)
2070 {
2071     std::vector<GLColor> pixels(256 * 256 * 100, GLColor::black);
2072 
2073     GLTexture texture;
2074     glBindTexture(GL_TEXTURE_3D, texture);
2075     glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 256, 256, 100, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2076                  pixels.data());
2077     glTexImage3D(GL_TEXTURE_3D, 1, GL_RGBA, 128, 128, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
2078                  pixels.data());
2079     glGenerateMipmap(GL_TEXTURE_3D);
2080 
2081     ASSERT_GL_NO_ERROR();
2082 }
2083 
2084 // Test the calling glGenerateMipmap on a texture with a zero dimension doesn't crash.
TEST_P(MipmapTestES3,GenerateMipmapZeroSize)2085 TEST_P(MipmapTestES3, GenerateMipmapZeroSize)
2086 {
2087     GLTexture texture;
2088     glBindTexture(GL_TEXTURE_2D, texture);
2089 
2090     // Create a texture with at least one dimension that's zero.
2091     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 2, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
2092 
2093     // Attempt to generate mipmap.  This shouldn't crash.
2094     glGenerateMipmap(GL_TEXTURE_2D);
2095     EXPECT_GL_NO_ERROR();
2096 
2097     // Try the same with a 3D texture where depth is 0.
2098     GLTexture texture2;
2099     glBindTexture(GL_TEXTURE_3D, texture2);
2100     glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, 2, 2, 0, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
2101     glGenerateMipmap(GL_TEXTURE_3D);
2102 }
2103 
2104 // Test that reducing the size of the mipchain by resizing the base image then deleting it doesn't
2105 // cause a crash. Issue found by fuzzer.
TEST_P(MipmapTestES3,ResizeBaseMipTo1x1ThenDelete)2106 TEST_P(MipmapTestES3, ResizeBaseMipTo1x1ThenDelete)
2107 {
2108     GLTexture tex;
2109     glBindTexture(GL_TEXTURE_2D, tex);
2110 
2111     std::vector<GLColor> data(2, GLColor::blue);
2112 
2113     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
2114     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
2115 
2116     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
2117     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
2118 
2119     data[0] = GLColor::green;
2120     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, data.data());
2121 
2122     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
2123 
2124     tex.reset();
2125     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
2126 }
2127 
2128 // Test the calling generateMipmap with redefining texture and modifying baselevel.
TEST_P(MipmapTestES3,GenerateMipmapWithRedefineLevelAndTexture)2129 TEST_P(MipmapTestES3, GenerateMipmapWithRedefineLevelAndTexture)
2130 {
2131     std::vector<GLColor> pixels(1000000, GLColor::black);
2132 
2133     GLTexture texture;
2134     glBindTexture(GL_TEXTURE_2D, texture);
2135     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
2136     glGenerateMipmap(GL_TEXTURE_2D);
2137     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels.data());
2138     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
2139     glGenerateMipmap(GL_TEXTURE_2D);
2140 
2141     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
2142     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::black);
2143 }
2144 
2145 // Test that manually generating mipmaps using draw calls is functional
TEST_P(MipmapTestES31,GenerateMipmapWithDraw)2146 TEST_P(MipmapTestES31, GenerateMipmapWithDraw)
2147 {
2148     constexpr char kVS[] = R"(#version 310 es
2149 precision highp float;
2150 
2151 in vec4 position;
2152 out vec2 texcoord;
2153 
2154 void main()
2155 {
2156     gl_Position = position;
2157     texcoord = (position.xy * 0.5) + 0.5;
2158 })";
2159 
2160     constexpr char kFS[] = R"(#version 310 es
2161 precision highp float;
2162 
2163 uniform highp sampler2D tex;
2164 
2165 in vec2 texcoord;
2166 out vec4 frag_color;
2167 
2168 void main()
2169 {
2170     highp vec4 samples = textureGatherOffset(tex, texcoord, ivec2(0, 0), 0);
2171     highp float max_r = max(max(samples.x, samples.y), max(samples.z, samples.w));
2172 
2173     frag_color = vec4(max_r, 0.0, 0.0, 1.0);
2174 }
2175 )";
2176     ANGLE_GL_PROGRAM(program, kVS, kFS);
2177     ASSERT_NE(0u, program);
2178 
2179     GLTexture tex;
2180     glBindTexture(GL_TEXTURE_2D, tex);
2181     glTexStorage2D(GL_TEXTURE_2D, 3, GL_RGBA8, 4, 4);
2182     EXPECT_GL_NO_ERROR();
2183 
2184     // clang-format off
2185     constexpr GLubyte kRedColor[16] = {
2186         0x0c, 0x08, 0x4c, 0x48,
2187         0x00, 0x04, 0x40, 0x44,
2188         0xcc, 0xc8, 0x8c, 0x88,
2189         0xc0, 0xc4, 0x80, 0x84,
2190     };
2191 
2192     constexpr GLubyte kExpectedMip1Color[4] = {
2193         0x0c, 0x4c,
2194         0xcc, 0x8c,
2195     };
2196 
2197     constexpr GLubyte kExpectedMip2Color[1] = {
2198         0xcc
2199     };
2200     // clang-format on
2201 
2202     GLubyte mip0Color[16 * 4];
2203     for (size_t i = 0; i < 16; i++)
2204     {
2205         mip0Color[i * 4 + 0] = kRedColor[i];
2206         mip0Color[i * 4 + 1] = 0;
2207         mip0Color[i * 4 + 2] = 0;
2208         mip0Color[i * 4 + 3] = 0xff;
2209     }
2210 
2211     GLFramebuffer fb0, fb1, fb2;
2212 
2213     glBindFramebuffer(GL_FRAMEBUFFER, fb0);
2214     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
2215     EXPECT_GL_NO_ERROR();
2216     glBindFramebuffer(GL_FRAMEBUFFER, fb1);
2217     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 1);
2218     EXPECT_GL_NO_ERROR();
2219     glBindFramebuffer(GL_FRAMEBUFFER, fb2);
2220     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 2);
2221     EXPECT_GL_NO_ERROR();
2222 
2223     // initialize base mip
2224     glBindFramebuffer(GL_FRAMEBUFFER, fb0);
2225     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 4, 4, GL_RGBA, GL_UNSIGNED_BYTE, &mip0Color[0]);
2226     EXPECT_GL_NO_ERROR();
2227 
2228     // draw mip 1 with mip 0
2229     glActiveTexture(GL_TEXTURE0);
2230     glBindTexture(GL_TEXTURE_2D, tex);
2231     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2232     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2233     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
2234     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
2235 
2236     glViewport(0, 0, 2, 2);
2237     glBindFramebuffer(GL_FRAMEBUFFER, fb1);
2238     EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
2239 
2240     glUseProgram(program);
2241     glUniform1i(glGetUniformLocation(program, "tex"), 0);
2242     drawQuad(program, "position", 0.5);
2243     EXPECT_GL_NO_ERROR();
2244 
2245     // draw mip 2 with mip 1
2246     glActiveTexture(GL_TEXTURE0);
2247     glBindTexture(GL_TEXTURE_2D, tex);
2248     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
2249     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
2250     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
2251     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
2252 
2253     glViewport(0, 0, 1, 1);
2254     glBindFramebuffer(GL_FRAMEBUFFER, fb2);
2255     EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
2256 
2257     glUseProgram(program);
2258     glUniform1i(glGetUniformLocation(program, "tex"), 0);
2259     drawQuad(program, "position", 0.5);
2260     EXPECT_GL_NO_ERROR();
2261 
2262     // Read back rendered pixel values and compare
2263     GLubyte resultColors[16];
2264     glBindFramebuffer(GL_FRAMEBUFFER, fb1);
2265     glReadPixels(0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, &resultColors[0]);
2266     for (size_t i = 0; i < 4; i++)
2267     {
2268         EXPECT_EQ(resultColors[i * 4], kExpectedMip1Color[i]);
2269     }
2270 
2271     glBindFramebuffer(GL_FRAMEBUFFER, fb2);
2272     glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &resultColors[0]);
2273     for (size_t i = 0; i < 1; i++)
2274     {
2275         EXPECT_EQ(resultColors[i * 4], kExpectedMip2Color[i]);
2276     }
2277 }
2278 
2279 // Test that manually generating lower mipmaps using draw calls is functional
TEST_P(MipmapTestES31,GenerateLowerMipsWithDraw)2280 TEST_P(MipmapTestES31, GenerateLowerMipsWithDraw)
2281 {
2282     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Texture2D(), essl1_shaders::fs::Texture2D());
2283     glUseProgram(program);
2284     glUniform1i(glGetUniformLocation(program, essl1_shaders::Texture2DUniform()), 0);
2285 
2286     GLTexture tex;
2287     glBindTexture(GL_TEXTURE_2D, tex);
2288     glTexStorage2D(GL_TEXTURE_2D, 4, GL_RGBA8, 8, 8);
2289     EXPECT_GL_NO_ERROR();
2290 
2291     glActiveTexture(GL_TEXTURE0);
2292     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
2293     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
2294     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
2295     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
2296 
2297     const std::array<GLColor, 4> kMip2Color = {
2298         GLColor::red,
2299         GLColor::green,
2300         GLColor::blue,
2301         GLColor::white,
2302     };
2303 
2304     GLFramebuffer fb0, fb1, fb2;
2305 
2306     glBindFramebuffer(GL_FRAMEBUFFER, fb0);
2307     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
2308     EXPECT_GL_NO_ERROR();
2309     glBindFramebuffer(GL_FRAMEBUFFER, fb1);
2310     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 1);
2311     EXPECT_GL_NO_ERROR();
2312     glBindFramebuffer(GL_FRAMEBUFFER, fb2);
2313     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 2);
2314     EXPECT_GL_NO_ERROR();
2315 
2316     // initialize mip 2
2317     glBindFramebuffer(GL_FRAMEBUFFER, fb2);
2318     glTexSubImage2D(GL_TEXTURE_2D, 2, 0, 0, 2, 2, GL_RGBA, GL_UNSIGNED_BYTE, kMip2Color.data());
2319     EXPECT_GL_NO_ERROR();
2320 
2321     // draw mip 1 with mip 2
2322     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 2);
2323     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
2324 
2325     glViewport(0, 0, 4, 4);
2326     glBindFramebuffer(GL_FRAMEBUFFER, fb1);
2327     EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
2328 
2329     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
2330     EXPECT_GL_NO_ERROR();
2331 
2332     // draw mip 0 with mip 1
2333     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
2334     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
2335 
2336     glViewport(0, 0, 8, 8);
2337     glBindFramebuffer(GL_FRAMEBUFFER, fb0);
2338     EXPECT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
2339 
2340     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5);
2341     EXPECT_GL_NO_ERROR();
2342 
2343     // Read back rendered pixel values and compare
2344     auto getCoeff = [](uint32_t x, uint32_t dimension) {
2345         const uint32_t dimDiv2 = dimension / 2;
2346         const uint32_t x2      = x > dimDiv2 ? (dimension - 1 - x) : x;
2347 
2348         const uint32_t denominator = dimension * dimension / 4;
2349         uint32_t numerator         = x2 * (x2 + 1) / 2;
2350         if (x > dimDiv2)
2351         {
2352             numerator = denominator - numerator;
2353         }
2354         return static_cast<float>(numerator) / static_cast<float>(denominator);
2355     };
2356     auto upscale = [&](uint32_t index, uint32_t dimension) {
2357         uint32_t x = index % dimension;
2358         uint32_t y = index / dimension;
2359 
2360         const float xCoeff = getCoeff(x, dimension);
2361         const float yCoeff = getCoeff(y, dimension);
2362 
2363         GLColor result;
2364         for (uint32_t channel = 0; channel < 4; ++channel)
2365         {
2366             const float mixX0 =
2367                 kMip2Color[0][channel] * (1 - xCoeff) + kMip2Color[1][channel] * xCoeff;
2368             const float mixX1 =
2369                 kMip2Color[2][channel] * (1 - xCoeff) + kMip2Color[3][channel] * xCoeff;
2370             const float mix = mixX0 * (1 - yCoeff) + mixX1 * yCoeff;
2371 
2372             result[channel] = static_cast<GLubyte>(round(mix));
2373         }
2374         return result;
2375     };
2376 
2377     glBindFramebuffer(GL_FRAMEBUFFER, fb0);
2378     for (uint32_t i = 0; i < 64; ++i)
2379     {
2380         const GLColor expect = upscale(i, 8);
2381         EXPECT_PIXEL_COLOR_NEAR(i % 8, i / 8, expect, 1);
2382     }
2383 
2384     glBindFramebuffer(GL_FRAMEBUFFER, fb1);
2385     for (uint32_t i = 0; i < 16; ++i)
2386     {
2387         const GLColor expect = upscale(i, 4);
2388         EXPECT_PIXEL_COLOR_NEAR(i % 4, i / 4, expect, 1);
2389     }
2390 }
2391 
2392 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
2393 // tests should be run against.
2394 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(MipmapTest);
2395 
2396 namespace extraPlatforms
2397 {
2398 ANGLE_INSTANTIATE_TEST(MipmapTest,
2399                        ES2_METAL().disable(Feature::AllowGenMultipleMipsPerPass),
2400                        ES2_OPENGLES().enable(Feature::UseIntermediateTextureForGenerateMipmap));
2401 
2402 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(Mipmap3DBoxFilterTest);
2403 ANGLE_INSTANTIATE_TEST(Mipmap3DBoxFilterTest,
2404                        ES2_METAL(),
2405                        ES2_METAL().disable(Feature::AllowGenMultipleMipsPerPass));
2406 }  // namespace extraPlatforms
2407 
2408 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MipmapTestES3);
2409 ANGLE_INSTANTIATE_TEST_ES3(MipmapTestES3);
2410 
2411 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MipmapTestES31);
2412 ANGLE_INSTANTIATE_TEST_ES31(MipmapTestES31);
2413