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 #include "test_utils/gl_raii.h"
9
10 namespace angle
11 {
12
13 class CopyTexImageTest : public ANGLETest
14 {
15 protected:
CopyTexImageTest()16 CopyTexImageTest()
17 {
18 setWindowWidth(32);
19 setWindowHeight(32);
20 setConfigRedBits(8);
21 setConfigGreenBits(8);
22 setConfigBlueBits(8);
23 setConfigAlphaBits(8);
24 }
25
testSetUp()26 void testSetUp() override
27 {
28 constexpr char kVS[] =
29 "precision highp float;\n"
30 "attribute vec4 position;\n"
31 "varying vec2 texcoord;\n"
32 "\n"
33 "void main()\n"
34 "{\n"
35 " gl_Position = position;\n"
36 " texcoord = (position.xy * 0.5) + 0.5;\n"
37 "}\n";
38
39 constexpr char kFS[] =
40 "precision highp float;\n"
41 "uniform sampler2D tex;\n"
42 "varying vec2 texcoord;\n"
43 "\n"
44 "void main()\n"
45 "{\n"
46 " gl_FragColor = texture2D(tex, texcoord);\n"
47 "}\n";
48
49 mTextureProgram = CompileProgram(kVS, kFS);
50 if (mTextureProgram == 0)
51 {
52 FAIL() << "shader compilation failed.";
53 }
54
55 mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
56
57 ASSERT_GL_NO_ERROR();
58 }
59
testTearDown()60 void testTearDown() override { glDeleteProgram(mTextureProgram); }
61
initializeResources(GLenum format,GLenum type)62 void initializeResources(GLenum format, GLenum type)
63 {
64 for (size_t i = 0; i < kFboCount; ++i)
65 {
66 glBindTexture(GL_TEXTURE_2D, mFboTextures[i]);
67 glTexImage2D(GL_TEXTURE_2D, 0, format, kFboSizes[i], kFboSizes[i], 0, format, type,
68 nullptr);
69
70 // Disable mipmapping
71 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
72 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
73
74 glBindFramebuffer(GL_FRAMEBUFFER, mFbos[i]);
75 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
76 mFboTextures[i], 0);
77
78 glClearColor(kFboColors[i][0], kFboColors[i][1], kFboColors[i][2], kFboColors[i][3]);
79 glClear(GL_COLOR_BUFFER_BIT);
80 }
81
82 ASSERT_GL_NO_ERROR();
83 }
84
verifyResults(GLuint texture,GLubyte data[4],GLint fboSize,GLint xs,GLint ys,GLint xe,GLint ye)85 void verifyResults(GLuint texture,
86 GLubyte data[4],
87 GLint fboSize,
88 GLint xs,
89 GLint ys,
90 GLint xe,
91 GLint ye)
92 {
93 glViewport(0, 0, fboSize, fboSize);
94
95 glBindFramebuffer(GL_FRAMEBUFFER, 0);
96
97 // Draw a quad with the target texture
98 glUseProgram(mTextureProgram);
99 glBindTexture(GL_TEXTURE_2D, texture);
100 glUniform1i(mTextureUniformLocation, 0);
101
102 drawQuad(mTextureProgram, "position", 0.5f);
103
104 // Expect that the rendered quad has the same color as the source texture
105 EXPECT_PIXEL_NEAR(xs, ys, data[0], data[1], data[2], data[3], 1.0);
106 EXPECT_PIXEL_NEAR(xs, ye - 1, data[0], data[1], data[2], data[3], 1.0);
107 EXPECT_PIXEL_NEAR(xe - 1, ys, data[0], data[1], data[2], data[3], 1.0);
108 EXPECT_PIXEL_NEAR(xe - 1, ye - 1, data[0], data[1], data[2], data[3], 1.0);
109 EXPECT_PIXEL_NEAR((xs + xe) / 2, (ys + ye) / 2, data[0], data[1], data[2], data[3], 1.0);
110 }
111
runCopyTexImageTest(GLenum format,GLubyte expected[3][4])112 void runCopyTexImageTest(GLenum format, GLubyte expected[3][4])
113 {
114 GLTexture tex;
115 glBindTexture(GL_TEXTURE_2D, tex);
116
117 // Disable mipmapping
118 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
119 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
120
121 // Perform the copy multiple times.
122 //
123 // - The first time, a new texture is created
124 // - The second time, as the fbo size is the same as previous, the texture storage is not
125 // recreated.
126 // - The third time, the fbo size is different, so a new texture is created.
127 for (size_t i = 0; i < kFboCount; ++i)
128 {
129 glBindFramebuffer(GL_FRAMEBUFFER, mFbos[i]);
130
131 glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, kFboSizes[i], kFboSizes[i], 0);
132 ASSERT_GL_NO_ERROR();
133
134 verifyResults(tex, expected[i], kFboSizes[i], 0, 0, kFboSizes[i], kFboSizes[i]);
135 }
136 }
137
runCopyTexSubImageTest(GLenum format,GLubyte expected[3][4])138 void runCopyTexSubImageTest(GLenum format, GLubyte expected[3][4])
139 {
140 GLTexture tex;
141 glBindTexture(GL_TEXTURE_2D, tex);
142
143 // Disable mipmapping
144 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
145 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
146
147 // Create the texture with copy of the first fbo.
148 glBindFramebuffer(GL_FRAMEBUFFER, mFbos[0]);
149 glCopyTexImage2D(GL_TEXTURE_2D, 0, format, 0, 0, kFboSizes[0], kFboSizes[0], 0);
150 ASSERT_GL_NO_ERROR();
151
152 verifyResults(tex, expected[0], kFboSizes[0], 0, 0, kFboSizes[0], kFboSizes[0]);
153
154 // Make sure out-of-bound writes to the texture return invalid value.
155 glBindFramebuffer(GL_FRAMEBUFFER, mFbos[1]);
156
157 // xoffset < 0 and yoffset < 0
158 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, -1, -1, 0, 0, kFboSizes[0], kFboSizes[0]);
159 ASSERT_GL_ERROR(GL_INVALID_VALUE);
160
161 // xoffset + width > w and yoffset + height > h
162 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 1, 1, 0, 0, kFboSizes[0], kFboSizes[0]);
163 ASSERT_GL_ERROR(GL_INVALID_VALUE);
164
165 // xoffset + width > w and yoffset + height > h, out of bounds
166 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, -1, -1, 1 + kFboSizes[0], 1 + kFboSizes[0]);
167 ASSERT_GL_ERROR(GL_INVALID_VALUE);
168
169 // Copy the second fbo over a portion of the image.
170 GLint offset = kFboSizes[0] / 2;
171 GLint extent = kFboSizes[0] - offset;
172
173 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, offset, offset, kFboSizes[1] / 2, kFboSizes[1] / 2,
174 extent, extent);
175 ASSERT_GL_NO_ERROR();
176
177 verifyResults(tex, expected[1], kFboSizes[0], offset, offset, kFboSizes[0], kFboSizes[0]);
178
179 // The rest of the image should be untouched
180 verifyResults(tex, expected[0], kFboSizes[0], 0, 0, offset, offset);
181 verifyResults(tex, expected[0], kFboSizes[0], offset, 0, kFboSizes[0], offset);
182 verifyResults(tex, expected[0], kFboSizes[0], 0, offset, offset, kFboSizes[0]);
183
184 // Copy the third fbo over another portion of the image.
185 glBindFramebuffer(GL_FRAMEBUFFER, mFbos[2]);
186
187 offset = kFboSizes[0] / 4;
188 extent = kFboSizes[0] - offset;
189
190 // While width and height are set as 3/4 of the size, the fbo offset is given such that
191 // after clipping, width and height are effectively 1/2 of the size.
192 GLint srcOffset = kFboSizes[2] - kFboSizes[0] / 2;
193 GLint effectiveExtent = kFboSizes[0] / 2;
194
195 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, offset, offset, srcOffset, srcOffset, extent, extent);
196 ASSERT_GL_NO_ERROR();
197
198 verifyResults(tex, expected[2], kFboSizes[0], offset, offset, effectiveExtent,
199 effectiveExtent);
200
201 // The rest of the image should be untouched
202 verifyResults(tex, expected[1], kFboSizes[0], offset + effectiveExtent, kFboSizes[0] / 2,
203 kFboSizes[0], kFboSizes[0]);
204 verifyResults(tex, expected[1], kFboSizes[0], kFboSizes[0] / 2, offset + effectiveExtent,
205 kFboSizes[0], kFboSizes[0]);
206
207 verifyResults(tex, expected[0], kFboSizes[0], 0, 0, kFboSizes[0], offset);
208 verifyResults(tex, expected[0], kFboSizes[0], 0, 0, offset, kFboSizes[0]);
209 verifyResults(tex, expected[0], kFboSizes[0], offset + effectiveExtent, 0, kFboSizes[0],
210 kFboSizes[0] / 2);
211 verifyResults(tex, expected[0], kFboSizes[0], 0, offset + effectiveExtent, kFboSizes[0] / 2,
212 kFboSizes[0]);
213 }
214
215 GLuint mTextureProgram;
216 GLint mTextureUniformLocation;
217
218 static constexpr uint32_t kFboCount = 3;
219 GLFramebuffer mFbos[kFboCount];
220 GLTexture mFboTextures[kFboCount];
221
222 static constexpr uint32_t kFboSizes[kFboCount] = {16, 16, 32};
223 static constexpr GLfloat kFboColors[kFboCount][4] = {{0.25f, 1.0f, 0.75f, 0.5f},
224 {1.0f, 0.75f, 0.5f, 0.25f},
225 {0.5f, 0.25f, 1.0f, 0.75f}};
226 };
227
228 // Until C++17, need to redundantly declare the constexpr members outside the class (only the
229 // arrays, because the others are already const-propagated and not needed by the linker).
230 constexpr uint32_t CopyTexImageTest::kFboSizes[];
231 constexpr GLfloat CopyTexImageTest::kFboColors[][4];
232
TEST_P(CopyTexImageTest,RGBAToRGB)233 TEST_P(CopyTexImageTest, RGBAToRGB)
234 {
235 GLubyte expected[3][4] = {
236 {64, 255, 191, 255},
237 {255, 191, 127, 255},
238 {127, 64, 255, 255},
239 };
240
241 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
242 runCopyTexImageTest(GL_RGB, expected);
243 }
244
TEST_P(CopyTexImageTest,RGBAToL)245 TEST_P(CopyTexImageTest, RGBAToL)
246 {
247 GLubyte expected[3][4] = {
248 {64, 64, 64, 255},
249 {255, 255, 255, 255},
250 {127, 127, 127, 255},
251 };
252
253 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
254 runCopyTexImageTest(GL_LUMINANCE, expected);
255 }
256
TEST_P(CopyTexImageTest,RGBToL)257 TEST_P(CopyTexImageTest, RGBToL)
258 {
259 GLubyte expected[3][4] = {
260 {64, 64, 64, 255},
261 {255, 255, 255, 255},
262 {127, 127, 127, 255},
263 };
264
265 initializeResources(GL_RGB, GL_UNSIGNED_BYTE);
266 runCopyTexImageTest(GL_LUMINANCE, expected);
267 }
268
TEST_P(CopyTexImageTest,RGBAToLA)269 TEST_P(CopyTexImageTest, RGBAToLA)
270 {
271 GLubyte expected[3][4] = {
272 {64, 64, 64, 127},
273 {255, 255, 255, 64},
274 {127, 127, 127, 191},
275 };
276
277 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
278 runCopyTexImageTest(GL_LUMINANCE_ALPHA, expected);
279 }
280
TEST_P(CopyTexImageTest,RGBAToA)281 TEST_P(CopyTexImageTest, RGBAToA)
282 {
283 GLubyte expected[3][4] = {
284 {0, 0, 0, 127},
285 {0, 0, 0, 64},
286 {0, 0, 0, 191},
287 };
288
289 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
290 runCopyTexImageTest(GL_ALPHA, expected);
291 }
292
TEST_P(CopyTexImageTest,SubImageRGBAToRGB)293 TEST_P(CopyTexImageTest, SubImageRGBAToRGB)
294 {
295 GLubyte expected[3][4] = {
296 {64, 255, 191, 255},
297 {255, 191, 127, 255},
298 {127, 64, 255, 255},
299 };
300
301 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
302 runCopyTexSubImageTest(GL_RGB, expected);
303 }
304
TEST_P(CopyTexImageTest,SubImageRGBAToL)305 TEST_P(CopyTexImageTest, SubImageRGBAToL)
306 {
307 GLubyte expected[3][4] = {
308 {64, 64, 64, 255},
309 {255, 255, 255, 255},
310 {127, 127, 127, 255},
311 };
312
313 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
314 runCopyTexSubImageTest(GL_LUMINANCE, expected);
315 }
316
TEST_P(CopyTexImageTest,SubImageRGBAToLA)317 TEST_P(CopyTexImageTest, SubImageRGBAToLA)
318 {
319 GLubyte expected[3][4] = {
320 {64, 64, 64, 127},
321 {255, 255, 255, 64},
322 {127, 127, 127, 191},
323 };
324
325 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
326 runCopyTexSubImageTest(GL_LUMINANCE_ALPHA, expected);
327 }
328
TEST_P(CopyTexImageTest,SubImageRGBToL)329 TEST_P(CopyTexImageTest, SubImageRGBToL)
330 {
331 GLubyte expected[3][4] = {
332 {64, 64, 64, 255},
333 {255, 255, 255, 255},
334 {127, 127, 127, 255},
335 };
336
337 initializeResources(GL_RGB, GL_UNSIGNED_BYTE);
338 runCopyTexSubImageTest(GL_LUMINANCE, expected);
339 }
340
341 // Read default framebuffer with glCopyTexImage2D().
TEST_P(CopyTexImageTest,DefaultFramebuffer)342 TEST_P(CopyTexImageTest, DefaultFramebuffer)
343 {
344 // Seems to be a bug in Mesa with the GLX back end: cannot read framebuffer until we draw to it.
345 // glCopyTexImage2D() below will fail without this clear.
346 glClear(GL_COLOR_BUFFER_BIT);
347
348 const GLint w = getWindowWidth(), h = getWindowHeight();
349 GLTexture tex;
350 glBindTexture(GL_TEXTURE_2D, tex);
351 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
352 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, w, h, 0);
353 EXPECT_GL_NO_ERROR();
354 }
355
356 // Read default framebuffer with glCopyTexSubImage2D().
TEST_P(CopyTexImageTest,SubDefaultFramebuffer)357 TEST_P(CopyTexImageTest, SubDefaultFramebuffer)
358 {
359 // Seems to be a bug in Mesa with the GLX back end: cannot read framebuffer until we draw to it.
360 // glCopyTexSubImage2D() below will fail without this clear.
361 glClear(GL_COLOR_BUFFER_BIT);
362
363 const GLint w = getWindowWidth(), h = getWindowHeight();
364 GLTexture tex;
365 glBindTexture(GL_TEXTURE_2D, tex);
366 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
367 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, w, h);
368 EXPECT_GL_NO_ERROR();
369 }
370
371 // Calling CopyTexSubImage from cubeMap texture.
TEST_P(CopyTexImageTest,CopyTexSubImageFromCubeMap)372 TEST_P(CopyTexImageTest, CopyTexSubImageFromCubeMap)
373 {
374 constexpr GLsizei kCubeMapFaceCount = 6;
375
376 // The framebuffer will be a face of a cube map with a different colors for each face. Each
377 // glCopyTexSubImage2D will take one face of this image to copy over a pixel in a 1x6
378 // framebuffer.
379 GLColor fboPixels[kCubeMapFaceCount] = {GLColor::red, GLColor::yellow, GLColor::green,
380 GLColor::cyan, GLColor::blue, GLColor::magenta};
381 GLColor whitePixels[kCubeMapFaceCount] = {GLColor::white, GLColor::white, GLColor::white,
382 GLColor::white, GLColor::white, GLColor::white};
383
384 GLTexture fboTex;
385 glBindTexture(GL_TEXTURE_CUBE_MAP, fboTex);
386 for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
387 face++)
388 {
389 GLsizei faceIndex = face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
390
391 glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &fboPixels[faceIndex]);
392 }
393
394 GLTexture dstTex;
395 glBindTexture(GL_TEXTURE_2D, dstTex);
396 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kCubeMapFaceCount, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
397 whitePixels);
398
399 GLFramebuffer fbo;
400 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
401
402 for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
403 face++)
404 {
405 GLsizei faceIndex = face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
406
407 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, fboTex, 0);
408
409 ASSERT_GL_NO_ERROR();
410 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
411
412 // Copy the fbo (a cube map face) into a pixel of the destination texture.
413 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, faceIndex, 0, 0, 0, 1, 1);
414 }
415
416 // Make sure all the copies are done correctly.
417 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dstTex, 0);
418
419 ASSERT_GL_NO_ERROR();
420 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
421
422 for (GLsizei faceIndex = 0; faceIndex < kCubeMapFaceCount; ++faceIndex)
423 {
424 EXPECT_PIXEL_COLOR_EQ(faceIndex, 0, fboPixels[faceIndex]);
425 }
426 }
427
428 // Calling CopyTexSubImage to a non-cube-complete texture.
TEST_P(CopyTexImageTest,CopyTexSubImageToNonCubeCompleteDestination)429 TEST_P(CopyTexImageTest, CopyTexSubImageToNonCubeCompleteDestination)
430 {
431 // TODO(hqle): Find what wrong with NVIDIA GPU. http://anglebug.com/4137
432 ANGLE_SKIP_TEST_IF(IsNVIDIA() && IsMetal());
433
434 constexpr GLsizei kCubeMapFaceCount = 6;
435
436 // The framebuffer will be a 1x6 image with 6 different colors. Each glCopyTexSubImage2D will
437 // take one pixel of this image to copy over each face of a cube map.
438 GLColor fboPixels[kCubeMapFaceCount] = {GLColor::red, GLColor::yellow, GLColor::green,
439 GLColor::cyan, GLColor::blue, GLColor::magenta};
440 GLColor whitePixel = GLColor::white;
441
442 GLTexture fboTex;
443 glBindTexture(GL_TEXTURE_2D, fboTex);
444 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kCubeMapFaceCount, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
445 fboPixels);
446
447 GLFramebuffer fbo;
448 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
449 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, fboTex, 0);
450
451 ASSERT_GL_NO_ERROR();
452 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
453
454 GLTexture cubeMap;
455 glBindTexture(GL_TEXTURE_CUBE_MAP, cubeMap);
456
457 for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
458 face++)
459 {
460 GLsizei faceIndex = face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
461
462 // Initialize the face with a color not found in the fbo.
463 glTexImage2D(face, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, &whitePixel);
464
465 // Copy one pixel from the fbo into this face. The first 5 copies are done on a
466 // non-cube-complete texture.
467 glCopyTexSubImage2D(face, 0, 0, 0, faceIndex, 0, 1, 1);
468 }
469
470 // Make sure all the copies are done correctly.
471 for (GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X; face <= GL_TEXTURE_CUBE_MAP_NEGATIVE_Z;
472 face++)
473 {
474 GLsizei faceIndex = face - GL_TEXTURE_CUBE_MAP_POSITIVE_X;
475
476 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, face, cubeMap, 0);
477
478 ASSERT_GL_NO_ERROR();
479 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
480
481 EXPECT_PIXEL_COLOR_EQ(0, 0, fboPixels[faceIndex]);
482 }
483 }
484
485 // Deleting textures after copying to them. http://anglebug.com/4267
TEST_P(CopyTexImageTest,DeleteAfterCopyingToTextures)486 TEST_P(CopyTexImageTest, DeleteAfterCopyingToTextures)
487 {
488 // Asserts on Vulkan backend. http://anglebug.com/4274
489 ANGLE_SKIP_TEST_IF(IsVulkan());
490
491 GLTexture texture;
492 glBindTexture(GL_TEXTURE_2D, texture);
493 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
494 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
495 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
496 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
497 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
498
499 GLTexture texture2;
500 glBindTexture(GL_TEXTURE_2D, texture2);
501 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
502 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
503 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
504 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
505 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
506
507 GLFramebuffer framebuffer;
508 glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
509 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
510 ASSERT_GL_FRAMEBUFFER_COMPLETE(GL_FRAMEBUFFER);
511
512 // Perform CopyTexImage2D
513 glBindTexture(GL_TEXTURE_2D, texture);
514 glCopyTexImage2D(GL_TEXTURE_2D, 1, GL_RGBA, 0, 0, 2, 2, 0);
515 ASSERT_GL_NO_ERROR();
516 // Not necessary to do any CopyTexImage2D operations to texture2.
517
518 // Perform CopyTexSubImage2D
519 glBindTexture(GL_TEXTURE_2D, texture);
520 glCopyTexSubImage2D(GL_TEXTURE_2D, 1, 0, 0, 0, 0, 1, 1);
521 ASSERT_GL_NO_ERROR();
522 glBindTexture(GL_TEXTURE_2D, texture2);
523 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
524 ASSERT_GL_NO_ERROR();
525
526 // Clean up - provokes crash on buggy drivers.
527 texture.reset();
528 // Crashes on Intel GPUs on macOS.
529 texture2.reset();
530 }
531
532 // specialization of CopyTexImageTest is added so that some tests can be explicitly run with an ES3
533 // context
534 class CopyTexImageTestES3 : public CopyTexImageTest
535 {
536 protected:
537 void initialize3DTexture(GLTexture &texture,
538 const GLsizei imageWidth,
539 const GLsizei imageHeight,
540 const GLsizei imageDepth,
541 const GLColor *textureData);
542 void initialize2DTexture(GLTexture &texture,
543 const GLsizei imageWidth,
544 const GLsizei imageHeight,
545 const GLColor *textureData);
546 void initialize2DTextureUShort4444(GLTexture &texture,
547 const GLsizei imageWidth,
548 const GLsizei imageHeight,
549 const GLColor *textureData);
550 void fillTexture(std::vector<GLColor> &texture, const GLColor color);
551 void clearTexture(GLFramebuffer &fbo, GLTexture &texture, const GLColor color);
552 void copyTexSubImage3D(GLTexture &subTexture2D,
553 const GLint xOffset,
554 const GLint yOffset,
555 const GLsizei subImageWidth,
556 const GLsizei subImageHeight,
557 const GLsizei imageDepth);
558 void verifyCopyTexSubImage3D(GLTexture &texture3D,
559 const GLint xOffset,
560 const GLint yOffset,
561 const GLColor subImageColor);
562
563 // Constants
564 const GLColor kSubImageColor = GLColor::yellow;
565 // 3D image dimensions
566 const GLsizei kImageWidth = getWindowWidth();
567 const GLsizei kImageHeight = getWindowHeight();
568 const GLsizei kImageDepth = 4;
569 // 2D sub-image dimensions
570 const GLsizei kSubImageWidth = getWindowWidth() / 4;
571 const GLsizei kSubImageHeight = getWindowHeight() / 4;
572 // Sub-Image Offsets
573 const GLint kXOffset = getWindowWidth() - kSubImageWidth;
574 const GLint kYOffset = getWindowHeight() - kSubImageHeight;
575 };
576
577 // The test verifies that glCopyTexSubImage2D generates a GL_INVALID_OPERATION error
578 // when the read buffer is GL_NONE.
579 // Reference: GLES 3.0.4, Section 3.8.5 Alternate Texture Image Specification Commands
TEST_P(CopyTexImageTestES3,ReadBufferIsNone)580 TEST_P(CopyTexImageTestES3, ReadBufferIsNone)
581 {
582 initializeResources(GL_RGBA, GL_UNSIGNED_BYTE);
583
584 GLTexture tex;
585 glBindTexture(GL_TEXTURE_2D, tex);
586 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
587 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
588
589 glBindFramebuffer(GL_FRAMEBUFFER, mFbos[0]);
590 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, kFboSizes[0], kFboSizes[0], 0);
591
592 glReadBuffer(GL_NONE);
593
594 EXPECT_GL_NO_ERROR();
595 glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 4, 4);
596 EXPECT_GL_ERROR(GL_INVALID_OPERATION);
597 }
598
599 // Test CopyTexImage3D with some simple parameters with a 2D array texture.
600 TEST_P(CopyTexImageTestES3, 2DArraySubImage)
601 {
602 // Seems to fail on AMD OpenGL Windows.
603 ANGLE_SKIP_TEST_IF(IsAMD() && IsOpenGL() & IsWindows());
604
605 GLTexture tex;
606 glBindTexture(GL_TEXTURE_2D_ARRAY, tex);
607
608 constexpr GLsizei kTexSize = 4;
609 constexpr GLsizei kLayerOffset = 1;
610 constexpr GLsizei kLayers = 2;
611
612 // Clear screen to green.
613 glClearColor(0, 1, 0, 1);
614 glClear(GL_COLOR_BUFFER_BIT);
615
616 // Initialize a two-layer 2D array texture with red.
617 std::vector<GLColor> red(kTexSize * kTexSize * kLayers, GLColor::red);
618 glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA8, kTexSize, kTexSize, kLayers, 0, GL_RGBA,
619 GL_UNSIGNED_BYTE, red.data());
620 glCopyTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, kLayerOffset, 0, 0, kTexSize, kTexSize);
621 ASSERT_GL_NO_ERROR();
622
623 // Check level 0 (red from image data) and 1 (green from backbuffer clear).
624 GLFramebuffer fbo;
625 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
626 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 0);
627 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
628 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex, 0, 1);
629 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
630 ASSERT_GL_NO_ERROR();
631 }
632
633 // Test if glCopyTexImage2D() implementation performs conversions well from GL_TEXTURE_3D to
634 // GL_TEXTURE_2D.
TEST_P(CopyTexImageTestES3,CopyTexSubImageFromTexture3D)635 TEST_P(CopyTexImageTestES3, CopyTexSubImageFromTexture3D)
636 {
637 // TODO(anglebug.com/3801)
638 // Seems to fail on D3D11 Windows.
639 ANGLE_SKIP_TEST_IF(IsD3D11() & IsWindows());
640
641 constexpr GLsizei kTexSize = 4;
642 constexpr GLsizei kLayers = 2;
643 std::vector<GLColor> red(kTexSize * kTexSize * kLayers, GLColor::red);
644
645 GLFramebuffer fbo;
646 glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
647 glBindTexture(GL_TEXTURE_2D, 0);
648
649 // We will be reading from zeroth color attachment.
650 glReadBuffer(GL_COLOR_ATTACHMENT0);
651
652 GLTexture src_object_id;
653 glBindTexture(GL_TEXTURE_3D, src_object_id);
654 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA8, kTexSize, kTexSize, kLayers, 0, GL_RGBA,
655 GL_UNSIGNED_BYTE, NULL);
656 glTexSubImage3D(GL_TEXTURE_3D, 0, 0, 0, 1, kTexSize, kTexSize, 1, GL_RGBA, GL_UNSIGNED_BYTE,
657 red.data());
658 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, src_object_id, 0, 1);
659 ASSERT_GL_NO_ERROR();
660
661 GLTexture dst_object_id;
662 glBindTexture(GL_TEXTURE_2D, dst_object_id);
663 glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 0, 0, kTexSize, kTexSize, 0);
664 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, dst_object_id,
665 0);
666 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
667 ASSERT_GL_NO_ERROR();
668 }
669
670 // Initialize the 3D texture we will copy the subImage data into
initialize3DTexture(GLTexture & texture,const GLsizei imageWidth,const GLsizei imageHeight,const GLsizei imageDepth,const GLColor * textureData)671 void CopyTexImageTestES3::initialize3DTexture(GLTexture &texture,
672 const GLsizei imageWidth,
673 const GLsizei imageHeight,
674 const GLsizei imageDepth,
675 const GLColor *textureData)
676 {
677 glBindTexture(GL_TEXTURE_3D, texture);
678 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, imageWidth, imageHeight, imageDepth, 0, GL_RGBA,
679 GL_UNSIGNED_BYTE, textureData);
680 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
681 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
682 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
683 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
684 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
685 }
686
initialize2DTexture(GLTexture & texture,const GLsizei imageWidth,const GLsizei imageHeight,const GLColor * textureData)687 void CopyTexImageTestES3::initialize2DTexture(GLTexture &texture,
688 const GLsizei imageWidth,
689 const GLsizei imageHeight,
690 const GLColor *textureData)
691 {
692 glBindTexture(GL_TEXTURE_2D, texture);
693 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
694 textureData);
695 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
696 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
697 }
698
initialize2DTextureUShort4444(GLTexture & texture,const GLsizei imageWidth,const GLsizei imageHeight,const GLColor * textureData)699 void CopyTexImageTestES3::initialize2DTextureUShort4444(GLTexture &texture,
700 const GLsizei imageWidth,
701 const GLsizei imageHeight,
702 const GLColor *textureData)
703 {
704 glBindTexture(GL_TEXTURE_2D, texture);
705 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA,
706 GL_UNSIGNED_SHORT_4_4_4_4, textureData);
707 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
708 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
709 }
710
fillTexture(std::vector<GLColor> & texture,const GLColor color)711 void CopyTexImageTestES3::fillTexture(std::vector<GLColor> &texture, const GLColor color)
712 {
713 for (auto &texel : texture)
714 {
715 texel = color;
716 }
717 }
718
clearTexture(GLFramebuffer & fbo,GLTexture & texture,const GLColor color)719 void CopyTexImageTestES3::clearTexture(GLFramebuffer &fbo, GLTexture &texture, const GLColor color)
720 {
721 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
722 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
723 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
724 glClearColor(color.R, color.G, color.B, color.A);
725 glClear(GL_COLOR_BUFFER_BIT);
726 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
727 }
728
copyTexSubImage3D(GLTexture & subTexture2D,const GLint xOffset,const GLint yOffset,const GLsizei subImageWidth,const GLsizei subImageHeight,const GLsizei imageDepth)729 void CopyTexImageTestES3::copyTexSubImage3D(GLTexture &subTexture2D,
730 const GLint xOffset,
731 const GLint yOffset,
732 const GLsizei subImageWidth,
733 const GLsizei subImageHeight,
734 const GLsizei imageDepth)
735 {
736 // Copy the 2D sub-image into the 3D texture
737 for (int currLayer = 0; currLayer < imageDepth; ++currLayer)
738 {
739 // Bind the 2D texture to GL_COLOR_ATTACHMENT0
740 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
741 subTexture2D, 0);
742 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
743 glCopyTexSubImage3D(GL_TEXTURE_3D, 0, xOffset, yOffset, currLayer, 0, 0, subImageWidth,
744 subImageHeight);
745 ASSERT_GL_NO_ERROR();
746 }
747 }
748
verifyCopyTexSubImage3D(GLTexture & texture3D,const GLint xOffset,const GLint yOffset,const GLColor subImageColor)749 void CopyTexImageTestES3::verifyCopyTexSubImage3D(GLTexture &texture3D,
750 const GLint xOffset,
751 const GLint yOffset,
752 const GLColor subImageColor)
753 {
754 // Bind to an FBO to check the copy was successful
755 for (int currLayer = 0; currLayer < kImageDepth; ++currLayer)
756 {
757 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0, currLayer);
758 ASSERT_GL_NO_ERROR();
759 EXPECT_PIXEL_COLOR_EQ(xOffset, yOffset, subImageColor);
760 }
761 }
762
763 // Test glCopyTexSubImage3D with initialized texture data
764 TEST_P(CopyTexImageTestES3, 3DSubImageRawTextureData)
765 {
766 // Texture data
767 std::vector<GLColor> textureData(kImageWidth * kImageHeight * kImageDepth);
768
769 // Fill the textures with color
770 fillTexture(textureData, GLColor::red);
771
772 GLFramebuffer fbo;
773 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
774
775 GLTexture texture3D;
776 initialize3DTexture(texture3D, kImageWidth, kImageHeight, kImageDepth, textureData.data());
777
778 // The 2D texture that will be the sub-image copied into the destination texture
779 GLTexture subTexture2D;
780 initialize2DTexture(subTexture2D, kSubImageWidth, kSubImageHeight, nullptr);
781 clearTexture(fbo, subTexture2D, kSubImageColor);
782
783 // Copy the 2D subimage into the 3D texture
784 copyTexSubImage3D(subTexture2D, kXOffset, kYOffset, kSubImageWidth, kSubImageHeight,
785 kImageDepth);
786
787 // Verify the color wasn't overwritten
788 verifyCopyTexSubImage3D(texture3D, 0, 0, GLColor::red);
789 // Verify the copy succeeded
790 verifyCopyTexSubImage3D(texture3D, kXOffset, kYOffset, kSubImageColor);
791
792 glBindFramebuffer(GL_FRAMEBUFFER, 0);
793 glBindTexture(GL_TEXTURE_2D, 0);
794 glBindTexture(GL_TEXTURE_3D, 0);
795 }
796
797 // Test glCopyTexSubImage3D with initialized texture data that was drawn to
798 TEST_P(CopyTexImageTestES3, 3DSubImageDrawTextureData)
799 {
800 // TODO(anglebug.com/3801)
801 ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11());
802
803 GLFramebuffer fbo;
804 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
805
806 // The 3D texture we will copy the sub-image into
807 GLTexture texture3D;
808 initialize3DTexture(texture3D, kImageWidth, kImageHeight, kImageDepth, nullptr);
809
810 // Draw to each layer in the 3D texture
811 for (int currLayer = 0; currLayer < kImageDepth; ++currLayer)
812 {
813 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
814 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0,
815 currLayer);
816 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0,
817 currLayer);
818 ASSERT_GL_NO_ERROR();
819 glUseProgram(greenProgram);
820 drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
821 ASSERT_GL_NO_ERROR();
822 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
823 }
824
825 // The 2D texture that will be the sub-image copied into the destination texture
826 GLTexture subTexture2D;
827 initialize2DTexture(subTexture2D, kSubImageWidth, kSubImageHeight, nullptr);
828 clearTexture(fbo, subTexture2D, kSubImageColor);
829
830 // Copy the 2D sub-image into the 3D texture
831 copyTexSubImage3D(subTexture2D, kXOffset, kYOffset, kSubImageWidth, kSubImageHeight,
832 kImageDepth);
833
834 // Verify the color wasn't overwritten
835 verifyCopyTexSubImage3D(texture3D, 0, 0, GLColor::green);
836 // Verify the copy succeeded
837 verifyCopyTexSubImage3D(texture3D, kXOffset, kYOffset, kSubImageColor);
838
839 glBindFramebuffer(GL_FRAMEBUFFER, 0);
840 glBindTexture(GL_TEXTURE_2D, 0);
841 glBindTexture(GL_TEXTURE_3D, 0);
842 }
843
844 // Test glCopyTexSubImage3D with mismatched texture formats
845 TEST_P(CopyTexImageTestES3, 3DSubImageDrawMismatchedTextureTypes)
846 {
847 // TODO(anglebug.com/3801)
848 ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11());
849
850 GLFramebuffer fbo;
851 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
852
853 // The 3D texture we will copy the sub-image into
854 GLTexture texture3D;
855 initialize3DTexture(texture3D, kImageWidth, kImageHeight, kImageDepth, nullptr);
856
857 // Draw to each layer in the 3D texture
858 for (int currLayer = 0; currLayer < kImageDepth; ++currLayer)
859 {
860 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
861 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0, currLayer);
862 ASSERT_GL_NO_ERROR();
863 glUseProgram(greenProgram);
864 drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
865 ASSERT_GL_NO_ERROR();
866 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
867 }
868
869 // The 2D texture that will be the sub-image copied into the destination texture
870 GLTexture subTexture2D;
871 initialize2DTextureUShort4444(subTexture2D, kSubImageWidth, kSubImageHeight, nullptr);
872 clearTexture(fbo, subTexture2D, kSubImageColor);
873
874 // Copy the 2D sub-image into the 3D texture
875 copyTexSubImage3D(subTexture2D, kXOffset, kYOffset, kSubImageWidth, kSubImageHeight,
876 kImageDepth);
877
878 // Verify the color wasn't overwritten
879 verifyCopyTexSubImage3D(texture3D, 0, 0, GLColor::green);
880 // Verify the copy succeeded
881 verifyCopyTexSubImage3D(texture3D, kXOffset, kYOffset, kSubImageColor);
882
883 glBindFramebuffer(GL_FRAMEBUFFER, 0);
884 glBindTexture(GL_TEXTURE_2D, 0);
885 glBindTexture(GL_TEXTURE_3D, 0);
886 }
887
888 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
889 // tests should be run against.
890 ANGLE_INSTANTIATE_TEST(CopyTexImageTest,
891 ES2_D3D9(),
892 ES2_D3D11(),
893 ES2_D3D11_PRESENT_PATH_FAST(),
894 ES2_METAL(),
895 ES2_OPENGL(),
896 ES2_OPENGLES(),
897 ES2_VULKAN(),
898 ES3_VULKAN(),
899 WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGL()),
900 WithEmulateCopyTexImage2DFromRenderbuffers(ES2_OPENGLES()));
901
902 ANGLE_INSTANTIATE_TEST(CopyTexImageTestES3,
903 ANGLE_ALL_TEST_PLATFORMS_ES3,
904 WithEmulateCopyTexImage2DFromRenderbuffers(ES3_OPENGL()),
905 WithEmulateCopyTexImage2DFromRenderbuffers(ES3_OPENGLES()));
906 } // namespace angle
907