From 05f711f6b473a62e96099895d90e31f2a3dd421a Mon Sep 17 00:00:00 2001 From: greggman Date: Thu, 26 Oct 2023 15:13:20 +0000 Subject: [PATCH] =?UTF-8?q?Deploying=20to=20gh-pages=20from=20=20@=2052904?= =?UTF-8?q?7ef6607671478e25ea59f9cf377d11adf6e=20=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 404.html | 4 ++-- .../samples/A-buffer.json | 0 .../samples/animometer.json | 0 .../samples/bitonicSort.json | 0 .../samples/cameras.json | 0 .../samples/computeBoids.json | 0 .../samples/cornell.json | 0 .../samples/cubemap.json | 0 .../samples/deferredRendering.json | 0 .../samples/fractalCube.json | 0 .../samples/gameOfLife.json | 0 .../samples/helloTriangle.json | 0 .../samples/helloTriangleMSAA.json | 0 .../samples/imageBlur.json | 0 .../samples/instancedCube.json | 0 .../samples/particles.json | 0 .../samples/renderBundles.json | 0 .../samples/resizeCanvas.json | 0 .../samples/reversedZ.json | 0 .../samples/rotatingCube.json | 0 .../samples/samplerParameters.json | 0 .../samples/shadowMapping.json | 0 .../samples/texturedCube.json | 0 .../samples/twoCubes.json | 0 .../samples/videoUploading.json | 0 .../samples/videoUploadingWebCodecs.json | 0 .../samples/worker.json | 0 _next/static/chunks/112.4c9c51222d0707fd.js | 1 - _next/static/chunks/112.964d8a7e473b18ae.js | 1 + .../{752.5364f7ac3622f2eb.js => 752.2b99aa35a30ac9aa.js} | 2 +- .../{841.3dd27e4f39a03532.js => 841.097bc46a54981e6b.js} | 2 +- ...ebpack-96bf41c256b905d0.js => webpack-52596f3636814f01.js} | 2 +- .../_buildManifest.js | 0 .../_ssgManifest.js | 0 index.html | 2 +- samples/A-buffer.html | 4 ++-- samples/animometer.html | 2 +- samples/bitonicSort.html | 2 +- samples/cameras.html | 2 +- samples/computeBoids.html | 2 +- samples/cornell.html | 2 +- samples/cubemap.html | 2 +- samples/deferredRendering.html | 4 ++-- samples/fractalCube.html | 2 +- samples/gameOfLife.html | 2 +- samples/helloTriangle.html | 2 +- samples/helloTriangleMSAA.html | 2 +- samples/imageBlur.html | 2 +- samples/instancedCube.html | 2 +- samples/particles.html | 2 +- samples/renderBundles.html | 4 ++-- samples/resizeCanvas.html | 2 +- samples/reversedZ.html | 4 ++-- samples/rotatingCube.html | 2 +- samples/samplerParameters.html | 2 +- samples/shadowMapping.html | 2 +- samples/texturedCube.html | 2 +- samples/twoCubes.html | 2 +- samples/videoUploading.html | 2 +- samples/videoUploadingWebCodecs.html | 2 +- samples/worker.html | 4 ++-- 61 files changed, 38 insertions(+), 38 deletions(-) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/A-buffer.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/animometer.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/bitonicSort.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/cameras.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/computeBoids.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/cornell.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/cubemap.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/deferredRendering.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/fractalCube.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/gameOfLife.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/helloTriangle.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/helloTriangleMSAA.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/imageBlur.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/instancedCube.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/particles.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/renderBundles.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/resizeCanvas.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/reversedZ.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/rotatingCube.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/samplerParameters.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/shadowMapping.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/texturedCube.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/twoCubes.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/videoUploading.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/videoUploadingWebCodecs.json (100%) rename _next/data/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/samples/worker.json (100%) delete mode 100644 _next/static/chunks/112.4c9c51222d0707fd.js create mode 100644 _next/static/chunks/112.964d8a7e473b18ae.js rename _next/static/chunks/{752.5364f7ac3622f2eb.js => 752.2b99aa35a30ac9aa.js} (65%) rename _next/static/chunks/{841.3dd27e4f39a03532.js => 841.097bc46a54981e6b.js} (81%) rename _next/static/chunks/{webpack-96bf41c256b905d0.js => webpack-52596f3636814f01.js} (96%) rename _next/static/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/_buildManifest.js (100%) rename _next/static/{BZ6gMEVN413qOHc1JcPkX => lvQoHI2oN8VKaFRrb5QRa}/_ssgManifest.js (100%) diff --git a/404.html b/404.html index 678f998d..821970f0 100644 --- a/404.html +++ b/404.html @@ -1,4 +1,4 @@ -404: This page could not be found

404

This page could not be found.

\ No newline at end of file + }

404

This page could not be found.

