| <!DOCTYPE html> |
| <html> |
| <meta charset=utf-8> |
| <meta name="timeout" content="long"> |
| <title>Test the WHLSL test harness.</title> |
| <script src="js/test-harness.js"></script> |
| <script src="../../resources/testharness.js"></script> |
| <script src="../../resources/testharnessreport.js"></script> |
| <script> |
| const epsilon = 0.0001; |
| |
| const numericScalarTypes = ["int", "uint", "float"]; |
| |
| const numericScalarFuncs = { |
| "int": callIntFunction, |
| "uint": callUintFunction, |
| "float": callFloatFunction |
| }; |
| |
| const scalarArgMakers = { |
| "bool": makeBool, |
| "int": makeInt, |
| "uint": makeUint, |
| "float": makeFloat |
| }; |
| |
| const arrayTypeLengths = { |
| "float4": 4, |
| "float4x4": 16 |
| }; |
| |
| const arrayFuncs = { |
| "float4": callFloat4Function, |
| "float4x4": callFloat4x4Function, |
| }; |
| |
| const arrayArgMakers = { |
| "float4": makeFloat4, |
| "float4x4": makeFloat4x4 |
| }; |
| |
| const float4x4expected = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]; |
| const float4x4ColumnExpected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; |
| const float4expected = [0, 1, 2, 3]; |
| |
| let whlslTests = {}; |
| |
| whlslTests.literals = () => { |
| checkBools("Return a literal of type bool.", "return true;"); |
| checkArrays("float4", "return float4(0, 1, 2, 3);", [], float4expected); |
| checkNumericScalars("return 42;", [], 42); |
| }; |
| |
| whlslTests.singleArgument = () => { |
| checkBools("Upload and return a bool value.", "return in0;", [true]); |
| checkArrays("float4", "return in0;", [float4expected], float4expected); |
| checkNumericScalars("return in0;", [42], 42); |
| }; |
| |
| whlslTests.manyArguments = () => { |
| checkBools("Upload many bool values and return a calculated result.", |
| "return in0 & in1 & in2 & in3 & in4 & in5 & in6 & in7;", |
| [true, true, true, true, true, true, true, true]); |
| |
| let body = "return float4(in0.x, in1.y, in2.z, in3.w);" |
| let args = []; |
| for (let i = 0; i < 4; ++i) |
| args.push(float4expected); |
| checkArrays("float4", body, args, float4expected); |
| |
| body = `return in0 + in1 + in2 + in3 + in4 + in5 + in6 + in7;`; |
| checkNumericScalars(body, [0, 1, 2, 3, 4, 5, 6, 7], 28); |
| }; |
| |
| whlslTests.buffersWithOneValue = () => { |
| const body = `return in0[0];` |
| checkArrays("float4", body, [[float4expected]], float4expected); |
| checkNumericScalars(body, [[42]], 42); |
| }; |
| |
| whlslTests.multipleBufferArguments = () => { |
| let body = ` |
| float x = in0[0].x + in0[1].x + in0[2].x + in1.x + in2[0].x; |
| float y = in0[0].y + in0[1].y + in0[2].y + in1.y + in2[0].y; |
| float z = in0[0].z + in0[1].z + in0[2].z + in1.z + in2[0].z; |
| float w = in0[0].w + in0[1].w + in0[2].w + in1.w + in2[0].w; |
| |
| return float4(x, y, z, w);`; |
| checkArrays("float4", body, [[float4expected, float4expected, float4expected], float4expected, [float4expected]], [0, 5, 10, 15]); |
| |
| body = `return in0[0] + in0[1] + in0[2] + in1 + in2[0];`; |
| checkNumericScalars(body, [[0, 1, 2], 3, [4]], 10); |
| }; |
| |
| whlslTests.multipleArgumentTypes = () => { |
| const src = `float test(int i, uint c, float4 f4, device uint[] u, device float[] fs) |
| { |
| return float(i) + float(c) + f4.x + f4.y + f4.z + f4.w + float(u[0]) + fs[0] + fs[1]; |
| }`; |
| const i = makeInt(1); |
| const c = makeUint(2); |
| const f4 = makeFloat4([4, 5, 6, 7]); |
| const u = makeUint([3]); |
| const fs = makeFloat([8, 9]); |
| webGPUPromiseTest(() => { |
| return callFloatFunction(src, "test", [i, c, f4, u, fs]).then(result => { |
| assert_approx_equals(result, 45, epsilon, "Test returned expected value."); |
| }); |
| }, "Upload and calculate a result from varied argument types."); |
| }; |
| |
| whlslTests.bufferStores = () => { |
| let src = `void test(device float4[] out) { |
| out[0] = float4(0, 1, 2, 3); |
| }`; |
| const float4Out = makeFloat4([[0, 0, 0, 0]]); |
| callVoidFunction(src, "test", float4Out); |
| |
| webGPUPromiseTest(() => { |
| return float4Out.getArrayBuffer().then(arrayBuffer => { |
| const result = new Float32Array(arrayBuffer); |
| for (let i; i < 4; ++i) { |
| assert_approx_equals(result[i], i, "Test stored expected values."); |
| } |
| }); |
| }, "Store into a float4[]."); |
| |
| src = `void test(device int[] in, device int[] out) { |
| for (uint i = 0; i < 5; i = i + 1) |
| out[i] = in[i]; |
| }`; |
| const array = [0, 1, 2, 3, 4]; |
| const input = makeInt(array); |
| const output = makeInt([0, 0, 0, 0, 0]); |
| callVoidFunction(src, "test", [input, output]); |
| |
| webGPUPromiseTest(() => { |
| return output.getArrayBuffer().then(arrayBuffer => { |
| const result = new Uint32Array(arrayBuffer); |
| assert_array_equals(array, result, "Test stored expected values."); |
| }); |
| }, "Upload a int[] and store into a int[]."); |
| }; |
| |
| whlslTests.float4x4tests = () => { |
| const src = `void test(device float4x4[] in, device float4x4[] out) { |
| for (uint i = 0; i < 4; i = i + 1) |
| out[i] = in[i]; |
| }`; |
| const input = makeFloat4x4([float4x4expected, float4x4expected, float4x4expected, float4x4expected]); |
| const output = makeFloat4x4(new Array(64).fill(0)); |
| callVoidFunction(src, "test", [input, output]); |
| |
| webGPUPromiseTest(() => { |
| return output.getArrayBuffer().then(arrayBuffer => { |
| const result = new Float32Array(arrayBuffer); |
| for (let i = 0; i < 4; ++i) { |
| for (let j = 0; j < 16; ++j) |
| assert_approx_equals(result[i * 16 + j], float4x4expected[j], epsilon, "Test stored expected values."); |
| } |
| }); |
| }, "Upload a float4x4[] and store into a float4x4[]."); |
| |
| checkArrays("float4x4", "return float4x4(float4(0, 1, 2, 3), float4(4, 5, 6, 7), float4(8, 9, 10, 11), float4(12, 13, 14, 15));", [], float4x4ColumnExpected); |
| checkArrays("float4x4", "return in0;", [float4x4expected], float4x4expected); |
| let multiple4x4args = []; |
| for (let i = 0; i < 16; ++i) { |
| const arg = new Array(16); |
| arg.fill(0); |
| arg[i] = i; |
| multiple4x4args.push(arg); |
| } |
| checkArrays("float4x4", "return in0 + in1 + in2 + in3 + in4 + in5 + in6 + in7 + in8 + in9 + in10 + in11 + in12 + in13 + in14 + in15;", multiple4x4args, float4x4ColumnExpected); |
| checkArrays("float4x4", "return in0[0];", [[float4x4expected]], float4x4expected); |
| }; |
| |
| whlslTests.checkFailTests = () => { |
| webGPUPromiseTest(() => { |
| return checkFail(`Doors and corners, kid. That's where they getcha.`); |
| }, "Code should not compile, and no error is reported."); |
| |
| webGPUPromiseTest(() => { |
| return checkFail("").catch(e => { |
| assert_true(e instanceof Error, "An expected Error was caught."); |
| }); |
| }, "Successfully compiling code in checkFail is an error.") |
| }; |
| |
| window.addEventListener("load", async () => { |
| try { |
| await harness.requestDevice(); |
| for (const name in whlslTests) { |
| if (!name.startsWith("_")) |
| whlslTests[name](); |
| } |
| } catch (e) { |
| if (window.testRunner) |
| testRunner.notifyDone(); |
| |
| throw e; |
| } |
| }); |
| |
| /* Helper functions */ |
| |
| const checkNumericScalars = (body, argValues, expected) => { |
| let functions = []; |
| let src = ""; |
| for (let type of numericScalarTypes) { |
| let name, values; |
| [src, name, values] = appendScalarFunctionToSource(src, type, body, argValues); |
| functions.push({ type: type, name: name, args: values, expected: expected }); |
| } |
| |
| for (const f of functions) { |
| const callFunc = numericScalarFuncs[f.type]; |
| webGPUPromiseTest(async () => { |
| return callFunc(src, f.name, f.args).then(result => { |
| assert_approx_equals(result, f.expected, epsilon, "Test returned expected value."); |
| }); |
| }, `Return an expected ${f.type} value.`); |
| } |
| }; |
| |
| const checkBools = (msg = "Return an expected bool value.", body, argValues = [], expected = true) => { |
| const [src, name, values] = appendScalarFunctionToSource("", "bool", body, argValues); |
| |
| webGPUPromiseTest(async () => { |
| return callBoolFunction(src, name, values).then(result => { |
| assert_equals(result, expected, "Test returned expected value."); |
| }, e => { |
| if (!(e instanceof WebGPUUnsupportedError)) |
| throw e; |
| }); |
| }, msg); |
| }; |
| |
| const checkArrays = (type, body, argValues = [], expected) => { |
| let inputArgs = []; |
| let values = []; |
| for (let i = 0; i < argValues.length; ++i) { |
| // Support arrays of float4, including one with a single float4. |
| const totalLength = argValues[i].flat().length; |
| const isBuffer = argValues[i].length === 1 || totalLength > arrayTypeLengths[type]; |
| inputArgs.push(`${isBuffer ? "device " : ""}${type}${isBuffer ? "[]" : ""} in${i}`); |
| values.push(arrayArgMakers[type](argValues[i])); |
| } |
| |
| const src = `${type} ${type}Test(${inputArgs.join(", ")}) { ${body} } |
| `; |
| |
| webGPUPromiseTest(async () => { |
| return arrayFuncs[type](src, `${type}Test`, values).then(result => { |
| for (let i = 0; i < arrayTypeLengths[type]; ++i) |
| assert_approx_equals(result[i], expected[i], epsilon, "Test returned expected value."); |
| }); |
| }, `Return an expected ${type} value.`); |
| } |
| |
| const appendScalarFunctionToSource = (src, type, body, argValues) => { |
| const name = `${type}Test`; |
| |
| let inputArgs = []; |
| let values = []; |
| for (let i = 0; i < argValues.length; ++i) { |
| const isBuffer = Array.isArray(argValues[i]); |
| inputArgs.push(`${isBuffer ? "device " : ""}${type}${isBuffer ? "[]" : ""} in${i}`); |
| values.push(scalarArgMakers[type](argValues[i])); |
| } |
| |
| src += `${type} ${name}(${inputArgs.join(", ")}) { ${body} } |
| `; |
| |
| return [src, name, values]; |
| }; |
| </script> |
| </html> |