summaryrefslogtreecommitdiffstats
path: root/dom/canvas/test/webgl-conf/checkout/js/glsl-conformance-test.js
blob: 12a056ce214498919a3b95f9a36d6e97e64112d6 (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
/*
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.
*/
GLSLConformanceTester = (function(){

var wtu = WebGLTestUtils;
var defaultVertexShader = [
  "attribute vec4 vPosition;",
  "void main()",
  "{",
  "    gl_Position = vPosition;",
  "}"
].join('\n');

var defaultFragmentShader = [
  "precision mediump float;",
  "void main()",
  "{",
  "    gl_FragColor = vec4(1.0,0.0,0.0,1.0);",
  "}"
].join('\n');

var defaultESSL3VertexShader = [
  "#version 300 es",
  "in vec4 vPosition;",
  "void main()",
  "{",
  "    gl_Position = vPosition;",
  "}"
].join('\n');

var defaultESSL3FragmentShader = [
  "#version 300 es",
  "precision mediump float;",
  "out vec4 my_FragColor;",
  "void main()",
  "{",
  "    my_FragColor = vec4(1.0,0.0,0.0,1.0);",
  "}"
].join('\n');

function log(msg) {
  bufferedLogToConsole(msg);
}

var vShaderDB = {};
var fShaderDB = {};

/**
 * The info parameter should contain the following keys. Note that you may leave
 * the parameters for one shader out, in which case the default shader will be
 * used.
 * vShaderSource: the source code for vertex shader
 * vShaderId: id of an element containing vertex shader source code. Used if
 *   vShaderSource is not specified.
 * vShaderSuccess: true if vertex shader compilation should
 *   succeed.
 * fShaderSource: the source code for fragment shader
 * fShaderId: id of an element containing fragment shader source code. Used if
 *   fShaderSource is not specified.
 * fShaderSuccess: true if fragment shader compilation should
 *   succeed.
 * linkSuccess: true if link should succeed
 * passMsg: msg to describe success condition.
 * render: if true render to unit quad. Green = success
 * uniforms: an array of objects specifying uniforms to set prior to rendering.
 *   Each object should have the following keys:
 *     name: uniform variable name in the shader source. Uniform location will
 *       be queried based on its name.
 *     functionName: name of the function used to set the uniform. For example:
 *       'uniform1i'
 *     value: value of the uniform to set.
 */
function runOneTest(gl, info) {
  var passMsg = info.passMsg
  debug("");
  debug("test: " + passMsg);

  var consoleDiv = document.getElementById("console");

  var vIsDefault = false;
  var fIsDefault = false;

  if (info.vShaderSource === undefined) {
    if (info.vShaderId) {
      info.vShaderSource = document.getElementById(info.vShaderId).text;
    } else {
      vIsDefault = true;
    }
  }
  if (info.fShaderSource === undefined) {
    if (info.fShaderId) {
      info.fShaderSource = document.getElementById(info.fShaderId).text;
    } else {
      fIsDefault = true;
    }
  }

  var vLabel = (vIsDefault ? "default" : "test") + " vertex shader";
  var fLabel = (fIsDefault ? "default" : "test") + " fragment shader";
  if (vIsDefault) {
    info.vShaderSource = defaultVertexShader;
    info.vShaderSuccess = true;
  }
  if (fIsDefault) {
    info.fShaderSource = defaultFragmentShader;
    info.fShaderSuccess = true;
  }

  if (vIsDefault != fIsDefault) {
    // The language version of the default shader is chosen
    // according to the language version of the other shader.
    // We rely on "#version 300 es" being in this usual format.
    // It must be on the first line of the shader according to the spec.
    if (fIsDefault) {
      // If we're using the default fragment shader, we need to make sure that
      // it's language version matches with the vertex shader.
      if (info.vShaderSource.split('\n')[0] == '#version 300 es') {
        info.fShaderSource = defaultESSL3FragmentShader;
      }
    } else {
      // If we're using the default vertex shader, we need to make sure that
      // it's language version matches with the fragment shader.
      if (info.fShaderSource.split('\n')[0] == '#version 300 es') {
        info.vShaderSource = defaultESSL3VertexShader;
      }
    }
  }

  var vSource = info.vShaderPrep ? info.vShaderPrep(info.vShaderSource) :
    info.vShaderSource;

  if (!quietMode()) {
    wtu.addShaderSource(consoleDiv, vLabel, vSource);
  }

  // Reuse identical shaders so we test shared shader.
  var vShader = vShaderDB[vSource];
  if (!vShader) {
    // loadShader, with opt_skipCompileStatus: true.
    vShader = wtu.loadShader(gl, vSource, gl.VERTEX_SHADER, null, null, null, null, true);
    let compiledVShader = vShader;
    if (vShader && !gl.getShaderParameter(vShader, gl.COMPILE_STATUS)) {
      compiledVShader = null;
    }
    if (info.vShaderTest) {
      if (!info.vShaderTest(compiledVShader)) {
        testFailed("[vertex shader test] " + passMsg);
        return;
      }
    }
    // As per GLSL 1.0.17 10.27 we can only check for success on
    // compileShader, not failure.
    if (!info.ignoreResults && info.vShaderSuccess && !compiledVShader) {
      testFailed("[unexpected vertex shader compile status] (expected: " +
                 info.vShaderSuccess + ") " + passMsg);
      if (!quietMode() && vShader) {
        const info = gl.getShaderInfoLog(vShader);
        wtu.addShaderSource(consoleDiv, vLabel + " info log", info);
      }
    }
    // Save the shaders so we test shared shader.
    if (compiledVShader) {
      vShaderDB[vSource] = compiledVShader;
    } else {
      vShader = null;
    }
  }

  var debugShaders = gl.getExtension('WEBGL_debug_shaders');
  if (debugShaders && vShader && !quietMode()) {
    wtu.addShaderSource(consoleDiv, vLabel + " translated for driver",
                        debugShaders.getTranslatedShaderSource(vShader));
  }

  var fSource = info.fShaderPrep ? info.fShaderPrep(info.fShaderSource) :
    info.fShaderSource;

  if (!quietMode()) {
    wtu.addShaderSource(consoleDiv, fLabel, fSource);
  }

  // Reuse identical shaders so we test shared shader.
  var fShader = fShaderDB[fSource];
  if (!fShader) {
    // loadShader, with opt_skipCompileStatus: true.
    fShader = wtu.loadShader(gl, fSource, gl.FRAGMENT_SHADER, null, null, null, null, true);
    let compiledFShader = fShader;
    if (fShader && !gl.getShaderParameter(fShader, gl.COMPILE_STATUS)) {
      compiledFShader = null;
    }
    if (info.fShaderTest) {
      if (!info.fShaderTest(compiledFShader)) {
        testFailed("[fragment shader test] " + passMsg);
        return;
      }
    }
    //debug(fShader == null ? "fail" : "succeed");
    // As per GLSL 1.0.17 10.27 we can only check for success on
    // compileShader, not failure.
    if (!info.ignoreResults && info.fShaderSuccess && !compiledFShader) {
      testFailed("[unexpected fragment shader compile status] (expected: " +
                info.fShaderSuccess + ") " + passMsg);
      if (!quietMode() && fShader) {
        const info = gl.getShaderInfoLog(fShader);
        wtu.addShaderSource(consoleDiv, fLabel + " info log", info);
      }
      return;
    }

    // Safe the shaders so we test shared shader.
    if (compiledFShader) {
      fShaderDB[fSource] = compiledFShader;
    } else {
      fShader = null;
    }
  }

  if (debugShaders && fShader && !quietMode()) {
    wtu.addShaderSource(consoleDiv, fLabel + " translated for driver",
                        debugShaders.getTranslatedShaderSource(fShader));
  }

  if (vShader && fShader) {
    var program = gl.createProgram();
    gl.attachShader(program, vShader);
    gl.attachShader(program, fShader);

    if (vSource.indexOf("vPosition") >= 0) {
      gl.bindAttribLocation(program, 0, "vPosition");
    }
    if (vSource.indexOf("texCoord0") >= 0) {
      gl.bindAttribLocation(program, 1, "texCoord0");
    }
    gl.linkProgram(program);
    var linked = (gl.getProgramParameter(program, gl.LINK_STATUS) != 0);
    if (!linked) {
      var error = gl.getProgramInfoLog(program);
      log("*** Error linking program '"+program+"':"+error);
    }
    if (!info.ignoreResults && linked != info.linkSuccess) {
      testFailed("[unexpected link status] (expected: " +
                info.linkSuccess + ") " + passMsg);
      return;
    }
  } else {
    if (!info.ignoreResults && info.linkSuccess) {
      testFailed("[link failed] " + passMsg);
      return;
    }
  }

  if (parseInt(wtu.getUrlOptions().dumpShaders)) {
    var vInfo = {
      shader: vShader,
      shaderSuccess: info.vShaderSuccess,
      label: vLabel,
      source: vSource
    };
    var fInfo = {
      shader: fShader,
      shaderSuccess: info.fShaderSuccess,
      label: fLabel,
      source: fSource
    };
    wtu.dumpShadersInfo(gl, window.location.pathname, passMsg, vInfo, fInfo);
  }

  if (!info.render) {
    testPassed(passMsg);
    return;
  }

  gl.useProgram(program);

  if (info.uniforms !== undefined) {
    for (var i = 0; i < info.uniforms.length; ++i) {
      var uniform = info.uniforms[i];
      var uniformLocation = gl.getUniformLocation(program, uniform.name);
      if (uniformLocation !== null) {
        if (uniform.functionName.includes("Matrix")) {
          gl[uniform.functionName](uniformLocation, false, uniform.value);
        } else {
          gl[uniform.functionName](uniformLocation, uniform.value);
        }
        debug(uniform.name + ' set to ' + uniform.value);
      } else {
        debug('uniform ' + uniform.name + ' had null location and was not set');
      }
    }
  }

  if (info.uniformBlocks !== undefined) {
    for (var i = 0; i < info.uniformBlocks.length; ++i) {
      var uniformBlockIndex = gl.getUniformBlockIndex(program, info.uniformBlocks[i].name);
      if (uniformBlockIndex !== null) {
        gl.uniformBlockBinding(program, uniformBlockIndex, i);
        debug(info.uniformBlocks[i].name + ' (index ' + uniformBlockIndex + ') bound to slot ' + i);

        var uboValueBuffer = gl.createBuffer();
        gl.bindBufferBase(gl.UNIFORM_BUFFER, i, uboValueBuffer);
        gl.bufferData(gl.UNIFORM_BUFFER, info.uniformBlocks[i].value, info.uniformBlocks[i].usage || gl.STATIC_DRAW);
      } else {
        debug('uniform block' + info.uniformBlocks[i].name + ' had null block index and was not set');
      }
    }
  }

  wtu.setupUnitQuad(gl);
  wtu.clearAndDrawUnitQuad(gl);

  var div = document.createElement("div");
  div.className = "testimages";
  wtu.insertImage(div, "result", wtu.makeImageFromCanvas(gl.canvas));
  div.appendChild(document.createElement('br'));
  consoleDiv.appendChild(div);

  var tolerance = 0;
  if (info.renderTolerance !== undefined) {
    tolerance = info.renderTolerance;
  }
  if (info.renderColor !== undefined) {
    wtu.checkCanvas(gl, info.renderColor, "should be expected color " + info.renderColor, tolerance);
  } else {
    wtu.checkCanvas(gl, [0, 255, 0, 255], "should be green", tolerance);
  }
}

function runTests(shaderInfos, opt_contextVersion) {
  var wtu = WebGLTestUtils;
  var canvas = document.createElement('canvas');
  canvas.width = 32;
  canvas.height = 32;
  var gl = wtu.create3DContext(canvas, undefined, opt_contextVersion);
  if (!gl) {
    testFailed("context does not exist");
    finishTest();
    return;
  }

  for (var i = 0; i < shaderInfos.length; i++) {
    runOneTest(gl, shaderInfos[i]);
  }

  finishTest();
};

function getSource(elem) {
  var str = elem.text;
  return str.replace(/^\s*/, '').replace(/\s*$/, '');
}

function getPassMessage(source) {
  var lines = source.split('\n');
  return lines[0].substring(3);
}

function getSuccess(msg) {
  if (msg.indexOf("fail") >= 0) {
    return false;
  }
  if (msg.indexOf("succeed") >= 0) {
    return true;
  }
  testFailed("bad test description. Must have 'fail' or 'succeed'");
}

function setupTest() {
  var info = {};

  var vShaderElem = document.getElementById('vertexShader');
  if (vShaderElem) {
    info.vShaderSource = getSource(vShaderElem);
    info.passMsg = getPassMessage(info.vShaderSource);
    info.vShaderSuccess = getSuccess(info.passMsg);
  }

  var fShaderElem = document.getElementById('fragmentShader');
  if (fShaderElem) {
    info.fShaderSource = getSource(fShaderElem);
    info.passMsg = getPassMessage(info.fShaderSource);
    info.fShaderSuccess = getSuccess(info.passMsg);
  }

  // linkSuccess should be true if shader success value is undefined or true for both shaders.
  info.linkSuccess = info.vShaderSuccess !== false && info.fShaderSuccess !== false;

  if (info.passMsg === undefined) {
    testFailed("no test shader found.");
    finishTest();
    return;
  }

  return info;
}

function runTest() {
  var info = setupTest();
  description(info.passMsg);
  runTests([info]);
}

function runRenderTests(tests, opt_contextVersion) {
  for (var ii = 0; ii < tests.length; ++ii) {
    tests[ii].render = true
  }
  runTests(tests, opt_contextVersion);
}

function runRenderTest() {
  var info = setupTest();
  description(info.passMsg);
  runRenderTests([info]);
}

return {
  runTest: runTest,
  runTests: runTests,
  runRenderTest: runRenderTest,
  runRenderTests: runRenderTests
};
}());