multisampling-fragment-evaluation.html (5654B)
1 <!-- 2 Copyright (c) 2019 The Khronos Group Inc. 3 Use of this source code is governed by an MIT-style license that can be 4 found in the LICENSE.txt file. 5 --> 6 7 <!DOCTYPE html> 8 <html> 9 <head> 10 <meta charset="utf-8"> 11 <title>WebGL multisampling fragment shader evaluation</title> 12 <link rel="stylesheet" href="../../resources/js-test-style.css"/> 13 <script src="../../js/js-test-pre.js"></script> 14 <script src="../../js/webgl-test-utils.js"></script> 15 16 <script id="vshader" type="x-shader/x-vertex">#version 300 es 17 layout(location=0) in vec4 aPosition; 18 out vec4 vPosition; 19 void main() 20 { 21 gl_Position = vec4(aPosition); 22 vPosition = aPosition; 23 } 24 </script> 25 <script id="fshader" type="x-shader/x-fragment">#version 300 es 26 precision highp float; 27 in vec4 vPosition; 28 layout(location=0) out vec4 oColor; 29 void main() 30 { 31 if (vPosition.x < 0.0) { 32 oColor = vec4(1, 0, 0, 1); 33 } else if (vPosition.y < 0.0) { 34 oColor = vec4(0, 1, 0, 1); 35 } else { 36 oColor = vec4(0, 0, 1, 1); 37 } 38 } 39 </script> 40 41 </head> 42 <body> 43 <div id="description"></div> 44 <div id="console"></div> 45 46 <script> 47 "use strict"; 48 49 var wtu = WebGLTestUtils; 50 description("Verify that fragment shader is evaluated only once per framebuffer pixel when multisampling is used."); 51 52 // GLES 3.0.5 section 3.6.3. Polygon Multisample Rasterization: 53 // "Polygon rasterization produces a fragment for each framebuffer pixel with one or more sample points that satisfy 54 // the point sampling criteria described in section 3.6.1." 55 56 debug("Regression test for <a href='http://crbug.com/682815'>http://crbug.com/682815</a>"); 57 58 function runTest(testParams) { 59 let canvas = document.createElement('canvas'); 60 canvas.width = 1; 61 canvas.height = 1; 62 let gl = wtu.create3DContext(canvas, {antialias: false}, 2); 63 64 // Find the supported samples for a multisampled renderbuffer of the appropriate internal format. 65 let samples = gl.getInternalformatParameter(gl.RENDERBUFFER, gl[testParams.internalformat], gl.SAMPLES); 66 if (!samples || !samples.length) { 67 testFailed("Could not query supported sample counts for required multisampling format " + testParams.internalformat); 68 return; 69 } 70 71 // Note that supported sample counts are required to be reported in descending order. 72 debug('Testing with sample count ' + samples[0]); 73 // Create a framebuffer with a multisampled renderbuffer with the maximum supported number of samples. 74 let rb = gl.createRenderbuffer(); 75 gl.bindRenderbuffer(gl.RENDERBUFFER, rb); 76 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, samples[0], gl[testParams.internalformat], 1, 1); 77 let fb = gl.createFramebuffer(); 78 gl.bindFramebuffer(gl.FRAMEBUFFER, fb); 79 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); 80 if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { 81 testFailed("Rendering to a multisampled renderbuffer of format " + testParams.internalformat + " is required."); 82 return; 83 } 84 85 // Create a program that will choose between one of different possible colors in the fragment shader. 86 // It should be evaluated only once per framebuffer pixel, so only one of the colors will end up in the framebuffer. 87 // However, if the multisampling mode is incorrectly implemented by supersampling, the samples may have different 88 // colors. 89 let program = wtu.setupProgram(gl, ["vshader", "fshader"], ["aPosition"]); 90 91 // Render one triangle using the program. The triangle needs to extend far outside the viewport on all sides, so 92 // that we can safely assume all samples fall inside the triangle. GLES 3.0.5: 93 // "The sample points associated with a pixel may be located inside or outside of the unit square that is considered to bound the pixel." 94 // Here we assume that sample points are less than 9999 pixels away from the pixel they are associated with. 95 let buffer = gl.createBuffer(); 96 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 97 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 10000, 30000, 98 -30000, -10000, 99 10000, -10000]), gl.STATIC_DRAW); 100 gl.enableVertexAttribArray(0); 101 gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 102 gl.drawArrays(gl.TRIANGLES, 0, 3); 103 104 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); 105 gl.blitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, gl.COLOR_BUFFER_BIT, gl.NEAREST); 106 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); 107 108 let readBuffer = new Uint8Array(4); 109 gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, readBuffer); 110 111 // Check that the canvas is one of the colors that the fragment shader may generate, and not a blend of them. 112 let possibleColors = [ 113 [255, 0, 0, 255], 114 [0, 255, 0, 255], 115 [0, 0, 255, 255] 116 ]; 117 let anyColorMatched = false; 118 for (let i = 0; i < possibleColors.length; ++i) { 119 let colorMatched = true; 120 for (let j = 0; j < 4; ++j) { 121 if (Math.abs(readBuffer[j] - possibleColors[i][j]) > 2) { 122 colorMatched = false; 123 } 124 } 125 if (colorMatched) { 126 anyColorMatched = true; 127 } 128 } 129 if (!anyColorMatched) { 130 testFailed("Color in framebuffer was not one of the colors generated by the fragment shader: " + readBuffer); 131 } else { 132 testPassed("Color in framebuffer was one of the colors generated by the fragment shader: " + readBuffer); 133 } 134 } 135 136 runTest({internalformat: 'RGBA8'}); 137 138 var successfullyParsed = true; 139 </script> 140 <script src="../../js/js-test-post.js"></script> 141 142 </body> 143 </html>