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