summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/conformance/extensions/oes-standard-derivatives.html
blob: f6f4de0243a5dc0f929439d827c9375a877beeb6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
<!--
Copyright (c) 2019 The Khronos Group Inc.
Use of this source code is governed by an MIT-style license that can be
found in the LICENSE.txt file.
-->

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>WebGL OES_standard_derivatives Conformance Tests</title>
<link rel="stylesheet" href="../../resources/js-test-style.css"/>
<script src="../../js/js-test-pre.js"></script>
<script src="../../js/webgl-test-utils.js"></script>
</head>
<body>
<div id="description"></div>
<canvas id="canvas" style="width: 50px; height: 50px;"> </canvas>
<div id="console"></div>
<!-- Shaders for testing standard derivatives -->

<!-- Shader omitting the required #extension pragma -->
<script id="missingPragmaFragmentShader" type="x-shader/x-fragment">
precision mediump float;
varying vec2 texCoord;
void main() {
    float dx = dFdx(texCoord.x);
    float dy = dFdy(texCoord.y);
    float w = fwidth(texCoord.x);
    gl_FragColor = vec4(dx, dy, w, 1.0);
}
</script>

<!-- Shader to test macro definition -->
<script id="macroFragmentShader" type="x-shader/x-fragment">
precision mediump float;
void main() {
#ifdef GL_OES_standard_derivatives
    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0);
#else
    // Error expected
    #error no GL_OES_standard_derivatives;
#endif
}
</script>

<!-- Shader with required #extension pragma -->
<script id="testFragmentShader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;
varying vec2 texCoord;
void main() {
    float dx = dFdx(texCoord.x);
    float dy = dFdy(texCoord.y);
    float w = fwidth(texCoord.x);
    gl_FragColor = vec4(dx, dy, w, 1.0);
}
</script>
<!-- Shader with #extension after other code -->
<script id="testFragmentShaderWithExtensionNotAtTop" type="x-shader/x-fragment">
precision mediump float;
varying vec2 texCoord;
void main() {
#extension GL_OES_standard_derivatives : enable
    float dx = dFdx(texCoord.x);
    float dy = dFdy(texCoord.y);
    float w = fwidth(texCoord.x);
    gl_FragColor = vec4(dx, dy, w, 1.0);
}
</script>
<!-- Shaders to link with test fragment shaders -->
<script id="goodVertexShader" type="x-shader/x-vertex">
attribute vec4 vPosition;
varying vec2 texCoord;
void main() {
    texCoord = vPosition.xy;
    gl_Position = vPosition;
}
</script>
<!-- Shaders to test output -->
<script id="outputVertexShader" type="x-shader/x-vertex">
attribute vec4 vPosition;
varying vec4 position;
void main() {
    position = vPosition;
    gl_Position = vPosition;
}
</script>
<script id="outputFragmentShader" type="x-shader/x-fragment">
#extension GL_OES_standard_derivatives : enable
precision mediump float;
varying vec4 position;
void main() {
    float dzdx = dFdx(position.z);
    float dzdy = dFdy(position.z);
    float fw = fwidth(position.z);
    gl_FragColor = vec4(abs(dzdx) * 40.0, abs(dzdy) * 40.0, fw * 40.0, 1.0);
}
</script>

<script>
"use strict";
description("This test verifies the functionality of the OES_standard_derivatives extension, if it is available.");

debug("");

var wtu = WebGLTestUtils;
var canvas = document.getElementById("canvas");
var gl = wtu.create3DContext(canvas);
var ext = null;

// Run all tests once.
runAllTests();

// Run all tests against with a new context to test for any cache issues.
debug("");
debug("Testing new context to catch cache errors");
gl = wtu.create3DContext();
ext = null;
runAllTests();