\ No newline at end of file diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/A-buffer.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/A-buffer.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/A-buffer.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/A-buffer.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/animometer.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/animometer.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/animometer.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/animometer.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/bitonicSort.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/bitonicSort.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/bitonicSort.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/bitonicSort.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/cameras.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/cameras.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/cameras.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/cameras.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/computeBoids.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/computeBoids.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/computeBoids.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/computeBoids.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/cornell.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/cornell.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/cornell.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/cornell.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/cubemap.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/cubemap.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/cubemap.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/cubemap.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/deferredRendering.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/deferredRendering.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/deferredRendering.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/deferredRendering.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/fractalCube.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/fractalCube.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/fractalCube.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/fractalCube.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/gameOfLife.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/gameOfLife.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/gameOfLife.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/gameOfLife.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/helloTriangle.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/helloTriangle.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/helloTriangle.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/helloTriangle.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/helloTriangleMSAA.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/helloTriangleMSAA.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/helloTriangleMSAA.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/helloTriangleMSAA.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/imageBlur.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/imageBlur.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/imageBlur.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/imageBlur.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/instancedCube.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/instancedCube.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/instancedCube.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/instancedCube.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/particles.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/particles.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/particles.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/particles.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/renderBundles.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/renderBundles.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/renderBundles.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/renderBundles.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/resizeCanvas.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/resizeCanvas.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/resizeCanvas.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/resizeCanvas.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/reversedZ.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/reversedZ.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/reversedZ.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/reversedZ.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/rotatingCube.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/rotatingCube.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/rotatingCube.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/rotatingCube.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/samplerParameters.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/samplerParameters.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/samplerParameters.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/samplerParameters.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/shadowMapping.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/shadowMapping.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/shadowMapping.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/shadowMapping.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/texturedCube.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/texturedCube.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/texturedCube.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/texturedCube.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/twoCubes.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/twoCubes.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/twoCubes.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/twoCubes.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/videoUploading.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/videoUploading.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/videoUploading.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/videoUploading.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/videoUploadingWebCodecs.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/videoUploadingWebCodecs.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/videoUploadingWebCodecs.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/videoUploadingWebCodecs.json diff --git a/_next/data/BZ6gMEVN413qOHc1JcPkX/samples/worker.json b/_next/data/lvQoHI2oN8VKaFRrb5QRa/samples/worker.json similarity index 100% rename from _next/data/BZ6gMEVN413qOHc1JcPkX/samples/worker.json rename to _next/data/lvQoHI2oN8VKaFRrb5QRa/samples/worker.json diff --git a/_next/static/chunks/112.4c9c51222d0707fd.js b/_next/static/chunks/112.4c9c51222d0707fd.js deleted file mode 100644 index 51d19ad1..00000000 --- a/_next/static/chunks/112.4c9c51222d0707fd.js +++ /dev/null @@ -1 +0,0 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[112],{5671:function(e,n,t){"use strict";t.d(n,{Tl:function(){return c},hu:function(){return p}});var r=t(5893),i=t(9008),o=t.n(i),a=t(1163),s=t(7294),l=t(9147),u=t.n(l);t(7319);let d=e=>{let n=(0,s.useRef)(null),i=(0,s.useMemo)(()=>e.sources.map(e=>{let{name:n,contents:i}=e;return{name:n,...function(e){let n;let i=null;{i=document.createElement("div");let o=t(4631);n=o(i,{lineNumbers:!0,lineWrapping:!0,theme:"monokai",readOnly:!0})}return{Container:function(t){return(0,r.jsx)("div",{...t,children:(0,r.jsx)("div",{ref(t){i&&t&&(t.appendChild(i),n.setOption("value",e))}})})}}}(i)}}),e.sources),l=(0,s.useRef)(null),d=(0,s.useMemo)(()=>{if(e.gui){let n=t(4376);return new n.GUI({autoPlace:!1})}},[]),c=(0,s.useRef)(null),p=(0,s.useMemo)(()=>{if(e.stats){let n=t(2792);return new n}},[]),m=(0,a.useRouter)(),h=m.asPath.match(/#([a-zA-Z0-9\.\/]+)/),[f,g]=(0,s.useState)(null),[E,v]=(0,s.useState)(null);return(0,s.useEffect)(()=>{if(h?v(h[1]):v(i[0].name),d&&l.current)for(l.current.appendChild(d.domElement);d.__controllers.length>0;)d.__controllers[0].remove();p&&c.current&&(p.dom.style.position="absolute",p.showPanel(1),c.current.appendChild(p.dom));let t={active:!0},r=()=>{t.active=!1};try{let o=n.current;if(!o)throw Error("The canvas is not available");let a=e.init({canvas:o,pageState:t,gui:d,stats:p});a instanceof Promise&&a.catch(e=>{console.error(e),g(e)})}catch(s){console.error(s),g(s)}return r},[]),(0,r.jsxs)("main",{children:[(0,r.jsxs)(o(),{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"\n .CodeMirror {\n height: auto !important;\n margin: 1em 0;\n }\n\n .CodeMirror-scroll {\n height: auto !important;\n overflow: visible !important;\n }\n "}}),(0,r.jsx)("title",{children:"".concat(e.name," - WebGPU Samples")}),(0,r.jsx)("meta",{name:"description",content:e.description}),(0,r.jsx)("meta",{httpEquiv:"origin-trial",content:e.originTrial})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("h1",{children:e.name}),(0,r.jsx)("a",{target:"_blank",rel:"noreferrer",href:"https://github.com/".concat("webgpu/webgpu-samples","/tree/main/").concat(e.filename),children:"See it on Github!"}),(0,r.jsx)("p",{children:e.description}),f?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("p",{children:"Something went wrong. Do your browser and device support WebGPU?"}),(0,r.jsx)("p",{children:"".concat(f)})]}):null]}),(0,r.jsxs)("div",{className:u().canvasContainer,children:[(0,r.jsx)("div",{style:{position:"absolute",left:10},ref:c}),(0,r.jsx)("div",{style:{position:"absolute",right:10},ref:l}),(0,r.jsx)("canvas",{ref:n})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("nav",{className:u().sourceFileNav,children:(0,r.jsx)("ul",{children:i.map((e,n)=>(0,r.jsx)("li",{children:(0,r.jsx)("a",{href:"#".concat(e.name),"data-active":E==e.name,onClick(){v(e.name)},children:e.name})},n))})}),i.map((e,n)=>(0,r.jsx)(e.Container,{className:u().sourceFileContainer,"data-active":E==e.name},n))]})]})},c=e=>(0,r.jsx)(d,{...e});function p(e,n){if(!e)throw Error(n)}},8112:function(e,n,t){"use strict";let r;t.r(n),t.d(n,{default:function(){return g}});var i,o,a=t(5671),s=t(134);let l=(e,n,t,r,i,o,a)=>{let s=[];for(let l=0;l{let n=async n=>{let{canvas:t,pageState:r,gui:i,stats:o}=n,a=await navigator.gpu.requestAdapter(),s=await a.requestDevice();if(!r.active)return;let l=t.getContext("webgpu"),u=window.devicePixelRatio||1;t.width=t.clientWidth*u,t.height=t.clientHeight*u;let d=navigator.gpu.getPreferredCanvasFormat();l.configure({device:s,format:d,alphaMode:"premultiplied"}),e({canvas:t,pageState:r,gui:i,device:s,context:l,presentationFormat:d,stats:o})};return n};class d{executeRun(e,n,t,r){let i=e.beginRenderPass(n);i.setPipeline(t);for(let o=0;o{this.bindGroupMap[r[n]]=e}),this.pipeline=super.create2DRenderPipeline(e,o,[s.bindGroupLayout,this.computeBGDescript.bindGroupLayout],c,n),this.switchBindGroup=e=>{this.currentBindGroup=this.bindGroupMap[e],this.currentBindGroupName=e},this.setArguments=n=>{super.setUniformArguments(e,a,n,["width","height"])}}}p.sourceInfo={name:"src/sample/bitonicSort/bitonicDisplay.ts".substring(23),contents:"import {\n BindGroupsObjectsAndLayout,\n createBindGroupDescriptor,\n Base2DRendererClass,\n} from './utils';\n\nimport bitonicDisplay from './bitonicDisplay.frag.wgsl';\n\ninterface BitonicDisplayRenderArgs {\n width: number;\n height: number;\n}\n\nexport default class BitonicDisplayRenderer extends Base2DRendererClass {\n static sourceInfo = {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n };\n\n switchBindGroup: (name: string) => void;\n setArguments: (args: BitonicDisplayRenderArgs) => void;\n computeBGDescript: BindGroupsObjectsAndLayout;\n\n constructor(\n device: GPUDevice,\n presentationFormat: GPUTextureFormat,\n renderPassDescriptor: GPURenderPassDescriptor,\n bindGroupNames: string[],\n computeBGDescript: BindGroupsObjectsAndLayout,\n label: string\n ) {\n super();\n this.renderPassDescriptor = renderPassDescriptor;\n this.computeBGDescript = computeBGDescript;\n\n const uniformBuffer = device.createBuffer({\n size: Float32Array.BYTES_PER_ELEMENT * 2,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n });\n\n const bgDescript = createBindGroupDescriptor(\n [0],\n [GPUShaderStage.FRAGMENT],\n ['buffer'],\n [{ type: 'uniform' }],\n [[{ buffer: uniformBuffer }]],\n label,\n device\n );\n\n this.currentBindGroup = bgDescript.bindGroups[0];\n this.currentBindGroupName = bindGroupNames[0];\n\n this.bindGroupMap = {};\n\n bgDescript.bindGroups.forEach((bg, idx) => {\n this.bindGroupMap[bindGroupNames[idx]] = bg;\n });\n\n this.pipeline = super.create2DRenderPipeline(\n device,\n label,\n [bgDescript.bindGroupLayout, this.computeBGDescript.bindGroupLayout],\n bitonicDisplay,\n presentationFormat\n );\n\n this.switchBindGroup = (name: string) => {\n this.currentBindGroup = this.bindGroupMap[name];\n this.currentBindGroupName = name;\n };\n\n this.setArguments = (args: BitonicDisplayRenderArgs) => {\n super.setUniformArguments(device, uniformBuffer, args, [\n 'width',\n 'height',\n ]);\n };\n }\n\n startRun(commandEncoder: GPUCommandEncoder, args: BitonicDisplayRenderArgs) {\n this.setArguments(args);\n super.executeRun(commandEncoder, this.renderPassDescriptor, this.pipeline, [\n this.currentBindGroup,\n this.computeBGDescript.bindGroups[0],\n ]);\n }\n}\n"};let m=e=>((e%2!=0||e>256)&&(e=256),"\n\nstruct Uniforms {\n width: f32,\n height: f32,\n algo: u32,\n blockHeight: u32,\n}\n\n// Create local workgroup data that can contain all elements\n\nvar local_data: array;\n\n//Compare and swap values in local_data\nfn compare_and_swap(idx_before: u32, idx_after: u32) {\n //idx_before should always be < idx_after\n if (local_data[idx_after] < local_data[idx_before]) {\n var temp: u32 = local_data[idx_before];\n local_data[idx_before] = local_data[idx_after];\n local_data[idx_after] = temp;\n }\n return;\n}\n\n// thread_id goes from 0 to threadsPerWorkgroup\nfn prepare_flip(thread_id: u32, block_height: u32) {\n let q: u32 = ((2 * thread_id) / block_height) * block_height;\n let half_height = block_height / 2;\n var idx: vec2 = vec2(\n thread_id % half_height, block_height - (thread_id % half_height) - 1,\n );\n idx.x += q;\n idx.y += q;\n compare_and_swap(idx.x, idx.y);\n}\n\nfn prepare_disperse(thread_id: u32, block_height: u32) {\n var q: u32 = ((2 * thread_id) / block_height) * block_height;\n let half_height = block_height / 2;\n var idx: vec2 = vec2(\n thread_id % half_height, (thread_id % half_height) + half_height\n );\n idx.x += q;\n idx.y += q;\n compare_and_swap(idx.x, idx.y);\n}\n\n@group(0) @binding(0) var input_data: array;\n@group(0) @binding(1) var output_data: array;\n@group(0) @binding(2) var uniforms: Uniforms;\n\n// Our compute shader will execute specified # of threads or elements / 2 threads\n@compute @workgroup_size(").concat(e,", 1, 1)\nfn computeMain(\n @builtin(global_invocation_id) global_id: vec3,\n @builtin(local_invocation_id) local_id: vec3,\n) {\n //Each thread will populate the workgroup data... (1 thread for every 2 elements)\n local_data[local_id.x * 2] = input_data[local_id.x * 2];\n local_data[local_id.x * 2 + 1] = input_data[local_id.x * 2 + 1];\n\n //...and wait for each other to finish their own bit of data population.\n workgroupBarrier();\n\n var num_elements = uniforms.width * uniforms.height;\n\n switch uniforms.algo {\n case 1: { //Local Flip\n prepare_flip(local_id.x, uniforms.blockHeight);\n }\n case 2: { //Local Disperse\n prepare_disperse(local_id.x, uniforms.blockHeight);\n }\n default: { \n \n }\n }\n\n //Ensure that all threads have swapped their own regions of data\n workgroupBarrier();\n\n //Repopulate global data with local data\n output_data[local_id.x * 2] = local_data[local_id.x * 2];\n output_data[local_id.x * 2 + 1] = local_data[local_id.x * 2 + 1];\n\n}"));var h="src/sample/bitonicSort/main.ts";(i=o||(o={}))[i.NONE=0]="NONE",i[i.FLIP_LOCAL=1]="FLIP_LOCAL",i[i.DISPERSE_LOCAL=2]="DISPERSE_LOCAL",i[i.FLIP_DISPERSE_LOCAL=3]="FLIP_DISPERSE_LOCAL",u(async e=>{let{pageState:n,device:t,gui:r,presentationFormat:i,context:a,canvas:s}=e,u=t.limits.maxComputeWorkgroupSizeX,d=[];for(let c=2*u;c>=4;c/=2)d.push(c);let h={"Total Elements":16,"Grid Width":4,"Grid Height":4,"Total Threads":8,hoveredElement:0,swappedElement:1,"Prev Step":"NONE","Next Step":"FLIP_LOCAL","Prev Swap Span":0,"Next Swap Span":2,workLoads:1,executeStep:!1,"Randomize Values"(){},"Execute Sort Step"(){},"Log Elements"(){},"Complete Sort"(){},sortSpeed:200},f=new Uint32Array(Array.from({length:h["Total Elements"]},(e,n)=>n)),g=512*Float32Array.BYTES_PER_ELEMENT,E=t.createBuffer({size:g,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),v=t.createBuffer({size:g,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC}),S=t.createBuffer({size:g,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),x=t.createBuffer({size:4*Float32Array.BYTES_PER_ELEMENT,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),b=l([0,1,2],[GPUShaderStage.COMPUTE|GPUShaderStage.FRAGMENT,GPUShaderStage.COMPUTE,GPUShaderStage.COMPUTE],["buffer","buffer","buffer"],[{type:"read-only-storage"},{type:"storage"},{type:"uniform"}],[[{buffer:E},{buffer:v},{buffer:x}]],"NaiveBitonicSort",t),y=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[b.bindGroupLayout]}),compute:{module:t.createShaderModule({code:m(h["Total Threads"])}),entryPoint:"computeMain"}}),_={colorAttachments:[{view:void 0,clearValue:{r:.1,g:.4,b:.5,a:1},loadOp:"clear",storeOp:"store"}]},w=new p(t,i,_,["default"],b,"BitonicDisplay"),P=()=>{U.setValue(h["Total Elements"]/2);let e=Math.sqrt(h["Total Elements"])%2==0?Math.floor(Math.sqrt(h["Total Elements"])):Math.floor(Math.sqrt(h["Total Elements"]/2)),n=h["Total Elements"]/e;V.setValue(e),z.setValue(n),F.setValue("NONE"),k.setValue("FLIP_LOCAL"),M.setValue(0),H.setValue(2),q=2},B=()=>{let e=f.length;for(;0!==e;){let n=Math.floor(Math.random()*e);e-=1,[f[e],f[n]]=[f[n],f[e]]}},G=()=>{f=new Uint32Array(Array.from({length:h["Total Elements"]},(e,n)=>n)),P(),y=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[b.bindGroupLayout]}),compute:{module:t.createShaderModule({code:m(h["Total Elements"]/2)}),entryPoint:"computeMain"}}),B(),q=2};B();let C=()=>{let e;switch(h["Next Step"]){case"FLIP_LOCAL":{let n=h["Next Swap Span"],t=Math.floor(h.hoveredElement/n)+1,r=h.hoveredElement%n;e=n*t-r-1,R.setValue(e)}break;case"DISPERSE_LOCAL":{let i=h["Next Swap Span"],o=i/2;e=h.hoveredElement%i{null!==I&&(clearInterval(I),I=null)},T=()=>{I=setInterval(()=>{"NONE"===h["Next Step"]&&(clearInterval(I),I=null),h.executeStep=!0,C()},h.sortSpeed)};r.add(h,"Total Elements",d).onChange(()=>{L(),G()});let U=r.add(h,"Total Threads"),D=r.addFolder("Sort Controls");D.add(h,"Execute Sort Step").onChange(()=>{L(),h.executeStep=!0}),D.add(h,"Randomize Values").onChange(()=>{L(),B(),P()}),D.add(h,"Log Elements").onChange(()=>console.log(f)),D.add(h,"Complete Sort").onChange(T),D.open();let A=r.addFolder("Hover Information"),N=A.add(h,"hoveredElement").onChange(C),R=A.add(h,"swappedElement"),O=r.addFolder("Execution Information"),F=O.add(h,"Prev Step"),k=O.add(h,"Next Step"),M=O.add(h,"Prev Swap Span"),H=O.add(h,"Next Swap Span"),V=O.add(h,"Grid Width"),z=O.add(h,"Grid Height"),W=document.getElementsByClassName("cr function");for(let j=0;j{let n=s.getBoundingClientRect().width,t=s.getBoundingClientRect().height,r=[n/h["Grid Width"],t/h["Grid Height"]],i=Math.floor(e.offsetX/r[0]),o=h["Grid Height"]-1-Math.floor(e.offsetY/r[1]);N.setValue(o*h["Grid Width"]+i),h.hoveredElement=o*h["Grid Width"]+i}),F.domElement.style.pointerEvents="none",M.domElement.style.pointerEvents="none",k.domElement.style.pointerEvents="none",H.domElement.style.pointerEvents="none",U.domElement.style.pointerEvents="none",V.domElement.style.pointerEvents="none",z.domElement.style.pointerEvents="none";let q=2;async function Y(){if(!n.active)return;t.queue.writeBuffer(E,0,f.buffer,f.byteOffset,f.byteLength);let e=new Float32Array([h["Grid Width"],h["Grid Height"]]),r=new Uint32Array([o[h["Next Step"]],h["Next Swap Span"]]);t.queue.writeBuffer(x,0,e.buffer,e.byteOffset,e.byteLength),t.queue.writeBuffer(x,8,r),_.colorAttachments[0].view=a.getCurrentTexture().createView();let i=t.createCommandEncoder();if(w.startRun(i,{width:h["Grid Width"],height:h["Grid Height"]}),h.executeStep&&q!==2*h["Total Elements"]){let s=i.beginComputePass();s.setPipeline(y),s.setBindGroup(0,b.bindGroups[0]),s.dispatchWorkgroups(1),s.end(),F.setValue(h["Next Step"]),M.setValue(h["Next Swap Span"]),H.setValue(h["Next Swap Span"]/2),1===h["Next Swap Span"]?(q*=2,k.setValue(q===2*h["Total Elements"]?"NONE":"FLIP_LOCAL"),H.setValue(q===2*h["Total Elements"]?0:q)):k.setValue("DISPERSE_LOCAL"),i.copyBufferToBuffer(v,0,S,0,g)}if(t.queue.submit([i.finish()]),h.executeStep){await S.mapAsync(GPUMapMode.READ,0,g);let l=S.getMappedRange(0,g),u=l.slice(0,Uint32Array.BYTES_PER_ELEMENT*h["Total Elements"]),d=new Uint32Array(u);S.unmap(),f=d,C()}h.executeStep=!1,requestAnimationFrame(Y)}requestAnimationFrame(Y)}).then(e=>r=e);let f=()=>(0,a.Tl)({name:"Bitonic Sort",description:"A naive bitonic sort algorithm executed on the GPU, based on tgfrerer's implementation at poniesandlight.co.uk/reflect/bitonic_merge_sort/. Each invocation of the bitonic sort shader dispatches a workgroup containing elements/2 threads. The GUI's Execution Information folder contains information about the sort's current state. The visualizer displays the sort's results as colored cells sorted from brightest to darkest.",init:r,gui:!0,sources:[{name:h.substring(23),contents:"import { makeSample, SampleInit } from '../../components/SampleLayout';\nimport { SampleInitFactoryWebGPU } from './utils';\nimport { createBindGroupDescriptor } from './utils';\nimport BitonicDisplayRenderer from './bitonicDisplay';\nimport bitonicDisplay from './bitonicDisplay.frag.wgsl';\nimport { NaiveBitonicCompute } from './computeShader';\nimport fullscreenTexturedQuad from '../../shaders/fullscreenTexturedQuad.wgsl';\n\n// Type of step that will be executed in our shader\nenum StepEnum {\n NONE = 0,\n FLIP_LOCAL = 1,\n DISPERSE_LOCAL = 2,\n FLIP_DISPERSE_LOCAL = 3,\n}\n\n// String access to StepEnum\ntype StepType =\n | 'NONE'\n | 'FLIP_LOCAL'\n | 'DISPERSE_LOCAL'\n | 'FLIP_DISPERSE_LOCAL';\n\n// Gui settings object\ninterface SettingsInterface {\n 'Total Elements': number;\n 'Grid Width': number;\n 'Grid Height': number;\n 'Total Threads': number;\n hoveredElement: number;\n swappedElement: number;\n 'Prev Step': StepType;\n 'Next Step': StepType;\n 'Prev Swap Span': number;\n 'Next Swap Span': number;\n workLoads: number;\n executeStep: boolean;\n 'Randomize Values': () => void;\n 'Execute Sort Step': () => void;\n 'Log Elements': () => void;\n 'Complete Sort': () => void;\n sortSpeed: number;\n}\n\nlet init: SampleInit;\nSampleInitFactoryWebGPU(\n async ({ pageState, device, gui, presentationFormat, context, canvas }) => {\n const maxWorkgroupsX = device.limits.maxComputeWorkgroupSizeX;\n\n const totalElementLengths = [];\n for (let i = maxWorkgroupsX * 2; i >= 4; i /= 2) {\n totalElementLengths.push(i);\n }\n\n const settings: SettingsInterface = {\n // number of cellElements. Must equal gridWidth * gridHeight and 'Total Threads' * 2\n 'Total Elements': 16,\n // width of screen in cells.\n 'Grid Width': 4,\n // height of screen in cells\n 'Grid Height': 4,\n // number of threads to execute in a workgroup ('Total Threads', 1, 1)\n 'Total Threads': 16 / 2,\n // currently highlighted element\n hoveredElement: 0,\n // element the hoveredElement just swapped with,\n swappedElement: 1,\n // Previously executed step\n 'Prev Step': 'NONE',\n // Next step to execute\n 'Next Step': 'FLIP_LOCAL',\n // Max thread span of previous block\n 'Prev Swap Span': 0,\n // Max thread span of next block\n 'Next Swap Span': 2,\n // workloads to dispatch per frame,\n workLoads: 1,\n // Whether we will dispatch a workload this frame\n executeStep: false,\n 'Randomize Values': () => {\n return;\n },\n 'Execute Sort Step': () => {\n return;\n },\n 'Log Elements': () => {\n return;\n },\n 'Complete Sort': () => {\n return;\n },\n sortSpeed: 200,\n };\n\n // Initialize initial elements array\n let elements = new Uint32Array(\n Array.from({ length: settings['Total Elements'] }, (_, i) => i)\n );\n\n // Initialize elementsBuffer and elementsStagingBuffer\n const elementsBufferSize = Float32Array.BYTES_PER_ELEMENT * 512;\n // Initialize input, output, staging buffers\n const elementsInputBuffer = device.createBuffer({\n size: elementsBufferSize,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n });\n const elementsOutputBuffer = device.createBuffer({\n size: elementsBufferSize,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,\n });\n const elementsStagingBuffer = device.createBuffer({\n size: elementsBufferSize,\n usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,\n });\n\n // Create uniform buffer for compute shader\n const computeUniformsBuffer = device.createBuffer({\n // width, height, blockHeight, algo\n size: Float32Array.BYTES_PER_ELEMENT * 4,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n });\n\n const computeBGDescript = createBindGroupDescriptor(\n [0, 1, 2],\n [\n GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,\n GPUShaderStage.COMPUTE,\n GPUShaderStage.COMPUTE,\n ],\n ['buffer', 'buffer', 'buffer'],\n [{ type: 'read-only-storage' }, { type: 'storage' }, { type: 'uniform' }],\n [\n [\n { buffer: elementsInputBuffer },\n { buffer: elementsOutputBuffer },\n { buffer: computeUniformsBuffer },\n ],\n ],\n 'NaiveBitonicSort',\n device\n );\n\n let computePipeline = device.createComputePipeline({\n layout: device.createPipelineLayout({\n bindGroupLayouts: [computeBGDescript.bindGroupLayout],\n }),\n compute: {\n module: device.createShaderModule({\n code: NaiveBitonicCompute(settings['Total Threads']),\n }),\n entryPoint: 'computeMain',\n },\n });\n\n // Create bitonic debug renderer\n const renderPassDescriptor: GPURenderPassDescriptor = {\n colorAttachments: [\n {\n view: undefined, // Assigned later\n\n clearValue: { r: 0.1, g: 0.4, b: 0.5, a: 1.0 },\n loadOp: 'clear',\n storeOp: 'store',\n },\n ],\n };\n\n const bitonicDisplayRenderer = new BitonicDisplayRenderer(\n device,\n presentationFormat,\n renderPassDescriptor,\n ['default'],\n computeBGDescript,\n 'BitonicDisplay'\n );\n\n const resetExecutionInformation = () => {\n totalThreadsCell.setValue(settings['Total Elements'] / 2);\n\n // Get new width and height of screen display in cells\n const newCellWidth =\n Math.sqrt(settings['Total Elements']) % 2 === 0\n ? Math.floor(Math.sqrt(settings['Total Elements']))\n : Math.floor(Math.sqrt(settings['Total Elements'] / 2));\n const newCellHeight = settings['Total Elements'] / newCellWidth;\n gridWidthCell.setValue(newCellWidth);\n gridHeightCell.setValue(newCellHeight);\n\n // Set prevStep to None (restart) and next step to FLIP\n prevStepCell.setValue('NONE');\n nextStepCell.setValue('FLIP_LOCAL');\n\n // Reset block heights\n prevBlockHeightCell.setValue(0);\n nextBlockHeightCell.setValue(2);\n highestBlockHeight = 2;\n };\n\n const randomizeElementArray = () => {\n let currentIndex = elements.length;\n // While there are elements to shuffle\n while (currentIndex !== 0) {\n // Pick a remaining element\n const randomIndex = Math.floor(Math.random() * currentIndex);\n currentIndex -= 1;\n [elements[currentIndex], elements[randomIndex]] = [\n elements[randomIndex],\n elements[currentIndex],\n ];\n }\n };\n\n const resizeElementArray = () => {\n // Recreate elements array with new length\n elements = new Uint32Array(\n Array.from({ length: settings['Total Elements'] }, (_, i) => i)\n );\n\n resetExecutionInformation();\n\n // Create new shader invocation with workgroupSize that reflects number of threads\n computePipeline = device.createComputePipeline({\n layout: device.createPipelineLayout({\n bindGroupLayouts: [computeBGDescript.bindGroupLayout],\n }),\n compute: {\n module: device.createShaderModule({\n code: NaiveBitonicCompute(settings['Total Elements'] / 2),\n }),\n entryPoint: 'computeMain',\n },\n });\n // Randomize array elements\n randomizeElementArray();\n highestBlockHeight = 2;\n };\n\n randomizeElementArray();\n\n const setSwappedElement = () => {\n let swappedIndex: number;\n switch (settings['Next Step']) {\n case 'FLIP_LOCAL':\n {\n const blockHeight = settings['Next Swap Span'];\n const p2 = Math.floor(settings.hoveredElement / blockHeight) + 1;\n const p3 = settings.hoveredElement % blockHeight;\n swappedIndex = blockHeight * p2 - p3 - 1;\n swappedElementCell.setValue(swappedIndex);\n }\n break;\n case 'DISPERSE_LOCAL':\n {\n const blockHeight = settings['Next Swap Span'];\n const halfHeight = blockHeight / 2;\n swappedIndex =\n settings.hoveredElement % blockHeight < halfHeight\n ? settings.hoveredElement + halfHeight\n : settings.hoveredElement - halfHeight;\n swappedElementCell.setValue(swappedIndex);\n }\n break;\n case 'NONE': {\n swappedIndex = settings.hoveredElement;\n swappedElementCell.setValue(swappedIndex);\n }\n default:\n {\n swappedIndex = settings.hoveredElement;\n swappedElementCell.setValue(swappedIndex);\n }\n break;\n }\n };\n\n let completeSortIntervalID: ReturnType | null = null;\n const endSortInterval = () => {\n if (completeSortIntervalID !== null) {\n clearInterval(completeSortIntervalID);\n completeSortIntervalID = null;\n }\n };\n const startSortInterval = () => {\n completeSortIntervalID = setInterval(() => {\n if (settings['Next Step'] === 'NONE') {\n clearInterval(completeSortIntervalID);\n completeSortIntervalID = null;\n }\n settings.executeStep = true;\n setSwappedElement();\n }, settings.sortSpeed);\n };\n\n // At top level, basic information about the number of elements sorted and the number of threads\n // deployed per workgroup.\n gui.add(settings, 'Total Elements', totalElementLengths).onChange(() => {\n endSortInterval();\n resizeElementArray();\n });\n const totalThreadsCell = gui.add(settings, 'Total Threads');\n\n // Folder with functions that control the execution of the sort\n const controlFolder = gui.addFolder('Sort Controls');\n controlFolder.add(settings, 'Execute Sort Step').onChange(() => {\n endSortInterval();\n settings.executeStep = true;\n });\n controlFolder.add(settings, 'Randomize Values').onChange(() => {\n endSortInterval();\n randomizeElementArray();\n resetExecutionInformation();\n });\n controlFolder\n .add(settings, 'Log Elements')\n .onChange(() => console.log(elements));\n controlFolder.add(settings, 'Complete Sort').onChange(startSortInterval);\n controlFolder.open();\n\n // Folder with indexes of the hovered element\n const hoverFolder = gui.addFolder('Hover Information');\n const hoveredElementCell = hoverFolder\n .add(settings, 'hoveredElement')\n .onChange(setSwappedElement);\n const swappedElementCell = hoverFolder.add(settings, 'swappedElement');\n\n // Additional Information about the execution state of the sort\n const executionInformationFolder = gui.addFolder('Execution Information');\n const prevStepCell = executionInformationFolder.add(settings, 'Prev Step');\n const nextStepCell = executionInformationFolder.add(settings, 'Next Step');\n const prevBlockHeightCell = executionInformationFolder.add(\n settings,\n 'Prev Swap Span'\n );\n const nextBlockHeightCell = executionInformationFolder.add(\n settings,\n 'Next Swap Span'\n );\n const gridWidthCell = executionInformationFolder.add(\n settings,\n 'Grid Width'\n );\n const gridHeightCell = executionInformationFolder.add(\n settings,\n 'Grid Height'\n );\n\n // Adjust styles of Function List Elements within GUI\n const liFunctionElements = document.getElementsByClassName('cr function');\n for (let i = 0; i < liFunctionElements.length; i++) {\n (liFunctionElements[i].children[0] as HTMLElement).style.display = 'flex';\n (liFunctionElements[i].children[0] as HTMLElement).style.justifyContent =\n 'center';\n (\n liFunctionElements[i].children[0].children[1] as HTMLElement\n ).style.position = 'absolute';\n }\n\n canvas.addEventListener('mousemove', (event) => {\n const currWidth = canvas.getBoundingClientRect().width;\n const currHeight = canvas.getBoundingClientRect().height;\n const cellSize: [number, number] = [\n currWidth / settings['Grid Width'],\n currHeight / settings['Grid Height'],\n ];\n const xIndex = Math.floor(event.offsetX / cellSize[0]);\n const yIndex =\n settings['Grid Height'] - 1 - Math.floor(event.offsetY / cellSize[1]);\n hoveredElementCell.setValue(yIndex * settings['Grid Width'] + xIndex);\n settings.hoveredElement = yIndex * settings['Grid Width'] + xIndex;\n });\n\n // Deactivate interaction with select GUI elements\n prevStepCell.domElement.style.pointerEvents = 'none';\n prevBlockHeightCell.domElement.style.pointerEvents = 'none';\n nextStepCell.domElement.style.pointerEvents = 'none';\n nextBlockHeightCell.domElement.style.pointerEvents = 'none';\n totalThreadsCell.domElement.style.pointerEvents = 'none';\n gridWidthCell.domElement.style.pointerEvents = 'none';\n gridHeightCell.domElement.style.pointerEvents = 'none';\n\n let highestBlockHeight = 2;\n\n async function frame() {\n if (!pageState.active) return;\n\n // Write elements buffer\n device.queue.writeBuffer(\n elementsInputBuffer,\n 0,\n elements.buffer,\n elements.byteOffset,\n elements.byteLength\n );\n\n const dims = new Float32Array([\n settings['Grid Width'],\n settings['Grid Height'],\n ]);\n const stepDetails = new Uint32Array([\n StepEnum[settings['Next Step']],\n settings['Next Swap Span'],\n ]);\n device.queue.writeBuffer(\n computeUniformsBuffer,\n 0,\n dims.buffer,\n dims.byteOffset,\n dims.byteLength\n );\n\n device.queue.writeBuffer(computeUniformsBuffer, 8, stepDetails);\n\n renderPassDescriptor.colorAttachments[0].view = context\n .getCurrentTexture()\n .createView();\n\n const commandEncoder = device.createCommandEncoder();\n bitonicDisplayRenderer.startRun(commandEncoder, {\n width: settings['Grid Width'],\n height: settings['Grid Height'],\n });\n if (\n settings.executeStep &&\n highestBlockHeight !== settings['Total Elements'] * 2\n ) {\n const computePassEncoder = commandEncoder.beginComputePass();\n computePassEncoder.setPipeline(computePipeline);\n computePassEncoder.setBindGroup(0, computeBGDescript.bindGroups[0]);\n computePassEncoder.dispatchWorkgroups(1);\n computePassEncoder.end();\n\n prevStepCell.setValue(settings['Next Step']);\n prevBlockHeightCell.setValue(settings['Next Swap Span']);\n nextBlockHeightCell.setValue(settings['Next Swap Span'] / 2);\n if (settings['Next Swap Span'] === 1) {\n highestBlockHeight *= 2;\n nextStepCell.setValue(\n highestBlockHeight === settings['Total Elements'] * 2\n ? 'NONE'\n : 'FLIP_LOCAL'\n );\n nextBlockHeightCell.setValue(\n highestBlockHeight === settings['Total Elements'] * 2\n ? 0\n : highestBlockHeight\n );\n } else {\n nextStepCell.setValue('DISPERSE_LOCAL');\n }\n commandEncoder.copyBufferToBuffer(\n elementsOutputBuffer,\n 0,\n elementsStagingBuffer,\n 0,\n elementsBufferSize\n );\n }\n device.queue.submit([commandEncoder.finish()]);\n\n if (settings.executeStep) {\n // Copy GPU element data to CPU\n await elementsStagingBuffer.mapAsync(\n GPUMapMode.READ,\n 0,\n elementsBufferSize\n );\n const copyElementsBuffer = elementsStagingBuffer.getMappedRange(\n 0,\n elementsBufferSize\n );\n // Get correct range of data from CPU copy of GPU Data\n const elementsData = copyElementsBuffer.slice(\n 0,\n Uint32Array.BYTES_PER_ELEMENT * settings['Total Elements']\n );\n // Extract data\n const elementsOutput = new Uint32Array(elementsData);\n elementsStagingBuffer.unmap();\n elements = elementsOutput;\n setSwappedElement();\n }\n settings.executeStep = false;\n requestAnimationFrame(frame);\n }\n requestAnimationFrame(frame);\n }\n).then((resultInit) => (init = resultInit));\n\nconst bitonicSortExample: () => JSX.Element = () =>\n makeSample({\n name: 'Bitonic Sort',\n description:\n \"A naive bitonic sort algorithm executed on the GPU, based on tgfrerer's implementation at poniesandlight.co.uk/reflect/bitonic_merge_sort/. Each invocation of the bitonic sort shader dispatches a workgroup containing elements/2 threads. The GUI's Execution Information folder contains information about the sort's current state. The visualizer displays the sort's results as colored cells sorted from brightest to darkest.\",\n init,\n gui: true,\n sources: [\n {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n },\n BitonicDisplayRenderer.sourceInfo,\n {\n name: '../../../shaders/fullscreenTexturedQuad.vert.wgsl',\n contents: fullscreenTexturedQuad,\n },\n {\n name: './bitonicDisplay.frag.wgsl',\n contents: bitonicDisplay,\n },\n {\n name: './bitonicCompute.frag.wgsl',\n contents: NaiveBitonicCompute(16),\n },\n ],\n filename: __filename,\n });\n\nexport default bitonicSortExample;\n"},p.sourceInfo,{name:"../../../shaders/fullscreenTexturedQuad.vert.wgsl",contents:s.Z},{name:"./bitonicDisplay.frag.wgsl",contents:c},{name:"./bitonicCompute.frag.wgsl",contents:m(16)}],filename:h});var g=f},9147:function(e){e.exports={canvasContainer:"SampleLayout_canvasContainer__zRR_l",sourceFileNav:"SampleLayout_sourceFileNav__ml48P",sourceFileContainer:"SampleLayout_sourceFileContainer__3s84x"}},134:function(e,n){"use strict";n.Z="@group(0) @binding(0) var mySampler : sampler;\n@group(0) @binding(1) var myTexture : texture_2d;\n\nstruct VertexOutput {\n @builtin(position) Position : vec4,\n @location(0) fragUV : vec2,\n}\n\n@vertex\nfn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {\n const pos = array(\n vec2( 1.0, 1.0),\n vec2( 1.0, -1.0),\n vec2(-1.0, -1.0),\n vec2( 1.0, 1.0),\n vec2(-1.0, -1.0),\n vec2(-1.0, 1.0),\n );\n\n const uv = array(\n vec2(1.0, 0.0),\n vec2(1.0, 1.0),\n vec2(0.0, 1.0),\n vec2(1.0, 0.0),\n vec2(0.0, 1.0),\n vec2(0.0, 0.0),\n );\n\n var output : VertexOutput;\n output.Position = vec4(pos[VertexIndex], 0.0, 1.0);\n output.fragUV = uv[VertexIndex];\n return output;\n}\n\n@fragment\nfn frag_main(@location(0) fragUV : vec2) -> @location(0) vec4 {\n return textureSample(myTexture, mySampler, fragUV);\n}\n"}}]); \ No newline at end of file diff --git a/_next/static/chunks/112.964d8a7e473b18ae.js b/_next/static/chunks/112.964d8a7e473b18ae.js new file mode 100644 index 00000000..3e59ea78 --- /dev/null +++ b/_next/static/chunks/112.964d8a7e473b18ae.js @@ -0,0 +1 @@ +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[112],{5671:function(e,n,t){"use strict";t.d(n,{Tl:function(){return c},hu:function(){return p}});var r=t(5893),i=t(9008),o=t.n(i),a=t(1163),s=t(7294),l=t(9147),u=t.n(l);t(7319);let d=e=>{let n=(0,s.useRef)(null),i=(0,s.useMemo)(()=>e.sources.map(e=>{let{name:n,contents:i}=e;return{name:n,...function(e){let n;let i=null;{i=document.createElement("div");let o=t(4631);n=o(i,{lineNumbers:!0,lineWrapping:!0,theme:"monokai",readOnly:!0})}return{Container:function(t){return(0,r.jsx)("div",{...t,children:(0,r.jsx)("div",{ref(t){i&&t&&(t.appendChild(i),n.setOption("value",e))}})})}}}(i)}}),e.sources),l=(0,s.useRef)(null),d=(0,s.useMemo)(()=>{if(e.gui){let n=t(4376);return new n.GUI({autoPlace:!1})}},[]),c=(0,s.useRef)(null),p=(0,s.useMemo)(()=>{if(e.stats){let n=t(2792);return new n}},[]),m=(0,a.useRouter)(),h=m.asPath.match(/#([a-zA-Z0-9\.\/]+)/),[f,g]=(0,s.useState)(null),[E,v]=(0,s.useState)(null);return(0,s.useEffect)(()=>{if(h?v(h[1]):v(i[0].name),d&&l.current)for(l.current.appendChild(d.domElement);d.__controllers.length>0;)d.__controllers[0].remove();p&&c.current&&(p.dom.style.position="absolute",p.showPanel(1),c.current.appendChild(p.dom));let t={active:!0},r=()=>{t.active=!1};try{let o=n.current;if(!o)throw Error("The canvas is not available");let a=e.init({canvas:o,pageState:t,gui:d,stats:p});a instanceof Promise&&a.catch(e=>{console.error(e),g(e)})}catch(s){console.error(s),g(s)}return r},[]),(0,r.jsxs)("main",{children:[(0,r.jsxs)(o(),{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"\n .CodeMirror {\n height: auto !important;\n margin: 1em 0;\n }\n\n .CodeMirror-scroll {\n height: auto !important;\n overflow: visible !important;\n }\n "}}),(0,r.jsx)("title",{children:"".concat(e.name," - WebGPU Samples")}),(0,r.jsx)("meta",{name:"description",content:e.description}),(0,r.jsx)("meta",{httpEquiv:"origin-trial",content:e.originTrial})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("h1",{children:e.name}),(0,r.jsx)("a",{target:"_blank",rel:"noreferrer",href:"https://github.com/".concat("webgpu/webgpu-samples","/tree/main/").concat(e.filename),children:"See it on Github!"}),(0,r.jsx)("p",{children:e.description}),f?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("p",{children:"Something went wrong. Do your browser and device support WebGPU?"}),(0,r.jsx)("p",{children:"".concat(f)})]}):null]}),(0,r.jsxs)("div",{className:u().canvasContainer,children:[(0,r.jsx)("div",{style:{position:"absolute",left:10},ref:c}),(0,r.jsx)("div",{style:{position:"absolute",right:10},ref:l}),(0,r.jsx)("canvas",{ref:n})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("nav",{className:u().sourceFileNav,children:(0,r.jsx)("ul",{children:i.map((e,n)=>(0,r.jsx)("li",{children:(0,r.jsx)("a",{href:"#".concat(e.name),"data-active":E==e.name,onClick(){v(e.name)},children:e.name})},n))})}),i.map((e,n)=>(0,r.jsx)(e.Container,{className:u().sourceFileContainer,"data-active":E==e.name},n))]})]})},c=e=>(0,r.jsx)(d,{...e});function p(e,n){if(!e)throw Error(n)}},8112:function(e,n,t){"use strict";let r;t.r(n),t.d(n,{default:function(){return g}});var i,o,a=t(5671),s=t(134);let l=(e,n,t,r,i,o,a)=>{let s=[];for(let l=0;l{let n=async n=>{let{canvas:t,pageState:r,gui:i,stats:o}=n,a=await navigator.gpu.requestAdapter(),s=await a.requestDevice();if(!r.active)return;let l=t.getContext("webgpu"),u=window.devicePixelRatio||1;t.width=t.clientWidth*u,t.height=t.clientHeight*u;let d=navigator.gpu.getPreferredCanvasFormat();l.configure({device:s,format:d,alphaMode:"premultiplied"}),e({canvas:t,pageState:r,gui:i,device:s,context:l,presentationFormat:d,stats:o})};return n};class d{executeRun(e,n,t,r){let i=e.beginRenderPass(n);i.setPipeline(t);for(let o=0;o{this.bindGroupMap[r[n]]=e}),this.pipeline=super.create2DRenderPipeline(e,o,[s.bindGroupLayout,this.computeBGDescript.bindGroupLayout],c,n),this.switchBindGroup=e=>{this.currentBindGroup=this.bindGroupMap[e],this.currentBindGroupName=e},this.setArguments=n=>{super.setUniformArguments(e,a,n,["width","height"])}}}p.sourceInfo={name:"src/sample/bitonicSort/bitonicDisplay.ts".substring(23),contents:"import {\n BindGroupsObjectsAndLayout,\n createBindGroupDescriptor,\n Base2DRendererClass,\n} from './utils';\n\nimport bitonicDisplay from './bitonicDisplay.frag.wgsl';\n\ninterface BitonicDisplayRenderArgs {\n width: number;\n height: number;\n}\n\nexport default class BitonicDisplayRenderer extends Base2DRendererClass {\n static sourceInfo = {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n };\n\n switchBindGroup: (name: string) => void;\n setArguments: (args: BitonicDisplayRenderArgs) => void;\n computeBGDescript: BindGroupsObjectsAndLayout;\n\n constructor(\n device: GPUDevice,\n presentationFormat: GPUTextureFormat,\n renderPassDescriptor: GPURenderPassDescriptor,\n bindGroupNames: string[],\n computeBGDescript: BindGroupsObjectsAndLayout,\n label: string\n ) {\n super();\n this.renderPassDescriptor = renderPassDescriptor;\n this.computeBGDescript = computeBGDescript;\n\n const uniformBuffer = device.createBuffer({\n size: Float32Array.BYTES_PER_ELEMENT * 2,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n });\n\n const bgDescript = createBindGroupDescriptor(\n [0],\n [GPUShaderStage.FRAGMENT],\n ['buffer'],\n [{ type: 'uniform' }],\n [[{ buffer: uniformBuffer }]],\n label,\n device\n );\n\n this.currentBindGroup = bgDescript.bindGroups[0];\n this.currentBindGroupName = bindGroupNames[0];\n\n this.bindGroupMap = {};\n\n bgDescript.bindGroups.forEach((bg, idx) => {\n this.bindGroupMap[bindGroupNames[idx]] = bg;\n });\n\n this.pipeline = super.create2DRenderPipeline(\n device,\n label,\n [bgDescript.bindGroupLayout, this.computeBGDescript.bindGroupLayout],\n bitonicDisplay,\n presentationFormat\n );\n\n this.switchBindGroup = (name: string) => {\n this.currentBindGroup = this.bindGroupMap[name];\n this.currentBindGroupName = name;\n };\n\n this.setArguments = (args: BitonicDisplayRenderArgs) => {\n super.setUniformArguments(device, uniformBuffer, args, [\n 'width',\n 'height',\n ]);\n };\n }\n\n startRun(commandEncoder: GPUCommandEncoder, args: BitonicDisplayRenderArgs) {\n this.setArguments(args);\n super.executeRun(commandEncoder, this.renderPassDescriptor, this.pipeline, [\n this.currentBindGroup,\n this.computeBGDescript.bindGroups[0],\n ]);\n }\n}\n"};let m=e=>((e%2!=0||e>256)&&(e=256),"\n\nstruct Uniforms {\n width: f32,\n height: f32,\n algo: u32,\n blockHeight: u32,\n}\n\n// Create local workgroup data that can contain all elements\n\nvar local_data: array;\n\n//Compare and swap values in local_data\nfn compare_and_swap(idx_before: u32, idx_after: u32) {\n //idx_before should always be < idx_after\n if (local_data[idx_after] < local_data[idx_before]) {\n var temp: u32 = local_data[idx_before];\n local_data[idx_before] = local_data[idx_after];\n local_data[idx_after] = temp;\n }\n return;\n}\n\n// thread_id goes from 0 to threadsPerWorkgroup\nfn prepare_flip(thread_id: u32, block_height: u32) {\n let q: u32 = ((2 * thread_id) / block_height) * block_height;\n let half_height = block_height / 2;\n var idx: vec2 = vec2(\n thread_id % half_height, block_height - (thread_id % half_height) - 1,\n );\n idx.x += q;\n idx.y += q;\n compare_and_swap(idx.x, idx.y);\n}\n\nfn prepare_disperse(thread_id: u32, block_height: u32) {\n var q: u32 = ((2 * thread_id) / block_height) * block_height;\n let half_height = block_height / 2;\n var idx: vec2 = vec2(\n thread_id % half_height, (thread_id % half_height) + half_height\n );\n idx.x += q;\n idx.y += q;\n compare_and_swap(idx.x, idx.y);\n}\n\n@group(0) @binding(0) var input_data: array;\n@group(0) @binding(1) var output_data: array;\n@group(0) @binding(2) var uniforms: Uniforms;\n\n// Our compute shader will execute specified # of threads or elements / 2 threads\n@compute @workgroup_size(").concat(e,", 1, 1)\nfn computeMain(\n @builtin(global_invocation_id) global_id: vec3,\n @builtin(local_invocation_id) local_id: vec3,\n) {\n //Each thread will populate the workgroup data... (1 thread for every 2 elements)\n local_data[local_id.x * 2] = input_data[local_id.x * 2];\n local_data[local_id.x * 2 + 1] = input_data[local_id.x * 2 + 1];\n\n //...and wait for each other to finish their own bit of data population.\n workgroupBarrier();\n\n var num_elements = uniforms.width * uniforms.height;\n\n switch uniforms.algo {\n case 1: { //Local Flip\n prepare_flip(local_id.x, uniforms.blockHeight);\n }\n case 2: { //Local Disperse\n prepare_disperse(local_id.x, uniforms.blockHeight);\n }\n default: { \n \n }\n }\n\n //Ensure that all threads have swapped their own regions of data\n workgroupBarrier();\n\n //Repopulate global data with local data\n output_data[local_id.x * 2] = local_data[local_id.x * 2];\n output_data[local_id.x * 2 + 1] = local_data[local_id.x * 2 + 1];\n\n}"));var h="src/sample/bitonicSort/main.ts";(i=o||(o={}))[i.NONE=0]="NONE",i[i.FLIP_LOCAL=1]="FLIP_LOCAL",i[i.DISPERSE_LOCAL=2]="DISPERSE_LOCAL",i[i.FLIP_DISPERSE_LOCAL=3]="FLIP_DISPERSE_LOCAL",u(async e=>{let{pageState:n,device:t,gui:r,presentationFormat:i,context:a,canvas:s}=e,u=t.limits.maxComputeWorkgroupSizeX,d=[];for(let c=2*u;c>=4;c/=2)d.push(c);let h={"Total Elements":16,"Grid Width":4,"Grid Height":4,"Total Threads":8,hoveredElement:0,swappedElement:1,"Prev Step":"NONE","Next Step":"FLIP_LOCAL","Prev Swap Span":0,"Next Swap Span":2,workLoads:1,executeStep:!1,"Randomize Values"(){},"Execute Sort Step"(){},"Log Elements"(){},"Complete Sort"(){},sortSpeed:200},f=new Uint32Array(Array.from({length:h["Total Elements"]},(e,n)=>n)),g=512*Float32Array.BYTES_PER_ELEMENT,E=t.createBuffer({size:g,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_DST}),v=t.createBuffer({size:g,usage:GPUBufferUsage.STORAGE|GPUBufferUsage.COPY_SRC}),S=t.createBuffer({size:g,usage:GPUBufferUsage.MAP_READ|GPUBufferUsage.COPY_DST}),x=t.createBuffer({size:4*Float32Array.BYTES_PER_ELEMENT,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),b=l([0,1,2],[GPUShaderStage.COMPUTE|GPUShaderStage.FRAGMENT,GPUShaderStage.COMPUTE,GPUShaderStage.COMPUTE],["buffer","buffer","buffer"],[{type:"read-only-storage"},{type:"storage"},{type:"uniform"}],[[{buffer:E},{buffer:v},{buffer:x}]],"NaiveBitonicSort",t),y=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[b.bindGroupLayout]}),compute:{module:t.createShaderModule({code:m(h["Total Threads"])}),entryPoint:"computeMain"}}),_={colorAttachments:[{view:void 0,clearValue:{r:.1,g:.4,b:.5,a:1},loadOp:"clear",storeOp:"store"}]},w=new p(t,i,_,["default"],b,"BitonicDisplay"),P=()=>{U.setValue(h["Total Elements"]/2);let e=Math.sqrt(h["Total Elements"])%2==0?Math.floor(Math.sqrt(h["Total Elements"])):Math.floor(Math.sqrt(h["Total Elements"]/2)),n=h["Total Elements"]/e;V.setValue(e),z.setValue(n),F.setValue("NONE"),k.setValue("FLIP_LOCAL"),M.setValue(0),H.setValue(2),q=2},B=()=>{let e=f.length;for(;0!==e;){let n=Math.floor(Math.random()*e);e-=1,[f[e],f[n]]=[f[n],f[e]]}},G=()=>{f=new Uint32Array(Array.from({length:h["Total Elements"]},(e,n)=>n)),P(),y=t.createComputePipeline({layout:t.createPipelineLayout({bindGroupLayouts:[b.bindGroupLayout]}),compute:{module:t.createShaderModule({code:m(h["Total Elements"]/2)}),entryPoint:"computeMain"}}),B(),q=2};B();let C=()=>{let e;switch(h["Next Step"]){case"FLIP_LOCAL":{let n=h["Next Swap Span"],t=Math.floor(h.hoveredElement/n)+1,r=h.hoveredElement%n;e=n*t-r-1,R.setValue(e)}break;case"DISPERSE_LOCAL":{let i=h["Next Swap Span"],o=i/2;e=h.hoveredElement%i{null!==I&&(clearInterval(I),I=null)},T=()=>{I=setInterval(()=>{"NONE"===h["Next Step"]&&(clearInterval(I),I=null),h.executeStep=!0,C()},h.sortSpeed)};r.add(h,"Total Elements",d).onChange(()=>{L(),G()});let U=r.add(h,"Total Threads"),D=r.addFolder("Sort Controls");D.add(h,"Execute Sort Step").onChange(()=>{L(),h.executeStep=!0}),D.add(h,"Randomize Values").onChange(()=>{L(),B(),P()}),D.add(h,"Log Elements").onChange(()=>console.log(f)),D.add(h,"Complete Sort").onChange(T),D.open();let A=r.addFolder("Hover Information"),N=A.add(h,"hoveredElement").onChange(C),R=A.add(h,"swappedElement"),O=r.addFolder("Execution Information"),F=O.add(h,"Prev Step"),k=O.add(h,"Next Step"),M=O.add(h,"Prev Swap Span"),H=O.add(h,"Next Swap Span"),V=O.add(h,"Grid Width"),z=O.add(h,"Grid Height"),W=document.getElementsByClassName("cr function");for(let j=0;j{let n=s.getBoundingClientRect().width,t=s.getBoundingClientRect().height,r=[n/h["Grid Width"],t/h["Grid Height"]],i=Math.floor(e.offsetX/r[0]),o=h["Grid Height"]-1-Math.floor(e.offsetY/r[1]);N.setValue(o*h["Grid Width"]+i),h.hoveredElement=o*h["Grid Width"]+i}),F.domElement.style.pointerEvents="none",M.domElement.style.pointerEvents="none",k.domElement.style.pointerEvents="none",H.domElement.style.pointerEvents="none",U.domElement.style.pointerEvents="none",V.domElement.style.pointerEvents="none",z.domElement.style.pointerEvents="none";let q=2;async function Y(){if(!n.active)return;t.queue.writeBuffer(E,0,f.buffer,f.byteOffset,f.byteLength);let e=new Float32Array([h["Grid Width"],h["Grid Height"]]),r=new Uint32Array([o[h["Next Step"]],h["Next Swap Span"]]);t.queue.writeBuffer(x,0,e.buffer,e.byteOffset,e.byteLength),t.queue.writeBuffer(x,8,r),_.colorAttachments[0].view=a.getCurrentTexture().createView();let i=t.createCommandEncoder();if(w.startRun(i,{width:h["Grid Width"],height:h["Grid Height"]}),h.executeStep&&q!==2*h["Total Elements"]){let s=i.beginComputePass();s.setPipeline(y),s.setBindGroup(0,b.bindGroups[0]),s.dispatchWorkgroups(1),s.end(),F.setValue(h["Next Step"]),M.setValue(h["Next Swap Span"]),H.setValue(h["Next Swap Span"]/2),1===h["Next Swap Span"]?(q*=2,k.setValue(q===2*h["Total Elements"]?"NONE":"FLIP_LOCAL"),H.setValue(q===2*h["Total Elements"]?0:q)):k.setValue("DISPERSE_LOCAL"),i.copyBufferToBuffer(v,0,S,0,g)}if(t.queue.submit([i.finish()]),h.executeStep){await S.mapAsync(GPUMapMode.READ,0,g);let l=S.getMappedRange(0,g),u=l.slice(0,Uint32Array.BYTES_PER_ELEMENT*h["Total Elements"]),d=new Uint32Array(u);S.unmap(),f=d,C()}h.executeStep=!1,requestAnimationFrame(Y)}requestAnimationFrame(Y)}).then(e=>r=e);let f=()=>(0,a.Tl)({name:"Bitonic Sort",description:"A naive bitonic sort algorithm executed on the GPU, based on tgfrerer's implementation at poniesandlight.co.uk/reflect/bitonic_merge_sort/. Each invocation of the bitonic sort shader dispatches a workgroup containing elements/2 threads. The GUI's Execution Information folder contains information about the sort's current state. The visualizer displays the sort's results as colored cells sorted from brightest to darkest.",init:r,gui:!0,sources:[{name:h.substring(23),contents:"import { makeSample, SampleInit } from '../../components/SampleLayout';\nimport { SampleInitFactoryWebGPU } from './utils';\nimport { createBindGroupDescriptor } from './utils';\nimport BitonicDisplayRenderer from './bitonicDisplay';\nimport bitonicDisplay from './bitonicDisplay.frag.wgsl';\nimport { NaiveBitonicCompute } from './computeShader';\nimport fullscreenTexturedQuad from '../../shaders/fullscreenTexturedQuad.wgsl';\n\n// Type of step that will be executed in our shader\nenum StepEnum {\n NONE = 0,\n FLIP_LOCAL = 1,\n DISPERSE_LOCAL = 2,\n FLIP_DISPERSE_LOCAL = 3,\n}\n\n// String access to StepEnum\ntype StepType =\n | 'NONE'\n | 'FLIP_LOCAL'\n | 'DISPERSE_LOCAL'\n | 'FLIP_DISPERSE_LOCAL';\n\n// Gui settings object\ninterface SettingsInterface {\n 'Total Elements': number;\n 'Grid Width': number;\n 'Grid Height': number;\n 'Total Threads': number;\n hoveredElement: number;\n swappedElement: number;\n 'Prev Step': StepType;\n 'Next Step': StepType;\n 'Prev Swap Span': number;\n 'Next Swap Span': number;\n workLoads: number;\n executeStep: boolean;\n 'Randomize Values': () => void;\n 'Execute Sort Step': () => void;\n 'Log Elements': () => void;\n 'Complete Sort': () => void;\n sortSpeed: number;\n}\n\nlet init: SampleInit;\nSampleInitFactoryWebGPU(\n async ({ pageState, device, gui, presentationFormat, context, canvas }) => {\n const maxWorkgroupsX = device.limits.maxComputeWorkgroupSizeX;\n\n const totalElementLengths = [];\n for (let i = maxWorkgroupsX * 2; i >= 4; i /= 2) {\n totalElementLengths.push(i);\n }\n\n const settings: SettingsInterface = {\n // number of cellElements. Must equal gridWidth * gridHeight and 'Total Threads' * 2\n 'Total Elements': 16,\n // width of screen in cells.\n 'Grid Width': 4,\n // height of screen in cells\n 'Grid Height': 4,\n // number of threads to execute in a workgroup ('Total Threads', 1, 1)\n 'Total Threads': 16 / 2,\n // currently highlighted element\n hoveredElement: 0,\n // element the hoveredElement just swapped with,\n swappedElement: 1,\n // Previously executed step\n 'Prev Step': 'NONE',\n // Next step to execute\n 'Next Step': 'FLIP_LOCAL',\n // Max thread span of previous block\n 'Prev Swap Span': 0,\n // Max thread span of next block\n 'Next Swap Span': 2,\n // workloads to dispatch per frame,\n workLoads: 1,\n // Whether we will dispatch a workload this frame\n executeStep: false,\n 'Randomize Values': () => {\n return;\n },\n 'Execute Sort Step': () => {\n return;\n },\n 'Log Elements': () => {\n return;\n },\n 'Complete Sort': () => {\n return;\n },\n sortSpeed: 200,\n };\n\n // Initialize initial elements array\n let elements = new Uint32Array(\n Array.from({ length: settings['Total Elements'] }, (_, i) => i)\n );\n\n // Initialize elementsBuffer and elementsStagingBuffer\n const elementsBufferSize = Float32Array.BYTES_PER_ELEMENT * 512;\n // Initialize input, output, staging buffers\n const elementsInputBuffer = device.createBuffer({\n size: elementsBufferSize,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_DST,\n });\n const elementsOutputBuffer = device.createBuffer({\n size: elementsBufferSize,\n usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC,\n });\n const elementsStagingBuffer = device.createBuffer({\n size: elementsBufferSize,\n usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST,\n });\n\n // Create uniform buffer for compute shader\n const computeUniformsBuffer = device.createBuffer({\n // width, height, blockHeight, algo\n size: Float32Array.BYTES_PER_ELEMENT * 4,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n });\n\n const computeBGDescript = createBindGroupDescriptor(\n [0, 1, 2],\n [\n GPUShaderStage.COMPUTE | GPUShaderStage.FRAGMENT,\n GPUShaderStage.COMPUTE,\n GPUShaderStage.COMPUTE,\n ],\n ['buffer', 'buffer', 'buffer'],\n [{ type: 'read-only-storage' }, { type: 'storage' }, { type: 'uniform' }],\n [\n [\n { buffer: elementsInputBuffer },\n { buffer: elementsOutputBuffer },\n { buffer: computeUniformsBuffer },\n ],\n ],\n 'NaiveBitonicSort',\n device\n );\n\n let computePipeline = device.createComputePipeline({\n layout: device.createPipelineLayout({\n bindGroupLayouts: [computeBGDescript.bindGroupLayout],\n }),\n compute: {\n module: device.createShaderModule({\n code: NaiveBitonicCompute(settings['Total Threads']),\n }),\n entryPoint: 'computeMain',\n },\n });\n\n // Create bitonic debug renderer\n const renderPassDescriptor: GPURenderPassDescriptor = {\n colorAttachments: [\n {\n view: undefined, // Assigned later\n\n clearValue: { r: 0.1, g: 0.4, b: 0.5, a: 1.0 },\n loadOp: 'clear',\n storeOp: 'store',\n },\n ],\n };\n\n const bitonicDisplayRenderer = new BitonicDisplayRenderer(\n device,\n presentationFormat,\n renderPassDescriptor,\n ['default'],\n computeBGDescript,\n 'BitonicDisplay'\n );\n\n const resetExecutionInformation = () => {\n totalThreadsCell.setValue(settings['Total Elements'] / 2);\n\n // Get new width and height of screen display in cells\n const newCellWidth =\n Math.sqrt(settings['Total Elements']) % 2 === 0\n ? Math.floor(Math.sqrt(settings['Total Elements']))\n : Math.floor(Math.sqrt(settings['Total Elements'] / 2));\n const newCellHeight = settings['Total Elements'] / newCellWidth;\n gridWidthCell.setValue(newCellWidth);\n gridHeightCell.setValue(newCellHeight);\n\n // Set prevStep to None (restart) and next step to FLIP\n prevStepCell.setValue('NONE');\n nextStepCell.setValue('FLIP_LOCAL');\n\n // Reset block heights\n prevBlockHeightCell.setValue(0);\n nextBlockHeightCell.setValue(2);\n highestBlockHeight = 2;\n };\n\n const randomizeElementArray = () => {\n let currentIndex = elements.length;\n // While there are elements to shuffle\n while (currentIndex !== 0) {\n // Pick a remaining element\n const randomIndex = Math.floor(Math.random() * currentIndex);\n currentIndex -= 1;\n [elements[currentIndex], elements[randomIndex]] = [\n elements[randomIndex],\n elements[currentIndex],\n ];\n }\n };\n\n const resizeElementArray = () => {\n // Recreate elements array with new length\n elements = new Uint32Array(\n Array.from({ length: settings['Total Elements'] }, (_, i) => i)\n );\n\n resetExecutionInformation();\n\n // Create new shader invocation with workgroupSize that reflects number of threads\n computePipeline = device.createComputePipeline({\n layout: device.createPipelineLayout({\n bindGroupLayouts: [computeBGDescript.bindGroupLayout],\n }),\n compute: {\n module: device.createShaderModule({\n code: NaiveBitonicCompute(settings['Total Elements'] / 2),\n }),\n entryPoint: 'computeMain',\n },\n });\n // Randomize array elements\n randomizeElementArray();\n highestBlockHeight = 2;\n };\n\n randomizeElementArray();\n\n const setSwappedElement = () => {\n let swappedIndex: number;\n switch (settings['Next Step']) {\n case 'FLIP_LOCAL':\n {\n const blockHeight = settings['Next Swap Span'];\n const p2 = Math.floor(settings.hoveredElement / blockHeight) + 1;\n const p3 = settings.hoveredElement % blockHeight;\n swappedIndex = blockHeight * p2 - p3 - 1;\n swappedElementCell.setValue(swappedIndex);\n }\n break;\n case 'DISPERSE_LOCAL':\n {\n const blockHeight = settings['Next Swap Span'];\n const halfHeight = blockHeight / 2;\n swappedIndex =\n settings.hoveredElement % blockHeight < halfHeight\n ? settings.hoveredElement + halfHeight\n : settings.hoveredElement - halfHeight;\n swappedElementCell.setValue(swappedIndex);\n }\n break;\n case 'NONE': {\n swappedIndex = settings.hoveredElement;\n swappedElementCell.setValue(swappedIndex);\n }\n default:\n {\n swappedIndex = settings.hoveredElement;\n swappedElementCell.setValue(swappedIndex);\n }\n break;\n }\n };\n\n let completeSortIntervalID: ReturnType | null = null;\n const endSortInterval = () => {\n if (completeSortIntervalID !== null) {\n clearInterval(completeSortIntervalID);\n completeSortIntervalID = null;\n }\n };\n const startSortInterval = () => {\n completeSortIntervalID = setInterval(() => {\n if (settings['Next Step'] === 'NONE') {\n clearInterval(completeSortIntervalID);\n completeSortIntervalID = null;\n }\n settings.executeStep = true;\n setSwappedElement();\n }, settings.sortSpeed);\n };\n\n // At top level, basic information about the number of elements sorted and the number of threads\n // deployed per workgroup.\n gui.add(settings, 'Total Elements', totalElementLengths).onChange(() => {\n endSortInterval();\n resizeElementArray();\n });\n const totalThreadsCell = gui.add(settings, 'Total Threads');\n\n // Folder with functions that control the execution of the sort\n const controlFolder = gui.addFolder('Sort Controls');\n controlFolder.add(settings, 'Execute Sort Step').onChange(() => {\n endSortInterval();\n settings.executeStep = true;\n });\n controlFolder.add(settings, 'Randomize Values').onChange(() => {\n endSortInterval();\n randomizeElementArray();\n resetExecutionInformation();\n });\n controlFolder\n .add(settings, 'Log Elements')\n .onChange(() => console.log(elements));\n controlFolder.add(settings, 'Complete Sort').onChange(startSortInterval);\n controlFolder.open();\n\n // Folder with indexes of the hovered element\n const hoverFolder = gui.addFolder('Hover Information');\n const hoveredElementCell = hoverFolder\n .add(settings, 'hoveredElement')\n .onChange(setSwappedElement);\n const swappedElementCell = hoverFolder.add(settings, 'swappedElement');\n\n // Additional Information about the execution state of the sort\n const executionInformationFolder = gui.addFolder('Execution Information');\n const prevStepCell = executionInformationFolder.add(settings, 'Prev Step');\n const nextStepCell = executionInformationFolder.add(settings, 'Next Step');\n const prevBlockHeightCell = executionInformationFolder.add(\n settings,\n 'Prev Swap Span'\n );\n const nextBlockHeightCell = executionInformationFolder.add(\n settings,\n 'Next Swap Span'\n );\n const gridWidthCell = executionInformationFolder.add(\n settings,\n 'Grid Width'\n );\n const gridHeightCell = executionInformationFolder.add(\n settings,\n 'Grid Height'\n );\n\n // Adjust styles of Function List Elements within GUI\n const liFunctionElements = document.getElementsByClassName('cr function');\n for (let i = 0; i < liFunctionElements.length; i++) {\n (liFunctionElements[i].children[0] as HTMLElement).style.display = 'flex';\n (liFunctionElements[i].children[0] as HTMLElement).style.justifyContent =\n 'center';\n (\n liFunctionElements[i].children[0].children[1] as HTMLElement\n ).style.position = 'absolute';\n }\n\n canvas.addEventListener('mousemove', (event) => {\n const currWidth = canvas.getBoundingClientRect().width;\n const currHeight = canvas.getBoundingClientRect().height;\n const cellSize: [number, number] = [\n currWidth / settings['Grid Width'],\n currHeight / settings['Grid Height'],\n ];\n const xIndex = Math.floor(event.offsetX / cellSize[0]);\n const yIndex =\n settings['Grid Height'] - 1 - Math.floor(event.offsetY / cellSize[1]);\n hoveredElementCell.setValue(yIndex * settings['Grid Width'] + xIndex);\n settings.hoveredElement = yIndex * settings['Grid Width'] + xIndex;\n });\n\n // Deactivate interaction with select GUI elements\n prevStepCell.domElement.style.pointerEvents = 'none';\n prevBlockHeightCell.domElement.style.pointerEvents = 'none';\n nextStepCell.domElement.style.pointerEvents = 'none';\n nextBlockHeightCell.domElement.style.pointerEvents = 'none';\n totalThreadsCell.domElement.style.pointerEvents = 'none';\n gridWidthCell.domElement.style.pointerEvents = 'none';\n gridHeightCell.domElement.style.pointerEvents = 'none';\n\n let highestBlockHeight = 2;\n\n async function frame() {\n if (!pageState.active) return;\n\n // Write elements buffer\n device.queue.writeBuffer(\n elementsInputBuffer,\n 0,\n elements.buffer,\n elements.byteOffset,\n elements.byteLength\n );\n\n const dims = new Float32Array([\n settings['Grid Width'],\n settings['Grid Height'],\n ]);\n const stepDetails = new Uint32Array([\n StepEnum[settings['Next Step']],\n settings['Next Swap Span'],\n ]);\n device.queue.writeBuffer(\n computeUniformsBuffer,\n 0,\n dims.buffer,\n dims.byteOffset,\n dims.byteLength\n );\n\n device.queue.writeBuffer(computeUniformsBuffer, 8, stepDetails);\n\n renderPassDescriptor.colorAttachments[0].view = context\n .getCurrentTexture()\n .createView();\n\n const commandEncoder = device.createCommandEncoder();\n bitonicDisplayRenderer.startRun(commandEncoder, {\n width: settings['Grid Width'],\n height: settings['Grid Height'],\n });\n if (\n settings.executeStep &&\n highestBlockHeight !== settings['Total Elements'] * 2\n ) {\n const computePassEncoder = commandEncoder.beginComputePass();\n computePassEncoder.setPipeline(computePipeline);\n computePassEncoder.setBindGroup(0, computeBGDescript.bindGroups[0]);\n computePassEncoder.dispatchWorkgroups(1);\n computePassEncoder.end();\n\n prevStepCell.setValue(settings['Next Step']);\n prevBlockHeightCell.setValue(settings['Next Swap Span']);\n nextBlockHeightCell.setValue(settings['Next Swap Span'] / 2);\n if (settings['Next Swap Span'] === 1) {\n highestBlockHeight *= 2;\n nextStepCell.setValue(\n highestBlockHeight === settings['Total Elements'] * 2\n ? 'NONE'\n : 'FLIP_LOCAL'\n );\n nextBlockHeightCell.setValue(\n highestBlockHeight === settings['Total Elements'] * 2\n ? 0\n : highestBlockHeight\n );\n } else {\n nextStepCell.setValue('DISPERSE_LOCAL');\n }\n commandEncoder.copyBufferToBuffer(\n elementsOutputBuffer,\n 0,\n elementsStagingBuffer,\n 0,\n elementsBufferSize\n );\n }\n device.queue.submit([commandEncoder.finish()]);\n\n if (settings.executeStep) {\n // Copy GPU element data to CPU\n await elementsStagingBuffer.mapAsync(\n GPUMapMode.READ,\n 0,\n elementsBufferSize\n );\n const copyElementsBuffer = elementsStagingBuffer.getMappedRange(\n 0,\n elementsBufferSize\n );\n // Get correct range of data from CPU copy of GPU Data\n const elementsData = copyElementsBuffer.slice(\n 0,\n Uint32Array.BYTES_PER_ELEMENT * settings['Total Elements']\n );\n // Extract data\n const elementsOutput = new Uint32Array(elementsData);\n elementsStagingBuffer.unmap();\n elements = elementsOutput;\n setSwappedElement();\n }\n settings.executeStep = false;\n requestAnimationFrame(frame);\n }\n requestAnimationFrame(frame);\n }\n).then((resultInit) => (init = resultInit));\n\nconst bitonicSortExample: () => JSX.Element = () =>\n makeSample({\n name: 'Bitonic Sort',\n description:\n \"A naive bitonic sort algorithm executed on the GPU, based on tgfrerer's implementation at poniesandlight.co.uk/reflect/bitonic_merge_sort/. Each invocation of the bitonic sort shader dispatches a workgroup containing elements/2 threads. The GUI's Execution Information folder contains information about the sort's current state. The visualizer displays the sort's results as colored cells sorted from brightest to darkest.\",\n init,\n gui: true,\n sources: [\n {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n },\n BitonicDisplayRenderer.sourceInfo,\n {\n name: '../../../shaders/fullscreenTexturedQuad.vert.wgsl',\n contents: fullscreenTexturedQuad,\n },\n {\n name: './bitonicDisplay.frag.wgsl',\n contents: bitonicDisplay,\n },\n {\n name: './bitonicCompute.frag.wgsl',\n contents: NaiveBitonicCompute(16),\n },\n ],\n filename: __filename,\n });\n\nexport default bitonicSortExample;\n"},p.sourceInfo,{name:"../../../shaders/fullscreenTexturedQuad.vert.wgsl",contents:s.Z},{name:"./bitonicDisplay.frag.wgsl",contents:c},{name:"./bitonicCompute.frag.wgsl",contents:m(16)}],filename:h});var g=f},9147:function(e){e.exports={canvasContainer:"SampleLayout_canvasContainer__zRR_l",sourceFileNav:"SampleLayout_sourceFileNav__ml48P",sourceFileContainer:"SampleLayout_sourceFileContainer__3s84x"}},134:function(e,n){"use strict";n.Z="@group(0) @binding(0) var mySampler : sampler;\n@group(0) @binding(1) var myTexture : texture_2d;\n\nstruct VertexOutput {\n @builtin(position) Position : vec4,\n @location(0) fragUV : vec2,\n}\n\n@vertex\nfn vert_main(@builtin(vertex_index) VertexIndex : u32) -> VertexOutput {\n const pos = array(\n vec2( 1.0, 1.0),\n vec2( 1.0, -1.0),\n vec2(-1.0, -1.0),\n vec2( 1.0, 1.0),\n vec2(-1.0, -1.0),\n vec2(-1.0, 1.0),\n );\n\n const uv = array(\n vec2(1.0, 0.0),\n vec2(1.0, 1.0),\n vec2(0.0, 1.0),\n vec2(1.0, 0.0),\n vec2(0.0, 1.0),\n vec2(0.0, 0.0),\n );\n\n var output : VertexOutput;\n output.Position = vec4(pos[VertexIndex], 0.0, 1.0);\n output.fragUV = uv[VertexIndex];\n return output;\n}\n\n@fragment\nfn frag_main(@location(0) fragUV : vec2) -> @location(0) vec4 {\n return textureSample(myTexture, mySampler, fragUV);\n}\n"}}]); \ No newline at end of file diff --git a/_next/static/chunks/752.5364f7ac3622f2eb.js b/_next/static/chunks/752.2b99aa35a30ac9aa.js similarity index 65% rename from _next/static/chunks/752.5364f7ac3622f2eb.js rename to _next/static/chunks/752.2b99aa35a30ac9aa.js index adad2dfc..4c1b9292 100644 --- a/_next/static/chunks/752.5364f7ac3622f2eb.js +++ b/_next/static/chunks/752.2b99aa35a30ac9aa.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[752],{5671:function(e,n,t){"use strict";t.d(n,{Tl:function(){return p},hu:function(){return d}});var a=t(5893),r=t(9008),i=t.n(r),s=t(1163),o=t(7294),l=t(9147),c=t.n(l);t(7319);let u=e=>{let n=(0,o.useRef)(null),r=(0,o.useMemo)(()=>e.sources.map(e=>{let{name:n,contents:r}=e;return{name:n,...function(e){let n;let r=null;{r=document.createElement("div");let i=t(4631);n=i(r,{lineNumbers:!0,lineWrapping:!0,theme:"monokai",readOnly:!0})}return{Container:function(t){return(0,a.jsx)("div",{...t,children:(0,a.jsx)("div",{ref(t){r&&t&&(t.appendChild(r),n.setOption("value",e))}})})}}}(r)}}),e.sources),l=(0,o.useRef)(null),u=(0,o.useMemo)(()=>{if(e.gui){let n=t(4376);return new n.GUI({autoPlace:!1})}},[]),p=(0,o.useRef)(null),d=(0,o.useMemo)(()=>{if(e.stats){let n=t(2792);return new n}},[]),f=(0,s.useRouter)(),m=f.asPath.match(/#([a-zA-Z0-9\.\/]+)/),[v,g]=(0,o.useState)(null),[h,P]=(0,o.useState)(null);return(0,o.useEffect)(()=>{if(m?P(m[1]):P(r[0].name),u&&l.current)for(l.current.appendChild(u.domElement);u.__controllers.length>0;)u.__controllers[0].remove();d&&p.current&&(d.dom.style.position="absolute",d.showPanel(1),p.current.appendChild(d.dom));let t={active:!0},a=()=>{t.active=!1};try{let i=n.current;if(!i)throw Error("The canvas is not available");let s=e.init({canvas:i,pageState:t,gui:u,stats:d});s instanceof Promise&&s.catch(e=>{console.error(e),g(e)})}catch(o){console.error(o),g(o)}return a},[]),(0,a.jsxs)("main",{children:[(0,a.jsxs)(i(),{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:"\n .CodeMirror {\n height: auto !important;\n margin: 1em 0;\n }\n\n .CodeMirror-scroll {\n height: auto !important;\n overflow: visible !important;\n }\n "}}),(0,a.jsx)("title",{children:"".concat(e.name," - WebGPU Samples")}),(0,a.jsx)("meta",{name:"description",content:e.description}),(0,a.jsx)("meta",{httpEquiv:"origin-trial",content:e.originTrial})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h1",{children:e.name}),(0,a.jsx)("a",{target:"_blank",rel:"noreferrer",href:"https://github.com/".concat("webgpu/webgpu-samples","/tree/main/").concat(e.filename),children:"See it on Github!"}),(0,a.jsx)("p",{children:e.description}),v?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{children:"Something went wrong. Do your browser and device support WebGPU?"}),(0,a.jsx)("p",{children:"".concat(v)})]}):null]}),(0,a.jsxs)("div",{className:c().canvasContainer,children:[(0,a.jsx)("div",{style:{position:"absolute",left:10},ref:p}),(0,a.jsx)("div",{style:{position:"absolute",right:10},ref:l}),(0,a.jsx)("canvas",{ref:n})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("nav",{className:c().sourceFileNav,children:(0,a.jsx)("ul",{children:r.map((e,n)=>(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#".concat(e.name),"data-active":h==e.name,onClick(){P(e.name)},children:e.name})},n))})}),r.map((e,n)=>(0,a.jsx)(e.Container,{className:c().sourceFileContainer,"data-active":h==e.name},n))]})]})},p=e=>(0,a.jsx)(u,{...e});function d(e,n){if(!e)throw Error(n)}},2752:function(e,n,t){"use strict";t.r(n),t.d(n,{default:function(){return c}});var a=t(5671),r="struct VertexOutput {\n @builtin(position) position : vec4,\n @location(4) color : vec4,\n}\n\n@vertex\nfn vert_main(\n @location(0) a_particlePos : vec2,\n @location(1) a_particleVel : vec2,\n @location(2) a_pos : vec2\n) -> VertexOutput {\n let angle = -atan2(a_particleVel.x, a_particleVel.y);\n let pos = vec2(\n (a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),\n (a_pos.x * sin(angle)) + (a_pos.y * cos(angle))\n );\n \n var output : VertexOutput;\n output.position = vec4(pos + a_particlePos, 0.0, 1.0);\n output.color = vec4(\n 1.0 - sin(angle + 1.0) - a_particleVel.y,\n pos.x * 100.0 - a_particleVel.y + 0.1,\n a_particleVel.x + cos(angle + 0.5),\n 1.0);\n return output;\n}\n\n@fragment\nfn frag_main(@location(4) color : vec4) -> @location(0) vec4 {\n return color;\n}",i="struct Particle {\n pos : vec2,\n vel : vec2,\n}\nstruct SimParams {\n deltaT : f32,\n rule1Distance : f32,\n rule2Distance : f32,\n rule3Distance : f32,\n rule1Scale : f32,\n rule2Scale : f32,\n rule3Scale : f32,\n}\nstruct Particles {\n particles : array,\n}\n@binding(0) @group(0) var params : SimParams;\n@binding(1) @group(0) var particlesA : Particles;\n@binding(2) @group(0) var particlesB : Particles;\n\n// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) {\n var index = GlobalInvocationID.x;\n\n var vPos = particlesA.particles[index].pos;\n var vVel = particlesA.particles[index].vel;\n var cMass = vec2(0.0);\n var cVel = vec2(0.0);\n var colVel = vec2(0.0);\n var cMassCount = 0u;\n var cVelCount = 0u;\n var pos : vec2;\n var vel : vec2;\n\n for (var i = 0u; i < arrayLength(&particlesA.particles); i++) {\n if (i == index) {\n continue;\n }\n\n pos = particlesA.particles[i].pos.xy;\n vel = particlesA.particles[i].vel.xy;\n if (distance(pos, vPos) < params.rule1Distance) {\n cMass += pos;\n cMassCount++;\n }\n if (distance(pos, vPos) < params.rule2Distance) {\n colVel -= pos - vPos;\n }\n if (distance(pos, vPos) < params.rule3Distance) {\n cVel += vel;\n cVelCount++;\n }\n }\n if (cMassCount > 0) {\n cMass = (cMass / vec2(f32(cMassCount))) - vPos;\n }\n if (cVelCount > 0) {\n cVel /= f32(cVelCount);\n }\n vVel += (cMass * params.rule1Scale) + (colVel * params.rule2Scale) + (cVel * params.rule3Scale);\n\n // clamp velocity for a more pleasing simulation\n vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);\n // kinematic update\n vPos = vPos + (vVel * params.deltaT);\n // Wrap around boundary\n if (vPos.x < -1.0) {\n vPos.x = 1.0;\n }\n if (vPos.x > 1.0) {\n vPos.x = -1.0;\n }\n if (vPos.y < -1.0) {\n vPos.y = 1.0;\n }\n if (vPos.y > 1.0) {\n vPos.y = -1.0;\n }\n // Write back\n particlesB.particles[index].pos = vPos;\n particlesB.particles[index].vel = vVel;\n}\n",s="src/sample/computeBoids/main.ts";let o=async e=>{let{canvas:n,pageState:t,gui:s}=e,o=await navigator.gpu.requestAdapter();(0,a.hu)(o,"requestAdapter returned null");let l=await o.requestDevice();if(!t.active)return;let c=n.getContext("webgpu"),u=window.devicePixelRatio||1;n.width=n.clientWidth*u,n.height=n.clientHeight*u;let p=navigator.gpu.getPreferredCanvasFormat();c.configure({device:l,format:p,alphaMode:"premultiplied"});let d=l.createShaderModule({code:r}),f=l.createRenderPipeline({layout:"auto",vertex:{module:d,entryPoint:"vert_main",buffers:[{arrayStride:16,stepMode:"instance",attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"}]},{arrayStride:8,stepMode:"vertex",attributes:[{shaderLocation:2,offset:0,format:"float32x2"}]}]},fragment:{module:d,entryPoint:"frag_main",targets:[{format:p}]},primitive:{topology:"triangle-list"}}),m=l.createComputePipeline({layout:"auto",compute:{module:l.createShaderModule({code:i}),entryPoint:"main"}}),v={colorAttachments:[{view:void 0,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]},g=new Float32Array([-.01,-.02,.01,-.02,0,.02]),h=l.createBuffer({size:g.byteLength,usage:GPUBufferUsage.VERTEX,mappedAtCreation:!0});new Float32Array(h.getMappedRange()).set(g),h.unmap();let P={deltaT:.04,rule1Distance:.1,rule2Distance:.025,rule3Distance:.025,rule1Scale:.02,rule2Scale:.05,rule3Scale:.005},x=7*Float32Array.BYTES_PER_ELEMENT,b=l.createBuffer({size:x,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});function y(){l.queue.writeBuffer(b,0,new Float32Array([P.deltaT,P.rule1Distance,P.rule2Distance,P.rule3Distance,P.rule1Scale,P.rule2Scale,P.rule3Scale]))}y(),Object.keys(P).forEach(e=>{void 0===s?console.error("GUI not initialized"):s.add(P,e).onFinishChange(y)});let S=new Float32Array(6e3);for(let B=0;B<1500;++B)S[4*B+0]=2*(Math.random()-.5),S[4*B+1]=2*(Math.random()-.5),S[4*B+2]=2*(Math.random()-.5)*.1,S[4*B+3]=2*(Math.random()-.5)*.1;let w=[,,],_=[,,];for(let C=0;C<2;++C)w[C]=l.createBuffer({size:S.byteLength,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.STORAGE,mappedAtCreation:!0}),new Float32Array(w[C].getMappedRange()).set(S),w[C].unmap();for(let E=0;E<2;++E)_[E]=l.createBindGroup({layout:m.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:b}},{binding:1,resource:{buffer:w[E],offset:0,size:S.byteLength}},{binding:2,resource:{buffer:w[(E+1)%2],offset:0,size:S.byteLength}}]});let M=0;requestAnimationFrame(function e(){if(!t.active)return;v.colorAttachments[0].view=c.getCurrentTexture().createView();let n=l.createCommandEncoder();{let a=n.beginComputePass();a.setPipeline(m),a.setBindGroup(0,_[M%2]),a.dispatchWorkgroups(Math.ceil(23.4375)),a.end()}{let r=n.beginRenderPass(v);r.setPipeline(f),r.setVertexBuffer(0,w[(M+1)%2]),r.setVertexBuffer(1,h),r.draw(3,1500,0,0),r.end()}l.queue.submit([n.finish()]),++M,requestAnimationFrame(e)})},l=()=>(0,a.Tl)({name:"Compute Boids",description:"A GPU compute particle simulation that mimics the flocking behavior of birds. A compute shader updates two ping-pong buffers which store particle data. The data is used to draw instanced particles.",gui:!0,init:o,sources:[{name:s.substring(24),contents:"import { assert, makeSample, SampleInit } from '../../components/SampleLayout';\n\nimport spriteWGSL from './sprite.wgsl';\nimport updateSpritesWGSL from './updateSprites.wgsl';\n\nconst init: SampleInit = async ({ canvas, pageState, gui }) => {\n const adapter = await navigator.gpu.requestAdapter();\n assert(adapter, 'requestAdapter returned null');\n const device = await adapter.requestDevice();\n\n if (!pageState.active) return;\n const context = canvas.getContext('webgpu') as GPUCanvasContext;\n const devicePixelRatio = window.devicePixelRatio || 1;\n canvas.width = canvas.clientWidth * devicePixelRatio;\n canvas.height = canvas.clientHeight * devicePixelRatio;\n const presentationFormat = navigator.gpu.getPreferredCanvasFormat();\n\n context.configure({\n device,\n format: presentationFormat,\n alphaMode: 'premultiplied',\n });\n\n const spriteShaderModule = device.createShaderModule({ code: spriteWGSL });\n const renderPipeline = device.createRenderPipeline({\n layout: 'auto',\n vertex: {\n module: spriteShaderModule,\n entryPoint: 'vert_main',\n buffers: [\n {\n // instanced particles buffer\n arrayStride: 4 * 4,\n stepMode: 'instance',\n attributes: [\n {\n // instance position\n shaderLocation: 0,\n offset: 0,\n format: 'float32x2',\n },\n {\n // instance velocity\n shaderLocation: 1,\n offset: 2 * 4,\n format: 'float32x2',\n },\n ],\n },\n {\n // vertex buffer\n arrayStride: 2 * 4,\n stepMode: 'vertex',\n attributes: [\n {\n // vertex positions\n shaderLocation: 2,\n offset: 0,\n format: 'float32x2',\n },\n ],\n },\n ],\n },\n fragment: {\n module: spriteShaderModule,\n entryPoint: 'frag_main',\n targets: [\n {\n format: presentationFormat,\n },\n ],\n },\n primitive: {\n topology: 'triangle-list',\n },\n });\n\n const computePipeline = device.createComputePipeline({\n layout: 'auto',\n compute: {\n module: device.createShaderModule({\n code: updateSpritesWGSL,\n }),\n entryPoint: 'main',\n },\n });\n\n const renderPassDescriptor = {\n colorAttachments: [\n {\n view: undefined as any, // Assigned later\n clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },\n loadOp: 'clear' as const,\n storeOp: 'store' as const,\n },\n ],\n };\n\n // prettier-ignore\n const vertexBufferData = new Float32Array([\n -0.01, -0.02, 0.01,\n -0.02, 0.0, 0.02,\n ]);\n\n const spriteVertexBuffer = device.createBuffer({\n size: vertexBufferData.byteLength,\n usage: GPUBufferUsage.VERTEX,\n mappedAtCreation: true,\n });\n new Float32Array(spriteVertexBuffer.getMappedRange()).set(vertexBufferData);\n spriteVertexBuffer.unmap();\n\n const simParams = {\n deltaT: 0.04,\n rule1Distance: 0.1,\n rule2Distance: 0.025,\n rule3Distance: 0.025,\n rule1Scale: 0.02,\n rule2Scale: 0.05,\n rule3Scale: 0.005,\n };\n\n const simParamBufferSize = 7 * Float32Array.BYTES_PER_ELEMENT;\n const simParamBuffer = device.createBuffer({\n size: simParamBufferSize,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n });\n\n function updateSimParams() {\n device.queue.writeBuffer(\n simParamBuffer,\n 0,\n new Float32Array([\n simParams.deltaT,\n simParams.rule1Distance,\n simParams.rule2Distance,\n simParams.rule3Distance,\n simParams.rule1Scale,\n simParams.rule2Scale,\n simParams.rule3Scale,\n ])\n );\n }\n\n updateSimParams();\n Object.keys(simParams).forEach((k) => {\n const key = k as keyof typeof simParams;\n if (gui === undefined) {\n console.error('GUI not initialized');\n } else {\n gui.add(simParams, key).onFinishChange(updateSimParams);\n }\n });\n\n const numParticles = 1500;\n const initialParticleData = new Float32Array(numParticles * 4);\n for (let i = 0; i < numParticles; ++i) {\n initialParticleData[4 * i + 0] = 2 * (Math.random() - 0.5);\n initialParticleData[4 * i + 1] = 2 * (Math.random() - 0.5);\n initialParticleData[4 * i + 2] = 2 * (Math.random() - 0.5) * 0.1;\n initialParticleData[4 * i + 3] = 2 * (Math.random() - 0.5) * 0.1;\n }\n\n const particleBuffers: GPUBuffer[] = new Array(2);\n const particleBindGroups: GPUBindGroup[] = new Array(2);\n for (let i = 0; i < 2; ++i) {\n particleBuffers[i] = device.createBuffer({\n size: initialParticleData.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE,\n mappedAtCreation: true,\n });\n new Float32Array(particleBuffers[i].getMappedRange()).set(\n initialParticleData\n );\n particleBuffers[i].unmap();\n }\n\n for (let i = 0; i < 2; ++i) {\n particleBindGroups[i] = device.createBindGroup({\n layout: computePipeline.getBindGroupLayout(0),\n entries: [\n {\n binding: 0,\n resource: {\n buffer: simParamBuffer,\n },\n },\n {\n binding: 1,\n resource: {\n buffer: particleBuffers[i],\n offset: 0,\n size: initialParticleData.byteLength,\n },\n },\n {\n binding: 2,\n resource: {\n buffer: particleBuffers[(i + 1) % 2],\n offset: 0,\n size: initialParticleData.byteLength,\n },\n },\n ],\n });\n }\n\n let t = 0;\n function frame() {\n // Sample is no longer the active page.\n if (!pageState.active) return;\n\n renderPassDescriptor.colorAttachments[0].view = context\n .getCurrentTexture()\n .createView();\n\n const commandEncoder = device.createCommandEncoder();\n {\n const passEncoder = commandEncoder.beginComputePass();\n passEncoder.setPipeline(computePipeline);\n passEncoder.setBindGroup(0, particleBindGroups[t % 2]);\n passEncoder.dispatchWorkgroups(Math.ceil(numParticles / 64));\n passEncoder.end();\n }\n {\n const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);\n passEncoder.setPipeline(renderPipeline);\n passEncoder.setVertexBuffer(0, particleBuffers[(t + 1) % 2]);\n passEncoder.setVertexBuffer(1, spriteVertexBuffer);\n passEncoder.draw(3, numParticles, 0, 0);\n passEncoder.end();\n }\n device.queue.submit([commandEncoder.finish()]);\n\n ++t;\n requestAnimationFrame(frame);\n }\n requestAnimationFrame(frame);\n};\n\nconst ComputeBoids: () => JSX.Element = () =>\n makeSample({\n name: 'Compute Boids',\n description:\n 'A GPU compute particle simulation that mimics \\\nthe flocking behavior of birds. A compute shader updates \\\ntwo ping-pong buffers which store particle data. The data \\\nis used to draw instanced particles.',\n gui: true,\n init,\n sources: [\n {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n },\n {\n name: 'updateSprites.wgsl',\n contents: updateSpritesWGSL,\n editable: true,\n },\n {\n name: 'sprite.wgsl',\n contents: spriteWGSL,\n editable: true,\n },\n ],\n filename: __filename,\n });\n\nexport default ComputeBoids;\n"},{name:"updateSprites.wgsl",contents:i,editable:!0},{name:"sprite.wgsl",contents:r,editable:!0}],filename:s});var c=l},9147:function(e){e.exports={canvasContainer:"SampleLayout_canvasContainer__zRR_l",sourceFileNav:"SampleLayout_sourceFileNav__ml48P",sourceFileContainer:"SampleLayout_sourceFileContainer__3s84x"}}}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[752],{5671:function(e,n,t){"use strict";t.d(n,{Tl:function(){return p},hu:function(){return d}});var a=t(5893),r=t(9008),i=t.n(r),s=t(1163),o=t(7294),l=t(9147),c=t.n(l);t(7319);let u=e=>{let n=(0,o.useRef)(null),r=(0,o.useMemo)(()=>e.sources.map(e=>{let{name:n,contents:r}=e;return{name:n,...function(e){let n;let r=null;{r=document.createElement("div");let i=t(4631);n=i(r,{lineNumbers:!0,lineWrapping:!0,theme:"monokai",readOnly:!0})}return{Container:function(t){return(0,a.jsx)("div",{...t,children:(0,a.jsx)("div",{ref(t){r&&t&&(t.appendChild(r),n.setOption("value",e))}})})}}}(r)}}),e.sources),l=(0,o.useRef)(null),u=(0,o.useMemo)(()=>{if(e.gui){let n=t(4376);return new n.GUI({autoPlace:!1})}},[]),p=(0,o.useRef)(null),d=(0,o.useMemo)(()=>{if(e.stats){let n=t(2792);return new n}},[]),f=(0,s.useRouter)(),m=f.asPath.match(/#([a-zA-Z0-9\.\/]+)/),[v,g]=(0,o.useState)(null),[h,P]=(0,o.useState)(null);return(0,o.useEffect)(()=>{if(m?P(m[1]):P(r[0].name),u&&l.current)for(l.current.appendChild(u.domElement);u.__controllers.length>0;)u.__controllers[0].remove();d&&p.current&&(d.dom.style.position="absolute",d.showPanel(1),p.current.appendChild(d.dom));let t={active:!0},a=()=>{t.active=!1};try{let i=n.current;if(!i)throw Error("The canvas is not available");let s=e.init({canvas:i,pageState:t,gui:u,stats:d});s instanceof Promise&&s.catch(e=>{console.error(e),g(e)})}catch(o){console.error(o),g(o)}return a},[]),(0,a.jsxs)("main",{children:[(0,a.jsxs)(i(),{children:[(0,a.jsx)("style",{dangerouslySetInnerHTML:{__html:"\n .CodeMirror {\n height: auto !important;\n margin: 1em 0;\n }\n\n .CodeMirror-scroll {\n height: auto !important;\n overflow: visible !important;\n }\n "}}),(0,a.jsx)("title",{children:"".concat(e.name," - WebGPU Samples")}),(0,a.jsx)("meta",{name:"description",content:e.description}),(0,a.jsx)("meta",{httpEquiv:"origin-trial",content:e.originTrial})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("h1",{children:e.name}),(0,a.jsx)("a",{target:"_blank",rel:"noreferrer",href:"https://github.com/".concat("webgpu/webgpu-samples","/tree/main/").concat(e.filename),children:"See it on Github!"}),(0,a.jsx)("p",{children:e.description}),v?(0,a.jsxs)(a.Fragment,{children:[(0,a.jsx)("p",{children:"Something went wrong. Do your browser and device support WebGPU?"}),(0,a.jsx)("p",{children:"".concat(v)})]}):null]}),(0,a.jsxs)("div",{className:c().canvasContainer,children:[(0,a.jsx)("div",{style:{position:"absolute",left:10},ref:p}),(0,a.jsx)("div",{style:{position:"absolute",right:10},ref:l}),(0,a.jsx)("canvas",{ref:n})]}),(0,a.jsxs)("div",{children:[(0,a.jsx)("nav",{className:c().sourceFileNav,children:(0,a.jsx)("ul",{children:r.map((e,n)=>(0,a.jsx)("li",{children:(0,a.jsx)("a",{href:"#".concat(e.name),"data-active":h==e.name,onClick(){P(e.name)},children:e.name})},n))})}),r.map((e,n)=>(0,a.jsx)(e.Container,{className:c().sourceFileContainer,"data-active":h==e.name},n))]})]})},p=e=>(0,a.jsx)(u,{...e});function d(e,n){if(!e)throw Error(n)}},2752:function(e,n,t){"use strict";t.r(n),t.d(n,{default:function(){return c}});var a=t(5671),r="struct VertexOutput {\n @builtin(position) position : vec4,\n @location(4) color : vec4,\n}\n\n@vertex\nfn vert_main(\n @location(0) a_particlePos : vec2,\n @location(1) a_particleVel : vec2,\n @location(2) a_pos : vec2\n) -> VertexOutput {\n let angle = -atan2(a_particleVel.x, a_particleVel.y);\n let pos = vec2(\n (a_pos.x * cos(angle)) - (a_pos.y * sin(angle)),\n (a_pos.x * sin(angle)) + (a_pos.y * cos(angle))\n );\n \n var output : VertexOutput;\n output.position = vec4(pos + a_particlePos, 0.0, 1.0);\n output.color = vec4(\n 1.0 - sin(angle + 1.0) - a_particleVel.y,\n pos.x * 100.0 - a_particleVel.y + 0.1,\n a_particleVel.x + cos(angle + 0.5),\n 1.0);\n return output;\n}\n\n@fragment\nfn frag_main(@location(4) color : vec4) -> @location(0) vec4 {\n return color;\n}",i="struct Particle {\n pos : vec2,\n vel : vec2,\n}\nstruct SimParams {\n deltaT : f32,\n rule1Distance : f32,\n rule2Distance : f32,\n rule3Distance : f32,\n rule1Scale : f32,\n rule2Scale : f32,\n rule3Scale : f32,\n}\nstruct Particles {\n particles : array,\n}\n@binding(0) @group(0) var params : SimParams;\n@binding(1) @group(0) var particlesA : Particles;\n@binding(2) @group(0) var particlesB : Particles;\n\n// https://github.com/austinEng/Project6-Vulkan-Flocking/blob/master/data/shaders/computeparticles/particle.comp\n@compute @workgroup_size(64)\nfn main(@builtin(global_invocation_id) GlobalInvocationID : vec3) {\n var index = GlobalInvocationID.x;\n\n var vPos = particlesA.particles[index].pos;\n var vVel = particlesA.particles[index].vel;\n var cMass = vec2(0.0);\n var cVel = vec2(0.0);\n var colVel = vec2(0.0);\n var cMassCount = 0u;\n var cVelCount = 0u;\n var pos : vec2;\n var vel : vec2;\n\n for (var i = 0u; i < arrayLength(&particlesA.particles); i++) {\n if (i == index) {\n continue;\n }\n\n pos = particlesA.particles[i].pos.xy;\n vel = particlesA.particles[i].vel.xy;\n if (distance(pos, vPos) < params.rule1Distance) {\n cMass += pos;\n cMassCount++;\n }\n if (distance(pos, vPos) < params.rule2Distance) {\n colVel -= pos - vPos;\n }\n if (distance(pos, vPos) < params.rule3Distance) {\n cVel += vel;\n cVelCount++;\n }\n }\n if (cMassCount > 0) {\n cMass = (cMass / vec2(f32(cMassCount))) - vPos;\n }\n if (cVelCount > 0) {\n cVel /= f32(cVelCount);\n }\n vVel += (cMass * params.rule1Scale) + (colVel * params.rule2Scale) + (cVel * params.rule3Scale);\n\n // clamp velocity for a more pleasing simulation\n vVel = normalize(vVel) * clamp(length(vVel), 0.0, 0.1);\n // kinematic update\n vPos = vPos + (vVel * params.deltaT);\n // Wrap around boundary\n if (vPos.x < -1.0) {\n vPos.x = 1.0;\n }\n if (vPos.x > 1.0) {\n vPos.x = -1.0;\n }\n if (vPos.y < -1.0) {\n vPos.y = 1.0;\n }\n if (vPos.y > 1.0) {\n vPos.y = -1.0;\n }\n // Write back\n particlesB.particles[index].pos = vPos;\n particlesB.particles[index].vel = vVel;\n}\n",s="src/sample/computeBoids/main.ts";let o=async e=>{let{canvas:n,pageState:t,gui:s}=e,o=await navigator.gpu.requestAdapter();(0,a.hu)(o,"requestAdapter returned null");let l=await o.requestDevice();if(!t.active)return;let c=n.getContext("webgpu"),u=window.devicePixelRatio||1;n.width=n.clientWidth*u,n.height=n.clientHeight*u;let p=navigator.gpu.getPreferredCanvasFormat();c.configure({device:l,format:p,alphaMode:"premultiplied"});let d=l.createShaderModule({code:r}),f=l.createRenderPipeline({layout:"auto",vertex:{module:d,entryPoint:"vert_main",buffers:[{arrayStride:16,stepMode:"instance",attributes:[{shaderLocation:0,offset:0,format:"float32x2"},{shaderLocation:1,offset:8,format:"float32x2"}]},{arrayStride:8,stepMode:"vertex",attributes:[{shaderLocation:2,offset:0,format:"float32x2"}]}]},fragment:{module:d,entryPoint:"frag_main",targets:[{format:p}]},primitive:{topology:"triangle-list"}}),m=l.createComputePipeline({layout:"auto",compute:{module:l.createShaderModule({code:i}),entryPoint:"main"}}),v={colorAttachments:[{view:void 0,clearValue:{r:0,g:0,b:0,a:1},loadOp:"clear",storeOp:"store"}]},g=new Float32Array([-.01,-.02,.01,-.02,0,.02]),h=l.createBuffer({size:g.byteLength,usage:GPUBufferUsage.VERTEX,mappedAtCreation:!0});new Float32Array(h.getMappedRange()).set(g),h.unmap();let P={deltaT:.04,rule1Distance:.1,rule2Distance:.025,rule3Distance:.025,rule1Scale:.02,rule2Scale:.05,rule3Scale:.005},x=7*Float32Array.BYTES_PER_ELEMENT,b=l.createBuffer({size:x,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST});function y(){l.queue.writeBuffer(b,0,new Float32Array([P.deltaT,P.rule1Distance,P.rule2Distance,P.rule3Distance,P.rule1Scale,P.rule2Scale,P.rule3Scale]))}y(),Object.keys(P).forEach(e=>{void 0===s?console.error("GUI not initialized"):s.add(P,e).onFinishChange(y)});let S=new Float32Array(6e3);for(let w=0;w<1500;++w)S[4*w+0]=2*(Math.random()-.5),S[4*w+1]=2*(Math.random()-.5),S[4*w+2]=2*(Math.random()-.5)*.1,S[4*w+3]=2*(Math.random()-.5)*.1;let B=[,,],_=[,,];for(let C=0;C<2;++C)B[C]=l.createBuffer({size:S.byteLength,usage:GPUBufferUsage.VERTEX|GPUBufferUsage.STORAGE,mappedAtCreation:!0}),new Float32Array(B[C].getMappedRange()).set(S),B[C].unmap();for(let E=0;E<2;++E)_[E]=l.createBindGroup({layout:m.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:b}},{binding:1,resource:{buffer:B[E],offset:0,size:S.byteLength}},{binding:2,resource:{buffer:B[(E+1)%2],offset:0,size:S.byteLength}}]});let M=0;requestAnimationFrame(function e(){if(!t.active)return;v.colorAttachments[0].view=c.getCurrentTexture().createView();let n=l.createCommandEncoder();{let a=n.beginComputePass();a.setPipeline(m),a.setBindGroup(0,_[M%2]),a.dispatchWorkgroups(Math.ceil(23.4375)),a.end()}{let r=n.beginRenderPass(v);r.setPipeline(f),r.setVertexBuffer(0,B[(M+1)%2]),r.setVertexBuffer(1,h),r.draw(3,1500,0,0),r.end()}l.queue.submit([n.finish()]),++M,requestAnimationFrame(e)})},l=()=>(0,a.Tl)({name:"Compute Boids",description:"A GPU compute particle simulation that mimics the flocking behavior of birds. A compute shader updates two ping-pong buffers which store particle data. The data is used to draw instanced particles.",gui:!0,init:o,sources:[{name:s.substring(24),contents:"import { assert, makeSample, SampleInit } from '../../components/SampleLayout';\n\nimport spriteWGSL from './sprite.wgsl';\nimport updateSpritesWGSL from './updateSprites.wgsl';\n\nconst init: SampleInit = async ({ canvas, pageState, gui }) => {\n const adapter = await navigator.gpu.requestAdapter();\n assert(adapter, 'requestAdapter returned null');\n const device = await adapter.requestDevice();\n\n if (!pageState.active) return;\n const context = canvas.getContext('webgpu') as GPUCanvasContext;\n const devicePixelRatio = window.devicePixelRatio || 1;\n canvas.width = canvas.clientWidth * devicePixelRatio;\n canvas.height = canvas.clientHeight * devicePixelRatio;\n const presentationFormat = navigator.gpu.getPreferredCanvasFormat();\n\n context.configure({\n device,\n format: presentationFormat,\n alphaMode: 'premultiplied',\n });\n\n const spriteShaderModule = device.createShaderModule({ code: spriteWGSL });\n const renderPipeline = device.createRenderPipeline({\n layout: 'auto',\n vertex: {\n module: spriteShaderModule,\n entryPoint: 'vert_main',\n buffers: [\n {\n // instanced particles buffer\n arrayStride: 4 * 4,\n stepMode: 'instance',\n attributes: [\n {\n // instance position\n shaderLocation: 0,\n offset: 0,\n format: 'float32x2',\n },\n {\n // instance velocity\n shaderLocation: 1,\n offset: 2 * 4,\n format: 'float32x2',\n },\n ],\n },\n {\n // vertex buffer\n arrayStride: 2 * 4,\n stepMode: 'vertex',\n attributes: [\n {\n // vertex positions\n shaderLocation: 2,\n offset: 0,\n format: 'float32x2',\n },\n ],\n },\n ],\n },\n fragment: {\n module: spriteShaderModule,\n entryPoint: 'frag_main',\n targets: [\n {\n format: presentationFormat,\n },\n ],\n },\n primitive: {\n topology: 'triangle-list',\n },\n });\n\n const computePipeline = device.createComputePipeline({\n layout: 'auto',\n compute: {\n module: device.createShaderModule({\n code: updateSpritesWGSL,\n }),\n entryPoint: 'main',\n },\n });\n\n const renderPassDescriptor = {\n colorAttachments: [\n {\n view: undefined as GPUTextureView, // Assigned later\n clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },\n loadOp: 'clear' as const,\n storeOp: 'store' as const,\n },\n ],\n };\n\n // prettier-ignore\n const vertexBufferData = new Float32Array([\n -0.01, -0.02, 0.01,\n -0.02, 0.0, 0.02,\n ]);\n\n const spriteVertexBuffer = device.createBuffer({\n size: vertexBufferData.byteLength,\n usage: GPUBufferUsage.VERTEX,\n mappedAtCreation: true,\n });\n new Float32Array(spriteVertexBuffer.getMappedRange()).set(vertexBufferData);\n spriteVertexBuffer.unmap();\n\n const simParams = {\n deltaT: 0.04,\n rule1Distance: 0.1,\n rule2Distance: 0.025,\n rule3Distance: 0.025,\n rule1Scale: 0.02,\n rule2Scale: 0.05,\n rule3Scale: 0.005,\n };\n\n const simParamBufferSize = 7 * Float32Array.BYTES_PER_ELEMENT;\n const simParamBuffer = device.createBuffer({\n size: simParamBufferSize,\n usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST,\n });\n\n function updateSimParams() {\n device.queue.writeBuffer(\n simParamBuffer,\n 0,\n new Float32Array([\n simParams.deltaT,\n simParams.rule1Distance,\n simParams.rule2Distance,\n simParams.rule3Distance,\n simParams.rule1Scale,\n simParams.rule2Scale,\n simParams.rule3Scale,\n ])\n );\n }\n\n updateSimParams();\n Object.keys(simParams).forEach((k) => {\n const key = k as keyof typeof simParams;\n if (gui === undefined) {\n console.error('GUI not initialized');\n } else {\n gui.add(simParams, key).onFinishChange(updateSimParams);\n }\n });\n\n const numParticles = 1500;\n const initialParticleData = new Float32Array(numParticles * 4);\n for (let i = 0; i < numParticles; ++i) {\n initialParticleData[4 * i + 0] = 2 * (Math.random() - 0.5);\n initialParticleData[4 * i + 1] = 2 * (Math.random() - 0.5);\n initialParticleData[4 * i + 2] = 2 * (Math.random() - 0.5) * 0.1;\n initialParticleData[4 * i + 3] = 2 * (Math.random() - 0.5) * 0.1;\n }\n\n const particleBuffers: GPUBuffer[] = new Array(2);\n const particleBindGroups: GPUBindGroup[] = new Array(2);\n for (let i = 0; i < 2; ++i) {\n particleBuffers[i] = device.createBuffer({\n size: initialParticleData.byteLength,\n usage: GPUBufferUsage.VERTEX | GPUBufferUsage.STORAGE,\n mappedAtCreation: true,\n });\n new Float32Array(particleBuffers[i].getMappedRange()).set(\n initialParticleData\n );\n particleBuffers[i].unmap();\n }\n\n for (let i = 0; i < 2; ++i) {\n particleBindGroups[i] = device.createBindGroup({\n layout: computePipeline.getBindGroupLayout(0),\n entries: [\n {\n binding: 0,\n resource: {\n buffer: simParamBuffer,\n },\n },\n {\n binding: 1,\n resource: {\n buffer: particleBuffers[i],\n offset: 0,\n size: initialParticleData.byteLength,\n },\n },\n {\n binding: 2,\n resource: {\n buffer: particleBuffers[(i + 1) % 2],\n offset: 0,\n size: initialParticleData.byteLength,\n },\n },\n ],\n });\n }\n\n let t = 0;\n function frame() {\n // Sample is no longer the active page.\n if (!pageState.active) return;\n\n renderPassDescriptor.colorAttachments[0].view = context\n .getCurrentTexture()\n .createView();\n\n const commandEncoder = device.createCommandEncoder();\n {\n const passEncoder = commandEncoder.beginComputePass();\n passEncoder.setPipeline(computePipeline);\n passEncoder.setBindGroup(0, particleBindGroups[t % 2]);\n passEncoder.dispatchWorkgroups(Math.ceil(numParticles / 64));\n passEncoder.end();\n }\n {\n const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);\n passEncoder.setPipeline(renderPipeline);\n passEncoder.setVertexBuffer(0, particleBuffers[(t + 1) % 2]);\n passEncoder.setVertexBuffer(1, spriteVertexBuffer);\n passEncoder.draw(3, numParticles, 0, 0);\n passEncoder.end();\n }\n device.queue.submit([commandEncoder.finish()]);\n\n ++t;\n requestAnimationFrame(frame);\n }\n requestAnimationFrame(frame);\n};\n\nconst ComputeBoids: () => JSX.Element = () =>\n makeSample({\n name: 'Compute Boids',\n description:\n 'A GPU compute particle simulation that mimics \\\nthe flocking behavior of birds. A compute shader updates \\\ntwo ping-pong buffers which store particle data. The data \\\nis used to draw instanced particles.',\n gui: true,\n init,\n sources: [\n {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n },\n {\n name: 'updateSprites.wgsl',\n contents: updateSpritesWGSL,\n editable: true,\n },\n {\n name: 'sprite.wgsl',\n contents: spriteWGSL,\n editable: true,\n },\n ],\n filename: __filename,\n });\n\nexport default ComputeBoids;\n"},{name:"updateSprites.wgsl",contents:i,editable:!0},{name:"sprite.wgsl",contents:r,editable:!0}],filename:s});var c=l},9147:function(e){e.exports={canvasContainer:"SampleLayout_canvasContainer__zRR_l",sourceFileNav:"SampleLayout_sourceFileNav__ml48P",sourceFileContainer:"SampleLayout_sourceFileContainer__3s84x"}}}]); \ No newline at end of file diff --git a/_next/static/chunks/841.3dd27e4f39a03532.js b/_next/static/chunks/841.097bc46a54981e6b.js similarity index 81% rename from _next/static/chunks/841.3dd27e4f39a03532.js rename to _next/static/chunks/841.097bc46a54981e6b.js index 62b526b7..a4d98599 100644 --- a/_next/static/chunks/841.3dd27e4f39a03532.js +++ b/_next/static/chunks/841.097bc46a54981e6b.js @@ -1 +1 @@ -(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[841],{5671:function(e,n,t){"use strict";t.d(n,{Tl:function(){return f},hu:function(){return c}});var r=t(5893),i=t(9008),a=t.n(i),o=t(1163),s=t(7294),u=t(9147),l=t.n(u);t(7319);let d=e=>{let n=(0,s.useRef)(null),i=(0,s.useMemo)(()=>e.sources.map(e=>{let{name:n,contents:i}=e;return{name:n,...function(e){let n;let i=null;{i=document.createElement("div");let a=t(4631);n=a(i,{lineNumbers:!0,lineWrapping:!0,theme:"monokai",readOnly:!0})}return{Container:function(t){return(0,r.jsx)("div",{...t,children:(0,r.jsx)("div",{ref(t){i&&t&&(t.appendChild(i),n.setOption("value",e))}})})}}}(i)}}),e.sources),u=(0,s.useRef)(null),d=(0,s.useMemo)(()=>{if(e.gui){let n=t(4376);return new n.GUI({autoPlace:!1})}},[]),f=(0,s.useRef)(null),c=(0,s.useMemo)(()=>{if(e.stats){let n=t(2792);return new n}},[]),m=(0,o.useRouter)(),p=m.asPath.match(/#([a-zA-Z0-9\.\/]+)/),[g,y]=(0,s.useState)(null),[E,v]=(0,s.useState)(null);return(0,s.useEffect)(()=>{if(p?v(p[1]):v(i[0].name),d&&u.current)for(u.current.appendChild(d.domElement);d.__controllers.length>0;)d.__controllers[0].remove();c&&f.current&&(c.dom.style.position="absolute",c.showPanel(1),f.current.appendChild(c.dom));let t={active:!0},r=()=>{t.active=!1};try{let a=n.current;if(!a)throw Error("The canvas is not available");let o=e.init({canvas:a,pageState:t,gui:d,stats:c});o instanceof Promise&&o.catch(e=>{console.error(e),y(e)})}catch(s){console.error(s),y(s)}return r},[]),(0,r.jsxs)("main",{children:[(0,r.jsxs)(a(),{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"\n .CodeMirror {\n height: auto !important;\n margin: 1em 0;\n }\n\n .CodeMirror-scroll {\n height: auto !important;\n overflow: visible !important;\n }\n "}}),(0,r.jsx)("title",{children:"".concat(e.name," - WebGPU Samples")}),(0,r.jsx)("meta",{name:"description",content:e.description}),(0,r.jsx)("meta",{httpEquiv:"origin-trial",content:e.originTrial})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("h1",{children:e.name}),(0,r.jsx)("a",{target:"_blank",rel:"noreferrer",href:"https://github.com/".concat("webgpu/webgpu-samples","/tree/main/").concat(e.filename),children:"See it on Github!"}),(0,r.jsx)("p",{children:e.description}),g?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("p",{children:"Something went wrong. Do your browser and device support WebGPU?"}),(0,r.jsx)("p",{children:"".concat(g)})]}):null]}),(0,r.jsxs)("div",{className:l().canvasContainer,children:[(0,r.jsx)("div",{style:{position:"absolute",left:10},ref:f}),(0,r.jsx)("div",{style:{position:"absolute",right:10},ref:u}),(0,r.jsx)("canvas",{ref:n})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("nav",{className:l().sourceFileNav,children:(0,r.jsx)("ul",{children:i.map((e,n)=>(0,r.jsx)("li",{children:(0,r.jsx)("a",{href:"#".concat(e.name),"data-active":E==e.name,onClick(){v(e.name)},children:e.name})},n))})}),i.map((e,n)=>(0,r.jsx)(e.Container,{className:l().sourceFileContainer,"data-active":E==e.name},n))]})]})},f=e=>(0,r.jsx)(d,{...e});function c(e,n){if(!e)throw Error(n)}},841:function(e,n,t){"use strict";t.r(n),t.d(n,{default:function(){return u}});var r=t(5671),i="struct Time {\n value : f32,\n}\n\nstruct Uniforms {\n scale : f32,\n offsetX : f32,\n offsetY : f32,\n scalar : f32,\n scalarOffset : f32,\n}\n\n@binding(0) @group(0) var time : Time;\n@binding(0) @group(1) var uniforms : Uniforms;\n\nstruct VertexOutput {\n @builtin(position) Position : vec4,\n @location(0) v_color : vec4,\n}\n\n@vertex\nfn vert_main(\n @location(0) position : vec4,\n @location(1) color : vec4\n) -> VertexOutput {\n var fade = (uniforms.scalarOffset + time.value * uniforms.scalar / 10.0) % 1.0;\n if (fade < 0.5) {\n fade = fade * 2.0;\n } else {\n fade = (1.0 - fade) * 2.0;\n }\n var xpos = position.x * uniforms.scale;\n var ypos = position.y * uniforms.scale;\n var angle = 3.14159 * 2.0 * fade;\n var xrot = xpos * cos(angle) - ypos * sin(angle);\n var yrot = xpos * sin(angle) + ypos * cos(angle);\n xpos = xrot + uniforms.offsetX;\n ypos = yrot + uniforms.offsetY;\n\n var output : VertexOutput;\n output.v_color = vec4(fade, 1.0 - fade, 0.0, 1.0) + color;\n output.Position = vec4(xpos, ypos, 0.0, 1.0);\n return output;\n}\n\n@fragment\nfn frag_main(@location(0) v_color : vec4) -> @location(0) vec4 {\n return v_color;\n}\n",a="src/sample/animometer/main.ts";let o=async e=>{let n,t,a,{canvas:o,pageState:s,gui:u}=e,l=await navigator.gpu.requestAdapter();(0,r.hu)(l,"requestAdapter returned null");let d=await l.requestDevice();if(!s.active)return;let f=document.createElement("div");f.style.color="white",f.style.background="black",f.style.position="absolute",f.style.top="10px",f.style.left="10px";let c=document.createElement("pre");f.appendChild(c),o.parentNode?o.parentNode.appendChild(f):console.error("canvas.parentNode is null");let m=new URLSearchParams(window.location.search),p={numTriangles:Number(m.get("numTriangles"))||2e4,renderBundles:Boolean(m.get("renderBundles")),dynamicOffsets:Boolean(m.get("dynamicOffsets"))},g=o.getContext("webgpu"),y=window.devicePixelRatio||1;o.width=o.clientWidth*y,o.height=o.clientHeight*y;let E=navigator.gpu.getPreferredCanvasFormat();g.configure({device:d,format:E,alphaMode:"premultiplied",usage:GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});let v=d.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",minBindingSize:4}}]}),h=d.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",minBindingSize:20}}]}),T=d.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:20}}]}),B=4*Float32Array.BYTES_PER_ELEMENT,b=d.createPipelineLayout({bindGroupLayouts:[v,h]}),P=d.createPipelineLayout({bindGroupLayouts:[v,T]}),_=d.createShaderModule({code:i}),x={layout:"auto",vertex:{module:_,entryPoint:"vert_main",buffers:[{arrayStride:2*B,stepMode:"vertex",attributes:[{shaderLocation:0,offset:0,format:"float32x4"},{shaderLocation:1,offset:B,format:"float32x4"}]}]},fragment:{module:_,entryPoint:"frag_main",targets:[{format:E}]},primitive:{topology:"triangle-list",frontFace:"ccw",cullMode:"none"}},S=d.createRenderPipeline({...x,layout:b}),A=d.createRenderPipeline({...x,layout:P}),w=d.createBuffer({size:6*B,usage:GPUBufferUsage.VERTEX,mappedAtCreation:!0});function F(){let e;let n=p.numTriangles,t=5*Float32Array.BYTES_PER_ELEMENT,r=256*Math.ceil(t/256),i=r/Float32Array.BYTES_PER_ELEMENT,a=d.createBuffer({size:n*r+Float32Array.BYTES_PER_ELEMENT,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.UNIFORM}),o=new Float32Array(n*i),s=Array(n);for(let u=0;u{G=F()};void 0===u?console.error("GUI not initialized"):(u.add(p,"numTriangles",0,2e5).step(1).onFinishChange(L),u.add(p,"renderBundles"),u.add(p,"dynamicOffsets"));let M=!0;requestAnimationFrame(function e(r){if(!s.active)return;let i=0;void 0!==n&&(i=r-n),n=r;let o=performance.now();G(r);let u=performance.now()-o;void 0===a&&(a=i),void 0===t&&(t=u),a=.8*a+.2*i,t=.8*t+.2*u,M&&(c.innerHTML="Avg Javascript: ".concat(t.toFixed(2)," ms\nAvg Frame: ").concat(a.toFixed(2)," ms"),M=!1,setTimeout(()=>{M=!0},100)),requestAnimationFrame(e)})},s=()=>(0,r.Tl)({name:"Animometer",description:"A WebGPU port of the Animometer MotionMark benchmark.",gui:!0,init:o,sources:[{name:a.substring(22),contents:"import { assert, makeSample, SampleInit } from '../../components/SampleLayout';\n\nimport animometerWGSL from './animometer.wgsl';\n\nconst init: SampleInit = async ({ canvas, pageState, gui }) => {\n const adapter = await navigator.gpu.requestAdapter();\n assert(adapter, 'requestAdapter returned null');\n const device = await adapter.requestDevice();\n\n if (!pageState.active) return;\n\n const perfDisplayContainer = document.createElement('div');\n perfDisplayContainer.style.color = 'white';\n perfDisplayContainer.style.background = 'black';\n perfDisplayContainer.style.position = 'absolute';\n perfDisplayContainer.style.top = '10px';\n perfDisplayContainer.style.left = '10px';\n\n const perfDisplay = document.createElement('pre');\n perfDisplayContainer.appendChild(perfDisplay);\n if (canvas.parentNode) {\n canvas.parentNode.appendChild(perfDisplayContainer);\n } else {\n console.error('canvas.parentNode is null');\n }\n\n const params = new URLSearchParams(window.location.search);\n const settings = {\n numTriangles: Number(params.get('numTriangles')) || 20000,\n renderBundles: Boolean(params.get('renderBundles')),\n dynamicOffsets: Boolean(params.get('dynamicOffsets')),\n };\n\n const context = canvas.getContext('webgpu') as GPUCanvasContext;\n\n const devicePixelRatio = window.devicePixelRatio || 1;\n canvas.width = canvas.clientWidth * devicePixelRatio;\n canvas.height = canvas.clientHeight * devicePixelRatio;\n const presentationFormat = navigator.gpu.getPreferredCanvasFormat();\n\n context.configure({\n device,\n format: presentationFormat,\n alphaMode: 'premultiplied',\n usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\n });\n\n const timeBindGroupLayout = device.createBindGroupLayout({\n entries: [\n {\n binding: 0,\n visibility: GPUShaderStage.VERTEX,\n buffer: {\n type: 'uniform',\n minBindingSize: 4,\n },\n },\n ],\n });\n\n const bindGroupLayout = device.createBindGroupLayout({\n entries: [\n {\n binding: 0,\n visibility: GPUShaderStage.VERTEX,\n buffer: {\n type: 'uniform',\n minBindingSize: 20,\n },\n },\n ],\n });\n\n const dynamicBindGroupLayout = device.createBindGroupLayout({\n entries: [\n {\n binding: 0,\n visibility: GPUShaderStage.VERTEX,\n buffer: {\n type: 'uniform',\n hasDynamicOffset: true,\n minBindingSize: 20,\n },\n },\n ],\n });\n\n const vec4Size = 4 * Float32Array.BYTES_PER_ELEMENT;\n const pipelineLayout = device.createPipelineLayout({\n bindGroupLayouts: [timeBindGroupLayout, bindGroupLayout],\n });\n const dynamicPipelineLayout = device.createPipelineLayout({\n bindGroupLayouts: [timeBindGroupLayout, dynamicBindGroupLayout],\n });\n\n const shaderModule = device.createShaderModule({\n code: animometerWGSL,\n });\n const pipelineDesc: GPURenderPipelineDescriptor = {\n layout: 'auto',\n vertex: {\n module: shaderModule,\n entryPoint: 'vert_main',\n buffers: [\n {\n // vertex buffer\n arrayStride: 2 * vec4Size,\n stepMode: 'vertex',\n attributes: [\n {\n // vertex positions\n shaderLocation: 0,\n offset: 0,\n format: 'float32x4',\n },\n {\n // vertex colors\n shaderLocation: 1,\n offset: vec4Size,\n format: 'float32x4',\n },\n ],\n },\n ],\n },\n fragment: {\n module: shaderModule,\n entryPoint: 'frag_main',\n targets: [\n {\n format: presentationFormat,\n },\n ],\n },\n primitive: {\n topology: 'triangle-list',\n frontFace: 'ccw',\n cullMode: 'none',\n },\n };\n\n const pipeline = device.createRenderPipeline({\n ...pipelineDesc,\n layout: pipelineLayout,\n });\n\n const dynamicPipeline = device.createRenderPipeline({\n ...pipelineDesc,\n layout: dynamicPipelineLayout,\n });\n\n const vertexBuffer = device.createBuffer({\n size: 2 * 3 * vec4Size,\n usage: GPUBufferUsage.VERTEX,\n mappedAtCreation: true,\n });\n\n // prettier-ignore\n new Float32Array(vertexBuffer.getMappedRange()).set([\n // position data /**/ color data\n 0, 0.1, 0, 1, /**/ 1, 0, 0, 1,\n -0.1, -0.1, 0, 1, /**/ 0, 1, 0, 1,\n 0.1, -0.1, 0, 1, /**/ 0, 0, 1, 1,\n ]);\n vertexBuffer.unmap();\n\n function configure() {\n const numTriangles = settings.numTriangles;\n const uniformBytes = 5 * Float32Array.BYTES_PER_ELEMENT;\n const alignedUniformBytes = Math.ceil(uniformBytes / 256) * 256;\n const alignedUniformFloats =\n alignedUniformBytes / Float32Array.BYTES_PER_ELEMENT;\n const uniformBuffer = device.createBuffer({\n size: numTriangles * alignedUniformBytes + Float32Array.BYTES_PER_ELEMENT,\n usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,\n });\n const uniformBufferData = new Float32Array(\n numTriangles * alignedUniformFloats\n );\n const bindGroups = new Array(numTriangles);\n for (let i = 0; i < numTriangles; ++i) {\n uniformBufferData[alignedUniformFloats * i + 0] =\n Math.random() * 0.2 + 0.2; // scale\n uniformBufferData[alignedUniformFloats * i + 1] =\n 0.9 * 2 * (Math.random() - 0.5); // offsetX\n uniformBufferData[alignedUniformFloats * i + 2] =\n 0.9 * 2 * (Math.random() - 0.5); // offsetY\n uniformBufferData[alignedUniformFloats * i + 3] =\n Math.random() * 1.5 + 0.5; // scalar\n uniformBufferData[alignedUniformFloats * i + 4] = Math.random() * 10; // scalarOffset\n\n bindGroups[i] = device.createBindGroup({\n layout: bindGroupLayout,\n entries: [\n {\n binding: 0,\n resource: {\n buffer: uniformBuffer,\n offset: i * alignedUniformBytes,\n size: 6 * Float32Array.BYTES_PER_ELEMENT,\n },\n },\n ],\n });\n }\n\n const dynamicBindGroup = device.createBindGroup({\n layout: dynamicBindGroupLayout,\n entries: [\n {\n binding: 0,\n resource: {\n buffer: uniformBuffer,\n offset: 0,\n size: 6 * Float32Array.BYTES_PER_ELEMENT,\n },\n },\n ],\n });\n\n const timeOffset = numTriangles * alignedUniformBytes;\n const timeBindGroup = device.createBindGroup({\n layout: timeBindGroupLayout,\n entries: [\n {\n binding: 0,\n resource: {\n buffer: uniformBuffer,\n offset: timeOffset,\n size: Float32Array.BYTES_PER_ELEMENT,\n },\n },\n ],\n });\n\n // writeBuffer too large may OOM. TODO: The browser should internally chunk uploads.\n const maxMappingLength =\n (14 * 1024 * 1024) / Float32Array.BYTES_PER_ELEMENT;\n for (\n let offset = 0;\n offset < uniformBufferData.length;\n offset += maxMappingLength\n ) {\n const uploadCount = Math.min(\n uniformBufferData.length - offset,\n maxMappingLength\n );\n\n device.queue.writeBuffer(\n uniformBuffer,\n offset * Float32Array.BYTES_PER_ELEMENT,\n uniformBufferData.buffer,\n uniformBufferData.byteOffset + offset * Float32Array.BYTES_PER_ELEMENT,\n uploadCount * Float32Array.BYTES_PER_ELEMENT\n );\n }\n\n function recordRenderPass(\n passEncoder: GPURenderBundleEncoder | GPURenderPassEncoder\n ) {\n if (settings.dynamicOffsets) {\n passEncoder.setPipeline(dynamicPipeline);\n } else {\n passEncoder.setPipeline(pipeline);\n }\n passEncoder.setVertexBuffer(0, vertexBuffer);\n passEncoder.setBindGroup(0, timeBindGroup);\n const dynamicOffsets = [0];\n for (let i = 0; i < numTriangles; ++i) {\n if (settings.dynamicOffsets) {\n dynamicOffsets[0] = i * alignedUniformBytes;\n passEncoder.setBindGroup(1, dynamicBindGroup, dynamicOffsets);\n } else {\n passEncoder.setBindGroup(1, bindGroups[i]);\n }\n passEncoder.draw(3);\n }\n }\n\n let startTime: number | undefined = undefined;\n const uniformTime = new Float32Array([0]);\n\n const renderPassDescriptor = {\n colorAttachments: [\n {\n view: undefined as any, // Assigned later\n clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },\n loadOp: 'clear' as const,\n storeOp: 'store' as const,\n },\n ],\n };\n\n const renderBundleEncoder = device.createRenderBundleEncoder({\n colorFormats: [presentationFormat],\n });\n recordRenderPass(renderBundleEncoder);\n const renderBundle = renderBundleEncoder.finish();\n\n return function doDraw(timestamp: number) {\n if (startTime === undefined) {\n startTime = timestamp;\n }\n uniformTime[0] = (timestamp - startTime) / 1000;\n device.queue.writeBuffer(uniformBuffer, timeOffset, uniformTime.buffer);\n\n renderPassDescriptor.colorAttachments[0].view = context\n .getCurrentTexture()\n .createView();\n\n const commandEncoder = device.createCommandEncoder();\n const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);\n\n if (settings.renderBundles) {\n passEncoder.executeBundles([renderBundle]);\n } else {\n recordRenderPass(passEncoder);\n }\n\n passEncoder.end();\n device.queue.submit([commandEncoder.finish()]);\n };\n }\n\n let doDraw = configure();\n\n const updateSettings = () => {\n doDraw = configure();\n };\n if (gui === undefined) {\n console.error('GUI not initialized');\n } else {\n gui\n .add(settings, 'numTriangles', 0, 200000)\n .step(1)\n .onFinishChange(updateSettings);\n gui.add(settings, 'renderBundles');\n gui.add(settings, 'dynamicOffsets');\n }\n\n let previousFrameTimestamp: number | undefined = undefined;\n let jsTimeAvg: number | undefined = undefined;\n let frameTimeAvg: number | undefined = undefined;\n let updateDisplay = true;\n\n function frame(timestamp: number) {\n // Sample is no longer the active page.\n if (!pageState.active) return;\n\n let frameTime = 0;\n if (previousFrameTimestamp !== undefined) {\n frameTime = timestamp - previousFrameTimestamp;\n }\n previousFrameTimestamp = timestamp;\n\n const start = performance.now();\n doDraw(timestamp);\n const jsTime = performance.now() - start;\n if (frameTimeAvg === undefined) {\n frameTimeAvg = frameTime;\n }\n if (jsTimeAvg === undefined) {\n jsTimeAvg = jsTime;\n }\n\n const w = 0.2;\n frameTimeAvg = (1 - w) * frameTimeAvg + w * frameTime;\n jsTimeAvg = (1 - w) * jsTimeAvg + w * jsTime;\n\n if (updateDisplay) {\n perfDisplay.innerHTML = `Avg Javascript: ${jsTimeAvg.toFixed(\n 2\n )} ms\\nAvg Frame: ${frameTimeAvg.toFixed(2)} ms`;\n updateDisplay = false;\n setTimeout(() => {\n updateDisplay = true;\n }, 100);\n }\n requestAnimationFrame(frame);\n }\n requestAnimationFrame(frame);\n};\n\nconst Animometer: () => JSX.Element = () =>\n makeSample({\n name: 'Animometer',\n description: 'A WebGPU port of the Animometer MotionMark benchmark.',\n gui: true,\n init,\n sources: [\n {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n },\n {\n name: './animometer.wgsl',\n contents: animometerWGSL,\n editable: true,\n },\n ],\n filename: __filename,\n });\n\nexport default Animometer;\n"},{name:"./animometer.wgsl",contents:i,editable:!0}],filename:a});var u=s},9147:function(e){e.exports={canvasContainer:"SampleLayout_canvasContainer__zRR_l",sourceFileNav:"SampleLayout_sourceFileNav__ml48P",sourceFileContainer:"SampleLayout_sourceFileContainer__3s84x"}}}]); \ No newline at end of file +(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[841],{5671:function(e,n,t){"use strict";t.d(n,{Tl:function(){return f},hu:function(){return c}});var r=t(5893),i=t(9008),a=t.n(i),o=t(1163),s=t(7294),u=t(9147),l=t.n(u);t(7319);let d=e=>{let n=(0,s.useRef)(null),i=(0,s.useMemo)(()=>e.sources.map(e=>{let{name:n,contents:i}=e;return{name:n,...function(e){let n;let i=null;{i=document.createElement("div");let a=t(4631);n=a(i,{lineNumbers:!0,lineWrapping:!0,theme:"monokai",readOnly:!0})}return{Container:function(t){return(0,r.jsx)("div",{...t,children:(0,r.jsx)("div",{ref(t){i&&t&&(t.appendChild(i),n.setOption("value",e))}})})}}}(i)}}),e.sources),u=(0,s.useRef)(null),d=(0,s.useMemo)(()=>{if(e.gui){let n=t(4376);return new n.GUI({autoPlace:!1})}},[]),f=(0,s.useRef)(null),c=(0,s.useMemo)(()=>{if(e.stats){let n=t(2792);return new n}},[]),m=(0,o.useRouter)(),p=m.asPath.match(/#([a-zA-Z0-9\.\/]+)/),[g,y]=(0,s.useState)(null),[E,v]=(0,s.useState)(null);return(0,s.useEffect)(()=>{if(p?v(p[1]):v(i[0].name),d&&u.current)for(u.current.appendChild(d.domElement);d.__controllers.length>0;)d.__controllers[0].remove();c&&f.current&&(c.dom.style.position="absolute",c.showPanel(1),f.current.appendChild(c.dom));let t={active:!0},r=()=>{t.active=!1};try{let a=n.current;if(!a)throw Error("The canvas is not available");let o=e.init({canvas:a,pageState:t,gui:d,stats:c});o instanceof Promise&&o.catch(e=>{console.error(e),y(e)})}catch(s){console.error(s),y(s)}return r},[]),(0,r.jsxs)("main",{children:[(0,r.jsxs)(a(),{children:[(0,r.jsx)("style",{dangerouslySetInnerHTML:{__html:"\n .CodeMirror {\n height: auto !important;\n margin: 1em 0;\n }\n\n .CodeMirror-scroll {\n height: auto !important;\n overflow: visible !important;\n }\n "}}),(0,r.jsx)("title",{children:"".concat(e.name," - WebGPU Samples")}),(0,r.jsx)("meta",{name:"description",content:e.description}),(0,r.jsx)("meta",{httpEquiv:"origin-trial",content:e.originTrial})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("h1",{children:e.name}),(0,r.jsx)("a",{target:"_blank",rel:"noreferrer",href:"https://github.com/".concat("webgpu/webgpu-samples","/tree/main/").concat(e.filename),children:"See it on Github!"}),(0,r.jsx)("p",{children:e.description}),g?(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)("p",{children:"Something went wrong. Do your browser and device support WebGPU?"}),(0,r.jsx)("p",{children:"".concat(g)})]}):null]}),(0,r.jsxs)("div",{className:l().canvasContainer,children:[(0,r.jsx)("div",{style:{position:"absolute",left:10},ref:f}),(0,r.jsx)("div",{style:{position:"absolute",right:10},ref:u}),(0,r.jsx)("canvas",{ref:n})]}),(0,r.jsxs)("div",{children:[(0,r.jsx)("nav",{className:l().sourceFileNav,children:(0,r.jsx)("ul",{children:i.map((e,n)=>(0,r.jsx)("li",{children:(0,r.jsx)("a",{href:"#".concat(e.name),"data-active":E==e.name,onClick(){v(e.name)},children:e.name})},n))})}),i.map((e,n)=>(0,r.jsx)(e.Container,{className:l().sourceFileContainer,"data-active":E==e.name},n))]})]})},f=e=>(0,r.jsx)(d,{...e});function c(e,n){if(!e)throw Error(n)}},841:function(e,n,t){"use strict";t.r(n),t.d(n,{default:function(){return u}});var r=t(5671),i="struct Time {\n value : f32,\n}\n\nstruct Uniforms {\n scale : f32,\n offsetX : f32,\n offsetY : f32,\n scalar : f32,\n scalarOffset : f32,\n}\n\n@binding(0) @group(0) var time : Time;\n@binding(0) @group(1) var uniforms : Uniforms;\n\nstruct VertexOutput {\n @builtin(position) Position : vec4,\n @location(0) v_color : vec4,\n}\n\n@vertex\nfn vert_main(\n @location(0) position : vec4,\n @location(1) color : vec4\n) -> VertexOutput {\n var fade = (uniforms.scalarOffset + time.value * uniforms.scalar / 10.0) % 1.0;\n if (fade < 0.5) {\n fade = fade * 2.0;\n } else {\n fade = (1.0 - fade) * 2.0;\n }\n var xpos = position.x * uniforms.scale;\n var ypos = position.y * uniforms.scale;\n var angle = 3.14159 * 2.0 * fade;\n var xrot = xpos * cos(angle) - ypos * sin(angle);\n var yrot = xpos * sin(angle) + ypos * cos(angle);\n xpos = xrot + uniforms.offsetX;\n ypos = yrot + uniforms.offsetY;\n\n var output : VertexOutput;\n output.v_color = vec4(fade, 1.0 - fade, 0.0, 1.0) + color;\n output.Position = vec4(xpos, ypos, 0.0, 1.0);\n return output;\n}\n\n@fragment\nfn frag_main(@location(0) v_color : vec4) -> @location(0) vec4 {\n return v_color;\n}\n",a="src/sample/animometer/main.ts";let o=async e=>{let n,t,a,{canvas:o,pageState:s,gui:u}=e,l=await navigator.gpu.requestAdapter();(0,r.hu)(l,"requestAdapter returned null");let d=await l.requestDevice();if(!s.active)return;let f=document.createElement("div");f.style.color="white",f.style.background="black",f.style.position="absolute",f.style.top="10px",f.style.left="10px";let c=document.createElement("pre");f.appendChild(c),o.parentNode?o.parentNode.appendChild(f):console.error("canvas.parentNode is null");let m=new URLSearchParams(window.location.search),p={numTriangles:Number(m.get("numTriangles"))||2e4,renderBundles:Boolean(m.get("renderBundles")),dynamicOffsets:Boolean(m.get("dynamicOffsets"))},g=o.getContext("webgpu"),y=window.devicePixelRatio||1;o.width=o.clientWidth*y,o.height=o.clientHeight*y;let E=navigator.gpu.getPreferredCanvasFormat();g.configure({device:d,format:E,alphaMode:"premultiplied",usage:GPUTextureUsage.COPY_DST|GPUTextureUsage.RENDER_ATTACHMENT});let v=d.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",minBindingSize:4}}]}),h=d.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",minBindingSize:20}}]}),T=d.createBindGroupLayout({entries:[{binding:0,visibility:GPUShaderStage.VERTEX,buffer:{type:"uniform",hasDynamicOffset:!0,minBindingSize:20}}]}),B=4*Float32Array.BYTES_PER_ELEMENT,b=d.createPipelineLayout({bindGroupLayouts:[v,h]}),P=d.createPipelineLayout({bindGroupLayouts:[v,T]}),_=d.createShaderModule({code:i}),x={layout:"auto",vertex:{module:_,entryPoint:"vert_main",buffers:[{arrayStride:2*B,stepMode:"vertex",attributes:[{shaderLocation:0,offset:0,format:"float32x4"},{shaderLocation:1,offset:B,format:"float32x4"}]}]},fragment:{module:_,entryPoint:"frag_main",targets:[{format:E}]},primitive:{topology:"triangle-list",frontFace:"ccw",cullMode:"none"}},S=d.createRenderPipeline({...x,layout:b}),A=d.createRenderPipeline({...x,layout:P}),w=d.createBuffer({size:6*B,usage:GPUBufferUsage.VERTEX,mappedAtCreation:!0});function G(){let e;let n=p.numTriangles,t=5*Float32Array.BYTES_PER_ELEMENT,r=256*Math.ceil(t/256),i=r/Float32Array.BYTES_PER_ELEMENT,a=d.createBuffer({size:n*r+Float32Array.BYTES_PER_ELEMENT,usage:GPUBufferUsage.COPY_DST|GPUBufferUsage.UNIFORM}),o=new Float32Array(n*i),s=Array(n);for(let u=0;u{F=G()};void 0===u?console.error("GUI not initialized"):(u.add(p,"numTriangles",0,2e5).step(1).onFinishChange(L),u.add(p,"renderBundles"),u.add(p,"dynamicOffsets"));let M=!0;requestAnimationFrame(function e(r){if(!s.active)return;let i=0;void 0!==n&&(i=r-n),n=r;let o=performance.now();F(r);let u=performance.now()-o;void 0===a&&(a=i),void 0===t&&(t=u),a=.8*a+.2*i,t=.8*t+.2*u,M&&(c.innerHTML="Avg Javascript: ".concat(t.toFixed(2)," ms\nAvg Frame: ").concat(a.toFixed(2)," ms"),M=!1,setTimeout(()=>{M=!0},100)),requestAnimationFrame(e)})},s=()=>(0,r.Tl)({name:"Animometer",description:"A WebGPU port of the Animometer MotionMark benchmark.",gui:!0,init:o,sources:[{name:a.substring(22),contents:"import { assert, makeSample, SampleInit } from '../../components/SampleLayout';\n\nimport animometerWGSL from './animometer.wgsl';\n\nconst init: SampleInit = async ({ canvas, pageState, gui }) => {\n const adapter = await navigator.gpu.requestAdapter();\n assert(adapter, 'requestAdapter returned null');\n const device = await adapter.requestDevice();\n\n if (!pageState.active) return;\n\n const perfDisplayContainer = document.createElement('div');\n perfDisplayContainer.style.color = 'white';\n perfDisplayContainer.style.background = 'black';\n perfDisplayContainer.style.position = 'absolute';\n perfDisplayContainer.style.top = '10px';\n perfDisplayContainer.style.left = '10px';\n\n const perfDisplay = document.createElement('pre');\n perfDisplayContainer.appendChild(perfDisplay);\n if (canvas.parentNode) {\n canvas.parentNode.appendChild(perfDisplayContainer);\n } else {\n console.error('canvas.parentNode is null');\n }\n\n const params = new URLSearchParams(window.location.search);\n const settings = {\n numTriangles: Number(params.get('numTriangles')) || 20000,\n renderBundles: Boolean(params.get('renderBundles')),\n dynamicOffsets: Boolean(params.get('dynamicOffsets')),\n };\n\n const context = canvas.getContext('webgpu') as GPUCanvasContext;\n\n const devicePixelRatio = window.devicePixelRatio || 1;\n canvas.width = canvas.clientWidth * devicePixelRatio;\n canvas.height = canvas.clientHeight * devicePixelRatio;\n const presentationFormat = navigator.gpu.getPreferredCanvasFormat();\n\n context.configure({\n device,\n format: presentationFormat,\n alphaMode: 'premultiplied',\n usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.RENDER_ATTACHMENT,\n });\n\n const timeBindGroupLayout = device.createBindGroupLayout({\n entries: [\n {\n binding: 0,\n visibility: GPUShaderStage.VERTEX,\n buffer: {\n type: 'uniform',\n minBindingSize: 4,\n },\n },\n ],\n });\n\n const bindGroupLayout = device.createBindGroupLayout({\n entries: [\n {\n binding: 0,\n visibility: GPUShaderStage.VERTEX,\n buffer: {\n type: 'uniform',\n minBindingSize: 20,\n },\n },\n ],\n });\n\n const dynamicBindGroupLayout = device.createBindGroupLayout({\n entries: [\n {\n binding: 0,\n visibility: GPUShaderStage.VERTEX,\n buffer: {\n type: 'uniform',\n hasDynamicOffset: true,\n minBindingSize: 20,\n },\n },\n ],\n });\n\n const vec4Size = 4 * Float32Array.BYTES_PER_ELEMENT;\n const pipelineLayout = device.createPipelineLayout({\n bindGroupLayouts: [timeBindGroupLayout, bindGroupLayout],\n });\n const dynamicPipelineLayout = device.createPipelineLayout({\n bindGroupLayouts: [timeBindGroupLayout, dynamicBindGroupLayout],\n });\n\n const shaderModule = device.createShaderModule({\n code: animometerWGSL,\n });\n const pipelineDesc: GPURenderPipelineDescriptor = {\n layout: 'auto',\n vertex: {\n module: shaderModule,\n entryPoint: 'vert_main',\n buffers: [\n {\n // vertex buffer\n arrayStride: 2 * vec4Size,\n stepMode: 'vertex',\n attributes: [\n {\n // vertex positions\n shaderLocation: 0,\n offset: 0,\n format: 'float32x4',\n },\n {\n // vertex colors\n shaderLocation: 1,\n offset: vec4Size,\n format: 'float32x4',\n },\n ],\n },\n ],\n },\n fragment: {\n module: shaderModule,\n entryPoint: 'frag_main',\n targets: [\n {\n format: presentationFormat,\n },\n ],\n },\n primitive: {\n topology: 'triangle-list',\n frontFace: 'ccw',\n cullMode: 'none',\n },\n };\n\n const pipeline = device.createRenderPipeline({\n ...pipelineDesc,\n layout: pipelineLayout,\n });\n\n const dynamicPipeline = device.createRenderPipeline({\n ...pipelineDesc,\n layout: dynamicPipelineLayout,\n });\n\n const vertexBuffer = device.createBuffer({\n size: 2 * 3 * vec4Size,\n usage: GPUBufferUsage.VERTEX,\n mappedAtCreation: true,\n });\n\n // prettier-ignore\n new Float32Array(vertexBuffer.getMappedRange()).set([\n // position data /**/ color data\n 0, 0.1, 0, 1, /**/ 1, 0, 0, 1,\n -0.1, -0.1, 0, 1, /**/ 0, 1, 0, 1,\n 0.1, -0.1, 0, 1, /**/ 0, 0, 1, 1,\n ]);\n vertexBuffer.unmap();\n\n function configure() {\n const numTriangles = settings.numTriangles;\n const uniformBytes = 5 * Float32Array.BYTES_PER_ELEMENT;\n const alignedUniformBytes = Math.ceil(uniformBytes / 256) * 256;\n const alignedUniformFloats =\n alignedUniformBytes / Float32Array.BYTES_PER_ELEMENT;\n const uniformBuffer = device.createBuffer({\n size: numTriangles * alignedUniformBytes + Float32Array.BYTES_PER_ELEMENT,\n usage: GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM,\n });\n const uniformBufferData = new Float32Array(\n numTriangles * alignedUniformFloats\n );\n const bindGroups = new Array(numTriangles);\n for (let i = 0; i < numTriangles; ++i) {\n uniformBufferData[alignedUniformFloats * i + 0] =\n Math.random() * 0.2 + 0.2; // scale\n uniformBufferData[alignedUniformFloats * i + 1] =\n 0.9 * 2 * (Math.random() - 0.5); // offsetX\n uniformBufferData[alignedUniformFloats * i + 2] =\n 0.9 * 2 * (Math.random() - 0.5); // offsetY\n uniformBufferData[alignedUniformFloats * i + 3] =\n Math.random() * 1.5 + 0.5; // scalar\n uniformBufferData[alignedUniformFloats * i + 4] = Math.random() * 10; // scalarOffset\n\n bindGroups[i] = device.createBindGroup({\n layout: bindGroupLayout,\n entries: [\n {\n binding: 0,\n resource: {\n buffer: uniformBuffer,\n offset: i * alignedUniformBytes,\n size: 6 * Float32Array.BYTES_PER_ELEMENT,\n },\n },\n ],\n });\n }\n\n const dynamicBindGroup = device.createBindGroup({\n layout: dynamicBindGroupLayout,\n entries: [\n {\n binding: 0,\n resource: {\n buffer: uniformBuffer,\n offset: 0,\n size: 6 * Float32Array.BYTES_PER_ELEMENT,\n },\n },\n ],\n });\n\n const timeOffset = numTriangles * alignedUniformBytes;\n const timeBindGroup = device.createBindGroup({\n layout: timeBindGroupLayout,\n entries: [\n {\n binding: 0,\n resource: {\n buffer: uniformBuffer,\n offset: timeOffset,\n size: Float32Array.BYTES_PER_ELEMENT,\n },\n },\n ],\n });\n\n // writeBuffer too large may OOM. TODO: The browser should internally chunk uploads.\n const maxMappingLength =\n (14 * 1024 * 1024) / Float32Array.BYTES_PER_ELEMENT;\n for (\n let offset = 0;\n offset < uniformBufferData.length;\n offset += maxMappingLength\n ) {\n const uploadCount = Math.min(\n uniformBufferData.length - offset,\n maxMappingLength\n );\n\n device.queue.writeBuffer(\n uniformBuffer,\n offset * Float32Array.BYTES_PER_ELEMENT,\n uniformBufferData.buffer,\n uniformBufferData.byteOffset + offset * Float32Array.BYTES_PER_ELEMENT,\n uploadCount * Float32Array.BYTES_PER_ELEMENT\n );\n }\n\n function recordRenderPass(\n passEncoder: GPURenderBundleEncoder | GPURenderPassEncoder\n ) {\n if (settings.dynamicOffsets) {\n passEncoder.setPipeline(dynamicPipeline);\n } else {\n passEncoder.setPipeline(pipeline);\n }\n passEncoder.setVertexBuffer(0, vertexBuffer);\n passEncoder.setBindGroup(0, timeBindGroup);\n const dynamicOffsets = [0];\n for (let i = 0; i < numTriangles; ++i) {\n if (settings.dynamicOffsets) {\n dynamicOffsets[0] = i * alignedUniformBytes;\n passEncoder.setBindGroup(1, dynamicBindGroup, dynamicOffsets);\n } else {\n passEncoder.setBindGroup(1, bindGroups[i]);\n }\n passEncoder.draw(3);\n }\n }\n\n let startTime: number | undefined = undefined;\n const uniformTime = new Float32Array([0]);\n\n const renderPassDescriptor = {\n colorAttachments: [\n {\n view: undefined as GPUTextureView, // Assigned later\n clearValue: { r: 0.0, g: 0.0, b: 0.0, a: 1.0 },\n loadOp: 'clear' as const,\n storeOp: 'store' as const,\n },\n ],\n };\n\n const renderBundleEncoder = device.createRenderBundleEncoder({\n colorFormats: [presentationFormat],\n });\n recordRenderPass(renderBundleEncoder);\n const renderBundle = renderBundleEncoder.finish();\n\n return function doDraw(timestamp: number) {\n if (startTime === undefined) {\n startTime = timestamp;\n }\n uniformTime[0] = (timestamp - startTime) / 1000;\n device.queue.writeBuffer(uniformBuffer, timeOffset, uniformTime.buffer);\n\n renderPassDescriptor.colorAttachments[0].view = context\n .getCurrentTexture()\n .createView();\n\n const commandEncoder = device.createCommandEncoder();\n const passEncoder = commandEncoder.beginRenderPass(renderPassDescriptor);\n\n if (settings.renderBundles) {\n passEncoder.executeBundles([renderBundle]);\n } else {\n recordRenderPass(passEncoder);\n }\n\n passEncoder.end();\n device.queue.submit([commandEncoder.finish()]);\n };\n }\n\n let doDraw = configure();\n\n const updateSettings = () => {\n doDraw = configure();\n };\n if (gui === undefined) {\n console.error('GUI not initialized');\n } else {\n gui\n .add(settings, 'numTriangles', 0, 200000)\n .step(1)\n .onFinishChange(updateSettings);\n gui.add(settings, 'renderBundles');\n gui.add(settings, 'dynamicOffsets');\n }\n\n let previousFrameTimestamp: number | undefined = undefined;\n let jsTimeAvg: number | undefined = undefined;\n let frameTimeAvg: number | undefined = undefined;\n let updateDisplay = true;\n\n function frame(timestamp: number) {\n // Sample is no longer the active page.\n if (!pageState.active) return;\n\n let frameTime = 0;\n if (previousFrameTimestamp !== undefined) {\n frameTime = timestamp - previousFrameTimestamp;\n }\n previousFrameTimestamp = timestamp;\n\n const start = performance.now();\n doDraw(timestamp);\n const jsTime = performance.now() - start;\n if (frameTimeAvg === undefined) {\n frameTimeAvg = frameTime;\n }\n if (jsTimeAvg === undefined) {\n jsTimeAvg = jsTime;\n }\n\n const w = 0.2;\n frameTimeAvg = (1 - w) * frameTimeAvg + w * frameTime;\n jsTimeAvg = (1 - w) * jsTimeAvg + w * jsTime;\n\n if (updateDisplay) {\n perfDisplay.innerHTML = `Avg Javascript: ${jsTimeAvg.toFixed(\n 2\n )} ms\\nAvg Frame: ${frameTimeAvg.toFixed(2)} ms`;\n updateDisplay = false;\n setTimeout(() => {\n updateDisplay = true;\n }, 100);\n }\n requestAnimationFrame(frame);\n }\n requestAnimationFrame(frame);\n};\n\nconst Animometer: () => JSX.Element = () =>\n makeSample({\n name: 'Animometer',\n description: 'A WebGPU port of the Animometer MotionMark benchmark.',\n gui: true,\n init,\n sources: [\n {\n name: __filename.substring(__dirname.length + 1),\n contents: __SOURCE__,\n },\n {\n name: './animometer.wgsl',\n contents: animometerWGSL,\n editable: true,\n },\n ],\n filename: __filename,\n });\n\nexport default Animometer;\n"},{name:"./animometer.wgsl",contents:i,editable:!0}],filename:a});var u=s},9147:function(e){e.exports={canvasContainer:"SampleLayout_canvasContainer__zRR_l",sourceFileNav:"SampleLayout_sourceFileNav__ml48P",sourceFileContainer:"SampleLayout_sourceFileContainer__3s84x"}}}]); \ No newline at end of file diff --git a/_next/static/chunks/webpack-96bf41c256b905d0.js b/_next/static/chunks/webpack-52596f3636814f01.js similarity index 96% rename from _next/static/chunks/webpack-96bf41c256b905d0.js rename to _next/static/chunks/webpack-52596f3636814f01.js index 325b58d0..b90be45d 100644 --- a/_next/static/chunks/webpack-96bf41c256b905d0.js +++ b/_next/static/chunks/webpack-52596f3636814f01.js @@ -1 +1 @@ -!function(){"use strict";var e,t,r,n,a,o,f,i,u,c,d={},b={};function l(e){var t=b[e];if(void 0!==t)return t.exports;var r=b[e]={exports:{}},n=!0;try{d[e].call(r.exports,r,r.exports,l),n=!1}finally{n&&delete b[e]}return r.exports}l.m=d,e=[],l.O=function(t,r,n,a){if(r){a=a||0;for(var o=e.length;o>0&&e[o-1][2]>a;o--)e[o]=e[o-1];e[o]=[r,n,a];return}for(var f=1/0,o=0;o=a&&Object.keys(l.O).every(function(e){return l.O[e](r[u])})?r.splice(u--,1):(i=!1,a0&&e[o-1][2]>a;o--)e[o]=e[o-1];e[o]=[r,n,a];return}for(var f=1/0,o=0;o=a&&Object.keys(l.O).every(function(e){return l.O[e](r[u])})?r.splice(u--,1):(i=!1,aWebGPU Samples \ No newline at end of file +WebGPU Samples \ No newline at end of file diff --git a/samples/A-buffer.html b/samples/A-buffer.html index e674c501..584a2007 100644 --- a/samples/A-buffer.html +++ b/samples/A-buffer.html @@ -10,6 +10,6 @@ } A-Buffer - WebGPU Samples

A-Buffer

See it on Github!

Demonstrates order independent transparency using a per-pixel + limiting memory usage (when required)."/>

\ No newline at end of file + limiting memory usage (when required).

\ No newline at end of file diff --git a/samples/animometer.html b/samples/animometer.html index a64f91f6..04b72abe 100644 --- a/samples/animometer.html +++ b/samples/animometer.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Animometer - WebGPU Samples \ No newline at end of file + Animometer - WebGPU Samples \ No newline at end of file diff --git a/samples/bitonicSort.html b/samples/bitonicSort.html index f4c9a21a..f3f79e6b 100644 --- a/samples/bitonicSort.html +++ b/samples/bitonicSort.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Bitonic Sort - WebGPU Samples

Bitonic Sort

See it on Github!

A naive bitonic sort algorithm executed on the GPU, based on tgfrerer's implementation at poniesandlight.co.uk/reflect/bitonic_merge_sort/. Each invocation of the bitonic sort shader dispatches a workgroup containing elements/2 threads. The GUI's Execution Information folder contains information about the sort's current state. The visualizer displays the sort's results as colored cells sorted from brightest to darkest.

\ No newline at end of file + Bitonic Sort - WebGPU Samples

Bitonic Sort

See it on Github!

A naive bitonic sort algorithm executed on the GPU, based on tgfrerer's implementation at poniesandlight.co.uk/reflect/bitonic_merge_sort/. Each invocation of the bitonic sort shader dispatches a workgroup containing elements/2 threads. The GUI's Execution Information folder contains information about the sort's current state. The visualizer displays the sort's results as colored cells sorted from brightest to darkest.

\ No newline at end of file diff --git a/samples/cameras.html b/samples/cameras.html index 1aadd184..d1cebf20 100644 --- a/samples/cameras.html +++ b/samples/cameras.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Cameras - WebGPU Samples \ No newline at end of file + Cameras - WebGPU Samples \ No newline at end of file diff --git a/samples/computeBoids.html b/samples/computeBoids.html index 4ce06dfe..7cf24cb2 100644 --- a/samples/computeBoids.html +++ b/samples/computeBoids.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Compute Boids - WebGPU Samples \ No newline at end of file + Compute Boids - WebGPU Samples \ No newline at end of file diff --git a/samples/cornell.html b/samples/cornell.html index f3e52660..de387744 100644 --- a/samples/cornell.html +++ b/samples/cornell.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Cornell box - WebGPU Samples \ No newline at end of file + Cornell box - WebGPU Samples \ No newline at end of file diff --git a/samples/cubemap.html b/samples/cubemap.html index 19d0061c..aa156d68 100644 --- a/samples/cubemap.html +++ b/samples/cubemap.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Cubemap - WebGPU Samples \ No newline at end of file + Cubemap - WebGPU Samples \ No newline at end of file diff --git a/samples/deferredRendering.html b/samples/deferredRendering.html index 94fd736c..25ffe849 100644 --- a/samples/deferredRendering.html +++ b/samples/deferredRendering.html @@ -16,7 +16,7 @@ We also update light position in a compute shader, where further operations like tile/cluster culling could happen. The debug view shows the depth buffer on the left (flipped and scaled a bit to make it more visible), the normal G buffer in the middle, and the albedo G-buffer on the right side of the screen. - "/>

Deferred Rendering

See it on Github!

This example shows how to do deferred rendering with webgpu. + "/>

Deferred Rendering

See it on Github!

This example shows how to do deferred rendering with webgpu. Render geometry info to multiple targets in the gBuffers in the first pass. In this sample we have 2 gBuffers for normals and albedo, along with a depth texture. And then do the lighting in a second pass with per fragment data read from gBuffers so it's independent of scene complexity. @@ -24,4 +24,4 @@ We also update light position in a compute shader, where further operations like tile/cluster culling could happen. The debug view shows the depth buffer on the left (flipped and scaled a bit to make it more visible), the normal G buffer in the middle, and the albedo G-buffer on the right side of the screen. -

\ No newline at end of file +

\ No newline at end of file diff --git a/samples/fractalCube.html b/samples/fractalCube.html index 657feed8..8999d66b 100644 --- a/samples/fractalCube.html +++ b/samples/fractalCube.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Fractal Cube - WebGPU Samples \ No newline at end of file + Fractal Cube - WebGPU Samples \ No newline at end of file diff --git a/samples/gameOfLife.html b/samples/gameOfLife.html index 05fdf5c5..a019ef47 100644 --- a/samples/gameOfLife.html +++ b/samples/gameOfLife.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Conway's Game of Life - WebGPU Samples \ No newline at end of file + Conway's Game of Life - WebGPU Samples \ No newline at end of file diff --git a/samples/helloTriangle.html b/samples/helloTriangle.html index f71535c5..d33b29af 100644 --- a/samples/helloTriangle.html +++ b/samples/helloTriangle.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Hello Triangle - WebGPU Samples \ No newline at end of file + Hello Triangle - WebGPU Samples \ No newline at end of file diff --git a/samples/helloTriangleMSAA.html b/samples/helloTriangleMSAA.html index 92543a1e..f6c75546 100644 --- a/samples/helloTriangleMSAA.html +++ b/samples/helloTriangleMSAA.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Hello Triangle MSAA - WebGPU Samples \ No newline at end of file + Hello Triangle MSAA - WebGPU Samples \ No newline at end of file diff --git a/samples/imageBlur.html b/samples/imageBlur.html index 7afa31f3..7e592a3a 100644 --- a/samples/imageBlur.html +++ b/samples/imageBlur.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Image Blur - WebGPU Samples \ No newline at end of file + Image Blur - WebGPU Samples \ No newline at end of file diff --git a/samples/instancedCube.html b/samples/instancedCube.html index 0ddd90e4..68354311 100644 --- a/samples/instancedCube.html +++ b/samples/instancedCube.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Instanced Cube - WebGPU Samples \ No newline at end of file + Instanced Cube - WebGPU Samples \ No newline at end of file diff --git a/samples/particles.html b/samples/particles.html index 571a1edc..59d63a87 100644 --- a/samples/particles.html +++ b/samples/particles.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Particles - WebGPU Samples \ No newline at end of file + Particles - WebGPU Samples \ No newline at end of file diff --git a/samples/renderBundles.html b/samples/renderBundles.html index 25464a94..f905530d 100644 --- a/samples/renderBundles.html +++ b/samples/renderBundles.html @@ -11,7 +11,7 @@ Render Bundles - WebGPU Samples

Render Bundles

See it on Github!

This example shows how to use render bundles. It renders a large number of + of instancing to reduce draw overhead.)"/>

Render Bundles

See it on Github!

This example shows how to use render bundles. It renders a large number of meshes individually as a proxy for a more complex scene in order to demonstrate the reduction in JavaScript time spent to issue render commands. (Typically a scene like this would make use - of instancing to reduce draw overhead.)

\ No newline at end of file + of instancing to reduce draw overhead.)

\ No newline at end of file diff --git a/samples/resizeCanvas.html b/samples/resizeCanvas.html index 105aedf1..49edc2bd 100644 --- a/samples/resizeCanvas.html +++ b/samples/resizeCanvas.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Resize Canvas - WebGPU Samples \ No newline at end of file + Resize Canvas - WebGPU Samples \ No newline at end of file diff --git a/samples/reversedZ.html b/samples/reversedZ.html index 04008768..16a7da21 100644 --- a/samples/reversedZ.html +++ b/samples/reversedZ.html @@ -17,7 +17,7 @@ Related reading: https://developer.nvidia.com/content/depth-precision-visualized https://thxforthefish.com/posts/reverse_z/ - "/>

Reversed Z

See it on Github!

This example shows the use of reversed z technique for better utilization of depth buffer precision. + "/>

Reversed Z

See it on Github!

This example shows the use of reversed z technique for better utilization of depth buffer precision. The left column uses regular method, while the right one uses reversed z technique. Both are using depth32float as their depth buffer format. A set of red and green planes are positioned very close to each other. Higher sets are placed further from camera (and are scaled for better visual purpose). @@ -26,4 +26,4 @@ Related reading: https://developer.nvidia.com/content/depth-precision-visualized https://thxforthefish.com/posts/reverse_z/ -

\ No newline at end of file +

\ No newline at end of file diff --git a/samples/rotatingCube.html b/samples/rotatingCube.html index d7b7f7f5..c075f5bf 100644 --- a/samples/rotatingCube.html +++ b/samples/rotatingCube.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Rotating Cube - WebGPU Samples \ No newline at end of file + Rotating Cube - WebGPU Samples \ No newline at end of file diff --git a/samples/samplerParameters.html b/samples/samplerParameters.html index 80b69d42..7ad8f8cb 100644 --- a/samples/samplerParameters.html +++ b/samples/samplerParameters.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Sampler Parameters - WebGPU Samples

Sampler Parameters

See it on Github!

Visualizes what all the sampler parameters do. Shows a textured plane at various scales (rotated, head-on, in perspective, and in vanishing perspective). The bottom-right view shows the raw contents of the 4 mipmap levels of the test texture (16x16, 8x8, 4x4, and 2x2).

\ No newline at end of file + Sampler Parameters - WebGPU Samples

Sampler Parameters

See it on Github!

Visualizes what all the sampler parameters do. Shows a textured plane at various scales (rotated, head-on, in perspective, and in vanishing perspective). The bottom-right view shows the raw contents of the 4 mipmap levels of the test texture (16x16, 8x8, 4x4, and 2x2).

\ No newline at end of file diff --git a/samples/shadowMapping.html b/samples/shadowMapping.html index cad1882b..246aba14 100644 --- a/samples/shadowMapping.html +++ b/samples/shadowMapping.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Shadow Mapping - WebGPU Samples \ No newline at end of file + Shadow Mapping - WebGPU Samples \ No newline at end of file diff --git a/samples/texturedCube.html b/samples/texturedCube.html index 51ee368e..150c2ae8 100644 --- a/samples/texturedCube.html +++ b/samples/texturedCube.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Textured Cube - WebGPU Samples \ No newline at end of file + Textured Cube - WebGPU Samples \ No newline at end of file diff --git a/samples/twoCubes.html b/samples/twoCubes.html index b71a958c..525e2771 100644 --- a/samples/twoCubes.html +++ b/samples/twoCubes.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Two Cubes - WebGPU Samples \ No newline at end of file + Two Cubes - WebGPU Samples \ No newline at end of file diff --git a/samples/videoUploading.html b/samples/videoUploading.html index c5072916..77ae2d33 100644 --- a/samples/videoUploading.html +++ b/samples/videoUploading.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Video Uploading - WebGPU Samples \ No newline at end of file + Video Uploading - WebGPU Samples \ No newline at end of file diff --git a/samples/videoUploadingWebCodecs.html b/samples/videoUploadingWebCodecs.html index ad4c69c6..67ecf6b1 100644 --- a/samples/videoUploadingWebCodecs.html +++ b/samples/videoUploadingWebCodecs.html @@ -8,4 +8,4 @@ height: auto !important; overflow: visible !important; } - Video Uploading with WebCodecs - WebGPU Samples \ No newline at end of file + Video Uploading with WebCodecs - WebGPU Samples \ No newline at end of file diff --git a/samples/worker.html b/samples/worker.html index 7499e345..c0d2692a 100644 --- a/samples/worker.html +++ b/samples/worker.html @@ -10,6 +10,6 @@ } WebGPU in a Worker - WebGPU Samples

WebGPU in a Worker

See it on Github!

This example shows one method of using WebGPU in a web worker and presenting to + which is then transferred to the worker where all the WebGPU calls are made."/>

\ No newline at end of file + which is then transferred to the worker where all the WebGPU calls are made.

\ No newline at end of file