blob: 8083987a92b11fbe661c6c20f4f15058274230bf [file] [log] [blame]
<!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>