4 ** Copyright (c) 2013 The Khronos Group Inc.
6 ** Permission is hereby granted, free of charge, to any person obtaining a
7 ** copy of this software and/or associated documentation files (the
8 ** "Materials"), to deal in the Materials without restriction, including
9 ** without limitation the rights to use, copy, modify, merge, publish,
10 ** distribute, sublicense, and/or sell copies of the Materials, and to
11 ** permit persons to whom the Materials are furnished to do so, subject to
12 ** the following conditions:
14 ** The above copyright notice and this permission notice shall be included
15 ** in all copies or substantial portions of the Materials.
17 ** THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 ** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 ** IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
21 ** CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
22 ** TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
23 ** MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
31 <meta charset="utf-8">
32 <title>WebGL Draw Buffers Conformance Tests</title>
33 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
34 <script src="../../resources/js-test-pre.js"></script>
35 <script src="../../conformance/resources/webgl-test-utils.js"></script>
38 <div id="description"></div>
39 <canvas id="canvas" width="64" height="64"> </canvas>
40 <div id="console"></div>
41 <script id="vshader" type="x-shader/x-vertex">
42 attribute vec4 a_position;
44 gl_Position = a_position;
47 <script id="fshader" type="x-shader/x-fragment">
49 precision mediump float;
50 uniform vec4 u_colors[$(numDrawingBuffers)];
52 for (int i = 0; i < $(numDrawingBuffers); ++i) {
53 gl_FragData[i] = u_colors[i];
57 <script id="fshaderRed" type="x-shader/x-fragment">
58 precision mediump float;
60 gl_FragColor = vec4(1,0,0,1);
63 <script id="fshaderBuiltInConstEnabled" type="x-shader/x-fragment">
64 precision mediump float;
66 gl_FragColor = (gl_MaxDrawBuffers == $(numDrawingBuffers)) ? vec4(0,1,0,1) : vec4(1,0,0,1);
71 description("This test verifies the functionality of Multiple Render Targets.");
75 var wtu = WebGLTestUtils;
76 var canvas = document.getElementById("canvas");
77 var output = document.getElementById("console");
78 var gl = wtu.create3DContext(canvas, null, 2);
81 testFailed("WebGL context does not exist");
83 testPassed("WebGL context exists");
89 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
92 function createDrawBuffersProgram(scriptId, sub) {
93 var fsource = wtu.getScript(scriptId);
94 fsource = wtu.replaceParams(fsource, sub);
95 wtu.addShaderSource(output, "fragment shader", fsource);
96 return wtu.setupProgram(gl, ["vshader", fsource], ["a_position"]);
99 function runShadersTest() {
101 debug("test shaders");
103 var sub = {numDrawingBuffers: gl.getParameter(gl.MAX_DRAW_BUFFERS)};
104 var program = createDrawBuffersProgram("fshaderBuiltInConstEnabled", sub);
105 wtu.setupUnitQuad(gl);
106 wtu.clearAndDrawUnitQuad(gl);
107 wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
108 gl.deleteProgram(program);
109 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
112 function makeArray(size, value) {
114 for (var ii = 0; ii < size; ++ii) {
120 function makeColorAttachmentArray(size) {
122 for (var ii = 0; ii < size; ++ii) {
123 array.push(gl.COLOR_ATTACHMENT0 + ii);
128 function runAttachmentTest() {
130 debug("test attachment enabled");
132 var maxDrawingBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS);
133 var maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
135 var tex = gl.createTexture();
136 var fb = gl.createFramebuffer();
137 gl.bindTexture(gl.TEXTURE_2D, tex);
138 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
139 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + maxColorAttachments, gl.TEXTURE_2D, tex, 0);
140 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "should not be able to attach pass the max attachment point: gl.COLOR_ATTACHMENT0 + " + maxColorAttachments);
141 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + maxColorAttachments - 1, gl.TEXTURE_2D, tex, 0);
142 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to attach to the max attachment point: gl.COLOR_ATTACHMENT0 + " + (maxColorAttachments - 1));
143 gl.drawBuffers(makeArray(maxDrawingBuffers, gl.NONE));
144 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with array NONE of size " + maxColorAttachments);
145 var bufs = makeColorAttachmentArray(maxDrawingBuffers);
146 gl.drawBuffers(bufs);
147 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with array attachments of size " + maxColorAttachments);
149 gl.drawBuffers(bufs);
150 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be able to call drawBuffers with mixed array attachments of size " + maxColorAttachments);
151 if (maxDrawingBuffers > 1) {
152 bufs[0] = gl.COLOR_ATTACHMENT1;
153 bufs[1] = gl.COLOR_ATTACHMENT0;
154 gl.drawBuffers(bufs);
155 glErrorShouldBe(gl, gl.INVALID_OPERATION, "should not be able to call drawBuffers with out of order attachments of size " + maxColorAttachments);
156 var bufs = makeColorAttachmentArray(Math.floor(maxDrawingBuffers / 2));
157 glErrorShouldBe(gl, gl.NO_ERROR, "should not be able to call drawBuffers with short array of attachments of size " + maxColorAttachments);
160 gl.deleteFramebuffer(fb);
161 gl.deleteTexture(tex);
162 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "there should be no errors");
165 function makeColorByIndex(index) {
166 var low = (index - 1) % 15 + 1;
167 var high = (index - 1) / 15;
169 var zeroOrOne = function(v) {
173 var oneOrTwo = function(v) {
177 var makeComponent = function(b0, b1, b2) {
178 return Math.floor(255 * zeroOrOne(b0) / oneOrTwo(b1) / oneOrTwo(b2));
181 makeComponent(low & (1 << 0), high & (1 << 0), high & (1 << 4)),
182 makeComponent(low & (1 << 1), high & (1 << 1), high & (1 << 5)),
183 makeComponent(low & (1 << 2), high & (1 << 2), high & (1 << 6)),
184 makeComponent(low & (1 << 3), high & (1 << 3), high & (1 << 7)),
188 function runDrawTests() {
190 debug("--------- draw tests -----------");
191 var fb = gl.createFramebuffer();
192 var fb2 = gl.createFramebuffer();
193 var halfFB1 = gl.createFramebuffer();
194 var halfFB2 = gl.createFramebuffer();
195 var endsFB = gl.createFramebuffer();
196 var middleFB = gl.createFramebuffer();
198 var maxDrawingBuffers = gl.getParameter(gl.MAX_DRAW_BUFFERS);
199 var maxColorAttachments = gl.getParameter(gl.MAX_COLOR_ATTACHMENTS);
200 var maxUniformVectors = gl.getParameter(gl.MAX_FRAGMENT_UNIFORM_VECTORS);
201 var maxUsable = Math.min(maxDrawingBuffers, maxColorAttachments, maxUniformVectors);
202 var half = Math.floor(maxUsable / 2);
203 var bufs = makeColorAttachmentArray(maxUsable);
204 var nones = makeArray(maxUsable, gl.NONE);
206 [fb, fb2, halfFB1, halfFB2, endsFB, middleFB].forEach(function(fbo) {
207 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
208 gl.drawBuffers(bufs);
211 var checkProgram = wtu.setupTexturedQuad(gl);
212 var redProgram = wtu.setupProgram(gl, ["vshader", "fshaderRed"], ["a_position"]);
213 var drawProgram = createDrawBuffersProgram("fshader", {numDrawingBuffers: maxDrawingBuffers});
216 var attachments = [];
217 // Makes 6 framebuffers.
218 // fb and fb2 have all the attachments.
219 // halfFB1 has the first half of the attachments
220 // halfFB2 has the second half of the attachments
221 // endsFB has the first and last attachments
222 // middleFB has all but the first and last attachments
223 for (var ii = 0; ii < maxUsable; ++ii) {
224 var tex = gl.createTexture();
225 gl.bindTexture(gl.TEXTURE_2D, tex);
226 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
227 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
228 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
229 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
230 gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
231 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
232 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0);
233 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
234 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0);
235 gl.bindFramebuffer(gl.FRAMEBUFFER, ii < half ? halfFB1 : halfFB2);
236 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0);
237 gl.bindFramebuffer(gl.FRAMEBUFFER, (ii == 0 || ii == (maxUsable - 1)) ? endsFB : middleFB);
238 gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + ii, gl.TEXTURE_2D, tex, 0);
239 var location = gl.getUniformLocation(drawProgram, "u_colors[" + ii + "]");
240 var color = makeColorByIndex(ii + 1);
241 var floatColor = [color[0] / 255, color[1] / 255, color[2] / 255, color[3] / 255];
242 gl.uniform4fv(location, floatColor);
249 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
250 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
251 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
252 shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
254 var checkAttachmentsForColorFn = function(attachments, colorFn) {
255 gl.bindFramebuffer(gl.FRAMEBUFFER, null);
256 gl.useProgram(checkProgram);
257 attachments.forEach(function(attachment, index) {
258 gl.bindTexture(gl.TEXTURE_2D, attachment.texture);
259 wtu.clearAndDrawUnitQuad(gl);
260 var expectedColor = colorFn(attachment, index);
262 expectedColor.forEach(function(v) {
263 if (v != 0 && v != 255) {
267 wtu.checkCanvas(gl, expectedColor, "attachment " + index + " should be " + expectedColor.toString(), tolerance);
272 var checkAttachmentsForColor = function(attachments, color) {
273 checkAttachmentsForColorFn(attachments, function(attachment, index) {
274 return color || attachment.color;
278 var drawAndCheckAttachments = function(testFB, msg, testFn) {
279 debug("test clearing " + msg);
281 gl.bindFramebuffer(gl.FRAMEBUFFER, testFB);
283 attachments.forEach(function(attachment, index) {
284 debug("attachment: " + index + " = " + wtu.glEnumToString(gl, gl.getParameter(gl.DRAW_BUFFER0 + index)) +
285 ", " + wtu.glEnumToString(gl, gl.getFramebufferAttachmentParameter(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + index, gl.FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE)));
288 if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
289 debug("framebuffer not complete");
294 // Clear all the attachments
295 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
296 gl.clearColor(0, 0, 0, 0);
297 gl.clear(gl.COLOR_BUFFER_BIT);
298 //checkAttachmentsForColorFn(attachments, function(attachment, index) {
299 // return [0, 0, 0, 0];
303 // Clear some attachments using testFB
304 gl.bindFramebuffer(gl.FRAMEBUFFER, testFB);
306 gl.clearColor(0, 1, 0, 1);
307 gl.clear(gl.COLOR_BUFFER_BIT);
308 checkAttachmentsForColorFn(attachments, function(attachment, index) {
309 return testFn(attachment, index) ? [0, 255, 0, 255] : [0, 0, 0, 0];
312 debug("test drawing to " + msg);
314 // Draw to some attachments using testFB
315 gl.useProgram(drawProgram);
316 gl.bindFramebuffer(gl.FRAMEBUFFER, testFB);
317 wtu.drawUnitQuad(gl);
319 checkAttachmentsForColorFn(attachments, function(attachment, index) {
320 return testFn(attachment, index) ? attachment.color : [0, 0, 0, 0];
324 gl.useProgram(drawProgram);
325 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
326 gl.drawBuffers(bufs);
327 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
328 gl.drawBuffers(bufs);
330 wtu.drawUnitQuad(gl);
332 debug("test that each texture got the correct color.");
334 checkAttachmentsForColor(attachments);
336 debug("test clearing clears all the textures");
337 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
338 gl.clearColor(0, 1, 0, 1);
339 gl.clear(gl.COLOR_BUFFER_BIT);
341 checkAttachmentsForColor(attachments, [0, 255, 0, 255]);
343 debug("test that NONE draws nothing");
344 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
345 gl.drawBuffers(nones);
346 gl.useProgram(redProgram);
347 wtu.clearAndDrawUnitQuad(gl);
349 checkAttachmentsForColor(attachments, [0, 255, 0, 255]);
351 debug("test that gl_FragColor broadcasts");
352 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
353 gl.drawBuffers(bufs);
354 gl.useProgram(redProgram);
355 wtu.drawUnitQuad(gl);
357 checkAttachmentsForColor(attachments, [255, 0, 0, 255]);
360 var bufs1 = makeColorAttachmentArray(maxUsable);
361 var bufs2 = makeColorAttachmentArray(maxUsable);
362 for (var ii = 0; ii < maxUsable; ++ii) {
370 debug("test setting first half to NONE and clearing");
372 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
373 gl.drawBuffers(bufs1);
374 gl.clearColor(0, 1, 0, 1);
375 gl.clear(gl.COLOR_BUFFER_BIT);
377 checkAttachmentsForColorFn(attachments, function(attachment, index) {
378 return index < half ? [255, 0, 0, 255] : [0, 255, 0, 255];
381 debug("test setting first half to NONE and drawing");
383 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
384 gl.useProgram(drawProgram);
385 wtu.drawUnitQuad(gl);
387 checkAttachmentsForColorFn(attachments, function(attachment, index) {
388 return index < half ? [255, 0, 0, 255] : attachment.color;
391 debug("test setting second half to NONE and clearing");
393 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
394 gl.drawBuffers(bufs);
395 gl.clearColor(1, 0, 0, 1);
396 gl.clear(gl.COLOR_BUFFER_BIT);
397 gl.drawBuffers(bufs2);
398 gl.clearColor(0, 0, 1, 1);
399 gl.clear(gl.COLOR_BUFFER_BIT);
400 checkAttachmentsForColorFn(attachments, function(attachment, index) {
401 return index < half ? [0, 0, 255, 255] : [255, 0, 0, 255];
404 debug("test setting second half to NONE and drawing");
406 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
407 gl.useProgram(drawProgram);
408 wtu.drawUnitQuad(gl);
410 checkAttachmentsForColorFn(attachments, function(attachment, index) {
411 return index < half ? attachment.color : [255, 0, 0, 255];
414 gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB1);
415 gl.drawBuffers(bufs);
416 drawAndCheckAttachments(
417 halfFB1, "framebuffer that only has first half of attachments",
418 function(attachment, index) {
422 gl.bindFramebuffer(gl.FRAMEBUFFER, halfFB2);
423 gl.drawBuffers(bufs);
424 drawAndCheckAttachments(
425 halfFB2, "framebuffer that only has second half of attachments",
426 function(attachment, index) {
427 return index >= half;
431 gl.bindFramebuffer(gl.FRAMEBUFFER, endsFB);
432 gl.drawBuffers(bufs);
433 drawAndCheckAttachments(
434 endsFB, "framebuffer that only has first and last attachments",
435 function(attachment, index) {
436 return index == 0 || index == (maxUsable - 1);
439 gl.bindFramebuffer(gl.FRAMEBUFFER, middleFB);
440 gl.drawBuffers(bufs);
441 drawAndCheckAttachments(
443 "framebuffer that has all but the first and last attachments",
444 function(attachment, index) {
445 return index != 0 && index != (maxUsable - 1);
450 debug("test switching between fbos keeps drawbuffer state");
451 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
452 gl.drawBuffers(nones);
454 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
455 gl.drawBuffers(bufs);
456 gl.clearColor(1, 0, 0, 1);
457 gl.clear(gl.COLOR_BUFFER_BIT);
458 checkAttachmentsForColor(attachments, [255, 0, 0, 255]);
460 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
461 gl.useProgram(drawProgram);
462 wtu.drawUnitQuad(gl);
463 checkAttachmentsForColor(attachments, [255, 0, 0, 255]);
465 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
466 gl.useProgram(drawProgram);
467 wtu.drawUnitQuad(gl);
468 checkAttachmentsForColor(attachments);
470 debug("test queries");
471 debug("check framebuffer with all attachments on");
472 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
473 for (var ii = 0; ii < maxUsable; ++ii) {
474 shouldBe("gl.getParameter(gl.DRAW_BUFFER0 + " + ii + ")", "gl.COLOR_ATTACHMENT0 + " + ii);
477 debug("check framebuffer with all attachments off");
478 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
479 for (var ii = 0; ii < maxUsable; ++ii) {
480 shouldBe("gl.getParameter(gl.DRAW_BUFFER0 + " + ii + ")", "gl.NONE");
483 debug("test attachment size mis-match");
484 gl.bindTexture(gl.TEXTURE_2D, attachments[0].texture);
485 gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width * 2, height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
486 gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
487 shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
488 gl.bindFramebuffer(gl.FRAMEBUFFER, fb2);
489 shouldBeTrue("gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE");
491 gl.deleteFramebuffer(fb);
492 gl.deleteFramebuffer(fb2);
493 gl.deleteFramebuffer(halfFB1);
494 gl.deleteFramebuffer(halfFB2);
495 attachments.forEach(function(attachment) {
496 gl.deleteTexture(attachment.texture);
498 gl.deleteProgram(checkProgram);
499 gl.deleteProgram(redProgram);
500 gl.deleteProgram(drawProgram);
504 var successfullyParsed = true;
506 <script src="../../resources/js-test-post.js"></script>