2 Copyright (c) 2022 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>WebGL WEBGL_clip_cull_distance Conformance Tests
</title>
12 <LINK rel=
"stylesheet" href=
"../../resources/js-test-style.css"/>
13 <script src=
"../../js/js-test-pre.js"></script>
14 <script src=
"../../js/webgl-test-utils.js"></script>
17 <canvas width=
"32" height=
"32" id=
"c"></canvas>
18 <div id=
"description"></div>
19 <div id=
"console"></div>
22 description("This test verifies the functionality of the WEBGL_clip_cull_distance extension, if it is available.");
26 var wtu
= WebGLTestUtils
;
27 var gl
= wtu
.create3DContext("c", null, 2);
29 const w
= gl
.drawingBufferWidth
;
30 const h
= gl
.drawingBufferHeight
;
32 function runTestNoExtension() {
34 debug("Check parameters and capabilities without the extension");
36 shouldBeNull("gl.getParameter(0x0D32 /* MAX_CLIP_DISTANCES_WEBGL */)");
37 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "parameter unknown without enabling the extension");
38 shouldBeNull("gl.getParameter(0x82F9 /* MAX_CULL_DISTANCES_WEBGL */)");
39 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "parameter unknown without enabling the extension");
40 shouldBeNull("gl.getParameter(0x82FA /* MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL */)");
41 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "parameter unknown without enabling the extension");
43 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
45 const assertState
= (i
) => {
46 shouldBeFalse(`gl.isEnabled(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`);
47 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "parameter unknown without enabling the extension");
49 shouldBeNull(`gl.getParameter(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`);
50 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "parameter unknown without enabling the extension");
53 for (let i
= 0; i
< 8; i
++) {
56 gl
.enable(0x3000 + i
/* CLIP_DISTANCEi_WEBGL */);
57 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "capability unknown without enabling the extension");
61 gl
.disable(0x3000 + i
/* CLIP_DISTANCEi_WEBGL */);
62 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "capability unknown without enabling the extension");
67 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
71 function checkEnums() {
74 shouldBe("ext.MAX_CLIP_DISTANCES_WEBGL", "0x0D32");
75 shouldBe("ext.MAX_CULL_DISTANCES_WEBGL", "0x82F9");
76 shouldBe("ext.MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL", "0x82FA");
77 shouldBe("ext.CLIP_DISTANCE0_WEBGL", "0x3000");
78 shouldBe("ext.CLIP_DISTANCE1_WEBGL", "0x3001");
79 shouldBe("ext.CLIP_DISTANCE2_WEBGL", "0x3002");
80 shouldBe("ext.CLIP_DISTANCE3_WEBGL", "0x3003");
81 shouldBe("ext.CLIP_DISTANCE4_WEBGL", "0x3004");
82 shouldBe("ext.CLIP_DISTANCE5_WEBGL", "0x3005");
83 shouldBe("ext.CLIP_DISTANCE6_WEBGL", "0x3006");
84 shouldBe("ext.CLIP_DISTANCE7_WEBGL", "0x3007");
87 function checkQueries() {
89 debug("Check parameters");
90 shouldBeGreaterThanOrEqual('gl.getParameter(ext.MAX_CLIP_DISTANCES_WEBGL)', '8');
91 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
93 const maxCullDistances
= gl
.getParameter(ext
.MAX_CULL_DISTANCES_WEBGL
);
94 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
96 if (maxCullDistances
== 0) {
97 testPassed("No cull distance support");
98 shouldBe("gl.getParameter(ext.MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL)", "0");
99 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
100 } else if (maxCullDistances
>= 8) {
101 testPassed("Optional cull distance support");
102 shouldBeGreaterThanOrEqual("gl.getParameter(ext.MAX_COMBINED_CLIP_AND_CULL_DISTANCES_WEBGL)", "8");
103 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
105 testFailed("Invalid number of supported cull distances");
109 debug("Check clip distance capabilities");
111 const assertState
= (i
, s
) => {
112 shouldBe(`gl.isEnabled(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`, s
? "true" : "false");
113 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
115 shouldBe(`gl.getParameter(${0x3000 + i} /* CLIP_DISTANCE${i}_WEBGL */)`, s
? "true" : "false");
116 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
119 for (let i
= 0; i
< 8; i
++) {
120 assertState(i
, false);
122 gl
.enable(ext
.CLIP_DISTANCE0_WEBGL
+ i
);
123 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
125 assertState(i
, true);
127 gl
.disable(ext
.CLIP_DISTANCE0_WEBGL
+ i
);
128 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
130 assertState(i
, false);
134 function checkClipDistance() {
136 debug("Check clip distance operation");
138 const vs
= `#version 300 es
139 #extension GL_ANGLE_clip_cull_distance : require
141 uniform vec4 u_plane;
145 gl_Position = vec4(a_position, 0.0, 1.0);
146 gl_ClipDistance[0] = dot(gl_Position, u_plane);
149 const program
= wtu
.setupProgram(gl
, [vs
, wtu
.simpleColorFragmentShaderESSL300
]);
150 gl
.useProgram(program
);
151 gl
.uniform4fv(gl
.getUniformLocation(program
, 'u_color'), [1.0, 0.0, 0.0, 1.0]);
153 gl
.enable(ext
.CLIP_DISTANCE0_WEBGL
);
156 gl
.clearColor(0, 0, 1, 1);
157 gl
.clear(gl
.COLOR_BUFFER_BIT
);
159 wtu
.setupUnitQuad(gl
);
161 // Draw full screen quad with color red
162 gl
.uniform4f(gl
.getUniformLocation(program
, "u_plane"), 1, 0, 0, 0.5);
163 wtu
.drawUnitQuad(gl
);
164 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
166 // All pixels on the left of the plane x = -0.5 must be blue
169 let width
= w
/ 4 - 1;
171 wtu
.checkCanvasRect(gl
, x
, y
, width
, height
,
172 [0, 0, 255, 255], "should be blue");
174 // All pixels on the right of the plane x = -0.5 must be red
179 wtu
.checkCanvasRect(gl
, x
, y
, width
, height
,
180 [255, 0, 0, 255], "should be red");
183 gl
.clearColor(0, 1, 0, 1);
184 gl
.clear(gl
.COLOR_BUFFER_BIT
);
186 // Draw full screen quad with color red
187 gl
.uniform4f(gl
.getUniformLocation(program
, "u_plane"), -1, 0, 0, -0.5);
188 wtu
.drawUnitQuad(gl
);
189 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
191 // All pixels on the left of the plane x = -0.5 must be red
196 wtu
.checkCanvasRect(gl
, x
, y
, width
, height
,
197 [255, 0, 0, 255], "should be red");
199 // All pixels on the right of the plane x = -0.5 must be green
204 wtu
.checkCanvasRect(gl
, x
, y
, width
, height
,
205 [0, 255, 0, 255], "should be green");
207 // Disable CLIP_DISTANCE0 and draw again
208 gl
.disable(ext
.CLIP_DISTANCE0_WEBGL
);
209 wtu
.drawUnitQuad(gl
);
211 // All pixels must be red
212 wtu
.checkCanvas(gl
, [255, 0, 0, 255], "should be red");
215 function checkClipDistanceInterpolation() {
217 debug("Check clip distance interpolation");
219 const vs
= `#version 300 es
220 #extension GL_ANGLE_clip_cull_distance : require
224 gl_Position = vec4(a_position, 0.0, 1.0);
225 gl_ClipDistance[0] = dot(gl_Position, vec4( 1, 0, 0, 0.5));
226 gl_ClipDistance[1] = dot(gl_Position, vec4(-1, 0, 0, 0.5));
227 gl_ClipDistance[2] = dot(gl_Position, vec4( 0, 1, 0, 0.5));
228 gl_ClipDistance[3] = dot(gl_Position, vec4( 0, -1, 0, 0.5));
229 gl_ClipDistance[4] = gl_ClipDistance[0];
230 gl_ClipDistance[5] = gl_ClipDistance[1];
231 gl_ClipDistance[6] = gl_ClipDistance[2];
232 gl_ClipDistance[7] = gl_ClipDistance[3];
235 const fs
= `#version 300 es
236 #extension GL_ANGLE_clip_cull_distance : require
237 precision highp float;
238 out vec4 my_FragColor;
241 float r = gl_ClipDistance[0] + gl_ClipDistance[1];
242 float g = gl_ClipDistance[2] + gl_ClipDistance[3];
243 float b = gl_ClipDistance[4] + gl_ClipDistance[5];
244 float a = gl_ClipDistance[6] + gl_ClipDistance[7];
245 my_FragColor = vec4(r, g, b, a) * 0.5;
248 const program
= wtu
.setupProgram(gl
, [vs
, fs
]);
249 gl
.useProgram(program
);
251 gl
.enable(ext
.CLIP_DISTANCE0_WEBGL
);
252 gl
.enable(ext
.CLIP_DISTANCE1_WEBGL
);
253 gl
.enable(ext
.CLIP_DISTANCE2_WEBGL
);
254 gl
.enable(ext
.CLIP_DISTANCE3_WEBGL
);
255 gl
.enable(ext
.CLIP_DISTANCE4_WEBGL
);
256 gl
.enable(ext
.CLIP_DISTANCE5_WEBGL
);
257 gl
.enable(ext
.CLIP_DISTANCE6_WEBGL
);
258 gl
.enable(ext
.CLIP_DISTANCE7_WEBGL
);
260 wtu
.setupUnitQuad(gl
);
263 gl
.clearColor(0, 0, 1, 1);
264 gl
.clear(gl
.COLOR_BUFFER_BIT
);
266 // Draw full screen quad with color gray
267 wtu
.drawUnitQuad(gl
);
268 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
270 const data
= new Uint8Array(w
* h
* 4);
271 gl
.readPixels(0, 0, w
, h
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, data
);
273 for (let x
= 0; x
< w
; x
++) {
274 for (let y
= 0; y
< h
; y
++) {
275 const currentPosition
= (y
* h
+ x
) * 4;
276 const inside
= (x
>= w
/ 4 && x
< w
* 3 / 4 && y
>= h
/ 4 && y
< h
* 3 / 4);
277 const expected
= inside
? [127, 127, 127, 127] : [0, 0, 255, 255];
278 const actual
= data
.slice(currentPosition
, currentPosition
+ 4);
279 if (Math
.abs(actual
[0] - expected
[0]) > 1 ||
280 Math
.abs(actual
[1] - expected
[1]) > 1 ||
281 Math
.abs(actual
[2] - expected
[2]) > 1 ||
282 Math
.abs(actual
[3] - expected
[3]) > 1) {
288 testPassed("Correct clip distance interpolation");
290 testFailed("Incorrect clip distance interpolation");
294 function checkCullDistance() {
296 debug("Check cull distance operation");
298 if (gl
.getParameter(ext
.MAX_CULL_DISTANCES_WEBGL
) == 0) {
299 testPassed("No cull distance support");
303 const vs
= `#version 300 es
304 #extension GL_ANGLE_clip_cull_distance : require
306 uniform vec4 u_plane;
310 gl_Position = vec4(a_position, 0.0, 1.0);
311 gl_CullDistance[0] = dot(gl_Position, u_plane);
314 const program
= wtu
.setupProgram(gl
, [vs
, wtu
.simpleColorFragmentShaderESSL300
]);
315 gl
.useProgram(program
);
316 gl
.uniform4fv(gl
.getUniformLocation(program
, 'u_color'), [1.0, 0.0, 0.0, 1.0]);
319 gl
.clearColor(0, 0, 1, 1);
320 gl
.clear(gl
.COLOR_BUFFER_BIT
);
322 wtu
.setupUnitQuad(gl
);
324 // Draw full screen quad with color red
325 gl
.uniform4f(gl
.getUniformLocation(program
, "u_plane"), 1, 0, 0, 0.5);
326 wtu
.drawUnitQuad(gl
);
327 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
329 // All pixels must be red
330 wtu
.checkCanvas(gl
, [255, 0, 0, 255], "should be red");
333 gl
.clearColor(0, 1, 0, 1);
334 gl
.clear(gl
.COLOR_BUFFER_BIT
);
336 // Draw full screen quad with color red
337 gl
.uniform4f(gl
.getUniformLocation(program
, "u_plane"), -1, 1, 0, -0.5);
338 wtu
.drawUnitQuad(gl
);
339 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
341 // All pixels above the y > x line must be red
342 const data
= new Uint8Array(w
* h
* 4);
343 gl
.readPixels(0, 0, w
, h
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, data
);
345 for (let x
= 0; x
< w
; ++x
) {
346 for (let y
= 0; y
< h
; ++y
) {
347 if (y
<= x
+ 2 && y
>= x
- 2) continue; // skip the edge
348 const currentPosition
= (y
* h
+ x
) * 4;
349 const actual
= data
.slice(currentPosition
, currentPosition
+ 2);
350 const expected
= (y
> x
) ? [255, 0] : [0, 255];
351 if (actual
[0] != expected
[0] || actual
[1] != expected
[1]) {
357 testPassed("Correct cull distance operation");
359 testFailed("Incorrect cull distance operation");
363 function checkCullDistanceInterpolation() {
365 debug("Check cull distance interpolation");
367 if (gl
.getParameter(ext
.MAX_CULL_DISTANCES_WEBGL
) == 0) {
368 testPassed("No cull distance support");
372 const vs
= `#version 300 es
373 #extension GL_ANGLE_clip_cull_distance : require
377 gl_Position = vec4(a_position, 0.0, 1.0);
378 gl_CullDistance[0] = dot(gl_Position, vec4( 1, 0, 0, 1));
379 gl_CullDistance[1] = dot(gl_Position, vec4(-1, 0, 0, 1));
380 gl_CullDistance[2] = dot(gl_Position, vec4( 0, 1, 0, 1));
381 gl_CullDistance[3] = dot(gl_Position, vec4( 0, -1, 0, 1));
382 gl_CullDistance[4] = gl_CullDistance[0];
383 gl_CullDistance[5] = gl_CullDistance[1];
384 gl_CullDistance[6] = gl_CullDistance[2];
385 gl_CullDistance[7] = gl_CullDistance[3];
388 const fs
= `#version 300 es
389 #extension GL_ANGLE_clip_cull_distance : require
390 precision highp float;
391 out vec4 my_FragColor;
394 float r = gl_CullDistance[0] + gl_CullDistance[1];
395 float g = gl_CullDistance[2] + gl_CullDistance[3];
396 float b = gl_CullDistance[4] + gl_CullDistance[5];
397 float a = gl_CullDistance[6] + gl_CullDistance[7];
398 my_FragColor = vec4(r, g, b, a) * 0.25;
401 const program
= wtu
.setupProgram(gl
, [vs
, fs
]);
402 gl
.useProgram(program
);
405 gl
.clearColor(0, 0, 1, 1);
406 gl
.clear(gl
.COLOR_BUFFER_BIT
);
408 wtu
.setupQuad(gl
, {scale
: 0.5});
410 // Draw a small quad with color gray
411 wtu
.drawUnitQuad(gl
);
412 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "should be no errors");
414 const data
= new Uint8Array(w
* h
* 4);
415 gl
.readPixels(0, 0, w
, h
, gl
.RGBA
, gl
.UNSIGNED_BYTE
, data
);
417 for (let x
= 0; x
< w
; x
++) {
418 for (let y
= 0; y
< h
; y
++) {
419 const currentPosition
= (y
* h
+ x
) * 4;
420 const inside
= (x
>= w
/ 4 && x
< w
* 3 / 4 && y
>= h
/ 4 && y
< h
* 3 / 4);
421 const expected
= inside
? [127, 127, 127, 127] : [0, 0, 255, 255];
422 const actual
= data
.slice(currentPosition
, currentPosition
+ 4);
423 if (Math
.abs(actual
[0] - expected
[0]) > 1 ||
424 Math
.abs(actual
[1] - expected
[1]) > 1 ||
425 Math
.abs(actual
[2] - expected
[2]) > 1 ||
426 Math
.abs(actual
[3] - expected
[3]) > 1) {
432 testPassed("Correct cull distance interpolation");
434 testFailed("Incorrect cull distance interpolation");
438 function runTestExtension() {
443 checkClipDistanceInterpolation();
446 checkCullDistanceInterpolation();
451 testFailed("context does not exist");
453 testPassed("context exists");
455 runTestNoExtension();
457 ext
= gl
.getExtension("WEBGL_clip_cull_distance");
459 wtu
.runExtensionSupportedTest(gl
, "WEBGL_clip_cull_distance", ext
!== null);
464 testPassed("No WEBGL_clip_cull_distance support -- this is legal");
471 var successfullyParsed
= true;
473 <script src=
"../../js/js-test-post.js"></script>