const canvas = document.querySelector("#canvas"); const context = canvas.getContext("webgpu"); const frameTime = document.querySelector("#frame_time"); canvas.width = window.innerWidth; canvas.height = window.innerHeight; const adapter = await navigator.gpu.requestAdapter(); 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 pipeline = device.createRenderPipeline({ layout: pipelineLayout, vertex: { module: device.createShaderModule({ code: await (await fetch("triangle.vert.wgsl")).text(), }), }, fragment: { module: device.createShaderModule({ code: await (await fetch("red.frag.wgsl")).text(), }), 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 = Date.now(); let time_history = []; let elapsed_time = 0; function frame() { 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() / 1000; let frame_time = end_time - time; elapsed_time += frame_time; time = end_time; time_history.push(frame_time); if(time_history.length > 10) time_history.shift(); frameTime.innerText = (1.0 / (time_history.reduce((acc, v) => acc + v) / time_history.length)).toFixed(2) + " FPS"; //frameTime.innerText = 1.0 / (frame_time / 1000); requestAnimationFrame(frame); } requestAnimationFrame(frame);