[WHLSL] Support float4x4 in test harness
https://bugs.webkit.org/show_bug.cgi?id=199484
Reviewed by Myles C. Maxfield.
Support WHLSL float4x4 parameters in the WHLSL test harness.
* webgpu/js/whlsl-test-harness.js:
(isScalar):
(convertTypeToArrayType):
(convertTypeToWHLSLType):
(Data):
(makeFloat4):
(makeFloat4x4):
(processArrays):
(async.callFloat4x4Function):
(isVectorType): Deleted. Replaced with isScalar.
Add some float4x4 sanity checks:
* webgpu/whlsl-test-harness-test-expected.txt:
* webgpu/whlsl-test-harness-test.html:
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@247130 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog
index 8de37d3..a88e8cc 100644
--- a/LayoutTests/ChangeLog
+++ b/LayoutTests/ChangeLog
@@ -1,3 +1,27 @@
+2019-07-03 Justin Fan <justin_fan@apple.com>
+
+ [WHLSL] Support float4x4 in test harness
+ https://bugs.webkit.org/show_bug.cgi?id=199484
+
+ Reviewed by Myles C. Maxfield.
+
+ Support WHLSL float4x4 parameters in the WHLSL test harness.
+
+ * webgpu/js/whlsl-test-harness.js:
+ (isScalar):
+ (convertTypeToArrayType):
+ (convertTypeToWHLSLType):
+ (Data):
+ (makeFloat4):
+ (makeFloat4x4):
+ (processArrays):
+ (async.callFloat4x4Function):
+ (isVectorType): Deleted. Replaced with isScalar.
+
+ Add some float4x4 sanity checks:
+ * webgpu/whlsl-test-harness-test-expected.txt:
+ * webgpu/whlsl-test-harness-test.html:
+
2019-07-03 Simon Fraser <simon.fraser@apple.com>
RELEASE_ASSERT in WebCore: WebCore::ScrollingStateTree::insertNode()
diff --git a/LayoutTests/webgpu/js/whlsl-test-harness.js b/LayoutTests/webgpu/js/whlsl-test-harness.js
index cfa4c35..482595c3 100644
--- a/LayoutTests/webgpu/js/whlsl-test-harness.js
+++ b/LayoutTests/webgpu/js/whlsl-test-harness.js
@@ -9,16 +9,18 @@
UINT: Symbol("uint"),
FLOAT: Symbol("float"),
FLOAT4: Symbol("float4"),
- MAX_SIZE: 16 // This needs to be big enough to hold any singular WHLSL type.
+ FLOAT4X4: Symbol("float4x4"),
+ MAX_SIZE: 64 // This needs to be big enough to hold any singular WHLSL type.
});
-function isVectorType(type)
+function isScalar(type)
{
switch(type) {
case Types.FLOAT4:
- return true;
- default:
+ case Types.FLOAT4X4:
return false;
+ default:
+ return true;
}
}
@@ -37,6 +39,7 @@
return Uint32Array;
case Types.FLOAT:
case Types.FLOAT4:
+ case Types.FLOAT4X4:
return Float32Array;
default:
throw new Error("Invalid TYPE provided!");
@@ -58,6 +61,8 @@
return "float";
case Types.FLOAT4:
return "float4";
+ case Types.FLOAT4X4:
+ return "float4x4";
default:
throw new Error("Invalid TYPE provided!");
}
@@ -107,7 +112,7 @@
// However, vector types are also created via an array of scalars.
// This ensures that buffers of just one vector are usable in a test function.
if (Array.isArray(values))
- this._isBuffer = isVectorType(type) ? isBuffer : true;
+ this._isBuffer = isScalar(type) ? true : isBuffer;
else {
this._isBuffer = false;
values = [values];
@@ -434,13 +439,33 @@
*/
function makeFloat4(values)
{
+ const results = processArrays(values, 4);
+ return new Data(harness, Types.FLOAT4, results.values, results.isBuffer);
+}
+
+/**
+ * @param {Array or Array[Array]} values - 1D or 2D array of float values.
+ * The total number of float values must be divisible by 16.
+ * A single 16-element array of floats will be treated as a single float4x4 argument in the shader.
+ * This should follow the glMatrix/OpenGL method of storing 4x4 matrices,
+ * where the x, y, z translation components are the 13th, 14th, and 15th elements respectively.
+ */
+function makeFloat4x4(values)
+{
+ const results = processArrays(values, 16);
+ return new Data(harness, Types.FLOAT4X4, results.values, results.isBuffer);
+}
+
+function processArrays(values, minimumLength)
+{
const originalLength = values.length;
// This works because float4 is tightly packed.
// When implementing other vector types, add padding if needed.
values = values.flat();
- if (values.length % 4 != 0)
- throw new Error("makeFloat4: Invalid number of elements!");
- return new Data(harness, Types.FLOAT4, values, originalLength === 1 || values.length > 4);
+ if (values.length % minimumLength != 0)
+ throw new Error("Invalid number of elements in non-scalar type!");
+
+ return { values: values, isBuffer: originalLength === 1 || values.length > minimumLength };
}
/**
@@ -479,6 +504,11 @@
return (await harness.callTypedFunction(Types.FLOAT4, functions, name, args)).subarray(0, 4);
}
+async function callFloat4x4Function(functions, name, args)
+{
+ return (await harness.callTypedFunction(Types.FLOAT4X4, functions, name, args)).subarray(0, 16);
+}
+
/**
* Does not return a Promise. To observe the results of a call,
* call 'getArrayBuffer' on the Data object retaining your output buffer.
diff --git a/LayoutTests/webgpu/whlsl-test-harness-test-expected.txt b/LayoutTests/webgpu/whlsl-test-harness-test-expected.txt
index 26b2bd8..a07577d 100644
--- a/LayoutTests/webgpu/whlsl-test-harness-test-expected.txt
+++ b/LayoutTests/webgpu/whlsl-test-harness-test-expected.txt
@@ -25,4 +25,9 @@
PASS Upload and calculate a result from varied argument types.
PASS Store into a float4[].
PASS Upload a int[] and store into a int[].
+PASS Upload a float4x4[] and store into a float4x4[].
+PASS Return an expected float4x4 value.
+PASS Return an expected float4x4 value.
+PASS Return an expected float4x4 value.
+PASS Return an expected float4x4 value.
diff --git a/LayoutTests/webgpu/whlsl-test-harness-test.html b/LayoutTests/webgpu/whlsl-test-harness-test.html
index 6128b35..7c622b6 100644
--- a/LayoutTests/webgpu/whlsl-test-harness-test.html
+++ b/LayoutTests/webgpu/whlsl-test-harness-test.html
@@ -27,17 +27,35 @@
"float": makeFloat
};
+const arrayTypeLengths = {
+ "float4": 4,
+ "float4x4": 16
+};
+
+const arrayFuncs = {
+ "float4": callFloat4Function,
+ "float4x4": callFloat4x4Function,
+};
+
+const arrayArgMakers = {
+ "float4": makeFloat4,
+ "float4x4": makeFloat4x4
+};
+
+const float4x4expected = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
+const float4expected = float4x4expected.slice(0, 4);
+
let whlslTests = {};
whlslTests.literals = () => {
checkBools("Return a literal of type bool.", "return true;");
- checkFloat4s("return float4(0, 1, 2, 3);");
+ 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]);
- checkFloat4s("return in0;", [[0, 1, 2, 3]]);
+ checkArrays("float4", "return in0;", [float4expected], float4expected);
checkNumericScalars("return in0;", [42], 42);
};
@@ -49,8 +67,8 @@
let body = "return float4(in0.x, in1.y, in2.z, in3.w);"
let args = [];
for (let i = 0; i < 4; ++i)
- args.push([0, 1, 2, 3]);
- checkFloat4s(body, args, [0, 1, 2, 3]);
+ 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);
@@ -58,7 +76,7 @@
whlslTests.buffersWithOneValue = () => {
const body = `return in0[0];`
- checkFloat4s(body, [[[0, 1, 2, 3]]]);
+ checkArrays("float4", body, [[float4expected]], float4expected);
checkNumericScalars(body, [[42]], 42);
};
@@ -70,8 +88,7 @@
float w = in0[0].w + in0[1].w + in0[2].w + in1.w + in2[0].w;
return float4(x, y, z, w);`;
- const vector = [0, 1, 2, 3];
- checkFloat4s(body, [[vector, vector, vector], vector, [vector]], [0, 5, 10, 15]);
+ 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);
@@ -127,6 +144,38 @@
}, "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));", [], float4x4expected);
+ 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, float4x4expected);
+ checkArrays("float4x4", "return in0[0];", [[float4x4expected]], float4x4expected);
+};
+
window.addEventListener("load", () => {
try {
for (const name in whlslTests) {
@@ -175,26 +224,26 @@
}, msg);
};
-const checkFloat4s = (body, argValues = [], expected = [0, 1, 2, 3]) => {
+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 > 4;
- inputArgs.push(`${isBuffer ? "device " : ""}float4${isBuffer ? "[]" : ""} in${i}`);
- values.push(makeFloat4(argValues[i]));
+ 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 = `float4 float4Test(${inputArgs.join(", ")}) { ${body} }
+ const src = `${type} ${type}Test(${inputArgs.join(", ")}) { ${body} }
`;
webGPUPromiseTest(async () => {
- return callFloat4Function(src, "float4Test", values).then(result => {
- for (let i = 0; i < 4; ++i)
+ 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 float4 value.");
+ }, `Return an expected ${type} value.`);
}
const appendScalarFunctionToSource = (src, type, body, argValues) => {