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.
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>
18 border:
20px solid transparent;
19 border-top:
20px solid black;
25 from { transorm: rotate(
0); }
26 to { transform: rotate(
360deg); }
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>
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
);
48 const wtu
= WebGLTestUtils
;
50 const gl
= wtu
.create3DContext();
51 const loseContext
= wtu
.getExtensionWithKnownPrefixes(gl
, "WEBGL_lose_context");
54 const vertexSource
= (extra
) => `
56 vec4 result = vec4(0.${counter++});
60 const fragmentSource
= (extra
) => `
61 precision highp float;
63 vec4 result = vec4(0.${counter++});
65 gl_FragColor = result;
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());
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());
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;
99 testPassed("No KHR_parallel_shader_compile support -- this is legal");
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
;
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)}`);
136 measuredCompileDuration
= performance
.now() - start
;
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
;
151 testFailed(`Querying shader status should not wait for compilation. Took ${duration} ms`);
154 const maximumTimeToWait
= measuredCompileDuration
* 4;
155 while (!gl
.getProgramParameter(program
, COMPLETION_STATUS_KHR
)
156 && performance
.now() - start
< maximumTimeToWait
) {
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.`);
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.");
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
);
188 for (let i
= 0; i
< programs
.length
; ++i
) {
189 if (!gl
.getProgramParameter(programs
[i
], COMPLETION_STATUS_KHR
)) {
195 await
new Promise(requestAnimationFrame
);
199 debug("Checking that status is true when context is lost.");
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();
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.');