diff options
Diffstat (limited to '')
-rw-r--r-- | dom/canvas/test/webgl-conf/checkout/conformance2/extensions/oes-sample-variables.html | 474 |
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> |