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 using namespace angle;
10 
11 namespace
12 {
13 
TexImageCubeMapFaces(GLint level,GLenum internalformat,GLsizei width,GLenum format,GLenum type,void * pixels)14 void TexImageCubeMapFaces(GLint level,
15                           GLenum internalformat,
16                           GLsizei width,
17                           GLenum format,
18                           GLenum type,
19                           void *pixels)
20 {
21     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, level, internalformat, width, width, 0, format,
22                  type, pixels);
23     glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_X, level, internalformat, width, width, 0, format,
24                  type, pixels);
25     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Y, level, internalformat, width, width, 0, format,
26                  type, pixels);
27     glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, level, internalformat, width, width, 0, format,
28                  type, pixels);
29     glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_Z, level, internalformat, width, width, 0, format,
30                  type, pixels);
31     glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, level, internalformat, width, width, 0, format,
32                  type, pixels);
33 }
34 
35 class BaseMipmapTest : public ANGLETest
36 {
37   protected:
clearAndDrawQuad(GLuint program,GLsizei viewportWidth,GLsizei viewportHeight)38     void clearAndDrawQuad(GLuint program, GLsizei viewportWidth, GLsizei viewportHeight)
39     {
40         glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
41         glClear(GL_COLOR_BUFFER_BIT);
42         glViewport(0, 0, viewportWidth, viewportHeight);
43         ASSERT_GL_NO_ERROR();
44 
45         drawQuad(program, "position", 0.0f);
46     }
47 };
48 
49 }  // namespace
50 
51 class MipmapTest : public BaseMipmapTest
52 {
53   protected:
MipmapTest()54     MipmapTest()
55         : m2DProgram(0),
56           mCubeProgram(0),
57           mTexture2D(0),
58           mTextureCube(0),
59           mLevelZeroBlueInitData(nullptr),
60           mLevelZeroWhiteInitData(nullptr),
61           mLevelOneInitData(nullptr),
62           mLevelTwoInitData(nullptr),
63           mOffscreenFramebuffer(0)
64     {
65         setWindowWidth(128);
66         setWindowHeight(128);
67         setConfigRedBits(8);
68         setConfigGreenBits(8);
69         setConfigBlueBits(8);
70         setConfigAlphaBits(8);
71     }
72 
setUp2DProgram()73     void setUp2DProgram()
74     {
75         // Vertex Shader source
76         // clang-format off
77         const std::string vs = SHADER_SOURCE
78         (
79             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 
89         // Fragment Shader source
90         const std::string fs = SHADER_SOURCE
91         (
92             precision mediump float;
93 
94             uniform sampler2D uTexture;
95             varying vec2 vTexCoord;
96 
97             void main()
98             {
99                 gl_FragColor = texture2D(uTexture, vTexCoord);
100             }
101         );
102         // clang-format on
103 
104         m2DProgram = CompileProgram(vs, fs);
105         ASSERT_NE(0u, m2DProgram);
106     }
107 
setUpCubeProgram()108     void setUpCubeProgram()
109     {
110         // A simple vertex shader for the texture cube
111         // clang-format off
112         const std::string cubeVS = SHADER_SOURCE
113         (
114             attribute vec4 position;
115             varying vec4 vPosition;
116             void main()
117             {
118                 gl_Position = position;
119                 vPosition = position;
120             }
121         );
122 
123         // A very simple fragment shader to sample from the negative-Y face of a texture cube.
124         const std::string cubeFS = SHADER_SOURCE
125         (
126             precision mediump float;
127             uniform samplerCube uTexture;
128             varying vec4 vPosition;
129 
130             void main()
131             {
132                 gl_FragColor = textureCube(uTexture, vec3(vPosition.x, -1, vPosition.y));
133             }
134         );
135         // clang-format on
136 
137         mCubeProgram = CompileProgram(cubeVS, cubeFS);
138         ASSERT_NE(0u, mCubeProgram);
139     }
140 
SetUp()141     void SetUp() override
142     {
143         ANGLETest::SetUp();
144 
145         setUp2DProgram();
146 
147         setUpCubeProgram();
148 
149         mLevelZeroBlueInitData = createRGBInitData(getWindowWidth(), getWindowHeight(), 0, 0, 255); // Blue
150         mLevelZeroWhiteInitData = createRGBInitData(getWindowWidth(), getWindowHeight(), 255, 255, 255); // White
151         mLevelOneInitData = createRGBInitData((getWindowWidth() / 2), (getWindowHeight() / 2), 0, 255, 0);   // Green
152         mLevelTwoInitData = createRGBInitData((getWindowWidth() / 4), (getWindowHeight() / 4), 255, 0, 0);   // Red
153 
154         glGenFramebuffers(1, &mOffscreenFramebuffer);
155         glGenTextures(1, &mTexture2D);
156 
157         // Initialize the texture2D to be empty, and don't use mips.
158         glBindTexture(GL_TEXTURE_2D, mTexture2D);
159         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
160         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
161         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
162 
163         ASSERT_EQ(getWindowWidth(), getWindowHeight());
164 
165         // Create a non-mipped texture cube. Set the negative-Y face to be blue.
166         glGenTextures(1, &mTextureCube);
167         glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
168         TexImageCubeMapFaces(0, GL_RGB, getWindowWidth(), GL_RGB, GL_UNSIGNED_BYTE, nullptr);
169         glTexImage2D(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, GL_RGB, getWindowWidth(), getWindowWidth(),
170                      0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData);
171 
172         // Complete the texture cube without mipmaps to start with.
173         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
174         glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
175 
176         ASSERT_GL_NO_ERROR();
177     }
178 
TearDown()179     void TearDown() override
180     {
181         glDeleteProgram(m2DProgram);
182         glDeleteProgram(mCubeProgram);
183         glDeleteFramebuffers(1, &mOffscreenFramebuffer);
184         glDeleteTextures(1, &mTexture2D);
185         glDeleteTextures(1, &mTextureCube);
186 
187         SafeDeleteArray(mLevelZeroBlueInitData);
188         SafeDeleteArray(mLevelZeroWhiteInitData);
189         SafeDeleteArray(mLevelOneInitData);
190         SafeDeleteArray(mLevelTwoInitData);
191 
192         ANGLETest::TearDown();
193     }
194 
createRGBInitData(GLint width,GLint height,GLint r,GLint g,GLint b)195     GLubyte *createRGBInitData(GLint width, GLint height, GLint r, GLint g, GLint b)
196     {
197         GLubyte *data = new GLubyte[3 * width * height];
198 
199         for (int i = 0; i < width * height; i+=1)
200         {
201             data[3 * i + 0] = static_cast<GLubyte>(r);
202             data[3 * i + 1] = static_cast<GLubyte>(g);
203             data[3 * i + 2] = static_cast<GLubyte>(b);
204         }
205 
206         return data;
207     }
208 
clearTextureLevel0(GLenum textarget,GLuint texture,GLfloat red,GLfloat green,GLfloat blue,GLfloat alpha)209     void clearTextureLevel0(GLenum textarget,
210                             GLuint texture,
211                             GLfloat red,
212                             GLfloat green,
213                             GLfloat blue,
214                             GLfloat alpha)
215     {
216         glBindFramebuffer(GL_FRAMEBUFFER, mOffscreenFramebuffer);
217         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textarget, texture, 0);
218         ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
219         glClearColor(red, green, blue, alpha);
220         glClear(GL_COLOR_BUFFER_BIT);
221         glBindFramebuffer(GL_FRAMEBUFFER, 0);
222     }
223 
224     GLuint m2DProgram;
225     GLuint mCubeProgram;
226     GLuint mTexture2D;
227     GLuint mTextureCube;
228 
229     GLubyte* mLevelZeroBlueInitData;
230     GLubyte* mLevelZeroWhiteInitData;
231     GLubyte* mLevelOneInitData;
232     GLubyte* mLevelTwoInitData;
233 
234   private:
235     GLuint mOffscreenFramebuffer;
236 };
237 
238 class MipmapTestES3 : public BaseMipmapTest
239 {
240   protected:
MipmapTestES3()241     MipmapTestES3()
242         : mTexture(0),
243           mArrayProgram(0),
244           mTextureArraySliceUniformLocation(-1),
245           m3DProgram(0),
246           mTexture3DSliceUniformLocation(-1),
247           mTexture3DLODUniformLocation(-1),
248           m2DProgram(0)
249 
250     {
251         setWindowWidth(128);
252         setWindowHeight(128);
253         setConfigRedBits(8);
254         setConfigGreenBits(8);
255         setConfigBlueBits(8);
256         setConfigAlphaBits(8);
257     }
258 
vertexShaderSource()259     std::string vertexShaderSource()
260     {
261         // Don't put "#version ..." on its own line. See [cpp]p1:
262         // "If there are sequences of preprocessing tokens within the list of arguments that
263         //  would otherwise act as preprocessing directives, the behavior is undefined"
264         // clang-format off
265         return SHADER_SOURCE
266         (   #version 300 es\n
267             precision highp float;
268             in vec4 position;
269             out vec2 texcoord;
270 
271             void main()
272             {
273                 gl_Position = vec4(position.xy, 0.0, 1.0);
274                 texcoord = (position.xy * 0.5) + 0.5;
275             }
276         );
277         // clang-format on
278     }
279 
setUpArrayProgram()280     void setUpArrayProgram()
281     {
282         const std::string fragmentShaderSourceArray = SHADER_SOURCE
283         (   #version 300 es\n
284             precision highp float;
285             uniform highp sampler2DArray tex;
286             uniform int slice;
287             in vec2 texcoord;
288             out vec4 out_FragColor;
289 
290             void main()
291             {
292                 out_FragColor = texture(tex, vec3(texcoord, float(slice)));
293             }
294         );
295 
296         mArrayProgram = CompileProgram(vertexShaderSource(), fragmentShaderSourceArray);
297         if (mArrayProgram == 0)
298         {
299             FAIL() << "shader compilation failed.";
300         }
301 
302         mTextureArraySliceUniformLocation = glGetUniformLocation(mArrayProgram, "slice");
303         ASSERT_NE(-1, mTextureArraySliceUniformLocation);
304 
305         glUseProgram(mArrayProgram);
306         glUseProgram(0);
307         ASSERT_GL_NO_ERROR();
308     }
309 
setUp3DProgram()310     void setUp3DProgram()
311     {
312         const std::string fragmentShaderSource3D = SHADER_SOURCE
313         (   #version 300 es\n
314             precision highp float;
315             uniform highp sampler3D tex;
316             uniform float slice;
317             uniform float lod;
318             in vec2 texcoord;
319             out vec4 out_FragColor;
320 
321             void main()
322             {
323                 out_FragColor = textureLod(tex, vec3(texcoord, slice), lod);
324             }
325         );
326 
327         m3DProgram = CompileProgram(vertexShaderSource(), fragmentShaderSource3D);
328         if (m3DProgram == 0)
329         {
330             FAIL() << "shader compilation failed.";
331         }
332 
333         mTexture3DSliceUniformLocation = glGetUniformLocation(m3DProgram, "slice");
334         ASSERT_NE(-1, mTexture3DSliceUniformLocation);
335 
336         mTexture3DLODUniformLocation = glGetUniformLocation(m3DProgram, "lod");
337         ASSERT_NE(-1, mTexture3DLODUniformLocation);
338 
339         glUseProgram(m3DProgram);
340         glUniform1f(mTexture3DLODUniformLocation, 0);
341         glUseProgram(0);
342         ASSERT_GL_NO_ERROR();
343     }
344 
setUp2DProgram()345     void setUp2DProgram()
346     {
347         // clang-format off
348         const std::string fragmentShaderSource2D = SHADER_SOURCE
349         (   #version 300 es\n
350             precision highp float;
351             uniform highp sampler2D tex;
352             in vec2 texcoord;
353             out vec4 out_FragColor;
354 
355             void main()
356             {
357                 out_FragColor = texture(tex, texcoord);
358             }
359         );
360         // clang-format on
361 
362         m2DProgram = CompileProgram(vertexShaderSource(), fragmentShaderSource2D);
363         ASSERT_NE(0u, m2DProgram);
364 
365         ASSERT_GL_NO_ERROR();
366     }
367 
setUpCubeProgram()368     void setUpCubeProgram()
369     {
370         // A very simple fragment shader to sample from the negative-Y face of a texture cube.
371         // clang-format off
372         const std::string cubeFS = SHADER_SOURCE
373         (   #version 300 es\n
374             precision mediump float;
375             uniform samplerCube uTexture;
376             in vec2 texcoord;
377             out vec4 out_FragColor;
378 
379             void main()
380             {
381                 out_FragColor = texture(uTexture, vec3(texcoord.x, -1, texcoord.y));
382             }
383         );
384         // clang-format on
385 
386         mCubeProgram = CompileProgram(vertexShaderSource(), cubeFS);
387         ASSERT_NE(0u, mCubeProgram);
388 
389         ASSERT_GL_NO_ERROR();
390     }
391 
SetUp()392     void SetUp() override
393     {
394         ANGLETest::SetUp();
395 
396         glGenTextures(1, &mTexture);
397         ASSERT_GL_NO_ERROR();
398 
399         setUpArrayProgram();
400         setUp3DProgram();
401         setUp2DProgram();
402         setUpCubeProgram();
403     }
404 
TearDown()405     void TearDown() override
406     {
407         glDeleteTextures(1, &mTexture);
408 
409         glDeleteProgram(mArrayProgram);
410         glDeleteProgram(m3DProgram);
411         glDeleteProgram(m2DProgram);
412         glDeleteProgram(mCubeProgram);
413 
414         ANGLETest::TearDown();
415     }
416 
417     GLuint mTexture;
418 
419     GLuint mArrayProgram;
420     GLint mTextureArraySliceUniformLocation;
421 
422     GLuint m3DProgram;
423     GLint mTexture3DSliceUniformLocation;
424     GLint mTexture3DLODUniformLocation;
425 
426     GLuint m2DProgram;
427 
428     GLuint mCubeProgram;
429 };
430 
431 // This test uses init data for the first three levels of the texture. It passes the level 0 data in, then renders, then level 1, then renders, etc.
432 // This ensures that renderers using the zero LOD workaround (e.g. D3D11 FL9_3) correctly pass init data to the mipmapped texture,
433 // even if the the zero-LOD texture is currently in use.
TEST_P(MipmapTest,DISABLED_ThreeLevelsInitData)434 TEST_P(MipmapTest, DISABLED_ThreeLevelsInitData)
435 {
436     // Pass in level zero init data.
437     glBindTexture(GL_TEXTURE_2D, mTexture2D);
438     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData);
439     ASSERT_GL_NO_ERROR();
440 
441     // Disable mips.
442     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
443 
444     // Draw a full-sized quad, and check it's blue.
445     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
446     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
447 
448     // Draw a half-sized quad, and check it's blue.
449     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
450     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
451 
452     // Draw a quarter-sized quad, and check it's blue.
453     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
454     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
455 
456     // Complete the texture by initializing the remaining levels.
457     int n = 1;
458     while (getWindowWidth() / (1U << n) >= 1)
459     {
460         glTexImage2D(GL_TEXTURE_2D, n, GL_RGB, getWindowWidth() / (1U << n), getWindowWidth() / (1U << n), 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
461         ASSERT_GL_NO_ERROR();
462         n+=1;
463     }
464 
465     // Pass in level one init data.
466     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGB, getWindowWidth() / 2, getWindowHeight() / 2, 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelOneInitData);
467     ASSERT_GL_NO_ERROR();
468 
469     // Draw a full-sized quad, and check it's blue.
470     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
471     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
472 
473     // Draw a half-sized quad, and check it's blue. We've not enabled mipmaps yet, so our init data for level one shouldn't be used.
474     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
475     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
476 
477     // Enable mipmaps.
478     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
479 
480     // Draw a half-sized quad, and check it's green.
481     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
482     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green);
483 
484     // Draw a quarter-sized quad, and check it's black, since we've not passed any init data for level two.
485     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
486     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::black);
487 
488     // Pass in level two init data.
489     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGB, getWindowWidth() / 4, getWindowHeight() / 4, 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelTwoInitData);
490     ASSERT_GL_NO_ERROR();
491 
492     // Draw a full-sized quad, and check it's blue.
493     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
494     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
495 
496     // Draw a half-sized quad, and check it's green.
497     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
498     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green);
499 
500     // Draw a quarter-sized quad, and check it's red.
501     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
502     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
503 
504     // Now disable mipmaps again, and render multiple sized quads. They should all be blue, since level 0 is blue.
505     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
506     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
507     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
508     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
509     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
510     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
511     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
512 
513     // Now reset level 0 to white, keeping mipmaps disabled. Then, render various sized quads. They should be white.
514     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroWhiteInitData);
515     ASSERT_GL_NO_ERROR();
516 
517     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
518     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::white);
519     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
520     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::white);
521     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
522     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::white);
523 
524     // Then enable mipmaps again. The quads should be white, green, red respectively.
525     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
526 
527     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
528     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::white);
529     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
530     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::green);
531     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
532     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
533 }
534 
535 // This test generates (and uses) mipmaps on a texture using init data. D3D11 will use a non-renderable TextureStorage for this.
536 // The test then disables mips, renders to level zero of the texture, and reenables mips before using the texture again.
537 // To do this, D3D11 has to convert the TextureStorage into a renderable one.
538 // This test ensures that the conversion works correctly.
539 // In particular, on D3D11 Feature Level 9_3 it ensures that both the zero LOD workaround texture AND the 'normal' texture are copied during conversion.
TEST_P(MipmapTest,GenerateMipmapFromInitDataThenRender)540 TEST_P(MipmapTest, GenerateMipmapFromInitDataThenRender)
541 {
542     // Pass in initial data so the texture is blue.
543     glBindTexture(GL_TEXTURE_2D, mTexture2D);
544     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, getWindowWidth(), getWindowHeight(), 0, GL_RGB, GL_UNSIGNED_BYTE, mLevelZeroBlueInitData);
545 
546     // Then generate the mips.
547     glGenerateMipmap(GL_TEXTURE_2D);
548     ASSERT_GL_NO_ERROR();
549 
550     // Enable mipmaps.
551     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
552 
553     // Now draw the texture to various different sized areas.
554     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
555     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
556 
557     // Use mip level 1
558     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
559     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
560 
561     // Use mip level 2
562     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
563     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
564 
565     ASSERT_GL_NO_ERROR();
566 
567     // Disable mips. Render a quad using the texture and ensure it's blue.
568     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
569     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
570     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
571 
572     // Clear level 0 of the texture to red.
573     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 1.0f, 0.0f, 0.0f, 1.0f);
574 
575     // Reenable mips, and try rendering different-sized quads.
576     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
577 
578     // Level 0 is now red, so this should render red.
579     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
580     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::red);
581 
582     // Use mip level 1, blue.
583     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
584     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
585 
586     // Use mip level 2, blue.
587     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
588     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
589 }
590 
591 // This test ensures that mips are correctly generated from a rendered image.
592 // In particular, on D3D11 Feature Level 9_3, the clear call will be performed on the zero-level texture, rather than the mipped one.
593 // The test ensures that the zero-level texture is correctly copied into the mipped texture before the mipmaps are generated.
TEST_P(MipmapTest,GenerateMipmapFromRenderedImage)594 TEST_P(MipmapTest, GenerateMipmapFromRenderedImage)
595 {
596     glBindTexture(GL_TEXTURE_2D, mTexture2D);
597     // Clear the texture to blue.
598     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f);
599 
600     // Then generate the mips
601     glGenerateMipmap(GL_TEXTURE_2D);
602     ASSERT_GL_NO_ERROR();
603 
604     // Enable mips.
605     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
606 
607     // Now draw the texture to various different sized areas.
608     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
609     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
610 
611     // Use mip level 1
612     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
613     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
614 
615     // Use mip level 2
616     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
617     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
618 }
619 
620 // Test to ensure that rendering to a mipmapped texture works, regardless of whether mipmaps are enabled or not.
621 // TODO: This test hits a texture rebind bug in the D3D11 renderer. Fix this.
TEST_P(MipmapTest,RenderOntoLevelZeroAfterGenerateMipmap)622 TEST_P(MipmapTest, RenderOntoLevelZeroAfterGenerateMipmap)
623 {
624     // TODO(geofflang): Figure out why this is broken on AMD OpenGL
625     if ((IsAMD() || IsIntel()) && getPlatformRenderer() == EGL_PLATFORM_ANGLE_TYPE_OPENGL_ANGLE)
626     {
627         std::cout << "Test skipped on Intel/AMD OpenGL." << std::endl;
628         return;
629     }
630 
631     glBindTexture(GL_TEXTURE_2D, mTexture2D);
632 
633     // Clear the texture to blue.
634     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 0.0f, 1.0f, 1.0f);
635 
636     // Now, draw the texture to a quad that's the same size as the texture. This draws to the default framebuffer.
637     // The quad should be blue.
638     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
639     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
640 
641     // Now go back to the texture, and generate mips on it.
642     glGenerateMipmap(GL_TEXTURE_2D);
643     ASSERT_GL_NO_ERROR();
644 
645     // Now try rendering the textured quad again. Note: we've not told GL to use the generated mips.
646     // The quad should be blue.
647     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
648     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
649 
650     // Now tell GL to use the generated mips.
651     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
652     EXPECT_GL_NO_ERROR();
653 
654     // Now render the textured quad again. It should be still be blue.
655     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
656     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
657 
658     // Now render the textured quad to an area smaller than the texture (i.e. to force minification). This should be blue.
659     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
660     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
661 
662     // Now clear the texture to green. This just clears the top level. The lower mips should remain blue.
663     clearTextureLevel0(GL_TEXTURE_2D, mTexture2D, 0.0f, 1.0f, 0.0f, 1.0f);
664 
665     // Render a textured quad equal in size to the texture. This should be green, since we just cleared level 0.
666     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
667     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green);
668 
669     // Render a small textured quad. This forces minification, so should render blue (the color of levels 1+).
670     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
671     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::blue);
672 
673     // Disable mipmaps again
674     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
675     ASSERT_GL_NO_ERROR();
676 
677     // Render a textured quad equal in size to the texture. This should be green, the color of level 0 in the texture.
678     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
679     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green);
680 
681     // Render a small textured quad. This would force minification if mips were enabled, but they're not. Therefore, this should be green.
682     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
683     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green);
684 }
685 
686 // This test ensures that the level-zero workaround for TextureCubes (on D3D11 Feature Level 9_3)
687 // works as expected. It tests enabling/disabling mipmaps, generating mipmaps, and rendering to level zero.
TEST_P(MipmapTest,TextureCubeGeneralLevelZero)688 TEST_P(MipmapTest, TextureCubeGeneralLevelZero)
689 {
690     glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
691 
692     // Draw. Since the negative-Y face's is blue, this should be blue.
693     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
694     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
695 
696     // Generate mipmaps, and render. This should be blue.
697     glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
698     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
699     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
700     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
701 
702     // Draw using a smaller viewport (to force a lower LOD of the texture). This should still be blue.
703     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
704     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
705 
706     // Now clear the negative-Y face of the cube to red.
707     clearTextureLevel0(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mTextureCube, 1.0f, 0.0f, 0.0f, 1.0f);
708 
709     // Draw using a full-size viewport. This should be red.
710     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
711     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
712 
713     // Draw using a quarter-size viewport, to force a lower LOD. This should be *BLUE*, since we only cleared level zero
714     // of the negative-Y face to red, and left its mipmaps blue.
715     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
716     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
717 
718     // Disable mipmaps again, and draw a to a quarter-size viewport.
719     // Since this should use level zero of the texture, this should be *RED*.
720     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
721     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
722     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
723 }
724 
725 // This test ensures that rendering to level-zero of a TextureCube works as expected.
TEST_P(MipmapTest,TextureCubeRenderToLevelZero)726 TEST_P(MipmapTest, TextureCubeRenderToLevelZero)
727 {
728     glBindTexture(GL_TEXTURE_CUBE_MAP, mTextureCube);
729 
730     // Draw. Since the negative-Y face's is blue, this should be blue.
731     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
732     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::blue);
733 
734     // Now clear the negative-Y face of the cube to red.
735     clearTextureLevel0(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, mTextureCube, 1.0f, 0.0f, 0.0f, 1.0f);
736 
737     // Draw using a full-size viewport. This should be red.
738     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
739     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
740 
741     // Draw a to a quarter-size viewport. This should also be red.
742     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
743     EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
744 }
745 
746 // Creates a mipmapped 2D array texture with three layers, and calls ANGLE's GenerateMipmap.
747 // Then tests if the mipmaps are rendered correctly for all three layers.
TEST_P(MipmapTestES3,MipmapsForTextureArray)748 TEST_P(MipmapTestES3, MipmapsForTextureArray)
749 {
750     int px = getWindowWidth() / 2;
751     int py = getWindowHeight() / 2;
752 
753     glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture);
754 
755     glTexStorage3D(GL_TEXTURE_2D_ARRAY, 5, GL_RGBA8, 16, 16, 3);
756 
757     // Fill the first layer with red
758     std::vector<GLColor> pixelsRed(16 * 16, GLColor::red);
759     glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
760                     pixelsRed.data());
761 
762     // Fill the second layer with green
763     std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green);
764     glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
765                     pixelsGreen.data());
766 
767     // Fill the third layer with blue
768     std::vector<GLColor> pixelsBlue(16 * 16, GLColor::blue);
769     glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 2, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
770                     pixelsBlue.data());
771 
772     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
773     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
774 
775     EXPECT_GL_NO_ERROR();
776 
777     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
778 
779     EXPECT_GL_NO_ERROR();
780 
781     glUseProgram(mArrayProgram);
782 
783     EXPECT_GL_NO_ERROR();
784 
785     // Draw the first slice
786     glUniform1i(mTextureArraySliceUniformLocation, 0);
787     drawQuad(mArrayProgram, "position", 0.5f);
788     EXPECT_GL_NO_ERROR();
789     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
790 
791     // Draw the second slice
792     glUniform1i(mTextureArraySliceUniformLocation, 1);
793     drawQuad(mArrayProgram, "position", 0.5f);
794     EXPECT_GL_NO_ERROR();
795     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
796 
797     // Draw the third slice
798     glUniform1i(mTextureArraySliceUniformLocation, 2);
799     drawQuad(mArrayProgram, "position", 0.5f);
800     EXPECT_GL_NO_ERROR();
801     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::blue);
802 }
803 
804 // Create a mipmapped 2D array texture with more layers than width / height, and call
805 // GenerateMipmap.
TEST_P(MipmapTestES3,MipmapForDeepTextureArray)806 TEST_P(MipmapTestES3, MipmapForDeepTextureArray)
807 {
808     int px = getWindowWidth() / 2;
809     int py = getWindowHeight() / 2;
810 
811     glBindTexture(GL_TEXTURE_2D_ARRAY, mTexture);
812 
813     // Fill the whole texture with red.
814     std::vector<GLColor> pixelsRed(2 * 2 * 4, GLColor::red);
815     glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, 2, 2, 4, 0, GL_RGBA, GL_UNSIGNED_BYTE,
816                  pixelsRed.data());
817 
818     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
819     glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
820 
821     EXPECT_GL_NO_ERROR();
822 
823     glGenerateMipmap(GL_TEXTURE_2D_ARRAY);
824 
825     EXPECT_GL_NO_ERROR();
826 
827     glUseProgram(mArrayProgram);
828 
829     EXPECT_GL_NO_ERROR();
830 
831     // Draw the first slice
832     glUniform1i(mTextureArraySliceUniformLocation, 0);
833     drawQuad(mArrayProgram, "position", 0.5f);
834     EXPECT_GL_NO_ERROR();
835     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
836 
837     // Draw the fourth slice
838     glUniform1i(mTextureArraySliceUniformLocation, 3);
839     drawQuad(mArrayProgram, "position", 0.5f);
840     EXPECT_GL_NO_ERROR();
841     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
842 }
843 
844 // Creates a mipmapped 3D texture with two layers, and calls ANGLE's GenerateMipmap.
845 // Then tests if the mipmaps are rendered correctly for all two layers.
TEST_P(MipmapTestES3,MipmapsForTexture3D)846 TEST_P(MipmapTestES3, MipmapsForTexture3D)
847 {
848     int px = getWindowWidth() / 2;
849     int py = getWindowHeight() / 2;
850 
851     glBindTexture(GL_TEXTURE_3D, mTexture);
852 
853     glTexStorage3D(GL_TEXTURE_3D, 5, GL_RGBA8, 16, 16, 2);
854 
855     // Fill the first layer with red
856     std::vector<GLColor> pixelsRed(16 * 16, GLColor::red);
857     glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 0, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
858                     pixelsRed.data());
859 
860     // Fill the second layer with green
861     std::vector<GLColor> pixelsGreen(16 * 16, GLColor::green);
862     glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 1, 16, 16, 1, GL_RGBA, GL_UNSIGNED_BYTE,
863                     pixelsGreen.data());
864 
865     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
866     glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
867 
868     EXPECT_GL_NO_ERROR();
869 
870     glGenerateMipmap(GL_TEXTURE_3D);
871 
872     EXPECT_GL_NO_ERROR();
873 
874     glUseProgram(m3DProgram);
875 
876     EXPECT_GL_NO_ERROR();
877 
878     // Mipmap level 0
879     // Draw the first slice
880     glUniform1f(mTexture3DLODUniformLocation, 0.);
881     glUniform1f(mTexture3DSliceUniformLocation, 0.25f);
882     drawQuad(m3DProgram, "position", 0.5f);
883     EXPECT_GL_NO_ERROR();
884     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::red);
885 
886     // Draw the second slice
887     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
888     drawQuad(m3DProgram, "position", 0.5f);
889     EXPECT_GL_NO_ERROR();
890     EXPECT_PIXEL_COLOR_EQ(px, py, GLColor::green);
891 
892     // Mipmap level 1
893     // The second mipmap should only have one slice.
894     glUniform1f(mTexture3DLODUniformLocation, 1.);
895     drawQuad(m3DProgram, "position", 0.5f);
896     EXPECT_GL_NO_ERROR();
897     EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0);
898 
899     glUniform1f(mTexture3DSliceUniformLocation, 0.75f);
900     drawQuad(m3DProgram, "position", 0.5f);
901     EXPECT_GL_NO_ERROR();
902     EXPECT_PIXEL_NEAR(px, py, 127, 127, 0, 255, 1.0);
903 }
904 
905 // Create a 2D texture with levels 0-2, call GenerateMipmap with base level 1 so that level 0 stays
906 // the same, and then sample levels 0 and 2.
907 // GLES 3.0.4 section 3.8.10:
908 // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from
909 // the levelbase array, regardless of their previous contents. All other mipmap arrays, including
910 // the levelbase array, are left unchanged by this computation."
TEST_P(MipmapTestES3,GenerateMipmapBaseLevel)911 TEST_P(MipmapTestES3, GenerateMipmapBaseLevel)
912 {
913     if (IsAMD() && IsDesktopOpenGL())
914     {
915         // Observed incorrect rendering on AMD, sampling level 2 returns black.
916         std::cout << "Test skipped on AMD OpenGL." << std::endl;
917         return;
918     }
919 
920     glBindTexture(GL_TEXTURE_2D, mTexture);
921 
922     ASSERT(getWindowWidth() == getWindowHeight());
923 
924     // Fill level 0 with blue
925     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue);
926     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
927                  GL_UNSIGNED_BYTE, pixelsBlue.data());
928 
929     // Fill level 1 with red
930     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight() / 4, GLColor::red);
931     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0,
932                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data());
933 
934     // Fill level 2 with green
935     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 16, GLColor::green);
936     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth() / 4, getWindowHeight() / 4, 0,
937                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsGreen.data());
938 
939     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
940     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
941     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1);
942 
943     EXPECT_GL_NO_ERROR();
944 
945     // The blue level 0 should be untouched by this since base level is 1.
946     glGenerateMipmap(GL_TEXTURE_2D);
947 
948     EXPECT_GL_NO_ERROR();
949 
950     // Draw using level 2. It should be set to red by GenerateMipmap.
951     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
952     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
953 
954     // Draw using level 0. It should still be blue.
955     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
956     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
957     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
958 }
959 
960 // Create a cube map with levels 0-2, call GenerateMipmap with base level 1 so that level 0 stays
961 // the same, and then sample levels 0 and 2.
962 // GLES 3.0.4 section 3.8.10:
963 // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from
964 // the levelbase array, regardless of their previous contents. All other mipmap arrays, including
965 // the levelbase array, are left unchanged by this computation."
TEST_P(MipmapTestES3,GenerateMipmapCubeBaseLevel)966 TEST_P(MipmapTestES3, GenerateMipmapCubeBaseLevel)
967 {
968     if (IsAMD() && IsDesktopOpenGL())
969     {
970         // Observed incorrect rendering on AMD, sampling level 2 returns black.
971         std::cout << "Test skipped on AMD OpenGL." << std::endl;
972         return;
973     }
974 
975     ASSERT_EQ(getWindowWidth(), getWindowHeight());
976 
977     glBindTexture(GL_TEXTURE_CUBE_MAP, mTexture);
978     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowWidth(), GLColor::blue);
979     TexImageCubeMapFaces(0, GL_RGBA8, getWindowWidth(), GL_RGBA, GL_UNSIGNED_BYTE,
980                          pixelsBlue.data());
981 
982     // Fill level 1 with red
983     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowWidth() / 4, GLColor::red);
984     TexImageCubeMapFaces(1, GL_RGBA8, getWindowWidth() / 2, GL_RGBA, GL_UNSIGNED_BYTE,
985                          pixelsRed.data());
986 
987     // Fill level 2 with green
988     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowWidth() / 16, GLColor::green);
989     TexImageCubeMapFaces(2, GL_RGBA8, getWindowWidth() / 4, GL_RGBA, GL_UNSIGNED_BYTE,
990                          pixelsGreen.data());
991 
992     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
993     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
994     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 1);
995 
996     EXPECT_GL_NO_ERROR();
997 
998     // The blue level 0 should be untouched by this since base level is 1.
999     glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
1000 
1001     EXPECT_GL_NO_ERROR();
1002 
1003     // Draw using level 2. It should be set to red by GenerateMipmap.
1004     clearAndDrawQuad(mCubeProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1005     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::red);
1006 
1007     if (IsNVIDIA() && IsOpenGL())
1008     {
1009         // Observed incorrect rendering on NVIDIA, level zero seems to be incorrectly affected by
1010         // GenerateMipmap.
1011         std::cout << "Test partially skipped on NVIDIA OpenGL." << std::endl;
1012         return;
1013     }
1014 
1015     // Draw using level 0. It should still be blue.
1016     glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_BASE_LEVEL, 0);
1017     clearAndDrawQuad(mCubeProgram, getWindowWidth(), getWindowHeight());
1018     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
1019 }
1020 
1021 // Create a texture with levels 0-2, call GenerateMipmap with max level 1 so that level 2 stays the
1022 // same, and then sample levels 1 and 2.
1023 // GLES 3.0.4 section 3.8.10:
1024 // "Mipmap generation replaces texel array levels levelbase + 1 through q with arrays derived from
1025 // the levelbase array, regardless of their previous contents. All other mipmap arrays, including
1026 // the levelbase array, are left unchanged by this computation."
TEST_P(MipmapTestES3,GenerateMipmapMaxLevel)1027 TEST_P(MipmapTestES3, GenerateMipmapMaxLevel)
1028 {
1029     glBindTexture(GL_TEXTURE_2D, mTexture);
1030 
1031     // Fill level 0 with blue
1032     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue);
1033     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
1034                  GL_UNSIGNED_BYTE, pixelsBlue.data());
1035 
1036     // Fill level 1 with red
1037     std::vector<GLColor> pixelsRed(getWindowWidth() * getWindowHeight() / 4, GLColor::red);
1038     glTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA8, getWindowWidth() / 2, getWindowHeight() / 2, 0,
1039                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsRed.data());
1040 
1041     // Fill level 2 with green
1042     std::vector<GLColor> pixelsGreen(getWindowWidth() * getWindowHeight() / 16, GLColor::green);
1043     glTexImage2D(GL_TEXTURE_2D, 2, GL_RGBA8, getWindowWidth() / 4, getWindowHeight() / 4, 0,
1044                  GL_RGBA, GL_UNSIGNED_BYTE, pixelsGreen.data());
1045 
1046     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
1047     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
1048     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 1);
1049 
1050     EXPECT_GL_NO_ERROR();
1051 
1052     // The green level 2 should be untouched by this since max level is 1.
1053     glGenerateMipmap(GL_TEXTURE_2D);
1054 
1055     EXPECT_GL_NO_ERROR();
1056 
1057     // Draw using level 1. It should be set to blue by GenerateMipmap.
1058     clearAndDrawQuad(m2DProgram, getWindowWidth() / 2, getWindowHeight() / 2);
1059     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 4, getWindowHeight() / 4, GLColor::blue);
1060 
1061     // Draw using level 2. It should still be green.
1062     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 2);
1063     clearAndDrawQuad(m2DProgram, getWindowWidth() / 4, getWindowHeight() / 4);
1064     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 8, getWindowHeight() / 8, GLColor::green);
1065 }
1066 
1067 // Call GenerateMipmap with out-of-range base level. The spec is interpreted so that an out-of-range
1068 // base level does not have a color-renderable/texture-filterable internal format, so the
1069 // GenerateMipmap call generates INVALID_OPERATION. GLES 3.0.4 section 3.8.10:
1070 // "If the levelbase array was not specified with an unsized internal format from table 3.3 or a
1071 // sized internal format that is both color-renderable and texture-filterable according to table
1072 // 3.13, an INVALID_OPERATION error is generated."
TEST_P(MipmapTestES3,GenerateMipmapBaseLevelOutOfRange)1073 TEST_P(MipmapTestES3, GenerateMipmapBaseLevelOutOfRange)
1074 {
1075     glBindTexture(GL_TEXTURE_2D, mTexture);
1076 
1077     // Fill level 0 with blue
1078     std::vector<GLColor> pixelsBlue(getWindowWidth() * getWindowHeight(), GLColor::blue);
1079     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, getWindowWidth(), getWindowHeight(), 0, GL_RGBA,
1080                  GL_UNSIGNED_BYTE, pixelsBlue.data());
1081 
1082     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1000);
1083 
1084     EXPECT_GL_NO_ERROR();
1085 
1086     // Expecting the out-of-range base level to be treated as not color-renderable and
1087     // texture-filterable.
1088     glGenerateMipmap(GL_TEXTURE_2D);
1089     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
1090 
1091     // Draw using level 0. It should still be blue.
1092     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
1093     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1094     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1095     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1096     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::blue);
1097 }
1098 
1099 // Call GenerateMipmap with out-of-range base level on an immutable texture. The base level should
1100 // be clamped, so the call doesn't generate an error.
TEST_P(MipmapTestES3,GenerateMipmapBaseLevelOutOfRangeImmutableTexture)1101 TEST_P(MipmapTestES3, GenerateMipmapBaseLevelOutOfRangeImmutableTexture)
1102 {
1103     glBindTexture(GL_TEXTURE_2D, mTexture);
1104 
1105     glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, 1, 1);
1106     glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, &GLColor::green);
1107 
1108     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 1000);
1109 
1110     EXPECT_GL_NO_ERROR();
1111 
1112     // This is essentially a no-op, since the texture only has one level.
1113     glGenerateMipmap(GL_TEXTURE_2D);
1114 
1115     EXPECT_GL_NO_ERROR();
1116 
1117     // The only level of the texture should still be green.
1118     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
1119     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
1120     clearAndDrawQuad(m2DProgram, getWindowWidth(), getWindowHeight());
1121     EXPECT_PIXEL_COLOR_EQ(getWindowWidth() / 2, getWindowHeight() / 2, GLColor::green);
1122 }
1123 
1124 // Use this to select which configurations (e.g. which renderer, which GLES major version) these tests should be run against.
1125 // Note: we run these tests against 9_3 on WARP due to hardware driver issues on Win7
1126 ANGLE_INSTANTIATE_TEST(MipmapTest,
1127                        ES2_D3D9(),
1128                        ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_COPY_ANGLE),
1129                        ES2_D3D11(EGL_EXPERIMENTAL_PRESENT_PATH_FAST_ANGLE),
1130                        ES2_D3D11_FL9_3_WARP(),
1131                        ES2_OPENGL(),
1132                        ES3_OPENGL(),
1133                        ES2_OPENGLES(),
1134                        ES3_OPENGLES());
1135 ANGLE_INSTANTIATE_TEST(MipmapTestES3, ES3_D3D11(), ES3_OPENGL(), ES3_OPENGLES());
1136