From c6fd528af85bc6d622cb812c1ede2d82a1e03fe2 Mon Sep 17 00:00:00 2001
From: Noratrieb <48135649+Noratrieb@users.noreply.github.com>
Date: Wed, 30 Oct 2024 21:30:30 +0100
Subject: [PATCH] mandelbrot
---
index.html | 15 ++++-
index.js | 152 ++++++++++++++++++++++++++++--------------------
mandelbrot.wgsl | 63 +++++++++++++++-----
3 files changed, 150 insertions(+), 80 deletions(-)
diff --git a/index.html b/index.html
index bd3a7b0..0d39125 100644
--- a/index.html
+++ b/index.html
@@ -18,7 +18,7 @@
-
You are using an unsupported browser.
+
You are using an unsupported browser or platform.
WebGPU is still experimental and does not have wide browser support.
@@ -31,14 +31,23 @@
the time of writing. Firefox may have one at the time of reading, in
which case I recommend it instead.
-
Also check out the Chrome Dev guide when using Chrome
+
+ Also check out the
+ Chrome Dev guide
+ when using Chrome
+
-
+
+
+
diff --git a/index.js b/index.js
index b8ff60c..31764a6 100644
--- a/index.js
+++ b/index.js
@@ -44,78 +44,104 @@ async function init() {
code: shader,
});
- document.getElementById("render-me").addEventListener("click", doStuff);
-}
+ // document.getElementById("render-me").addEventListener("click", doStuff);
-async function doStuff() {
- const output = device.createBuffer({
- size: 1000,
- usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,
- });
- const stagingBuffer = device.createBuffer({
- size: 1000,
- usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,
+ const canvas = document.getElementById("result");
+ const context = canvas.getContext("webgpu");
+ context.configure({
+ device: device,
+ format: navigator.gpu.getPreferredCanvasFormat(),
+ alphaMode: "premultiplied",
});
+ const vertices = new Float32Array([
+ // Triangle 1 (Blue)
+ -1,
+ -1,
+ 1,
+ -1,
+ 1,
+ 1,
- const bindGroupLayout = device.createBindGroupLayout({
- entries: [
- {
- binding: 0,
- visibility: GPUShaderStage.COMPUTE,
- buffer: {
- type: "storage",
+ -1, // Triangle 2 (Red)
+ -1,
+ 1,
+ 1,
+ -1,
+ 1,
+ ]);
+
+ const vertexBuffer = device.createBuffer({
+ label: "Vertices",
+ size: vertices.byteLength, // make it big enough to store vertices in
+ usage: GPUBufferUsage.VERTEX | GPUBufferUsage.COPY_DST,
+ });
+ device.queue.writeBuffer(vertexBuffer, 0, vertices);
+
+ const GRID_SIZE = 4;
+ const uniformArray = new Float32Array([GRID_SIZE, GRID_SIZE]);
+ const uniformBuffer = device.createBuffer({
+ label: "Grid uniforms",
+ size: uniformArray.byteLength,
+ usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
+ });
+ device.queue.writeBuffer(uniformBuffer, 0, uniformArray);
+
+ const vertexBuffers = [
+ {
+ attributes: [
+ {
+ shaderLocation: 0,
+ offset: 0,
+ format: "float32x2",
},
- },
- ],
- });
-
- const bindGroup = device.createBindGroup({
- layout: bindGroupLayout,
- entries: [
- {
- binding: 0,
- resource: {
- buffer: output,
- },
- },
- ],
- });
-
- const computePipeline = device.createComputePipeline({
- layout: device.createPipelineLayout({
- bindGroupLayouts: [bindGroupLayout],
- }),
- compute: {
- module: shaderModule,
- entryPoint: "main",
+ ],
+ arrayStride: 8,
+ stepMode: "vertex",
},
- });
-
+ ];
+ const pipelineDescriptor = {
+ label: "Render Pipeline",
+ vertex: {
+ module: shaderModule,
+ entryPoint: "vertex_main",
+ buffers: vertexBuffers,
+ },
+ fragment: {
+ module: shaderModule,
+ entryPoint: "fragment_main",
+ targets: [
+ {
+ format: navigator.gpu.getPreferredCanvasFormat(),
+ },
+ ],
+ },
+ primitive: {
+ topology: "triangle-list",
+ },
+ layout: "auto",
+ };
+ const renderPipeline = device.createRenderPipeline(pipelineDescriptor);
const commandEncoder = device.createCommandEncoder();
- const passEncoder = commandEncoder.beginComputePass();
- passEncoder.setPipeline(computePipeline);
- passEncoder.setBindGroup(0, bindGroup);
- passEncoder.dispatchWorkgroups(Math.ceil(1000 / 64));
+ const clearColor = { r: 0.0, g: 0.5, b: 1.0, a: 1.0 };
+
+ const renderPassDescriptor = {
+ colorAttachments: [
+ {
+ clearValue: clearColor,
+ loadOp: "clear",
+ storeOp: "store",
+ view: context.getCurrentTexture().createView(),
+ },
+ ],
+ };
+
+ const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
+ passEncoder.setPipeline(renderPipeline);
+ passEncoder.setVertexBuffer(0, vertexBuffer);
+ passEncoder.draw(vertices.length / 2);
passEncoder.end();
- commandEncoder.copyBufferToBuffer(
- output,
- 0, // source offset
- stagingBuffer,
- 0, // destination offest
- 1000
- );
device.queue.submit([commandEncoder.finish()]);
-
- await stagingBuffer.mapAsync(
- GPUMapMode.READ,
- 0, // offset
- 1000 // length
- );
- const copyArrayBuffer = stagingBuffer.getMappedRange(0, 1000);
- const data = copyArrayBuffer.slice();
- stagingBuffer.unmap();
- console.log(new Float32Array(data));
}
init();
diff --git a/mandelbrot.wgsl b/mandelbrot.wgsl
index 6d53156..f16ace2 100644
--- a/mandelbrot.wgsl
+++ b/mandelbrot.wgsl
@@ -1,19 +1,54 @@
-@group(0) @binding(0)
-var output: array;
+const CANVAS: vec2f = vec2f(1200, 800);
-@compute @workgroup_size(64)
-fn main(
- @builtin(global_invocation_id)
- global_id : vec3u,
+@vertex
+fn vertex_main(@location(0) pos: vec2f) ->
+ @builtin(position) vec4f {
+ return vec4f(pos, 0, 1);
+}
- @builtin(local_invocation_id)
- local_id : vec3u,
-) {
- // Avoid accessing the buffer out of bounds
- if (global_id.x >= 1000) {
- return;
+@fragment
+fn fragment_main(
+ @builtin(position) pos: vec4f
+) -> @location(0) vec4f {
+ let step_size = vec2f(3.0, 2.0) / CANVAS;
+ let center = vec2f(-0.75, 0.0);
+ let offset = vec2f(
+ center.x - CANVAS.x / 2 * step_size.x,
+ -(center.y - CANVAS.y / 2.0 * step_size.y) - 2.0,
+ );
+ let sample_pos = offset + (step_size * vec2f(pos.x, pos.y));
+
+ let value = mandelbrot_pixel(sample_pos);
+
+ let multiplier = 1.0 - (value * value) / f32(ITERATIONS);
+ let i = 255.0 * multiplier;
+
+ return vec4f(i, i, i, 1);
+}
+
+const ITERATIONS = 50000;
+const THRESHOLD: f32 = 100;
+
+fn mandelbrot_pixel(position: vec2f) -> f32 {
+ var n = vec2f(0, 0);
+ let c = position;
+ n = n + c;
+
+ var passed_threshold_after: i32 = ITERATIONS;
+
+ for (var i = 0; i < ITERATIONS; i += 1) {
+ n = cmul(n, n) + c;
+ if n.x > THRESHOLD || n.y > THRESHOLD && passed_threshold_after == ITERATIONS {
+ passed_threshold_after = i;
+ }
}
- output[global_id.x] =
- f32(local_id.x);
+ return f32(passed_threshold_after);
}
+
+fn cmul(a: vec2f, b: vec2f) -> vec2f {
+ return vec2f(
+ a.x * b.x - a.y * b.y,
+ a.x * b.y + a.y * b.x,
+ );
+}
\ No newline at end of file