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",
|
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue