OLD | NEW |
| (Empty) |
1 GLSLGenerator = (function() { | |
2 | |
3 var vertexShaderTemplate = [ | |
4 "attribute vec4 aPosition;", | |
5 "", | |
6 "varying vec4 vColor;", | |
7 "", | |
8 "$(extra)", | |
9 "$(emu)", | |
10 "", | |
11 "void main()", | |
12 "{", | |
13 " gl_Position = aPosition;", | |
14 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));", | |
15 " vec4 color = vec4(", | |
16 " texcoord,", | |
17 " texcoord.x * texcoord.y,", | |
18 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);", | |
19 " $(test)", | |
20 "}" | |
21 ].join("\n"); | |
22 | |
23 var fragmentShaderTemplate = [ | |
24 "#if defined(GL_ES)", | |
25 "precision mediump float;", | |
26 "#endif", | |
27 "", | |
28 "varying vec4 vColor;", | |
29 "", | |
30 "$(extra)", | |
31 "$(emu)", | |
32 "", | |
33 "void main()", | |
34 "{", | |
35 " $(test)", | |
36 "}" | |
37 ].join("\n"); | |
38 | |
39 var baseVertexShader = [ | |
40 "attribute vec4 aPosition;", | |
41 "", | |
42 "varying vec4 vColor;", | |
43 "", | |
44 "void main()", | |
45 "{", | |
46 " gl_Position = aPosition;", | |
47 " vec2 texcoord = vec2(aPosition.xy * 0.5 + vec2(0.5, 0.5));", | |
48 " vColor = vec4(", | |
49 " texcoord,", | |
50 " texcoord.x * texcoord.y,", | |
51 " (1.0 - texcoord.x) * texcoord.y * 0.5 + 0.5);", | |
52 "}" | |
53 ].join("\n"); | |
54 | |
55 var baseFragmentShader = [ | |
56 "#if defined(GL_ES)", | |
57 "precision mediump float;", | |
58 "#endif", | |
59 "varying vec4 vColor;", | |
60 "", | |
61 "void main()", | |
62 "{", | |
63 " gl_FragColor = vColor;", | |
64 "}" | |
65 ].join("\n"); | |
66 | |
67 var types = [ | |
68 { type: "float", | |
69 code: [ | |
70 "float $(func)_emu($(args)) {", | |
71 " return $(func)_base($(baseArgs));", | |
72 "}"].join("\n") | |
73 }, | |
74 { type: "vec2", | |
75 code: [ | |
76 "vec2 $(func)_emu($(args)) {", | |
77 " return vec2(", | |
78 " $(func)_base($(baseArgsX)),", | |
79 " $(func)_base($(baseArgsY)));", | |
80 "}"].join("\n") | |
81 }, | |
82 { type: "vec3", | |
83 code: [ | |
84 "vec3 $(func)_emu($(args)) {", | |
85 " return vec3(", | |
86 " $(func)_base($(baseArgsX)),", | |
87 " $(func)_base($(baseArgsY)),", | |
88 " $(func)_base($(baseArgsZ)));", | |
89 "}"].join("\n") | |
90 }, | |
91 { type: "vec4", | |
92 code: [ | |
93 "vec4 $(func)_emu($(args)) {", | |
94 " return vec4(", | |
95 " $(func)_base($(baseArgsX)),", | |
96 " $(func)_base($(baseArgsY)),", | |
97 " $(func)_base($(baseArgsZ)),", | |
98 " $(func)_base($(baseArgsW)));", | |
99 "}"].join("\n") | |
100 } | |
101 ]; | |
102 | |
103 var bvecTypes = [ | |
104 { type: "bvec2", | |
105 code: [ | |
106 "bvec2 $(func)_emu($(args)) {", | |
107 " return bvec2(", | |
108 " $(func)_base($(baseArgsX)),", | |
109 " $(func)_base($(baseArgsY)));", | |
110 "}"].join("\n") | |
111 }, | |
112 { type: "bvec3", | |
113 code: [ | |
114 "bvec3 $(func)_emu($(args)) {", | |
115 " return bvec3(", | |
116 " $(func)_base($(baseArgsX)),", | |
117 " $(func)_base($(baseArgsY)),", | |
118 " $(func)_base($(baseArgsZ)));", | |
119 "}"].join("\n") | |
120 }, | |
121 { type: "bvec4", | |
122 code: [ | |
123 "vec4 $(func)_emu($(args)) {", | |
124 " return bvec4(", | |
125 " $(func)_base($(baseArgsX)),", | |
126 " $(func)_base($(baseArgsY)),", | |
127 " $(func)_base($(baseArgsZ)),", | |
128 " $(func)_base($(baseArgsW)));", | |
129 "}"].join("\n") | |
130 } | |
131 ]; | |
132 | |
133 var replaceRE = /\$\((\w+)\)/g; | |
134 | |
135 var replaceParams = function(str, params) { | |
136 return str.replace(replaceRE, function(str, p1, offset, s) { | |
137 if (params[p1] === undefined) { | |
138 throw "unknown string param '" + p1 + "'"; | |
139 } | |
140 return params[p1]; | |
141 }); | |
142 }; | |
143 | |
144 var generateReferenceShader = function( | |
145 shaderInfo, template, params, typeInfo, test) { | |
146 var input = shaderInfo.input; | |
147 var output = shaderInfo.output; | |
148 var feature = params.feature; | |
149 var testFunc = params.testFunc; | |
150 var emuFunc = params.emuFunc || ""; | |
151 var extra = params.extra || ''; | |
152 var args = params.args || "$(type) value"; | |
153 var type = typeInfo.type; | |
154 var typeCode = typeInfo.code; | |
155 | |
156 var baseArgs = params.baseArgs || "value$(field)"; | |
157 var baseArgsX = replaceParams(baseArgs, {field: ".x"}); | |
158 var baseArgsY = replaceParams(baseArgs, {field: ".y"}); | |
159 var baseArgsZ = replaceParams(baseArgs, {field: ".z"}); | |
160 var baseArgsW = replaceParams(baseArgs, {field: ".w"}); | |
161 var baseArgs = replaceParams(baseArgs, {field: ""}); | |
162 | |
163 test = replaceParams(test, { | |
164 input: input, | |
165 output: output, | |
166 func: feature + "_emu" | |
167 }); | |
168 emuFunc = replaceParams(emuFunc, { | |
169 func: feature | |
170 }); | |
171 args = replaceParams(args, { | |
172 type: type | |
173 }); | |
174 typeCode = replaceParams(typeCode, { | |
175 func: feature, | |
176 type: type, | |
177 args: args, | |
178 baseArgs: baseArgs, | |
179 baseArgsX: baseArgsX, | |
180 baseArgsY: baseArgsY, | |
181 baseArgsZ: baseArgsZ, | |
182 baseArgsW: baseArgsW | |
183 }); | |
184 var shader = replaceParams(template, { | |
185 extra: extra, | |
186 emu: emuFunc + "\n\n" + typeCode, | |
187 test: test | |
188 }); | |
189 return shader; | |
190 }; | |
191 | |
192 var generateTestShader = function( | |
193 shaderInfo, template, params, test) { | |
194 var input = shaderInfo.input; | |
195 var output = shaderInfo.output; | |
196 var feature = params.feature; | |
197 var testFunc = params.testFunc; | |
198 var extra = params.extra || ''; | |
199 | |
200 test = replaceParams(test, { | |
201 input: input, | |
202 output: output, | |
203 func: feature | |
204 }); | |
205 var shader = replaceParams(template, { | |
206 extra: extra, | |
207 emu: '', | |
208 test: test | |
209 }); | |
210 return shader; | |
211 }; | |
212 | |
213 var makeImage = function(canvas) { | |
214 var img = document.createElement('img'); | |
215 img.src = canvas.toDataURL(); | |
216 return img; | |
217 }; | |
218 | |
219 var runFeatureTest = function(params) { | |
220 if (window.initNonKhronosFramework) { | |
221 window.initNonKhronosFramework(false); | |
222 } | |
223 | |
224 var wtu = WebGLTestUtils; | |
225 var gridRes = params.gridRes; | |
226 var tolerance = params.tolerance || 0; | |
227 | |
228 description("Testing GLSL feature: " + params.feature); | |
229 | |
230 var width = 32; | |
231 var height = 32; | |
232 | |
233 var console = document.getElementById("console"); | |
234 var canvas = document.createElement('canvas'); | |
235 canvas.width = width; | |
236 canvas.height = height; | |
237 var gl = wtu.create3DContext(canvas); | |
238 if (!gl) { | |
239 testFailed("context does not exist"); | |
240 finishTest(); | |
241 return; | |
242 } | |
243 | |
244 var canvas2d = document.createElement('canvas'); | |
245 canvas2d.width = width; | |
246 canvas2d.height = height; | |
247 var ctx = canvas2d.getContext("2d"); | |
248 var imgData = ctx.getImageData(0, 0, width, height); | |
249 | |
250 var shaderInfos = [ | |
251 { type: "vertex", | |
252 input: "color", | |
253 output: "vColor", | |
254 vertexShaderTemplate: vertexShaderTemplate, | |
255 fragmentShaderTemplate: baseFragmentShader | |
256 }, | |
257 { type: "fragment", | |
258 input: "vColor", | |
259 output: "gl_FragColor", | |
260 vertexShaderTemplate: baseVertexShader, | |
261 fragmentShaderTemplate: fragmentShaderTemplate | |
262 } | |
263 ]; | |
264 for (var ss = 0; ss < shaderInfos.length; ++ss) { | |
265 var shaderInfo = shaderInfos[ss]; | |
266 var tests = params.tests; | |
267 var testTypes = params.emuFuncs || (params.bvecTest ? bvecTypes : types); | |
268 // Test vertex shaders | |
269 for (var ii = 0; ii < tests.length; ++ii) { | |
270 var type = testTypes[ii]; | |
271 if (params.simpleEmu) { | |
272 type = { | |
273 type: type.type, | |
274 code: params.simpleEmu | |
275 }; | |
276 } | |
277 debug(""); | |
278 var str = replaceParams(params.testFunc, { | |
279 func: params.feature, | |
280 type: type.type, | |
281 arg0: type.type | |
282 }); | |
283 debug("Testing: " + str + " in " + shaderInfo.type + " shader"); | |
284 | |
285 var referenceVertexShaderSource = generateReferenceShader( | |
286 shaderInfo, | |
287 shaderInfo.vertexShaderTemplate, | |
288 params, | |
289 type, | |
290 tests[ii]); | |
291 var referenceFragmentShaderSource = generateReferenceShader( | |
292 shaderInfo, | |
293 shaderInfo.fragmentShaderTemplate, | |
294 params, | |
295 type, | |
296 tests[ii]); | |
297 var testVertexShaderSource = generateTestShader( | |
298 shaderInfo, | |
299 shaderInfo.vertexShaderTemplate, | |
300 params, | |
301 tests[ii]); | |
302 var testFragmentShaderSource = generateTestShader( | |
303 shaderInfo, | |
304 shaderInfo.fragmentShaderTemplate, | |
305 params, | |
306 tests[ii]); | |
307 | |
308 debug(""); | |
309 addShaderSource( | |
310 "reference vertex shader", referenceVertexShaderSource); | |
311 addShaderSource( | |
312 "reference fragment shader", referenceFragmentShaderSource); | |
313 addShaderSource( | |
314 "test vertex shader", testVertexShaderSource); | |
315 addShaderSource( | |
316 "test fragment shader", testFragmentShaderSource); | |
317 debug(""); | |
318 | |
319 var refData = draw( | |
320 canvas, referenceVertexShaderSource, referenceFragmentShaderSource); | |
321 var refImg = makeImage(canvas); | |
322 if (ss == 0) { | |
323 var testData = draw( | |
324 canvas, testVertexShaderSource, referenceFragmentShaderSource); | |
325 } else { | |
326 var testData = draw( | |
327 canvas, referenceVertexShaderSource, testFragmentShaderSource); | |
328 } | |
329 var testImg = makeImage(canvas); | |
330 | |
331 reportResults(refData, refImg, testData, testImg); | |
332 } | |
333 } | |
334 | |
335 finishTest(); | |
336 | |
337 function addShaderSource(label, source) { | |
338 var div = document.createElement("div"); | |
339 var s = document.createElement("pre"); | |
340 s.className = "shader-source"; | |
341 s.style.display = "none"; | |
342 var ol = document.createElement("ol"); | |
343 //s.appendChild(document.createTextNode(source)); | |
344 var lines = source.split("\n"); | |
345 for (var ii = 0; ii < lines.length; ++ii) { | |
346 var line = lines[ii]; | |
347 var li = document.createElement("li"); | |
348 li.appendChild(document.createTextNode(line)); | |
349 ol.appendChild(li); | |
350 } | |
351 s.appendChild(ol); | |
352 var l = document.createElement("a"); | |
353 l.href = "show-shader-source"; | |
354 l.appendChild(document.createTextNode(label)); | |
355 l.addEventListener('click', function(event) { | |
356 if (event.preventDefault) { | |
357 event.preventDefault(); | |
358 } | |
359 s.style.display = (s.style.display == 'none') ? 'block' : 'none'; | |
360 return false; | |
361 }, false); | |
362 div.appendChild(l); | |
363 div.appendChild(s); | |
364 console.appendChild(div); | |
365 } | |
366 | |
367 function reportResults(refData, refImage, testData, testImage) { | |
368 var same = true; | |
369 for (var yy = 0; yy < height; ++yy) { | |
370 for (var xx = 0; xx < width; ++xx) { | |
371 var offset = (yy * width + xx) * 4; | |
372 var imgOffset = ((height - yy - 1) * width + xx) * 4; | |
373 imgData.data[imgOffset + 0] = 0; | |
374 imgData.data[imgOffset + 1] = 0; | |
375 imgData.data[imgOffset + 2] = 0; | |
376 imgData.data[imgOffset + 3] = 255; | |
377 if (Math.abs(refData[offset + 0] - testData[offset + 0]) > tolerance || | |
378 Math.abs(refData[offset + 1] - testData[offset + 1]) > tolerance || | |
379 Math.abs(refData[offset + 2] - testData[offset + 2]) > tolerance || | |
380 Math.abs(refData[offset + 3] - testData[offset + 3]) > tolerance) { | |
381 imgData.data[imgOffset] = 255; | |
382 same = false; | |
383 } | |
384 } | |
385 } | |
386 | |
387 var diffImg = null; | |
388 if (!same) { | |
389 ctx.putImageData(imgData, 0, 0); | |
390 diffImg = makeImage(canvas2d); | |
391 } | |
392 | |
393 var div = document.createElement("div"); | |
394 div.className = "testimages"; | |
395 insertImg(div, "ref", refImg); | |
396 insertImg(div, "test", testImg); | |
397 if (diffImg) { | |
398 insertImg(div, "diff", diffImg); | |
399 } | |
400 div.appendChild(document.createElement('br')); | |
401 | |
402 function insertImg(element, caption, img) { | |
403 var div = document.createElement("div"); | |
404 div.appendChild(img); | |
405 var label = document.createElement("div"); | |
406 label.appendChild(document.createTextNode(caption)); | |
407 div.appendChild(label); | |
408 element.appendChild(div); | |
409 } | |
410 | |
411 console.appendChild(div); | |
412 | |
413 if (!same) { | |
414 testFailed("images are different"); | |
415 } else { | |
416 testPassed("images are the same"); | |
417 } | |
418 | |
419 console.appendChild(document.createElement('hr')); | |
420 } | |
421 | |
422 function draw(canvas, vsSource, fsSource) { | |
423 var program = wtu.loadProgram(gl, vsSource, fsSource, testFailed); | |
424 | |
425 var posLoc = gl.getAttribLocation(program, "aPosition"); | |
426 WebGLTestUtils.setupQuad(gl, gridRes, posLoc); | |
427 | |
428 gl.useProgram(program); | |
429 gl.clearColor(0, 0, 1, 1); | |
430 gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | |
431 gl.drawElements(gl.TRIANGLES, gridRes * gridRes * 6, gl.UNSIGNED_SHORT, 0); | |
432 wtu.glErrorShouldBe(gl, gl.NO_ERROR, "no errors from draw"); | |
433 | |
434 var img = new Uint8Array(width * height * 4); | |
435 gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, img); | |
436 return img; | |
437 } | |
438 | |
439 }; | |
440 | |
441 return { | |
442 /** | |
443 * runs a bunch of GLSL tests using the passed in parameters | |
444 * The parameters are: | |
445 * | |
446 * feature: | |
447 * the name of the function being tested (eg, sin, dot, | |
448 * normalize) | |
449 * | |
450 * testFunc: | |
451 * The prototype of function to be tested not including the | |
452 * return type. | |
453 * | |
454 * emuFunc: | |
455 * A base function that can be used to generate emulation | |
456 * functions. Example for 'ceil' | |
457 * | |
458 * float $(func)_base(float value) { | |
459 * float m = mod(value, 1.0); | |
460 * return m != 0.0 ? (value + 1.0 - m) : value; | |
461 * } | |
462 * | |
463 * args: | |
464 * The arguments to the function | |
465 * | |
466 * baseArgs: (optional) | |
467 * The arguments when a base function is used to create an | |
468 * emulation function. For example 'float sign_base(float v)' | |
469 * is used to implemenent vec2 sign_emu(vec2 v). | |
470 * | |
471 * simpleEmu: | |
472 * if supplied, the code that can be used to generate all | |
473 * functions for all types. | |
474 * | |
475 * Example for 'normalize': | |
476 * | |
477 * $(type) $(func)_emu($(args)) { | |
478 * return value / length(value); | |
479 * } | |
480 * | |
481 * gridRes: (optional) | |
482 * The resolution of the mesh to generate. The default is a | |
483 * 1x1 grid but many vertex shaders need a higher resolution | |
484 * otherwise the only values passed in are the 4 corners | |
485 * which often have the same value. | |
486 * | |
487 * tests: | |
488 * The code for each test. It is assumed the tests are for | |
489 * float, vec2, vec3, vec4 in that order. | |
490 */ | |
491 runFeatureTest: runFeatureTest, | |
492 | |
493 none: false | |
494 }; | |
495 | |
496 }()); | |
497 | |
OLD | NEW |