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.
10 <meta charset=
"utf-8">
11 <title>Parallel Shader Compile test
</title>
22 50% { left: calc(
100% -
64px); }
31 background-color: #
07f;
34 animation-duration:
2000ms;
35 animation-iteration-count: infinite;
49 <!-- The smoothness of the block's moving indicates whether the main thread is too busy. -->
50 <div id='block'
></div>
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';
62 function setupGLContextSerial(testRun
) {
63 var infoElement
= testRun
.logElement
;
65 testRun
.gl
= document
.createElement('canvas').getContext('webgl2');
67 infoElement
.textContent
+= 'webgl2 context created.' + '\n\n';
70 infoElement
.textContent
+= 'webgl2 context is not supported.' + '\n\n';
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');
83 infoElement
.textContent
+= 'KHR_parallel_shader_compile is unavailable, you' +
84 ' may need to turn on the webgl draft extensions for your browser.'
90 function releasePrograms(testRun
) {
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;
100 if (program
.fShader
) {
101 gl
.deleteShader(program
.fShader
);
102 program
.fShader
= null;
104 if (program
.program
) {
105 gl
.deleteProgram(program
.program
);
106 program
.program
= null;
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';
119 releasePrograms(testRun
);
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';
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';
143 function makeAllProgramsSerial(testRun
) {
145 var infoElement
= testRun
.logElement
;
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
);
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
);
163 var programHandle
= gl
.createProgram();
164 gl
.attachShader(programHandle
, vShader
);
165 gl
.attachShader(programHandle
, fShader
);
166 gl
.linkProgram(programHandle
);
167 checkProgram(gl
, programHandle
, infoElement
);
169 testRun
.elapsedTotal
= performance
.now() - testRun
.start
;
170 showStatistics(testRun
);
173 function makeAllProgramsParallel(testRun
) {
175 var infoElement
= testRun
.logElement
;
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
);
184 var fShader
= gl
.createShader(gl
.FRAGMENT_SHADER
);
185 gl
.shaderSource(fShader
, program
.fSource
);
186 gl
.compileShader(fShader
);
188 programHandle
= gl
.createProgram();
189 gl
.attachShader(programHandle
, vShader
);
190 gl
.attachShader(programHandle
, fShader
);
192 program
.vShader
= vShader
;
193 program
.fShader
= fShader
;
194 program
.program
= programHandle
;
195 program
.status
= "Compiling";
198 function checkCompletion() {
199 var ext
= testRun
.ext
;
201 var allProgramsLinked
= true;
203 for (var i
= 0; i
< programs
.length
; i
++) {
204 var program
= programs
[i
];
205 switch (program
.status
) {
207 if (gl
.getShaderParameter(program
.vShader
, ext
.COMPLETION_STATUS_KHR
) &&
208 gl
.getShaderParameter(program
.fShader
, ext
.COMPLETION_STATUS_KHR
))
210 checkShader(gl
, program
.vShader
, infoElement
);
211 checkShader(gl
, program
.fShader
, infoElement
);
212 gl
.linkProgram(program
.program
);
213 program
.status
= "Linking";
215 allProgramsLinked
= false;
219 if (gl
.getProgramParameter(program
.program
, ext
.COMPLETION_STATUS_KHR
))
221 checkProgram(gl
, program
.program
, infoElement
);
222 program
.status
= "Done";
225 allProgramsLinked
= false;
234 if (allProgramsLinked
) {
235 testRun
.elapsedTotal
= performance
.now() - testRun
.start
;
236 showStatistics(testRun
);
239 requestAnimationFrame(checkCompletion
);
242 requestAnimationFrame(checkCompletion
);
245 function parsePrograms(testRun
) {
247 var infoElement
= testRun
.logElement
;
249 // Parse programs from the cached text, formatted as:
252 // shader source line
254 // __FRAGMENTSHADER__
255 // shader source line
261 var arrayOfLines
= testRun
.test
.shaderCache
.match(/[^\r\n]+/g);
263 var currentProgram
= {};
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())
273 if (cur
== '__VERTEXSHADER__') {
275 shaderSourceLine
= true;
276 } else if (cur
== '__FRAGMENTSHADER__') {
277 currentProgram
.vSource
= currentShader
.join('\n');
280 shaderSourceLine
= true;
281 } else if (cur
== '__ENDPROGRAM__') {
282 currentProgram
.fSource
= currentShader
.join('\n');
283 programs
.push(currentProgram
);
287 shaderSourceLine
= false;
288 } else if (shaderSourceLine
) {
289 currentShader
.push(cur
);
293 infoElement
.textContent
+= programs
.length
+ ' programs found.' + '\n';
294 infoElement
.textContent
+= 'starting compilations ...' + '\n';
296 testRun
.start
= performance
.now();
298 testRun
.programs
= programs
;
300 testRun
.makeAllPrograms(testRun
);
304 function runTest(index
, isParallel
) {
306 var test
= testGroup
[index
];
308 testRun
.name
= test
.name
+ (isParallel
? "_parallel" : "_serial");
309 testRun
.logElement
= document
.getElementById(testRun
.name
);
310 testRun
.logElement
.textContent
= '';
312 testRun
.setupGLContext
=
313 (isParallel
? setupGLContextParallel
: setupGLContextSerial
);
315 testRun
.makeAllPrograms
=
316 (isParallel
? makeAllProgramsParallel
: makeAllProgramsSerial
);
318 if (!testRun
.setupGLContext(testRun
)) {
322 if (test
.shaderCache
=== undefined) {
324 var xhr
= new XMLHttpRequest();
325 xhr
.addEventListener('load', function() {
326 test
.shaderCache
= xhr
.responseText
;
328 requestAnimationFrame(function() {
329 parsePrograms(testRun
);
332 xhr
.open('GET', test
.location
);
335 parsePrograms(testRun
);
339 function createElement(element
, attribute
, inner
) {
340 if (element
=== undefined) {
343 if (inner
=== undefined) {
346 var el
= document
.createElement(element
);
347 if (typeof(attribute
) === 'object') {
348 for (var key
in attribute
) {
349 el
.setAttribute(key
, attribute
[key
]);
352 if (!Array
.isArray(inner
)) {
355 for (var k
= 0; k
< inner
.length
; k
++) {
356 if (inner
[k
].tagName
) {
357 el
.appendChild(inner
[k
]);
359 el
.appendChild(document
.createTextNode(inner
[k
]));
365 var container
= createElement("div", {"class": "container"});
366 document
.body
.appendChild(container
);
369 'location': './shaders/aquarium/shader-cache.txt',
374 testGroup
.forEach((test
, index
) => {
376 function createTestView(test
, index
, isParallel
) {
378 testName
= test
.name
+ (isParallel
? "_parallel" : "_serial");
380 var tButton
= createElement(
382 {'class': 'button', 'onclick': 'runTest(' + index
+ ', ' + isParallel
+ ')'},
386 var tPrex
= createElement("pre");
387 var tPre
= createElement("textarea", { "id": testName
, "rows": 10, "cols": 30});
388 var tDivContainer
= createElement(
390 {"id": " " + testName
+ "_container"},
391 [tButton
, tPrex
, tPre
]
393 container
.appendChild(tDivContainer
);
396 createTestView(test
, index
, false);
397 createTestView(test
, index
, true);