Reputation: 13
I'm trying to use WEBGL_draw_buffers with OES_texture_float, which works. However, when using two render targets with a different type, this does not work on my windows machine (amd). It does, however, work on my linux machine (open source radeon driver).
So a framebuffer with the following color attachments does not work on windows:
attachment 0 : rgb * unsigned byte
attachment 1 : rgb * float
but the following layout does work:
attachment 0 : rgb * float
attachment 1 : rgb * float
I wrote a small test program that illustrates the problem:
<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script type="text/javascript">
var canvas = document.createElement('canvas');
var gl = canvas.getContext("webgl");
var WEBGL_draw_buffers = gl.getExtension("WEBGL_draw_buffers") || gl.getExtension("GL_EXT_draw_buffers") || gl.getExtension("EXT_draw_buffers");
gl.getExtension("OES_texture_float");
gl.getExtension("WEBGL_depth_texture");
var result = "";
result += "UNSIGNED_BYTE, FLOAT: " + test(gl.UNSIGNED_BYTE, gl.FLOAT) + "<br />";
result += "FLOAT, FLOAT: " + test(gl.FLOAT, gl.FLOAT);
var div = document.createElement('div');
div.innerHTML = result;
document.body.appendChild(div);
function setParams() {
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
}
function test(type1, type2) {
var w = 2, h = 2;
var t1 = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, t1);
setParams();
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, w, h, 0, gl.RGB, type1, null);
var t2 = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, t2);
setParams();
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, w, h, 0, gl.RGB, type2, null);
var framebuffer = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
WEBGL_draw_buffers.drawBuffersWEBGL([WEBGL_draw_buffers.COLOR_ATTACHMENT0_WEBGL, WEBGL_draw_buffers.COLOR_ATTACHMENT1_WEBGL]);
gl.framebufferTexture2D(gl.FRAMEBUFFER, WEBGL_draw_buffers.COLOR_ATTACHMENT0_WEBGL, gl.TEXTURE_2D, t1, 0);
gl.framebufferTexture2D(gl.FRAMEBUFFER, WEBGL_draw_buffers.COLOR_ATTACHMENT1_WEBGL, gl.TEXTURE_2D, t2, 0);
var status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
switch (status) {
case gl.FRAMEBUFFER_COMPLETE:
return "FRAMEBUFFER_COMPLETE";
case gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
return "FRAMEBUFFER_INCOMPLETE_ATTACHMENT";
case gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
return "FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT";
case gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS:
return "FRAMEBUFFER_INCOMPLETE_DIMENSIONS";
case gl.FRAMEBUFFER_UNSUPPORTED:
return "FRAMEBUFFER_UNSUPPORTED";
default:
return "Error: " + status;
}
}
</script>
</body>
</html>
On windows this outputs:
UNSIGNED_BYTE, FLOAT: FRAMEBUFFER_UNSUPPORTED
FLOAT, FLOAT: FRAMEBUFFER_COMPLETE
and on linux this outputs:
UNSIGNED_BYTE, FLOAT: FRAMEBUFFER_COMPLETE
FLOAT, FLOAT: FRAMEBUFFER_COMPLETE
I would like to know why. Note that my results are the same in firefox and chrome.
Update: when the use of angle
is disabled on windows, it will behave just like linux.
Upvotes: 0
Views: 2158
Reputation:
WebGL only requires 3 combinations of attachments to work.
The following combinations of framebuffer object attachments, when all of the attachments are framebuffer attachment complete, non-zero, and have the same width and height, must result in the framebuffer being framebuffer complete:
- COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture
- COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_ATTACHMENT = DEPTH_COMPONENT16 renderbuffer
- COLOR_ATTACHMENT0 = RGBA/UNSIGNED_BYTE texture + DEPTH_STENCIL_ATTACHMENT = DEPTH_STENCIL renderbuffer
The WEBGL_draw_buffers
extension adds a few more. From that spec.
If: A framebuffer's color attachments are all textures allocated with format
RGBA
and typeUNSIGNED_BYTE
, and The framebuffer has either:
No depth or stencil attachment
A valid
DEPTH
orDEPTH_STENCIL
attachmentThen a call to checkFramebufferStatus against this framebuffer must not return
FRAMEBUFFER_UNSUPPORTED
. (In other words, the implementation must support the use ofRGBA
/UNSIGNED_BYTE
textures as color attachments, plus either aDEPTH
orDEPTH_STENCIL
attachment.)Attaching n consecutive color attachments starting at
COLOR_ATTACHMENT0_WEBGL
, where n is between 1 andMAX_DRAW_BUFFERS_WEBGL
, must not returnFRAMEBUFFER_UNSUPPORTED
from a call to checkFramebufferStatus. In other words, ifMAX_DRAW_BUFFERS_WEBGL
is 4, then the implementation is required to support the following combinations of color attachments:
COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE
COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE
COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT2_WEBGL = RGBA/UNSIGNED_BYTE
COLOR_ATTACHMENT0_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT1_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT2_WEBGL = RGBA/UNSIGNED_BYTE COLOR_ATTACHMENT3_WEBGL = RGBA/UNSIGNED_BYTE
ALL other combinations are driver dependent.
As @Kimixa mentions the spec lists combinations that will not work but conversely the spec also says which combinations work is 100% driver dependent. In fact in OpenGL ES 2.0 even the ones mentioned above are not required to work. WebGL added that requirement on top of OpenGL ES 2.0
This is also one reason why WEBGL_draw_buffers
is not enabled on some GPUs. Chrome tests all the required combinations the first time WebGL is initialized. If the driver does not return FRAMEBUFFER_COMPLETE
for every required combination then Chrome does not enable the WEBGL_draw_buffers
extension.
Except for the required combinations the only way to know if a specific combination will work is to set them up and then check with gl.checkFramebufferStatus
.
Upvotes: 1
Reputation: 16
I replied on reddit, but I'll copy the same here:
As far as I can see, it is 'correct' that the driver reject multiple colour buffers that do not have the same number of bitplanes for all colours (see https://www.khronos.org/registry/gles/extensions/EXT/EXT_draw_buffers.txt Issue 5), and according to section 4.0 of the OpenGLES2 spec: "all color buffers attached to an application-created framebuffer object must have the same number of bitplanes"
I read that as all colours attached to a fragment output should have the same bit depth, which is not the case if some have 8 bits (unsigned byte) and others 32 (float).
It's possible that the Linux driver is not strictly spec compliant, or implementing an extension that relaxes this restriction. In other words - you can't rely on a driver supporting multiple colour outputs of different bit depths.
Upvotes: 0