wgpu/main.js
2024-05-31 15:06:20 +12:00

136 lines
3.6 KiB
JavaScript

const canvas = document.querySelector("#canvas");
const context = canvas.getContext("webgpu");
const frameTime = document.querySelector("#frame_time");
const adapter = await navigator.gpu.requestAdapter();
const adapterInfo = await adapter.requestAdapterInfo();
const device = await adapter.requestDevice();
const devicePixelRatio = window.devicePixelRatio;
canvas.width = canvas.clientWidth * devicePixelRatio;
canvas.height = canvas.clientHeight * devicePixelRatio;
const presentationFormat = navigator.gpu.getPreferredCanvasFormat();
context.configure({
device,
format: presentationFormat,
alphaMode: 'premultiplied',
});
const bindGroupLayout = device.createBindGroupLayout({
entries: [
{
binding: 0,
visibility: GPUShaderStage.FRAGMENT,
buffer: {},
},
{
binding: 1,
visibility: GPUShaderStage.FRAGMENT,
buffer: {},
}]
});
const pipelineLayout = device.createPipelineLayout({
bindGroupLayouts: [
bindGroupLayout,
]
});
const module = device.createShaderModule({
code: await (await fetch("raytracing.wgsl")).text(),
});
const pipeline = device.createRenderPipeline({
layout: pipelineLayout,
vertex: {
module,
entry: "vs_main",
},
fragment: {
module,
entry: "fs_main",
targets: [
{
format: presentationFormat,
},
],
},
primitive: {
topology: 'triangle-list',
},
});
const timeBuffer = device.createBuffer({
size: 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const aspectBuffer = device.createBuffer({
size: 4,
usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,
});
const bindGroup = device.createBindGroup({
layout: bindGroupLayout,
entries: [
{ binding: 0, resource: { buffer: timeBuffer }},
{ binding: 1, resource: { buffer: aspectBuffer }}
],
});
const timeArray = new Float32Array(1);
const aspectArray = new Float32Array(1);
let time = 0;
let time_history = [];
let elapsed_time = 0;
function frame() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
timeArray.set([Math.sin(elapsed_time)]);
device.queue.writeBuffer(timeBuffer, 0, timeArray);
aspectArray.set([window.innerWidth / window.innerHeight]);
device.queue.writeBuffer(aspectBuffer, 0, aspectArray);
const commandEncoder = device.createCommandEncoder();
const textureView = context.getCurrentTexture().createView();
const renderPassDescriptor = {
colorAttachments: [
{
view: textureView,
clearValue: [0, 0, 0, 1],
loadOp: 'clear',
storeOp: 'store',
},
],
};
const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);
passEncoder.setPipeline(pipeline);
passEncoder.setBindGroup(0, bindGroup);
passEncoder.draw(6);
passEncoder.end();
device.queue.submit([commandEncoder.finish()]);
let end_time = Date.now();
let frame_time = end_time - time;
elapsed_time += frame_time / 1000;
time = end_time;
time_history.push(frame_time);
if(time_history.length > 10) time_history.shift();
let avg_frame_time = time_history.reduce((acc, v) => acc + v) / time_history.length;
frameTime.innerText = `FPS: ${(1 / (avg_frame_time / 1000)).toFixed(2)}
Frame time: ${avg_frame_time.toFixed(1)}ms
${adapterInfo.vendor} ${adapterInfo.device} (${adapterInfo.architecture})`;
requestAnimationFrame(frame);
}
requestAnimationFrame(frame);