line-rendering-quality.js (5614B)
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 'use strict'; 8 description("Verifies that lines, both aliased and antialiased, have acceptable quality."); 9 10 let wtu = WebGLTestUtils; 11 let gl; 12 13 let aa_fbo; 14 15 function setupWebGL1Test(canvasId, useAntialiasing) { 16 gl = wtu.create3DContext(canvasId, { antialias: useAntialiasing }, contextVersion); 17 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors during WebGL 1.0 setup"); 18 } 19 20 function setupWebGL2Test(canvasId, useAntialiasing) { 21 gl = wtu.create3DContext(canvasId, { antialias: false }, contextVersion); 22 // In WebGL 2.0, we always allocate the back buffer without 23 // antialiasing. The only question is whether we allocate a 24 // framebuffer with a multisampled renderbuffer attachment. 25 aa_fbo = null; 26 if (useAntialiasing) { 27 aa_fbo = gl.createFramebuffer(); 28 gl.bindFramebuffer(gl.FRAMEBUFFER, aa_fbo); 29 let rb = gl.createRenderbuffer(); 30 gl.bindRenderbuffer(gl.RENDERBUFFER, rb); 31 let supported = gl.getInternalformatParameter(gl.RENDERBUFFER, gl.RGBA8, gl.SAMPLES); 32 // Prefer 4, then 8, then max. 33 let preferred = [4, 8]; 34 let allocated = false; 35 for (let value of preferred) { 36 if (supported.indexOf(value) >= 0) { 37 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, value, gl.RGBA8, 38 gl.drawingBufferWidth, gl.drawingBufferHeight); 39 allocated = true; 40 break; 41 } 42 } 43 if (!allocated) { 44 gl.renderbufferStorageMultisample(gl.RENDERBUFFER, supported[supported.length - 1], 45 gl.RGBA8, gl.drawingBufferWidth, gl.drawingBufferHeight); 46 } 47 gl.framebufferRenderbuffer(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.RENDERBUFFER, rb); 48 } 49 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "Should be no errors during WebGL 2.0 setup"); 50 } 51 52 function setupLines() { 53 let prog = wtu.setupSimpleColorProgram(gl, 0); 54 let loc = gl.getUniformLocation(prog, 'u_color'); 55 if (loc == null) { 56 testFailed('Failed to fetch color uniform'); 57 } 58 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of line program"); 59 gl.uniform4f(loc, 1.0, 1.0, 1.0, 1.0); 60 let buffer = gl.createBuffer(); 61 let scale = 0.5; 62 gl.bindBuffer(gl.ARRAY_BUFFER, buffer); 63 gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([ 64 -scale, -scale, 0.0, 1.0, 65 -scale, scale, 0.0, 1.0, 66 scale, scale, 0.0, 1.0, 67 scale, -scale, 0.0, 1.0, 68 -scale, -scale, 0.0, 1.0, 69 ]), gl.STATIC_DRAW); 70 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of buffer"); 71 gl.vertexAttribPointer(0, 4, gl.FLOAT, false, 0, 0); 72 gl.enableVertexAttribArray(0); 73 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "After setup of attribute array"); 74 } 75 76 function renderLines(contextVersion, useAntialiasing) { 77 gl.clearColor(0.0, 0.0, 0.5, 1.0); 78 gl.clear(gl.COLOR_BUFFER_BIT); 79 gl.drawArrays(gl.LINE_STRIP, 0, 5); 80 if (contextVersion == 2 && useAntialiasing) { 81 // Blit aa_fbo into the real back buffer. 82 gl.bindFramebuffer(gl.READ_FRAMEBUFFER, aa_fbo); 83 gl.bindFramebuffer(gl.DRAW_FRAMEBUFFER, null); 84 let w = gl.drawingBufferWidth; 85 let h = gl.drawingBufferHeight; 86 gl.blitFramebuffer(0, 0, w, h, 87 0, 0, w, h, 88 gl.COLOR_BUFFER_BIT, gl.NEAREST); 89 gl.bindFramebuffer(gl.FRAMEBUFFER, null); 90 } 91 } 92 93 function pixelAboveThreshold(arr, pixelIndex, threshold) { 94 return (arr[4 * pixelIndex + 0] >= threshold && 95 arr[4 * pixelIndex + 1] >= threshold && 96 arr[4 * pixelIndex + 2] >= threshold && 97 arr[4 * pixelIndex + 3] >= threshold); 98 } 99 100 function checkLine(arr, threshold, direction) { 101 // Count number of crossings from below threshold to above (or equal 102 // to) threshold. Must be equal to 2. 103 104 let numPixels = arr.length / 4; 105 let numUpCrossings = 0; 106 let numDownCrossings = 0; 107 for (let index = 0; index < numPixels - 1; ++index) { 108 let curPixel = pixelAboveThreshold(arr, index, threshold); 109 let nextPixel = pixelAboveThreshold(arr, index + 1, threshold); 110 if (!curPixel && nextPixel) { 111 ++numUpCrossings; 112 } else if (curPixel && !nextPixel) { 113 ++numDownCrossings; 114 } 115 } 116 if (numUpCrossings != numDownCrossings) { 117 testFailed('Found differing numbers of up->down and down->up transitions'); 118 } 119 if (numUpCrossings == 2) { 120 testPassed('Found 2 lines, looking in the ' + direction + ' direction'); 121 } else { 122 testFailed('Found ' + numUpCrossings + ' lines, looking in the ' + 123 direction + ' direction, expected 2'); 124 } 125 } 126 127 function checkResults() { 128 // Check the middle horizontal and middle vertical line of the canvas. 129 let w = gl.drawingBufferWidth; 130 let h = gl.drawingBufferHeight; 131 let t = 100; 132 let arr = new Uint8Array(4 * w); 133 gl.readPixels(0, Math.floor(h / 2), 134 w, 1, gl.RGBA, gl.UNSIGNED_BYTE, arr); 135 checkLine(arr, t, 'horizontal'); 136 arr = new Uint8Array(4 * h); 137 gl.readPixels(Math.floor(w / 2), 0, 138 1, h, gl.RGBA, gl.UNSIGNED_BYTE, arr); 139 checkLine(arr, t, 'vertical'); 140 } 141 142 function runTest(contextVersion, canvasId, useAntialiasing) { 143 switch (contextVersion) { 144 case 1: { 145 setupWebGL1Test(canvasId, useAntialiasing); 146 break; 147 } 148 case 2: { 149 setupWebGL2Test(canvasId, useAntialiasing); 150 } 151 } 152 setupLines(); 153 renderLines(contextVersion, useAntialiasing); 154 checkResults(); 155 } 156 157 function runTests() { 158 runTest(contextVersion, 'testbed', false); 159 runTest(contextVersion, 'testbed2', true); 160 } 161 162 runTests(); 163 let successfullyParsed = true;