UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

132 lines (126 loc) 3.52 kB
import { Shader } from "../shader.js"; import { SHADERLANGUAGE_WGSL } from "../constants.js"; class WebgpuMipmapRenderer { device; pipelineCache = /* @__PURE__ */ new Map(); constructor(device) { this.device = device; const code = ` var<private> pos : array<vec2f, 4> = array<vec2f, 4>( vec2(-1.0, 1.0), vec2(1.0, 1.0), vec2(-1.0, -1.0), vec2(1.0, -1.0) ); struct VertexOutput { @builtin(position) position : vec4f, @location(0) texCoord : vec2f }; @vertex fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput { var output : VertexOutput; output.texCoord = pos[vertexIndex] * vec2f(0.5, -0.5) + vec2f(0.5); output.position = vec4f(pos[vertexIndex], 0, 1); return output; } @group(0) @binding(0) var imgSampler : sampler; @group(0) @binding(1) var img : texture_2d<f32>; @fragment fn fragmentMain(@location(0) texCoord : vec2f) -> @location(0) vec4f { return textureSample(img, imgSampler, texCoord); } `; this.shader = new Shader(device, { name: "WebGPUMipmapRendererShader", shaderLanguage: SHADERLANGUAGE_WGSL, vshader: code, fshader: code }); this.minSampler = device.wgpu.createSampler({ minFilter: "linear" }); } destroy() { this.shader.destroy(); this.shader = null; this.pipelineCache.clear(); } generate(webgpuTexture) { const textureDescr = webgpuTexture.desc; if (textureDescr.mipLevelCount <= 1) { return; } if (webgpuTexture.texture.volume) { return; } const device = this.device; const wgpu = device.wgpu; const format = textureDescr.format; let pipeline = this.pipelineCache.get(format); if (!pipeline) { const webgpuShader = this.shader.impl; pipeline = wgpu.createRenderPipeline({ layout: "auto", vertex: { module: webgpuShader.getVertexShaderModule(), entryPoint: webgpuShader.vertexEntryPoint }, fragment: { module: webgpuShader.getFragmentShaderModule(), entryPoint: webgpuShader.fragmentEntryPoint, targets: [{ format }] }, primitive: { topology: "triangle-strip" } }); this.pipelineCache.set(format, pipeline); } const texture = webgpuTexture.texture; const numFaces = texture.cubemap ? 6 : texture.array ? texture.arrayLength : 1; const srcViews = []; for (let face = 0; face < numFaces; face++) { srcViews.push(webgpuTexture.createView({ dimension: "2d", baseMipLevel: 0, mipLevelCount: 1, baseArrayLayer: face })); } const commandEncoder = device.getCommandEncoder(); for (let i = 1; i < textureDescr.mipLevelCount; i++) { for (let face = 0; face < numFaces; face++) { const dstView = webgpuTexture.createView({ dimension: "2d", baseMipLevel: i, mipLevelCount: 1, baseArrayLayer: face }); const passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [{ view: dstView, loadOp: "clear", storeOp: "store" }] }); const bindGroup = wgpu.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [{ binding: 0, resource: this.minSampler }, { binding: 1, resource: srcViews[face] }] }); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, bindGroup); passEncoder.draw(4); passEncoder.end(); srcViews[face] = dstView; } } device.pipeline = null; } } export { WebgpuMipmapRenderer };