renderer might actually render something next commit

This commit is contained in:
41666 2023-10-08 15:04:51 -04:00
parent 627adcc07f
commit 000f35f19d
7 changed files with 126 additions and 9 deletions

BIN
bun.lockb

Binary file not shown.

File diff suppressed because one or more lines are too long

View file

@ -22,6 +22,7 @@
"esbuild-plugin-glsl": "^1.2.2",
"glob": "^10.3.10",
"serve": "^14.2.1",
"typescript": "^5.2.2"
"typescript": "^5.2.2",
"wgpu-matrix": "^2.5.0"
}
}

View file

@ -6,6 +6,7 @@ import { Shader } from "../renderer/shader";
const app = new WebGPUApp({ fov: 20 });
const renderer = new MeshRenderer(app, plane);
const shader = new Shader(rainbowPlane);
const renderer = new MeshRenderer(app, plane, shader);
app.start();

View file

@ -1,3 +1,4 @@
import { Mat4, mat4, vec3 } from "wgpu-matrix";
import { Behavior } from "./behavior";
import { Mesh } from "./mesh";
import { Shader } from "./shader";
@ -6,29 +7,49 @@ import { WebGPUApp } from "./webgpu";
export class MeshRenderer extends Behavior {
private depthTexture?: GPUTexture;
private uniformBuffer?: GPUBuffer;
private vertexBuffer?: GPUBuffer;
private texture?: GPUTexture;
private sampler?: GPUSampler;
private uniformBindGroup?: GPUBindGroup;
private renderPassDescriptor?: GPURenderPassDescriptor;
private pipeline?: GPURenderPipeline;
private viewMatrix = mat4.translate(
mat4.identity(),
vec3.fromValues(0, 0, -4)
);
private projectionMatrix = mat4.perspective(
2 * Math.PI * 0.2,
1920 / 1080,
1,
100
);
constructor(
public app: WebGPUApp,
public mesh: Mesh,
public shader: Shader
public shader: Shader,
public textures?: any[]
) {
super(app);
}
onStart() {
this.projectionMatrix = mat4.perspective(
2 * Math.PI * 0.2,
this.app.canvas.width / this.app.canvas.height,
1,
100
);
this.depthTexture = this.app.device.createTexture({
size: [this.app.canvas.width, this.app.canvas.height],
format: "depth24plus",
usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
// float32x4x4 + float32
this.uniformBuffer = this.app.device.createBuffer({
size: 4 * 4 + 4,
size: 4 * 16 + 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
@ -62,7 +83,64 @@ export class MeshRenderer extends Behavior {
depthStoreOp: "store",
},
};
this.vertexBuffer = this.mesh.buffer(this.app);
}
onUpdate(time: number) {}
private writeUniforms(modelViewProjection: Mat4, time: number) {
if (!this.uniformBuffer) {
return;
}
const {
device: { queue },
} = this.app;
const mvpBuf = modelViewProjection as Float32Array;
queue.writeBuffer(
this.uniformBuffer,
0,
mvpBuf.buffer,
mvpBuf.byteOffset,
mvpBuf.length
);
const timeBuf = new Float32Array([time]);
queue.writeBuffer(
this.uniformBuffer,
mvpBuf.length + 1,
timeBuf.buffer,
timeBuf.byteOffset,
timeBuf.byteLength
);
}
onUpdate(time: number) {
if (
!this.renderPassDescriptor ||
!this.pipeline ||
!this.uniformBindGroup ||
!this.vertexBuffer
) {
return;
}
const mvp = mat4.multiply(this.projectionMatrix, this.viewMatrix);
this.writeUniforms(mvp, time);
const { device } = this.app;
const commandEncoder = device.createCommandEncoder();
const passEncoder = commandEncoder.beginRenderPass(
this.renderPassDescriptor
);
passEncoder.setPipeline(this.pipeline);
passEncoder.setBindGroup(0, this.uniformBindGroup);
passEncoder.setVertexBuffer(0, this.vertexBuffer);
passEncoder.draw(this.mesh.config.vertexCount);
passEncoder.end();
this.app.commit(commandEncoder.finish());
}
}

View file

@ -6,6 +6,7 @@ export type MeshConfig = {
positionSize: number;
colorSize: number;
uvSize: number;
vertexCount: number;
};
export class Mesh {

View file

@ -12,7 +12,9 @@ export class WebGPUApp {
private _adapter?: GPUAdapter;
private _device?: GPUDevice;
private _context?: GPUCanvasContext;
public telemetry: Telemetry;
public telemetry?: Telemetry;
private jobsToSubmitThisFrame: GPUCommandBuffer[] = [];
private renderOK = false;
public registry: {
onBeforeUpdate: RenderHandle[];
@ -34,7 +36,10 @@ export class WebGPUApp {
this.canvas = document.querySelector("canvas") as HTMLCanvasElement;
this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight;
this.telemetry = new Telemetry(this);
if (location.search.includes("telemetry")) {
this.telemetry = new Telemetry(this);
}
this.init().catch((e) => {
const main = document.querySelector("main");
@ -72,6 +77,23 @@ export class WebGPUApp {
alphaMode: "premultiplied",
...this.config.context,
});
this.renderOK = true;
}
awaitRendererReady(timeout: number = 5000) {
const start = Date.now();
return new Promise((resolve, reject) => {
const interval = setInterval(() => {
if (this.renderOK) {
return resolve(true);
}
if (Date.now() - start > timeout) {
return reject(`Renderer was not OK within ${timeout}ms`);
}
}, 10);
});
}
get context() {
@ -115,9 +137,15 @@ export class WebGPUApp {
}
doUpdate(time: number) {
this.jobsToSubmitThisFrame = [];
this.registry.onBeforeUpdate.forEach((handle) => handle(time, this));
this.registry.onUpdate.forEach((handle) => handle(time, this));
this.registry.onAfterUpdate.forEach((handle) => handle(time, this));
if (this.jobsToSubmitThisFrame.length !== 0) {
this.device.queue.submit(this.jobsToSubmitThisFrame);
}
}
doStart(time: number = 0) {
@ -125,11 +153,15 @@ export class WebGPUApp {
}
async oneShot(time: number = 0) {
await this.awaitRendererReady();
this.doStart(time);
this.doUpdate(time);
}
start() {
async start() {
await this.awaitRendererReady();
this.doStart();
const run = (time: number) => {
@ -138,4 +170,8 @@ export class WebGPUApp {
};
requestAnimationFrame(run);
}
commit(commandEncoder: GPUCommandBuffer) {
this.jobsToSubmitThisFrame.push(commandEncoder);
}
}