Trying to implement the spec for the alignment and size of struct and array fields in gpu-uniforms.

This commit is contained in:
Rezmason
2021-11-08 08:58:55 -08:00
parent bbe3d62331
commit 6ab16b3091
2 changed files with 47 additions and 31 deletions

View File

@@ -24,18 +24,19 @@ const simpleTypes = {
["f32"]: [1, 1, "f32", zero],
["atomic<i32>"]: [1, 1, "i32", zero],
["vec2<i32>"]: [2, 2, "i32", array(2)],
["vec3<i32>"]: [4, 3, "i32", array(3)],
["vec4<i32>"]: [4, 4, "i32", array(4)],
["atomic<u32>"]: [1, 1, "u32", zero],
["vec2<u32>"]: [2, 2, "u32", array(2)],
["vec3<u32>"]: [4, 3, "u32", array(3)],
["vec4<u32>"]: [4, 4, "u32", array(4)],
["atomic<f32>"]: [1, 1, "f32", zero],
["vec2<i32>"]: [2, 2, "i32", array(2)],
["vec2<u32>"]: [2, 2, "u32", array(2)],
["vec2<f32>"]: [2, 2, "f32", array(2)],
["vec3<i32>"]: [4, 3, "i32", array(3)],
["vec3<u32>"]: [4, 3, "u32", array(3)],
["vec3<f32>"]: [4, 3, "f32", array(3)],
["vec4<i32>"]: [4, 4, "i32", array(4)],
["vec4<u32>"]: [4, 4, "u32", array(4)],
["vec4<f32>"]: [4, 4, "f32", array(4)],
["mat2x2<f32>"]: [2, 4, "f32", array(2 * 2)],
@@ -51,21 +52,33 @@ const simpleTypes = {
const getTypeData = (type, attributes, otherStructLayouts) => {
if (simpleTypes[type] != null) {
const [alignAtByte, sizeInBytes, baseType, defaultValue] = simpleTypes[type];
let [align, size, baseType, defaultValue] = simpleTypes[type];
if (attributes.align != null) {
align = parseInt(attributes.align) / 4;
}
if (attributes.size != null) {
size = parseInt(attributes.size) / 4;
}
return {
baseType,
alignAtByte,
sizeInBytes,
align,
size,
defaultValue,
};
} else if (type in otherStructLayouts) {
const innerLayout = otherStructLayouts[type];
const { alignAtByte, sizeInBytes } = innerLayout;
let { align, size } = innerLayout;
if (attributes.align != null) {
align = parseInt(attributes.align) / 4;
}
if (attributes.size != null) {
size = parseInt(attributes.size) / 4;
}
return {
isStruct: true,
innerLayout,
sizeInBytes,
alignAtByte,
size,
align,
defaultValue: () => makeDataForLayout(otherStructLayouts, innerLayout),
};
} else if (type.startsWith("array<")) {
@@ -77,16 +90,19 @@ const getTypeData = (type, attributes, otherStructLayouts) => {
const innerTypeData = getTypeData(innerType, [], otherStructLayouts);
const mult = parseInt(fixedSize ?? "0");
const alignAtByte = 1; // TODO: calculate based on align rule of arrays
const sizeInBytes = 1; // TODO: calculate based on size rule of arrays
// TODO: support stride attribute
let align = innerTypeData.align;
let size = Math.ceil(innerTypeData.size / align) * align * mult;
if (attributes.stride != null) {
size = parseInt(attributes.stride) * mult;
}
return {
isArray: true,
isFixedSize: mult > 0,
innerTypeData,
mult,
sizeInBytes,
alignAtByte,
size,
align,
defaultValue: () =>
Array(mult)
.fill()
@@ -99,11 +115,11 @@ const getTypeData = (type, attributes, otherStructLayouts) => {
};
const parseAttributes = (str) => {
const attributes = [];
const attributes = {};
for (const attr of str.split(",").filter((attr) => attr.length > 0)) {
const match = attr.match(/(\w+)(\((.*)\))?/); // foo(bar)
const [_, name, __, value] = match;
attributes.push({ name, value });
attributes[name] = value;
}
return attributes;
};
@@ -126,8 +142,7 @@ const parseStruct = (str, structLayouts) => {
return null;
}
byteOffset = Math.ceil(byteOffset / typeData.alignAtByte) * typeData.alignAtByte;
// TODO: support align and size attributes
byteOffset = Math.ceil(byteOffset / typeData.align) * typeData.align;
fields.push({
attributes: parseAttributes(leftAttributes ?? ""),
identifier,
@@ -135,13 +150,13 @@ const parseStruct = (str, structLayouts) => {
...typeData,
byteOffset,
});
byteOffset += typeData.sizeInBytes;
byteOffset += typeData.size;
}
const minSizeInBytes = byteOffset * BYTES_PER_ELEMENT;
const sizeInBytes = minSizeInBytes; // TODO: support runtime-sized arrays
const alignAtByte = 1; // TODO: calculate based on align rule of structs
return { name, fields, sizeInBytes, alignAtByte };
const align = Math.max(...fields.map((field) => field.align));
const size = Math.ceil(minSizeInBytes / align) * align; // TODO: support runtime-sized arrays
return { name, fields, size, align };
};
const parseStructLayoutsFromShader = (wgsl) => {
@@ -189,12 +204,12 @@ const writeField = (allLayouts, field, value, views, byteOffset, warnMissingFiel
};
const makeGenerator = (layout, structLayouts) => {
const minSize = layout.sizeInBytes;
const minSize = layout.size;
return Object.freeze({
minSize,
create: () => makeDataForLayout(structLayouts, layout),
write: (object, destination, warnMissingFields = false) => {
destination ??= new ArrayBuffer(layout.sizeInBytes); // TODO: expand to support runtime-sized arrays, via the length of the array on the data object
destination ??= new ArrayBuffer(layout.size); // TODO: expand to support runtime-sized arrays, via the length of the array on the data object
const views = {
i32: new Int32Array(destination),