<!--Copyright (c) 2019 The Khronos Group Inc.Use of this source code is governed by an MIT-style license that can befound in the LICENSE.txt file.--><!doctype html><htmllang="en"><head><metacharset="utf-8"><title>Parallel Shader Compile test</title><style>body{margin:0;}#log{margin:16px;}@keyframesmove{0%{left:0%;}50%{left:calc(100%-64px);}100%{left:0%;}}#block{position:relative;bottom:0%;left:0%;width:32px;height:32px;background-color:#07f;animation-name:move;animation-duration:2000ms;animation-iteration-count:infinite;}.container{display:flex;flex-wrap:wrap;}.button{width:260px}</style></head><body><preid='log'></pre><!-- The smoothness of the block's moving indicates whether the main thread is too busy. --><divid='block'></div><script>vartestGroup;window.addEventListener('error',function(err){varlogElement=document.getElementById('log');logElement.textContent+=' \n';logElement.textContent+=err.error.stack.replace(newRegExp(window.location.href,'g'),'/')+'\n';});functionsetupGLContextSerial(testRun){varinfoElement=testRun.logElement;testRun.gl=document.createElement('canvas').getContext('webgl2');if(testRun.gl){infoElement.textContent+='webgl2 context created.'+'\n\n';returntrue;}else{infoElement.textContent+='webgl2 context is not supported.'+'\n\n';returnfalse;}}functionsetupGLContextParallel(testRun){varinfoElement=testRun.logElement;if(setupGLContextSerial(testRun)){// Enable KHR_parallel_shader_compile extensiontestRun.ext=testRun.gl.getExtension('KHR_parallel_shader_compile');if(testRun.ext){returntrue;}else{infoElement.textContent+='KHR_parallel_shader_compile is unavailable, you'+' may need to turn on the webgl draft extensions for your browser.'}}returnfalse;}functionreleasePrograms(testRun){vargl=testRun.gl;varprograms=testRun.programs;for(vari=0;i<programs.length;i++){varprogram=programs[i];if(program.vShader){gl.deleteShader(program.vShader);program.vShader=null;}if(program.fShader){gl.deleteShader(program.fShader);program.fShader=null;}if(program.program){gl.deleteProgram(program.program);program.program=null;}}}functionshowStatistics(testRun){varinfoElement=testRun.logElement;infoElement.textContent+=' '+'\n';infoElement.textContent+=(Math.round(testRun.elapsedTotal*100)/100)+'ms - '+'all shaders compiled, and linked.\n';infoElement.textContent+=' '+'\n';infoElement.textContent+='done.'+'\n';releasePrograms(testRun);}functioncheckShader(gl,shader,infoElement){if(!gl.getShaderParameter(shader,gl.COMPILE_STATUS)){varinfo=gl.getShaderInfoLog(shader);infoElement.textContent+='couldn\'t compile shader:\n';infoElement.textContent+=info.toString()+'\n';returnfalse;}returntrue;}functioncheckProgram(gl,program,infoElement){if(!gl.getProgramParameter(program,gl.LINK_STATUS)){varinfo=gl.getProgramInfoLog(program);infoElement.textContent+=' '+'\n';infoElement.textContent+='couldn\'t link program:\n';infoElement.textContent+=info.toString()+'\n';returnfalse;}returntrue;}functionmakeAllProgramsSerial(testRun){vargl=testRun.gl;varinfoElement=testRun.logElement;varprograms=testRun.programs;for(vari=0;i<programs.length;i++){varprogram=programs[i];// vertex shader compilationvarvShader=gl.createShader(gl.VERTEX_SHADER);gl.shaderSource(vShader,program.vSource);gl.compileShader(vShader);checkShader(gl,vShader,infoElement);// fragment shader compilationvarfShader=gl.createShader(gl.FRAGMENT_SHADER);gl.shaderSource(fShader,program.fSource);gl.compileShader(fShader);checkShader(gl,fShader,infoElement);// programvarprogramHandle=gl.createProgram();gl.attachShader(programHandle,vShader);gl.attachShader(programHandle,fShader);gl.linkProgram(programHandle);checkProgram(gl,programHandle,infoElement);}testRun.elapsedTotal=performance.now()-testRun.start;showStatistics(testRun);};functionmakeAllProgramsParallel(testRun){vargl=testRun.gl;varinfoElement=testRun.logElement;varprograms=testRun.programs;for(vari=0;i<programs.length;i++){varprogram=programs[i];varvShader=gl.createShader(gl.VERTEX_SHADER);gl.shaderSource(vShader,program.vSource);gl.compileShader(vShader);varfShader=gl.createShader(gl.FRAGMENT_SHADER);gl.shaderSource(fShader,program.fSource);gl.compileShader(fShader);programHandle=gl.createProgram();gl.attachShader(programHandle,vShader);gl.attachShader(programHandle,fShader);program.vShader=vShader;program.fShader=fShader;program.program=programHandle;program.status="Compiling";}functioncheckCompletion(){varext=testRun.ext;varallProgramsLinked=true;for(vari=0;i<programs.length;i++){varprogram=programs[i];switch(program.status){case"Compiling":if(gl.getShaderParameter(program.vShader,ext.COMPLETION_STATUS_KHR)&&gl.getShaderParameter(program.fShader,ext.COMPLETION_STATUS_KHR)){checkShader(gl,program.vShader,infoElement);checkShader(gl,program.fShader,infoElement);gl.linkProgram(program.program);program.status="Linking";}allProgramsLinked=false;break;case"Linking":if(gl.getProgramParameter(program.program,ext.COMPLETION_STATUS_KHR)){checkProgram(gl,program.program,infoElement);program.status="Done";}else{allProgramsLinked=false;}break;case"Done":break;}}if(allProgramsLinked){testRun.elapsedTotal=performance.now()-testRun.start;showStatistics(testRun);}else{requestAnimationFrame(checkCompletion);}}requestAnimationFrame(checkCompletion);}functionparsePrograms(testRun){vargl=testRun.gl;varinfoElement=testRun.logElement;// Parse programs from the cached text, formatted as:// __BEGINPROGRAM__// __VERTEXSHADER__// shader source line// ...// __FRAGMENTSHADER__// shader source line// ...// __ENDPROGRAM__//// __BEGINPROGRAM__// ...vararrayOfLines=testRun.test.shaderCache.match(/[^\r\n]+/g);varprograms=[];varcurrentProgram={};varcurrentShader;varshaderSourceLine=false;for(varii=0;ii<arrayOfLines.length;ii++){varcur=arrayOfLines[ii];// Use random numbers to fool the program cache mechanism.if(cur.indexOf('PROGRAM_CACHE_BREAKER_RANDOM')!=-1){cur=cur.replace('PROGRAM_CACHE_BREAKER_RANDOM',Math.random())}if(cur=='__VERTEXSHADER__'){currentShader=[];shaderSourceLine=true;}elseif(cur=='__FRAGMENTSHADER__'){currentProgram.vSource=currentShader.join('\n');currentShader=[];shaderSourceLine=true;}elseif(cur=='__ENDPROGRAM__'){currentProgram.fSource=currentShader.join('\n');programs.push(currentProgram);currentProgram={};currentShader=[];shaderSourceLine=false;}elseif(shaderSourceLine){currentShader.push(cur);}}infoElement.textContent+=programs.length+' programs found.'+'\n';infoElement.textContent+='starting compilations ...'+'\n';testRun.start=performance.now();testRun.programs=programs;testRun.makeAllPrograms(testRun);};functionrunTest(index,isParallel){vartestRun={};vartest=testGroup[index];testRun.test=test;testRun.name=test.name+(isParallel?"_parallel":"_serial");testRun.logElement=document.getElementById(testRun.name);testRun.logElement.textContent='';testRun.setupGLContext=(isParallel?setupGLContextParallel:setupGLContextSerial);testRun.makeAllPrograms=(isParallel?makeAllProgramsParallel:makeAllProgramsSerial);if(!testRun.setupGLContext(testRun)){return;}if(test.shaderCache===undefined){// load shader cachevarxhr=newXMLHttpRequest();xhr.addEventListener('load',function(){test.shaderCache=xhr.responseText;requestAnimationFrame(function(){parsePrograms(testRun);});});xhr.open('GET',test.location);xhr.send();}else{parsePrograms(testRun);}}functioncreateElement(element,attribute,inner){if(element===undefined){returnfalse;}if(inner===undefined){inner=[];}varel=document.createElement(element);if(typeof(attribute)==='object'){for(varkeyinattribute){el.setAttribute(key,attribute[key]);}}if(!Array.isArray(inner)){inner=[inner];}for(vark=0;k<inner.length;k++){if(inner[k].tagName){el.appendChild(inner[k]);}else{el.appendChild(document.createTextNode(inner[k]));}}returnel;}varcontainer=createElement("div",{"class":"container"});document.body.appendChild(container);testGroup=[{'location':'./shaders/aquarium/shader-cache.txt','name':'aquarium'},];testGroup.forEach((test,index)=>{functioncreateTestView(test,index,isParallel){testName=test.name+(isParallel?"_parallel":"_serial");vartButton=createElement('button',{'class':'button','onclick':'runTest('+index+', '+isParallel+')'},testName);vartPrex=createElement("pre");vartPre=createElement("textarea",{"id":testName,"rows":10,"cols":30});vartDivContainer=createElement("div",{"id":" "+testName+"_container"},[tButton,tPrex,tPre]);container.appendChild(tDivContainer);}createTestView(test,index,false);createTestView(test,index,true);});</script></body>