tex-image-and-sub-image-2d-with-video.js (12958B)
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 // This block needs to be outside the onload handler in order for this 8 // test to run reliably in WebKit's test harness (at least the 9 // Chromium port). https://bugs.webkit.org/show_bug.cgi?id=87448 10 initTestingHarness(); 11 12 var old = debug; 13 var debug = function(msg) { 14 bufferedLogToConsole(msg); 15 old(msg); 16 }; 17 18 function generateTest(internalFormat, pixelFormat, pixelType, prologue, resourcePath, defaultContextVersion) { 19 var wtu = WebGLTestUtils; 20 var tiu = TexImageUtils; 21 var gl = null; 22 var successfullyParsed = false; 23 24 // Test each format separately because many browsers implement each 25 // differently. Some might be GPU accelerated, some might not. Etc... 26 var videos = [ 27 { src: resourcePath + "red-green.mp4" , type: 'video/mp4; codecs="avc1.42E01E, mp4a.40.2"', }, 28 { src: resourcePath + "red-green.webmvp8.webm" , type: 'video/webm; codecs="vp8, vorbis"', }, 29 { src: resourcePath + "red-green.bt601.vp9.webm", type: 'video/webm; codecs="vp9"', }, 30 ]; 31 32 function init() 33 { 34 description('Verify texImage2D and texSubImage2D code paths taking video elements (' + internalFormat + '/' + pixelFormat + '/' + pixelType + ')'); 35 36 // Set the default context version while still allowing the webglVersion URL query string to override it. 37 wtu.setDefault3DContextVersion(defaultContextVersion); 38 gl = wtu.create3DContext("example"); 39 40 if (!prologue(gl)) { 41 finishTest(); 42 return; 43 } 44 45 gl.clearColor(0,0,0,1); 46 gl.clearDepth(1); 47 48 runTest(); 49 } 50 51 function runOneIteration(videoElement, unpackColorSpace, useTexSubImage2D, flipY, topColorName, bottomColorName, sourceSubRectangle, program, bindingTarget) 52 { 53 sourceSubRectangleString = ''; 54 if (sourceSubRectangle) { 55 sourceSubRectangleString = ' sourceSubRectangle=' + sourceSubRectangle; 56 } 57 unpackColorSpaceString = ''; 58 if (unpackColorSpace) { 59 unpackColorSpaceString = ' unpackColorSpace=' + unpackColorSpace; 60 } 61 debug('Testing ' + (useTexSubImage2D ? 'texSubImage2D' : 'texImage2D') + 62 ' with flipY=' + flipY + ' bindingTarget=' + 63 (bindingTarget == gl.TEXTURE_2D ? 'TEXTURE_2D' : 'TEXTURE_CUBE_MAP') + 64 sourceSubRectangleString + unpackColorSpaceString); 65 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 66 // Disable any writes to the alpha channel 67 gl.colorMask(1, 1, 1, 0); 68 var texture = gl.createTexture(); 69 // Bind the texture to texture unit 0 70 gl.bindTexture(bindingTarget, texture); 71 // Set up texture parameters 72 gl.texParameteri(bindingTarget, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 73 gl.texParameteri(bindingTarget, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 74 gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 75 gl.texParameteri(bindingTarget, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 76 // Set up pixel store parameters 77 gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, flipY); 78 gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false); 79 var targets = [gl.TEXTURE_2D]; 80 if (bindingTarget == gl.TEXTURE_CUBE_MAP) { 81 targets = [gl.TEXTURE_CUBE_MAP_POSITIVE_X, 82 gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 83 gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 84 gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 85 gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 86 gl.TEXTURE_CUBE_MAP_NEGATIVE_Z]; 87 } 88 // Handle target color space. 89 if (unpackColorSpace) { 90 gl.unpackColorSpace = unpackColorSpace; 91 } 92 // Handle the source sub-rectangle if specified (WebGL 2.0 only) 93 if (sourceSubRectangle) { 94 gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, sourceSubRectangle[0]); 95 gl.pixelStorei(gl.UNPACK_SKIP_ROWS, sourceSubRectangle[1]); 96 } 97 // Upload the videoElement into the texture 98 for (var tt = 0; tt < targets.length; ++tt) { 99 if (sourceSubRectangle) { 100 // Initialize the texture to black first 101 if (useTexSubImage2D) { 102 // Skip sub-rectangle tests for cube map textures for the moment. 103 if (bindingTarget == gl.TEXTURE_CUBE_MAP) { 104 continue; 105 } 106 gl.texImage2D(targets[tt], 0, gl[internalFormat], 107 sourceSubRectangle[2], sourceSubRectangle[3], 0, 108 gl[pixelFormat], gl[pixelType], null); 109 gl.texSubImage2D(targets[tt], 0, 0, 0, 110 sourceSubRectangle[2], sourceSubRectangle[3], 111 gl[pixelFormat], gl[pixelType], videoElement); 112 } else { 113 gl.texImage2D(targets[tt], 0, gl[internalFormat], 114 sourceSubRectangle[2], sourceSubRectangle[3], 0, 115 gl[pixelFormat], gl[pixelType], videoElement); 116 } 117 } else { 118 // Initialize the texture to black first 119 if (useTexSubImage2D) { 120 var width = videoElement.videoWidth; 121 var height = videoElement.videoHeight; 122 if (bindingTarget == gl.TEXTURE_CUBE_MAP) { 123 // cube map texture must be square. 124 width = Math.max(width, height); 125 height = width; 126 } 127 gl.texImage2D(targets[tt], 0, gl[internalFormat], 128 width, height, 0, 129 gl[pixelFormat], gl[pixelType], null); 130 gl.texSubImage2D(targets[tt], 0, 0, 0, gl[pixelFormat], gl[pixelType], videoElement); 131 } else { 132 gl.texImage2D(targets[tt], 0, gl[internalFormat], gl[pixelFormat], gl[pixelType], videoElement); 133 } 134 } 135 } 136 137 if (sourceSubRectangle) { 138 gl.pixelStorei(gl.UNPACK_SKIP_PIXELS, 0); 139 gl.pixelStorei(gl.UNPACK_SKIP_ROWS, 0); 140 } 141 142 var c = document.createElement("canvas"); 143 c.width = 16; 144 c.height = 16; 145 c.style.border = "1px solid black"; 146 var ctx = c.getContext("2d"); 147 ctx.drawImage(videoElement, 0, 0, 16, 16); 148 document.body.appendChild(c); 149 150 var loc; 151 if (bindingTarget == gl.TEXTURE_CUBE_MAP) { 152 loc = gl.getUniformLocation(program, "face"); 153 } 154 155 // Compute the test colors. This test only tests RGB (not A). 156 const topColor = wtu.colorAsSampledWithInternalFormat( 157 wtu.namedColorInColorSpace(topColorName, unpackColorSpace), 158 internalFormat).slice(0, 3); 159 const bottomColor = wtu.colorAsSampledWithInternalFormat( 160 wtu.namedColorInColorSpace(bottomColorName, unpackColorSpace), 161 internalFormat).slice(0, 3); 162 for (var tt = 0; tt < targets.length; ++tt) { 163 if (bindingTarget == gl.TEXTURE_CUBE_MAP) { 164 gl.uniform1i(loc, targets[tt]); 165 } 166 // Draw the triangles 167 wtu.clearAndDrawUnitQuad(gl, [0, 0, 0, 255]); 168 // Check a few pixels near the top and bottom and make sure they have 169 // the right color. 170 const tolerance = Math.max(6, tiu.tolerance(internalFormat, pixelFormat, pixelType)); 171 debug("Checking lower left corner"); 172 wtu.checkCanvasRect(gl, 4, 4, 2, 2, bottomColor, 173 "shouldBe " + bottomColor, tolerance); 174 debug("Checking upper left corner"); 175 wtu.checkCanvasRect(gl, 4, gl.canvas.height - 8, 2, 2, topColor, 176 "shouldBe " + topColor, tolerance); 177 } 178 } 179 180 function runTest(videoElement) 181 { 182 var cases = [ 183 { sub: false, flipY: true, topColor: 'Red', bottomColor: 'Green' }, 184 { sub: false, flipY: false, topColor: 'Green', bottomColor: 'Red' }, 185 { sub: true, flipY: true, topColor: 'Red', bottomColor: 'Green' }, 186 { sub: true, flipY: false, topColor: 'Green', bottomColor: 'Red' }, 187 ]; 188 189 if (wtu.getDefault3DContextVersion() > 1) { 190 cases = cases.concat([ 191 { sub: false, flipY: false, topColor: 'Red', bottomColor: 'Red', 192 sourceSubRectangle: [20, 16, 40, 32] }, 193 { sub: false, flipY: true, topColor: 'Green', bottomColor: 'Green', 194 sourceSubRectangle: [20, 16, 40, 32] }, 195 { sub: false, flipY: false, topColor: 'Green', bottomColor: 'Green', 196 sourceSubRectangle: [20, 80, 40, 32] }, 197 { sub: false, flipY: true, topColor: 'Red', bottomColor: 'Red', 198 sourceSubRectangle: [20, 80, 40, 32] }, 199 { sub: true, flipY: false, topColor: 'Red', bottomColor: 'Red', 200 sourceSubRectangle: [20, 16, 40, 32] }, 201 { sub: true, flipY: true, topColor: 'Green', bottomColor: 'Green', 202 sourceSubRectangle: [20, 16, 40, 32] }, 203 { sub: true, flipY: false, topColor: 'Green', bottomColor: 'Green', 204 sourceSubRectangle: [20, 80, 40, 32] }, 205 { sub: true, flipY: true, topColor: 'Red', bottomColor: 'Red', 206 sourceSubRectangle: [20, 80, 40, 32] }, 207 ]); 208 } 209 210 cases = tiu.crossProductTestCasesWithUnpackColorSpaces( 211 cases, tiu.unpackColorSpacesToTest(gl)); 212 213 function runTexImageTest(bindingTarget) { 214 var program; 215 if (bindingTarget == gl.TEXTURE_2D) { 216 program = tiu.setupTexturedQuad(gl, internalFormat); 217 } else { 218 program = tiu.setupTexturedQuadWithCubeMap(gl, internalFormat); 219 } 220 221 return new Promise(function(resolve, reject) { 222 var videoNdx = 0; 223 var video; 224 function runNextVideo() { 225 if (video) { 226 video.pause(); 227 } 228 229 if (videoNdx == videos.length) { 230 resolve("SUCCESS"); 231 return; 232 } 233 234 var info = videos[videoNdx++]; 235 debug(""); 236 debug("testing: " + info.type); 237 video = document.createElement("video"); 238 video.muted = true; 239 var canPlay = true; 240 if (!video.canPlayType) { 241 testFailed("video.canPlayType required method missing"); 242 runNextVideo(); 243 return; 244 } 245 246 if(!video.canPlayType(info.type).replace(/no/, '')) { 247 debug(info.type + " unsupported"); 248 runNextVideo(); 249 return; 250 }; 251 252 document.body.appendChild(video); 253 video.type = info.type; 254 video.src = info.src; 255 wtu.startPlayingAndWaitForVideo(video, runTest); 256 } 257 function runTest() { 258 for (var i in cases) { 259 if (bindingTarget == gl.TEXTURE_CUBE_MAP) { 260 // Cube map texture must be square but video is not square. 261 if (!cases[i].sub) { 262 break; 263 } 264 // Skip sub-rectangle tests for cube map textures for the moment. 265 if (cases[i].sourceSubRectangle) { 266 break; 267 } 268 } 269 runOneIteration(video, cases[i].unpackColorSpace, cases[i].sub, cases[i].flipY, 270 cases[i].topColor, 271 cases[i].bottomColor, 272 cases[i].sourceSubRectangle, 273 program, bindingTarget); 274 } 275 runNextVideo(); 276 } 277 runNextVideo(); 278 }); 279 } 280 281 runTexImageTest(gl.TEXTURE_2D).then(function(val) { 282 runTexImageTest(gl.TEXTURE_CUBE_MAP).then(function(val) { 283 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "should be no errors"); 284 finishTest(); 285 }); 286 }); 287 } 288 289 return init; 290 }