xref: /aosp_15_r20/external/angle/src/tests/gl_tests/WebGLFramebufferTest.cpp (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
1 //
2 // Copyright 2017 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 // WebGLFramebufferTest.cpp : Framebuffer tests for GL_ANGLE_webgl_compatibility.
8 // Based on WebGL 1 test renderbuffers/framebuffer-object-attachment.
9 
10 #include "test_utils/ANGLETest.h"
11 
12 #include "test_utils/gl_raii.h"
13 
14 namespace angle
15 {
16 
17 class WebGLFramebufferTest : public ANGLETest<>
18 {
19   protected:
WebGLFramebufferTest()20     WebGLFramebufferTest()
21     {
22         setWindowWidth(128);
23         setWindowHeight(128);
24         setConfigRedBits(8);
25         setConfigGreenBits(8);
26         setConfigBlueBits(8);
27         setConfigAlphaBits(8);
28         setWebGLCompatibilityEnabled(true);
29     }
30 
31     void drawUByteColorQuad(GLuint program, GLint uniformLoc, const GLColor &color);
32     void testDepthStencilDepthStencil(GLint width, GLint height);
33     void testDepthStencilRenderbuffer(GLint width,
34                                       GLint height,
35                                       GLRenderbuffer *colorBuffer,
36                                       GLbitfield allowedStatuses);
37     void testRenderingAndReading(GLuint program);
38     void testUsingIncompleteFramebuffer(GLenum depthFormat, GLenum depthAttachment);
39     void testDrawingMissingAttachment();
40 };
41 
42 constexpr GLint ALLOW_COMPLETE              = 0x1;
43 constexpr GLint ALLOW_UNSUPPORTED           = 0x2;
44 constexpr GLint ALLOW_INCOMPLETE_ATTACHMENT = 0x4;
45 
checkFramebufferForAllowedStatuses(GLbitfield allowedStatuses)46 void checkFramebufferForAllowedStatuses(GLbitfield allowedStatuses)
47 {
48     // If the framebuffer is in an error state for multiple reasons,
49     // we can't guarantee which one will be reported.
50     GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
51     bool statusAllowed =
52         ((allowedStatuses & ALLOW_COMPLETE) && (status == GL_FRAMEBUFFER_COMPLETE)) ||
53         ((allowedStatuses & ALLOW_UNSUPPORTED) && (status == GL_FRAMEBUFFER_UNSUPPORTED)) ||
54         ((allowedStatuses & ALLOW_INCOMPLETE_ATTACHMENT) &&
55          (status == GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
56     EXPECT_TRUE(statusAllowed);
57 }
58 
checkBufferBits(GLenum attachment0,GLenum attachment1)59 void checkBufferBits(GLenum attachment0, GLenum attachment1)
60 {
61     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
62         return;
63 
64     bool haveDepthBuffer =
65         attachment0 == GL_DEPTH_ATTACHMENT || attachment0 == GL_DEPTH_STENCIL_ATTACHMENT ||
66         attachment1 == GL_DEPTH_ATTACHMENT || attachment1 == GL_DEPTH_STENCIL_ATTACHMENT;
67     bool haveStencilBuffer =
68         attachment0 == GL_STENCIL_ATTACHMENT || attachment0 == GL_DEPTH_STENCIL_ATTACHMENT ||
69         attachment1 == GL_STENCIL_ATTACHMENT || attachment1 == GL_DEPTH_STENCIL_ATTACHMENT;
70 
71     GLint redBits     = 0;
72     GLint greenBits   = 0;
73     GLint blueBits    = 0;
74     GLint alphaBits   = 0;
75     GLint depthBits   = 0;
76     GLint stencilBits = 0;
77     glGetIntegerv(GL_RED_BITS, &redBits);
78     glGetIntegerv(GL_GREEN_BITS, &greenBits);
79     glGetIntegerv(GL_BLUE_BITS, &blueBits);
80     glGetIntegerv(GL_ALPHA_BITS, &alphaBits);
81     glGetIntegerv(GL_DEPTH_BITS, &depthBits);
82     glGetIntegerv(GL_STENCIL_BITS, &stencilBits);
83 
84     EXPECT_GE(redBits + greenBits + blueBits + alphaBits, 16);
85 
86     if (haveDepthBuffer)
87         EXPECT_GE(depthBits, 16);
88     else
89         EXPECT_EQ(0, depthBits);
90 
91     if (haveStencilBuffer)
92         EXPECT_GE(stencilBits, 8);
93     else
94         EXPECT_EQ(0, stencilBits);
95 }
96 
97 // Tests that certain required combinations work in WebGL compatiblity.
TEST_P(WebGLFramebufferTest,TestFramebufferRequiredCombinations)98 TEST_P(WebGLFramebufferTest, TestFramebufferRequiredCombinations)
99 {
100     // Per discussion with the OpenGL ES working group, the following framebuffer attachment
101     // combinations are required to work in all WebGL implementations:
102     // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
103     // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16
104     // renderbuffer
105     // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL
106     // renderbuffer
107 
108     GLFramebuffer fbo;
109     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
110 
111     constexpr int width  = 64;
112     constexpr int height = 64;
113 
114     // 1. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
115     GLTexture texture;
116     glBindTexture(GL_TEXTURE_2D, texture);
117     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
118     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
119     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
120     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
121     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
122     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
123     EXPECT_GL_NO_ERROR();
124     checkFramebufferForAllowedStatuses(ALLOW_COMPLETE);
125     checkBufferBits(GL_NONE, GL_NONE);
126 
127     // 2. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16
128     // renderbuffer
129     GLRenderbuffer renderbuffer;
130     glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer);
131     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
132     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, renderbuffer);
133     EXPECT_GL_NO_ERROR();
134     checkFramebufferForAllowedStatuses(ALLOW_COMPLETE);
135     checkBufferBits(GL_DEPTH_ATTACHMENT, GL_NONE);
136     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
137 
138     if (getClientMajorVersion() == 2)
139     {
140         // 3. COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT =
141         // DEPTH_STENCIL renderbuffer
142         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
143         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
144                                   renderbuffer);
145         EXPECT_GL_NO_ERROR();
146         checkFramebufferForAllowedStatuses(ALLOW_COMPLETE);
147         checkBufferBits(GL_DEPTH_STENCIL_ATTACHMENT, GL_NONE);
148     }
149 }
150 
testAttachment(GLint width,GLint height,GLRenderbuffer * colorBuffer,GLenum attachment,GLRenderbuffer * buffer,GLbitfield allowedStatuses)151 void testAttachment(GLint width,
152                     GLint height,
153                     GLRenderbuffer *colorBuffer,
154                     GLenum attachment,
155                     GLRenderbuffer *buffer,
156                     GLbitfield allowedStatuses)
157 {
158     GLFramebuffer fbo;
159     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
160     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, *colorBuffer);
161     glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment, GL_RENDERBUFFER, *buffer);
162     EXPECT_GL_NO_ERROR();
163     checkFramebufferForAllowedStatuses(allowedStatuses);
164     if ((allowedStatuses & ALLOW_COMPLETE) == 0)
165     {
166         std::vector<uint8_t> tempBuffer(width * height * 4);
167 
168         glClear(GL_COLOR_BUFFER_BIT);
169         EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
170         glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, tempBuffer.data());
171         EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
172     }
173     checkBufferBits(attachment, GL_NONE);
174 }
175 
testAttachments(GLRenderbuffer & colorBuffer,GLenum attachment0,GLRenderbuffer & buffer0,GLenum attachment1,GLRenderbuffer & buffer1,GLbitfield allowedStatuses)176 void testAttachments(GLRenderbuffer &colorBuffer,
177                      GLenum attachment0,
178                      GLRenderbuffer &buffer0,
179                      GLenum attachment1,
180                      GLRenderbuffer &buffer1,
181                      GLbitfield allowedStatuses)
182 {
183     GLFramebuffer fbo;
184     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
185     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
186     glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment0, GL_RENDERBUFFER, buffer0);
187     EXPECT_GL_NO_ERROR();
188     glFramebufferRenderbuffer(GL_FRAMEBUFFER, attachment1, GL_RENDERBUFFER, buffer1);
189     EXPECT_GL_NO_ERROR();
190     checkFramebufferForAllowedStatuses(allowedStatuses);
191     checkBufferBits(attachment0, attachment1);
192 }
193 
testColorRenderbuffer(GLint width,GLint height,GLenum internalformat,GLbitfield allowedStatuses)194 void testColorRenderbuffer(GLint width,
195                            GLint height,
196                            GLenum internalformat,
197                            GLbitfield allowedStatuses)
198 {
199     GLRenderbuffer colorBuffer;
200     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
201     glRenderbufferStorage(GL_RENDERBUFFER, internalformat, width, height);
202     EXPECT_GL_NO_ERROR();
203     testAttachment(width, height, &colorBuffer, GL_COLOR_ATTACHMENT0, &colorBuffer,
204                    allowedStatuses);
205 }
206 
getRenderbufferParameter(GLenum paramName)207 GLint getRenderbufferParameter(GLenum paramName)
208 {
209     GLint paramValue = 0;
210     glGetRenderbufferParameteriv(GL_RENDERBUFFER, paramName, &paramValue);
211     return paramValue;
212 }
213 
drawUByteColorQuad(GLuint program,GLint uniformLoc,const GLColor & color)214 void WebGLFramebufferTest::drawUByteColorQuad(GLuint program,
215                                               GLint uniformLoc,
216                                               const GLColor &color)
217 {
218     Vector4 vecColor = color.toNormalizedVector();
219     glUseProgram(program);
220     glUniform4fv(uniformLoc, 1, vecColor.data());
221     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
222 }
223 
testDepthStencilDepthStencil(GLint width,GLint height)224 void WebGLFramebufferTest::testDepthStencilDepthStencil(GLint width, GLint height)
225 {
226     if (width == 0 || height == 0)
227     {
228         return;
229     }
230 
231     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::UniformColor());
232     GLint uniformLoc = glGetUniformLocation(program, essl1_shaders::ColorUniform());
233     ASSERT_NE(-1, uniformLoc);
234 
235     struct TestInfo
236     {
237         GLenum firstFormat;
238         GLenum firstAttach;
239         GLenum secondFormat;
240         GLenum secondAttach;
241     };
242 
243     TestInfo tests[2] = {
244         {GL_DEPTH_COMPONENT16, GL_DEPTH_ATTACHMENT, GL_DEPTH_STENCIL, GL_DEPTH_STENCIL_ATTACHMENT},
245         {GL_DEPTH_STENCIL, GL_DEPTH_STENCIL_ATTACHMENT, GL_DEPTH_COMPONENT16, GL_DEPTH_ATTACHMENT}};
246 
247     for (const TestInfo &test : tests)
248     {
249         for (GLint opIndex = 0; opIndex < 2; ++opIndex)
250         {
251             GLFramebuffer fbo;
252             GLTexture tex;
253             GLRenderbuffer firstRb;
254 
255             // test: firstFormat vs secondFormat with unbind or delete.
256 
257             glBindFramebuffer(GL_FRAMEBUFFER, fbo);
258             // attach texture as color
259             glBindTexture(GL_TEXTURE_2D, tex);
260             glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
261                          nullptr);
262             glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
263 
264             // attach first
265             glBindRenderbuffer(GL_RENDERBUFFER, firstRb);
266             glRenderbufferStorage(GL_RENDERBUFFER, test.firstFormat, width, height);
267             glFramebufferRenderbuffer(GL_FRAMEBUFFER, test.firstAttach, GL_RENDERBUFFER, firstRb);
268 
269             EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
270 
271             // TODO(jmadill): Remove clear - this should be implicit in WebGL_
272             glClear(GL_DEPTH_BUFFER_BIT);
273 
274             glEnable(GL_DEPTH_TEST);
275             // Test it works
276             drawUByteColorQuad(program, uniformLoc, GLColor::green);
277             // should not draw since DEPTH_FUNC == LESS
278             drawUByteColorQuad(program, uniformLoc, GLColor::red);
279             // should be green
280             EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
281 
282             GLuint secondRb = 0;
283             glGenRenderbuffers(1, &secondRb);
284 
285             // attach second
286             glBindRenderbuffer(GL_RENDERBUFFER, secondRb);
287             glRenderbufferStorage(GL_RENDERBUFFER, test.secondFormat, width, height);
288             glFramebufferRenderbuffer(GL_FRAMEBUFFER, test.secondAttach, GL_RENDERBUFFER, secondRb);
289 
290             if (opIndex == 0)
291             {
292                 // now delete it
293                 glDeleteRenderbuffers(1, &secondRb);
294                 secondRb = 0;
295             }
296             else
297             {
298                 // unbind it
299                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, test.secondAttach, GL_RENDERBUFFER, 0);
300             }
301 
302             // If the first attachment is not restored this may fail
303             EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
304             EXPECT_GL_NO_ERROR();
305 
306             // If the first attachment is not restored this may fail.
307             glClear(GL_DEPTH_BUFFER_BIT);
308             drawUByteColorQuad(program, uniformLoc, GLColor::green);
309             // should not draw since DEPTH_FUNC == LESS
310             drawUByteColorQuad(program, uniformLoc, GLColor::red);
311             // should be green
312             EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::green);
313             glDisable(GL_DEPTH_TEST);
314 
315             if (opIndex == 1)
316             {
317                 glDeleteRenderbuffers(1, &secondRb);
318                 secondRb = 0;
319             }
320         }
321     }
322     EXPECT_GL_NO_ERROR();
323 }
324 
testDepthStencilRenderbuffer(GLint width,GLint height,GLRenderbuffer * colorBuffer,GLbitfield allowedStatuses)325 void WebGLFramebufferTest::testDepthStencilRenderbuffer(GLint width,
326                                                         GLint height,
327                                                         GLRenderbuffer *colorBuffer,
328                                                         GLbitfield allowedStatuses)
329 {
330     GLRenderbuffer depthStencilBuffer;
331     glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
332     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
333     EXPECT_GL_NO_ERROR();
334 
335     // OpenGL itself doesn't seem to guarantee that e.g. a 2 x 0
336     // renderbuffer will report 2 for its width when queried.
337     if (!(height == 0 && width > 0))
338     {
339         EXPECT_EQ(width, getRenderbufferParameter(GL_RENDERBUFFER_WIDTH));
340     }
341     if (!(width == 0 && height > 0))
342     {
343         EXPECT_EQ(height, getRenderbufferParameter(GL_RENDERBUFFER_HEIGHT));
344     }
345     EXPECT_EQ(GL_DEPTH_STENCIL, getRenderbufferParameter(GL_RENDERBUFFER_INTERNAL_FORMAT));
346     EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_RED_SIZE));
347     EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_GREEN_SIZE));
348     EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_BLUE_SIZE));
349     EXPECT_EQ(0, getRenderbufferParameter(GL_RENDERBUFFER_ALPHA_SIZE));
350 
351     // Avoid verifying these for zero-sized renderbuffers for the time
352     // being since it appears that even OpenGL doesn't guarantee them.
353     if (width > 0 && height > 0)
354     {
355         EXPECT_GT(getRenderbufferParameter(GL_RENDERBUFFER_DEPTH_SIZE), 0);
356         EXPECT_GT(getRenderbufferParameter(GL_RENDERBUFFER_STENCIL_SIZE), 0);
357     }
358     EXPECT_GL_NO_ERROR();
359     testAttachment(width, height, colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, &depthStencilBuffer,
360                    allowedStatuses);
361     testDepthStencilDepthStencil(width, height);
362 }
363 
364 // Test various attachment combinations with WebGL framebuffers.
TEST_P(WebGLFramebufferTest,TestAttachments)365 TEST_P(WebGLFramebufferTest, TestAttachments)
366 {
367     // GL_DEPTH_STENCIL renderbuffer format is only valid for WebGL1
368     ANGLE_SKIP_TEST_IF(getClientMajorVersion() != 2);
369 
370     for (GLint width = 2; width <= 2; width += 2)
371     {
372         for (GLint height = 2; height <= 2; height += 2)
373         {
374             // Dimensions width x height.
375             GLRenderbuffer colorBuffer;
376             glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
377             glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, width, height);
378             EXPECT_GL_NO_ERROR();
379 
380             GLRenderbuffer depthBuffer;
381             glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
382             glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
383             EXPECT_GL_NO_ERROR();
384 
385             GLRenderbuffer stencilBuffer;
386             glBindRenderbuffer(GL_RENDERBUFFER, stencilBuffer);
387             glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height);
388             EXPECT_GL_NO_ERROR();
389 
390             GLRenderbuffer depthStencilBuffer;
391             glBindRenderbuffer(GL_RENDERBUFFER, depthStencilBuffer);
392             glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, width, height);
393             EXPECT_GL_NO_ERROR();
394 
395             GLbitfield allowedStatusForGoodCase =
396                 (width == 0 || height == 0) ? ALLOW_INCOMPLETE_ATTACHMENT : ALLOW_COMPLETE;
397 
398             // some cases involving stencil seem to be implementation-dependent
399             GLbitfield allowedStatusForImplDependentCase =
400                 allowedStatusForGoodCase | ALLOW_UNSUPPORTED;
401 
402             // Attach depth using DEPTH_ATTACHMENT.
403             testAttachment(width, height, &colorBuffer, GL_DEPTH_ATTACHMENT, &depthBuffer,
404                            allowedStatusForGoodCase);
405 
406             // Attach depth using STENCIL_ATTACHMENT.
407             testAttachment(width, height, &colorBuffer, GL_STENCIL_ATTACHMENT, &depthBuffer,
408                            ALLOW_INCOMPLETE_ATTACHMENT);
409 
410             // Attach depth using DEPTH_STENCIL_ATTACHMENT.
411             testAttachment(width, height, &colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, &depthBuffer,
412                            ALLOW_INCOMPLETE_ATTACHMENT);
413 
414             // Attach stencil using STENCIL_ATTACHMENT.
415             testAttachment(width, height, &colorBuffer, GL_STENCIL_ATTACHMENT, &stencilBuffer,
416                            allowedStatusForImplDependentCase);
417 
418             // Attach stencil using DEPTH_ATTACHMENT.
419             testAttachment(width, height, &colorBuffer, GL_DEPTH_ATTACHMENT, &stencilBuffer,
420                            ALLOW_INCOMPLETE_ATTACHMENT);
421 
422             // Attach stencil using DEPTH_STENCIL_ATTACHMENT.
423             testAttachment(width, height, &colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, &stencilBuffer,
424                            ALLOW_INCOMPLETE_ATTACHMENT);
425 
426             // Attach depthStencil using DEPTH_STENCIL_ATTACHMENT.
427             testAttachment(width, height, &colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT,
428                            &depthStencilBuffer, allowedStatusForGoodCase);
429 
430             // Attach depthStencil using DEPTH_ATTACHMENT.
431             testAttachment(width, height, &colorBuffer, GL_DEPTH_ATTACHMENT, &depthStencilBuffer,
432                            ALLOW_INCOMPLETE_ATTACHMENT);
433 
434             // Attach depthStencil using STENCIL_ATTACHMENT.
435             testAttachment(width, height, &colorBuffer, GL_STENCIL_ATTACHMENT, &depthStencilBuffer,
436                            ALLOW_INCOMPLETE_ATTACHMENT);
437 
438             GLbitfield allowedStatusForConflictedAttachment =
439                 (width == 0 || height == 0) ? ALLOW_UNSUPPORTED | ALLOW_INCOMPLETE_ATTACHMENT
440                                             : ALLOW_UNSUPPORTED;
441 
442             // Attach depth, then stencil, causing conflict.
443             testAttachments(colorBuffer, GL_DEPTH_ATTACHMENT, depthBuffer, GL_STENCIL_ATTACHMENT,
444                             stencilBuffer, allowedStatusForConflictedAttachment);
445 
446             // Attach stencil, then depth, causing conflict.
447             testAttachments(colorBuffer, GL_STENCIL_ATTACHMENT, stencilBuffer, GL_DEPTH_ATTACHMENT,
448                             depthBuffer, allowedStatusForConflictedAttachment);
449 
450             // Attach depth, then depthStencil, causing conflict.
451             testAttachments(colorBuffer, GL_DEPTH_ATTACHMENT, depthBuffer,
452                             GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer,
453                             allowedStatusForConflictedAttachment);
454 
455             // Attach depthStencil, then depth, causing conflict.
456             testAttachments(colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer,
457                             GL_DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
458 
459             // Attach stencil, then depthStencil, causing conflict.
460             testAttachments(colorBuffer, GL_DEPTH_ATTACHMENT, depthBuffer,
461                             GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer,
462                             allowedStatusForConflictedAttachment);
463 
464             // Attach depthStencil, then stencil, causing conflict.
465             testAttachments(colorBuffer, GL_DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer,
466                             GL_STENCIL_ATTACHMENT, stencilBuffer,
467                             allowedStatusForConflictedAttachment);
468 
469             // Attach color renderbuffer with internalformat == RGBA4.
470             testColorRenderbuffer(width, height, GL_RGBA4, allowedStatusForGoodCase);
471 
472             // Attach color renderbuffer with internalformat == RGB5_A1.
473             // This particular format seems to be bugged on NVIDIA Retina. http://crbug.com/635081
474             // TODO(jmadill): Figure out if we can add a format workaround.
475             if (!(IsNVIDIA() && IsMac() && IsOpenGL()))
476             {
477                 testColorRenderbuffer(width, height, GL_RGB5_A1, allowedStatusForGoodCase);
478             }
479 
480             // Attach color renderbuffer with internalformat == RGB565.
481             testColorRenderbuffer(width, height, GL_RGB565, allowedStatusForGoodCase);
482 
483             // Create and attach depthStencil renderbuffer.
484             testDepthStencilRenderbuffer(width, height, &colorBuffer, allowedStatusForGoodCase);
485         }
486     }
487 }
488 
tryDepth(GLRenderbuffer * depthBuffer,GLenum * depthFormat,GLenum * depthAttachment,GLenum try_format,GLenum try_attachment)489 bool tryDepth(GLRenderbuffer *depthBuffer,
490               GLenum *depthFormat,
491               GLenum *depthAttachment,
492               GLenum try_format,
493               GLenum try_attachment)
494 {
495     if (*depthAttachment != GL_NONE)
496     {
497         // If we've tried once unattach the old one.
498         glFramebufferRenderbuffer(GL_FRAMEBUFFER, *depthAttachment, GL_RENDERBUFFER, 0);
499     }
500     *depthFormat     = try_format;
501     *depthAttachment = try_attachment;
502     glFramebufferRenderbuffer(GL_FRAMEBUFFER, *depthAttachment, GL_RENDERBUFFER, *depthBuffer);
503     glRenderbufferStorage(GL_RENDERBUFFER, *depthFormat, 16, 16);
504     EXPECT_GL_NO_ERROR();
505     return glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE;
506 }
507 
checkValidColorDepthCombination(GLenum * depthFormat,GLenum * depthAttachment)508 bool checkValidColorDepthCombination(GLenum *depthFormat, GLenum *depthAttachment)
509 {
510     GLFramebuffer fbo;
511     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
512     GLRenderbuffer colorBuffer;
513     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
514     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
515     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
516 
517     GLRenderbuffer depthBuffer;
518     glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
519 
520     return tryDepth(&depthBuffer, depthFormat, depthAttachment, GL_DEPTH_COMPONENT16,
521                     GL_DEPTH_ATTACHMENT) ||
522            tryDepth(&depthBuffer, depthFormat, depthAttachment, GL_DEPTH_STENCIL,
523                     GL_DEPTH_STENCIL_ATTACHMENT);
524 }
525 
526 // glCheckFramebufferStatus(GL_FRAMEBUFFER) should be either complete or (unsupported/expected).
checkFramebuffer(GLenum expected)527 void checkFramebuffer(GLenum expected)
528 {
529     GLenum actual = glCheckFramebufferStatus(GL_FRAMEBUFFER);
530     EXPECT_TRUE(actual == expected ||
531                 (expected != GL_FRAMEBUFFER_COMPLETE && actual == GL_FRAMEBUFFER_UNSUPPORTED));
532 }
533 
testRenderingAndReading(GLuint program)534 void WebGLFramebufferTest::testRenderingAndReading(GLuint program)
535 {
536     EXPECT_GL_NO_ERROR();
537 
538     // drawArrays with incomplete framebuffer
539     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
540     EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
541 
542     // readPixels from incomplete framebuffer
543     std::vector<uint8_t> incompleteBuffer(4);
544     glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, incompleteBuffer.data());
545     EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
546 
547     // copyTexImage and copyTexSubImage can be either INVALID_FRAMEBUFFER_OPERATION because
548     // the framebuffer is invalid OR INVALID_OPERATION because in the case of no attachments
549     // the framebuffer is not of a compatible type.
550 
551     // copyTexSubImage2D from incomplete framebuffer
552     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
553     GLenum error = glGetError();
554     EXPECT_TRUE(error == GL_INVALID_FRAMEBUFFER_OPERATION || error == GL_INVALID_OPERATION);
555 
556     // copyTexImage2D from incomplete framebuffer
557     glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, 1, 1, 0);
558     error = glGetError();
559     EXPECT_TRUE(error == GL_INVALID_FRAMEBUFFER_OPERATION || error == GL_INVALID_OPERATION);
560 
561     // clear with incomplete framebuffer
562     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
563     EXPECT_GL_ERROR(GL_INVALID_FRAMEBUFFER_OPERATION);
564 }
565 
566 // Test drawing or reading from an incomplete framebuffer
testUsingIncompleteFramebuffer(GLenum depthFormat,GLenum depthAttachment)567 void WebGLFramebufferTest::testUsingIncompleteFramebuffer(GLenum depthFormat,
568                                                           GLenum depthAttachment)
569 {
570     // Simple draw program.
571     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
572 
573     GLFramebuffer fbo;
574     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
575     GLRenderbuffer colorBuffer;
576     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
577     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
578     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
579 
580     GLRenderbuffer depthBuffer;
581     glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
582     glFramebufferRenderbuffer(GL_FRAMEBUFFER, depthAttachment, GL_RENDERBUFFER, depthBuffer);
583     glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16);
584     EXPECT_GL_NO_ERROR();
585     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
586 
587     // We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES
588     // 2.0
589     glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 32, 16);
590     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
591 
592     // Drawing or reading from an incomplete framebuffer should generate
593     // INVALID_FRAMEBUFFER_OPERATION.
594     testRenderingAndReading(program);
595 
596     GLFramebuffer fbo2;
597     glBindFramebuffer(GL_FRAMEBUFFER, fbo2);
598     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
599 
600     // Drawing or reading from an incomplete framebuffer should generate
601     // INVALID_FRAMEBUFFER_OPERATION.
602     testRenderingAndReading(program);
603 
604     GLRenderbuffer colorBuffer2;
605     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer2);
606     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer2);
607     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 0, 0);
608 
609     // Drawing or reading from an incomplete framebuffer should generate
610     // INVALID_FRAMEBUFFER_OPERATION.
611     testRenderingAndReading(program);
612 }
613 
testFramebufferIncompleteAttachment(GLenum depthFormat)614 void testFramebufferIncompleteAttachment(GLenum depthFormat)
615 {
616     GLFramebuffer fbo;
617     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
618     GLRenderbuffer colorBuffer;
619     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
620     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
621     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
622     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
623 
624     // Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0
625     // 4.4.5).
626     glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16);
627     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
628 
629     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
630     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
631 
632     // 0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5).
633     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 0, 0);
634     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
635 
636     EXPECT_GL_NO_ERROR();
637 }
638 
639 // No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5).
testFramebufferIncompleteMissingAttachment()640 void testFramebufferIncompleteMissingAttachment()
641 {
642     GLFramebuffer fbo;
643     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
644     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
645 
646     GLRenderbuffer colorBuffer;
647     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
648     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
649     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
650     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
651 
652     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
653     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
654 
655     EXPECT_GL_NO_ERROR();
656 }
657 
658 // Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5).
testFramebufferIncompleteDimensions(GLenum depthFormat,GLenum depthAttachment)659 void testFramebufferIncompleteDimensions(GLenum depthFormat, GLenum depthAttachment)
660 {
661     GLFramebuffer fbo;
662     glBindFramebuffer(GL_FRAMEBUFFER, fbo);
663     GLRenderbuffer colorBuffer;
664     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
665     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
666     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
667 
668     GLRenderbuffer depthBuffer;
669     glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
670     glFramebufferRenderbuffer(GL_FRAMEBUFFER, depthAttachment, GL_RENDERBUFFER, depthBuffer);
671     glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16);
672     EXPECT_GL_NO_ERROR();
673     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
674 
675     glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 32, 16);
676     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
677     glRenderbufferStorage(GL_RENDERBUFFER, depthFormat, 16, 16);
678     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
679     glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
680     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 32);
681     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
682     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA4, 16, 16);
683     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
684     EXPECT_GL_NO_ERROR();
685 
686     GLTexture tex;
687     glBindTexture(GL_TEXTURE_2D, tex);
688     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
689     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, tex, 0);
690     EXPECT_GL_NO_ERROR();
691     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
692     {
693         return;
694     }
695 
696     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
697     checkFramebuffer(GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
698     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 16, 16, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
699     checkFramebuffer(GL_FRAMEBUFFER_COMPLETE);
700 
701     EXPECT_GL_NO_ERROR();
702 }
703 
704 class NoColorFB final : angle::NonCopyable
705 {
706   public:
NoColorFB(int size)707     NoColorFB(int size)
708     {
709         glBindFramebuffer(GL_FRAMEBUFFER, mFBO);
710 
711         // The only scenario we can verify is an attempt to read or copy
712         // from a missing color attachment while the framebuffer is still
713         // complete.
714         glBindRenderbuffer(GL_RENDERBUFFER, mDepthBuffer);
715         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER,
716                                   mDepthBuffer);
717         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, size, size);
718 
719         // After depth renderbuffer setup
720         EXPECT_GL_NO_ERROR();
721 
722         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
723         {
724             // Unable to allocate a framebuffer with just a depth attachment; this is legal.
725             // Try just a depth/stencil renderbuffer.
726             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, 0);
727 
728             glBindRenderbuffer(GL_RENDERBUFFER, mDepthStencilBuffer);
729             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
730                                       mDepthStencilBuffer);
731             glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_STENCIL, size, size);
732 
733             // After depth+stencil renderbuffer setup
734             EXPECT_GL_NO_ERROR();
735         }
736     }
737 
738   private:
739     GLRenderbuffer mDepthBuffer;
740     GLRenderbuffer mDepthStencilBuffer;
741     GLFramebuffer mFBO;
742 };
743 
744 // Test reading from a missing framebuffer attachment.
TestReadingMissingAttachment(int size)745 void TestReadingMissingAttachment(int size)
746 {
747     // The FBO has no color attachment. ReadPixels, CopyTexImage2D,
748     // and CopyTexSubImage2D should all generate INVALID_OPERATION.
749 
750     // Before ReadPixels from missing attachment
751     std::vector<uint8_t> incompleteBuffer(4);
752     EXPECT_GL_NO_ERROR();
753     glReadPixels(0, 0, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, incompleteBuffer.data());
754     // After ReadPixels from missing attachment
755     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
756 
757     GLTexture tex;
758     glBindTexture(GL_TEXTURE_2D, tex);
759     // Before CopyTexImage2D from missing attachment
760     EXPECT_GL_NO_ERROR();
761     glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, size, size, 0);
762     // After CopyTexImage2D from missing attachment
763     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
764 
765     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
766     // Before CopyTexSubImage2D from missing attachment
767     EXPECT_GL_NO_ERROR();
768     glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, size, size);
769     // After CopyTexSubImage2D from missing attachment
770     EXPECT_GL_ERROR(GL_INVALID_OPERATION);
771 }
772 
773 // Test drawing to a missing framebuffer attachment.
testDrawingMissingAttachment()774 void WebGLFramebufferTest::testDrawingMissingAttachment()
775 {
776     // Simple draw program.
777     ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
778 
779     glClear(GL_COLOR_BUFFER_BIT);
780     EXPECT_GL_NO_ERROR();
781 
782     // try glDrawArrays
783     drawQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
784     EXPECT_GL_NO_ERROR();
785 
786     // try glDrawElements
787     drawIndexedQuad(program, essl1_shaders::PositionAttrib(), 0.5f, 1.0f, true);
788     EXPECT_GL_NO_ERROR();
789 }
790 
791 // Determine if we can attach both color and depth or color and depth_stencil
TEST_P(WebGLFramebufferTest,CheckValidColorDepthCombination)792 TEST_P(WebGLFramebufferTest, CheckValidColorDepthCombination)
793 {
794     GLenum depthFormat     = GL_NONE;
795     GLenum depthAttachment = GL_NONE;
796 
797     if (checkValidColorDepthCombination(&depthFormat, &depthAttachment))
798     {
799         testFramebufferIncompleteDimensions(depthFormat, depthAttachment);
800         testFramebufferIncompleteAttachment(depthFormat);
801         testFramebufferIncompleteMissingAttachment();
802         testUsingIncompleteFramebuffer(depthFormat, depthAttachment);
803 
804         constexpr int size = 16;
805         NoColorFB fb(size);
806         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE)
807         {
808             // The FBO has no color attachment. ReadPixels, CopyTexImage2D,
809             // and CopyTexSubImage2D should all generate INVALID_OPERATION.
810             TestReadingMissingAttachment(size);
811 
812             // The FBO has no color attachment. Clear, DrawArrays,
813             // and DrawElements should not generate an error.
814             testDrawingMissingAttachment();
815         }
816     }
817 }
818 
819 // Test to cover a bug in preserving the texture image index for WebGL framebuffer attachments
TEST_P(WebGLFramebufferTest,TextureAttachmentCommitBug)820 TEST_P(WebGLFramebufferTest, TextureAttachmentCommitBug)
821 {
822     ANGLE_SKIP_TEST_IF(!EnsureGLExtensionEnabled("GL_ANGLE_depth_texture"));
823 
824     GLTexture depthTexture;
825     glBindTexture(GL_TEXTURE_2D, depthTexture);
826     glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, 1, 1, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,
827                  nullptr);
828 
829     GLFramebuffer framebuffer;
830     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
831     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTexture, 0);
832 
833     glCheckFramebufferStatus(GL_FRAMEBUFFER);
834 
835     EXPECT_GL_NO_ERROR();
836 }
837 
838 // Test combinations of ordering in setting the resource format and attaching it to the depth
839 // stencil attacchment.  Covers http://crbug.com/997702
TEST_P(WebGLFramebufferTest,DepthStencilAttachmentOrdering)840 TEST_P(WebGLFramebufferTest, DepthStencilAttachmentOrdering)
841 {
842     constexpr GLsizei kFramebufferSize = 16;
843 
844     GLTexture color;
845     glBindTexture(GL_TEXTURE_2D, color);
846     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kFramebufferSize, kFramebufferSize, 0, GL_RGBA,
847                  GL_UNSIGNED_BYTE, nullptr);
848 
849     GLRenderbuffer depthStencil;
850     glBindRenderbuffer(GL_RENDERBUFFER, depthStencil);
851 
852     // Attach the renderbuffer to the framebuffer when it has no format
853     GLFramebuffer framebuffer;
854     glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
855     glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color, 0);
856     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
857                               depthStencil);
858 
859     // Framebuffer is incomplete because the depth stencil attachment doesn't a format/size
860     EXPECT_GL_NO_ERROR();
861     EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
862                      GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
863 
864     // Set depth stencil attachment to a color format
865     if (EnsureGLExtensionEnabled("GL_OES_rgb8_rgba8"))
866     {
867         glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, kFramebufferSize, kFramebufferSize);
868 
869         // Non-depth stencil format on the depth stencil attachment
870         EXPECT_GL_NO_ERROR();
871         EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
872                          GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
873     }
874 
875     {
876         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, kFramebufferSize,
877                               kFramebufferSize);
878 
879         // Depth-stencil attachment only has a depth format, not complete
880         EXPECT_GL_NO_ERROR();
881         EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER),
882                          GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
883     }
884 
885     if (EnsureGLExtensionEnabled("GL_OES_packed_depth_stencil"))
886     {
887         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, kFramebufferSize,
888                               kFramebufferSize);
889 
890         // Framebuffer should be complete now with a depth-stencil format
891         EXPECT_GL_NO_ERROR();
892         EXPECT_GLENUM_EQ(glCheckFramebufferStatus(GL_FRAMEBUFFER), GL_FRAMEBUFFER_COMPLETE);
893     }
894 }
895 
896 // Only run against WebGL 1 validation, since much was changed in 2.
897 ANGLE_INSTANTIATE_TEST_ES2_AND_ES3(WebGLFramebufferTest);
898 
899 }  // namespace angle
900