Spaces:
Running
Running
| import { createCompute } from "./gpu.js"; | |
| import shader from "./world.wgsl?raw"; | |
| export class World { | |
| constructor(canvas, gpu) { | |
| this.ctx = canvas.getContext("2d"); | |
| this.gpu = gpu; | |
| this.power = 8.0; | |
| this.time = 0; | |
| this.image = this.ctx.createImageData(256, 256); | |
| if (gpu.mode === "gpu") this.initGPU(); | |
| } | |
| async initGPU() { | |
| const d = this.gpu.device; | |
| this.pipeline = createCompute(d, shader); | |
| this.param = d.createBuffer({ size: 8, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST }); | |
| this.buf = d.createBuffer({ size: 256*256*4*4, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC }); | |
| this.read = d.createBuffer({ size: 256*256*4*4, usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.MAP_READ }); | |
| } | |
| update(dt) { | |
| this.time += dt; | |
| if (this.gpu.mode !== "gpu") return; | |
| const d = this.gpu.device; | |
| d.queue.writeBuffer(this.param, 0, new Float32Array([this.time, this.power])); | |
| const enc = d.createCommandEncoder(); | |
| const pass = enc.beginComputePass(); | |
| pass.setPipeline(this.pipeline); | |
| pass.setBindGroup(0, d.createBindGroup({ | |
| layout: this.pipeline.getBindGroupLayout(0), | |
| entries: [ | |
| { binding: 0, resource: { buffer: this.param } }, | |
| { binding: 1, resource: { buffer: this.buf } } | |
| ] | |
| })); | |
| pass.dispatchWorkgroups(32, 32); | |
| pass.end(); | |
| enc.copyBufferToBuffer(this.buf, 0, this.read, 0, 256*256*4*4); | |
| d.queue.submit([enc.finish()]); | |
| } | |
| async render() { | |
| if (this.gpu.mode !== "gpu") return; | |
| await this.read.mapAsync(GPUMapMode.READ); | |
| const f = new Float32Array(this.read.getMappedRange()); | |
| for (let i = 0; i < f.length; i++) { | |
| this.image.data[i] = Math.min(255, f[i] * 255); | |
| } | |
| this.read.unmap(); | |
| this.ctx.putImageData(this.image, 0, 0); | |
| } | |
| apply(cmd) { | |
| if (cmd.payload?.power) this.power = cmd.payload.power; | |
| } | |
| } |