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