UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

132 lines (129 loc) 5.93 kB
import { Shader } from '../shader.js'; import { SHADERLANGUAGE_WGSL } from '../constants.js'; import { Debug, DebugHelper } from '../../../core/debug.js'; import { DebugGraphics } from '../debug-graphics.js'; /** * @import { WebgpuGraphicsDevice } from './webgpu-graphics-device.js' * @import { WebgpuShader } from './webgpu-shader.js' * @import { WebgpuTexture } from './webgpu-texture.js' */ /** * A WebGPU helper class implementing texture mipmap generation. * * @ignore */ class WebgpuMipmapRenderer { destroy() { this.shader.destroy(); this.shader = null; } /** * Generates mipmaps for the specified WebGPU texture. * * @param {WebgpuTexture} webgpuTexture - The texture to generate mipmaps for. */ generate(webgpuTexture) { // ignore texture with no mipmaps var textureDescr = webgpuTexture.desc; if (textureDescr.mipLevelCount <= 1) { return; } // not all types are currently supported if (webgpuTexture.texture.volume) { Debug.warnOnce('WebGPU mipmap generation is not supported volume texture.', webgpuTexture.texture); return; } var device = this.device; var wgpu = device.wgpu; /** @type {WebgpuShader} */ var webgpuShader = this.shader.impl; var pipeline = wgpu.createRenderPipeline({ layout: 'auto', vertex: { module: webgpuShader.getVertexShaderModule(), entryPoint: webgpuShader.vertexEntryPoint }, fragment: { module: webgpuShader.getFragmentShaderModule(), entryPoint: webgpuShader.fragmentEntryPoint, targets: [ { format: textureDescr.format // use the same format as the texture } ] }, primitive: { topology: 'triangle-strip' } }); DebugHelper.setLabel(pipeline, 'RenderPipeline-MipmapRenderer'); var texture = webgpuTexture.texture; var numFaces = texture.cubemap ? 6 : texture.array ? texture.arrayLength : 1; var srcViews = []; for(var face = 0; face < numFaces; face++){ srcViews.push(webgpuTexture.createView({ dimension: '2d', baseMipLevel: 0, mipLevelCount: 1, baseArrayLayer: face })); } // loop through each mip level and render the previous level's contents into it. var commandEncoder = device.getCommandEncoder(); DebugGraphics.pushGpuMarker(device, 'MIPMAP-RENDERER'); for(var i = 1; i < textureDescr.mipLevelCount; i++){ for(var face1 = 0; face1 < numFaces; face1++){ var dstView = webgpuTexture.createView({ dimension: '2d', baseMipLevel: i, mipLevelCount: 1, baseArrayLayer: face1 }); var passEncoder = commandEncoder.beginRenderPass({ colorAttachments: [ { view: dstView, loadOp: 'clear', storeOp: 'store' } ] }); DebugHelper.setLabel(passEncoder, "MipmapRenderer-PassEncoder_" + i); var bindGroup = wgpu.createBindGroup({ layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: this.minSampler }, { binding: 1, resource: srcViews[face1] } ] }); passEncoder.setPipeline(pipeline); passEncoder.setBindGroup(0, bindGroup); passEncoder.draw(4); passEncoder.end(); // next iteration srcViews[face1] = dstView; } } DebugGraphics.popGpuMarker(device); // clear invalidated state device.pipeline = null; } constructor(device){ this.device = device; // Shader that renders a fullscreen textured quad var code = "\n \n var<private> pos : array<vec2f, 4> = array<vec2f, 4>(\n vec2(-1.0, 1.0), vec2(1.0, 1.0),\n vec2(-1.0, -1.0), vec2(1.0, -1.0)\n );\n\n struct VertexOutput {\n @builtin(position) position : vec4f,\n @location(0) texCoord : vec2f\n };\n\n @vertex\n fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {\n var output : VertexOutput;\n output.texCoord = pos[vertexIndex] * vec2f(0.5, -0.5) + vec2f(0.5);\n output.position = vec4f(pos[vertexIndex], 0, 1);\n return output;\n }\n\n @group(0) @binding(0) var imgSampler : sampler;\n @group(0) @binding(1) var img : texture_2d<f32>;\n\n @fragment\n fn fragmentMain(@location(0) texCoord : vec2f) -> @location(0) vec4f {\n return textureSample(img, imgSampler, texCoord);\n }\n "; this.shader = new Shader(device, { name: 'WebGPUMipmapRendererShader', shaderLanguage: SHADERLANGUAGE_WGSL, vshader: code, fshader: code }); // using minified rendering, so that's the only filter mode we need to set. this.minSampler = device.wgpu.createSampler({ minFilter: 'linear' }); } } export { WebgpuMipmapRenderer };