Source code
Revision control
Copy as Markdown
Other Tools
<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
<script id="eVsSrc" type="text/plain">
void main()
{
gl_PointSize = 1.0;
gl_Position = vec4(0, 0, 0, 1);
}
</script>
<script id="eFsSrc" type="text/plain">
precision mediump float;
uniform vec4 uColor;
void main()
{
gl_FragColor = uColor;
}
</script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
"use strict";
description('Blending tests');
const wtu = WebGLTestUtils;
function CreateContext() {
const gl = wtu.create3DContext();
gl.viewport(0, 0, 1, 1);
gl.prog = wtu.setupProgram(gl, [eVsSrc.innerHTML, eFsSrc.innerHTML]);
gl.prog.uColor = (() => {
const loc = gl.getUniformLocation(gl.prog, 'uColor');
return x => gl.uniform4fv(loc, x);
})();
gl.useProgram(gl.prog);
gl.prog.uColor([1 / 255, 2 / 255, 3 / 255, 4 / 255]);
gl.drawAndRead = type => {
gl.drawArrays(gl.POINTS, 0, 1);
let ret;
if (type == gl.UNSIGNED_BYTE) {
ret = new Uint8Array(4);
} else if (type == gl.FLOAT) {
ret = new Float32Array(4);
}
gl.readPixels(0, 0, 1, 1, gl.RGBA, type, ret);
return ret;
};
gl.enable(gl.BLEND);
gl.blendFunc(gl.CONSTANT_COLOR, gl.ZERO);
return gl;
}
function CreateValidFb(gl, formats) {
const fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
for (let i in formats) {
i = i|0; // Otherwise i is a string. :(
const f = formats[i];
if (!f)
continue;
if (f.length == 1) {
const rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorage(gl.RENDERBUFFER, f[0], 1, 1);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+i,
gl.RENDERBUFFER, rb);
continue;
}
if (f.length == 3) {
let internalFormat = f[0];
if (internalFormat === undefined) {
internalFormat = f[1];
}
const tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, 1,1,0, f[1],f[2], null);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0+i,
gl.TEXTURE_2D, tex, 0);
continue;
}
throw new Error('Invalid format length: ' + f);
}
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (status != gl.FRAMEBUFFER_COMPLETE) {
gl.deleteFramebuffer(fb);
return null;
}
return fb;
}
let was, fb;
const TESTS = [
() => {
debug('');
debug('Clamping of blendColor args:');
const gl = wtu.create3DContext();
if (!gl.texImage3D) { // WebGL 1.0
// WebGL 1.0 clamps without EXT_color_buffer_half_float or WEBGL_color_buffer_float.
gl.blendColor(1000, 1, 1, 1);
const was = gl.getParameter(gl.BLEND_COLOR);
expectArray(was, [1, 1, 1, 1]);
const ext = gl.getExtension('EXT_color_buffer_half_float') ||
gl.getExtension('WEBGL_color_buffer_float');
if (!ext) return;
}
// WebGL 2.0 or extended WebGL 1.0 may still clamp the value on store
// when the underlying platform does the same.
gl.blendColor(1000, 1, 1, 1);
const was = gl.getParameter(gl.BLEND_COLOR);
if (was[0] == 1000) {
expectArray(was, [1000, 1, 1, 1]);
} else {
debug("Platform does not support unclamped blend color.")
expectArray(was, [1, 1, 1, 1]);
}
},
() => {
debug('');
debug('Blending for RGBA8:');
const gl = CreateContext();
fb = CreateValidFb(gl, [[gl.RGBA8, gl.RGBA, gl.UNSIGNED_BYTE]]);
shouldBeNonNull('fb');
// Regardless of the context version and enabled extensions,
// the value will be clamped at draw time,
gl.blendColor(10, 1, 1, 1);
const was = gl.drawAndRead(gl.UNSIGNED_BYTE);
expectArray(was, [1, 2, 3, 4]);
if (gl.getExtension('EXT_color_buffer_half_float') ||
gl.getExtension('WEBGL_color_buffer_float') ||
gl.getExtension('EXT_color_buffer_float'))
{
debug('Enable floating-point color buffers and retest');
gl.blendColor(1000, 1, 1, 1);
const was = gl.drawAndRead(gl.UNSIGNED_BYTE);
expectArray(was, [1, 2, 3, 4]);
}
},
() => {
debug('');
debug('Blending for RGBA16F:');
const gl = CreateContext();
// Set the value before enabling the extension.
// It must be clamped only on WebGL 1.0 contexts.
gl.blendColor(10, 1, 1, 1);
if (!gl.getExtension('EXT_color_buffer_half_float')) {
testPassed('Missing ext EXT_color_buffer_half_float is optional, skipping.');
return;
}
if (!gl.texImage3D) { // WebGL 1.0
const ext = gl.getExtension('OES_texture_half_float');
gl.HALF_FLOAT = ext.HALF_FLOAT_OES; // These aren't the same value, but this'll work.
}
fb = CreateValidFb(gl, [[gl.RGBA16F, gl.RGBA, gl.HALF_FLOAT]]);
shouldBeNonNull('fb');
gl.prog.uColor([1, 2, 3, 4]);
let was = gl.drawAndRead(gl.FLOAT);
if (!gl.texImage3D) { // WebGL 1.0
expectArray(was, [1, 2, 3, 4]);
} else {
// Some WebGL 2.0 implementations may clamp the blend color anyway.
const r = gl.getParameter(gl.BLEND_COLOR)[0];
expectArray(was, [r, 2, 3, 4]);
}
// Re-set the value after the extension was enabled.
gl.blendColor(100, 1, 1, 1);
const r = gl.getParameter(gl.BLEND_COLOR)[0];
was = gl.drawAndRead(gl.FLOAT);
expectArray(was, [r, 2, 3, 4]);
},
() => {
debug('');
debug('Blending for RGBA32F:');
const gl = CreateContext();
// Set the value before enabling the extension.
// It must be clamped only on WebGL 1.0 contexts.
gl.blendColor(10, 1, 1, 1);
if (gl.texImage3D) { // WebGL 2.0
if (!gl.getExtension('EXT_color_buffer_float')) {
testPassed('Missing ext EXT_color_buffer_float is optional, skipping.');
return;
}
} else {
if (!gl.getExtension('WEBGL_color_buffer_float')) {
testPassed('Missing ext WEBGL_color_buffer_float is optional, skipping.');
return;
}
gl.getExtension('OES_texture_float');
}
fb = CreateValidFb(gl, [[gl.RGBA32F, gl.RGBA, gl.FLOAT]]);
shouldBeNonNull('fb');
gl.prog.uColor([1, 2, 3, 4]);
let was = gl.drawAndRead(gl.FLOAT);
if (!gl.texImage3D) { // WebGL 1.0
expectArray(was, [1, 2, 3, 4]);
} else {
// Some WebGL 2.0 implementations may clamp the blend color anyway.
const r = gl.getParameter(gl.BLEND_COLOR)[0];
expectArray(was, [r, 2, 3, 4]);
}
// Re-set the value after the extension was enabled.
gl.blendColor(100, 1, 1, 1);
const r = gl.getParameter(gl.BLEND_COLOR)[0];
was = gl.drawAndRead(gl.FLOAT);
if (!gl.getExtension('EXT_float_blend')) {
wtu.glErrorShouldBe(gl, gl.INVALID_OPERATION, 'Should not be able to blend 32F formats.');
return;
}
wtu.glErrorShouldBe(gl, 0, 'Should be able to blend 32F formats.');
expectArray(was, [r, 2, 3, 4]);
},
];
async function Test() {
for (const fn of TESTS) {
await wtu.dispatchPromise(fn);
}
wtu.destroyAllContexts();
finishTest();
}
Test();
var successfullyParsed = true;
</script>
</body>
</html>