function runAllTests() {
    if (!gl) {
        testFailed("WebGL context does not exist");
    } else {
        testPassed("WebGL context exists");

        // Run tests with extension disabled
        runHintTestDisabled();
        runShaderTests(false);

        // Query the extension and store globally so shouldBe can access it
        ext = gl.getExtension("OES_standard_derivatives");
        if (!ext) {
            testPassed("No OES_standard_derivatives support -- this is legal");

            runSupportedTest(false);
        } else {
            testPassed("Successfully enabled OES_standard_derivatives extension");

            runSupportedTest(true);

            runHintTestEnabled();
            runShaderTests(true);
            runOutputTests();
            runUniqueObjectTest();

            // Run deferred link tests.
            runDeferredLinkTests();
        }
    }

}

function runSupportedTest(extensionEnabled) {
    var supported = gl.getSupportedExtensions();
    if (supported.indexOf("OES_standard_derivatives") >= 0) {
        if (extensionEnabled) {
            testPassed("OES_standard_derivatives listed as supported and getExtension succeeded");
        } else {
            testFailed("OES_standard_derivatives listed as supported but getExtension failed");
        }
    } else {
        if (extensionEnabled) {
            testFailed("OES_standard_derivatives not listed as supported but getExtension succeeded");
        } else {
            testPassed("OES_standard_derivatives not listed as supported and getExtension failed -- this is legal");
        }
    }
}

function runHintTestDisabled() {
    debug("Testing FRAGMENT_SHADER_DERIVATIVE_HINT_OES with extension disabled");

    // Use the constant directly as we don't have the extension
    var FRAGMENT_SHADER_DERIVATIVE_HINT_OES = 0x8B8B;

    gl.getParameter(FRAGMENT_SHADER_DERIVATIVE_HINT_OES);
    wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "FRAGMENT_SHADER_DERIVATIVE_HINT_OES should not be queryable if extension is disabled");

    gl.hint(FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.DONT_CARE);
    wtu.glErrorShouldBe(gl, gl.INVALID_ENUM, "hint should not accept FRAGMENT_SHADER_DERIVATIVE_HINT_OES if extension is disabled");
}

function runHintTestEnabled() {
    debug("Testing FRAGMENT_SHADER_DERIVATIVE_HINT_OES with extension enabled");

    shouldBe("ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES", "0x8B8B");

    gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES);
    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "FRAGMENT_SHADER_DERIVATIVE_HINT_OES query should succeed if extension is enabled");

    // Default value is DONT_CARE
    if (gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES) == gl.DONT_CARE) {
        testPassed("Default value of FRAGMENT_SHADER_DERIVATIVE_HINT_OES is DONT_CARE");
    } else {
        testFailed("Default value of FRAGMENT_SHADER_DERIVATIVE_HINT_OES is not DONT_CARE");
    }

    // Ensure that we can set the target
    gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.DONT_CARE);
    wtu.glErrorShouldBe(gl, gl.NO_ERROR, "hint should accept FRAGMENT_SHADER_DERIVATIVE_HINT_OES");

    // Test all the hint modes
    var validModes = ["FASTEST", "NICEST", "DONT_CARE"];
    var anyFailed = false;
    for (var n = 0; n < validModes.length; n++) {
        var mode = validModes[n];
        gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl[mode]);
        if (gl.getParameter(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES) != gl[mode]) {
            testFailed("Round-trip of hint()/getParameter() failed on mode " + mode);
            anyFailed = true;
        }
    }
    if (!anyFailed) {
        testPassed("Round-trip of hint()/getParameter() with all supported modes");
    }
}

