summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html474
1 files changed, 474 insertions, 0 deletions
diff --git a/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html
new file mode 100644
index 0000000000..41fc8f8242
--- /dev/null
+++ b/dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html
@@ -0,0 +1,474 @@
+<!--
+Copyright (c) 2023 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 OES_sample_variables 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>
+<canvas width="32" height="32" id="c"></canvas>
+<div id="description"></div>
+<div id="console"></div>
+<script>
+"use strict";
+description("This test verifies the functionality of the OES_sample_variables extension, if it is available.");
+
+debug("");
+
+var wtu = WebGLTestUtils;
+var gl = wtu.create3DContext("c", { antialias: false }, 2);
+var ext;
+
+function runShaderTests(extensionEnabled) {
+ debug("");
+ debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));
+
+ const macro = `#version 300 es
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ #ifdef GL_OES_sample_variables
+ my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
+ #else
+ #error no GL_OES_sample_variables;
+ #endif
+ }`;
+
+ // Expect the macro shader to succeed ONLY if enabled
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, macro])) {
+ if (extensionEnabled) {
+ testPassed("Macro defined in shaders when extension is enabled");
+ } else {
+ testFailed("Macro defined in shaders when extension is disabled");
+ }
+ } else {
+ if (extensionEnabled) {
+ testFailed("Macro not defined in shaders when extension is enabled");
+ } else {
+ testPassed("Macro not defined in shaders when extension is disabled");
+ }
+ }
+
+ const missing = `#version 300 es
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ gl_SampleMask[0] = gl_SampleMaskIn[0] & 0x55555555;
+ my_FragColor = vec4(gl_SamplePosition.yx, float(gl_SampleID), float(gl_MaxSamples + gl_NumSamples));
+ }`;
+
+ // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, missing])) {
+ testFailed("Sample variables allowed without #extension pragma");
+ } else {
+ testPassed("Sample variables disallowed without #extension pragma");
+ }
+
+ const valid = `#version 300 es
+ #extension GL_OES_sample_variables : enable
+ precision highp float;
+ out vec4 my_FragColor;
+ void main() {
+ gl_SampleMask[0] = gl_SampleMaskIn[0] & 0x55555555;
+ my_FragColor = vec4(gl_SamplePosition.yx, float(gl_SampleID), float(gl_MaxSamples + gl_NumSamples));
+ }`;
+
+ // Try to compile a shader using sample variables that should only succeed if enabled
+ if (wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, valid])) {
+ if (extensionEnabled) {
+ testPassed("Sample variables compiled successfully when extension enabled");
+ } else {
+ testFailed("Sample variables compiled successfully when extension disabled");
+ }
+ } else {
+ if (extensionEnabled) {
+ testFailed("Sample variables failed to compile when extension enabled");
+ } else {
+ testPassed("Sample variables failed to compile when extension disabled");
+ }
+ }
+
+ debug("");
+}
+
+function runMaxSamplesTest() {
+ debug("");
+ debug("Testing gl_MaxSamples");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ void main() {
+ color = vec4(float(gl_MaxSamples * 4) / 255.0, 0.0, 0.0, 1.0);
+ }`;
+ gl.useProgram(wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]));
+
+ wtu.setupUnitQuad(gl);
+ wtu.drawUnitQuad(gl);
+
+ wtu.checkCanvas(gl, [gl.getParameter(gl.MAX_SAMPLES) * 4, 0, 0, 255], "should match MAX_SAMPLES", 1);
+}
+
+function runNumSamplesTest() {
+ debug("");
+ debug("Testing gl_NumSamples");
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ void main() {
+ if (gl_NumSamples == 4) {
+ color = vec4(0.0, 1.0, 0.0, 1.0);
+ } else {
+ color = vec4(1.0, 0.0, 0.0, 1.0);
+ }
+ }`;
+ gl.useProgram(wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]));
+
+ wtu.setupUnitQuad(gl);
+ wtu.drawUnitQuad(gl);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green");
+}
+
+function runSampleIDTest() {
+ debug("");
+ debug("Testing gl_SampleID");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ uniform int id;
+ void main() {
+ // Special value when the selected sample is processed, 0.0 otherwise
+ float r = float(gl_SampleID == id ? (1 << gl_SampleID) : 0) * 32.0 / 255.0;
+ // Must always be 0.0
+ float g = float(gl_SampleID < 0 || gl_SampleID >= gl_NumSamples);
+ color = vec4(r, g, 0.0, 1.0);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ wtu.setupUnitQuad(gl);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ for (let sample = 0; sample < 4; sample++) {
+ debug(`Sample ${sample} is selected`);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.uniform1i(gl.getUniformLocation(program, "id"), sample);
+ wtu.drawUnitQuad(gl);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.checkCanvas(gl, [(1 << sample) * 8, 0, 0, 255], undefined, 1);
+ }
+}
+
+function runSampleMaskInTest() {
+ debug("");
+ debug("Testing gl_SampleMaskIn");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ uint popcount(uint v) {
+ uint c = 0u;
+ for (; v != 0u; v >>= 1) c += v & 1u;
+ return c;
+ }
+ void main() {
+ float r = float(popcount(uint(gl_SampleMaskIn[0])));
+ color = vec4(r * 4.0 / 255.0, 0, 0, 1);
+ }`;
+
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ // Use a triangle instead of the WTU's quad
+ // to avoid artifacts along the diagonal
+ const vertices = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+ -1.0, 1.0,
+ 1.0, -1.0,
+ -1.0, -1.0]), gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(0);
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+
+ function test(sampleCount, sampleCoverageEnabled, coverage) {
+ if (sampleCoverageEnabled) {
+ gl.enable(gl.SAMPLE_COVERAGE);
+ } else {
+ gl.disable(gl.SAMPLE_COVERAGE);
+ }
+
+ gl.sampleCoverage(coverage, false);
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ // Shader scales up the number of input samples to increase precision in unorm8 space.
+ let expected = Math.max(sampleCount, 1) * 4;
+
+ // Sample coverage must not affect single sampled buffers
+ if (sampleCoverageEnabled && sampleCount > 0) {
+ // The number of samples in gl_SampleMaskIn must be affected by the sample
+ // coverage GL state and then the resolved value must be scaled down again.
+ expected *= coverage * coverage;
+ }
+
+ // Check only the red channel
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ const pixel = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+ const message = `Expected: ${expected}, Actual: ${pixel[0]}, ` +
+ `Samples: ${sampleCount}, Sample Coverage: ${sampleCoverageEnabled}, Coverage: ${coverage}`;
+ if (Math.abs(pixel[0] - expected) > 2) {
+ testFailed(message);
+ } else {
+ testPassed(message);
+ }
+ }
+
+ // Include all exposed sample counts and additionally test single-sampled rendering
+ const sampleCounts = [...gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES), 0];
+
+ for (const sampleCount of sampleCounts) {
+ if (sampleCount > 32) {
+ // This test will not work with more than 32 samples.
+ continue;
+ }
+
+ for (const sampleCoverageEnabled of [false, true]) {
+ for (const coverage of [0.0, 0.5, 1.0]) {
+ if (sampleCount == 1 && coverage != 0.0 && coverage != 1.0) {
+ continue;
+ }
+ test(sampleCount, sampleCoverageEnabled, coverage);
+ }
+ }
+ }
+}
+
+function runSampleMaskInPerSampleTest() {
+ debug("");
+ debug("Testing gl_SampleMaskIn with per-sample shading");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ out vec4 color;
+ void main() {
+ float r = float(gl_SampleMaskIn[0] == (1 << gl_SampleID));
+ color = vec4(r, 0, 0, 1);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ wtu.setupUnitQuad(gl);
+
+ // Include all exposed sample counts and additionally test single-sampled rendering
+ const sampleCounts = [...gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES), 0];
+ for (const sampleCount of sampleCounts) {
+ if (sampleCount > 32) {
+ // This test will not work with more than 32 samples.
+ continue;
+ }
+
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ wtu.drawUnitQuad(gl);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ wtu.checkCanvas(gl, [255, 0, 0, 255], `Samples: ${sampleCount}`, 1);
+ }
+}
+
+function runSampleMaskTest() {
+ debug("");
+ debug("Testing gl_SampleMask");
+
+ const frag = `#version 300 es
+ #extension GL_OES_sample_variables : require
+ precision highp float;
+ uniform highp int sampleMask;
+ out vec4 color;
+ void main() {
+ gl_SampleMask[0] = sampleMask;
+ color = vec4(1, 0, 0, 1);
+ }`;
+ const program = wtu.setupProgram(gl, [wtu.simpleVertexShaderESSL300, frag]);
+ gl.useProgram(program);
+
+ // Use a triangle instead of the WTU's quad
+ // to avoid artifacts along the diagonal
+ const vertices = gl.createBuffer();
+ gl.bindBuffer(gl.ARRAY_BUFFER, vertices);
+ gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
+ -1.0, 1.0,
+ 1.0, -1.0,
+ -1.0, -1.0]), gl.STATIC_DRAW);
+ gl.enableVertexAttribArray(0);
+ gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
+
+ function test(sampleCount, sampleMask) {
+ const rbo = gl.createRenderbuffer();
+ gl.bindRenderbuffer(gl.RENDERBUFFER, rbo);
+ gl.renderbufferStorageMultisample(gl.RENDERBUFFER, sampleCount, gl.RGBA8, 32, 32);
+
+ const fbo = gl.createFramebuffer();
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
+ gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo);
+
+ wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE);
+
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, fbo);
+ gl.clear(gl.COLOR_BUFFER_BIT);
+ gl.uniform1i(gl.getUniformLocation(program, "sampleMask"), sampleMask);
+ gl.drawArrays(gl.TRIANGLES, 0, 3);
+
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo);
+ gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
+ gl.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl.COLOR_BUFFER_BIT, gl.NEAREST);
+
+ let expected = 1.0;
+ if (sampleCount > 0) {
+ let mask = sampleMask & ((1 << Math.max(sampleCount, 1)) - 1);
+ let bits = 0;
+ for (; mask != 0; mask >>= 1) bits += mask & 1;
+ expected = bits / Math.max(sampleCount, 1);
+ }
+ expected *= 255;
+
+ // Check only the red channel
+ gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
+ const pixel = new Uint8Array(4);
+ gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, pixel);
+ const message = `Samples: ${sampleCount}, `
+ + `gl_SampleMask[0]: 0x${sampleMask.toString(16).padStart(8, "0").toUpperCase()}, `
+ + `Actual: ${pixel[0]}, Expected: ${expected}`;
+ if (Math.abs(pixel[0] - expected) > 2) {
+ testFailed(message);
+ } else {
+ testPassed(message);
+ }
+ }
+
+ // Include all exposed sample counts and additionally test single-sampled rendering
+ const sampleCounts = [...gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES), 0];
+
+ for (const sampleCount of sampleCounts) {
+ if (sampleCount > 31) {
+ // This test will not work with more than 31 samples.
+ continue;
+ }
+
+ for (const sampleMask of [0xFFFFFFFF, 0x55555555, 0xAAAAAAAA, 0x00000000]) {
+ test(sampleCount, sampleMask);
+ }
+ }
+}
+
+function runTest() {
+ if (!gl) {
+ testFailed("WebGL context does not exist");
+ return;
+ }
+ testPassed("WebGL context exists");
+
+ runShaderTests(false);
+
+ ext = gl.getExtension("OES_sample_variables");
+ wtu.runExtensionSupportedTest(gl, "OES_sample_variables", ext !== null);
+
+ if (!ext) {
+ testPassed("No OES_sample_variables support -- this is legal");
+ } else {
+ testPassed("Successfully enabled OES_sample_variables extension");
+ runShaderTests(true);
+
+ debug("Testing sample variables");
+ runMaxSamplesTest();
+ runNumSamplesTest();
+ runSampleIDTest();
+ runSampleMaskInTest();
+ runSampleMaskInPerSampleTest();
+ runSampleMaskTest();
+ }
+}
+
+runTest();
+
+var successfullyParsed = true;
+</script>
+<script src="../../js/js-test-post.js"></script>
+</body>
+</html>