2 Copyright (c) 2023 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 OES_sample_variables 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 OES_sample_variables extension, if it is available.");
26 var wtu
= WebGLTestUtils
;
27 var gl
= wtu
.create3DContext("c", { antialias
: false }, 2);
30 function runShaderTests(extensionEnabled
) {
32 debug("Testing various shader compiles with extension " + (extensionEnabled
? "enabled" : "disabled"));
34 const macro
= `#version 300 es
35 precision highp float;
36 out vec4 my_FragColor;
38 #ifdef GL_OES_sample_variables
39 my_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
41 #error no GL_OES_sample_variables;
45 // Expect the macro shader to succeed ONLY if enabled
46 if (wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, macro
])) {
47 if (extensionEnabled
) {
48 testPassed("Macro defined in shaders when extension is enabled");
50 testFailed("Macro defined in shaders when extension is disabled");
53 if (extensionEnabled
) {
54 testFailed("Macro not defined in shaders when extension is enabled");
56 testPassed("Macro not defined in shaders when extension is disabled");
60 const missing
= `#version 300 es
61 precision highp float;
62 out vec4 my_FragColor;
64 gl_SampleMask[0] = gl_SampleMaskIn[0] & 0x55555555;
65 my_FragColor = vec4(gl_SamplePosition.yx, float(gl_SampleID), float(gl_MaxSamples + gl_NumSamples));
68 // Always expect the shader missing the #extension pragma to fail (whether enabled or not)
69 if (wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, missing
])) {
70 testFailed("Sample variables allowed without #extension pragma");
72 testPassed("Sample variables disallowed without #extension pragma");
75 const valid
= `#version 300 es
76 #extension GL_OES_sample_variables : enable
77 precision highp float;
78 out vec4 my_FragColor;
80 gl_SampleMask[0] = gl_SampleMaskIn[0] & 0x55555555;
81 my_FragColor = vec4(gl_SamplePosition.yx, float(gl_SampleID), float(gl_MaxSamples + gl_NumSamples));
84 // Try to compile a shader using sample variables that should only succeed if enabled
85 if (wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, valid
])) {
86 if (extensionEnabled
) {
87 testPassed("Sample variables compiled successfully when extension enabled");
89 testFailed("Sample variables compiled successfully when extension disabled");
92 if (extensionEnabled
) {
93 testFailed("Sample variables failed to compile when extension enabled");
95 testPassed("Sample variables failed to compile when extension disabled");
102 function runMaxSamplesTest() {
104 debug("Testing gl_MaxSamples");
106 const frag
= `#version 300 es
107 #extension GL_OES_sample_variables : require
108 precision highp float;
111 color = vec4(float(gl_MaxSamples * 4) / 255.0, 0.0, 0.0, 1.0);
113 gl
.useProgram(wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, frag
]));
115 wtu
.setupUnitQuad(gl
);
116 wtu
.drawUnitQuad(gl
);
118 wtu
.checkCanvas(gl
, [gl
.getParameter(gl
.MAX_SAMPLES
) * 4, 0, 0, 255], "should match MAX_SAMPLES", 1);
121 function runNumSamplesTest() {
123 debug("Testing gl_NumSamples");
125 const rbo
= gl
.createRenderbuffer();
126 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, rbo
);
127 gl
.renderbufferStorageMultisample(gl
.RENDERBUFFER
, 4, gl
.RGBA8
, 32, 32);
129 const fbo
= gl
.createFramebuffer();
130 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
131 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, rbo
);
133 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, gl
.FRAMEBUFFER_COMPLETE
);
135 const frag
= `#version 300 es
136 #extension GL_OES_sample_variables : require
137 precision highp float;
140 if (gl_NumSamples == 4) {
141 color = vec4(0.0, 1.0, 0.0, 1.0);
143 color = vec4(1.0, 0.0, 0.0, 1.0);
146 gl
.useProgram(wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, frag
]));
148 wtu
.setupUnitQuad(gl
);
149 wtu
.drawUnitQuad(gl
);
151 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo
);
152 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, null);
153 gl
.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
155 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, null);
156 wtu
.checkCanvas(gl
, [0, 255, 0, 255], "should be green");
159 function runSampleIDTest() {
161 debug("Testing gl_SampleID");
163 const frag
= `#version 300 es
164 #extension GL_OES_sample_variables : require
165 precision highp float;
169 // Special value when the selected sample is processed, 0.0 otherwise
170 float r = float(gl_SampleID == id ? (1 << gl_SampleID) : 0) * 32.0 / 255.0;
171 // Must always be 0.0
172 float g = float(gl_SampleID < 0 || gl_SampleID >= gl_NumSamples);
173 color = vec4(r, g, 0.0, 1.0);
175 const program
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, frag
]);
176 gl
.useProgram(program
);
178 wtu
.setupUnitQuad(gl
);
180 const rbo
= gl
.createRenderbuffer();
181 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, rbo
);
182 gl
.renderbufferStorageMultisample(gl
.RENDERBUFFER
, 4, gl
.RGBA8
, 32, 32);
184 const fbo
= gl
.createFramebuffer();
185 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
186 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, rbo
);
188 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, gl
.FRAMEBUFFER_COMPLETE
);
190 for (let sample
= 0; sample
< 4; sample
++) {
191 debug(`Sample ${sample} is selected`);
193 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo
);
194 gl
.uniform1i(gl
.getUniformLocation(program
, "id"), sample
);
195 wtu
.drawUnitQuad(gl
);
197 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo
);
198 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, null);
199 gl
.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
201 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, null);
202 wtu
.checkCanvas(gl
, [(1 << sample
) * 8, 0, 0, 255], undefined, 1);
206 function runSampleMaskInTest() {
208 debug("Testing gl_SampleMaskIn");
210 const frag
= `#version 300 es
211 #extension GL_OES_sample_variables : require
212 precision highp float;
214 uint popcount(uint v) {
216 for (; v != 0u; v >>= 1) c += v & 1u;
220 float r = float(popcount(uint(gl_SampleMaskIn[0])));
221 color = vec4(r * 4.0 / 255.0, 0, 0, 1);
224 const program
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, frag
]);
225 gl
.useProgram(program
);
227 // Use a triangle instead of the WTU's quad
228 // to avoid artifacts along the diagonal
229 const vertices
= gl
.createBuffer();
230 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertices
);
231 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
234 -1.0, -1.0]), gl
.STATIC_DRAW
);
235 gl
.enableVertexAttribArray(0);
236 gl
.vertexAttribPointer(0, 2, gl
.FLOAT
, false, 0, 0);
238 function test(sampleCount
, sampleCoverageEnabled
, coverage
) {
239 if (sampleCoverageEnabled
) {
240 gl
.enable(gl
.SAMPLE_COVERAGE
);
242 gl
.disable(gl
.SAMPLE_COVERAGE
);
245 gl
.sampleCoverage(coverage
, false);
247 const rbo
= gl
.createRenderbuffer();
248 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, rbo
);
249 gl
.renderbufferStorageMultisample(gl
.RENDERBUFFER
, sampleCount
, gl
.RGBA8
, 32, 32);
251 const fbo
= gl
.createFramebuffer();
252 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
253 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, rbo
);
255 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, gl
.FRAMEBUFFER_COMPLETE
);
257 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo
);
258 gl
.clear(gl
.COLOR_BUFFER_BIT
);
259 gl
.drawArrays(gl
.TRIANGLES
, 0, 3);
261 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo
);
262 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, null);
263 gl
.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
265 // Shader scales up the number of input samples to increase precision in unorm8 space.
266 let expected
= Math
.max(sampleCount
, 1) * 4;
268 // Sample coverage must not affect single sampled buffers
269 if (sampleCoverageEnabled
&& sampleCount
> 0) {
270 // The number of samples in gl_SampleMaskIn must be affected by the sample
271 // coverage GL state and then the resolved value must be scaled down again.
272 expected
*= coverage
* coverage
;
275 // Check only the red channel
276 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, null);
277 const pixel
= new Uint8Array(4);
278 gl
.readPixels(0, 0, 1, 1, gl
.RGBA
, gl
.UNSIGNED_BYTE
, pixel
);
279 const message
= `Expected: ${expected}, Actual: ${pixel[0]}, ` +
280 `Samples: ${sampleCount}, Sample Coverage: ${sampleCoverageEnabled}, Coverage: ${coverage}`;
281 if (Math
.abs(pixel
[0] - expected
) > 2) {
288 // Include all exposed sample counts and additionally test single-sampled rendering
289 const sampleCounts
= [...gl
.getInternalformatParameter(gl
.RENDERBUFFER
, gl
.RGBA8
, gl
.SAMPLES
), 0];
291 for (const sampleCount
of sampleCounts
) {
292 if (sampleCount
> 32) {
293 // This test will not work with more than 32 samples.
297 for (const sampleCoverageEnabled
of [false, true]) {
298 for (const coverage
of [0.0, 0.5, 1.0]) {
299 if (sampleCount
== 1 && coverage
!= 0.0 && coverage
!= 1.0) {
302 test(sampleCount
, sampleCoverageEnabled
, coverage
);
308 function runSampleMaskInPerSampleTest() {
310 debug("Testing gl_SampleMaskIn with per-sample shading");
312 const frag
= `#version 300 es
313 #extension GL_OES_sample_variables : require
314 precision highp float;
317 float r = float(gl_SampleMaskIn[0] == (1 << gl_SampleID));
318 color = vec4(r, 0, 0, 1);
320 const program
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, frag
]);
321 gl
.useProgram(program
);
323 wtu
.setupUnitQuad(gl
);
325 // Include all exposed sample counts and additionally test single-sampled rendering
326 const sampleCounts
= [...gl
.getInternalformatParameter(gl
.RENDERBUFFER
, gl
.RGBA8
, gl
.SAMPLES
), 0];
327 for (const sampleCount
of sampleCounts
) {
328 if (sampleCount
> 32) {
329 // This test will not work with more than 32 samples.
333 const rbo
= gl
.createRenderbuffer();
334 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, rbo
);
335 gl
.renderbufferStorageMultisample(gl
.RENDERBUFFER
, sampleCount
, gl
.RGBA8
, 32, 32);
337 const fbo
= gl
.createFramebuffer();
338 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
339 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, rbo
);
341 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, gl
.FRAMEBUFFER_COMPLETE
);
343 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo
);
344 wtu
.drawUnitQuad(gl
);
346 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo
);
347 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, null);
348 gl
.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
350 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, null);
351 wtu
.checkCanvas(gl
, [255, 0, 0, 255], `Samples: ${sampleCount}`, 1);
355 function runSampleMaskTest() {
357 debug("Testing gl_SampleMask");
359 const frag
= `#version 300 es
360 #extension GL_OES_sample_variables : require
361 precision highp float;
362 uniform highp int sampleMask;
365 gl_SampleMask[0] = sampleMask;
366 color = vec4(1, 0, 0, 1);
368 const program
= wtu
.setupProgram(gl
, [wtu
.simpleVertexShaderESSL300
, frag
]);
369 gl
.useProgram(program
);
371 // Use a triangle instead of the WTU's quad
372 // to avoid artifacts along the diagonal
373 const vertices
= gl
.createBuffer();
374 gl
.bindBuffer(gl
.ARRAY_BUFFER
, vertices
);
375 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
378 -1.0, -1.0]), gl
.STATIC_DRAW
);
379 gl
.enableVertexAttribArray(0);
380 gl
.vertexAttribPointer(0, 2, gl
.FLOAT
, false, 0, 0);
382 function test(sampleCount
, sampleMask
) {
383 const rbo
= gl
.createRenderbuffer();
384 gl
.bindRenderbuffer(gl
.RENDERBUFFER
, rbo
);
385 gl
.renderbufferStorageMultisample(gl
.RENDERBUFFER
, sampleCount
, gl
.RGBA8
, 32, 32);
387 const fbo
= gl
.createFramebuffer();
388 gl
.bindFramebuffer(gl
.FRAMEBUFFER
, fbo
);
389 gl
.framebufferRenderbuffer(gl
.FRAMEBUFFER
, gl
.COLOR_ATTACHMENT0
, gl
.RENDERBUFFER
, rbo
);
391 wtu
.framebufferStatusShouldBe(gl
, gl
.FRAMEBUFFER
, gl
.FRAMEBUFFER_COMPLETE
);
393 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, fbo
);
394 gl
.clear(gl
.COLOR_BUFFER_BIT
);
395 gl
.uniform1i(gl
.getUniformLocation(program
, "sampleMask"), sampleMask
);
396 gl
.drawArrays(gl
.TRIANGLES
, 0, 3);
398 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, fbo
);
399 gl
.bindFramebuffer(gl
.DRAW_FRAMEBUFFER
, null);
400 gl
.blitFramebuffer(0, 0, 32, 32, 0, 0, 32, 32, gl
.COLOR_BUFFER_BIT
, gl
.NEAREST
);
403 if (sampleCount
> 0) {
404 let mask
= sampleMask
& ((1 << Math
.max(sampleCount
, 1)) - 1);
406 for (; mask
!= 0; mask
>>= 1) bits
+= mask
& 1;
407 expected
= bits
/ Math
.max(sampleCount
, 1);
411 // Check only the red channel
412 gl
.bindFramebuffer(gl
.READ_FRAMEBUFFER
, null);
413 const pixel
= new Uint8Array(4);
414 gl
.readPixels(0, 0, 1, 1, gl
.RGBA
, gl
.UNSIGNED_BYTE
, pixel
);
415 const message
= `Samples: ${sampleCount}, `
416 + `gl_SampleMask[0]: 0x${sampleMask.toString(16).padStart(8, "0").toUpperCase()}, `
417 + `Actual: ${pixel[0]}, Expected: ${expected}`;
418 if (Math
.abs(pixel
[0] - expected
) > 2) {
425 // Include all exposed sample counts and additionally test single-sampled rendering
426 const sampleCounts
= [...gl
.getInternalformatParameter(gl
.RENDERBUFFER
, gl
.RGBA8
, gl
.SAMPLES
), 0];
428 for (const sampleCount
of sampleCounts
) {
429 if (sampleCount
> 31) {
430 // This test will not work with more than 31 samples.
434 for (const sampleMask
of [0xFFFFFFFF, 0x55555555, 0xAAAAAAAA, 0x00000000]) {
435 test(sampleCount
, sampleMask
);
442 testFailed("WebGL context does not exist");
445 testPassed("WebGL context exists");
447 runShaderTests(false);
449 ext
= gl
.getExtension("OES_sample_variables");
450 wtu
.runExtensionSupportedTest(gl
, "OES_sample_variables", ext
!== null);
453 testPassed("No OES_sample_variables support -- this is legal");
455 testPassed("Successfully enabled OES_sample_variables extension");
456 runShaderTests(true);
458 debug("Testing sample variables");
462 runSampleMaskInTest();
463 runSampleMaskInPerSampleTest();
470 var successfullyParsed
= true;
472 <script src=
"../../js/js-test-post.js"></script>