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 // 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)634 void CopyTexImageTestES3::initialize3DTexture(GLTexture &texture,
635 const GLsizei imageWidth,
636 const GLsizei imageHeight,
637 const GLsizei imageDepth,
638 const GLColor *textureData)
639 {
640 glBindTexture(GL_TEXTURE_3D, texture);
641 glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, imageWidth, imageHeight, imageDepth, 0, GL_RGBA,
642 GL_UNSIGNED_BYTE, textureData);
643 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
644 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
645 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
646 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
647 glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
648 }
649
initialize2DTexture(GLTexture & texture,const GLsizei imageWidth,const GLsizei imageHeight,const GLColor * textureData)650 void CopyTexImageTestES3::initialize2DTexture(GLTexture &texture,
651 const GLsizei imageWidth,
652 const GLsizei imageHeight,
653 const GLColor *textureData)
654 {
655 glBindTexture(GL_TEXTURE_2D, texture);
656 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
657 textureData);
658 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
659 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
660 }
661
initialize2DTextureUShort4444(GLTexture & texture,const GLsizei imageWidth,const GLsizei imageHeight,const GLColor * textureData)662 void CopyTexImageTestES3::initialize2DTextureUShort4444(GLTexture &texture,
663 const GLsizei imageWidth,
664 const GLsizei imageHeight,
665 const GLColor *textureData)
666 {
667 glBindTexture(GL_TEXTURE_2D, texture);
668 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageWidth, imageHeight, 0, GL_RGBA,
669 GL_UNSIGNED_SHORT_4_4_4_4, textureData);
670 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
671 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
672 }
673
fillTexture(std::vector<GLColor> & texture,const GLColor color)674 void CopyTexImageTestES3::fillTexture(std::vector<GLColor> &texture, const GLColor color)
675 {
676 for (auto &texel : texture)
677 {
678 texel = color;
679 }
680 }
681
clearTexture(GLFramebuffer & fbo,GLTexture & texture,const GLColor color)682 void CopyTexImageTestES3::clearTexture(GLFramebuffer &fbo, GLTexture &texture, const GLColor color)
683 {
684 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
685 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
686 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
687 glClearColor(color.R, color.G, color.B, color.A);
688 glClear(GL_COLOR_BUFFER_BIT);
689 EXPECT_PIXEL_COLOR_EQ(0, 0, color);
690 }
691
copyTexSubImage3D(GLTexture & subTexture2D,const GLint xOffset,const GLint yOffset,const GLsizei subImageWidth,const GLsizei subImageHeight,const GLsizei imageDepth)692 void CopyTexImageTestES3::copyTexSubImage3D(GLTexture &subTexture2D,
693 const GLint xOffset,
694 const GLint yOffset,
695 const GLsizei subImageWidth,
696 const GLsizei subImageHeight,
697 const GLsizei imageDepth)
698 {
699 // Copy the 2D sub-image into the 3D texture
700 for (int currLayer = 0; currLayer < imageDepth; ++currLayer)
701 {
702 // Bind the 2D texture to GL_COLOR_ATTACHMENT0
703 glFramebufferTexture2D(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D,
704 subTexture2D, 0);
705 ASSERT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
706 glCopyTexSubImage3D(GL_TEXTURE_3D, 0, xOffset, yOffset, currLayer, 0, 0, subImageWidth,
707 subImageHeight);
708 ASSERT_GL_NO_ERROR();
709 }
710 }
711
verifyCopyTexSubImage3D(GLTexture & texture3D,const GLint xOffset,const GLint yOffset,const GLColor subImageColor)712 void CopyTexImageTestES3::verifyCopyTexSubImage3D(GLTexture &texture3D,
713 const GLint xOffset,
714 const GLint yOffset,
715 const GLColor subImageColor)
716 {
717 // Bind to an FBO to check the copy was successful
718 for (int currLayer = 0; currLayer < kImageDepth; ++currLayer)
719 {
720 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0, currLayer);
721 ASSERT_GL_NO_ERROR();
722 EXPECT_PIXEL_COLOR_EQ(xOffset, yOffset, subImageColor);
723 }
724 }
725
726 // Test glCopyTexSubImage3D with initialized texture data
727 TEST_P(CopyTexImageTestES3, 3DSubImageRawTextureData)
728 {
729 // Texture data
730 std::vector<GLColor> textureData(kImageWidth * kImageHeight * kImageDepth);
731
732 // Fill the textures with color
733 fillTexture(textureData, GLColor::red);
734
735 GLFramebuffer fbo;
736 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
737
738 GLTexture texture3D;
739 initialize3DTexture(texture3D, kImageWidth, kImageHeight, kImageDepth, textureData.data());
740
741 // The 2D texture that will be the sub-image copied into the destination texture
742 GLTexture subTexture2D;
743 initialize2DTexture(subTexture2D, kSubImageWidth, kSubImageHeight, nullptr);
744 clearTexture(fbo, subTexture2D, kSubImageColor);
745
746 // Copy the 2D subimage into the 3D texture
747 copyTexSubImage3D(subTexture2D, kXOffset, kYOffset, kSubImageWidth, kSubImageHeight,
748 kImageDepth);
749
750 // Verify the color wasn't overwritten
751 verifyCopyTexSubImage3D(texture3D, 0, 0, GLColor::red);
752 // Verify the copy succeeded
753 verifyCopyTexSubImage3D(texture3D, kXOffset, kYOffset, kSubImageColor);
754
755 glBindFramebuffer(GL_FRAMEBUFFER, 0);
756 glBindTexture(GL_TEXTURE_2D, 0);
757 glBindTexture(GL_TEXTURE_3D, 0);
758 }
759
760 // Test glCopyTexSubImage3D with initialized texture data that was drawn to
761 TEST_P(CopyTexImageTestES3, 3DSubImageDrawTextureData)
762 {
763 // TODO(anglebug.com/3801)
764 ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11());
765
766 GLFramebuffer fbo;
767 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
768
769 // The 3D texture we will copy the sub-image into
770 GLTexture texture3D;
771 initialize3DTexture(texture3D, kImageWidth, kImageHeight, kImageDepth, nullptr);
772
773 // Draw to each layer in the 3D texture
774 for (int currLayer = 0; currLayer < kImageDepth; ++currLayer)
775 {
776 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
777 glFramebufferTextureLayer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0,
778 currLayer);
779 glFramebufferTextureLayer(GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0,
780 currLayer);
781 ASSERT_GL_NO_ERROR();
782 glUseProgram(greenProgram);
783 drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
784 ASSERT_GL_NO_ERROR();
785 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
786 }
787
788 // The 2D texture that will be the sub-image copied into the destination texture
789 GLTexture subTexture2D;
790 initialize2DTexture(subTexture2D, kSubImageWidth, kSubImageHeight, nullptr);
791 clearTexture(fbo, subTexture2D, kSubImageColor);
792
793 // Copy the 2D sub-image into the 3D texture
794 copyTexSubImage3D(subTexture2D, kXOffset, kYOffset, kSubImageWidth, kSubImageHeight,
795 kImageDepth);
796
797 // Verify the color wasn't overwritten
798 verifyCopyTexSubImage3D(texture3D, 0, 0, GLColor::green);
799 // Verify the copy succeeded
800 verifyCopyTexSubImage3D(texture3D, kXOffset, kYOffset, kSubImageColor);
801
802 glBindFramebuffer(GL_FRAMEBUFFER, 0);
803 glBindTexture(GL_TEXTURE_2D, 0);
804 glBindTexture(GL_TEXTURE_3D, 0);
805 }
806
807 // Test glCopyTexSubImage3D with mismatched texture formats
808 TEST_P(CopyTexImageTestES3, 3DSubImageDrawMismatchedTextureTypes)
809 {
810 // TODO(anglebug.com/3801)
811 ANGLE_SKIP_TEST_IF(IsWindows() && IsD3D11());
812
813 GLFramebuffer fbo;
814 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
815
816 // The 3D texture we will copy the sub-image into
817 GLTexture texture3D;
818 initialize3DTexture(texture3D, kImageWidth, kImageHeight, kImageDepth, nullptr);
819
820 // Draw to each layer in the 3D texture
821 for (int currLayer = 0; currLayer < kImageDepth; ++currLayer)
822 {
823 ANGLE_GL_PROGRAM(greenProgram, essl1_shaders::vs::Simple(), essl1_shaders::fs::Green());
824 glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, texture3D, 0, currLayer);
825 ASSERT_GL_NO_ERROR();
826 glUseProgram(greenProgram);
827 drawQuad(greenProgram.get(), std::string(essl1_shaders::PositionAttrib()), 0.0f);
828 ASSERT_GL_NO_ERROR();
829 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
830 }
831
832 // The 2D texture that will be the sub-image copied into the destination texture
833 GLTexture subTexture2D;
834 initialize2DTextureUShort4444(subTexture2D, kSubImageWidth, kSubImageHeight, nullptr);
835 clearTexture(fbo, subTexture2D, kSubImageColor);
836
837 // Copy the 2D sub-image into the 3D texture
838 copyTexSubImage3D(subTexture2D, kXOffset, kYOffset, kSubImageWidth, kSubImageHeight,
839 kImageDepth);
840
841 // Verify the color wasn't overwritten
842 verifyCopyTexSubImage3D(texture3D, 0, 0, GLColor::green);
843 // Verify the copy succeeded
844 verifyCopyTexSubImage3D(texture3D, kXOffset, kYOffset, kSubImageColor);
845
846 glBindFramebuffer(GL_FRAMEBUFFER, 0);
847 glBindTexture(GL_TEXTURE_2D, 0);
848 glBindTexture(GL_TEXTURE_3D, 0);
849 }
850
851 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
852 // tests should be run against.
853 ANGLE_INSTANTIATE_TEST(CopyTexImageTest,
854 ES2_D3D9(),
855 ES2_D3D11(),
856 ES2_D3D11_PRESENT_PATH_FAST(),
857 ES2_METAL(),
858 ES2_OPENGL(),
859 ES2_OPENGLES(),
860 ES2_VULKAN(),
861 ES3_VULKAN());
862
863 ANGLE_INSTANTIATE_TEST_ES3(CopyTexImageTestES3);
864 } // namespace angle
865