Skip to content

Commit 64b1172

Browse files
committed
randomize button
1 parent 3300755 commit 64b1172

File tree

2 files changed

+55
-23
lines changed

2 files changed

+55
-23
lines changed

src/pages/particle-life-gpu/index.tsx

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ export const meta: RouteMeta = {
2020

2121
export default function ParticleLifeGPUPage() {
2222
const canvasRef = useRef<HTMLCanvasElement>(null)
23+
const randomizeRef = useRef<HTMLButtonElement>(null)
2324
const [supported] = useState(() => Boolean(navigator.gpu))
2425
const [fps, setFps] = useState(0)
2526

@@ -31,33 +32,53 @@ export default function ParticleLifeGPUPage() {
3132
canvas.width = window.innerWidth * devicePixelRatio
3233
canvas.height = window.innerHeight * devicePixelRatio
3334

35+
const randomizeButton = randomizeRef.current!
36+
3437
const frameCounter = makeFrameCounter(60)
35-
start(controller, canvas, (dt) => setFps(Math.round(frameCounter(dt / 1000))))
38+
start({
39+
controller,
40+
canvas,
41+
onFrame: (dt) => setFps(Math.round(frameCounter(dt / 1000))),
42+
controls: {
43+
randomize: randomizeButton,
44+
}
45+
})
3646

3747
return () => {
3848
controller.abort()
3949
}
4050
}, [supported])
41-
51+
4252
const [numberFormat] = useState(() => new Intl.NumberFormat('en-US'))
4353

4454
return (
4555
<div className={styles.main}>
4656
<div className={styles.head}>
4757
<Head />
48-
{supported && <pre>{numberFormat.format(particleCount)} particles, {fps} fps</pre>}
4958
{!supported && <pre>Your browser does not support WebGPU.</pre>}
59+
{supported && <>
60+
<pre>{numberFormat.format(particleCount)} particles, {fps} fps</pre>
61+
<button ref={randomizeRef} type="button" name="randomize">🎲</button>
62+
</>}
5063
</div>
5164
<canvas ref={canvasRef} />
5265
</div>
5366
)
5467
}
5568

56-
async function start(
69+
async function start({
70+
controller,
71+
canvas,
72+
onFrame,
73+
controls,
74+
}: {
5775
controller: AbortController,
5876
canvas: HTMLCanvasElement,
5977
onFrame: (dt: number) => void,
60-
) {
78+
controls: {
79+
randomize: HTMLButtonElement,
80+
},
81+
}) {
6182

6283
const onAbort = (cb: () => void) => {
6384
if (controller.signal.aborted) return
@@ -102,7 +123,7 @@ async function start(
102123
const particlePositionBuffer = device.createBuffer({
103124
label: 'particle position storage buffer',
104125
size: particleCount * 2 * Float32Array.BYTES_PER_ELEMENT,
105-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
126+
usage: GPUBufferUsage.STORAGE,
106127
mappedAtCreation: true,
107128
})
108129
{
@@ -117,7 +138,7 @@ async function start(
117138
const particleColorBuffer = device.createBuffer({
118139
label: 'particle color storage buffer',
119140
size: particleCount * 1 * Uint32Array.BYTES_PER_ELEMENT,
120-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
141+
usage: GPUBufferUsage.STORAGE,
121142
mappedAtCreation: true,
122143
})
123144
{
@@ -131,7 +152,7 @@ async function start(
131152
const particleVelocityBuffer = device.createBuffer({
132153
label: 'particle velocity storage buffer',
133154
size: particleCount * 2 * Float32Array.BYTES_PER_ELEMENT,
134-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
155+
usage: GPUBufferUsage.STORAGE,
135156
mappedAtCreation: true,
136157
})
137158
{
@@ -143,49 +164,55 @@ async function start(
143164
particleVelocityBuffer.unmap()
144165
onAbort(() => particleVelocityBuffer.destroy())
145166
}
167+
function makeInteractionMatrix(buffer = new ArrayBuffer(numberOfColors * numberOfColors * Float32Array.BYTES_PER_ELEMENT)) {
168+
const staticStorageArray = new Float32Array(buffer)
169+
for (let i = 0; i < numberOfColors; i++) {
170+
for (let j = 0; j < numberOfColors; j++) {
171+
staticStorageArray[i * numberOfColors + j] = Math.random() * 2 - 1
172+
// staticStorageArray[i * numberOfColors + j] = i === j ? 1 : 0
173+
}
174+
}
175+
176+
return buffer
177+
}
146178
const particleInteractionsBuffer = device.createBuffer({
147179
label: 'particle interactions storage buffer',
148180
size: numberOfColors * numberOfColors * Float32Array.BYTES_PER_ELEMENT,
149181
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,
150182
mappedAtCreation: true,
151183
})
152184
{
153-
const staticStorageArray = new Float32Array(particleInteractionsBuffer.getMappedRange())
154-
for (let i = 0; i < numberOfColors; i++) {
155-
for (let j = 0; j < numberOfColors; j++) {
156-
staticStorageArray[i * numberOfColors + j] = Math.random() * 2 - 1
157-
// staticStorageArray[i * numberOfColors + j] = i === j ? 1 : 0
158-
}
159-
}
185+
const buffer = particleInteractionsBuffer.getMappedRange()
186+
makeInteractionMatrix(buffer)
160187
particleInteractionsBuffer.unmap()
161188
onAbort(() => particleInteractionsBuffer.destroy())
162189
}
163190

164191
const binSizeBuffer = device.createBuffer({
165192
label: 'bin size storage buffer',
166193
size: binCount * Uint32Array.BYTES_PER_ELEMENT,
167-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
194+
usage: GPUBufferUsage.STORAGE,
168195
})
169196
onAbort(() => binSizeBuffer.destroy())
170197

171198
const binOffsetBuffer = device.createBuffer({
172199
label: 'bin offset storage buffer',
173200
size: binCount * Uint32Array.BYTES_PER_ELEMENT,
174-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
201+
usage: GPUBufferUsage.STORAGE,
175202
})
176203
onAbort(() => binOffsetBuffer.destroy())
177204

178205
const binCursorBuffer = device.createBuffer({
179206
label: 'bin cursor storage buffer',
180207
size: binCount * Uint32Array.BYTES_PER_ELEMENT,
181-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
208+
usage: GPUBufferUsage.STORAGE,
182209
})
183210
onAbort(() => binCursorBuffer.destroy())
184211

185212
const binContentsBuffer = device.createBuffer({
186213
label: 'bin contents storage buffer',
187214
size: particleCount * Uint32Array.BYTES_PER_ELEMENT,
188-
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST | GPUBufferUsage.COPY_SRC,
215+
usage: GPUBufferUsage.STORAGE,
189216
})
190217
onAbort(() => binContentsBuffer.destroy())
191218

@@ -547,7 +574,7 @@ async function start(
547574
const drawConfigBuffer = device.createBuffer({
548575
label: 'draw config uniform buffer',
549576
size: 2 * 4,
550-
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
577+
usage: GPUBufferUsage.UNIFORM,
551578
mappedAtCreation: true,
552579
})
553580
{
@@ -649,7 +676,7 @@ async function start(
649676
lastTime = time
650677
frameCount++
651678
onFrame(dt)
652-
if (frameCount > 10) {
679+
if (frameCount > 4) {
653680
computeBins()
654681
frameCount = 0
655682
}
@@ -666,4 +693,9 @@ async function start(
666693
playing = false
667694
}
668695
}, { signal: controller.signal })
696+
697+
controls.randomize.addEventListener('click', () => {
698+
const buffer = makeInteractionMatrix()
699+
device.queue.writeBuffer(particleInteractionsBuffer, 0, buffer)
700+
}, { signal: controller.signal })
669701
}

src/router.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,11 +232,11 @@ export const ROUTES = {
232232
Component: lazy(() => import("./pages/particle-life-gpu/index.tsx")),
233233
meta: {
234234
title: 'Particle Life GPU',
235-
tags: ['simulation', 'webgpu', 'particles', 'wip'],
235+
tags: ['simulation', 'webgpu', 'particles'],
236236
image: particle_life_gpu_image
237237
},
238238
git: {
239-
lastModified: 1762621979000,
239+
lastModified: 1762625075000,
240240
firstAdded: 1762551884000
241241
},
242242
},

0 commit comments

Comments
 (0)