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>WebGL OES_standard_derivatives 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 <div id=
"description"></div>
18 <canvas id=
"canvas" style=
"width: 50px; height: 50px;"> </canvas>
19 <div id=
"console"></div>
20 <!-- Shaders for testing standard derivatives -->
22 <!-- Shader omitting the required #extension pragma -->
23 <script id=
"missingPragmaFragmentShader" type=
"x-shader/x-fragment">
24 precision mediump float;
25 varying vec2 texCoord;
27 float dx = dFdx(texCoord.x);
28 float dy = dFdy(texCoord.y);
29 float w = fwidth(texCoord.x);
30 gl_FragColor = vec4(dx, dy, w,
1.0);
34 <!-- Shader to test macro definition -->
35 <script id=
"macroFragmentShader" type=
"x-shader/x-fragment">
36 precision mediump float;
38 #ifdef GL_OES_standard_derivatives
39 gl_FragColor = vec4(
0.0,
0.0,
0.0,
0.0);
42 #error no GL_OES_standard_derivatives;
47 <!-- Shader with required #extension pragma -->
48 <script id=
"testFragmentShader" type=
"x-shader/x-fragment">
49 #extension GL_OES_standard_derivatives : enable
50 precision mediump float;
51 varying vec2 texCoord;
53 float dx = dFdx(texCoord.x);
54 float dy = dFdy(texCoord.y);
55 float w = fwidth(texCoord.x);
56 gl_FragColor = vec4(dx, dy, w,
1.0);
59 <!-- Shader with #extension after other code -->
60 <script id=
"testFragmentShaderWithExtensionNotAtTop" type=
"x-shader/x-fragment">
61 precision mediump float;
62 varying vec2 texCoord;
64 #extension GL_OES_standard_derivatives : enable
65 float dx = dFdx(texCoord.x);
66 float dy = dFdy(texCoord.y);
67 float w = fwidth(texCoord.x);
68 gl_FragColor = vec4(dx, dy, w,
1.0);
71 <!-- Shaders to link with test fragment shaders -->
72 <script id=
"goodVertexShader" type=
"x-shader/x-vertex">
73 attribute vec4 vPosition;
74 varying vec2 texCoord;
76 texCoord = vPosition.xy;
77 gl_Position = vPosition;
80 <!-- Shaders to test output -->
81 <script id=
"outputVertexShader" type=
"x-shader/x-vertex">
82 attribute vec4 vPosition;
83 varying vec4 position;
86 gl_Position = vPosition;
89 <script id=
"outputFragmentShader" type=
"x-shader/x-fragment">
90 #extension GL_OES_standard_derivatives : enable
91 precision mediump float;
92 varying vec4 position;
94 float dzdx = dFdx(position.z);
95 float dzdy = dFdy(position.z);
96 float fw = fwidth(position.z);
97 gl_FragColor = vec4(abs(dzdx) *
40.0, abs(dzdy) *
40.0, fw *
40.0,
1.0);
103 description("This test verifies the functionality of the OES_standard_derivatives extension, if it is available.");
107 var wtu
= WebGLTestUtils
;
108 var canvas
= document
.getElementById("canvas");
109 var gl
= wtu
.create3DContext(canvas
);
112 // Run all tests once.
115 // Run all tests against with a new context to test for any cache issues.
117 debug("Testing new context to catch cache errors");
118 gl
= wtu
.create3DContext();
122 function runAllTests() {
124 testFailed("WebGL context does not exist");
126 testPassed("WebGL context exists");
128 // Run tests with extension disabled
129 runHintTestDisabled();
130 runShaderTests(false);
132 // Query the extension and store globally so shouldBe can access it
133 ext
= gl
.getExtension("OES_standard_derivatives");
135 testPassed("No OES_standard_derivatives support -- this is legal");
137 runSupportedTest(false);
139 testPassed("Successfully enabled OES_standard_derivatives extension");
141 runSupportedTest(true);
143 runHintTestEnabled();
144 runShaderTests(true);
146 runUniqueObjectTest();
148 // Run deferred link tests.
149 runDeferredLinkTests();
155 function runSupportedTest(extensionEnabled
) {
156 var supported
= gl
.getSupportedExtensions();
157 if (supported
.indexOf("OES_standard_derivatives") >= 0) {
158 if (extensionEnabled
) {
159 testPassed("OES_standard_derivatives listed as supported and getExtension succeeded");
161 testFailed("OES_standard_derivatives listed as supported but getExtension failed");
164 if (extensionEnabled
) {
165 testFailed("OES_standard_derivatives not listed as supported but getExtension succeeded");
167 testPassed("OES_standard_derivatives not listed as supported and getExtension failed -- this is legal");
172 function runHintTestDisabled() {
173 debug("Testing FRAGMENT_SHADER_DERIVATIVE_HINT_OES with extension disabled");
175 // Use the constant directly as we don't have the extension
176 var FRAGMENT_SHADER_DERIVATIVE_HINT_OES
= 0x8B8B;
178 gl
.getParameter(FRAGMENT_SHADER_DERIVATIVE_HINT_OES
);
179 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "FRAGMENT_SHADER_DERIVATIVE_HINT_OES should not be queryable if extension is disabled");
181 gl
.hint(FRAGMENT_SHADER_DERIVATIVE_HINT_OES
, gl
.DONT_CARE
);
182 wtu
.glErrorShouldBe(gl
, gl
.INVALID_ENUM
, "hint should not accept FRAGMENT_SHADER_DERIVATIVE_HINT_OES if extension is disabled");
185 function runHintTestEnabled() {
186 debug("Testing FRAGMENT_SHADER_DERIVATIVE_HINT_OES with extension enabled");
188 shouldBe("ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES", "0x8B8B");
190 gl
.getParameter(ext
.FRAGMENT_SHADER_DERIVATIVE_HINT_OES
);
191 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "FRAGMENT_SHADER_DERIVATIVE_HINT_OES query should succeed if extension is enabled");
193 // Default value is DONT_CARE
194 if (gl
.getParameter(ext
.FRAGMENT_SHADER_DERIVATIVE_HINT_OES
) == gl
.DONT_CARE
) {
195 testPassed("Default value of FRAGMENT_SHADER_DERIVATIVE_HINT_OES is DONT_CARE");
197 testFailed("Default value of FRAGMENT_SHADER_DERIVATIVE_HINT_OES is not DONT_CARE");
200 // Ensure that we can set the target
201 gl
.hint(ext
.FRAGMENT_SHADER_DERIVATIVE_HINT_OES
, gl
.DONT_CARE
);
202 wtu
.glErrorShouldBe(gl
, gl
.NO_ERROR
, "hint should accept FRAGMENT_SHADER_DERIVATIVE_HINT_OES");
204 // Test all the hint modes
205 var validModes
= ["FASTEST", "NICEST", "DONT_CARE"];
206 var anyFailed
= false;
207 for (var n
= 0; n
< validModes
.length
; n
++) {
208 var mode
= validModes
[n
];
209 gl
.hint(ext
.FRAGMENT_SHADER_DERIVATIVE_HINT_OES
, gl
[mode
]);
210 if (gl
.getParameter(ext
.FRAGMENT_SHADER_DERIVATIVE_HINT_OES
) != gl
[mode
]) {
211 testFailed("Round-trip of hint()/getParameter() failed on mode " + mode
);
216 testPassed("Round-trip of hint()/getParameter() with all supported modes");
220 function runShaderTests(extensionEnabled
) {
222 debug("Testing various shader compiles with extension " + (extensionEnabled
? "enabled" : "disabled"));
224 // Expect the macro shader to succeed ONLY if enabled
226 const macroFragmentProgram
= wtu
.loadProgramFromScriptExpectError(gl
, "goodVertexShader", "macroFragmentShader");
227 if (extensionEnabled
) {
228 if (macroFragmentProgram
) {
230 testPassed("GL_OES_standard_derivatives defined in shaders when extension is enabled");
232 testFailed("GL_OES_standard_derivatives not defined in shaders when extension is enabled");
235 if (macroFragmentProgram
) {
236 testFailed("GL_OES_standard_derivatives defined in shaders when extension is disabled");
238 testPassed("GL_OES_standard_derivatives not defined in shaders when extension disabled");
243 // Always expect the shader missing the #pragma to fail (whether enabled or not)
245 const missingPragmaFragmentProgram
= wtu
.loadProgramFromScriptExpectError(gl
, "goodVertexShader", "missingPragmaFragmentShader");
246 if (missingPragmaFragmentProgram
) {
247 testFailed("Shader built-ins allowed without #extension pragma");
249 testPassed("Shader built-ins disallowed without #extension pragma");
253 // Try to compile a shader using the built-ins that should only succeed if enabled
255 const testFragmentProgram
= wtu
.loadProgramFromScriptExpectError(gl
, "goodVertexShader", "testFragmentShader");
256 if (extensionEnabled
) {
257 if (testFragmentProgram
) {
258 testPassed("Shader built-ins compiled successfully when extension enabled");
260 testFailed("Shader built-ins failed to compile when extension enabled");
263 if (testFragmentProgram
) {
264 testFailed("Shader built-ins compiled successfully when extension disabled");
266 testPassed("Shader built-ins failed to compile when extension disabled");
271 // This tests specifically that #extension directives after other code are
272 // valid, per spec (6.35 GLSL ES #extension directive location).
274 // This test actually has nothing to do with OES_standard_derivatives, but
275 // is inserted here because this extension is ubiquitous.
277 // This test is not as strict as the spec - it doesn't require that "the
278 // scope ... is always the whole shader", because implementations (ANGLE
279 // shader translator) do not actually implement this correctly. The test
280 // coverage is intentionally left incomplete - in practice, all WebGL
281 // shaders already work with the current implementation, so there's no
282 // practical reason to update them to match the spec. Conversely, the
283 // currently implemented rules are too complex to formalize in spec.
285 // Regression test for https://crbug.com/971660 .
287 const testFragmentProgramWithExtensionNotAtTop
= wtu
.loadProgramFromScriptExpectError(gl
, "goodVertexShader", "testFragmentShaderWithExtensionNotAtTop");
288 if (extensionEnabled
) {
289 if (testFragmentProgramWithExtensionNotAtTop
) {
290 testPassed("Shader with #extension after non-preprocessor code: compiled successfully when extension enabled");
292 testFailed("Shader with #extension after non-preprocessor code: failed to compile when extension enabled");
298 function runOutputTests() {
299 // This tests does several draws with various values of z.
300 // The output of the fragment shader is:
301 // [dFdx(z), dFdy(z), fwidth(z), 1.0]
302 // The expected math: (note the conversion to uint8)
303 // canvas.width = canvas.height = 50
304 // dFdx = totalChange.x / canvas.width = 0.5 / 50.0 = 0.01
305 // dFdy = totalChange.y / canvas.height = 0.5 / 50.0 = 0.01
306 // fw = abs(dFdx + dFdy) = 0.01 + 0.01 = 0.02
307 // r = floor(dFdx * 40.0 * 255) = 102
308 // g = floor(dFdy * 40.0 * 255) = 102
309 // b = floor(fw * 40.0 * 255) = 204
311 var e
= 5; // Amount of variance to allow in result pixels - may need to be tweaked higher
313 debug("Testing various draws for valid built-in function behavior");
315 canvas
.width
= 50; canvas
.height
= 50;
316 gl
.viewport(0, 0, canvas
.width
, canvas
.height
);
317 gl
.hint(ext
.FRAGMENT_SHADER_DERIVATIVE_HINT_OES
, gl
.NICEST
);
321 var program
= wtu
.setupProgram(gl
, ["outputVertexShader", "outputFragmentShader"], ['vPosition', 'texCoord0'], [0, 1]);
322 var quadParameters
= wtu
.setupUnitQuad(gl
, positionLoc
, texcoordLoc
);
324 function expectResult(target
, message
) {
332 for (var n
= 0; n
< locations
.length
; n
++) {
333 var loc
= locations
[n
];
334 var px
= Math
.floor(loc
[0] * canvas
.width
);
335 var py
= Math
.floor(loc
[1] * canvas
.height
);
336 wtu
.checkCanvasRect(gl
, px
, py
, 1, 1, target
, message
, 4);
340 function setupBuffers(tl
, tr
, bl
, br
) {
341 gl
.bindBuffer(gl
.ARRAY_BUFFER
, quadParameters
[0]);
342 gl
.bufferData(gl
.ARRAY_BUFFER
, new Float32Array([
348 1.0, -1.0, br
]), gl
.STATIC_DRAW
);
349 gl
.vertexAttribPointer(positionLoc
, 3, gl
.FLOAT
, false, 0, 0);
352 // Draw 1: (no variation)
353 setupBuffers(0.0, 0.0, 0.0, 0.0);
354 wtu
.clearAndDrawUnitQuad(gl
);
355 expectResult([0, 0, 0, 255],
356 "Draw 1 (no variation) should pass");
358 // Draw 2: (variation in x)
359 setupBuffers(1.0, 0.0, 1.0, 0.0);
360 wtu
.clearAndDrawUnitQuad(gl
);
361 expectResult([204, 0, 204, 255],
362 "Draw 2 (variation in x) should pass");
364 // Draw 3: (variation in y)
365 setupBuffers(1.0, 1.0, 0.0, 0.0);
366 wtu
.clearAndDrawUnitQuad(gl
);
367 expectResult([0, 204, 204, 255],
368 "Draw 3 (variation in y) should pass");
370 // Draw 4: (variation in x & y)
371 setupBuffers(1.0, 0.5, 0.5, 0.0);
372 wtu
.clearAndDrawUnitQuad(gl
);
373 expectResult([102, 102, 204, 255],
374 "Draw 4 (variation in x & y) should pass");
377 function runUniqueObjectTest()
379 debug("Testing that getExtension() returns the same object each time");
381 gl
.getExtension("OES_standard_derivatives").myProperty
= 2;
382 webglHarnessCollectGarbage();
383 shouldBe('gl.getExtension("OES_standard_derivatives").myProperty', '2');
386 function runDeferredLinkTests() {
388 debug("Testing deferred shader compilation tests.");
390 // Test for compilation failures that are caused by missing extensions
391 // do not succeed if extensions are enabled during linking. This would
392 // only happen for deferred shader compilations.
394 // First test if link succeeds with extension enabled.
395 var glEnabled
= wtu
.create3DContext();
396 var extEnabled
= glEnabled
.getExtension("OES_standard_derivatives");
398 testFailed("Deferred link test expects the extension to be supported");
401 var vertexShader
= wtu
.loadShaderFromScript(glEnabled
, "goodVertexShader");
402 var fragmentShader
= wtu
.loadShaderFromScript(glEnabled
, "macroFragmentShader");
404 if (!vertexShader
|| !fragmentShader
) {
405 testFailed("Could not create good shaders.");
409 var program
= wtu
.setupProgram(glEnabled
, [vertexShader
, fragmentShader
]);
412 testFailed("Compilation with extension enabled failed.");
416 // Create new context to test link failure without extension enabled.
417 var glDeferred
= wtu
.create3DContext();
419 var vertexShader
= wtu
.loadShaderFromScript(glDeferred
, "goodVertexShader", glDeferred
.VERTEX_SHADER
, undefined, undefined, true);
420 var fragmentShader
= wtu
.loadShaderFromScript(glDeferred
, "macroFragmentShader", glDeferred
.FRAGMENT_SHADER
, undefined, undefined, true);
422 if (vertexShader
== null || fragmentShader
== null) {
423 testFailed("Could not create shaders.");
427 // Shader compilations should have failed due to extensions not enabled.
428 glDeferred
.getExtension("OES_standard_derivatives");
429 var program
= wtu
.setupProgram(glDeferred
, [vertexShader
, fragmentShader
]);
431 testFailed("Compilation with extension disabled then linking with extension enabled should have failed.");
435 testPassed("Compilation with extension disabled then linking with extension enabled.");
439 var successfullyParsed
= true;
441 <script src=
"../../js/js-test-post.js"></script>