oes-shader-multisample-interpolation.html (11431B)
1 <!-- 2 Copyright (c) 2023 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 OES_shader_multisample_interpolation Conformance Tests</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 </head> 16 <body> 17 <canvas width="32" height="32" id="c"></canvas> 18 <div id="description"></div> 19 <div id="console"></div> 20 <script> 21 "use strict"; 22 description("This test verifies the functionality of the OES_shader_multisample_interpolation extension, if it is available."); 23 24 debug(""); 25 26 var wtu = WebGLTestUtils; 27 var gl = wtu.create3DContext("c", { antialias: false }, 2); 28 var ext; 29 30 function runShaderTests(extensionEnabled) { 31 debug(""); 32 debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled")); 33 34 const macroVertex = `#version 300 es 35 in vec4 vPosition; 36 void main() { 37 #ifdef GL_OES_shader_multisample_interpolation 38 gl_Position = vPosition; 39 #else 40 #error no GL_OES_shader_multisample_interpolation; 41 #endif 42 }`; 43 44 const macroFragment = `#version 300 es 45 precision highp float; 46 out vec4 my_FragColor; 47 void main() { 48 #ifdef GL_OES_shader_multisample_interpolation 49 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 50 #else 51 #error no GL_OES_shader_multisample_interpolation; 52 #endif 53 }`; 54 55 for (const shaders of [[wtu.simpleVertexShaderESSL300, macroFragment], 56 [macroVertex, wtu.simpleColorFragmentShaderESSL300]]) { 57 // Expect the macro shader to succeed ONLY if enabled 58 if (wtu.setupProgram(gl, shaders)) { 59 if (extensionEnabled) { 60 testPassed("Macro defined in shaders when extension is enabled"); 61 } else { 62 testFailed("Macro defined in shaders when extension is disabled"); 63 } 64 } else { 65 if (extensionEnabled) { 66 testFailed("Macro not defined in shaders when extension is enabled"); 67 } else { 68 testPassed("Macro not defined in shaders when extension is disabled"); 69 } 70 } 71 } 72 73 const missingVertex = `#version 300 es 74 sample out float interpolant; 75 in vec4 vPosition; 76 void main() { 77 gl_Position = vPosition; 78 }`; 79 80 const missingFragment = `#version 300 es 81 precision highp float; 82 sample in float interpolant; 83 out vec4 my_FragColor; 84 void main() { 85 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 86 }`; 87 88 // Always expect the shader missing the #extension pragma to fail (whether enabled or not) 89 for (const shaders of [[missingVertex, wtu.simpleColorFragmentShaderESSL300], 90 [wtu.simpleVertexShaderESSL300, missingFragment], 91 [missingVertex, missingFragment]]) { 92 if (wtu.setupProgram(gl, shaders)) { 93 testFailed("Sample interpolation qualifier allowed without #extension pragma"); 94 } else { 95 testPassed("Sample interpolation qualifier disallowed without #extension pragma"); 96 } 97 } 98 99 const validVertex = `#version 300 es 100 #extension GL_OES_shader_multisample_interpolation : enable 101 sample out float interpolant; 102 in vec4 vPosition; 103 void main() { 104 gl_Position = vPosition; 105 }`; 106 107 const validFragment = `#version 300 es 108 #extension GL_OES_shader_multisample_interpolation : enable 109 precision highp float; 110 sample in float interpolant; 111 out vec4 my_FragColor; 112 void main() { 113 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 114 }`; 115 116 // Try to compile a shader using a sample qualifier that should only succeed if enabled 117 if (wtu.setupProgram(gl, [validVertex, validFragment])) { 118 if (extensionEnabled) { 119 testPassed("Sample interpolation qualifier compiled successfully when extension enabled"); 120 } else { 121 testFailed("Sample interpolation qualifier compiled successfully when extension disabled"); 122 } 123 } else { 124 if (extensionEnabled) { 125 testFailed("Sample interpolation qualifier failed to compile when extension enabled"); 126 } else { 127 testPassed("Sample interpolation qualifier failed to compile when extension disabled"); 128 } 129 } 130 } 131 132 function runQueryTests(extensionEnabled) { 133 debug(""); 134 debug("Testing parameters with extension " + (extensionEnabled ? "enabled" : "disabled")); 135 if (extensionEnabled) { 136 shouldBeGreaterThanOrEqual("gl.getParameter(ext.FRAGMENT_INTERPOLATION_OFFSET_BITS_OES)", "4"); 137 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 138 139 const limit = 0.5 - Math.pow(2, -gl.getParameter(ext.FRAGMENT_INTERPOLATION_OFFSET_BITS_OES)); 140 shouldBeLessThanOrEqual("gl.getParameter(ext.MIN_FRAGMENT_INTERPOLATION_OFFSET_OES)", `-${limit}`); 141 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 142 shouldBeGreaterThanOrEqual("gl.getParameter(ext.MAX_FRAGMENT_INTERPOLATION_OFFSET_OES)", `${limit}`); 143 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 144 } else { 145 shouldBeNull("gl.getParameter(0x8E5B /* MIN_FRAGMENT_INTERPOLATION_OFFSET_OES */)"); 146 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension"); 147 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 148 shouldBeNull("gl.getParameter(0x8E5C /* MAX_FRAGMENT_INTERPOLATION_OFFSET_OES */)"); 149 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension"); 150 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 151 shouldBeNull("gl.getParameter(0x8E5D /* FRAGMENT_INTERPOLATION_OFFSET_BITS_OES */)"); 152 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "parameter unknown without enabling the extension"); 153 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 154 } 155 } 156 157 function checkEnums() { 158 debug(""); 159 debug("Check enums"); 160 shouldBe("ext.MIN_FRAGMENT_INTERPOLATION_OFFSET_OES", "0x8E5B"); 161 shouldBe("ext.MAX_FRAGMENT_INTERPOLATION_OFFSET_OES", "0x8E5C"); 162 shouldBe("ext.FRAGMENT_INTERPOLATION_OFFSET_BITS_OES", "0x8E5D"); 163 } 164 165 /* 166 * This test renders a triangle using MSAAx4 and 1x1 viewport 167 * with the following vertex colors. 168 * 169 * | Position | Color | 170 * |==========|===========| 171 * | (-1, -1) | (0, 0, 0) | 172 * | (-1, +1) | (0, 1, 0) | 173 * | (+1, -1) | (1, 0, 0) | 174 * 175 * This triangle cannot cover all four samples. 176 * 177 * When default interpolation is used, the vertex color is interpolated 178 * once, most likely in the pixel center. 179 * 180 * When per-sample interpolation is used, the vertex color is interpolated 181 * several times, producing a distinct value for each covered sample. 182 * Due to the asymmetry of sample positions, the resolved pixel color must 183 * not match the color produced by default interpolation. 184 * 185 * OpenGL specs do not guarantee specific sample positions, so the test 186 * checks only that the resolved colors are different. 187 */ 188 function runInterpolationTest() { 189 debug(""); 190 debug("Testing multisample interpolation"); 191 192 function draw(program) { 193 gl.viewport(0, 0, 1, 1); 194 gl.useProgram(program); 195 196 const posLoc = gl.getAttribLocation(program, "position"); 197 const buf = gl.createBuffer(); 198 gl.bindBuffer(gl.ARRAY_BUFFER, buf); 199 gl.bufferData( 200 gl.ARRAY_BUFFER, 201 new Float32Array([ 202 -1.0, -1.0, 203 -1.0, +1.0, 204 +1.0, -1.0]), 205 gl.STATIC_DRAW); 206 207 gl.vertexAttribPointer(posLoc, 2, gl.FLOAT, false, 0, 0); 208 gl.enableVertexAttribArray(posLoc); 209 210 const rbo = gl.createRenderbuffer(); 211 gl.bindRenderbuffer(gl.RENDERBUFFER, rbo); 212 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, 4, gl.RGBA8, 1, 1); 213 214 const fbo = gl.createFramebuffer(); 215 gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); 216 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rbo); 217 218 wtu.framebufferStatusShouldBe(gl, gl.FRAMEBUFFER, gl.FRAMEBUFFER_COMPLETE); 219 220 gl.clear(gl.COLOR_BUFFER_BIT); 221 gl.drawArrays(gl.TRIANGLES, 0, 3); 222 223 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, fbo); 224 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); 225 gl.blitFramebuffer(0, 0, 1, 1, 0, 0, 1, 1, gl.COLOR_BUFFER_BIT, gl.NEAREST); 226 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 227 228 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, null); 229 } 230 231 const vertexCenter = `#version 300 es 232 in vec4 position; 233 out vec4 interp_color; 234 void main() { 235 gl_Position = position; 236 interp_color = vec4(position.xy * 0.5 + 0.5, 0.0, 1.0); 237 }`; 238 239 const fragmentCenter = `#version 300 es 240 precision highp float; 241 in vec4 interp_color; 242 out vec4 fragColor; 243 void main() { 244 fragColor = interp_color; 245 }`; 246 const programCenter = wtu.setupProgram(gl, [vertexCenter, fragmentCenter]); 247 248 draw(programCenter); 249 const centerColor = new Uint8Array(4); 250 gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, centerColor); 251 252 const vertexSample = `#version 300 es 253 #extension GL_OES_shader_multisample_interpolation : require 254 in vec4 position; 255 sample out vec4 interp_color; 256 void main() { 257 gl_Position = position; 258 interp_color = vec4(position.xy * 0.5 + 0.5, 0.0, 1.0); 259 }`; 260 261 const fragmentSample = `#version 300 es 262 #extension GL_OES_shader_multisample_interpolation : require 263 precision highp float; 264 sample in vec4 interp_color; 265 out vec4 fragColor; 266 void main() { 267 fragColor = interp_color; 268 }`; 269 const programSample = wtu.setupProgram(gl, [vertexSample, fragmentSample]); 270 271 draw(programSample); 272 const sampleColor = new Uint8Array(4); 273 gl.readPixels(0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, sampleColor); 274 275 const message = `Pixel-center value: ${centerColor}, sample-average value: ${sampleColor}`; 276 if (centerColor[0] == sampleColor[0] && centerColor[1] == sampleColor[1]) { 277 testFailed(message); 278 } else { 279 testPassed(message); 280 } 281 } 282 283 function runTest() { 284 if (!gl) { 285 testFailed("WebGL context does not exist"); 286 return; 287 } 288 testPassed("WebGL context exists"); 289 290 runQueryTests(false); 291 runShaderTests(false); 292 293 debug(""); 294 ext = gl.getExtension("OES_shader_multisample_interpolation"); 295 wtu.runExtensionSupportedTest(gl, "OES_shader_multisample_interpolation", ext !== null); 296 297 if (!ext) { 298 testPassed("No OES_shader_multisample_interpolation support -- this is legal"); 299 } else { 300 testPassed("Successfully enabled OES_shader_multisample_interpolation extension"); 301 runQueryTests(true); 302 runShaderTests(true); 303 runInterpolationTest(); 304 } 305 } 306 307 runTest(); 308 309 var successfullyParsed = true; 310 </script> 311 <script src="../../js/js-test-post.js"></script> 312 </body> 313 </html>