2 Copyright (c) 2019 The Khronos Group Inc.
3 Use of this source code is governed by an MIT-style license that can be
4 found in the LICENSE.txt file.
10 <meta charset=
"utf-8">
11 <title>WebGL WEBGL_draw_buffers Conformance Tests
</title>
12 <link rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/js-test-pre.js"></script>
14 <script src=
"../../js/webgl-test-utils.js"></script>
15 <script src=
"../../js/tests/webgl-draw-buffers-utils.js"></script>
18 <div id=
"description"></div>
19 <canvas id=
"canvas" width=
"64" height=
"64"> </canvas>
20 <div id=
"console"></div>
21 <script id=
"fshader" type=
"x-shader/x-fragment">
22 #extension GL_EXT_draw_buffers : require
23 precision mediump float;
24 uniform vec4 u_colors[$(numDrawingBuffers)];
26 for (int i =
0; i < $(numDrawingBuffers); ++i) {
27 gl_FragData[i] = u_colors[i];
31 <script id=
"fshaderNoWrite" type=
"x-shader/x-fragment">
32 #extension GL_EXT_draw_buffers : require
36 <script id=
"fshaderRed" type=
"x-shader/x-fragment">
37 precision mediump float;
39 gl_FragColor = vec4(
1,
0,
0,
1);
42 <script id=
"fshaderRedWithExtension" type=
"x-shader/x-fragment">
43 #extension GL_EXT_draw_buffers : require
44 precision mediump float;
46 gl_FragColor = vec4(
1,
0,
0,
1);
49 <script id=
"fshaderMacroDisabled" type=
"x-shader/x-fragment">
50 #ifdef GL_EXT_draw_buffers
53 precision mediump float;
55 gl_FragColor = vec4(
0,
0,
0,
0);
58 <script id=
"fshaderMacroEnabled" type=
"x-shader/x-fragment">
59 #ifdef GL_EXT_draw_buffers
60 #if GL_EXT_draw_buffers ==
1
63 #define CODE this_code_is_bad_it_should_have_compiled
66 #define CODE this_code_is_bad_it_should_have_compiled
69 precision mediump float;
71 gl_FragColor = vec4(
0,
0,
0,
0);
74 <script id=
"fshaderBuiltInConstEnabled" type=
"x-shader/x-fragment">
75 precision mediump float;
77 gl_FragColor = (gl_MaxDrawBuffers == $(numDrawingBuffers)) ? vec4(
0,
1,
0,
1) : vec4(
1,
0,
0,
1);
82 description("This test verifies the functionality of the WEBGL_draw_buffers extension, if it is available.");
86 var wtu
= WebGLTestUtils
;
87 var canvas
= document
.getElementById("canvas");
88 var gl
= wtu
.create3DContext(canvas
);
90 var programWithMaxDrawBuffersEqualOne
= null;
94 var extensionConstants
= [
95 { name
: "MAX_COLOR_ATTACHMENTS_WEBGL", enum: 0x8CDF, expectedFn: function(v
) { return v
>= 4; }, passMsg
: " should be >= 4"},
96 { name
: "MAX_DRAW_BUFFERS_WEBGL", enum: 0x8824, expectedFn: function(v
) { return v
> 0; }, passMsg
: " should be > 0"},
98 { name
: "COLOR_ATTACHMENT0_WEBGL", enum: 0x8CE0, },
99 { name
: "COLOR_ATTACHMENT1_WEBGL", enum: 0x8CE1, },
100 { name
: "COLOR_ATTACHMENT2_WEBGL", enum: 0x8CE2, },
101 { name
: "COLOR_ATTACHMENT3_WEBGL", enum: 0x8CE3, },
102 { name
: "COLOR_ATTACHMENT4_WEBGL", enum: 0x8CE4, },
103 { name
: "COLOR_ATTACHMENT5_WEBGL", enum: 0x8CE5, },
104 { name
: "COLOR_ATTACHMENT6_WEBGL", enum: 0x8CE6, },
105 { name
: "COLOR_ATTACHMENT7_WEBGL", enum: 0x8CE7, },
106 { name
: "COLOR_ATTACHMENT8_WEBGL", enum: 0x8CE8, },
107 { name
: "COLOR_ATTACHMENT9_WEBGL", enum: 0x8CE9, },
108 { name
: "COLOR_ATTACHMENT10_WEBGL", enum: 0x8CEA, },
109 { name
: "COLOR_ATTACHMENT11_WEBGL", enum: 0x8CEB, },
110 { name
: "COLOR_ATTACHMENT12_WEBGL", enum: 0x8CEC, },
111 { name
: "COLOR_ATTACHMENT13_WEBGL", enum: 0x8CED, },
112 { name
: "COLOR_ATTACHMENT14_WEBGL", enum: 0x8CEE, },
113 { name
: "COLOR_ATTACHMENT15_WEBGL", enum: 0x8CEF, },
115 { name
: "DRAW_BUFFER0_WEBGL", enum: 0x8825, },
116 { name
: "DRAW_BUFFER1_WEBGL", enum: 0x8826, },
117 { name
: "DRAW_BUFFER2_WEBGL", enum: 0x8827, },
118 { name
: "DRAW_BUFFER3_WEBGL", enum: 0x8828, },
119 { name
: "DRAW_BUFFER4_WEBGL", enum: 0x8829, },
120 { name
: "DRAW_BUFFER5_WEBGL", enum: 0x882A, },
121 { name
: "DRAW_BUFFER6_WEBGL", enum: 0x882B, },
122 { name
: "DRAW_BUFFER7_WEBGL", enum: 0x882C, },
123 { name
: "DRAW_BUFFER8_WEBGL", enum: 0x882D, },
124 { name
: "DRAW_BUFFER9_WEBGL", enum: 0x882E, },
125 { name
: "DRAW_BUFFER10_WEBGL", enum: 0x882F, },
126 { name
: "DRAW_BUFFER11_WEBGL", enum: 0x8830, },
127 { name
: "DRAW_BUFFER12_WEBGL", enum: 0x8831, },
128 { name
: "DRAW_BUFFER13_WEBGL", enum: 0x8832, },
129 { name
: "DRAW_BUFFER14_WEBGL", enum: 0x8833, },
130 { name
: "DRAW_BUFFER15_WEBGL", enum: 0x8834, },
134 testFailed("WebGL context does not exist");
136 testPassed("WebGL context exists");
138 // Run tests with extension disabled
139 runEnumTestDisabled();
140 runShadersTestDisabled();
141 runAttachmentTestDisabled();
145 // Query the extension and store globally so shouldBe can access it
146 ext
= gl
.getExtension("WEBGL_draw_buffers");
148 testPassed("No WEBGL_draw_buffers support -- this is legal");
150 runSupportedTest(false);
153 testPassed("Successfully enabled WEBGL_draw_buffers extension");
155 drawBuffersUtils
= WebGLDrawBuffersUtils(gl
, ext
);
156 runSupportedTest(true);
157 runEnumTestEnabled();
158 runShadersTestEnabled();
159 runAttachmentTestEnabled();
165 function createExtDrawBuffersProgram(scriptId
, sub
) {
166 var fsource
= wtu
.getScript(scriptId
);
167 fsource
= wtu
.replaceParams(fsource
, sub
);
168 return wtu
.setupProgram(gl
, [wtu
.simpleVertexShader
, fsource
], ["vPosition"], undefined, true);
171 function runSupportedTest(extensionEnabled
) {
172 var supported
= gl
.getSupportedExtensions();
173 if (supported
.indexOf("WEBGL_draw_buffers") >= 0) {
174 if (extensionEnabled
) {
175 testPassed("WEBGL_draw_buffers listed as supported and getExtension succeeded");
177 testFailed("WEBGL_draw_buffers listed as supported but getExtension failed");
180 if (extensionEnabled
) {
181 testFailed("WEBGL_draw_buffers not listed as supported but getExtension succeeded");
183 testPassed("WEBGL_draw_buffers not listed as supported and getExtension failed -- this is legal");
188 function runEnumTestDisabled() {
190 debug("Testing binding enum with extension disabled");
192 // Use the constant directly as we don't have the extension
193 extensionConstants
.forEach(function(c
) {
195 shouldBeNull(`gl.getParameter(${c.enum})`);
196 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, c
.name
+ " should not be queryable if extension is disabled");
199 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
202 function runEnumTestEnabled() {
204 debug("Testing enums with extension enabled");
206 extensionConstants
.forEach(function(c
) {
207 shouldBe("ext." + c
.name
, "0x" + c
.enum.toString(16));
209 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "before getParameter");
210 debug(c
.name
+ ": 0x" + ext
[c
.name
].toString(16));
211 expectTrue(c
.expectedFn(gl
.getParameter(ext
[c
.name
])), "gl.getParameter(ext." + c
.name
+ ")" + c
.passMsg
);
212 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, c
.name
+ " query should succeed if extension is enabled");
216 shouldBeTrue("gl.getParameter(ext.MAX_COLOR_ATTACHMENTS_WEBGL) >= gl.getParameter(ext.MAX_DRAW_BUFFERS_WEBGL)");
218 debug("Testing drawBuffersWEBGL with default drawing buffer");
219 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK");
220 wtu
.shouldGenerateGLError(gl
, gl
.INVALID_OPERATION
, "ext.drawBuffersWEBGL([])");
221 wtu
.shouldGenerateGLError(gl
, gl
.INVALID_OPERATION
, "ext.drawBuffersWEBGL([gl.NONE, gl.NONE])");
222 wtu
.shouldGenerateGLError(gl
, gl
.INVALID_OPERATION
, "ext.drawBuffersWEBGL([ext.COLOR_ATTACHMENT0_WEBGL])");
223 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK");
224 wtu
.shouldGenerateGLError(gl
, gl
.NO_ERROR
, "ext.drawBuffersWEBGL([gl.NONE])");
225 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE");
226 wtu
.shouldGenerateGLError(gl
, gl
.NO_ERROR
, "ext.drawBuffersWEBGL([gl.BACK])");
227 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK");
228 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
230 debug("Testing drawBuffers and getParameter with bindFramebuffer, without drawing.");
231 fb
= gl
.createFramebuffer();
232 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
233 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.COLOR_ATTACHMENT0");
234 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.NONE");
235 wtu
.shouldGenerateGLError(gl
, gl
.NO_ERROR
, "ext.drawBuffersWEBGL([gl.NONE])");
236 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, null);
237 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK");
238 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
239 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE");
241 wtu
.shouldGenerateGLError(gl
, gl
.NO_ERROR
, "ext.drawBuffersWEBGL([gl.NONE,gl.COLOR_ATTACHMENT0+1])");
242 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.NONE");
243 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.COLOR_ATTACHMENT0+1");
245 wtu
.shouldGenerateGLError(gl
, gl
.NO_ERROR
, "ext.drawBuffersWEBGL([gl.COLOR_ATTACHMENT0,gl.COLOR_ATTACHMENT0+1])");
246 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.COLOR_ATTACHMENT0");
247 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL+1)", "gl.COLOR_ATTACHMENT0+1");
249 wtu
.shouldGenerateGLError(gl
, gl
.NO_ERROR
, "gl.deleteFramebuffer(fb)");
250 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL)", "gl.BACK");
253 function testShaders(tests
, sub
) {
254 tests
.forEach(function(test
) {
255 var shaders
= [wtu
.simpleVertexShader
, wtu
.replaceParams(wtu
.getScript(test
.fragmentShaderTemplate
), sub
)];
256 var program
= wtu
.setupProgram(gl
, shaders
, ["vPosition"], undefined, true);
257 var programLinkedSuccessfully
= (program
!= null);
258 var expectedProgramToLinkSuccessfully
= (test
.expectFailure
== true);
259 expectTrue(programLinkedSuccessfully
!= expectedProgramToLinkSuccessfully
, test
.msg
);
260 gl
.deleteProgram(program
);
264 function runShadersTestDisabled() {
266 debug("test shaders disabled");
268 var sub
= {numDrawingBuffers
: 1};
270 { fragmentShaderTemplate
: "fshaderMacroDisabled",
271 msg
: "GL_EXT_draw_buffers should not be defined in GLSL",
273 { fragmentShaderTemplate
: "fshader",
274 msg
: "#extension GL_EXT_draw_buffers should not be allowed in GLSL",
279 programWithMaxDrawBuffersEqualOne
= createExtDrawBuffersProgram("fshaderBuiltInConstEnabled", sub
);
280 wtu
.setupUnitQuad(gl
);
281 wtu
.clearAndDrawUnitQuad(gl
);
282 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green");
283 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
286 function runShadersTestEnabled() {
288 debug("test shaders enabled");
290 var sub
= {numDrawingBuffers
: gl
.getParameter(ext
.MAX_DRAW_BUFFERS_WEBGL
)};
292 { fragmentShaderTemplate
: "fshaderMacroEnabled",
293 msg
: "GL_EXT_draw_buffers should be defined as 1 in GLSL",
295 { fragmentShaderTemplate
: "fshader",
296 msg
: "fragment shader containing the #extension directive should compile",
300 var program
= createExtDrawBuffersProgram("fshaderBuiltInConstEnabled", sub
);
301 wtu
.setupUnitQuad(gl
);
302 wtu
.clearAndDrawUnitQuad(gl
);
303 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green");
304 gl
.deleteProgram(program
);
305 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
308 debug("test that gl_MaxDrawBuffers is frozen at link time and enabling the extension won't change it.");
309 gl
.useProgram(programWithMaxDrawBuffersEqualOne
);
310 wtu
.clearAndDrawUnitQuad(gl
);
311 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green");
312 gl
.deleteProgram(programWithMaxDrawBuffersEqualOne
);
313 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
316 function runAttachmentTestDisabled() {
318 debug("test attachment disabled");
319 var tex
= gl
.createTexture();
320 var fb
= gl
.createFramebuffer();
321 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
322 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
323 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ 1, gl
.TEXTURE_2D
, tex
, 0);
324 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "should not be able to attach to gl.COLOR_ATTACHMENT1");
325 gl
.deleteFramebuffer(fb
);
326 gl
.deleteTexture(tex
);
327 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
330 function makeArray(size
, value
) {
332 for (var ii
= 0; ii
< size
; ++ii
) {
338 function runAttachmentTestEnabled() {
340 debug("test attachment enabled");
342 var maxDrawingBuffers
= gl
.getParameter(ext
.MAX_DRAW_BUFFERS_WEBGL
);
343 var maxColorAttachments
= gl
.getParameter(ext
.MAX_COLOR_ATTACHMENTS_WEBGL
);
345 var tex
= gl
.createTexture();
346 var fb
= gl
.createFramebuffer();
347 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
348 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
349 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ maxColorAttachments
, gl
.TEXTURE_2D
, tex
, 0);
350 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "should not be able to attach pass the max attachment point: gl.COLOR_ATTACHMENT0 + " + maxColorAttachments
);
351 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ maxColorAttachments
- 1, gl
.TEXTURE_2D
, tex
, 0);
352 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be able to attach to the max attachment point: gl.COLOR_ATTACHMENT0 + " + (maxColorAttachments
- 1));
353 ext
.drawBuffersWEBGL(makeArray(maxDrawingBuffers
, gl
.NONE
));
354 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be able to call drawBuffersWEBGL with array NONE of size " + maxColorAttachments
);
355 var bufs
= drawBuffersUtils
.makeColorAttachmentArray(maxDrawingBuffers
);
356 ext
.drawBuffersWEBGL(bufs
);
357 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be able to call drawBuffersWEBGL with array attachments of size " + maxColorAttachments
);
359 ext
.drawBuffersWEBGL(bufs
);
360 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be able to call drawBuffersWEBGL with mixed array attachments of size " + maxColorAttachments
);
361 if (maxDrawingBuffers
> 1) {
362 bufs
[0] = ext
.COLOR_ATTACHMENT1_WEBGL
;
363 bufs
[1] = ext
.COLOR_ATTACHMENT0_WEBGL
;
364 ext
.drawBuffersWEBGL(bufs
);
365 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "should not be able to call drawBuffersWEBGL with out of order attachments of size " + maxColorAttachments
);
366 var bufs
= drawBuffersUtils
.makeColorAttachmentArray(Math
.floor(maxDrawingBuffers
/ 2));
367 ext
.drawBuffersWEBGL(bufs
);
368 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be able to call drawBuffersWEBGL with short array of attachments of size " + bufs
.length
);
371 gl
.deleteFramebuffer(fb
);
372 gl
.deleteTexture(tex
);
373 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
376 function makeColorByIndex(index
) {
377 var low
= (index
- 1) % 15 + 1;
378 var high
= (index
- 1) / 15;
380 var zeroOrOne = function(v
) {
384 var oneOrTwo = function(v
) {
388 var makeComponent = function(b0
, b1
, b2
) {
389 return Math
.floor(255 * zeroOrOne(b0
) / oneOrTwo(b1
) / oneOrTwo(b2
));
392 makeComponent(low
& (1 << 0), high
& (1 << 0), high
& (1 << 4)),
393 makeComponent(low
& (1 << 1), high
& (1 << 1), high
& (1 << 5)),
394 makeComponent(low
& (1 << 2), high
& (1 << 2), high
& (1 << 6)),
395 makeComponent(low
& (1 << 3), high
& (1 << 3), high
& (1 << 7)),
399 function runDrawTests() {
401 debug("--------- draw tests -----------");
402 var fb
= gl
.createFramebuffer();
403 var fb2
= gl
.createFramebuffer();
404 var halfFB1
= gl
.createFramebuffer();
405 var halfFB2
= gl
.createFramebuffer();
406 var endsFB
= gl
.createFramebuffer();
407 var middleFB
= gl
.createFramebuffer();
409 var maxDrawingBuffers
= gl
.getParameter(ext
.MAX_DRAW_BUFFERS_WEBGL
);
410 var maxUsable
= drawBuffersUtils
.getMaxUsableColorAttachments();
411 var half
= Math
.floor(maxUsable
/ 2);
412 var bufs
= drawBuffersUtils
.makeColorAttachmentArray(maxUsable
);
413 var nones
= makeArray(maxUsable
, gl
.NONE
);
415 [fb
, fb2
, halfFB1
, halfFB2
, endsFB
, middleFB
].forEach(function(fbo
) {
416 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
417 ext
.drawBuffersWEBGL(bufs
);
420 var checkProgram
= wtu
.setupTexturedQuad(gl
);
421 var redProgram
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShader
, "fshaderRed"], ["vPosition"]);
422 var redProgramWithExtension
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShader
, "fshaderRedWithExtension"], ["vPosition"]);
423 var drawProgram
= createExtDrawBuffersProgram("fshader", {numDrawingBuffers
: maxDrawingBuffers
});
426 var attachments
= [];
427 // Makes 6 framebuffers.
428 // fb and fb2 have all the attachments.
429 // halfFB1 has the first half of the attachments
430 // halfFB2 has the second half of the attachments
431 // endsFB has the first and last attachments
432 // middleFB has all but the first and last attachments
433 for (var ii
= 0; ii
< maxUsable
; ++ii
) {
434 var tex
= gl
.createTexture();
435 gl
.bindTexture(gl
.TEXTURE_2D
, tex
);
436 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, width
, height
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
437 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MIN_FILTER
, gl
.NEAREST
);
438 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_MAG_FILTER
, gl
.NEAREST
);
439 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_S
, gl
.CLAMP_TO_EDGE
);
440 gl
.texParameteri(gl
.TEXTURE_2D
, gl
.TEXTURE_WRAP_T
, gl
.CLAMP_TO_EDGE
);
441 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
442 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ ii
, gl
.TEXTURE_2D
, tex
, 0);
443 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
444 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ ii
, gl
.TEXTURE_2D
, tex
, 0);
445 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, ii
< half
? halfFB1
: halfFB2
);
446 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ ii
, gl
.TEXTURE_2D
, tex
, 0);
447 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, (ii
== 0 || ii
== (maxUsable
- 1)) ? endsFB
: middleFB
);
448 gl
.framebufferTexture2D(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ ii
, gl
.TEXTURE_2D
, tex
, 0);
449 var location
= gl
.getUniformLocation(drawProgram
, "u_colors[" + ii
+ "]");
450 var color
= makeColorByIndex(ii
+ 1);
451 var floatColor
= [color
[0] / 255, color
[1] / 255, color
[2] / 255, color
[3] / 255];
452 gl
.uniform4fv(location
, floatColor
);
458 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
459 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
460 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
461 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
463 var drawAndCheckAttachments = function(testFB
, msg
, testFn
) {
464 debug("test clearing " + msg
);
466 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, testFB
);
468 attachments
.forEach(function(attachment
, index
) {
469 debug("attachment: " + index
+ " = " + wtu
.glEnumToString(gl
, gl
.getParameter(ext
.DRAW_BUFFER0_WEBGL
+ index
)) +
470 ", " + wtu
.glEnumToString(gl
, gl
.getFramebufferAttachmentParameter(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
+ index
, gl
.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE
)));
473 if (gl
.checkFramebufferStatus(gl
.FRAMEBUFFER
) != gl
.FRAMEBUFFER_COMPLETE
) {
474 debug("framebuffer not complete");
479 // Clear all the attachments
480 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
481 gl
.clearColor(0, 0, 0, 0);
482 gl
.clear(gl
.COLOR_BUFFER_BIT
);
483 //drawBuffersUtils.checkAttachmentsForColorFn(attachments, function(attachment, index) {
484 // return [0, 0, 0, 0];
488 // Clear some attachments using testFB
489 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, testFB
);
491 gl
.clearColor(0, 1, 0, 1);
492 gl
.clear(gl
.COLOR_BUFFER_BIT
);
493 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
494 return testFn(attachment
, index
) ? [0, 255, 0, 255] : [0, 0, 0, 0];
497 debug("test drawing to " + msg
);
499 // Draw to some attachments using testFB
500 gl
.useProgram(drawProgram
);
501 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, testFB
);
502 wtu
.drawUnitQuad(gl
);
504 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
505 return testFn(attachment
, index
) ? attachment
.color
: [0, 0, 0, 0];
509 gl
.useProgram(drawProgram
);
510 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
511 ext
.drawBuffersWEBGL(bufs
);
512 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
513 ext
.drawBuffersWEBGL(bufs
);
515 wtu
.drawUnitQuad(gl
);
517 debug("test that each texture got the correct color.");
519 drawBuffersUtils
.checkAttachmentsForColor(attachments
);
521 debug("test clearing clears all the textures");
522 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
523 gl
.clearColor(0, 1, 0, 1);
524 gl
.clear(gl
.COLOR_BUFFER_BIT
);
526 drawBuffersUtils
.checkAttachmentsForColor(attachments
, [0, 255, 0, 255]);
528 debug("test a fragment shader writing to neither gl_FragColor nor gl_FragData does not touch attachments");
529 var noWriteProgram
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShader
, "fshaderNoWrite"], ["vPosition"]);
530 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "Should be no GL error setting up the program");
531 if (!noWriteProgram
) {
532 testFailed("Setup a program where fragment shader writes nothing failed");
534 gl
.useProgram(noWriteProgram
);
535 wtu
.drawUnitQuad(gl
);
536 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "Active draw buffers with missing frag outputs.");
537 drawBuffersUtils
.checkAttachmentsForColor(attachments
, [0, 255, 0, 255]);
538 gl
.deleteProgram(noWriteProgram
);
541 debug("test that NONE draws nothing");
542 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
543 ext
.drawBuffersWEBGL(nones
);
544 gl
.useProgram(redProgram
);
545 wtu
.clearAndDrawUnitQuad(gl
);
547 drawBuffersUtils
.checkAttachmentsForColor(attachments
, [0, 255, 0, 255]);
549 debug("test that gl_FragColor does not broadcast unless extension is enabled in fragment shader");
550 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
551 ext
.drawBuffersWEBGL(bufs
);
552 gl
.useProgram(redProgram
);
553 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
554 wtu
.drawUnitQuad(gl
);
555 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "Active draw buffers with missing frag outputs.");
556 gl
.colorMask(false, false, false, false);
557 wtu
.drawUnitQuad(gl
);
558 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors when all 4 channels of color mask are disabled.");
559 gl
.colorMask(false, true, false, false);
560 wtu
.drawUnitQuad(gl
);
561 wtu
.glErrorShouldBe(gl
, gl
.INVALID_OPERATION
, "partially diabled color mask shall have no impact.");
562 gl
.colorMask(true, true, true, true);
564 debug("test that gl_FragColor broadcasts if extension is enabled in fragment shader");
565 gl
.clear(gl
.COLOR_BUFFER_BIT
);
566 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
567 ext
.drawBuffersWEBGL(bufs
);
568 gl
.useProgram(redProgramWithExtension
);
569 wtu
.drawUnitQuad(gl
);
571 drawBuffersUtils
.checkAttachmentsForColor(attachments
, [255, 0, 0, 255]);
574 // First half of color buffers disable.
575 var bufs1
= drawBuffersUtils
.makeColorAttachmentArray(maxUsable
);
576 // Second half of color buffers disable.
577 var bufs2
= drawBuffersUtils
.makeColorAttachmentArray(maxUsable
);
578 // Color buffers with even indices disabled.
579 var bufs3
= drawBuffersUtils
.makeColorAttachmentArray(maxUsable
);
580 // Color buffers with odd indices disabled.
581 var bufs4
= drawBuffersUtils
.makeColorAttachmentArray(maxUsable
);
582 for (var ii
= 0; ii
< maxUsable
; ++ii
) {
595 debug("test setting first half to NONE and clearing");
597 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
598 // We should clear all buffers rather than depending on the previous
599 // gl_FragColor broadcasts test to succeed and setting the colors.
600 ext
.drawBuffersWEBGL(bufs
);
601 gl
.clearColor(1, 0, 0, 1);
602 gl
.clear(gl
.COLOR_BUFFER_BIT
);
604 ext
.drawBuffersWEBGL(bufs1
);
605 gl
.clearColor(0, 1, 0, 1);
606 gl
.clear(gl
.COLOR_BUFFER_BIT
);
608 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
609 return index
< half
? [255, 0, 0, 255] : [0, 255, 0, 255];
612 debug("test setting first half to NONE and drawing");
614 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
615 gl
.useProgram(drawProgram
);
616 wtu
.drawUnitQuad(gl
);
618 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
619 return index
< half
? [255, 0, 0, 255] : attachment
.color
;
622 debug("test setting second half to NONE and clearing");
624 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
625 ext
.drawBuffersWEBGL(bufs
);
626 gl
.clearColor(1, 0, 0, 1);
627 gl
.clear(gl
.COLOR_BUFFER_BIT
);
629 ext
.drawBuffersWEBGL(bufs2
);
630 gl
.clearColor(0, 0, 1, 1);
631 gl
.clear(gl
.COLOR_BUFFER_BIT
);
632 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
633 return index
< half
? [0, 0, 255, 255] : [255, 0, 0, 255];
636 debug("test setting second half to NONE and drawing");
638 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
639 gl
.useProgram(drawProgram
);
640 wtu
.drawUnitQuad(gl
);
642 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
643 return index
< half
? attachment
.color
: [255, 0, 0, 255];
646 debug("test setting buffers with even indices to NONE and clearing");
648 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
649 ext
.drawBuffersWEBGL(bufs
);
650 gl
.clearColor(1, 0, 0, 1);
651 gl
.clear(gl
.COLOR_BUFFER_BIT
);
652 ext
.drawBuffersWEBGL(bufs3
);
653 gl
.clearColor(1, 0, 1, 1);
654 gl
.clear(gl
.COLOR_BUFFER_BIT
);
656 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
657 return (index
% 2) ? [255, 0, 0, 255] : [255, 0, 255, 255];
660 debug("test setting buffers with odd indices to NONE and drawing");
662 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
663 ext
.drawBuffersWEBGL(bufs
);
664 gl
.clearColor(0, 0, 0, 1);
665 gl
.clear(gl
.COLOR_BUFFER_BIT
);
666 gl
.useProgram(drawProgram
);
667 ext
.drawBuffersWEBGL(bufs4
);
668 wtu
.drawUnitQuad(gl
);
670 drawBuffersUtils
.checkAttachmentsForColorFn(attachments
, function(attachment
, index
) {
671 return (index
% 2 == 0) ? [0, 0, 0, 255] : attachment
.color
;
674 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, halfFB1
);
675 ext
.drawBuffersWEBGL(bufs
);
676 drawAndCheckAttachments(
677 halfFB1
, "framebuffer that only has first half of attachments",
678 function(attachment
, index
) {
682 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, halfFB2
);
683 ext
.drawBuffersWEBGL(bufs
);
684 drawAndCheckAttachments(
685 halfFB2
, "framebuffer that only has second half of attachments",
686 function(attachment
, index
) {
687 return index
>= half
;
691 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, endsFB
);
692 ext
.drawBuffersWEBGL(bufs
);
693 drawAndCheckAttachments(
694 endsFB
, "framebuffer that only has first and last attachments",
695 function(attachment
, index
) {
696 return index
== 0 || index
== (maxUsable
- 1);
699 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, middleFB
);
700 ext
.drawBuffersWEBGL(bufs
);
701 drawAndCheckAttachments(
703 "framebuffer that has all but the first and last attachments",
704 function(attachment
, index
) {
705 return index
!= 0 && index
!= (maxUsable
- 1);
710 debug("test switching between fbos does not affect any color attachment contents");
711 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
712 ext
.drawBuffersWEBGL(nones
);
714 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
715 ext
.drawBuffersWEBGL(bufs
);
716 gl
.clearColor(1, 0, 0, 1);
717 gl
.clear(gl
.COLOR_BUFFER_BIT
);
718 drawBuffersUtils
.checkAttachmentsForColor(attachments
, [255, 0, 0, 255]);
720 // fb2 still has the NONE draw buffers from before, so this draw should be a no-op.
721 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
722 gl
.useProgram(drawProgram
);
723 wtu
.drawUnitQuad(gl
);
724 drawBuffersUtils
.checkAttachmentsForColor(attachments
, [255, 0, 0, 255]);
726 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
727 gl
.useProgram(drawProgram
);
728 wtu
.drawUnitQuad(gl
);
729 drawBuffersUtils
.checkAttachmentsForColor(attachments
);
731 debug("test queries");
732 debug("check framebuffer with all attachments on");
733 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
734 for (var ii
= 0; ii
< maxUsable
; ++ii
) {
735 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL + " + ii
+ ")", "gl.COLOR_ATTACHMENT0 + " + ii
);
738 debug("check framebuffer with all attachments off");
739 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
740 for (var ii
= 0; ii
< maxUsable
; ++ii
) {
741 shouldBe("gl.getParameter(ext.DRAW_BUFFER0_WEBGL + " + ii
+ ")", "gl.NONE");
744 debug("test attachment size mis-match");
745 gl
.bindTexture(gl
.TEXTURE_2D
, attachments
[0].texture
);
746 gl
.texImage2D(gl
.TEXTURE_2D
, 0, gl
.RGBA
, width
* 2, height
, 0, gl
.RGBA
, gl
.UNSIGNED_BYTE
, null);
747 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb
);
748 shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
749 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fb2
);
750 shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
752 gl
.deleteFramebuffer(fb
);
753 gl
.deleteFramebuffer(fb2
);
754 gl
.deleteFramebuffer(halfFB1
);
755 gl
.deleteFramebuffer(halfFB2
);
756 attachments
.forEach(function(attachment
) {
757 gl
.deleteTexture(attachment
.texture
);
759 gl
.deleteProgram(checkProgram
);
760 gl
.deleteProgram(redProgram
);
761 gl
.deleteProgram(redProgramWithExtension
);
762 gl
.deleteProgram(drawProgram
);
765 function runPreserveTests() {
767 debug("--------- preserve tests -----------");
769 debug("Testing that frame buffer is cleared after compositing");
770 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, null);
772 gl
.clearColor(1, 1, 0, 1);
773 gl
.clear(gl
.COLOR_BUFFER_BIT
);
774 wtu
.checkCanvas(gl
, [255, 255, 0, 255], "should be yellow");
776 // set the draw buffer to NONE
777 ext
.drawBuffersWEBGL([gl
.NONE
]);
778 gl
.clearColor(1, 0, 1, 1);
779 gl
.clear(gl
.COLOR_BUFFER_BIT
);
781 // make sure the canvas is still clear
782 wtu
.checkCanvas(gl
, [255, 255, 0, 255], "should be yellow");
784 wtu
.waitForComposite(function() {
785 gl
.clearColor(1, 0, 0, 1);
786 gl
.clear(gl
.COLOR_BUFFER_BIT
);
787 wtu
.checkCanvas(gl
, [0, 0, 0, 0], "should be clear");
788 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "there should be no errors");
794 function runEndTests() {
795 // Create new context and verify shader tests with no extension still succeeds.
797 debug("Testing new context with no extension");
798 gl
= wtu
.create3DContext();
800 testFailed("New WebGL context does not exist");
802 testPassed("New WebGL context exists");
803 runEnumTestDisabled();
804 runShadersTestDisabled();
805 runAttachmentTestDisabled();