b52da154d98014138bbc65588c23a83b8d748a22
[WebKit-https.git] / LayoutTests / fast / canvas / webgl / framebuffer-object-attachment.html
1 <!DOCTYPE html>
2 <html>
3 <head>
4 <meta charset="utf-8">
5 <script src="../../../resources/js-test.js"></script>
6 <script src="resources/webgl-test.js"></script>
7 <script src="resources/webgl-test-utils.js"></script>
8 </head>
9 <body>
10 <div id="description"></div>
11 <div id="console"></div>
12
13 <script>
14 if (window.internals)
15     window.internals.settings.setWebGLErrorsToConsoleEnabled(false);
16
17 var wtu = WebGLTestUtils;
18 var gl;
19 var fbo;
20 var depthBuffer;
21 var stencilBuffer;
22 var depthStencilBuffer;
23 var colorBuffer;
24 var width;
25 var height;
26
27 const ALLOW_COMPLETE              = 0x01;
28 const ALLOW_UNSUPPORTED           = 0x02;
29 const ALLOW_INCOMPLETE_ATTACHMENT = 0x04;
30
31 function checkFramebufferForAllowedStatuses(allowedStatuses)
32 {
33     // If the framebuffer is in an error state for multiple reasons,
34     // we can't guarantee which one will be reported.
35     var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
36     var statusAllowed = ((allowedStatuses & ALLOW_COMPLETE) && (status == gl.FRAMEBUFFER_COMPLETE)) ||
37                         ((allowedStatuses & ALLOW_UNSUPPORTED) && (status == gl.FRAMEBUFFER_UNSUPPORTED)) ||
38                         ((allowedStatuses & ALLOW_INCOMPLETE_ATTACHMENT) && (status == gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT));
39     var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) returned " + status;
40     if (statusAllowed)
41         testPassed(msg);
42     else
43         testFailed(msg);
44 }
45
46 function checkBufferBits(attachment0, attachment1)
47 {
48     if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE)
49       return;
50
51     var haveDepthBuffer = attachment0 == gl.DEPTH_ATTACHMENT ||
52                           attachment0 == gl.DEPTH_STENCIL_ATTACHMENT ||
53                           attachment1 == gl.DEPTH_ATTACHMENT ||
54                           attachment1 == gl.DEPTH_STENCIL_ATTACHMENT;
55     var haveStencilBuffer = attachment0 == gl.STENCIL_ATTACHMENT ||
56                             attachment0 == gl.DEPTH_STENCIL_ATTACHMENT ||
57                             attachment1 == gl.STENCIL_ATTACHMENT ||
58                             attachment1 == gl.DEPTH_STENCIL_ATTACHMENT;
59
60     shouldBeTrue("gl.getParameter(gl.RED_BITS) + gl.getParameter(gl.GREEN_BITS) + gl.getParameter(gl.BLUE_BITS) + gl.getParameter(gl.ALPHA_BITS) >= 16");
61
62     if (haveDepthBuffer)
63       shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) >= 16");
64     else
65       shouldBeTrue("gl.getParameter(gl.DEPTH_BITS) == 0");
66
67     if (haveStencilBuffer)
68       shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) >= 8");
69     else
70       shouldBeTrue("gl.getParameter(gl.STENCIL_BITS) == 0");
71 }
72
73 function testAttachment(attachment, buffer, allowedStatuses)
74 {
75     shouldBeNonNull("fbo = gl.createFramebuffer()");
76     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
77     gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
78     gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment, gl.RENDERBUFFER, buffer);
79     glErrorShouldBe(gl, gl.NO_ERROR);
80     checkFramebufferForAllowedStatuses(allowedStatuses);
81     if ((allowedStatuses & ALLOW_COMPLETE) == 0) {
82         gl.clear(gl.COLOR_BUFFER_BIT);
83         glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION);
84         gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(width * height * 4));
85         glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION);
86     }
87     checkBufferBits(attachment);
88     gl.deleteFramebuffer(fbo);
89 }
90
91 function testAttachments(attachment0, buffer0, attachment1, buffer1, allowedStatuses)
92 {
93     shouldBeNonNull("fbo = gl.createFramebuffer()");
94     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
95     gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
96     gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment0, gl.RENDERBUFFER, buffer0);
97     glErrorShouldBe(gl, gl.NO_ERROR);
98     gl.framebufferRenderbuffer(gl.FRAMEBUFFER, attachment1, gl.RENDERBUFFER, buffer1);
99     glErrorShouldBe(gl, gl.NO_ERROR);
100     checkFramebufferForAllowedStatuses(allowedStatuses);
101     checkBufferBits(attachment0, attachment1);
102     gl.deleteFramebuffer(fbo);
103 }
104
105 function testColorRenderbuffer(internalformat, allowedStatuses)
106 {
107     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
108     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
109     gl.renderbufferStorage(gl.RENDERBUFFER, internalformat, width, height);
110     glErrorShouldBe(gl, gl.NO_ERROR);
111     testAttachment(gl.COLOR_ATTACHMENT0, colorBuffer, allowedStatuses);
112 }
113
114 function testDepthStencilRenderbuffer(allowedStatuses)
115 {
116     shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
117     gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
118     gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
119     glErrorShouldBe(gl, gl.NO_ERROR);
120
121     // OpenGL itself doesn't seem to guarantee that e.g. a 2 x 0
122     // renderbuffer will report 2 for its width when queried.
123     if (!(height == 0 && width > 0))
124         shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_WIDTH)", "width");
125     if (!(width == 0 && height > 0))
126         shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_HEIGHT)", "height");
127     shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_INTERNAL_FORMAT)", "gl.DEPTH_STENCIL");
128     shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_RED_SIZE)", "0");
129     shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_GREEN_SIZE)", "0");
130     shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_BLUE_SIZE)", "0");
131     shouldBe("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_ALPHA_SIZE)", "0");
132     // Avoid verifying these for zero-sized renderbuffers for the time
133     // being since it appears that even OpenGL doesn't guarantee them.
134     if (width > 0 && height > 0) {
135         shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_DEPTH_SIZE) > 0");
136         shouldBeTrue("gl.getRenderbufferParameter(gl.RENDERBUFFER, gl.RENDERBUFFER_STENCIL_SIZE) > 0");
137     }
138     glErrorShouldBe(gl, gl.NO_ERROR);
139     testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatuses);
140     testDepthStencilDepthStencil();
141 }
142
143 function testDepthStencilDepthStencil()
144 {
145     if (!width || !height) {
146         return;
147     }
148
149     var tests = [
150         {   firstFormat: gl.DEPTH_COMPONENT16,
151             firstAttach: gl.DEPTH_ATTACHMENT,
152             secondFormat: gl.DEPTH_STENCIL,
153             secondAttach: gl.DEPTH_STENCIL_ATTACHMENT
154         },
155         {   firstFormat: gl.DEPTH_STENCIL,
156             firstAttach: gl.DEPTH_STENCIL_ATTACHMENT,
157             secondFormat: gl.DEPTH_COMPONENT16,
158             secondAttach: gl.DEPTH_ATTACHMENT
159         }
160     ];
161     for (var ii = 0; ii < tests.length; ++ii) {
162         var test = tests[ii];
163         for (var jj = 0; jj < 2; ++jj) {
164             var fbo = gl.createFramebuffer();
165             var tex = gl.createTexture();
166             var firstRb = gl.createRenderbuffer();
167
168             debug("");
169             debug("test: " + wtu.glEnumToString(gl, test.firstFormat) + " vs " + wtu.glEnumToString(gl, test.secondFormat) + " with " + (jj ? "unbind" : "delete"));
170
171             gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
172             // attach texture as color
173             gl.bindTexture(gl.TEXTURE_2D, tex);
174             gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
175             gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
176
177             // attach first
178             gl.bindRenderbuffer(gl.RENDERBUFFER, firstRb);
179             gl.renderbufferStorage(gl.RENDERBUFFER, test.firstFormat, width, height);
180             gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.firstAttach, gl.RENDERBUFFER, firstRb);
181
182             if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
183                 gl.enable(gl.DEPTH_TEST);
184                 var program = wtu.setupColorQuad(gl);
185                 // Test it works
186                 wtu.drawUByteColorQuad(gl, [0, 255, 0, 255]);
187                 wtu.drawUByteColorQuad(gl, [255, 0, 0, 255]);  // should not draw since DEPTH_FUNC == LESS
188                 wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255], "should be green");
189
190                 var secondRb = gl.createRenderbuffer();
191
192                 // attach second
193                 gl.bindRenderbuffer(gl.RENDERBUFFER, secondRb);
194                 gl.renderbufferStorage(gl.RENDERBUFFER, test.secondFormat, width, height);
195                 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.secondAttach, gl.RENDERBUFFER, secondRb);
196
197                 if (jj == 0) {
198                   // now delete it
199                   debug("test deleting second renderbuffer");
200                   gl.deleteRenderbuffer(secondRb);
201                 } else {
202                   // unbind it
203                   debug("test unbinding second renderbuffer");
204                   gl.framebufferRenderbuffer(gl.FRAMEBUFFER, test.secondAttach, gl.RENDERBUFFER, null);
205                 }
206
207                 // If the first attachment is not restored this may fail
208                 shouldBe('gl.checkFramebufferStatus(gl.FRAMEBUFFER)', 'gl.FRAMEBUFFER_COMPLETE');
209                 glErrorShouldBe(gl, gl.NO_ERROR);
210
211                 // If the first attachment is not restored this may fail.
212                 gl.clear(gl.DEPTH_BUFFER_BIT);
213                 wtu.drawUByteColorQuad(gl, [0, 255, 0, 255]);
214                 wtu.drawUByteColorQuad(gl, [255, 0, 0, 255]);  // should not draw since DEPTH_FUNC == LESS
215                 wtu.checkCanvasRect(gl, 0, 0, width, height, [0, 255, 0, 255], "should be green");
216                 gl.disable(gl.DEPTH_TEST);
217
218                 if (jj == 1) {
219                   gl.deleteRenderbuffer(secondRb);
220                 }
221             }
222
223             gl.deleteRenderbuffer(secondRb);
224             gl.deleteFramebuffer(fbo);
225         }
226     }
227     glErrorShouldBe(gl, gl.NO_ERROR);
228 }
229
230 description("Test framebuffer object attachment behaviors");
231
232 for (width = 0; width <= 2; width += 2)
233 {
234     for (height = 0; height <= 2; height += 2)
235     {
236         debug("");
237         debug("Dimensions " + width + " x " + height);
238
239         debug("Create renderbuffers");
240         shouldBeNonNull("gl = create3DContext()");
241         shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
242         gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
243         gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, width, height);
244         glErrorShouldBe(gl, gl.NO_ERROR);
245         shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
246         gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
247         gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_COMPONENT16, width, height);
248         glErrorShouldBe(gl, gl.NO_ERROR);
249         shouldBeNonNull("stencilBuffer = gl.createRenderbuffer()");
250         gl.bindRenderbuffer(gl.RENDERBUFFER, stencilBuffer);
251         gl.renderbufferStorage(gl.RENDERBUFFER, gl.STENCIL_INDEX8, width, height);
252         glErrorShouldBe(gl, gl.NO_ERROR);
253         shouldBeNonNull("depthStencilBuffer = gl.createRenderbuffer()");
254         gl.bindRenderbuffer(gl.RENDERBUFFER, depthStencilBuffer);
255         gl.renderbufferStorage(gl.RENDERBUFFER, gl.DEPTH_STENCIL, width, height);
256         glErrorShouldBe(gl, gl.NO_ERROR);
257
258         var allowedStatusForGoodCase
259             = (width == 0 || height == 0) ? ALLOW_INCOMPLETE_ATTACHMENT : ALLOW_COMPLETE;
260
261         // some cases involving stencil seem to be implementation-dependent
262         var allowedStatusForImplDependentCase = allowedStatusForGoodCase | ALLOW_UNSUPPORTED;
263
264         debug("Attach depth using DEPTH_ATTACHMENT");
265         testAttachment(gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForGoodCase);
266         debug("Attach depth using STENCIL_ATTACHMENT");
267         testAttachment(gl.STENCIL_ATTACHMENT, depthBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
268         debug("Attach depth using DEPTH_STENCIL_ATTACHMENT");
269         testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
270         debug("Attach stencil using STENCIL_ATTACHMENT");
271         testAttachment(gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForImplDependentCase);
272         debug("Attach stencil using DEPTH_ATTACHMENT");
273         testAttachment(gl.DEPTH_ATTACHMENT, stencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
274         debug("Attach stencil using DEPTH_STENCIL_ATTACHMENT");
275         testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, stencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
276         debug("Attach depthStencil using DEPTH_STENCIL_ATTACHMENT");
277         testAttachment(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForGoodCase);
278         debug("Attach depthStencil using DEPTH_ATTACHMENT");
279         testAttachment(gl.DEPTH_ATTACHMENT, depthStencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
280         debug("Attach depthStencil using STENCIL_ATTACHMENT");
281         testAttachment(gl.STENCIL_ATTACHMENT, depthStencilBuffer, ALLOW_INCOMPLETE_ATTACHMENT);
282
283         var allowedStatusForConflictedAttachment
284             = (width == 0 || height == 0) ? ALLOW_UNSUPPORTED | ALLOW_INCOMPLETE_ATTACHMENT
285                                           : ALLOW_UNSUPPORTED;
286
287         debug("Attach depth, then stencil, causing conflict");
288         testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForConflictedAttachment);
289         debug("Attach stencil, then depth, causing conflict");
290         testAttachments(gl.STENCIL_ATTACHMENT, stencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
291         debug("Attach depth, then depthStencil, causing conflict");
292         testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForConflictedAttachment);
293         debug("Attach depthStencil, then depth, causing conflict");
294         testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.DEPTH_ATTACHMENT, depthBuffer, allowedStatusForConflictedAttachment);
295         debug("Attach stencil, then depthStencil, causing conflict");
296         testAttachments(gl.DEPTH_ATTACHMENT, depthBuffer, gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, allowedStatusForConflictedAttachment);
297         debug("Attach depthStencil, then stencil, causing conflict");
298         testAttachments(gl.DEPTH_STENCIL_ATTACHMENT, depthStencilBuffer, gl.STENCIL_ATTACHMENT, stencilBuffer, allowedStatusForConflictedAttachment);
299
300         debug("Attach color renderbuffer with internalformat == RGBA4");
301         testColorRenderbuffer(gl.RGBA4, allowedStatusForGoodCase);
302
303         debug("Attach color renderbuffer with internalformat == RGB5_A1");
304         testColorRenderbuffer(gl.RGB5_A1, allowedStatusForGoodCase);
305
306         debug("Attach color renderbuffer with internalformat == RGB565");
307         testColorRenderbuffer(gl.RGB565, allowedStatusForGoodCase);
308
309         debug("Create and attach depthStencil renderbuffer");
310         testDepthStencilRenderbuffer(allowedStatusForGoodCase);
311     }
312 }
313
314 // Determine if we can attach both color and depth or color and depth_stencil
315 var depthFormat;
316 var depthAttachment;
317
318 function checkValidColorDepthCombination() {
319     shouldBeNonNull("fbo = gl.createFramebuffer()");
320     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
321     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
322     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
323     gl.framebufferRenderbuffer(
324         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
325     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
326
327     shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
328     gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
329
330     return tryDepth(gl.DEPTH_COMPONENT16, gl.DEPTH_ATTACHMENT) || tryDepth(gl.DEPTH_STENCIL, gl.DEPTH_STENCIL_ATTACHMENT);
331
332     function tryDepth(try_format, try_attachment) {
333         if (depthAttachment) {
334             // If we've tried once unattach the old one.
335             gl.framebufferRenderbuffer(
336                 gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, null);
337         }
338         depthFormat = try_format;
339         depthAttachment = try_attachment;
340         gl.framebufferRenderbuffer(
341             gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
342         gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
343         glErrorShouldBe(gl, gl.NO_ERROR);
344         return gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE;
345     }
346 }
347
348 if (checkValidColorDepthCombination()) {
349     testFramebufferIncompleteDimensions();
350     testFramebufferIncompleteAttachment();
351     testFramebufferIncompleteMissingAttachment();
352     testUsingIncompleteFramebuffer();
353 }
354
355 function checkFramebuffer(expected) {
356     var actual = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
357     var msg = "gl.checkFramebufferStatus(gl.FRAMEBUFFER) should be " + wtu.glEnumToString(gl, expected) + " was " + wtu.glEnumToString(gl, actual);
358     if (expected != gl.FRAMEBUFFER_COMPLETE) {
359         msg += " or FRAMEBUFFER_UNSUPPORTED";
360     }
361     if (actual == expected ||
362         (expected != gl.FRAMEBUFFER_COMPLETE &&
363          actual == gl.FRAMBUFFER_UNSUPPORTED)) {
364         testPassed(msg);
365     } else {
366         testFailed(msg);
367     }
368 }
369
370 function testUsingIncompleteFramebuffer() {
371     debug("");
372     debug("Test drawing or reading from an incomplete framebuffer");
373     var program = wtu.setupTexturedQuad(gl);
374     var tex = gl.createTexture();
375     wtu.fillTexture(gl, tex, 1, 1, [0,255,0,255]);
376
377     shouldBeNonNull("fbo = gl.createFramebuffer()");
378     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
379     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
380     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
381     gl.framebufferRenderbuffer(
382         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
383     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
384
385     shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
386     gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
387     gl.framebufferRenderbuffer(
388         gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
389     gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
390     glErrorShouldBe(gl, gl.NO_ERROR);
391     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
392
393     // We pick this combination because it works on desktop OpenGL but should not work on OpenGL ES 2.0
394     gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
395     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
396     debug("");
397     debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
398     testRenderingAndReading();
399
400     shouldBeNonNull("fbo2 = gl.createFramebuffer()");
401     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo2);
402     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
403     debug("");
404     debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
405     testRenderingAndReading();
406
407     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
408     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
409     gl.framebufferRenderbuffer(
410         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
411     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
412     debug("");
413     debug("Drawing or reading from an incomplete framebuffer should generate INVALID_FRAMEBUFFER_OPERATION");
414     testRenderingAndReading();
415
416     function testRenderingAndReading() {
417         glErrorShouldBe(gl, gl.NO_ERROR);
418         wtu.drawQuad(gl);
419         glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "drawArrays with incomplete framebuffer");
420         gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, new Uint8Array(4));
421         glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "readPixels from incomplete framebuffer");
422         // copyTexImage and copyTexSubImage can be either INVALID_FRAMEBUFFER_OPERATION because
423         // the framebuffer is invalid OR INVALID_OPERATION because in the case of no attachments
424         // the framebuffer is not of a compatible type.
425         gl.copyTexSubImage2D(gl.TEXTURE_2D, 0, 0, 0, 0, 0, 1, 1);
426         glErrorShouldBe(gl, [gl.INVALID_FRAMEBUFFER_OPERATION, gl.INVALID_OPERATION], "copyTexImage2D from incomplete framebuffer");
427         gl.copyTexImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 0, 0, 1, 1, 0);
428         glErrorShouldBe(gl, [gl.INVALID_FRAMEBUFFER_OPERATION, gl.INVALID_OPERATION], "copyTexSubImage2D from incomplete framebuffer");
429         gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
430         glErrorShouldBe(gl, gl.INVALID_FRAMEBUFFER_OPERATION, "clear with incomplete framebuffer");
431     }
432 }
433
434 function testFramebufferIncompleteAttachment() {
435     shouldBeNonNull("fbo = gl.createFramebuffer()");
436     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
437     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
438     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
439     gl.framebufferRenderbuffer(
440         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
441     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
442     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
443
444     debug("");
445     debug("Wrong storage type for type of attachment be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
446     gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
447     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
448
449     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
450     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
451
452     debug("");
453     debug("0 size attachment should be FRAMEBUFFER_INCOMPLETE_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
454     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 0, 0);
455     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT);
456
457     glErrorShouldBe(gl, gl.NO_ERROR);
458 }
459
460 function testFramebufferIncompleteMissingAttachment() {
461     debug("");
462     debug("No attachments should be INCOMPLETE_FRAMEBUFFER_MISSING_ATTACHMENT (OpenGL ES 2.0 4.4.5)");
463     shouldBeNonNull("fbo = gl.createFramebuffer()");
464     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
465     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
466
467     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
468     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
469     gl.framebufferRenderbuffer(
470         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
471     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
472     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
473
474     gl.framebufferRenderbuffer(
475         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, null);
476     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT);
477
478     glErrorShouldBe(gl, gl.NO_ERROR);
479 }
480
481 function testFramebufferIncompleteDimensions() {
482     debug("");
483     debug("Attachments of different sizes should be FRAMEBUFFER_INCOMPLETE_DIMENSIONS (OpenGL ES 2.0 4.4.5)");
484
485     shouldBeNonNull("fbo = gl.createFramebuffer()");
486     gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
487     shouldBeNonNull("colorBuffer = gl.createRenderbuffer()");
488     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
489     gl.framebufferRenderbuffer(
490         gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorBuffer);
491     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
492
493     shouldBeNonNull("depthBuffer = gl.createRenderbuffer()");
494     gl.bindRenderbuffer(gl.RENDERBUFFER, depthBuffer);
495     gl.framebufferRenderbuffer(
496         gl.FRAMEBUFFER, depthAttachment, gl.RENDERBUFFER, depthBuffer);
497     gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
498     glErrorShouldBe(gl, gl.NO_ERROR);
499     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
500
501     gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 32, 16);
502     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
503     gl.renderbufferStorage(gl.RENDERBUFFER, depthFormat, 16, 16);
504     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
505     gl.bindRenderbuffer(gl.RENDERBUFFER, colorBuffer);
506     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 32);
507     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
508     gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGBA4, 16, 16);
509     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
510     glErrorShouldBe(gl, gl.NO_ERROR);
511
512     var tex = gl.createTexture();
513     gl.bindTexture(gl.TEXTURE_2D, tex);
514     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
515     gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, tex, 0);
516     glErrorShouldBe(gl, gl.NO_ERROR);
517     if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
518         return;
519     }
520
521     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 32, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
522     checkFramebuffer(gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS);
523     gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 16, 16, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
524     checkFramebuffer(gl.FRAMEBUFFER_COMPLETE);
525
526     glErrorShouldBe(gl, gl.NO_ERROR);
527 }
528 </script>
529 </body>
530 </html>