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">
<title>WebGL multisampling fragment shader evaluation</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>
<script id="vshader" type="x-shader/x-vertex">#version 300 es
layout(location=0) in vec4 aPosition;
out vec4 vPosition;
void main()
{
gl_Position = vec4(aPosition);
vPosition = aPosition;
}
</script>
<script id="fshader" type="x-shader/x-fragment">#version 300 es
precision highp float;
in vec4 vPosition;
layout(location=0) out vec4 oColor;
void main()
{
if (vPosition.x < 0.0) {
oColor = vec4(1, 0, 0, 1);
} else if (vPosition.y < 0.0) {
oColor = vec4(0, 1, 0, 1);
} else {
oColor = vec4(0, 0, 1, 1);
}
}
</script>
</head>
<body>
<div id="description"></div>
<div id="console"></div>
<script>
"use strict";
var wtu = WebGLTestUtils;
description("Verify that fragment shader is evaluated only once per framebuffer pixel when multisampling is used.");
// GLES 3.0.5 section 3.6.3. Polygon Multisample Rasterization:
// "Polygon rasterization produces a fragment for each framebuffer pixel with one or more sample points that satisfy
// the point sampling criteria described in section 3.6.1."
function runTest(testParams) {
let canvas = document.createElement('canvas');
canvas.width = 1;
canvas.height = 1;
let gl = wtu.create3DContext(canvas, {antialias: false}, 2);
// Find the supported samples for a multisampled renderbuffer of the appropriate internal format.
let samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl[testParams.internalformat], gl.SAMPLES);
if (!samples || !samples.length) {
testFailed("Could not query supported sample counts for required multisampling format " + testParams.internalformat);
return;
}
// Note that supported sample counts are required to be reported in descending order.
debug('Testing with sample count ' + samples[0]);
// Create a framebuffer with a multisampled renderbuffer with the maximum supported number of samples.
let rb = gl.createRenderbuffer();
gl.bindRenderbuffer(gl.RENDERBUFFER, rb);
gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl[testParams.internalformat], 1, 1);
let fb = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, fb);
gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb);
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) {
testFailed("Rendering to a multisampled renderbuffer of format " + testParams.internalformat + " is required.");
return;
}
// Create a program that will choose between one of different possible colors in the fragment shader.
// It should be evaluated only once per framebuffer pixel, so only one of the colors will end up in the framebuffer.
// However, if the multisampling mode is incorrectly implemented by supersampling, the samples may have different
// colors.
let program = wtu.setupProgram(gl, ["vshader", "fshader"], ["aPosition"]);
// Render one triangle using the program. The triangle needs to extend far outside the viewport on all sides, so
// that we can safely assume all samples fall inside the triangle. GLES 3.0.5:
// "The sample points associated with a pixel may be located inside or outside of the unit square that is considered to bound the pixel."
// Here we assume that sample points are less than 9999 pixels away from the pixel they are associated with.
let buffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 10000, 30000,
-30000, -10000,
10000, -10000]), gl.STATIC_DRAW);
gl.enableVertexAttribArray(0);
gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0);
gl.drawArrays(gl.TRIANGLES, 0, 3);
gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null);
gl.blitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, gl.COLOR_BUFFER_BIT, gl.NEAREST);
gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null);
let readBuffer = new Uint8Array(4);
gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer);
// Check that the canvas is one of the colors that the fragment shader may generate, and not a blend of them.
let possibleColors = [
[255, 0, 0, 255],
[0, 255, 0, 255],
[0, 0, 255, 255]
];
let anyColorMatched = false;
for (let i = 0; i < possibleColors.length; ++i) {
let colorMatched = true;
for (let j = 0; j < 4; ++j) {
if (Math.abs(readBuffer[j] - possibleColors[i][j]) > 2) {
colorMatched = false;
}
}
if (colorMatched) {
anyColorMatched = true;
}
}
if (!anyColorMatched) {
testFailed("Color in framebuffer was not one of the colors generated by the fragment shader: " + readBuffer);
} else {
testPassed("Color in framebuffer was one of the colors generated by the fragment shader: " + readBuffer);
}
}
runTest({internalformat: 'RGBA8'});
var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>
</body>
</html>