From 000f35f19df564e2b5ceb3e365ef64af452465e8 Mon Sep 17 00:00:00 2001 From: mekanoe <1581674+mekanoe@users.noreply.github.com> Date: Sun, 8 Oct 2023 15:04:51 -0400 Subject: [PATCH] renderer might actually render something next commit --- bun.lockb | Bin 54909 -> 55262 bytes html/002-webgpu-instead/main.js | 2 +- package.json | 3 +- src/002-webgpu-instead/main.ts | 3 +- src/renderer/mesh-renderer.ts | 84 ++++++++++++++++++++++++++++++-- src/renderer/mesh.ts | 1 + src/renderer/webgpu.ts | 42 ++++++++++++++-- 7 files changed, 126 insertions(+), 9 deletions(-) diff --git a/bun.lockb b/bun.lockb index 4a6bea461afb086f8e5aa1a66315d1629278997a..d67313535019ad51429891f5890782007426ab2a 100755 GIT binary patch delta 11244 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 qW{32AETP{H{const C=document.querySelector("main");if(C)C.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.",x)})}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",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}onBeforeUpdate(v){this.registry.onBeforeUpdate.push(v)}onAfterUpdate(v){this.registry.onAfterUpdate.push(v)}onUpdate(v){this.registry.onUpdate.push(v)}onStart(v){this.registry.onStart.push(v)}doUpdate(v){this.registry.onBeforeUpdate.forEach((x)=>x(v,this)),this.registry.onUpdate.forEach((x)=>x(v,this)),this.registry.onAfterUpdate.forEach((x)=>x(v,this))}doStart(v=0){this.registry.onStart.forEach((x)=>x(v,this))}async oneShot(v=0){this.doStart(v),this.doUpdate(v)}start(){this.doStart();const v=(x)=>{this.doUpdate(x),requestAnimationFrame(v)};requestAnimationFrame(v)}}class j extends D{v;x;depthTexture;uniformBuffer;texture;sampler;uniformBindGroup;renderPassDescriptor;constructor(v,x){super(v);this.app=v;this.mesh=x}onStart(){console.log("hello from meshrenderer!"),console.log(`i've got a ${this.mesh.constructor.name}`)}onUpdate(v){}}class k{v;constructor(v){this.config=v}buffer(v){const x=v.device.createBuffer({size:this.config.mesh.byteLength,usage:GPUBufferUsage.VERTEX,mappedAtCreation:!0});return new Float32Array(x.getMappedRange()).set(this.config.mesh),x.unmap(),x}pipeline(v,x,C){const q=x.module(v);return v.device.createRenderPipeline({layout:"auto",vertex:{module:q,entryPoint:"main",buffers:[{arrayStride:4,attributes:[{shaderLocation:0,offset:0,format:"float32x4"},{shaderLocation:2,offset:this.config.positionSize,format:"float32x4"},{shaderLocation:1,offset:this.config.positionSize+this.config.colorSize,format:"float32x4"}]}]},fragment:{module:q,entryPoint:"main",targets:[{format:"rgba8unorm"}]},primitive:{topology:"triangle-list",cullMode:C.cullMode??"back"},depthStencil:C.stencil&&{depthWriteEnabled:!0,depthCompare:"less",format:"depth24plus"}})}}var J=new Float32Array([-1,-1,0,1,1,1,1,1,0,0,1,-1,0,1,1,1,1,1,1,0,-1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1]),E=new k({mesh:J,positionSize:16,colorSize:16,uvSize:8});var F="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);}struct Uniforms{modelViewProjectionMatrix:mat4x4,time:f32,}@group(0)@binding(0)var uniforms:Uniforms;struct v2f{@builtin(position)position:vec4f,@location(0)color:vec4f,@location(1)uv:vec2f,}@vertex fn main(@builtin(position)position:vec4f,@location(0)color:vec4f,@location(1)uv:vec2f,)->v2f{return v2f(uniforms.modelViewProjectionMatrix*position,color,uv);}@fragment fn main(@location(0)uv:vec2f,)->@location(0)vec4f{f32 z=sin(uniforms.time)*0.001*0.5+0.5;vec3f hsv=vec3f(uv.x,uv.y,z);hsv.x+=uniforms.time*0.0001;hsv.y=1.0;hsv.z=1.0;vec3f rgb=hsv2rgb(hsv);return saturate(vec4f(rgb,1.0));}";var H="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);}";var N=new RegExp("#pragma ([a-z]+) ([a-zA-Z_0-9]+)","g");class P{_module=null;vertexMain="main";fragmentMain="main";code;constructor(...v){this.code=v.join("\n");const x=this.code.matchAll(N);for(let C of x||[])switch(C[1]){case"fragment":this.fragmentMain=C[2];break;case"vertex":this.vertexMain=C[2];break}}module(v){return this._module=this._module||(this._module=v.device.createShaderModule({code:this.code}))}}var S=new P(H);var I=new _({fov:20}),s=new j(I,E),u=new P(F);I.start(); +import{a as S0,b as Z0} from"../chunk-818093f0cbbefec3.js";class H0{G;canvas;_adapter;_device;_context;telemetry;jobsToSubmitThisFrame=[];renderOK=!1;registry={onBeforeUpdate:[],onAfterUpdate:[],onUpdate:[],onStart:[]};constructor(G={}){this.config=G;if(this.config={fov:45,...G},this.canvas=document.querySelector("canvas"),this.canvas.width=window.innerWidth,this.canvas.height=window.innerHeight,location.search.includes("telemetry"))this.telemetry=new Z0(this);this.init().catch((H)=>{const J=document.querySelector("main");if(J)J.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.",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"),this.context.configure({device:this.device,format:"bgra8unorm",alphaMode:"premultiplied",...this.config.context}),this.renderOK=!0}awaitRendererReady(G=5000){const H=Date.now();return new Promise((J,K)=>{const W=setInterval(()=>{if(this.renderOK)return J(!0);if(Date.now()-H>G)return K(`Renderer was not OK within ${G}ms`)},10)})}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}onBeforeUpdate(G){this.registry.onBeforeUpdate.push(G)}onAfterUpdate(G){this.registry.onAfterUpdate.push(G)}onUpdate(G){this.registry.onUpdate.push(G)}onStart(G){this.registry.onStart.push(G)}doUpdate(G){if(this.jobsToSubmitThisFrame=[],this.registry.onBeforeUpdate.forEach((H)=>H(G,this)),this.registry.onUpdate.forEach((H)=>H(G,this)),this.registry.onAfterUpdate.forEach((H)=>H(G,this)),this.jobsToSubmitThisFrame.length!==0)this.device.queue.submit(this.jobsToSubmitThisFrame)}doStart(G=0){this.registry.onStart.forEach((H)=>H(G,this))}async oneShot(G=0){await this.awaitRendererReady(),this.doStart(G),this.doUpdate(G)}async start(){await this.awaitRendererReady(),this.doStart();const G=(H)=>{this.doUpdate(H),requestAnimationFrame(G)};requestAnimationFrame(G)}commit(G){this.jobsToSubmitThisFrame.push(G)}}var T0=function(G){const H=Q;return Q=G,H},i=function(G,H,J){const K=new Q(3);if(G!==void 0){if(K[0]=G,H!==void 0){if(K[1]=H,J!==void 0)K[2]=J}}return K},g0=function(G,H,J,K){return K=K||new Q(3),K[0]=G,K[1]=H,K[2]=J,K},o0=function(G,H){return H=H||new Q(3),H[0]=Math.ceil(G[0]),H[1]=Math.ceil(G[1]),H[2]=Math.ceil(G[2]),H},r0=function(G,H){return H=H||new Q(3),H[0]=Math.floor(G[0]),H[1]=Math.floor(G[1]),H[2]=Math.floor(G[2]),H},z0=function(G,H){return H=H||new Q(3),H[0]=Math.round(G[0]),H[1]=Math.round(G[1]),H[2]=Math.round(G[2]),H},$0=function(G,H=0,J=1,K){return K=K||new Q(3),K[0]=Math.min(J,Math.max(H,G[0])),K[1]=Math.min(J,Math.max(H,G[1])),K[2]=Math.min(J,Math.max(H,G[2])),K},l0=function(G,H,J){return J=J||new Q(3),J[0]=G[0]+H[0],J[1]=G[1]+H[1],J[2]=G[2]+H[2],J},i0=function(G,H,J,K){return K=K||new Q(3),K[0]=G[0]+H[0]*J,K[1]=G[1]+H[1]*J,K[2]=G[2]+H[2]*J,K},c0=function(G,H){const J=G[0],K=G[1],W=G[2],C=G[0],F=G[1],j=G[2],_=Math.sqrt(J*J+K*K+W*W),U=Math.sqrt(C*C+F*F+j*j),Y=_*U,B=Y&&_0(G,H)/Y;return Math.acos(B)},v=function(G,H,J){return J=J||new Q(3),J[0]=G[0]-H[0],J[1]=G[1]-H[1],J[2]=G[2]-H[2],J},p0=function(G,H){return Math.abs(G[0]-H[0])0.00001)H[0]=J/C,H[1]=K/C,H[2]=W/C;else H[0]=0,H[1]=0,H[2]=0;return H},JG=function(G,H){return H=H||new Q(3),H[0]=-G[0],H[1]=-G[1],H[2]=-G[2],H},Q0=function(G,H){return H=H||new Q(3),H[0]=G[0],H[1]=G[1],H[2]=G[2],H},E0=function(G,H,J){return J=J||new Q(3),J[0]=G[0]*H[0],J[1]=G[1]*H[1],J[2]=G[2]*H[2],J},O0=function(G,H,J){return J=J||new Q(3),J[0]=G[0]/H[0],J[1]=G[1]/H[1],J[2]=G[2]/H[2],J},CG=function(G=1,H){H=H||new Q(3);const J=Math.random()*2*Math.PI,K=Math.random()*2-1,W=Math.sqrt(1-K*K)*G;return H[0]=Math.cos(J)*W,H[1]=Math.sin(J)*W,H[2]=K*G,H},BG=function(G){return G=G||new Q(3),G[0]=0,G[1]=0,G[2]=0,G},XG=function(G,H,J){J=J||new Q(3);const K=G[0],W=G[1],C=G[2],F=H[3]*K+H[7]*W+H[11]*C+H[15]||1;return J[0]=(H[0]*K+H[4]*W+H[8]*C+H[12])/F,J[1]=(H[1]*K+H[5]*W+H[9]*C+H[13])/F,J[2]=(H[2]*K+H[6]*W+H[10]*C+H[14])/F,J},ZG=function(G,H,J){J=J||new Q(3);const K=G[0],W=G[1],C=G[2];return J[0]=K*H[0]+W*H[4]+C*H[8],J[1]=K*H[1]+W*H[5]+C*H[9],J[2]=K*H[2]+W*H[6]+C*H[10],J},YG=function(G,H,J){J=J||new Q(3);const K=G[0],W=G[1],C=G[2];return J[0]=K*H[0]+W*H[4]+C*H[8],J[1]=K*H[1]+W*H[5]+C*H[9],J[2]=K*H[2]+W*H[6]+C*H[10],J},jG=function(G,H,J){J=J||new Q(3);const K=H[0],W=H[1],C=H[2],F=H[3]*2,j=G[0],_=G[1],U=G[2],Y=W*U-C*_,B=C*j-K*U,X=K*_-W*j;return J[0]=j+Y*F+(W*X-C*B)*2,J[1]=_+B*F+(C*Y-K*X)*2,J[2]=U+X*F+(K*B-W*Y)*2,J},_G=function(G,H){return H=H||new Q(3),H[0]=G[12],H[1]=G[13],H[2]=G[14],H},UG=function(G,H,J){J=J||new Q(3);const K=H*4;return J[0]=G[K+0],J[1]=G[K+1],J[2]=G[K+2],J},hG=function(G,H){H=H||new Q(3);const J=G[0],K=G[1],W=G[2],C=G[4],F=G[5],j=G[6],_=G[8],U=G[9],Y=G[10];return H[0]=Math.sqrt(J*J+K*K+W*W),H[1]=Math.sqrt(C*C+F*F+j*j),H[2]=Math.sqrt(_*_+U*U+Y*Y),H},DG=function(G){const H=O;return O=G,H},PG=function(G,H,J,K,W,C,F,j,_,U,Y,B,X,D,h,P){const Z=new O(16);if(G!==void 0){if(Z[0]=G,H!==void 0){if(Z[1]=H,J!==void 0){if(Z[2]=J,K!==void 0){if(Z[3]=K,W!==void 0){if(Z[4]=W,C!==void 0){if(Z[5]=C,F!==void 0){if(Z[6]=F,j!==void 0){if(Z[7]=j,_!==void 0){if(Z[8]=_,U!==void 0){if(Z[9]=U,Y!==void 0){if(Z[10]=Y,B!==void 0){if(Z[11]=B,X!==void 0){if(Z[12]=X,D!==void 0){if(Z[13]=D,h!==void 0){if(Z[14]=h,P!==void 0)Z[15]=P}}}}}}}}}}}}}}}return Z},QG=function(G,H,J,K,W,C,F,j,_,U,Y,B,X,D,h,P,Z){return Z=Z||new O(16),Z[0]=G,Z[1]=H,Z[2]=J,Z[3]=K,Z[4]=W,Z[5]=C,Z[6]=F,Z[7]=j,Z[8]=_,Z[9]=U,Z[10]=Y,Z[11]=B,Z[12]=X,Z[13]=D,Z[14]=h,Z[15]=P,Z},EG=function(G,H){return H=H||new O(16),H[0]=G[0],H[1]=G[1],H[2]=G[2],H[3]=0,H[4]=G[4],H[5]=G[5],H[6]=G[6],H[7]=0,H[8]=G[8],H[9]=G[9],H[10]=G[10],H[11]=0,H[12]=0,H[13]=0,H[14]=0,H[15]=1,H},OG=function(G,H){H=H||new O(16);const J=G[0],K=G[1],W=G[2],C=G[3],F=J+J,j=K+K,_=W+W,U=J*F,Y=K*F,B=K*j,X=W*F,D=W*j,h=W*_,P=C*F,Z=C*j,N=C*_;return H[0]=1-B-h,H[1]=Y+N,H[2]=X-Z,H[3]=0,H[4]=Y-N,H[5]=1-U-h,H[6]=D+P,H[7]=0,H[8]=X+Z,H[9]=D-P,H[10]=1-U-B,H[11]=0,H[12]=0,H[13]=0,H[14]=0,H[15]=1,H},NG=function(G,H){return H=H||new O(16),H[0]=-G[0],H[1]=-G[1],H[2]=-G[2],H[3]=-G[3],H[4]=-G[4],H[5]=-G[5],H[6]=-G[6],H[7]=-G[7],H[8]=-G[8],H[9]=-G[9],H[10]=-G[10],H[11]=-G[11],H[12]=-G[12],H[13]=-G[13],H[14]=-G[14],H[15]=-G[15],H},J0=function(G,H){return H=H||new O(16),H[0]=G[0],H[1]=G[1],H[2]=G[2],H[3]=G[3],H[4]=G[4],H[5]=G[5],H[6]=G[6],H[7]=G[7],H[8]=G[8],H[9]=G[9],H[10]=G[10],H[11]=G[11],H[12]=G[12],H[13]=G[13],H[14]=G[14],H[15]=G[15],H},RG=function(G,H){return Math.abs(G[0]-H[0])new Float32Array(12)],[Float64Array,()=>new Float64Array(12)],[Array,()=>new Array(12).fill(0)]]),CH=V0.get(Float32Array);var Q=Float32Array,q0=i,n0=v,a0=Y0,d0=j0,s0=U0,t0=h0,GG=D0,HG=P0,KG=Q0,WG=E0,FG=O0,N0=Object.freeze({__proto__:null,create:i,setDefaultType:T0,fromValues:q0,set:g0,ceil:o0,floor:r0,round:z0,clamp:$0,add:l0,addScaled:i0,angle:c0,subtract:v,sub:n0,equalsApproximately:p0,equals:x0,lerp:y0,lerpV:v0,max:b0,min:e0,mulScalar:Y0,scale:a0,divScalar:m0,inverse:j0,invert:d0,cross:x,dot:_0,length:U0,len:s0,lengthSq:h0,lenSq:t0,distance:D0,dist:GG,distanceSq:P0,distSq:HG,normalize:p,negate:JG,copy:Q0,clone:KG,multiply:E0,mul:WG,divide:O0,div:FG,random:CG,zero:BG,transformMat4:XG,transformMat4Upper3x3:ZG,transformMat3:YG,transformQuat:jG,getTranslation:_G,getAxis:UG,getScaling:hG}),O=Float32Array,LG=J0,SG=R0,wG=k0,R,k,L,bG=f0,eG=M0,y=Object.freeze({__proto__:null,setDefaultType:DG,create:PG,set:QG,fromMat3:EG,fromQuat:OG,negate:NG,copy:J0,clone:LG,equalsApproximately:RG,equals:kG,identity:L0,transpose:fG,inverse:R0,determinant:MG,invert:SG,multiply:k0,mul:wG,setTranslation:IG,getTranslation:AG,getAxis:uG,setAxis:VG,getScaling:TG,perspective:qG,ortho:gG,frustum:oG,aim:rG,cameraAim:zG,lookAt:$G,translation:lG,translate:iG,rotationX:cG,rotateX:nG,rotationY:pG,rotateY:xG,rotationZ:yG,rotateZ:vG,axisRotation:f0,rotation:bG,axisRotate:M0,rotate:eG,scaling:aG,scale:mG,uniformScaling:dG,uniformScale:sG});class K0 extends S0{G;H;J;K;depthTexture;uniformBuffer;vertexBuffer;texture;sampler;uniformBindGroup;renderPassDescriptor;pipeline;viewMatrix=y.translate(y.identity(),N0.fromValues(0,0,-4));projectionMatrix=y.perspective(2*Math.PI*0.2,1.7777777777777777,1,100);constructor(G,H,J,K){super(G);this.app=G;this.mesh=H;this.shader=J;this.textures=K}onStart(){this.projectionMatrix=y.perspective(2*Math.PI*0.2,this.app.canvas.width/this.app.canvas.height,1,100),this.depthTexture=this.app.device.createTexture({size:[this.app.canvas.width,this.app.canvas.height],format:"depth24plus",usage:GPUTextureUsage.RENDER_ATTACHMENT}),this.uniformBuffer=this.app.device.createBuffer({size:68,usage:GPUBufferUsage.UNIFORM|GPUBufferUsage.COPY_DST}),this.pipeline=this.mesh.pipeline(this.app,this.shader,{}),this.uniformBindGroup=this.app.device.createBindGroup({layout:this.pipeline.getBindGroupLayout(0),entries:[{binding:0,resource:{buffer:this.uniformBuffer}}]}),this.renderPassDescriptor={colorAttachments:[],depthStencilAttachment:{view:this.depthTexture.createView(),depthClearValue:1,depthLoadOp:"clear",depthStoreOp:"store"}},this.vertexBuffer=this.mesh.buffer(this.app)}writeUniforms(G,H){if(!this.uniformBuffer)return;const{device:{queue:J}}=this.app,K=G;J.writeBuffer(this.uniformBuffer,0,K.buffer,K.byteOffset,K.length);const W=new Float32Array([H]);J.writeBuffer(this.uniformBuffer,K.length+1,W.buffer,W.byteOffset,W.byteLength)}onUpdate(G){if(!this.renderPassDescriptor||!this.pipeline||!this.uniformBindGroup||!this.vertexBuffer)return;const H=y.multiply(this.projectionMatrix,this.viewMatrix);this.writeUniforms(H,G);const{device:J}=this.app,K=J.createCommandEncoder(),W=K.beginRenderPass(this.renderPassDescriptor);W.setPipeline(this.pipeline),W.setBindGroup(0,this.uniformBindGroup),W.setVertexBuffer(0,this.vertexBuffer),W.draw(this.mesh.config.vertexCount),W.end(),this.app.commit(K.finish())}}class W0{G;constructor(G){this.config=G}buffer(G){const H=G.device.createBuffer({size:this.config.mesh.byteLength,usage:GPUBufferUsage.VERTEX,mappedAtCreation:!0});return new Float32Array(H.getMappedRange()).set(this.config.mesh),H.unmap(),H}pipeline(G,H,J){const K=H.module(G);return G.device.createRenderPipeline({layout:"auto",vertex:{module:K,entryPoint:"main",buffers:[{arrayStride:4,attributes:[{shaderLocation:0,offset:0,format:"float32x4"},{shaderLocation:2,offset:this.config.positionSize,format:"float32x4"},{shaderLocation:1,offset:this.config.positionSize+this.config.colorSize,format:"float32x4"}]}]},fragment:{module:K,entryPoint:"main",targets:[{format:"rgba8unorm"}]},primitive:{topology:"triangle-list",cullMode:J.cullMode??"back"},depthStencil:J.stencil&&{depthWriteEnabled:!0,depthCompare:"less",format:"depth24plus"}})}}var tG=new Float32Array([-1,-1,0,1,1,1,1,1,0,0,1,-1,0,1,1,1,1,1,1,0,-1,1,0,1,1,1,1,1,0,1,1,1,0,1,1,1,1,1,1,1]),w0=new W0({mesh:tG,positionSize:16,colorSize:16,uvSize:8});var I0="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);}struct Uniforms{modelViewProjectionMatrix:mat4x4,time:f32,}@group(0)@binding(0)var uniforms:Uniforms;struct v2f{@builtin(position)position:vec4f,@location(0)color:vec4f,@location(1)uv:vec2f,}@vertex fn main(@builtin(position)position:vec4f,@location(0)color:vec4f,@location(1)uv:vec2f,)->v2f{return v2f(uniforms.modelViewProjectionMatrix*position,color,uv);}@fragment fn main(@location(0)uv:vec2f,)->@location(0)vec4f{f32 z=sin(uniforms.time)*0.001*0.5+0.5;vec3f hsv=vec3f(uv.x,uv.y,z);hsv.x+=uniforms.time*0.0001;hsv.y=1.0;hsv.z=1.0;vec3f rgb=hsv2rgb(hsv);return saturate(vec4f(rgb,1.0));}";var A0="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);}";var JH=new RegExp("#pragma ([a-z]+) ([a-zA-Z_0-9]+)","g");class G0{_module=null;vertexMain="main";fragmentMain="main";code;constructor(...G){this.code=G.join("\n");const H=this.code.matchAll(JH);for(let J of H||[])switch(J[1]){case"fragment":this.fragmentMain=J[2];break;case"vertex":this.vertexMain=J[2];break}}module(G){return this._module=this._module||(this._module=G.device.createShaderModule({code:this.code}))}}var EH=new G0(A0);var u0=new H0({fov:20}),KH=new G0(I0),MH=new K0(u0,w0,KH);u0.start(); diff --git a/package.json b/package.json index 9fdfb2a..4a07b39 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "esbuild-plugin-glsl": "^1.2.2", "glob": "^10.3.10", "serve": "^14.2.1", - "typescript": "^5.2.2" + "typescript": "^5.2.2", + "wgpu-matrix": "^2.5.0" } } diff --git a/src/002-webgpu-instead/main.ts b/src/002-webgpu-instead/main.ts index cd0e8d0..98eceee 100644 --- a/src/002-webgpu-instead/main.ts +++ b/src/002-webgpu-instead/main.ts @@ -6,6 +6,7 @@ import { Shader } from "../renderer/shader"; const app = new WebGPUApp({ fov: 20 }); -const renderer = new MeshRenderer(app, plane); const shader = new Shader(rainbowPlane); +const renderer = new MeshRenderer(app, plane, shader); + app.start(); diff --git a/src/renderer/mesh-renderer.ts b/src/renderer/mesh-renderer.ts index 2f29222..671c58c 100644 --- a/src/renderer/mesh-renderer.ts +++ b/src/renderer/mesh-renderer.ts @@ -1,3 +1,4 @@ +import { Mat4, mat4, vec3 } from "wgpu-matrix"; import { Behavior } from "./behavior"; import { Mesh } from "./mesh"; import { Shader } from "./shader"; @@ -6,29 +7,49 @@ import { WebGPUApp } from "./webgpu"; export class MeshRenderer extends Behavior { private depthTexture?: GPUTexture; private uniformBuffer?: GPUBuffer; + private vertexBuffer?: GPUBuffer; private texture?: GPUTexture; private sampler?: GPUSampler; private uniformBindGroup?: GPUBindGroup; private renderPassDescriptor?: GPURenderPassDescriptor; private pipeline?: GPURenderPipeline; + private viewMatrix = mat4.translate( + mat4.identity(), + vec3.fromValues(0, 0, -4) + ); + private projectionMatrix = mat4.perspective( + 2 * Math.PI * 0.2, + 1920 / 1080, + 1, + 100 + ); constructor( public app: WebGPUApp, public mesh: Mesh, - public shader: Shader + public shader: Shader, + public textures?: any[] ) { super(app); } onStart() { + this.projectionMatrix = mat4.perspective( + 2 * Math.PI * 0.2, + this.app.canvas.width / this.app.canvas.height, + 1, + 100 + ); + this.depthTexture = this.app.device.createTexture({ size: [this.app.canvas.width, this.app.canvas.height], format: "depth24plus", usage: GPUTextureUsage.RENDER_ATTACHMENT, }); + // float32x4x4 + float32 this.uniformBuffer = this.app.device.createBuffer({ - size: 4 * 4 + 4, + size: 4 * 16 + 4, usage: GPUBufferUsage.UNIFORM | GPUBufferUsage.COPY_DST, }); @@ -62,7 +83,64 @@ export class MeshRenderer extends Behavior { depthStoreOp: "store", }, }; + + this.vertexBuffer = this.mesh.buffer(this.app); } - onUpdate(time: number) {} + private writeUniforms(modelViewProjection: Mat4, time: number) { + if (!this.uniformBuffer) { + return; + } + + const { + device: { queue }, + } = this.app; + + const mvpBuf = modelViewProjection as Float32Array; + queue.writeBuffer( + this.uniformBuffer, + 0, + mvpBuf.buffer, + mvpBuf.byteOffset, + mvpBuf.length + ); + + const timeBuf = new Float32Array([time]); + queue.writeBuffer( + this.uniformBuffer, + mvpBuf.length + 1, + timeBuf.buffer, + timeBuf.byteOffset, + timeBuf.byteLength + ); + } + + onUpdate(time: number) { + if ( + !this.renderPassDescriptor || + !this.pipeline || + !this.uniformBindGroup || + !this.vertexBuffer + ) { + return; + } + + const mvp = mat4.multiply(this.projectionMatrix, this.viewMatrix); + this.writeUniforms(mvp, time); + + const { device } = this.app; + + const commandEncoder = device.createCommandEncoder(); + const passEncoder = commandEncoder.beginRenderPass( + this.renderPassDescriptor + ); + + passEncoder.setPipeline(this.pipeline); + passEncoder.setBindGroup(0, this.uniformBindGroup); + passEncoder.setVertexBuffer(0, this.vertexBuffer); + passEncoder.draw(this.mesh.config.vertexCount); + passEncoder.end(); + + this.app.commit(commandEncoder.finish()); + } } diff --git a/src/renderer/mesh.ts b/src/renderer/mesh.ts index eaeb130..03a149b 100644 --- a/src/renderer/mesh.ts +++ b/src/renderer/mesh.ts @@ -6,6 +6,7 @@ export type MeshConfig = { positionSize: number; colorSize: number; uvSize: number; + vertexCount: number; }; export class Mesh { diff --git a/src/renderer/webgpu.ts b/src/renderer/webgpu.ts index bf0b54d..1cab4e2 100644 --- a/src/renderer/webgpu.ts +++ b/src/renderer/webgpu.ts @@ -12,7 +12,9 @@ export class WebGPUApp { private _adapter?: GPUAdapter; private _device?: GPUDevice; private _context?: GPUCanvasContext; - public telemetry: Telemetry; + public telemetry?: Telemetry; + private jobsToSubmitThisFrame: GPUCommandBuffer[] = []; + private renderOK = false; public registry: { onBeforeUpdate: RenderHandle[]; @@ -34,7 +36,10 @@ export class WebGPUApp { this.canvas = document.querySelector("canvas") as HTMLCanvasElement; this.canvas.width = window.innerWidth; this.canvas.height = window.innerHeight; - this.telemetry = new Telemetry(this); + + if (location.search.includes("telemetry")) { + this.telemetry = new Telemetry(this); + } this.init().catch((e) => { const main = document.querySelector("main"); @@ -72,6 +77,23 @@ export class WebGPUApp { alphaMode: "premultiplied", ...this.config.context, }); + + this.renderOK = true; + } + + awaitRendererReady(timeout: number = 5000) { + const start = Date.now(); + return new Promise((resolve, reject) => { + const interval = setInterval(() => { + if (this.renderOK) { + return resolve(true); + } + + if (Date.now() - start > timeout) { + return reject(`Renderer was not OK within ${timeout}ms`); + } + }, 10); + }); } get context() { @@ -115,9 +137,15 @@ export class WebGPUApp { } doUpdate(time: number) { + this.jobsToSubmitThisFrame = []; + this.registry.onBeforeUpdate.forEach((handle) => handle(time, this)); this.registry.onUpdate.forEach((handle) => handle(time, this)); this.registry.onAfterUpdate.forEach((handle) => handle(time, this)); + + if (this.jobsToSubmitThisFrame.length !== 0) { + this.device.queue.submit(this.jobsToSubmitThisFrame); + } } doStart(time: number = 0) { @@ -125,11 +153,15 @@ export class WebGPUApp { } async oneShot(time: number = 0) { + await this.awaitRendererReady(); + this.doStart(time); this.doUpdate(time); } - start() { + async start() { + await this.awaitRendererReady(); + this.doStart(); const run = (time: number) => { @@ -138,4 +170,8 @@ export class WebGPUApp { }; requestAnimationFrame(run); } + + commit(commandEncoder: GPUCommandBuffer) { + this.jobsToSubmitThisFrame.push(commandEncoder); + } }