Backed out changeset b462e7b742d8 (bug 1908261) for causing multiple reftest failures...
[gecko.git] / dom / canvas / test / webgl-conf / checkout / conformance / extensions / khr-parallel-shader-compile.html
blob6a8b1981e54d0b5ce06c8e323cc7a9d75354b76e
1 <!--
2 Copyright (c) 2020 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 -->
7 <!DOCTYPE html>
8 <html>
9 <head>
10 <meta charset="utf-8">
11 <link rel="stylesheet" href="../../resources/js-test-style.css"/>
12 <script src="../../js/js-test-pre.js"></script>
13 <script src="../../js/webgl-test-utils.js"></script>
14 <style>
15 .spinner {
16 width: 100px;
17 height: 100px;
18 border: 20px solid transparent;
19 border-top: 20px solid black;
20 border-radius: 100%;
21 text-align: center;
22 padding: 10px;
24 @keyframes rotation {
25 from { transorm: rotate(0); }
26 to { transform: rotate(360deg); }
28 </style>
29 </head>
30 <body>
31 <div id="description"></div>
32 <button onclick='compileShaders()'>The spinners below should not stutter when you click this button.</button>
33 <div class="spinner" style="animation: rotation 2s infinite linear;">CSS</div>
34 <div class="spinner" id=spinner>JS</div>
35 <div id="console"></div>
36 <canvas id=canvas></canvas>
37 <script>
38 "use strict";
39 description("Test KHR_parallel_shader_compile");
41 function spinSpinner() {
42 let degrees = (performance.now() / 1000 / 2 % 1.) * 360;
43 spinner.style.transform = `rotate(${degrees}deg)`;
44 requestAnimationFrame(spinSpinner);
46 spinSpinner();
48 const wtu = WebGLTestUtils;
50 const gl = wtu.create3DContext();
51 const loseContext = wtu.getExtensionWithKnownPrefixes(gl, "WEBGL_lose_context");
53 let counter = 0;
54 const vertexSource = (extra) => `
55 void main() {
56 vec4 result = vec4(0.${counter++});
57 ${extra || ''}
58 gl_Position = result;
59 }`;
60 const fragmentSource = (extra) => `
61 precision highp float;
62 void main() {
63 vec4 result = vec4(0.${counter++});
64 ${extra || ''}
65 gl_FragColor = result;
66 }`;
68 let vs = gl.createShader(gl.VERTEX_SHADER);
69 let fs = gl.createShader(gl.FRAGMENT_SHADER);
70 let program = gl.createProgram();
71 gl.attachShader(program, vs);
72 gl.attachShader(program, fs);
74 const COMPLETION_STATUS_KHR = 0x91B1;
76 gl.shaderSource(vs, vertexSource());
77 gl.compileShader(vs);
78 let status = gl.getShaderParameter(vs, COMPLETION_STATUS_KHR);
79 if (status !== null) testFailed('Extension disabled, status should be null');
80 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "extension disabled");
82 gl.shaderSource(fs, fragmentSource());
83 gl.compileShader(fs);
85 gl.linkProgram(program);
86 status = gl.getProgramParameter(program, COMPLETION_STATUS_KHR);
87 if (status !== null) testFailed('Extension disabled, status should be null');
88 wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "extension disabled");
90 const ext = wtu.getExtensionWithKnownPrefixes(gl, "KHR_parallel_shader_compile");
92 let successfullyParsed = false;
94 let extraCode = '';
96 (async () => {
98 if (!ext) {
99 testPassed("No KHR_parallel_shader_compile support -- this is legal");
100 } else {
101 testPassed("Successfully enabled KHR_parallel_shader_compile extension");
103 shouldBe("ext.COMPLETION_STATUS_KHR", "0x91B1");
105 debug("Checking that status is a boolean.");
106 gl.shaderSource(vs, vertexSource());
107 gl.compileShader(vs);
108 let status = gl.getShaderParameter(vs, COMPLETION_STATUS_KHR);
109 if (status !== true && status !== false) testFailed("status should be a boolean");
111 gl.linkProgram(program);
112 status = gl.getProgramParameter(program, COMPLETION_STATUS_KHR);
113 if (status !== true && status !== false) testFailed("status should be a boolean");
115 const minimumShaderCompileDurationMs = 500;
116 debug(`Constructing shader that takes > ${minimumShaderCompileDurationMs} ms to compile.`);
117 let measuredCompileDuration = 0;
118 extraCode = '\n if (true) { result += vec4(0.0000001); }';
119 for (let i = 0; measuredCompileDuration < minimumShaderCompileDurationMs; i++) {
120 extraCode += extraCode;
121 extraCode += extraCode;
122 if (i < 4) continue;
123 gl.shaderSource(vs, vertexSource(extraCode));
124 gl.shaderSource(fs, fragmentSource(extraCode));
125 gl.compileShader(vs);
126 gl.compileShader(fs);
127 gl.linkProgram(program);
128 const start = performance.now();
129 if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
130 testFailed(`Shaders failed to compile.
131 program: ${gl.getProgramInfoLog(program)}
132 vs: ${gl.getShaderInfoLog(vs)}
133 fs: ${gl.getShaderInfoLog(fs)}`);
134 break;
136 measuredCompileDuration = performance.now() - start;
139 debug('');
140 gl.shaderSource(vs, vertexSource(extraCode));
141 gl.shaderSource(fs, fragmentSource(extraCode));
142 gl.compileShader(vs);
143 gl.compileShader(fs);
144 gl.linkProgram(program);
146 let start = performance.now();
147 gl.getShaderParameter(fs, COMPLETION_STATUS_KHR);
148 gl.getShaderParameter(vs, COMPLETION_STATUS_KHR);
149 let duration = performance.now() - start;
150 if (duration > 100)
151 testFailed(`Querying shader status should not wait for compilation. Took ${duration} ms`);
153 let frames = 0;
154 const maximumTimeToWait = measuredCompileDuration * 4;
155 while (!gl.getProgramParameter(program, COMPLETION_STATUS_KHR)
156 && performance.now() - start < maximumTimeToWait) {
157 frames++;
158 await new Promise(requestAnimationFrame);
160 duration = performance.now() - start;
161 if (!gl.getProgramParameter(program, COMPLETION_STATUS_KHR)) {
162 testFailed(`Program took longer than ${maximumTimeToWait} ms to compile. Expected: ${measuredCompileDuration} ms, actual: ${duration} ms`);
163 } else if (!gl.getShaderParameter(vs, COMPLETION_STATUS_KHR) || !gl.getShaderParameter(fs, COMPLETION_STATUS_KHR)) {
164 testFailed('Program linked before shaders finished compiling.');
165 } else if (frames <= 6) {
166 testFailed(`Program should have taken many more than 6 frames to compile. Actual value: ${frames} frames, duration ${performance.now() - start} ms.`);
167 } else {
168 console.log(`COMPLETION_STATUS_KHR sucessfully transitioned from false to true in ${frames} frames and ${duration} ms.`);
169 testPassed(`COMPLETION_STATUS_KHR sucessfully transitioned from false to true`);
172 debug("Checking that compiling lots of programs in parallel eventually completes.");
173 let programs = [];
174 for (let i = 0; i < 256; ++i) {
175 gl.shaderSource(vs, vertexSource());
176 gl.shaderSource(fs, fragmentSource());
177 gl.compileShader(vs);
178 gl.compileShader(fs);
179 let program = gl.createProgram();
180 gl.attachShader(program, vs);
181 gl.attachShader(program, fs);
182 gl.linkProgram(program);
183 programs.push(program);
185 let allDone = false;
186 while (!allDone) {
187 allDone = true;
188 for (let i = 0; i < programs.length; ++i) {
189 if (!gl.getProgramParameter(programs[i], COMPLETION_STATUS_KHR)) {
190 allDone = false;
191 break;
194 if (!allDone) {
195 await new Promise(requestAnimationFrame);
199 debug("Checking that status is true when context is lost.");
200 if (loseContext) {
201 gl.shaderSource(vs, vertexSource(extraCode));
202 gl.shaderSource(fs, fragmentSource(extraCode));
203 gl.compileShader(vs);
204 gl.compileShader(fs);
205 gl.linkProgram(program);
206 loseContext.loseContext();
207 status = gl.getShaderParameter(vs, COMPLETION_STATUS_KHR);
208 if (status !== true) testFailed("shader status should be true when context is lost");
209 status = gl.getProgramParameter(program, COMPLETION_STATUS_KHR);
210 if (status !== true) testFailed("program status should be true when context is lost");
211 loseContext.restoreContext();
212 vs = gl.createShader(gl.VERTEX_SHADER);
213 fs = gl.createShader(gl.FRAGMENT_SHADER);
214 program = gl.createProgram();
217 finishTest();
218 })();
220 async function compileShaders() {
221 console.log('Compiling shaders');
222 const gl = canvas.getContext('webgl');
223 const vs = gl.createShader(gl.VERTEX_SHADER);
224 const fs = gl.createShader(gl.FRAGMENT_SHADER);
225 const program = gl.createProgram();
226 gl.getExtension(wtu.getExtensionWithKnownPrefixes(gl, "KHR_parallel_shader_compile"));
227 gl.attachShader(program, vs);
228 gl.attachShader(program, fs);
229 gl.shaderSource(vs, vertexSource(extraCode));
230 gl.shaderSource(fs, fragmentSource(extraCode));
231 gl.compileShader(vs);
232 gl.compileShader(fs);
233 gl.linkProgram(program);
234 while (!gl.getProgramParameter(program, COMPLETION_STATUS_KHR)) {
235 gl.getShaderParameter(vs, COMPLETION_STATUS_KHR);
236 gl.getShaderParameter(fs, COMPLETION_STATUS_KHR);
237 await new Promise(requestAnimationFrame);
239 gl.getProgramParameter(program, gl.LINK_STATUS);
240 console.log('Compilation finished.');
243 </script>
245 </body>
246 </html>