webgpu-mandelbrot/index.js
2024-10-30 21:30:30 +01:00

147 lines
3.5 KiB
JavaScript

function error(msg) {
const elem = document.getElementById("error");
elem.innerText = msg;
elem.classList.remove("hidden");
}
let device;
let shaderModule;
async function init() {
const unsupportedBrowser = document.getElementById("unsupported-browser");
if (navigator.gpu) {
unsupportedBrowser.classList.add("hidden");
} else {
return;
}
const adapter = await navigator.gpu.requestAdapter();
if (!adapter) {
error(
"failed to get adapter from navigator.gpu. it looks like your environment does not have a GPU or is not supported."
);
unsupportedBrowser.classList.remove("hidden");
return;
}
device = await adapter.requestDevice();
if (!device) {
error(
"failed to get device from WebGPU adapter. it looks like your environment does not have a GPU or is not supported."
);
unsupportedBrowser.classList.remove("hidden");
return;
}
const shaderResp = await fetch("mandelbrot.wgsl");
if (!shaderResp.ok) {
error("failed to load shader");
return;
}
const shader = await shaderResp.text();
shaderModule = device.createShaderModule({
code: shader,
});
// document.getElementById("render-me").addEventListener("click", doStuff);
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,
-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",
},
],
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 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();
device.queue.submit([commandEncoder.finish()]);
}
init();