From 441ab63660c5435130d43baeb7ca51de0945600c Mon Sep 17 00:00:00 2001 From: Noelle Calliope <1581674+mekanoe@users.noreply.github.com> Date: Fri, 6 Oct 2023 22:37:05 -0400 Subject: [PATCH] more webgpu scaffolding --- .vscode/settings.json | 3 + build.ts | 2 +- bun.lockb | Bin 53631 -> 54484 bytes generate.ts | 2 + html/001-platform-provenance/main.js | 6 +- html/002-webgpu-instead/main.js | 2 +- html/chunk-32eefd9b41c928dd.js | 5 ++ html/chunk-9587381547d8adb9.js | 5 -- stuff.d.ts => index.d.ts | 3 + package.json | 6 +- src/002-webgpu-instead/main.ts | 7 +- src/002-webgpu-instead/rainbow-plane.wgsl | 49 +++++++++++ src/app.js | 2 +- src/meshes/plane.ts | 0 src/renderer/mesh.ts | 16 ++++ src/renderer/oops.wgsl | 26 ++++++ src/renderer/shader.ts | 19 +++++ src/{telemetry.js => renderer/telemetry.ts} | 18 ++-- src/renderer/webgpu.ts | 86 ++++++++++++++++++++ src/webgpu-app.js | 40 --------- tsconfig.json | 30 ++++--- 21 files changed, 253 insertions(+), 74 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 html/chunk-32eefd9b41c928dd.js delete mode 100644 html/chunk-9587381547d8adb9.js rename stuff.d.ts => index.d.ts (70%) create mode 100644 src/002-webgpu-instead/rainbow-plane.wgsl create mode 100644 src/meshes/plane.ts create mode 100644 src/renderer/mesh.ts create mode 100644 src/renderer/oops.wgsl create mode 100644 src/renderer/shader.ts rename src/{telemetry.js => renderer/telemetry.ts} (75%) create mode 100644 src/renderer/webgpu.ts delete mode 100644 src/webgpu-app.js diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..25fa621 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "typescript.tsdk": "node_modules/typescript/lib" +} diff --git a/build.ts b/build.ts index 0193d84..57247d3 100644 --- a/build.ts +++ b/build.ts @@ -31,5 +31,5 @@ const publics = globSync("src/public/*"); for (const file of publics) { const dest = file.replace("src/public/", "html/"); cpSync(file, dest); - console.log(chalk.yellow(` -> html/${dest}...`)); + console.log(chalk.yellow(` -> ${dest}...`)); } diff --git a/bun.lockb b/bun.lockb index 01b6040677c82d9113b63ff9899372aaa9bd3227..01d3de194f3e29f0e494a20c2a87f6ab35a2bd9b 100755 GIT binary patch delta 12290 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 RTKkg2Gf`a982yEZ^>0m9>7M`q delta 11662 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 diff --git a/generate.ts b/generate.ts index 433e247..cc8774f 100644 --- a/generate.ts +++ b/generate.ts @@ -2,6 +2,7 @@ import indexTemplate from "./templates/index.html.txt"; import workTemplate from "./templates/work.html.txt"; import readmeTemplate from "./templates/README.md.txt"; import chalk from "chalk"; +import { mkdirSync } from "node:fs"; export const generate = async (works: string[]) => { const allWorks = works @@ -12,6 +13,7 @@ export const generate = async (works: string[]) => { for (const work of allWorks) { const html = `${workTemplate}`.replace(/##name##/g, work); + mkdirSync(`html/${work}`, { recursive: true }); await Bun.write(`html/${work}/index.html`, html); console.log(chalk.yellow(` -> html/${work}/index.html...`)); } diff --git a/html/001-platform-provenance/main.js b/html/001-platform-provenance/main.js index 38350db..d1b453f 100644 --- a/html/001-platform-provenance/main.js +++ b/html/001-platform-provenance/main.js @@ -1,4 +1,4 @@ -import{a as k} from"../chunk-9587381547d8adb9.js";class K{constructor(C){this.gl=C.gl,this.app=C,this.program=this.gl.createProgram()}attach(C,R){console.log("attaching shader",{type:C,source:R});const q=this.gl.createShader(C);if(this.gl.shaderSource(q,R),this.gl.compileShader(q),!this.gl.getShaderParameter(q,this.gl.COMPILE_STATUS))throw new Error("An error occurred compiling the shaders: "+this.gl.getShaderInfoLog(q));return this.gl.attachShader(this.program,q),this}link(){if(this.gl.linkProgram(this.program),!this.gl.getProgramParameter(this.program,this.gl.LINK_STATUS))throw new Error("Unable to initialize the shader program: "+this.gl.getProgramInfoLog(this.program));return console.log("shader linked"),this}location(C){if(C[0]==="a")return this.gl.getAttribLocation(this.program,C);else if(C[0]==="u")return this.gl.getUniformLocation(this.program,C)}updateTime(){const C=this.app.now(),R=Math.sin(C),q=Math.cos(C);this.gl.uniform1f(this.location("uTime"),C),this.gl.uniform1f(this.location("uSinTime"),R),this.gl.uniform1f(this.location("uCosTime"),q)}activate(C,R){this.gl.useProgram(this.program),this.gl.uniformMatrix4fv(this.location("uProjectionMatrix"),!1,C),this.gl.uniformMatrix4fv(this.location("uModelViewMatrix"),!1,R),this.updateTime()}}class P{constructor(C){this.gl=C.gl,this.app=C,this.vertexPositions=new Float32Array([]),this.positionBuffer=null,this.textureBuffer=null}initBuffer(C,R=this.gl.STATIC_DRAW){const q=this.gl.createBuffer();return this.gl.bindBuffer(this.gl.ARRAY_BUFFER,q),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array(C),R),q}attachShader(C){return this.shader=C,this.vertexPosition=C.location("aVertexPosition"),this.textureCoord=C.location("aTextureCoord"),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.vertexAttribPointer(this.vertexPosition,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.vertexPosition),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.textureBuffer),this.gl.vertexAttribPointer(this.textureCoord,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.textureCoord),this}draw2D(){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.shader.activate(this.app.projectionMatrix,this.app.modelViewMatrix),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,this.vertexPositions.length/2)}draw3D(){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.shader.activate(this.app.projectionMatrix,this.app.modelViewMatrix),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,this.vertexPositions.length/3)}}class U extends P{constructor(C){super(C);this.vertexPositions=new Float32Array([-1,-1,1,-1,-1,1,1,1]),this.positionBuffer=this.initBuffer(this.vertexPositions),this.textureBuffer=this.initBuffer(new Float32Array([0,0,1,0,0,1,1,1])),this.app.onUpdate(()=>this.draw2D())}}class Y{constructor(C={fov:45}){if(this._now=0,this.registry={onStart:[],onUpdate:[],onBeforeUpdate:[],onAfterUpdate:[]},this.config=C,this.canvas=document.querySelector("canvas"),this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight,this.gl=this.canvas.getContext("webgl2"),this.gl===null)throw document.querySelector("main").innerHTML="
your browser didn't let me set up webgl
",new Error("Unable to initialize WebGL. Your browser or machine may not support it.");const R=this.gl,q=this.config.fov*Math.PI/180,G=R.canvas.clientWidth/R.canvas.clientHeight,H=0.1,J=100,_=glMatrix.mat4.create();glMatrix.mat4.perspective(_,q,G,H,J);const I=glMatrix.mat4.create();glMatrix.mat4.translate(I,I,[-0,0,-6]),this.projectionMatrix=_,this.modelViewMatrix=I,this.clear(),this.onBeforeUpdate(()=>this.clear()),this.telemetry=new k(this)}clear(){const C=this.gl;C.clearColor(0,0,0,1),C.clearDepth(1),C.enable(C.DEPTH_TEST),C.depthFunc(C.LEQUAL),C.clear(C.COLOR_BUFFER_BIT|C.DEPTH_BUFFER_BIT)}onStart(C){this.registry.onStart.push(C)}onUpdate(C){this.registry.onUpdate.push(C)}onBeforeUpdate(C){this.registry.onBeforeUpdate.push(C)}onAfterUpdate(C){this.registry.onAfterUpdate.push(C)}start(){this.registry.onStart.forEach((C)=>C(this))}update(){this.registry.onBeforeUpdate.forEach((C)=>C(this)),this.registry.onUpdate.forEach((C)=>C(this)),this.registry.onAfterUpdate.forEach((C)=>C(this))}oneShot(){requestAnimationFrame((C)=>{this._now=C,this.start(),this.update()})}loop(){const C=(R)=>{this._now=R,this.update(),requestAnimationFrame(C)};requestAnimationFrame(C)}now(){return this._now}}var v=`precision highp float; +import{a as Y} from"../chunk-32eefd9b41c928dd.js";class E{constructor(C){this.gl=C.gl,this.app=C,this.program=this.gl.createProgram()}attach(C,D){console.log("attaching shader",{type:C,source:D});const R=this.gl.createShader(C);if(this.gl.shaderSource(R,D),this.gl.compileShader(R),!this.gl.getShaderParameter(R,this.gl.COMPILE_STATUS))throw new Error("An error occurred compiling the shaders: "+this.gl.getShaderInfoLog(R));return this.gl.attachShader(this.program,R),this}link(){if(this.gl.linkProgram(this.program),!this.gl.getProgramParameter(this.program,this.gl.LINK_STATUS))throw new Error("Unable to initialize the shader program: "+this.gl.getProgramInfoLog(this.program));return console.log("shader linked"),this}location(C){if(C[0]==="a")return this.gl.getAttribLocation(this.program,C);else if(C[0]==="u")return this.gl.getUniformLocation(this.program,C)}updateTime(){const C=this.app.now(),D=Math.sin(C),R=Math.cos(C);this.gl.uniform1f(this.location("uTime"),C),this.gl.uniform1f(this.location("uSinTime"),D),this.gl.uniform1f(this.location("uCosTime"),R)}activate(C,D){this.gl.useProgram(this.program),this.gl.uniformMatrix4fv(this.location("uProjectionMatrix"),!1,C),this.gl.uniformMatrix4fv(this.location("uModelViewMatrix"),!1,D),this.updateTime()}}class I{constructor(C){this.gl=C.gl,this.app=C,this.vertexPositions=new Float32Array([]),this.positionBuffer=null,this.textureBuffer=null}initBuffer(C,D=this.gl.STATIC_DRAW){const R=this.gl.createBuffer();return this.gl.bindBuffer(this.gl.ARRAY_BUFFER,R),this.gl.bufferData(this.gl.ARRAY_BUFFER,new Float32Array(C),D),R}attachShader(C){return this.shader=C,this.vertexPosition=C.location("aVertexPosition"),this.textureCoord=C.location("aTextureCoord"),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.gl.vertexAttribPointer(this.vertexPosition,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.vertexPosition),this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.textureBuffer),this.gl.vertexAttribPointer(this.textureCoord,2,this.gl.FLOAT,!1,0,0),this.gl.enableVertexAttribArray(this.textureCoord),this}draw2D(){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.shader.activate(this.app.projectionMatrix,this.app.modelViewMatrix),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,this.vertexPositions.length/2)}draw3D(){this.gl.bindBuffer(this.gl.ARRAY_BUFFER,this.positionBuffer),this.shader.activate(this.app.projectionMatrix,this.app.modelViewMatrix),this.gl.drawArrays(this.gl.TRIANGLE_STRIP,0,this.vertexPositions.length/3)}}class K extends I{constructor(C){super(C);this.vertexPositions=new Float32Array([-1,-1,1,-1,-1,1,1,1]),this.positionBuffer=this.initBuffer(this.vertexPositions),this.textureBuffer=this.initBuffer(new Float32Array([0,0,1,0,0,1,1,1])),this.app.onUpdate(()=>this.draw2D())}}class L{constructor(C={fov:45}){if(this._now=0,this.registry={onStart:[],onUpdate:[],onBeforeUpdate:[],onAfterUpdate:[]},this.config=C,this.canvas=document.querySelector("canvas"),this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight,this.gl=this.canvas.getContext("webgl2"),this.gl===null)throw document.querySelector("main").innerHTML="
your browser didn't let me set up webgl
",new Error("Unable to initialize WebGL. Your browser or machine may not support it.");const D=this.gl,R=this.config.fov*Math.PI/180,G=D.canvas.clientWidth/D.canvas.clientHeight,H=0.1,J=100,P=glMatrix.mat4.create();glMatrix.mat4.perspective(P,R,G,H,J);const q=glMatrix.mat4.create();glMatrix.mat4.translate(q,q,[-0,0,-6]),this.projectionMatrix=P,this.modelViewMatrix=q,this.clear(),this.onBeforeUpdate(()=>this.clear()),this.telemetry=new Y(this)}clear(){const C=this.gl;C.clearColor(0,0,0,1),C.clearDepth(1),C.enable(C.DEPTH_TEST),C.depthFunc(C.LEQUAL),C.clear(C.COLOR_BUFFER_BIT|C.DEPTH_BUFFER_BIT)}onStart(C){this.registry.onStart.push(C)}onUpdate(C){this.registry.onUpdate.push(C)}onBeforeUpdate(C){this.registry.onBeforeUpdate.push(C)}onAfterUpdate(C){this.registry.onAfterUpdate.push(C)}start(){this.registry.onStart.forEach((C)=>C(this))}update(){this.registry.onBeforeUpdate.forEach((C)=>C(this)),this.registry.onUpdate.forEach((C)=>C(this)),this.registry.onAfterUpdate.forEach((C)=>C(this))}oneShot(){requestAnimationFrame((C)=>{this._now=C,this.start(),this.update()})}loop(){const C=(D)=>{this._now=D,this.update(),requestAnimationFrame(C)};requestAnimationFrame(C)}now(){return this._now}}var k=`precision highp float; uniform float uTime; uniform float uSinTime; @@ -32,7 +32,7 @@ void main() { gl_FragColor = vec4(rgb, 1.0); gl_FragColor = clamp(gl_FragColor, 0.0, 1.0); -}`;var B=`attribute vec4 aVertexPosition; +}`;var A=`attribute vec4 aVertexPosition; attribute vec2 aTextureCoord; uniform mat4 uModelViewMatrix; @@ -43,4 +43,4 @@ varying highp vec2 vTextureCoord; void main() { gl_Position = uProjectionMatrix * uModelViewMatrix * aVertexPosition; vTextureCoord = aTextureCoord; -}`;var D=new Y({fov:20}),E=D.gl,W=new K(D).attach(E.VERTEX_SHADER,B).attach(E.FRAGMENT_SHADER,v).link(),X=new U(D);X.attachShader(W);D.loop(); +}`;var _=new L({fov:20}),B=_.gl,U=new E(_).attach(B.VERTEX_SHADER,A).attach(B.FRAGMENT_SHADER,k).link(),W=new K(_);W.attachShader(U);_.loop(); diff --git a/html/002-webgpu-instead/main.js b/html/002-webgpu-instead/main.js index 4859990..3a1eb9f 100644 --- a/html/002-webgpu-instead/main.js +++ b/html/002-webgpu-instead/main.js @@ -1 +1 @@ -import{a as i} from"../chunk-9587381547d8adb9.js";class d{constructor(w){this.config=w,this.canvas=document.querySelector("canvas"),this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight,this.telemetry=new i(this),this.init().catch((h)=>{throw console.error(h),document.querySelector("main").innerHTML="
your browser didn't let me set up webgpu. firefox nightly or enable dom.webgpu.enable.
",new Error("Unable to initialize WebGPU. Your browser or machine may not support it.",h)})}async init(){if(!navigator.gpu)throw new Error("WebGPU not supported");if(this.adapter=await navigator.gpu.requestAdapter(),!this.adapter)throw new Error("No GPU adapter found");if(this.device=await this.adapter.requestDevice(),!this.device)throw new Error("No GPU device found");this.context=this.canvas.getContext("webgpu")}}var x=new d({fov:20}); +import{a as s} from"../chunk-32eefd9b41c928dd.js";class i{a;canvas;_adapter;_device;_context;telemetry;constructor(a={}){this.config=a;this.config={fov:45,...a},this.canvas=document.querySelector("canvas"),this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight,this.telemetry=new s(this),this.init().catch((c)=>{const v=document.querySelector("main");if(v)v.innerHTML="
your browser didn't let me set up webgpu. firefox nightly or enable dom.webgpu.enable.
";throw new Error("Unable to initialize WebGPU. Your browser or machine may not support it.",c)})}async init(){if(!navigator.gpu)throw new Error("WebGPU not supported");if(this._adapter=await navigator.gpu.requestAdapter(),!this._adapter)throw new Error("No GPU adapter found");if(this._device=await this.adapter.requestDevice(),!this._device)throw new Error("No GPU device found");this._context=this.canvas.getContext("webgpu"),this.context.configure({device:this.device,format:"bgra8unorm"})}get context(){if(!this._context)throw new Error("WebGPU context not initialized");return this._context}get adapter(){if(!this._adapter)throw new Error("WebGPU adapter not initialized");return this._adapter}get device(){if(!this._device)throw new Error("WebGPU device not initialized");return this._device}}var G=new i({fov:20}); diff --git a/html/chunk-32eefd9b41c928dd.js b/html/chunk-32eefd9b41c928dd.js new file mode 100644 index 0000000..7964611 --- /dev/null +++ b/html/chunk-32eefd9b41c928dd.js @@ -0,0 +1,5 @@ +class h{n;el;frameTimes=[];maxFrameTimes=100;lastFrameTime=0;constructor(n,u="#telemetry"){this.app=n;if(this.el=document.querySelector(u),this.el&&location.search.includes("telemetry"))this.el.style.display="block",this.app.onAfterUpdate(()=>this.onAfterUpdate())}insertTime(n){if(this.frameTimes.push(n),this.frameTimes.length>this.maxFrameTimes)this.frameTimes.shift()}onAfterUpdate(){const n=this.app.now()-this.lastFrameTime;this.insertTime(n);const u=this.frameTimes.reduce((w,d)=>w+d,0)/this.frameTimes.length,p=1000/u;this.el.innerHTML=` + ${p.toFixed(1)} FPS (${u.toFixed(3)} ms)
+ bU: ${this.app.registry.onBeforeUpdate.length} | U: ${this.app.registry.onUpdate.length} | aU: ${this.app.registry.onAfterUpdate.length} + `,this.lastFrameTime=this.app.now()}} +export{h as a}; diff --git a/html/chunk-9587381547d8adb9.js b/html/chunk-9587381547d8adb9.js deleted file mode 100644 index 1b0c0df..0000000 --- a/html/chunk-9587381547d8adb9.js +++ /dev/null @@ -1,5 +0,0 @@ -class q{constructor(h,y="#telemetry"){if(this.app=h,this.el=document.querySelector(y),this.el&&location.search.includes("telemetry"))this.el.style.display="block",this.app.onAfterUpdate(()=>this.onAfterUpdate());this.frameTimes=[],this.maxFrameTimes=100,this.lastFrameTime=0}insertTime(h){if(this.frameTimes.push(h),this.frameTimes.length>this.maxFrameTimes)this.frameTimes.shift()}onAfterUpdate(){const h=this.app.now()-this.lastFrameTime;this.insertTime(h);const y=this.frameTimes.reduce((j,k)=>j+k,0)/this.frameTimes.length,f=1000/y;this.el.innerHTML=` - ${f.toFixed(1)} FPS (${y.toFixed(3)} ms)
- bU: ${this.app.registry.onBeforeUpdate.length} | U: ${this.app.registry.onUpdate.length} | aU: ${this.app.registry.onAfterUpdate.length} - `,this.lastFrameTime=this.app.now()}} -export{q as a}; diff --git a/stuff.d.ts b/index.d.ts similarity index 70% rename from stuff.d.ts rename to index.d.ts index 3457f60..d82326c 100644 --- a/stuff.d.ts +++ b/index.d.ts @@ -1,3 +1,6 @@ +/// +/// + declare module "*.glsl" { const content: string; export default content; diff --git a/package.json b/package.json index 7e550c6..dd72e8e 100644 --- a/package.json +++ b/package.json @@ -9,8 +9,9 @@ "dev": "run-p serve build:watch" }, "devDependencies": { + "@webgpu/types": "^0.1.37", "bun-types": "latest", - "npm-run-all2": "^6.0.6", + "npm-run-all2": "^6.1.1", "prettier": "^3.0.3" }, "peerDependencies": { @@ -19,6 +20,7 @@ "dependencies": { "chalk": "^5.3.0", "glob": "^10.3.10", - "serve": "^14.2.1" + "serve": "^14.2.1", + "typescript": "^5.2.2" } } diff --git a/src/002-webgpu-instead/main.ts b/src/002-webgpu-instead/main.ts index 4032a7d..b63e8d4 100644 --- a/src/002-webgpu-instead/main.ts +++ b/src/002-webgpu-instead/main.ts @@ -1,3 +1,8 @@ -import { WebGPUApp } from "../webgpu-app"; +import { WebGPUApp } from "../renderer/webgpu"; const app = new WebGPUApp({ fov: 20 }); + +// TODO: +// - plane +// - white shader +// - real shader with UVs and uniforms diff --git a/src/002-webgpu-instead/rainbow-plane.wgsl b/src/002-webgpu-instead/rainbow-plane.wgsl new file mode 100644 index 0000000..991cbd9 --- /dev/null +++ b/src/002-webgpu-instead/rainbow-plane.wgsl @@ -0,0 +1,49 @@ +struct Uniforms { + modelViewProjectionMatrix: mat4x4, + time: f32, +} + +@group(0) @binding(0) var uniforms: Uniforms; + +struct v2f { + @builtin(position) position : vec4f, + @location(0) uv : vec2f, +} + +@vertex +fn vertex_main( + @builtin(position) position : vec4f, + @location(0) uv : vec2f, +) -> v2f { + return v2f(uniforms.modelViewProjectionMatrix * position, uv); +} + +@fragment +fn fragment_main( + @location(0) uv : vec2f, +) -> @location(0) vec4f { + f32 zComponent = sin(uniforms.time) * 0.001 * 0.5 + 0.5; + vec3f hsv = vec3f(uv.x, uv.y, zComponent); + hsv.x += uniforms.time * 0.0001; + hsv.y = 1.0; + hsv.z = 1.0; + vec3f rgb = hsv2rgb(hsv); + + return saturate(vec4f(rgb, 1.0)); +} + +fn rgb2hsv(vec3f c) -> vec3f { + vec4f K = vec4f(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0); + vec4f p = mix(vec4f(c.bg, K.wz), vec4f(c.gb, K.xy), step(c.b, c.g)); + vec4f q = mix(vec4f(p.xyw, c.r), vec4f(c.r, p.yzx), step(p.x, c.r)); + + f32 d = q.x - min(q.w, q.y); + f32 e = 1.0e-10; + return vec3f(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x); +} + +fn hsv2rgb(vec3f c) -> vec3f { + vec4f K = vec4f(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); + vec3f p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); + return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); +} \ No newline at end of file diff --git a/src/app.js b/src/app.js index 30db9c5..6c1a6bb 100644 --- a/src/app.js +++ b/src/app.js @@ -1,4 +1,4 @@ -import { Telemetry } from "./telemetry.js"; +import { Telemetry } from "./renderer/telemetry"; export class App { constructor( diff --git a/src/meshes/plane.ts b/src/meshes/plane.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/renderer/mesh.ts b/src/renderer/mesh.ts new file mode 100644 index 0000000..c4303cc --- /dev/null +++ b/src/renderer/mesh.ts @@ -0,0 +1,16 @@ +import { Oops, Shader } from "./shader"; + +export type MeshConfig = { + vertexData: Float32Array; + vertexSize: number; + positionOffset: number; + colorOffset: number; + uvOffset: number; +}; + +export class Mesh { + private shader: Shader = Oops; + + constructor(public config: MeshConfig) {} + shader(shader: Shader) {} +} diff --git a/src/renderer/oops.wgsl b/src/renderer/oops.wgsl new file mode 100644 index 0000000..a07e038 --- /dev/null +++ b/src/renderer/oops.wgsl @@ -0,0 +1,26 @@ +struct Uniforms { + modelViewProjectionMatrix: mat4x4, + time: f32, +} + +@group(0) @binding(0) var uniforms: Uniforms; + +struct v2f { + @builtin(position) position : vec4f, + @location(0) uv : vec2f, +} + +@vertex +fn vertex_main( + @builtin(position) position : vec4f, + @location(0) uv : vec2f, +) -> v2f { + return v2f(uniforms.modelViewProjectionMatrix * position, uv); +} + +@fragment +fn fragment_main( + @location(0) uv : vec2f, +) -> vec4f { + return vec4f(1.0, 0.0, 1.0, 1.0); +} \ No newline at end of file diff --git a/src/renderer/shader.ts b/src/renderer/shader.ts new file mode 100644 index 0000000..1dc425d --- /dev/null +++ b/src/renderer/shader.ts @@ -0,0 +1,19 @@ +import { WebGPUApp } from "./webgpu"; +import oopsWsgl from "./oops.wgsl"; + +export class Shader { + private _module: GPUShaderModule | null = null; + constructor(private code: string[]) {} + + module(app: WebGPUApp) { + this._module = + this._module || + (this._module = app.device.createShaderModule({ + code: this.code.join("\n"), + })); + + return this._module; + } +} + +export const Oops = new Shader([oopsWsgl]); diff --git a/src/telemetry.js b/src/renderer/telemetry.ts similarity index 75% rename from src/telemetry.js rename to src/renderer/telemetry.ts index 844f79e..bc6e8ac 100644 --- a/src/telemetry.js +++ b/src/renderer/telemetry.ts @@ -1,18 +1,20 @@ export class Telemetry { - constructor(app, selector = "#telemetry") { - this.app = app; - this.el = document.querySelector(selector); + public el: HTMLElement; + public frameTimes: number[] = []; + public maxFrameTimes: number = 100; + public lastFrameTime: number = 0; + constructor( + public app: any, + selector = "#telemetry" + ) { + this.el = document.querySelector(selector) as HTMLElement; if (this.el && location.search.includes("telemetry")) { this.el.style.display = "block"; this.app.onAfterUpdate(() => this.onAfterUpdate()); } - - this.frameTimes = []; - this.maxFrameTimes = 100; - this.lastFrameTime = 0; } - insertTime(time) { + insertTime(time: number) { this.frameTimes.push(time); if (this.frameTimes.length > this.maxFrameTimes) { diff --git a/src/renderer/webgpu.ts b/src/renderer/webgpu.ts new file mode 100644 index 0000000..63c92db --- /dev/null +++ b/src/renderer/webgpu.ts @@ -0,0 +1,86 @@ +import { Telemetry } from "./telemetry"; + +export type WebGPUAppConfig = { + fov?: number; + context?: GPUCanvasConfiguration; +}; + +export class WebGPUApp { + public canvas: HTMLCanvasElement; + private _adapter?: GPUAdapter; + private _device?: GPUDevice; + private _context?: GPUCanvasContext; + public telemetry: Telemetry; + + constructor(public config: WebGPUAppConfig = {}) { + this.config = { + fov: 45, + ...config, + }; + this.canvas = document.querySelector("canvas") as HTMLCanvasElement; + this.canvas.width = window.innerWidth; + this.canvas.height = window.innerHeight; + this.telemetry = new Telemetry(this); + + this.init().catch((e) => { + const main = document.querySelector("main"); + if (main) { + main.innerHTML = `
your browser didn't let me set up webgpu. firefox nightly or enable dom.webgpu.enable.
`; + } + + throw new Error( + "Unable to initialize WebGPU. Your browser or machine may not support it.", + e + ); + }); + } + + async init() { + if (!navigator.gpu) { + throw new Error("WebGPU not supported"); + } + + this._adapter = (await navigator.gpu.requestAdapter()) as GPUAdapter; + if (!this._adapter) { + throw new Error("No GPU adapter found"); + } + + this._device = await this.adapter.requestDevice(); + if (!this._device) { + throw new Error("No GPU device found"); + } + + this._context = this.canvas.getContext("webgpu") as GPUCanvasContext; + + this.context.configure({ + device: this.device, + format: "bgra8unorm", + alphaMode: "premultiplied", + ...this.config.context, + }); + } + + get context() { + if (!this._context) { + throw new Error("WebGPU context not initialized"); + } + + return this._context; + } + + get adapter() { + if (!this._adapter) { + throw new Error("WebGPU adapter not initialized"); + } + + return this._adapter; + } + + get device() { + if (!this._device) { + throw new Error("WebGPU device not initialized"); + } + + return this._device; + } +} diff --git a/src/webgpu-app.js b/src/webgpu-app.js deleted file mode 100644 index a2c3141..0000000 --- a/src/webgpu-app.js +++ /dev/null @@ -1,40 +0,0 @@ -import { Telemetry } from "./telemetry.js"; - -export class WebGPUApp { - constructor(config) { - this.config = config; - this.canvas = document.querySelector("canvas"); - this.canvas.width = window.innerWidth; - this.canvas.height = window.innerHeight; - this.telemetry = new Telemetry(this); - - this.init().catch((e) => { - console.error(e); - document.querySelector( - "main" - ).innerHTML = `
your browser didn't let me set up webgpu. firefox nightly or enable dom.webgpu.enable.
`; - throw new Error( - "Unable to initialize WebGPU. Your browser or machine may not support it.", - e - ); - }); - } - - async init() { - if (!navigator.gpu) { - throw new Error("WebGPU not supported"); - } - - this.adapter = await navigator.gpu.requestAdapter(); - if (!this.adapter) { - throw new Error("No GPU adapter found"); - } - - this.device = await this.adapter.requestDevice(); - if (!this.device) { - throw new Error("No GPU device found"); - } - - this.context = this.canvas.getContext("webgpu"); - } -} diff --git a/tsconfig.json b/tsconfig.json index 7556e1d..5e21cb1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,22 +1,28 @@ { "compilerOptions": { - "lib": ["ESNext"], + // add Bun type definitions + "types": ["bun-types", "@webgpu/types"], + + // enable latest features + "lib": ["esnext"], "module": "esnext", "target": "esnext", + + // if TS 5.x+ "moduleResolution": "bundler", - "moduleDetection": "force", - "allowImportingTsExtensions": true, "noEmit": true, - "composite": true, + "allowImportingTsExtensions": true, + "moduleDetection": "force", + // if TS 4.x or earlier + // "moduleResolution": "nodenext", + + "jsx": "react-jsx", // support JSX + "allowJs": true, // allow importing `.js` from `.ts` + "esModuleInterop": true, // allow default imports for CommonJS modules + + // best practices "strict": true, - "downlevelIteration": true, - "skipLibCheck": true, - "jsx": "react-jsx", - "allowSyntheticDefaultImports": true, "forceConsistentCasingInFileNames": true, - "allowJs": true, - "types": [ - "bun-types" // add Bun global - ] + "skipLibCheck": true } }