mirror of
https://github.com/Rezmason/matrix.git
synced 2026-04-14 12:29: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
|
||||
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)
|
||||
|
||||
@@ -32,74 +32,80 @@ const loadTexture = async (device, url) => {
|
||||
};
|
||||
|
||||
const supportedLayoutTypes = {
|
||||
i32: { alignAtByte: 1, sizeInBytes: 1 },
|
||||
u32: { alignAtByte: 1, sizeInBytes: 1 },
|
||||
f32: { alignAtByte: 1, sizeInBytes: 1 },
|
||||
atomic: { alignAtByte: 1, sizeInBytes: 1 },
|
||||
vec2: { alignAtByte: 2, sizeInBytes: 2 },
|
||||
vec3: { alignAtByte: 4, sizeInBytes: 3 },
|
||||
vec4: { alignAtByte: 4, sizeInBytes: 4 },
|
||||
mat2x2: { alignAtByte: 2, sizeInBytes: 4 },
|
||||
mat3x2: { alignAtByte: 2, sizeInBytes: 6 },
|
||||
mat4x2: { alignAtByte: 2, sizeInBytes: 8 },
|
||||
mat2x3: { alignAtByte: 4, sizeInBytes: 8 },
|
||||
mat3x3: { alignAtByte: 4, sizeInBytes: 12 },
|
||||
mat4x3: { alignAtByte: 4, sizeInBytes: 16 },
|
||||
mat2x4: { alignAtByte: 4, sizeInBytes: 8 },
|
||||
mat3x4: { alignAtByte: 4, sizeInBytes: 12 },
|
||||
mat4x4: { alignAtByte: 4, sizeInBytes: 16 },
|
||||
["i32"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "i32" },
|
||||
["u32"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "u32" },
|
||||
["f32"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "f32" },
|
||||
|
||||
["atomic<i32>"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "i32" },
|
||||
["vec2<i32>"]: { alignAtByte: 2, sizeInBytes: 2, baseType: "i32" },
|
||||
["vec3<i32>"]: { alignAtByte: 4, sizeInBytes: 3, baseType: "i32" },
|
||||
["vec4<i32>"]: { alignAtByte: 4, sizeInBytes: 4, baseType: "i32" },
|
||||
|
||||
["atomic<u32>"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "u32" },
|
||||
["vec2<u32>"]: { alignAtByte: 2, sizeInBytes: 2, baseType: "u32" },
|
||||
["vec3<u32>"]: { alignAtByte: 4, sizeInBytes: 3, baseType: "u32" },
|
||||
["vec4<u32>"]: { alignAtByte: 4, sizeInBytes: 4, baseType: "u32" },
|
||||
|
||||
["atomic<f32>"]: { alignAtByte: 1, sizeInBytes: 1, baseType: "f32" },
|
||||
["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 byteOffsets = [];
|
||||
let sizeInBytes = 0;
|
||||
const entries = [];
|
||||
let byteOffset = 0;
|
||||
for (const type of types) {
|
||||
const layout = supportedLayoutTypes[type.split("<")[0]];
|
||||
if (layout == null) {
|
||||
if (supportedLayoutTypes[type] == null) {
|
||||
throw new Error(`Unsupported type: ${type}`);
|
||||
}
|
||||
sizeInBytes += sizeInBytes % layout.alignAtByte;
|
||||
byteOffsets.push(sizeInBytes);
|
||||
sizeInBytes += layout.sizeInBytes;
|
||||
const { alignAtByte, sizeInBytes, baseType } = supportedLayoutTypes[type];
|
||||
byteOffset = Math.ceil(byteOffset / alignAtByte) * alignAtByte;
|
||||
entries.push({ baseType, byteOffset });
|
||||
byteOffset += sizeInBytes;
|
||||
}
|
||||
|
||||
// console.log(types);
|
||||
// console.log(entries);
|
||||
|
||||
return {
|
||||
byteOffsets,
|
||||
sizeInBytes,
|
||||
size: sizeInBytes * Float32Array.BYTES_PER_ELEMENT,
|
||||
entries,
|
||||
size: byteOffset * Float32Array.BYTES_PER_ELEMENT,
|
||||
};
|
||||
};
|
||||
|
||||
const buildStruct = (layout, values) => {
|
||||
const { byteOffsets, sizeInBytes } = layout;
|
||||
if (values.length !== byteOffsets.length) {
|
||||
throw new Error(`This struct contains ${byteOffsets.length} values, and you supplied only ${values.length}.`);
|
||||
const buildStruct = (buffer, layout, values) => {
|
||||
const { entries } = layout;
|
||||
|
||||
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++) {
|
||||
const diff = byteOffsets[i] - count;
|
||||
if (diff > 0) {
|
||||
buffer.push(Array(diff).fill());
|
||||
}
|
||||
const view = views[entries[i].baseType];
|
||||
const value = values[i];
|
||||
let array;
|
||||
if (Array.isArray(value)) {
|
||||
array = value;
|
||||
} else if (value[Symbol.iterator] != null) {
|
||||
array = Array.from(value);
|
||||
} else {
|
||||
array = [value];
|
||||
}
|
||||
buffer.push(array);
|
||||
count += array.length + diff;
|
||||
const array = value[Symbol.iterator] == null ? [value] : value;
|
||||
view.set(array, entries[i].byteOffset);
|
||||
}
|
||||
{
|
||||
const diff = sizeInBytes - count;
|
||||
if (diff > 0) {
|
||||
buffer.push(Array(diff).fill());
|
||||
}
|
||||
}
|
||||
return buffer.flat();
|
||||
return buffer;
|
||||
};
|
||||
|
||||
export default async (canvas, config) => {
|
||||
@@ -141,14 +147,14 @@ export default async (canvas, config) => {
|
||||
const sampler = device.createSampler();
|
||||
const msdfTexture = await loadTexture(device, config.glyphTexURL);
|
||||
|
||||
const configStructLayout = computeStructLayout(["i32", "i32"]);
|
||||
const configStructLayout = computeStructLayout(["i32", "i32", "f32"]);
|
||||
const configBufferSize = configStructLayout.size;
|
||||
const configBuffer = device.createBuffer({
|
||||
size: configBufferSize,
|
||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.VERTEX | GPUBufferUsage.FRAGMENT, // Which of these are necessary?
|
||||
mappedAtCreation: true,
|
||||
});
|
||||
new Int32Array(configBuffer.getMappedRange()).set(buildStruct(configStructLayout, [numColumns, numRows]));
|
||||
buildStruct(configBuffer.getMappedRange(), configStructLayout, [numColumns, numRows, config.glyphHeightToWidth]);
|
||||
configBuffer.unmap();
|
||||
|
||||
// prettier-ignore
|
||||
@@ -158,7 +164,7 @@ export default async (canvas, config) => {
|
||||
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.FRAGMENT, // Which of these are necessary?
|
||||
mappedAtCreation: true,
|
||||
});
|
||||
new Int32Array(msdfBuffer.getMappedRange()).set(buildStruct(msdfStructLayout, [config.glyphTextureColumns, config.glyphSequenceLength]));
|
||||
buildStruct(msdfBuffer.getMappedRange(), msdfStructLayout, [config.glyphTextureColumns, config.glyphSequenceLength]);
|
||||
msdfBuffer.unmap();
|
||||
|
||||
// prettier-ignore
|
||||
@@ -187,7 +193,7 @@ export default async (canvas, config) => {
|
||||
const aspectRatio = canvasSize[0] / canvasSize[1];
|
||||
mat4.perspectiveZO(camera, (Math.PI / 180) * 90, aspectRatio, 0.0001, 1000);
|
||||
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();
|
||||
|
||||
@@ -291,7 +297,7 @@ export default async (canvas, config) => {
|
||||
updateCameraBuffer();
|
||||
}
|
||||
|
||||
queue.writeBuffer(timeBuffer, 0, new Int32Array(buildStruct(timeStructLayout, [now, frame])));
|
||||
queue.writeBuffer(timeBuffer, 0, buildStruct(null, timeStructLayout, [now, frame]));
|
||||
frame++;
|
||||
|
||||
renderPassConfig.colorAttachments[0].view = canvasContext.getCurrentTexture().createView();
|
||||
@@ -303,7 +309,7 @@ export default async (canvas, config) => {
|
||||
const commandBuffer = encoder.finish();
|
||||
queue.submit([commandBuffer]);
|
||||
|
||||
requestAnimationFrame(renderLoop);
|
||||
// requestAnimationFrame(renderLoop);
|
||||
};
|
||||
|
||||
requestAnimationFrame(renderLoop);
|
||||
|
||||
@@ -5,6 +5,7 @@ let TWO_PI:f32 = 6.28318530718;
|
||||
[[block]] struct Config {
|
||||
numColumns: i32;
|
||||
numRows: i32;
|
||||
glyphHeightToWidth: f32;
|
||||
};
|
||||
[[group(0), binding(0)]] var<uniform> config:Config;
|
||||
|
||||
@@ -58,11 +59,12 @@ struct VertexOutput {
|
||||
f32(config.numRows)
|
||||
);
|
||||
position = 1.0 - position * 2.0;
|
||||
|
||||
// position = position * scene.screenSize;
|
||||
|
||||
var depth:f32 = 0.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;
|
||||
|
||||
return VertexOutput(
|
||||
|
||||
Reference in New Issue
Block a user