function runShaderTests(extensionEnabled) {
    debug("");
    debug("Testing various shader compiles with extension " + (extensionEnabled ? "enabled" : "disabled"));

    // Expect the macro shader to succeed ONLY if enabled
    {
        const macroFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "macroFragmentShader");
        if (extensionEnabled) {
            if (macroFragmentProgram) {
                // Expected result
                testPassed("GL_OES_standard_derivatives defined in shaders when extension is enabled");
            } else {
                testFailed("GL_OES_standard_derivatives not defined in shaders when extension is enabled");
            }
        } else {
            if (macroFragmentProgram) {
                testFailed("GL_OES_standard_derivatives defined in shaders when extension is disabled");
            } else {
                testPassed("GL_OES_standard_derivatives not defined in shaders when extension disabled");
            }
        }
    }

    // Always expect the shader missing the #pragma to fail (whether enabled or not)
    {
        const missingPragmaFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "missingPragmaFragmentShader");
        if (missingPragmaFragmentProgram) {
            testFailed("Shader built-ins allowed without #extension pragma");
        } else {
            testPassed("Shader built-ins disallowed without #extension pragma");
        }
    }

    // Try to compile a shader using the built-ins that should only succeed if enabled
    {
        const testFragmentProgram = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShader");
        if (extensionEnabled) {
            if (testFragmentProgram) {
                testPassed("Shader built-ins compiled successfully when extension enabled");
            } else {
                testFailed("Shader built-ins failed to compile when extension enabled");
            }
        } else {
            if (testFragmentProgram) {
                testFailed("Shader built-ins compiled successfully when extension disabled");
            } else {
                testPassed("Shader built-ins failed to compile when extension disabled");
            }
        }
    }

    // This tests specifically that #extension directives after other code are
    // valid, per spec (6.35 GLSL ES #extension directive location).
    //
    // This test actually has nothing to do with OES_standard_derivatives, but
    // is inserted here because this extension is ubiquitous.
    //
    // This test is not as strict as the spec - it doesn't require that "the
    // scope ... is always the whole shader", because implementations (ANGLE
    // shader translator) do not actually implement this correctly. The test
    // coverage is intentionally left incomplete - in practice, all WebGL
    // shaders already work with the current implementation, so there's no
    // practical reason to update them to match the spec. Conversely, the
    // currently implemented rules are too complex to formalize in spec.
    //
    // Regression test for https://crbug.com/971660 .
    {
        const testFragmentProgramWithExtensionNotAtTop = wtu.loadProgramFromScriptExpectError(gl, "goodVertexShader", "testFragmentShaderWithExtensionNotAtTop");
        if (extensionEnabled) {
            if (testFragmentProgramWithExtensionNotAtTop) {
                testPassed("Shader with #extension after non-preprocessor code: compiled successfully when extension enabled");
            } else {
                testFailed("Shader with #extension after non-preprocessor code: failed to compile when extension enabled");
            }
        }
    }
}

function runOutputTests() {
    // This tests does several draws with various values of z.
    // The output of the fragment shader is:
    // [dFdx(z), dFdy(z), fwidth(z), 1.0]
    // The expected math: (note the conversion to uint8)
    //    canvas.width = canvas.height = 50
    //    dFdx = totalChange.x / canvas.width  = 0.5 / 50.0 = 0.01
    //    dFdy = totalChange.y / canvas.height = 0.5 / 50.0 = 0.01
    //    fw = abs(dFdx + dFdy) = 0.01 + 0.01 = 0.02
    //    r = floor(dFdx * 40.0 * 255) = 102
    //    g = floor(dFdy * 40.0 * 255) = 102
    //    b = floor(fw * 40.0 * 255) = 204

    var e = 5; // Amount of variance to allow in result pixels - may need to be tweaked higher

    debug("Testing various draws for valid built-in function behavior");

    canvas.width = 50; canvas.height = 50;
    gl.viewport(0, 0, canvas.width, canvas.height);
    gl.hint(ext.FRAGMENT_SHADER_DERIVATIVE_HINT_OES, gl.NICEST);

    var positionLoc = 0;
    var texcoordLoc = 1;
    var program = wtu.setupProgram(gl, ["outputVertexShader", "outputFragmentShader"], ['vPosition', 'texCoord0'], [0, 1]);
    var quadParameters = wtu.setupUnitQuad(gl, positionLoc, texcoordLoc);

    function expectResult(target, message) {
        var locations = [
            [ 0.1, 0.1 ],
            [ 0.9, 0.1 ],
            [ 0.1, 0.9 ],
            [ 0.9, 0.9 ],
            [ 0.5, 0.5 ]
        ];
        for (var n = 0; n < locations.length; n++) {
            var loc = locations[n];
            var px = Math.floor(loc[0] * canvas.width);
            var py = Math.floor(loc[1] * canvas.height);
            wtu.checkCanvasRect(gl, px, py, 1, 1, target, message, 4);
        }
    };

    function setupBuffers(tl, tr, bl, br) {
        gl.bindBuffer(gl.ARRAY_BUFFER, quadParameters[0]);
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
           1.0,  1.0, tr,
          -1.0,  1.0, tl,
          -1.0, -1.0, bl,
           1.0,  1.0, tr,
          -1.0, -1.0, bl,
           1.0, -1.0, br]), gl.STATIC_DRAW);
        gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
    };

    // Draw 1: (no variation)
    setupBuffers(0.0, 0.0, 0.0, 0.0);
    wtu.clearAndDrawUnitQuad(gl);
    expectResult([0, 0, 0, 255],
                 "Draw 1 (no variation) should pass");

    // Draw 2: (variation in x)
    setupBuffers(1.0, 0.0, 1.0, 0.0);
    wtu.clearAndDrawUnitQuad(gl);
    expectResult([204, 0, 204, 255],
                 "Draw 2 (variation in x) should pass");

    // Draw 3: (variation in y)
    setupBuffers(1.0, 1.0, 0.0, 0.0);
    wtu.clearAndDrawUnitQuad(gl);
    expectResult([0, 204, 204, 255],
                 "Draw 3 (variation in y) should pass");

    // Draw 4: (variation in x & y)
    setupBuffers(1.0, 0.5, 0.5, 0.0);
    wtu.clearAndDrawUnitQuad(gl);
    expectResult([102, 102, 204, 255],
                 "Draw 4 (variation in x & y) should pass");
}

