index.html (14340B)
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 lang="en"> 9 <head> 10 <meta charset="utf-8"> 11 <title>Parallel Shader Compile test</title> 12 13 <style> 14 body { 15 margin: 0; 16 } 17 #log { 18 margin: 16px; 19 } 20 @keyframes move { 21 0% { left: 0%; } 22 50% { left: calc(100% - 64px); } 23 100% { left: 0%; } 24 } 25 #block { 26 position: relative; 27 bottom: 0%; 28 left: 0%; 29 width: 32px; 30 height: 32px; 31 background-color: #07f; 32 33 animation-name: move; 34 animation-duration: 2000ms; 35 animation-iteration-count: infinite; 36 } 37 .container { 38 display: flex; 39 flex-wrap: wrap; 40 } 41 .button { 42 width: 260px 43 } 44 </style> 45 </head> 46 <body> 47 <pre id='log'></pre> 48 49 <!-- The smoothness of the block's moving indicates whether the main thread is too busy. --> 50 <div id='block'></div> 51 52 <script> 53 var testGroup; 54 55 window.addEventListener('error', function (err) { 56 var logElement = document.getElementById('log'); 57 logElement.textContent += ' \n'; 58 logElement.textContent += err.error.stack.replace( 59 new RegExp(window.location.href, 'g'), '/') + '\n'; 60 }); 61 62 function setupGLContextSerial(testRun) { 63 var infoElement = testRun.logElement; 64 65 testRun.gl = document.createElement('canvas').getContext('webgl2'); 66 if (testRun.gl) { 67 infoElement.textContent += 'webgl2 context created.' + '\n\n'; 68 return true; 69 } else { 70 infoElement.textContent += 'webgl2 context is not supported.' + '\n\n'; 71 return false; 72 } 73 } 74 75 function setupGLContextParallel(testRun) { 76 var infoElement = testRun.logElement; 77 if (setupGLContextSerial(testRun)) { 78 // Enable KHR_parallel_shader_compile extension 79 testRun.ext = testRun.gl.getExtension('KHR_parallel_shader_compile'); 80 if (testRun.ext) { 81 return true; 82 } else { 83 infoElement.textContent += 'KHR_parallel_shader_compile is unavailable, you' + 84 ' may need to turn on the webgl draft extensions for your browser.' 85 } 86 } 87 return false; 88 } 89 90 function releasePrograms(testRun) { 91 var gl = testRun.gl; 92 93 var programs = testRun.programs; 94 for (var i = 0; i < programs.length; i++) { 95 var program = programs[i]; 96 if (program.vShader) { 97 gl.deleteShader(program.vShader); 98 program.vShader = null; 99 } 100 if (program.fShader) { 101 gl.deleteShader(program.fShader); 102 program.fShader = null; 103 } 104 if (program.program) { 105 gl.deleteProgram(program.program); 106 program.program = null; 107 } 108 } 109 } 110 111 function showStatistics(testRun) { 112 var infoElement = testRun.logElement; 113 infoElement.textContent += ' ' + '\n'; 114 infoElement.textContent += (Math.round(testRun.elapsedTotal * 100) / 100) + 115 'ms - ' + 'all shaders compiled, and linked.\n'; 116 infoElement.textContent += ' ' + '\n'; 117 infoElement.textContent += 'done.' + '\n'; 118 119 releasePrograms(testRun); 120 } 121 122 function checkShader(gl, shader, infoElement) { 123 if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) { 124 var info = gl.getShaderInfoLog(shader); 125 infoElement.textContent += 'couldn\'t compile shader:\n'; 126 infoElement.textContent += info.toString() + '\n'; 127 return false; 128 } 129 return true; 130 } 131 132 function checkProgram(gl, program, infoElement) { 133 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) { 134 var info = gl.getProgramInfoLog(program); 135 infoElement.textContent += ' ' + '\n'; 136 infoElement.textContent += 'couldn\'t link program:\n'; 137 infoElement.textContent += info.toString() + '\n'; 138 return false; 139 } 140 return true; 141 } 142 143 function makeAllProgramsSerial(testRun) { 144 var gl = testRun.gl; 145 var infoElement = testRun.logElement; 146 147 var programs = testRun.programs; 148 for (var i = 0; i < programs.length; i++) { 149 var program = programs[i]; 150 // vertex shader compilation 151 var vShader = gl.createShader(gl.VERTEX_SHADER); 152 gl.shaderSource(vShader, program.vSource); 153 gl.compileShader(vShader); 154 checkShader(gl, vShader, infoElement); 155 156 // fragment shader compilation 157 var fShader = gl.createShader(gl.FRAGMENT_SHADER); 158 gl.shaderSource(fShader, program.fSource); 159 gl.compileShader(fShader); 160 checkShader(gl, fShader, infoElement); 161 162 // program 163 var programHandle = gl.createProgram(); 164 gl.attachShader(programHandle, vShader); 165 gl.attachShader(programHandle, fShader); 166 gl.linkProgram(programHandle); 167 checkProgram(gl, programHandle, infoElement); 168 } 169 testRun.elapsedTotal = performance.now() - testRun.start; 170 showStatistics(testRun); 171 }; 172 173 function makeAllProgramsParallel(testRun) { 174 var gl = testRun.gl; 175 var infoElement = testRun.logElement; 176 177 var programs = testRun.programs; 178 for (var i = 0; i < programs.length; i++) { 179 var program = programs[i]; 180 var vShader = gl.createShader(gl.VERTEX_SHADER); 181 gl.shaderSource(vShader, program.vSource); 182 gl.compileShader(vShader); 183 184 var fShader = gl.createShader(gl.FRAGMENT_SHADER); 185 gl.shaderSource(fShader, program.fSource); 186 gl.compileShader(fShader); 187 188 programHandle = gl.createProgram(); 189 gl.attachShader(programHandle, vShader); 190 gl.attachShader(programHandle, fShader); 191 192 program.vShader = vShader; 193 program.fShader = fShader; 194 program.program = programHandle; 195 program.status = "Compiling"; 196 } 197 198 function checkCompletion() { 199 var ext = testRun.ext; 200 201 var allProgramsLinked = true; 202 203 for (var i = 0; i < programs.length; i++) { 204 var program = programs[i]; 205 switch (program.status) { 206 case "Compiling": 207 if (gl.getShaderParameter(program.vShader, ext.COMPLETION_STATUS_KHR) && 208 gl.getShaderParameter(program.fShader, ext.COMPLETION_STATUS_KHR)) 209 { 210 checkShader(gl, program.vShader, infoElement); 211 checkShader(gl, program.fShader, infoElement); 212 gl.linkProgram(program.program); 213 program.status = "Linking"; 214 } 215 allProgramsLinked = false; 216 break; 217 218 case "Linking": 219 if (gl.getProgramParameter(program.program, ext.COMPLETION_STATUS_KHR)) 220 { 221 checkProgram(gl, program.program, infoElement); 222 program.status = "Done"; 223 } 224 else { 225 allProgramsLinked = false; 226 } 227 break; 228 229 case "Done": 230 break; 231 } 232 } 233 234 if (allProgramsLinked) { 235 testRun.elapsedTotal = performance.now() - testRun.start; 236 showStatistics(testRun); 237 } 238 else { 239 requestAnimationFrame(checkCompletion); 240 } 241 } 242 requestAnimationFrame(checkCompletion); 243 } 244 245 function parsePrograms(testRun) { 246 var gl = testRun.gl; 247 var infoElement = testRun.logElement; 248 249 // Parse programs from the cached text, formatted as: 250 // __BEGINPROGRAM__ 251 // __VERTEXSHADER__ 252 // shader source line 253 // ... 254 // __FRAGMENTSHADER__ 255 // shader source line 256 // ... 257 // __ENDPROGRAM__ 258 // 259 // __BEGINPROGRAM__ 260 // ... 261 var arrayOfLines = testRun.test.shaderCache.match(/[^\r\n]+/g); 262 var programs = []; 263 var currentProgram = {}; 264 var currentShader; 265 var shaderSourceLine = false; 266 for (var ii = 0; ii < arrayOfLines.length; ii++) { 267 var cur = arrayOfLines[ii]; 268 // Use random numbers to fool the program cache mechanism. 269 if (cur.indexOf('PROGRAM_CACHE_BREAKER_RANDOM') != -1) { 270 cur = cur.replace('PROGRAM_CACHE_BREAKER_RANDOM', Math.random()) 271 } 272 273 if (cur == '__VERTEXSHADER__') { 274 currentShader = []; 275 shaderSourceLine = true; 276 } else if (cur == '__FRAGMENTSHADER__') { 277 currentProgram.vSource = currentShader.join('\n'); 278 279 currentShader = []; 280 shaderSourceLine = true; 281 } else if (cur == '__ENDPROGRAM__') { 282 currentProgram.fSource = currentShader.join('\n'); 283 programs.push(currentProgram); 284 285 currentProgram = {}; 286 currentShader = []; 287 shaderSourceLine = false; 288 } else if (shaderSourceLine) { 289 currentShader.push(cur); 290 } 291 } 292 293 infoElement.textContent += programs.length + ' programs found.' + '\n'; 294 infoElement.textContent += 'starting compilations ...' + '\n'; 295 296 testRun.start = performance.now(); 297 298 testRun.programs = programs; 299 300 testRun.makeAllPrograms(testRun); 301 }; 302 303 304 function runTest(index, isParallel) { 305 var testRun = {}; 306 var test = testGroup[index]; 307 testRun.test = test; 308 testRun.name = test.name + (isParallel ? "_parallel" : "_serial"); 309 testRun.logElement = document.getElementById(testRun.name); 310 testRun.logElement.textContent = ''; 311 312 testRun.setupGLContext = 313 (isParallel ? setupGLContextParallel : setupGLContextSerial); 314 315 testRun.makeAllPrograms = 316 (isParallel ? makeAllProgramsParallel : makeAllProgramsSerial); 317 318 if (!testRun.setupGLContext(testRun)) { 319 return; 320 } 321 322 if (test.shaderCache === undefined) { 323 // load shader cache 324 var xhr = new XMLHttpRequest(); 325 xhr.addEventListener('load', function() { 326 test.shaderCache = xhr.responseText; 327 328 requestAnimationFrame(function() { 329 parsePrograms(testRun); 330 }); 331 }); 332 xhr.open('GET', test.location); 333 xhr.send(); 334 } else { 335 parsePrograms(testRun); 336 } 337 } 338 339 function createElement(element, attribute, inner) { 340 if (element === undefined) { 341 return false; 342 } 343 if (inner === undefined) { 344 inner = []; 345 } 346 var el = document.createElement(element); 347 if (typeof(attribute) === 'object') { 348 for (var key in attribute) { 349 el.setAttribute(key, attribute[key]); 350 } 351 } 352 if (!Array.isArray(inner)) { 353 inner = [inner]; 354 } 355 for (var k = 0; k < inner.length; k++) { 356 if (inner[k].tagName) { 357 el.appendChild(inner[k]); 358 } else { 359 el.appendChild(document.createTextNode(inner[k])); 360 } 361 } 362 return el; 363 } 364 365 var container = createElement("div", {"class": "container"}); 366 document.body.appendChild(container); 367 368 testGroup = [{ 369 'location': './shaders/aquarium/shader-cache.txt', 370 'name': 'aquarium' 371 }, 372 ]; 373 374 testGroup.forEach((test, index) => { 375 376 function createTestView(test, index, isParallel) { 377 378 testName = test.name + (isParallel ? "_parallel" : "_serial"); 379 380 var tButton = createElement( 381 'button', 382 {'class': 'button', 'onclick': 'runTest(' + index + ', ' + isParallel + ')'}, 383 testName 384 ); 385 386 var tPrex = createElement("pre"); 387 var tPre = createElement("textarea", { "id": testName, "rows": 10, "cols": 30}); 388 var tDivContainer = createElement( 389 "div", 390 {"id": " " + testName + "_container"}, 391 [tButton, tPrex, tPre] 392 ); 393 container.appendChild(tDivContainer); 394 } 395 396 createTestView(test, index, false); 397 createTestView(test, index, true); 398 }); 399 400 </script> 401 </body>