mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-18 14:19:30 -07:00
Fixed a struct layout bug. Struct layout and builder now support mixing integer and float data in a struct, and output an ArrayBuffer.
This commit is contained in:
7
TODO.txt
7
TODO.txt
@@ -2,13 +2,6 @@ TODO:
|
|||||||
|
|
||||||
WebGPU
|
WebGPU
|
||||||
std140
|
std140
|
||||||
Right now, buildStruct packs an array, which is changed to a typedarray and fed to a GPUBuffer
|
|
||||||
That limits the type to whatever the typedarray is
|
|
||||||
Instead, we need to represent the type of each value, and write it as such
|
|
||||||
Create an array buffer
|
|
||||||
Create a Float32Array view into it, and an Int32Array view
|
|
||||||
Transcribe the values into these views
|
|
||||||
Return the array buffer
|
|
||||||
|
|
||||||
|
|
||||||
Write an explanation of the rain pass (and include images)
|
Write an explanation of the rain pass (and include images)
|
||||||
|
|||||||
@@ -32,74 +32,80 @@ const loadTexture = async (device, url) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const supportedLayoutTypes = {
|
const supportedLayoutTypes = {
|
||||||
i32: { alignAtByte: 1, sizeInBytes: 1 },
|
["i32"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "i32" },
|
||||||
u32: { alignAtByte: 1, sizeInBytes: 1 },
|
["u32"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "u32" },
|
||||||
f32: { alignAtByte: 1, sizeInBytes: 1 },
|
["f32"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "f32" },
|
||||||
atomic: { alignAtByte: 1, sizeInBytes: 1 },
|
|
||||||
vec2: { alignAtByte: 2, sizeInBytes: 2 },
|
["atomic<i32>"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "i32" },
|
||||||
vec3: { alignAtByte: 4, sizeInBytes: 3 },
|
["vec2<i32>"]: { alignAtByte: 2, sizeInBytes: 2, baseType: "i32" },
|
||||||
vec4: { alignAtByte: 4, sizeInBytes: 4 },
|
["vec3<i32>"]: { alignAtByte: 4, sizeInBytes: 3, baseType: "i32" },
|
||||||
mat2x2: { alignAtByte: 2, sizeInBytes: 4 },
|
["vec4<i32>"]: { alignAtByte: 4, sizeInBytes: 4, baseType: "i32" },
|
||||||
mat3x2: { alignAtByte: 2, sizeInBytes: 6 },
|
|
||||||
mat4x2: { alignAtByte: 2, sizeInBytes: 8 },
|
["atomic<u32>"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "u32" },
|
||||||
mat2x3: { alignAtByte: 4, sizeInBytes: 8 },
|
["vec2<u32>"]: { alignAtByte: 2, sizeInBytes: 2, baseType: "u32" },
|
||||||
mat3x3: { alignAtByte: 4, sizeInBytes: 12 },
|
["vec3<u32>"]: { alignAtByte: 4, sizeInBytes: 3, baseType: "u32" },
|
||||||
mat4x3: { alignAtByte: 4, sizeInBytes: 16 },
|
["vec4<u32>"]: { alignAtByte: 4, sizeInBytes: 4, baseType: "u32" },
|
||||||
mat2x4: { alignAtByte: 4, sizeInBytes: 8 },
|
|
||||||
mat3x4: { alignAtByte: 4, sizeInBytes: 12 },
|
["atomic<f32>"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "f32" },
|
||||||
mat4x4: { alignAtByte: 4, sizeInBytes: 16 },
|
["vec2<f32>"]: { alignAtByte: 2, sizeInBytes: 2, baseType: "f32" },
|
||||||
|
["vec3<f32>"]: { alignAtByte: 4, sizeInBytes: 3, baseType: "f32" },
|
||||||
|
["vec4<f32>"]: { alignAtByte: 4, sizeInBytes: 4, baseType: "f32" },
|
||||||
|
|
||||||
|
["mat2x2<f32>"]: { alignAtByte: 2, sizeInBytes: 4, baseType: "f32" },
|
||||||
|
["mat3x2<f32>"]: { alignAtByte: 2, sizeInBytes: 6, baseType: "f32" },
|
||||||
|
["mat4x2<f32>"]: { alignAtByte: 2, sizeInBytes: 8, baseType: "f32" },
|
||||||
|
["mat2x3<f32>"]: { alignAtByte: 4, sizeInBytes: 8, baseType: "f32" },
|
||||||
|
["mat3x3<f32>"]: { alignAtByte: 4, sizeInBytes: 12, baseType: "f32" },
|
||||||
|
["mat4x3<f32>"]: { alignAtByte: 4, sizeInBytes: 16, baseType: "f32" },
|
||||||
|
["mat2x4<f32>"]: { alignAtByte: 4, sizeInBytes: 8, baseType: "f32" },
|
||||||
|
["mat3x4<f32>"]: { alignAtByte: 4, sizeInBytes: 12, baseType: "f32" },
|
||||||
|
["mat4x4<f32>"]: { alignAtByte: 4, sizeInBytes: 16, baseType: "f32" },
|
||||||
};
|
};
|
||||||
|
|
||||||
const computeStructLayout = (types) => {
|
const computeStructLayout = (types) => {
|
||||||
const byteOffsets = [];
|
const entries = [];
|
||||||
let sizeInBytes = 0;
|
let byteOffset = 0;
|
||||||
for (const type of types) {
|
for (const type of types) {
|
||||||
const layout = supportedLayoutTypes[type.split("<")[0]];
|
if (supportedLayoutTypes[type] == null) {
|
||||||
if (layout == null) {
|
|
||||||
throw new Error(`Unsupported type: ${type}`);
|
throw new Error(`Unsupported type: ${type}`);
|
||||||
}
|
}
|
||||||
sizeInBytes += sizeInBytes % layout.alignAtByte;
|
const { alignAtByte, sizeInBytes, baseType } = supportedLayoutTypes[type];
|
||||||
byteOffsets.push(sizeInBytes);
|
byteOffset = Math.ceil(byteOffset / alignAtByte) * alignAtByte;
|
||||||
sizeInBytes += layout.sizeInBytes;
|
entries.push({ baseType, byteOffset });
|
||||||
|
byteOffset += sizeInBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// console.log(types);
|
||||||
|
// console.log(entries);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
byteOffsets,
|
entries,
|
||||||
sizeInBytes,
|
size: byteOffset * Float32Array.BYTES_PER_ELEMENT,
|
||||||
size: sizeInBytes * Float32Array.BYTES_PER_ELEMENT,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const buildStruct = (layout, values) => {
|
const buildStruct = (buffer, layout, values) => {
|
||||||
const { byteOffsets, sizeInBytes } = layout;
|
const { entries } = layout;
|
||||||
if (values.length !== byteOffsets.length) {
|
|
||||||
throw new Error(`This struct contains ${byteOffsets.length} values, and you supplied only ${values.length}.`);
|
if (values.length !== entries.length) {
|
||||||
|
throw new Error(`This struct contains ${entries.length} values, and you supplied ${values.length}.`);
|
||||||
}
|
}
|
||||||
let buffer = [];
|
|
||||||
let count = 0;
|
buffer ??= new ArrayBuffer(layout.size);
|
||||||
|
|
||||||
|
const views = {
|
||||||
|
i32: new Int32Array(buffer),
|
||||||
|
u32: new Uint32Array(buffer),
|
||||||
|
f32: new Float32Array(buffer),
|
||||||
|
};
|
||||||
|
|
||||||
for (let i = 0; i < values.length; i++) {
|
for (let i = 0; i < values.length; i++) {
|
||||||
const diff = byteOffsets[i] - count;
|
const view = views[entries[i].baseType];
|
||||||
if (diff > 0) {
|
|
||||||
buffer.push(Array(diff).fill());
|
|
||||||
}
|
|
||||||
const value = values[i];
|
const value = values[i];
|
||||||
let array;
|
const array = value[Symbol.iterator] == null ? [value] : value;
|
||||||
if (Array.isArray(value)) {
|
view.set(array, entries[i].byteOffset);
|
||||||
array = value;
|
|
||||||
} else if (value[Symbol.iterator] != null) {
|
|
||||||
array = Array.from(value);
|
|
||||||
} else {
|
|
||||||
array = [value];
|
|
||||||
}
|
|
||||||
buffer.push(array);
|
|
||||||
count += array.length + diff;
|
|
||||||
}
|
}
|
||||||
{
|
return buffer;
|
||||||
const diff = sizeInBytes - count;
|
|
||||||
if (diff > 0) {
|
|
||||||
buffer.push(Array(diff).fill());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buffer.flat();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async (canvas, config) => {
|
export default async (canvas, config) => {
|
||||||
@@ -141,14 +147,14 @@ export default async (canvas, config) => {
|
|||||||
const sampler = device.createSampler();
|
const sampler = device.createSampler();
|
||||||
const msdfTexture = await loadTexture(device, config.glyphTexURL);
|
const msdfTexture = await loadTexture(device, config.glyphTexURL);
|
||||||
|
|
||||||
const configStructLayout = computeStructLayout(["i32", "i32"]);
|
const configStructLayout = computeStructLayout(["i32", "i32", "f32"]);
|
||||||
const configBufferSize = configStructLayout.size;
|
const configBufferSize = configStructLayout.size;
|
||||||
const configBuffer = device.createBuffer({
|
const configBuffer = device.createBuffer({
|
||||||
size: configBufferSize,
|
size: configBufferSize,
|
||||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.VERTEX | GPUBufferUsage.FRAGMENT, // Which of these are necessary?
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.VERTEX | GPUBufferUsage.FRAGMENT, // Which of these are necessary?
|
||||||
mappedAtCreation: true,
|
mappedAtCreation: true,
|
||||||
});
|
});
|
||||||
new Int32Array(configBuffer.getMappedRange()).set(buildStruct(configStructLayout, [numColumns, numRows]));
|
buildStruct(configBuffer.getMappedRange(), configStructLayout, [numColumns, numRows, config.glyphHeightToWidth]);
|
||||||
configBuffer.unmap();
|
configBuffer.unmap();
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -158,7 +164,7 @@ export default async (canvas, config) => {
|
|||||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.FRAGMENT, // Which of these are necessary?
|
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.FRAGMENT, // Which of these are necessary?
|
||||||
mappedAtCreation: true,
|
mappedAtCreation: true,
|
||||||
});
|
});
|
||||||
new Int32Array(msdfBuffer.getMappedRange()).set(buildStruct(msdfStructLayout, [config.glyphTextureColumns, config.glyphSequenceLength]));
|
buildStruct(msdfBuffer.getMappedRange(), msdfStructLayout, [config.glyphTextureColumns, config.glyphSequenceLength]);
|
||||||
msdfBuffer.unmap();
|
msdfBuffer.unmap();
|
||||||
|
|
||||||
// prettier-ignore
|
// prettier-ignore
|
||||||
@@ -187,7 +193,7 @@ export default async (canvas, config) => {
|
|||||||
const aspectRatio = canvasSize[0] / canvasSize[1];
|
const aspectRatio = canvasSize[0] / canvasSize[1];
|
||||||
mat4.perspectiveZO(camera, (Math.PI / 180) * 90, aspectRatio, 0.0001, 1000);
|
mat4.perspectiveZO(camera, (Math.PI / 180) * 90, aspectRatio, 0.0001, 1000);
|
||||||
const screenSize = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
|
const screenSize = aspectRatio > 1 ? [1, aspectRatio] : [1 / aspectRatio, 1];
|
||||||
queue.writeBuffer(sceneBuffer, 0, new Float32Array(buildStruct(sceneStructLayout, [screenSize, camera, transform])));
|
queue.writeBuffer(sceneBuffer, 0, buildStruct(null, sceneStructLayout, [screenSize, camera, transform]));
|
||||||
};
|
};
|
||||||
updateCameraBuffer();
|
updateCameraBuffer();
|
||||||
|
|
||||||
@@ -291,7 +297,7 @@ export default async (canvas, config) => {
|
|||||||
updateCameraBuffer();
|
updateCameraBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
queue.writeBuffer(timeBuffer, 0, new Int32Array(buildStruct(timeStructLayout, [now, frame])));
|
queue.writeBuffer(timeBuffer, 0, buildStruct(null, timeStructLayout, [now, frame]));
|
||||||
frame++;
|
frame++;
|
||||||
|
|
||||||
renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView();
|
renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView();
|
||||||
@@ -303,7 +309,7 @@ export default async (canvas, config) => {
|
|||||||
const commandBuffer = encoder.finish();
|
const commandBuffer = encoder.finish();
|
||||||
queue.submit([commandBuffer]);
|
queue.submit([commandBuffer]);
|
||||||
|
|
||||||
requestAnimationFrame(renderLoop);
|
// requestAnimationFrame(renderLoop);
|
||||||
};
|
};
|
||||||
|
|
||||||
requestAnimationFrame(renderLoop);
|
requestAnimationFrame(renderLoop);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ let TWO_PI:f32 = 6.28318530718;
|
|||||||
[[block]] struct Config {
|
[[block]] struct Config {
|
||||||
numColumns: i32;
|
numColumns: i32;
|
||||||
numRows: i32;
|
numRows: i32;
|
||||||
|
glyphHeightToWidth: f32;
|
||||||
};
|
};
|
||||||
[[group(0), binding(0)]] var<uniform> config:Config;
|
[[group(0), binding(0)]] var<uniform> config:Config;
|
||||||
|
|
||||||
@@ -58,11 +59,12 @@ struct VertexOutput {
|
|||||||
f32(config.numRows)
|
f32(config.numRows)
|
||||||
);
|
);
|
||||||
position = 1.0 - position * 2.0;
|
position = 1.0 - position * 2.0;
|
||||||
|
|
||||||
// position = position * scene.screenSize;
|
// position = position * scene.screenSize;
|
||||||
|
|
||||||
var depth:f32 = 0.0;
|
var depth:f32 = 0.0;
|
||||||
var pos: vec4<f32> = vec4<f32>(position, depth, 1.0);
|
var pos: vec4<f32> = vec4<f32>(position, depth, 1.0);
|
||||||
// pos.x = pos.x / glyphHeightToWidth;
|
pos.x = pos.x / config.glyphHeightToWidth;
|
||||||
pos = scene.camera * scene.transform * pos;
|
pos = scene.camera * scene.transform * pos;
|
||||||
|
|
||||||
return VertexOutput(
|
return VertexOutput(
|
||||||
|
|||||||
Reference in New Issue
Block a user