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