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", "esbuild-plugin-glsl": "^1.2.2",
"glob": "^10.3.10", "glob": "^10.3.10",
"serve": "^14.2.1", "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 app = new WebGPUApp({ fov: 20 });
const renderer = new MeshRenderer(app, plane);
const shader = new Shader(rainbowPlane); const shader = new Shader(rainbowPlane);
const renderer = new MeshRenderer(app, plane, shader);
app.start(); app.start();

View file

@ -1,3 +1,4 @@
import { Mat4, mat4, vec3 } from "wgpu-matrix";
import { Behavior } from "./behavior"; import { Behavior } from "./behavior";
import { Mesh } from "./mesh"; import { Mesh } from "./mesh";
import { Shader } from "./shader"; import { Shader } from "./shader";
@ -6,29 +7,49 @@ import { WebGPUApp } from "./webgpu";
export class MeshRenderer extends Behavior { export class MeshRenderer extends Behavior {
private depthTexture?: GPUTexture; private depthTexture?: GPUTexture;
private uniformBuffer?: GPUBuffer; private uniformBuffer?: GPUBuffer;
private vertexBuffer?: GPUBuffer;
private texture?: GPUTexture; private texture?: GPUTexture;
private sampler?: GPUSampler; private sampler?: GPUSampler;
private uniformBindGroup?: GPUBindGroup; private uniformBindGroup?: GPUBindGroup;
private renderPassDescriptor?: GPURenderPassDescriptor; private renderPassDescriptor?: GPURenderPassDescriptor;
private pipeline?: GPURenderPipeline; 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( constructor(
public app: WebGPUApp, public app: WebGPUApp,
public mesh: Mesh, public mesh: Mesh,
public shader: Shader public shader: Shader,
public textures?: any[]
) { ) {
super(app); super(app);
} }
onStart() { 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({ this.depthTexture = this.app.device.createTexture({
size: [this.app.canvas.width, this.app.canvas.height], size: [this.app.canvas.width, this.app.canvas.height],
format: "depth24plus", format: "depth24plus",
usage: GPUTextureUsage.RENDER_ATTACHMENT, usage: GPUTextureUsage.RENDER_ATTACHMENT,
}); });
// float32x4x4 + float32
this.uniformBuffer = this.app.device.createBuffer({ this.uniformBuffer = this.app.device.createBuffer({
size: 4 * 4 + 4, size: 4 * 16 + 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
}); });
@ -62,7 +83,64 @@ export class MeshRenderer extends Behavior {
depthStoreOp: "store", 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; positionSize: number;
colorSize: number; colorSize: number;
uvSize: number; uvSize: number;
vertexCount: number;
}; };
export class Mesh { export class Mesh {

View file

@ -12,7 +12,9 @@ export class WebGPUApp {
private _adapter?: GPUAdapter; private _adapter?: GPUAdapter;
private _device?: GPUDevice; private _device?: GPUDevice;
private _context?: GPUCanvasContext; private _context?: GPUCanvasContext;
public telemetry: Telemetry; public telemetry?: Telemetry;
private jobsToSubmitThisFrame: GPUCommandBuffer[] = [];
private renderOK = false;
public registry: { public registry: {
onBeforeUpdate: RenderHandle[]; onBeforeUpdate: RenderHandle[];
@ -34,7 +36,10 @@ export class WebGPUApp {
this.canvas = document.querySelector("canvas") as HTMLCanvasElement; this.canvas = document.querySelector("canvas") as HTMLCanvasElement;
this.canvas.width = window.innerWidth; this.canvas.width = window.innerWidth;
this.canvas.height = window.innerHeight; this.canvas.height = window.innerHeight;
if (location.search.includes("telemetry")) {
this.telemetry = new Telemetry(this); this.telemetry = new Telemetry(this);
}
this.init().catch((e) => { this.init().catch((e) => {
const main = document.querySelector("main"); const main = document.querySelector("main");
@ -72,6 +77,23 @@ export class WebGPUApp {
alphaMode: "premultiplied", alphaMode: "premultiplied",
...this.config.context, ...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() { get context() {
@ -115,9 +137,15 @@ export class WebGPUApp {
} }
doUpdate(time: number) { doUpdate(time: number) {
this.jobsToSubmitThisFrame = [];
this.registry.onBeforeUpdate.forEach((handle) => handle(time, this)); this.registry.onBeforeUpdate.forEach((handle) => handle(time, this));
this.registry.onUpdate.forEach((handle) => handle(time, this)); this.registry.onUpdate.forEach((handle) => handle(time, this));
this.registry.onAfterUpdate.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) { doStart(time: number = 0) {
@ -125,11 +153,15 @@ export class WebGPUApp {
} }
async oneShot(time: number = 0) { async oneShot(time: number = 0) {
await this.awaitRendererReady();
this.doStart(time); this.doStart(time);
this.doUpdate(time); this.doUpdate(time);
} }
start() { async start() {
await this.awaitRendererReady();
this.doStart(); this.doStart();
const run = (time: number) => { const run = (time: number) => {
@ -138,4 +170,8 @@ export class WebGPUApp {
}; };
requestAnimationFrame(run); requestAnimationFrame(run);
} }
commit(commandEncoder: GPUCommandBuffer) {
this.jobsToSubmitThisFrame.push(commandEncoder);
}
} }