function runUniqueObjectTest()
{
    debug("Testing that getExtension() returns the same object each time");
    ext = null;
    gl.getExtension("OES_standard_derivatives").myProperty = 2;
    webglHarnessCollectGarbage();
    shouldBe('gl.getExtension("OES_standard_derivatives").myProperty', '2');
}

function runDeferredLinkTests() {
    debug("");
    debug("Testing deferred shader compilation tests.");

    // Test for compilation failures that are caused by missing extensions
    // do not succeed if extensions are enabled during linking. This would
    // only happen for deferred shader compilations.

    // First test if link succeeds with extension enabled.
    var glEnabled = wtu.create3DContext();
    var extEnabled = glEnabled.getExtension("OES_standard_derivatives");
    if (!extEnabled) {
        testFailed("Deferred link test expects the extension to be supported");
    }

    var vertexShader = wtu.loadShaderFromScript(glEnabled, "goodVertexShader");
    var fragmentShader = wtu.loadShaderFromScript(glEnabled, "macroFragmentShader");

    if (!vertexShader || !fragmentShader) {
        testFailed("Could not create good shaders.");
        return;
    }

    var program = wtu.setupProgram(glEnabled, [vertexShader, fragmentShader]);

    if (!program) {
        testFailed("Compilation with extension enabled failed.");
        return;
    }

    // Create new context to test link failure without extension enabled.
    var glDeferred = wtu.create3DContext();

    var vertexShader = wtu.loadShaderFromScript(glDeferred, "goodVertexShader", glDeferred.VERTEX_SHADER, undefined, undefined, true);
    var fragmentShader = wtu.loadShaderFromScript(glDeferred, "macroFragmentShader", glDeferred.FRAGMENT_SHADER, undefined, undefined, true);

    if (vertexShader == null || fragmentShader == null) {
        testFailed("Could not create shaders.");
        return;
    }

    // Shader compilations should have failed due to extensions not enabled.
    glDeferred.getExtension("OES_standard_derivatives");
    var program = wtu.setupProgram(glDeferred, [vertexShader, fragmentShader]);
    if (program) {
        testFailed("Compilation with extension disabled then linking with extension enabled should have failed.");
        return;
    }

    testPassed("Compilation with extension disabled then linking with extension enabled.");
}

debug("");
var successfullyParsed = true;
</script>
<script src="../../js/js-test-post.js"></script>

</body>
</html>