mirror of
https://github.com/Floriansylvain/WhateverWebGPU.git
synced 2026-08-19 11:43:26 +02:00
feat: add FPS display & grid state management
This commit is contained in:
@@ -8,6 +8,7 @@
|
|||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<canvas id="canvas" width="512" height="512"></canvas>
|
<canvas id="canvas" width="512" height="512"></canvas>
|
||||||
|
<p id="fps">FPS:</p>
|
||||||
<script type="module" src="/src/main.ts"></script>
|
<script type="module" src="/src/main.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
+72
-10
@@ -83,20 +83,52 @@ function createTimeBuffer(device: GPUDevice): GPUBuffer {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function createBindGroup(
|
function createStateStorageBuffers(device: GPUDevice): GPUBuffer[] {
|
||||||
|
const cellStateArray = new Uint32Array(GRID_SIZE * GRID_SIZE)
|
||||||
|
const size = cellStateArray.byteLength
|
||||||
|
const usage = GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST
|
||||||
|
const cellStateStorage = [
|
||||||
|
device.createBuffer({ label: "Cell State A", size, usage }),
|
||||||
|
device.createBuffer({ label: "Cell State B", size, usage }),
|
||||||
|
]
|
||||||
|
for (let i = 0; i < cellStateArray.length; i += 3) {
|
||||||
|
cellStateArray[i] = 1
|
||||||
|
}
|
||||||
|
device.queue.writeBuffer(cellStateStorage[0], 0, cellStateArray)
|
||||||
|
for (let i = 0; i < cellStateArray.length; i++) {
|
||||||
|
cellStateArray[i] = i % 2
|
||||||
|
}
|
||||||
|
device.queue.writeBuffer(cellStateStorage[1], 0, cellStateArray)
|
||||||
|
return cellStateStorage
|
||||||
|
}
|
||||||
|
|
||||||
|
function createBindGroups(
|
||||||
device: GPUDevice,
|
device: GPUDevice,
|
||||||
cellPipeline: GPURenderPipeline,
|
cellPipeline: GPURenderPipeline,
|
||||||
gridUniformBuffer: GPUBuffer,
|
gridUniformBuffer: GPUBuffer,
|
||||||
timeBuffer: GPUBuffer,
|
timeBuffer: GPUBuffer,
|
||||||
): GPUBindGroup {
|
cellStateStorage: GPUBuffer[],
|
||||||
return device.createBindGroup({
|
): GPUBindGroup[] {
|
||||||
label: "Cell renderer bind group",
|
return [
|
||||||
|
device.createBindGroup({
|
||||||
|
label: "Cell renderer bind group A",
|
||||||
layout: cellPipeline.getBindGroupLayout(0),
|
layout: cellPipeline.getBindGroupLayout(0),
|
||||||
entries: [
|
entries: [
|
||||||
{ binding: 0, resource: { buffer: gridUniformBuffer } },
|
{ binding: 0, resource: { buffer: gridUniformBuffer } },
|
||||||
{ binding: 1, resource: { buffer: timeBuffer } },
|
{ binding: 1, resource: { buffer: timeBuffer } },
|
||||||
|
{ binding: 2, resource: { buffer: cellStateStorage[0] } },
|
||||||
],
|
],
|
||||||
})
|
}),
|
||||||
|
device.createBindGroup({
|
||||||
|
label: "Cell updater bind group B",
|
||||||
|
layout: cellPipeline.getBindGroupLayout(0),
|
||||||
|
entries: [
|
||||||
|
{ binding: 0, resource: { buffer: gridUniformBuffer } },
|
||||||
|
{ binding: 1, resource: { buffer: timeBuffer } },
|
||||||
|
{ binding: 2, resource: { buffer: cellStateStorage[1] } },
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
function createVertexBuffer(device: GPUDevice): GPUBuffer {
|
function createVertexBuffer(device: GPUDevice): GPUBuffer {
|
||||||
@@ -113,18 +145,44 @@ function createVertexBuffer(device: GPUDevice): GPUBuffer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class Renderer {
|
class Renderer {
|
||||||
|
private lastFrameTime: number = 0
|
||||||
|
private frameCount: number = 0
|
||||||
|
private fps: number = 0
|
||||||
|
private bindGroupIndex: number = 0
|
||||||
|
private lastBindGroupSwitchTime: number = 0
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
private device: GPUDevice,
|
private device: GPUDevice,
|
||||||
private context: GPUCanvasContext,
|
private context: GPUCanvasContext,
|
||||||
private cellPipeline: GPURenderPipeline,
|
private cellPipeline: GPURenderPipeline,
|
||||||
private vertexBuffer: GPUBuffer,
|
private vertexBuffer: GPUBuffer,
|
||||||
private bindGroup: GPUBindGroup,
|
private bindGroups: GPUBindGroup[],
|
||||||
private timeBuffer: GPUBuffer,
|
private timeBuffer: GPUBuffer,
|
||||||
private vertices: Float32Array,
|
private vertices: Float32Array,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public render(timeMs: number): void {
|
private updateFpsCount(timeMs: number): void {
|
||||||
|
if (this.lastFrameTime === 0) this.lastFrameTime = timeMs
|
||||||
|
this.frameCount++
|
||||||
|
if (timeMs - this.lastFrameTime >= 1000) {
|
||||||
|
this.fps = this.frameCount
|
||||||
|
this.frameCount = 0
|
||||||
|
this.lastFrameTime = timeMs
|
||||||
|
document.querySelector("#fps")!.textContent = `FPS: ${this.fps}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateBindGroupIndex(timeMs: number): void {
|
||||||
|
if (timeMs - this.lastBindGroupSwitchTime >= 500) {
|
||||||
|
this.bindGroupIndex = (this.bindGroupIndex + 1) % this.bindGroups.length
|
||||||
|
this.lastBindGroupSwitchTime = timeMs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async render(timeMs: number): Promise<void> {
|
||||||
const time = timeMs / 1000
|
const time = timeMs / 1000
|
||||||
|
this.updateFpsCount(timeMs)
|
||||||
|
this.updateBindGroupIndex(timeMs)
|
||||||
this.device.queue.writeBuffer(this.timeBuffer, 0, new Float32Array([time]))
|
this.device.queue.writeBuffer(this.timeBuffer, 0, new Float32Array([time]))
|
||||||
|
|
||||||
const encoder = this.device.createCommandEncoder()
|
const encoder = this.device.createCommandEncoder()
|
||||||
@@ -141,7 +199,9 @@ class Renderer {
|
|||||||
|
|
||||||
pass.setPipeline(this.cellPipeline)
|
pass.setPipeline(this.cellPipeline)
|
||||||
pass.setVertexBuffer(0, this.vertexBuffer)
|
pass.setVertexBuffer(0, this.vertexBuffer)
|
||||||
pass.setBindGroup(0, this.bindGroup)
|
|
||||||
|
pass.setBindGroup(0, this.bindGroups[this.bindGroupIndex])
|
||||||
|
|
||||||
pass.draw(this.vertices.length / 2, GRID_SIZE * GRID_SIZE)
|
pass.draw(this.vertices.length / 2, GRID_SIZE * GRID_SIZE)
|
||||||
pass.end()
|
pass.end()
|
||||||
|
|
||||||
@@ -158,11 +218,13 @@ async function init() {
|
|||||||
const cellPipeline = await createCellPipeline(device, canvasFormat)
|
const cellPipeline = await createCellPipeline(device, canvasFormat)
|
||||||
const gridUniformBuffer = createGridUniformBuffer(device)
|
const gridUniformBuffer = createGridUniformBuffer(device)
|
||||||
const timeBuffer = createTimeBuffer(device)
|
const timeBuffer = createTimeBuffer(device)
|
||||||
const bindGroup = createBindGroup(
|
const cellStateStorage = createStateStorageBuffers(device)
|
||||||
|
const bindGroups = createBindGroups(
|
||||||
device,
|
device,
|
||||||
cellPipeline,
|
cellPipeline,
|
||||||
gridUniformBuffer,
|
gridUniformBuffer,
|
||||||
timeBuffer,
|
timeBuffer,
|
||||||
|
cellStateStorage,
|
||||||
)
|
)
|
||||||
const vertexBuffer = createVertexBuffer(device)
|
const vertexBuffer = createVertexBuffer(device)
|
||||||
const vertices = new Float32Array([
|
const vertices = new Float32Array([
|
||||||
@@ -174,7 +236,7 @@ async function init() {
|
|||||||
context,
|
context,
|
||||||
cellPipeline,
|
cellPipeline,
|
||||||
vertexBuffer,
|
vertexBuffer,
|
||||||
bindGroup,
|
bindGroups,
|
||||||
timeBuffer,
|
timeBuffer,
|
||||||
vertices,
|
vertices,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
@group(0) @binding(0) var<uniform> grid: vec2f;
|
@group(0) @binding(0) var<uniform> grid: vec2f;
|
||||||
@group(0) @binding(1) var<uniform> time: f32;
|
@group(0) @binding(1) var<uniform> time: f32;
|
||||||
|
@group(0) @binding(2) var<storage> cellState: array<u32>;
|
||||||
|
|
||||||
const TAU: f32 = 6.28318530718;
|
const TAU: f32 = 6.28318530718;
|
||||||
|
|
||||||
@@ -17,8 +18,10 @@ struct VertexOutput {
|
|||||||
fn vertexMain(input: VertexInput) -> VertexOutput {
|
fn vertexMain(input: VertexInput) -> VertexOutput {
|
||||||
let i = f32(input.instance);
|
let i = f32(input.instance);
|
||||||
let cell = vec2f(i % grid.x, floor(i / grid.x));
|
let cell = vec2f(i % grid.x, floor(i / grid.x));
|
||||||
|
let state = f32(cellState[input.instance]);
|
||||||
|
|
||||||
let cellOffset = cell / grid * 2.0;
|
let cellOffset = cell / grid * 2.0;
|
||||||
let gridPos = (input.pos + 1.0) / grid - 1.0 + cellOffset;
|
let gridPos = (input.pos * state + 1.0) / grid - 1.0 + cellOffset;
|
||||||
|
|
||||||
var output: VertexOutput;
|
var output: VertexOutput;
|
||||||
output.pos = vec4f(gridPos, 0.0, 1.0);
|
output.pos = vec4f(gridPos, 0.0, 1.0);
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
body {
|
body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
|
||||||
background-color: #222;
|
background-color: #222;
|
||||||
|
|
||||||
|
color: #fff;
|
||||||
|
font-family: "Courier New", Courier, monospace;
|
||||||
}
|
}
|
||||||
|
|
||||||
canvas {
|
canvas {
|
||||||
|
|||||||
Reference in New Issue
Block a user