renderer might actually render something next commit
This commit is contained in:
parent
627adcc07f
commit
000f35f19d
7 changed files with 126 additions and 9 deletions
BIN
bun.lockb
BIN
bun.lockb
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ export type MeshConfig = {
|
|||
positionSize: number;
|
||||
colorSize: number;
|
||||
uvSize: number;
|
||||
vertexCount: number;
|
||||
};
|
||||
|
||||
export class Mesh {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue