@@ -20,6 +20,7 @@ export const meta: RouteMeta = {
2020
2121export 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}
0 commit comments