UNPKG

@animech-public/playcanvas

Version:
2 lines (1 loc) 13 kB
import{DEVICETYPE_WEBGPU as e,PIXELFORMAT_RGBA32F as t,PIXELFORMAT_RGBA8 as s,PIXELFORMAT_BGRA8 as r,BUFFERUSAGE_READ as i,BUFFERUSAGE_COPY_DST as n,semanticToLocation as o}from"../constants.js";import{GraphicsDevice as a}from"../graphics-device.js";import{RenderTarget as h}from"../render-target.js";import{StencilParameters as u}from"../stencil-parameters.js";import{WebgpuBindGroup as p}from"./webgpu-bind-group.js";import{WebgpuBindGroupFormat as l}from"./webgpu-bind-group-format.js";import{WebgpuIndexBuffer as m}from"./webgpu-index-buffer.js";import{WebgpuRenderPipeline as d}from"./webgpu-render-pipeline.js";import{WebgpuComputePipeline as f}from"./webgpu-compute-pipeline.js";import{WebgpuRenderTarget as c}from"./webgpu-render-target.js";import{WebgpuShader as g}from"./webgpu-shader.js";import{WebgpuTexture as x}from"./webgpu-texture.js";import{WebgpuUniformBuffer as b}from"./webgpu-uniform-buffer.js";import{WebgpuVertexBuffer as w}from"./webgpu-vertex-buffer.js";import{WebgpuClearRenderer as B}from"./webgpu-clear-renderer.js";import{WebgpuMipmapRenderer as T}from"./webgpu-mipmap-renderer.js";import{WebgpuDynamicBuffers as C}from"./webgpu-dynamic-buffers.js";import{WebgpuGpuProfiler as v}from"./webgpu-gpu-profiler.js";import{WebgpuResolver as E}from"./webgpu-resolver.js";import{WebgpuCompute as S}from"./webgpu-compute.js";import{WebgpuBuffer as P}from"./webgpu-buffer.js";const y=new Map;class R extends a{constructor(t,s={}){var r,i;super(t,s),this.renderPipeline=new d(this),this.computePipeline=new f(this),this.clearRenderer=void 0,this.mipmapRenderer=void 0,this.pipeline=void 0,this.bindGroupFormats=[],this.commandEncoder=null,this.commandBuffers=[],this.limits=void 0,(s=this.initOptions).alpha=null==(r=s.alpha)||r,this.backBufferAntialias=null!=(i=s.antialias)&&i,this.isWebGPU=!0,this._deviceType=e}destroy(){this.clearRenderer.destroy(),this.clearRenderer=null,this.mipmapRenderer.destroy(),this.mipmapRenderer=null,this.resolver.destroy(),this.resolver=null,super.destroy()}initDeviceCaps(){var e;const r=null==(e=this.wgpu)?void 0:e.limits;this.limits=r,this.precision="highp",this.maxPrecision="highp",this.maxSamples=4,this.maxTextures=16,this.maxTextureSize=r.maxTextureDimension2D,this.maxCubeMapSize=r.maxTextureDimension2D,this.maxVolumeSize=r.maxTextureDimension3D,this.maxColorAttachments=r.maxColorAttachments,this.maxPixelRatio=1,this.maxAnisotropy=16,this.fragmentUniformsCount=r.maxUniformBufferBindingSize/16,this.vertexUniformsCount=r.maxUniformBufferBindingSize/16,this.supportsInstancing=!0,this.supportsUniformBuffers=!0,this.supportsVolumeTextures=!0,this.supportsBoneTextures=!0,this.supportsMorphTargetTexturesCore=!0,this.supportsAreaLights=!0,this.supportsDepthShadow=!0,this.supportsGpuParticles=!0,this.supportsMrt=!0,this.supportsCompute=!0,this.extUintElement=!0,this.extTextureFloat=!0,this.textureFloatRenderable=!0,this.textureHalfFloatFilterable=!0,this.extTextureHalfFloat=!0,this.textureHalfFloatRenderable=!0,this.textureHalfFloatUpdatable=!0,this.boneLimit=1024,this.supportsImageBitmap=!0,this.extStandardDerivatives=!0,this.extBlendMinmax=!0,this.areaLightLutFormat=this.textureFloatFilterable?t:s,this.supportsTextureFetch=!0,this.samples=this.backBufferAntialias?4:1;const i=navigator.gpu.wgslLanguageFeatures;this.supportsStorageTextureRead=null==i?void 0:i.has("readonly_and_readwrite_storage_textures")}async initWebGpu(e,t){var i,n;if(!window.navigator.gpu)throw new Error("Unable to retrieve GPU. Ensure you are using a browser that supports WebGPU rendering.");const o=e=>new URL(e,window.location.href).toString(),a=await Promise.all([import(`${o(t)}`).then((e=>twgsl(t.replace(".js",".wasm")))),import(`${o(e)}`).then((e=>e.default()))]);this.twgsl=a[0],this.glslang=a[1];const h={powerPreference:"default"!==this.initOptions.powerPreference?this.initOptions.powerPreference:void 0};this.gpuAdapter=await window.navigator.gpu.requestAdapter(h);const u=[],p=e=>{const t=this.gpuAdapter.features.has(e);return t&&u.push(e),t};this.textureFloatFilterable=p("float32-filterable"),this.extCompressedTextureS3TC=p("texture-compression-bc"),this.extCompressedTextureETC=p("texture-compression-etc2"),this.extCompressedTextureASTC=p("texture-compression-astc"),this.supportsTimestampQuery=p("timestamp-query"),this.supportsDepthClip=p("depth-clip-control"),this.supportsDepth32Stencil=p("depth32float-stencil8"),this.supportsIndirectFirstInstance=p("indirect-first-instance"),this.supportsShaderF16=p("shader-f16"),this.supportsStorageRGBA8=p("bgra8unorm-storage"),this.textureRG11B10Renderable=p("rg11b10ufloat-renderable");const l=null==(i=this.gpuAdapter)?void 0:i.limits,m={};if(l)for(const e in l)"minSubgroupSize"!==e&&"maxSubgroupSize"!==e&&(m[e]=l[e]);const d={requiredFeatures:u,requiredLimits:m,defaultQueue:{label:"Default Queue"}};this.wgpu=await this.gpuAdapter.requestDevice(d),null==(n=this.wgpu.lost)||n.then((e=>{e.reason})),this.initDeviceCaps(),this.gpuContext=this.canvas.getContext("webgpu");const f=navigator.gpu.getPreferredCanvasFormat();return this.backBufferFormat="rgba8unorm"===f?s:r,this.canvasConfig={device:this.wgpu,colorSpace:"srgb",alphaMode:this.initOptions.alpha?"premultiplied":"opaque",format:f,usage:GPUTextureUsage.RENDER_ATTACHMENT|GPUTextureUsage.COPY_SRC|GPUTextureUsage.COPY_DST,viewFormats:[]},this.gpuContext.configure(this.canvasConfig),this.createBackbuffer(),this.clearRenderer=new B(this),this.mipmapRenderer=new T(this),this.resolver=new E(this),this.postInit(),this}postInit(){super.postInit(),this.initializeRenderState(),this.setupPassEncoderDefaults(),this.gpuProfiler=new v(this),this.dynamicBuffers=new C(this,1048576,this.limits.minUniformBufferOffsetAlignment)}createBackbuffer(){this.supportsStencil=this.initOptions.stencil,this.backBuffer=new h({name:"WebgpuFramebuffer",graphicsDevice:this,depth:this.initOptions.depth,stencil:this.supportsStencil,samples:this.samples})}frameStart(){super.frameStart(),this.gpuProfiler.frameStart(),this.submit();const e=this.gpuContext.getCurrentTexture();this.backBufferSize.x===e.width&&this.backBufferSize.y===e.height||(this.backBufferSize.set(e.width,e.height),this.backBuffer.destroy(),this.backBuffer=null,this.createBackbuffer());const t=this.backBuffer,s=t.impl;s.setColorAttachment(0,void 0,e.format),this.initRenderTarget(t),s.assignColorTexture(e)}frameEnd(){super.frameEnd(),this.gpuProfiler.frameEnd(),this.submit(),this.contextLost||this.gpuProfiler.request()}createBufferImpl(e){return new P(e)}createUniformBufferImpl(e){return new b(e)}createVertexBufferImpl(e,t,s){return new w(e,t,s)}createIndexBufferImpl(e,t){return new m(e,t)}createShaderImpl(e){return new g(e)}createTextureImpl(e){return new x(e)}createRenderTargetImpl(e){return new c(e)}createBindGroupFormatImpl(e){return new l(e)}createBindGroupImpl(e){return new p}createComputeImpl(e){return new S(e)}setBindGroup(e,t){this.passEncoder&&(this.passEncoder.setBindGroup(e,t.impl.bindGroup,t.uniformBufferOffsets),this.bindGroupFormats[e]=t.format.impl)}submitVertexBuffer(e,t){const s=e.format,{interleaved:r,elements:i}=s,n=i.length,o=e.impl.buffer;if(r)return this.passEncoder.setVertexBuffer(t,o),1;for(let e=0;e<n;e++)this.passEncoder.setVertexBuffer(t+e,o,i[e].offset);return n}validateVBLocations(e,t){const s=e=>{const{elements:t}=e.format;for(let e=0;e<t.length;e++){const s=t[e].name,r=o[s];y.has(r),y.set(r,s)}};s(e),s(t),y.clear()}draw(e,t=1,s){if(this.shader.ready&&!this.shader.failed){const s=this.passEncoder,r=this.vertexBuffers[0],i=this.vertexBuffers[1];if(this.vertexBuffers.length=0,r){const e=this.submitVertexBuffer(r,0);i&&this.submitVertexBuffer(i,e)}const n=this.renderPipeline.get(e,null==r?void 0:r.format,null==i?void 0:i.format,this.shader,this.renderTarget,this.bindGroupFormats,this.blendState,this.depthState,this.cullMode,this.stencilEnabled,this.stencilFront,this.stencilBack);this.pipeline!==n&&(this.pipeline=n,s.setPipeline(n));const o=this.indexBuffer;o?(this.indexBuffer=null,s.setIndexBuffer(o.impl.buffer,o.impl.format),s.drawIndexed(e.count,t,e.base,0,0)):s.draw(e.count,t,e.base,0)}}setShader(e,t=!1){e!==this.shader&&(this.shader=e)}setBlendState(e){this.blendState.copy(e)}setDepthState(e){this.depthState.copy(e)}setStencilState(e,t){if(e||t){this.stencilEnabled=!0,this.stencilFront.copy(null!=e?e:u.DEFAULT),this.stencilBack.copy(null!=t?t:u.DEFAULT);const s=this.stencilFront.ref;this.stencilRef!==s&&(this.stencilRef=s,this.passEncoder.setStencilReference(s))}else this.stencilEnabled=!1}setBlendColor(e,t,s,r){const i=this.blendColor;e===i.r&&t===i.g&&s===i.b&&r===i.a||(i.set(e,t,s,r),this.passEncoder.setBlendConstant(i))}setCullMode(e){this.cullMode=e}setAlphaToCoverage(e){}initializeContextCaches(){super.initializeContextCaches()}setupPassEncoderDefaults(){this.pipeline=null,this.stencilRef=0,this.blendColor.set(0,0,0,0)}_uploadDirtyTextures(){this.textures.forEach((e=>{(e._needsUpload||e._needsMipmaps)&&e.upload()}))}startRenderPass(e){this._uploadDirtyTextures();const t=e.renderTarget||this.backBuffer;this.renderTarget=t;const s=t.impl;this.commandEncoder=this.wgpu.createCommandEncoder(),t!==this.backBuffer&&this.initRenderTarget(t),s.setupForRenderPass(e);const r=s.renderPassDescriptor;if(this.gpuProfiler._enabled&&this.gpuProfiler.timestampQueriesSet){const t=this.gpuProfiler.getSlot(e.name);r.timestampWrites={querySet:this.gpuProfiler.timestampQueriesSet.querySet,beginningOfPassWriteIndex:2*t,endOfPassWriteIndex:2*t+1}}this.passEncoder=this.commandEncoder.beginRenderPass(r),this.setupPassEncoderDefaults();const{width:i,height:n}=t;this.setViewport(0,0,i,n),this.setScissor(0,0,i,n),this.insideRenderPass=!0}endRenderPass(e){this.passEncoder.end(),this.passEncoder=null,this.insideRenderPass=!1,this.bindGroupFormats.length=0;for(let t=0;t<e.colorArrayOps.length;t++){e.colorArrayOps[t].mipmaps&&this.mipmapRenderer.generate(e.renderTarget._colorBuffers[t].impl)}const t=this.commandEncoder.finish();this.addCommandBuffer(t),this.commandEncoder=null}startComputePass(){this.commandEncoder=this.wgpu.createCommandEncoder(),this.pipeline=null,this.passEncoder=this.commandEncoder.beginComputePass(),this.insideRenderPass=!0}endComputePass(){this.passEncoder.end(),this.passEncoder=null,this.insideRenderPass=!1,this.bindGroupFormats.length=0;const e=this.commandEncoder.finish();this.addCommandBuffer(e),this.commandEncoder=null}computeDispatch(e){this.startComputePass();for(let t=0;t<e.length;t++){const s=e[t];s.applyParameters(),s.impl.updateBindGroup()}for(let t=0;t<e.length;t++){const s=e[t];s.impl.dispatch(s.countX,s.countY,s.countZ)}this.endComputePass()}addCommandBuffer(e,t=!1){t?this.commandBuffers.unshift(e):this.commandBuffers.push(e)}submit(){this.commandBuffers.length>0&&(this.dynamicBuffers.submit(),this.wgpu.queue.submit(this.commandBuffers),this.commandBuffers.length=0,this.dynamicBuffers.onCommandBuffersSubmitted())}clear(e){e.flags&&this.clearRenderer.clear(this,this.renderTarget,e,this.defaultClearOptions)}setViewport(e,t,s,r){this.passEncoder&&(this.renderTarget.flipY||(t=this.renderTarget.height-t-r),this.vx=e,this.vy=t,this.vw=s,this.vh=r,this.passEncoder.setViewport(e,t,s,r,0,1))}setScissor(e,t,s,r){this.passEncoder&&(this.renderTarget.flipY||(t=this.renderTarget.height-t-r),this.sx=e,this.sy=t,this.sw=s,this.sh=r,this.passEncoder.setScissorRect(e,t,s,r))}clearStorageBuffer(e,t=0,s=e.byteSize){var r;const i=null!=(r=this.commandEncoder)?r:this.wgpu.createCommandEncoder();if(i.clearBuffer(e.buffer,t,s),!this.commandEncoder){const e=i.finish();this.addCommandBuffer(e)}}readStorageBuffer(e,t=0,s=e.byteSize-t,r=null,o=!1){var a;const h=this.createBufferImpl(i|n);h.allocate(this,s);const u=h.buffer,p=null!=(a=this.commandEncoder)?a:this.wgpu.createCommandEncoder();if(p.copyBufferToBuffer(e.buffer,t,u,0,s),!this.commandEncoder){const e=p.finish();this.addCommandBuffer(e)}return new Promise(((e,t)=>{const i=()=>{null==u||u.mapAsync(GPUMapMode.READ).then((()=>{null!=r||(r=new Uint8Array(s));const t=u.getMappedRange(0,s),i=r.constructor;r.set(new i(t)),u.unmap(),h.destroy(this),e(r)}))};o?(this.submit(),i()):setTimeout((()=>{i()}))}))}writeStorageBuffer(e,t=0,s,r=0,i){this.wgpu.queue.writeBuffer(e.buffer,t,s,r,i)}copyRenderTarget(e,t,s,r){var i;const n={width:e?e.width:t.width,height:e?e.height:t.height,depthOrArrayLayers:1},o=null!=(i=this.commandEncoder)?i:this.wgpu.createCommandEncoder();if(s){const s={texture:e?e.colorBuffer.impl.gpuTexture:this.renderTarget.impl.assignedColorTexture,mipLevel:0},r={texture:t?t.colorBuffer.impl.gpuTexture:this.renderTarget.impl.assignedColorTexture,mipLevel:0};o.copyTextureToTexture(s,r,n)}if(r){const s=(e||this.renderTarget).impl.depthTexture;if(e.samples>1){const e=t.colorBuffer.impl.gpuTexture;this.resolver.resolveDepth(o,s,e)}else{const e={texture:s,mipLevel:0},r={texture:t?t.depthBuffer.impl.gpuTexture:this.renderTarget.impl.depthTexture,mipLevel:0};o.copyTextureToTexture(e,r,n)}}if(!this.commandEncoder){const e=o.finish();this.addCommandBuffer(e)}return!0}}export{R as WebgpuGraphicsDevice};