even more webgpu stuff
This commit is contained in:
parent
7da1fa8366
commit
96e992467f
14 changed files with 8421 additions and 30 deletions
|
@ -1,9 +1,14 @@
|
|||
import { WebGPUApp } from "../renderer/webgpu";
|
||||
import Torus from "../meshes/torus";
|
||||
import { MeshRenderer } from "../renderer/mesh-renderer";
|
||||
|
||||
const app = new WebGPUApp({ fov: 20 });
|
||||
|
||||
app.start();
|
||||
// TODO:
|
||||
// - plane
|
||||
// - torus!
|
||||
// - white shader
|
||||
// - real shader with UVs and uniforms
|
||||
|
||||
const torusRenderer = new MeshRenderer(app, Torus);
|
||||
|
||||
app.start();
|
||||
|
|
|
@ -11,7 +11,7 @@ struct v2f {
|
|||
}
|
||||
|
||||
@vertex
|
||||
fn vertex_main(
|
||||
fn main(
|
||||
@builtin(position) position : vec4f,
|
||||
@location(0) uv : vec2f,
|
||||
) -> v2f {
|
||||
|
@ -19,7 +19,7 @@ fn vertex_main(
|
|||
}
|
||||
|
||||
@fragment
|
||||
fn fragment_main(
|
||||
fn main(
|
||||
@location(0) uv : vec2f,
|
||||
) -> @location(0) vec4f {
|
||||
f32 zComponent = sin(uniforms.time) * 0.001 * 0.5 + 0.5;
|
||||
|
|
1805
src/meshes/torus.ply
Normal file
1805
src/meshes/torus.ply
Normal file
File diff suppressed because it is too large
Load diff
6383
src/meshes/torus.ts
Normal file
6383
src/meshes/torus.ts
Normal file
File diff suppressed because it is too large
Load diff
15
src/renderer/behavior.ts
Normal file
15
src/renderer/behavior.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { WebGPUApp } from "./webgpu";
|
||||
|
||||
export abstract class Behavior {
|
||||
constructor(public app: WebGPUApp) {
|
||||
this.onBeforeUpdate && app.onBeforeUpdate(this.onBeforeUpdate.bind(this));
|
||||
this.onUpdate && app.onUpdate(this.onUpdate.bind(this));
|
||||
this.onAfterUpdate && app.onAfterUpdate(this.onAfterUpdate.bind(this));
|
||||
this.onStart && app.onStart(this.onStart.bind(this));
|
||||
}
|
||||
|
||||
onStart(time: number) {}
|
||||
onBeforeUpdate(time: number) {}
|
||||
onUpdate(time: number) {}
|
||||
onAfterUpdate(time: number) {}
|
||||
}
|
24
src/renderer/mesh-renderer.ts
Normal file
24
src/renderer/mesh-renderer.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { Behavior } from "./behavior";
|
||||
import { Mesh } from "./mesh";
|
||||
import { WebGPUApp } from "./webgpu";
|
||||
|
||||
export class MeshRenderer extends Behavior {
|
||||
private depthTexture?: GPUTexture;
|
||||
private uniformBuffer?: GPUBuffer;
|
||||
private texture?: GPUTexture;
|
||||
private sampler?: GPUSampler;
|
||||
private uniformBindGroup?: GPUBindGroup;
|
||||
private renderPassDescriptor?: GPURenderBundleDescriptor;
|
||||
|
||||
constructor(
|
||||
public app: WebGPUApp,
|
||||
public mesh: Mesh
|
||||
) {
|
||||
super(app);
|
||||
}
|
||||
onStart() {
|
||||
console.log("hello from meshrenderer!");
|
||||
console.log(`i've got a ${this.mesh.constructor.name}`);
|
||||
}
|
||||
onUpdate(time: number) {}
|
||||
}
|
|
@ -1,16 +1,86 @@
|
|||
import { Oops, Shader } from "./shader";
|
||||
import { WebGPUApp } from "./webgpu";
|
||||
|
||||
export type MeshConfig = {
|
||||
vertexData: Float32Array;
|
||||
vertexSize: number;
|
||||
positionOffset: number;
|
||||
colorOffset: number;
|
||||
uvOffset: number;
|
||||
mesh: Float32Array;
|
||||
positionSize: number;
|
||||
colorSize: number;
|
||||
uvSize: number;
|
||||
};
|
||||
|
||||
export class Mesh {
|
||||
private shader: Shader = Oops;
|
||||
private _shader: Shader = Oops;
|
||||
|
||||
constructor(public config: MeshConfig) {}
|
||||
shader(shader: Shader) {}
|
||||
|
||||
shader(shader: Shader) {
|
||||
this._shader = shader;
|
||||
return this;
|
||||
}
|
||||
|
||||
buffer(app: WebGPUApp) {
|
||||
const buffer = app.device.createBuffer({
|
||||
size: this.config.mesh.byteLength,
|
||||
usage: GPUBufferUsage.VERTEX,
|
||||
mappedAtCreation: true,
|
||||
});
|
||||
new Float32Array(buffer.getMappedRange()).set(this.config.mesh);
|
||||
buffer.unmap();
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
pipeline(app: WebGPUApp, config: Record<string, any>) {
|
||||
const module = this._shader.module(app);
|
||||
return app.device.createRenderPipeline({
|
||||
layout: "auto",
|
||||
vertex: {
|
||||
module,
|
||||
entryPoint: "main",
|
||||
buffers: [
|
||||
{
|
||||
arrayStride: 4,
|
||||
attributes: [
|
||||
{
|
||||
// position
|
||||
shaderLocation: 0,
|
||||
offset: 0,
|
||||
format: "float32x4",
|
||||
},
|
||||
{
|
||||
// color
|
||||
shaderLocation: 2,
|
||||
offset: this.config.positionSize,
|
||||
format: "float32x4",
|
||||
},
|
||||
{
|
||||
// uv
|
||||
shaderLocation: 1,
|
||||
offset: this.config.positionSize + this.config.colorSize,
|
||||
format: "float32x4",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
fragment: {
|
||||
module,
|
||||
entryPoint: "main",
|
||||
targets: [
|
||||
{
|
||||
format: "rgba8unorm",
|
||||
},
|
||||
],
|
||||
},
|
||||
primitive: {
|
||||
topology: "triangle-list",
|
||||
cullMode: config.cullMode ?? "back",
|
||||
},
|
||||
depthStencil: config.stencil && {
|
||||
depthWriteEnabled: true,
|
||||
depthCompare: "less",
|
||||
format: "depth24plus",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Behavior } from "./behavior";
|
||||
import { WebGPUApp } from "./webgpu";
|
||||
|
||||
export class Telemetry {
|
||||
export class Telemetry extends Behavior {
|
||||
public el: HTMLElement;
|
||||
public frameTimes: number[] = [];
|
||||
public maxFrameTimes: number = 100;
|
||||
|
@ -9,11 +10,10 @@ export class Telemetry {
|
|||
public app: WebGPUApp,
|
||||
selector = "#telemetry"
|
||||
) {
|
||||
super(app);
|
||||
this.el = document.querySelector(selector) as HTMLElement;
|
||||
if (this.el && location.search.includes("telemetry")) {
|
||||
this.el.style.display = "block";
|
||||
this.app.onAfterUpdate((time) => this.onAfterUpdate(time));
|
||||
this.app.onStart(() => this.onStart());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,18 +28,14 @@ export class Telemetry {
|
|||
onStart() {
|
||||
this.lastFrameTime = 0;
|
||||
this.frameTimes = [];
|
||||
}
|
||||
|
||||
onAfterUpdate(time: number) {
|
||||
const frameTime = time - this.lastFrameTime;
|
||||
this.insertTime(frameTime);
|
||||
setInterval(() => {
|
||||
const averageFrameTime =
|
||||
this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;
|
||||
|
||||
const averageFrameTime =
|
||||
this.frameTimes.reduce((a, b) => a + b, 0) / this.frameTimes.length;
|
||||
const framesPerSecond = 1000 / averageFrameTime;
|
||||
|
||||
const framesPerSecond = 1000 / averageFrameTime;
|
||||
|
||||
this.el.innerHTML = `
|
||||
this.el.innerHTML = `
|
||||
${framesPerSecond.toFixed(1)} FPS (${averageFrameTime.toFixed(
|
||||
3
|
||||
)} ms)<br />
|
||||
|
@ -47,7 +43,12 @@ export class Telemetry {
|
|||
this.app.registry.onUpdate.length
|
||||
} | aU: ${this.app.registry.onAfterUpdate.length}
|
||||
`;
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
onAfterUpdate(time: number) {
|
||||
const frameTime = time - this.lastFrameTime;
|
||||
this.insertTime(frameTime);
|
||||
this.lastFrameTime = time;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue