blob: b57a6ca10d6bfb3694e8fb1ebe62310426302a5d [file] [log] [blame]
<!--
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">
<title>WebGL EXT_color_buffer_float Conformance Tests</title>
<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>
</head>
<body>
<div id="description"></div>
<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
<div id="console"></div>
<!-- Shaders for testing floating-point textures -->
<script id="testFragmentShader" type="x-shader/x-fragment">
precision mediump float;
uniform sampler2D tex;
uniform vec4 subtractor;
varying vec2 texCoord;
void main()
{
vec4 color = texture2D(tex, texCoord);
if (abs(color.r - subtractor.r) +
abs(color.g - subtractor.g) +
abs(color.b - subtractor.b) +
abs(color.a - subtractor.a) < 16.0) {
gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
} else {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
}
</script>
<!-- Shaders for testing floating-point render targets -->
<script id="floatingPointFragmentShader" type="x-shader/x-fragment">
void main()
{
gl_FragColor = vec4(1000.0, 1000.0, 1000.0, 1000.0);
}
</script>
<script>
"use strict";
function allocateTexture()
{
var texture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_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);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texture parameter setup should succeed");
return texture;
}
function checkRenderingResults()
{
wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
}
function arrayToString(arr, size) {
var mySize;
if (!size)
mySize = arr.length;
else
mySize = size;
var out = "[";
for (var ii = 0; ii < mySize; ++ii) {
if (ii > 0) {
out += ", ";
}
out += arr[ii];
}
return out + "]";
}
function runReadbackTest(testProgram, subtractor)
{
// Verify floating point readback
debug("Checking readback of floating-point values");
var buf = new Float32Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.FLOAT , buf);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "readPixels from floating-point framebuffer should succeed");
var ok = true;
var tolerance = 8.0; // TODO: factor this out from both this test and the subtractor shader above.
for (var ii = 0; ii < buf.length; ++ii) {
if (Math.abs(buf[ii] - subtractor[ii]) > tolerance) {
ok = false;
break;
}
}
if (ok) {
testPassed("readPixels of float-type data from floating-point framebuffer succeeded");
} else {
testFailed("readPixels of float-type data from floating-point framebuffer failed: expected "
+ arrayToString(subtractor, 4) + ", got " + arrayToString(buf));
}
}
function runFloatTextureRenderTargetTest(enabled, internalFormat, format, testProgram, numberOfChannels, subtractor, texSubImageCover)
{
var formatString = wtu.glEnumToString(gl, internalFormat);
debug("");
debug("testing floating-point " + formatString + " texture render target" + (texSubImageCover > 0 ? " after calling texSubImage" : ""));
var texture = allocateTexture();
var width = 2;
var height = 2;
gl.texImage2D(gl.TEXTURE_2D, 0, internalFormat, width, height, 0, format, gl.FLOAT, null);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point texture allocation should succeed");
// Try to use this texture as a render target.
var fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.bindTexture(gl.TEXTURE_2D, null);
var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (!enabled) {
if (completeStatus == gl.FRAMEBUFFER_COMPLETE && !enabled)
testFailed("floating-point " + formatString + " render target should not be supported without enabling EXT_color_buffer_float");
else
testPassed("floating-point " + formatString + " render target should not be supported without enabling EXT_color_buffer_float");
return;
}
if (completeStatus != gl.FRAMEBUFFER_COMPLETE) {
testFailed("floating-point " + formatString + " render target not supported");
return;
}
if (texSubImageCover > 0) {
// Ensure that replacing the whole texture or a part of it with texSubImage2D doesn't affect renderability
gl.bindTexture(gl.TEXTURE_2D, texture);
var data = new Float32Array(width * height * numberOfChannels * texSubImageCover);
gl.texSubImage2D(gl.TEXTURE_2D, 0, 0, 0, width, height * texSubImageCover, format, gl.FLOAT, data);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "texSubImage2D should succeed if EXT_color_buffer_float is enabled");
gl.bindTexture(gl.TEXTURE_2D, null);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
testFailed("render target support changed after calling texSubImage2D");
return;
}
}
var renderProgram =
wtu.setupProgram(gl,
[wtu.simpleVertexShader, "floatingPointFragmentShader"],
['vPosition'],
[0]);
wtu.clearAndDrawUnitQuad(gl);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering to floating-point texture should succeed");
// Now sample from the floating-point texture and verify we got the correct values.
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.bindTexture(gl.TEXTURE_2D, texture);
gl.useProgram(testProgram);
gl.uniform1i(gl.getUniformLocation(testProgram, "tex"), 0);
gl.uniform4fv(gl.getUniformLocation(testProgram, "subtractor"), subtractor);
wtu.clearAndDrawUnitQuad(gl);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "rendering from floating-point texture should succeed");
checkRenderingResults();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
runReadbackTest(testProgram, subtractor);
}
function runFloatRenderbufferRenderTargetTest(enabled, internalFormat, testProgram, numberOfChannels, subtractor)
{
var formatString = wtu.glEnumToString(gl, internalFormat);
var samples = [0];
if (enabled) {
samples = Array.prototype.slice.call(gl.getInternalformatParameter(gl.RENDERBUFFER, internalFormat, gl.SAMPLES));
samples.push(0);
}
for (var ndx = 0; ndx < samples.length; ++ndx) {
debug("");
debug("testing floating-point " + formatString + " renderbuffer render target with number of samples " + samples[ndx]);
var colorbuffer = gl.createRenderbuffer();
var width = 2;
var height = 2;
gl.bindRenderbuffer(gl.RENDERBUFFER, colorbuffer);
if (samples[ndx] == 0)
gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height);
else
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[ndx], internalFormat, width, height);
if (!enabled) {
wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "floating-point renderbuffer allocation should fail if EXT_color_buffer_float is not enabled");
return;
} else {
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "floating-point renderbuffer allocation should succeed if EXT_color_buffer_float is enabled");
}
// Try to use this renderbuffer as a render target.
var fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, colorbuffer);
var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (completeStatus != gl.FRAMEBUFFER_COMPLETE) {
testFailed("floating-point " + formatString + " render target not supported");
return;
}
var resolveColorRbo = null;
var resolveFbo = null;
if (samples[ndx] > 0) {
resolveColorRbo = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, resolveColorRbo);
gl.renderbufferStorage(gl.RENDERBUFFER, internalFormat, width, height);
resolveFbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, resolveFbo);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, resolveColorRbo);
completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (completeStatus != gl.FRAMEBUFFER_COMPLETE) {
testFailed("Failed to create resolve framebuffer");
return;
}
}
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.clearColor(1000.0, 1000.0, 1000.0, 1000.0);
gl.clear(gl.COLOR_BUFFER_BIT);
if (samples[ndx] > 0) {
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, resolveFbo);
gl.blitFramebuffer(0, 0, width, height, 0, 0, width, height, gl.COLOR_BUFFER_BIT, gl.NEAREST);
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, resolveFbo);
}
runReadbackTest(testProgram, subtractor);
}
}
function runRGB16FNegativeTest()
{
debug("");
debug("testing RGB16F isn't color renderable");
var texture = allocateTexture();
var width = 2;
var height = 2;
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB16F, width, height, 0, gl.RGB, gl.FLOAT, null);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "RGB16F texture allocation should succeed");
// Try to use this texture as a render target.
var fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
gl.bindTexture(gl.TEXTURE_2D, null);
var completeStatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
if (completeStatus == gl.FRAMEBUFFER_COMPLETE)
testFailed("RGB16F render target should not be supported with or without enabling EXT_color_buffer_float");
else
testPassed("RGB16F render target should not be supported with or without enabling EXT_color_buffer_float");
gl.deleteTexture(texture);
var colorbuffer = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, colorbuffer);
gl.renderbufferStorage(gl.RENDERBUFFER, gl.RGB16F, width, height);
wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "RGB16F renderbuffer allocation should fail with or without enabling EXT_color_buffer_float");
gl.bindRenderbuffer(gl.RENDERBUFFER, null);
gl.deleteRenderbuffer(colorbuffer);
gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.deleteFramebuffer(fbo);
}
function runUniqueObjectTest()
{
debug("");
debug("Testing that getExtension() returns the same object each time");
gl.getExtension("EXT_color_buffer_float").myProperty = 2;
webglHarnessCollectGarbage();
shouldBe('gl.getExtension("EXT_color_buffer_float").myProperty', '2');
}
function runInternalFormatQueryTest()
{
debug("");
debug("testing the internal format query");
var maxSamples = gl.getParameter(gl.MAX_SAMPLES);
const formats = [gl.RGBA16F, gl.R32F, gl.RG32F, gl.RGBA32F, gl.R16F, gl.RG16F, gl.R11F_G11F_B10F];
var firstMultiOnlyFormat = 4;
for (var fmt = 0; fmt < formats.length; ++fmt) {
var samples = gl.getInternalformatParameter(gl.RENDERBUFFER, formats[fmt], gl.SAMPLES);
if (fmt >= firstMultiOnlyFormat && (samples.length == 0 || samples[0] < maxSamples)) {
testFailed("the maximum value in SAMPLES should be at least " + maxSamples);
return;
}
var prevSampleCount = 0;
var sampleCount;
for (var ndx = 0; ndx < samples.length; ++ndx, prevSampleCount = sampleCount) {
sampleCount = samples[ndx];
// sample count must be > 0
if (sampleCount <= 0) {
testFailed("Expected sample count to be at least one; got " + sampleCount);
return;
}
// samples must be ordered descending
if (ndx > 0 && sampleCount >= prevSampleCount) {
testFailed("Expected sample count to be ordered in descending order; got " + prevSampleCount + " at index " + (ndx - 1) + ", and " + sampleCount + " at index " + ndx);
return;
}
}
}
testPassed("Internal format query succeeded");
}
function runCopyTexImageTest(enabled)
{
var width = 16;
var height = 16;
var level = 0;
var cases = [
{
internalformat: "RGBA16F",
format: "RGBA",
type: "HALF_FLOAT",
destInternalformat: "R16F",
data: new Uint16Array(width * height * 4)
},
{
internalformat: "RGBA32F",
format: "RGBA",
type: "FLOAT",
destInternalformat: "R32F",
data: new Float32Array(width * height * 4)
},
{
internalformat: "RGBA16F",
format: "RGBA",
type: "HALF_FLOAT",
destInternalformat: "RG16F",
data: new Uint16Array(width * height * 4)
},
{
internalformat: "RGBA32F",
format: "RGBA",
type: "FLOAT",
destInternalformat: "RG32F",
data: new Float32Array(width * height * 4)
},
{
internalformat: "RGBA16F",
format: "RGBA",
type: "HALF_FLOAT",
destInternalformat: "RGB16F",
data: new Uint16Array(width * height * 4)
},
{
internalformat: "RGBA32F",
format: "RGBA",
type: "FLOAT",
destInternalformat: "RGB32F",
data: new Float32Array(width * height * 4)
},
{
internalformat: "RGBA16F",
format: "RGBA",
type: "HALF_FLOAT",
destInternalformat: "RGBA16F",
data: new Uint16Array(width * height * 4)
},
{
internalformat: "RGBA32F",
format: "RGBA",
type: "FLOAT",
destInternalformat: "RGBA32F",
data: new Float32Array(width * height * 4)
},
{
internalformat: "R11F_G11F_B10F",
format: "RGB",
type: "FLOAT",
destInternalformat: "R11F_G11F_B10F",
data: new Float32Array(width * height * 3)
}
];
cases.forEach(function(testcase) {
debug("");
debug("Testing CopyTexImage2D for internalformat: " + testcase.destInternalformat);
var internalformat = gl[testcase.internalformat];
var format = gl[testcase.format];
var type = gl[testcase.type];
var destInternalformat = gl[testcase.destInternalformat];
var texSrc = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texSrc);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
var data = testcase.data;
gl.texImage2D(gl.TEXTURE_2D, level, internalformat, width, height, 0, format, type, data);
var fbo = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texSrc, level);
var texDest = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texDest);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Setup framebuffer with texture should succeed.");
if (enabled) {
shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_COMPLETE");
gl.copyTexImage2D(gl.TEXTURE_2D, level, destInternalformat, 0, 0, width, height, 0);
wtu.glErrorShouldBe(gl, gl.NO_ERROR, "CopyTexImage2D should succeed.");
} else {
shouldBe("gl.checkFramebufferStatus(gl.FRAMEBUFFER)", "gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
gl.copyTexImage2D(gl.TEXTURE_2D, level, destInternalformat, 0, 0, width, height, 0);
wtu.glErrorShouldBe(gl, [gl.INVALID_ENUM, gl.INVALID_FRAMEBUFFER_OPERATION], "CopyTexImage2D should fail.");
}
gl.deleteTexture(texDest);
gl.deleteTexture(texSrc);
gl.deleteFramebuffer(fbo);
});
}
description("This test verifies the functionality of the EXT_color_buffer_float extension, if it is available.");
debug("");
var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas, null, 2);
if (!gl) {
testFailed("WebGL context does not exist");
} else {
testPassed("WebGL context exists");
var texturedShaders = [
wtu.simpleTextureVertexShader,
"testFragmentShader"
];
var testProgram =
wtu.setupProgram(gl,
texturedShaders,
['vPosition', 'texCoord0'],
[0, 1]);
var quadParameters = wtu.setupUnitQuad(gl, 0, 1);
// Ensure these formats can't be used for rendering if the extension is disabled
runFloatTextureRenderTargetTest(false, gl.R16F, gl.RED);
runFloatTextureRenderTargetTest(false, gl.RG16F, gl.RG);
runFloatTextureRenderTargetTest(false, gl.RGBA16F, gl.RGBA);
runFloatTextureRenderTargetTest(false, gl.R32F, gl.RED);
runFloatTextureRenderTargetTest(false, gl.RG32F, gl.RG);
runFloatTextureRenderTargetTest(false, gl.RGBA32F, gl.RGBA);
runFloatTextureRenderTargetTest(false, gl.R11F_G11F_B10F, gl.RGB);
runFloatRenderbufferRenderTargetTest(false, gl.R16F);
runFloatRenderbufferRenderTargetTest(false, gl.RG16F);
runFloatRenderbufferRenderTargetTest(false, gl.RGBA16F);
runFloatRenderbufferRenderTargetTest(false, gl.R32F);
runFloatRenderbufferRenderTargetTest(false, gl.RG32F);
runFloatRenderbufferRenderTargetTest(false, gl.RGBA32F);
runFloatRenderbufferRenderTargetTest(false, gl.R11F_G11F_B10F);
// Ensure RGB16F can't be used for rendering.
runRGB16FNegativeTest();
runCopyTexImageTest(false);
if (!gl.getExtension("EXT_color_buffer_float")) {
testPassed("No EXT_color_buffer_float support -- this is legal");
} else {
testPassed("Successfully enabled EXT_color_buffer_float extension");
runInternalFormatQueryTest();
runFloatTextureRenderTargetTest(true, gl.R16F, gl.RED, testProgram, 1, [1000, 1, 1, 1], 0);
runFloatTextureRenderTargetTest(true, gl.RG16F, gl.RG, testProgram, 2, [1000, 1000, 1, 1], 0);
runFloatTextureRenderTargetTest(true, gl.RGBA16F, gl.RGBA, testProgram, 4, [1000, 1000, 1000, 1000], 0);
runFloatTextureRenderTargetTest(true, gl.R32F, gl.RED, testProgram, 1, [1000, 1, 1, 1], 0);
runFloatTextureRenderTargetTest(true, gl.RG32F, gl.RG, testProgram, 2, [1000, 1000, 1, 1], 0);
runFloatTextureRenderTargetTest(true, gl.RGBA32F, gl.RGBA, testProgram, 4, [1000, 1000, 1000, 1000], 0);
runFloatTextureRenderTargetTest(true, gl.R11F_G11F_B10F, gl.RGB, testProgram, 3, [1000, 1000, 1000, 1], 0);
runFloatTextureRenderTargetTest(true, gl.RGBA32F, gl.RGBA, testProgram, 4, [1000, 1000, 1000, 1000], 1);
runFloatTextureRenderTargetTest(true, gl.RGBA32F, gl.RGBA, testProgram, 4, [1000, 1000, 1000, 1000], 0.5);
runFloatRenderbufferRenderTargetTest(true, gl.R16F, testProgram, 1, [1000, 1, 1, 1]);
runFloatRenderbufferRenderTargetTest(true, gl.RG16F, testProgram, 2, [1000, 1000, 1, 1]);
runFloatRenderbufferRenderTargetTest(true, gl.RGBA16F, testProgram, 4, [1000, 1000, 1000, 1000]);
runFloatRenderbufferRenderTargetTest(true, gl.R32F, testProgram, 1, [1000, 1, 1, 1]);
runFloatRenderbufferRenderTargetTest(true, gl.RG32F, testProgram, 2, [1000, 1000, 1, 1]);
runFloatRenderbufferRenderTargetTest(true, gl.RGBA32F, testProgram, 4, [1000, 1000, 1000, 1000]);
runFloatRenderbufferRenderTargetTest(true, gl.R11F_G11F_B10F, testProgram, 3, [1000, 1000, 1000, 1]);
// Ensure EXT_color_buffer_float does not enable RGB16F as color renderable.
runRGB16FNegativeTest();
runCopyTexImageTest(true);
runUniqueObjectTest();
}
}
debug("");
var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>