| <!DOCTYPE html> |
| <html> |
| <meta charset=utf-8> |
| <title>Test the WHLSL test harness.</title> |
| <script src="whlsl/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", "uchar", "uint", "float"]; |
| |
| const numericScalarFuncs = { |
| "int": callIntFunction, |
| "uchar": callUcharFunction, |
| "uint": callUintFunction, |
| "float": callFloatFunction |
| }; |
| |
| const scalarArgMakers = { |
| "int": makeInt, |
| "uchar": makeUchar, |
| "uint": makeUint, |
| "float": makeFloat |
| }; |
| |
| let mslTests = {}; |
| |
| mslTests.literals = () => { |
| checkBools("Return a literal of type bool.", "return true;"); |
| checkFloat4s("return float4(0, 1, 2, 3);"); |
| checkNumericScalars("return 42;", [], 42); |
| }; |
| |
| mslTests.singleArgument = () => { |
| checkBools("Upload and return a bool value.", "return in0;", [true]); |
| checkFloat4s("return in0.wzyx;", [[3, 2, 1, 0]]); |
| checkNumericScalars("return in0;", [42], 42); |
| }; |
| |
| mslTests.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]); |
| |
| const body = `return in0 + in1 + in2 + in3 + in4 + in5 + in6 + in7;`; |
| let args = []; |
| for (let i = 0; i < 8; ++i) |
| args.push([0, 1, 2, 3]); |
| checkFloat4s(body, args, [0, 8, 16, 24]); |
| checkNumericScalars(body, [0, 1, 2, 3, 4, 5, 6, 7], 28); |
| }; |
| |
| mslTests.buffersWithOneValue = () => { |
| const body = `return in0[0];` |
| checkBools("Access and return a single bool through a bool*.", body, [[true]]); |
| checkFloat4s(body, [[[0, 1, 2, 3]]]); |
| checkNumericScalars(body, [[42]], 42); |
| }; |
| |
| mslTests.multipleBufferArguments = () => { |
| checkBools("Access multiple bools through various buffers and return a bool.", |
| "return in0[0] & in0[1] & in0[2] & in1 & in2[0];", |
| [[true, true, true], true, [true]]); |
| |
| const body = `return in0[0] + in0[1] + in0[2] + in1 + in2[0];`; |
| const vector = [0, 1, 2, 3]; |
| checkFloat4s(body, [[vector, vector, vector], vector, [vector]], [0, 5, 10, 15]); |
| checkNumericScalars(body, [[0, 1, 2], 3, [4]], 10); |
| }; |
| |
| mslTests.multipleArgumentTypes = () => { |
| const src = `float test(int i, uchar c, device uint* u, bool b, device bool* bs, float4 f4, device float* fs) |
| { |
| if (b && bs[0] && bs[1]) |
| return i + c + u[0] + f4.x + f4.y + f4.z + f4.w + fs[0] + fs[1]; |
| |
| return 0; |
| }`; |
| const i = makeInt(1); |
| const c = makeUchar(2); |
| const u = makeUint([3]); |
| const b = makeBool(true); |
| const bs = makeBool([true, true]); |
| const f4 = makeFloat4([4, 5, 6, 7]); |
| const fs = makeFloat([8, 9]); |
| webGPUPromiseTest(() => { |
| return callFloatFunction(src, "test", [i, c, u, b, bs, f4, fs]).then(result => { |
| assert_approx_equals(result, 45, epsilon, "Test returned expected value."); |
| }); |
| }, "Upload and calculate a result from varied argument types."); |
| }; |
| |
| mslTests.bufferStores = () => { |
| let src = `void test(device float4* out) { |
| *out = 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 uchar* in, device uchar* out) { |
| for (uint i = 0; i < 5; ++i) |
| out[i] = in[i]; |
| }`; |
| const array = [0, 1, 2, 3, 4]; |
| const input = makeUchar(array); |
| const output = makeUchar([0, 0, 0, 0, 0]); |
| callVoidFunction(src, "test", [input, output]); |
| |
| webGPUPromiseTest(() => { |
| return output.getArrayBuffer().then(arrayBuffer => { |
| const result = new Uint8Array(arrayBuffer); |
| assert_array_equals(array, result, "Test stored expected values."); |
| }); |
| }, "Upload a uchar* and store into a uchar*."); |
| }; |
| |
| window.addEventListener("load", () => { |
| harness.isWHLSL = false; |
| for (const name in mslTests) { |
| mslTests[name](); |
| } |
| }); |
| |
| /* Helper functions */ |
| |
| const checkNumericScalars = (body, argValues, expected) => { |
| let functions = []; |
| let src = ""; |
| for (let type of numericScalarTypes) { |
| const name = `${type}Test`; |
| |
| let inputArgs = []; |
| let values = []; |
| for (let i = 0; i < argValues.length; ++i) { |
| const isPointer = Array.isArray(argValues[i]); |
| inputArgs.push(`${isPointer ? "device " : ""}${type}${isPointer ? "*" : ""} in${i}`); |
| values.push(scalarArgMakers[type](argValues[i])); |
| } |
| |
| src += `${type} ${name}(${inputArgs.join(", ")}) { ${body} } |
| `; |
| 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) => { |
| let src = ""; |
| let inputArgs = []; |
| let values = []; |
| for (let i = 0; i < argValues.length; ++i) { |
| const isPointer = Array.isArray(argValues[i]); |
| inputArgs.push(`${isPointer ? "device " : ""}bool${isPointer ? "*" : ""} in${i}`); |
| values.push(makeBool(argValues[i])); |
| } |
| |
| src += `bool boolTest(${inputArgs.join(", ")}) { ${body} } |
| `; |
| |
| webGPUPromiseTest(async () => { |
| return callBoolFunction(src, "boolTest", values).then(result => { |
| assert_equals(result, expected, "Test returned expected value."); |
| }); |
| }, msg); |
| }; |
| |
| const checkFloat4s = (body, argValues = [], expected = [0, 1, 2, 3]) => { |
| let src = ""; |
| 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 isPointer = argValues[i].length === 1 || totalLength > 4; |
| inputArgs.push(`${isPointer ? "device " : ""}float4${isPointer ? "*" : ""} in${i}`); |
| values.push(makeFloat4(argValues[i])); |
| } |
| |
| src += `float4 float4Test(${inputArgs.join(", ")}) { ${body} } |
| `; |
| |
| webGPUPromiseTest(async () => { |
| return callFloat4Function(src, "float4Test", values).then(result => { |
| for (let i = 0; i < 4; ++i) |
| assert_approx_equals(result[i], expected[i], epsilon, "Test returned expected value."); |
| }); |
| }, "Return an expected float4 value."); |
| } |
| </script> |
| </html> |