mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-22 07:39:30 -07:00
Trying to implement the spec for the alignment and size of struct and array fields in gpu-uniforms.
This commit is contained in:
5
TODO.txt
5
TODO.txt
@@ -7,14 +7,15 @@ WebGPU
|
|||||||
|
|
||||||
Try to change post processing to compute shaders once they're easier to support
|
Try to change post processing to compute shaders once they're easier to support
|
||||||
|
|
||||||
gpu-uniforms
|
gpu-uniforms, working title
|
||||||
Is this an adequate name for it? Can't it be useful for non-uniform-related things?
|
Is this an adequate name for it? Can't it be useful for non-uniform-related things?
|
||||||
gpu-buffer maybe?
|
gpu-buffer maybe?
|
||||||
Resolve the remaining to-dos
|
Get all the units to be the same
|
||||||
Try and use it for the palette color buffer
|
Try and use it for the palette color buffer
|
||||||
Test it
|
Test it
|
||||||
Demo it to others
|
Demo it to others
|
||||||
Make improvements
|
Make improvements
|
||||||
|
Capture expected requirements down the road, make roadmap
|
||||||
License it and put it somewhere else
|
License it and put it somewhere else
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -24,18 +24,19 @@ const simpleTypes = {
|
|||||||
["f32"]: [1, 1, "f32", zero],
|
["f32"]: [1, 1, "f32", zero],
|
||||||
|
|
||||||
["atomic<i32>"]: [1, 1, "i32", 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],
|
["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],
|
["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)],
|
["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)],
|
["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)],
|
["vec4<f32>"]: [4, 4, "f32", array(4)],
|
||||||
|
|
||||||
["mat2x2<f32>"]: [2, 4, "f32", array(2 * 2)],
|
["mat2x2<f32>"]: [2, 4, "f32", array(2 * 2)],
|
||||||
@@ -51,21 +52,33 @@ const simpleTypes = {
|
|||||||
|
|
||||||
const getTypeData = (type, attributes, otherStructLayouts) => {
|
const getTypeData = (type, attributes, otherStructLayouts) => {
|
||||||
if (simpleTypes[type] != null) {
|
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 {
|
return {
|
||||||
baseType,
|
baseType,
|
||||||
alignAtByte,
|
align,
|
||||||
sizeInBytes,
|
size,
|
||||||
defaultValue,
|
defaultValue,
|
||||||
};
|
};
|
||||||
} else if (type in otherStructLayouts) {
|
} else if (type in otherStructLayouts) {
|
||||||
const innerLayout = otherStructLayouts[type];
|
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 {
|
return {
|
||||||
isStruct: true,
|
isStruct: true,
|
||||||
innerLayout,
|
innerLayout,
|
||||||
sizeInBytes,
|
size,
|
||||||
alignAtByte,
|
align,
|
||||||
defaultValue: () => makeDataForLayout(otherStructLayouts, innerLayout),
|
defaultValue: () => makeDataForLayout(otherStructLayouts, innerLayout),
|
||||||
};
|
};
|
||||||
} else if (type.startsWith("array<")) {
|
} else if (type.startsWith("array<")) {
|
||||||
@@ -77,16 +90,19 @@ const getTypeData = (type, attributes, otherStructLayouts) => {
|
|||||||
const innerTypeData = getTypeData(innerType, [], otherStructLayouts);
|
const innerTypeData = getTypeData(innerType, [], otherStructLayouts);
|
||||||
|
|
||||||
const mult = parseInt(fixedSize ?? "0");
|
const mult = parseInt(fixedSize ?? "0");
|
||||||
const alignAtByte = 1; // TODO: calculate based on align rule of arrays
|
let align = innerTypeData.align;
|
||||||
const sizeInBytes = 1; // TODO: calculate based on size rule of arrays
|
let size = Math.ceil(innerTypeData.size / align) * align * mult;
|
||||||
// TODO: support stride attribute
|
if (attributes.stride != null) {
|
||||||
|
size = parseInt(attributes.stride) * mult;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isArray: true,
|
isArray: true,
|
||||||
isFixedSize: mult > 0,
|
isFixedSize: mult > 0,
|
||||||
innerTypeData,
|
innerTypeData,
|
||||||
mult,
|
mult,
|
||||||
sizeInBytes,
|
size,
|
||||||
alignAtByte,
|
align,
|
||||||
defaultValue: () =>
|
defaultValue: () =>
|
||||||
Array(mult)
|
Array(mult)
|
||||||
.fill()
|
.fill()
|
||||||
@@ -99,11 +115,11 @@ const getTypeData = (type, attributes, otherStructLayouts) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const parseAttributes = (str) => {
|
const parseAttributes = (str) => {
|
||||||
const attributes = [];
|
const attributes = {};
|
||||||
for (const attr of str.split(",").filter((attr) => attr.length > 0)) {
|
for (const attr of str.split(",").filter((attr) => attr.length > 0)) {
|
||||||
const match = attr.match(/(\w+)(\((.*)\))?/); // foo(bar)
|
const match = attr.match(/(\w+)(\((.*)\))?/); // foo(bar)
|
||||||
const [_, name, __, value] = match;
|
const [_, name, __, value] = match;
|
||||||
attributes.push({ name, value });
|
attributes[name] = value;
|
||||||
}
|
}
|
||||||
return attributes;
|
return attributes;
|
||||||
};
|
};
|
||||||
@@ -126,8 +142,7 @@ const parseStruct = (str, structLayouts) => {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
byteOffset = Math.ceil(byteOffset / typeData.alignAtByte) * typeData.alignAtByte;
|
byteOffset = Math.ceil(byteOffset / typeData.align) * typeData.align;
|
||||||
// TODO: support align and size attributes
|
|
||||||
fields.push({
|
fields.push({
|
||||||
attributes: parseAttributes(leftAttributes ?? ""),
|
attributes: parseAttributes(leftAttributes ?? ""),
|
||||||
identifier,
|
identifier,
|
||||||
@@ -135,13 +150,13 @@ const parseStruct = (str, structLayouts) => {
|
|||||||
...typeData,
|
...typeData,
|
||||||
byteOffset,
|
byteOffset,
|
||||||
});
|
});
|
||||||
byteOffset += typeData.sizeInBytes;
|
byteOffset += typeData.size;
|
||||||
}
|
}
|
||||||
|
|
||||||
const minSizeInBytes = byteOffset * BYTES_PER_ELEMENT;
|
const minSizeInBytes = byteOffset * BYTES_PER_ELEMENT;
|
||||||
const sizeInBytes = minSizeInBytes; // TODO: support runtime-sized arrays
|
const align = Math.max(...fields.map((field) => field.align));
|
||||||
const alignAtByte = 1; // TODO: calculate based on align rule of structs
|
const size = Math.ceil(minSizeInBytes / align) * align; // TODO: support runtime-sized arrays
|
||||||
return { name, fields, sizeInBytes, alignAtByte };
|
return { name, fields, size, align };
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseStructLayoutsFromShader = (wgsl) => {
|
const parseStructLayoutsFromShader = (wgsl) => {
|
||||||
@@ -189,12 +204,12 @@ const writeField = (allLayouts, field, value, views, byteOffset, warnMissingFiel
|
|||||||
};
|
};
|
||||||
|
|
||||||
const makeGenerator = (layout, structLayouts) => {
|
const makeGenerator = (layout, structLayouts) => {
|
||||||
const minSize = layout.sizeInBytes;
|
const minSize = layout.size;
|
||||||
return Object.freeze({
|
return Object.freeze({
|
||||||
minSize,
|
minSize,
|
||||||
create: () => makeDataForLayout(structLayouts, layout),
|
create: () => makeDataForLayout(structLayouts, layout),
|
||||||
write: (object, destination, warnMissingFields = false) => {
|
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 = {
|
const views = {
|
||||||
i32: new Int32Array(destination),
|
i32: new Int32Array(destination),
|
||||||
|
|||||||
Reference in New Issue
Block a user