{"version":3,"file":"GpuMipmapGenerator.mjs","sources":["../../../../../../src/rendering/renderers/gpu/texture/utils/GpuMipmapGenerator.ts"],"sourcesContent":["/**\n * A class which generates mipmaps for a GPUTexture.\n * Thanks to @toji for the original implementation\n * https://github.com/toji/web-texture-tool/blob/main/src/webgpu-mipmap-generator.js\n * @memberof rendering\n * @ignore\n */\nexport class GpuMipmapGenerator\n{\n    public device: GPUDevice;\n    public sampler: GPUSampler;\n    public pipelines: Record<string, GPURenderPipeline>;\n\n    public mipmapShaderModule: any;\n\n    constructor(device: GPUDevice)\n    {\n        this.device = device;\n        this.sampler = device.createSampler({ minFilter: 'linear' });\n        // We'll need a new pipeline for every texture format used.\n        this.pipelines = {};\n    }\n\n    private _getMipmapPipeline(format: GPUTextureFormat)\n    {\n        let pipeline = this.pipelines[format];\n\n        if (!pipeline)\n        {\n            // Shader modules is shared between all pipelines, so only create once.\n            if (!this.mipmapShaderModule)\n            {\n                this.mipmapShaderModule = this.device.createShaderModule({\n                    code: /* wgsl */ `\n                        var<private> pos : array<vec2<f32>, 3> = array<vec2<f32>, 3>(\n                        vec2<f32>(-1.0, -1.0), vec2<f32>(-1.0, 3.0), vec2<f32>(3.0, -1.0));\n\n                        struct VertexOutput {\n                        @builtin(position) position : vec4<f32>,\n                        @location(0) texCoord : vec2<f32>,\n                        };\n\n                        @vertex\n                        fn vertexMain(@builtin(vertex_index) vertexIndex : u32) -> VertexOutput {\n                        var output : VertexOutput;\n                        output.texCoord = pos[vertexIndex] * vec2<f32>(0.5, -0.5) + vec2<f32>(0.5);\n                        output.position = vec4<f32>(pos[vertexIndex], 0.0, 1.0);\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 : vec2<f32>) -> @location(0) vec4<f32> {\n                        return textureSample(img, imgSampler, texCoord);\n                        }\n                    `,\n                });\n            }\n\n            pipeline = this.device.createRenderPipeline({\n                layout: 'auto',\n                vertex: {\n                    module: this.mipmapShaderModule,\n                    entryPoint: 'vertexMain',\n                },\n                fragment: {\n                    module: this.mipmapShaderModule,\n                    entryPoint: 'fragmentMain',\n                    targets: [{ format }],\n                }\n            });\n\n            this.pipelines[format] = pipeline;\n        }\n\n        return pipeline;\n    }\n\n    /**\n     * Generates mipmaps for the given GPUTexture from the data in level 0.\n     * @param {module:External.GPUTexture} texture - Texture to generate mipmaps for.\n     * @returns {module:External.GPUTexture} - The originally passed texture\n     */\n    public generateMipmap(texture: GPUTexture)\n    {\n        const pipeline = this._getMipmapPipeline(texture.format);\n\n        if (texture.dimension === '3d' || texture.dimension === '1d')\n        {\n            throw new Error('Generating mipmaps for non-2d textures is currently unsupported!');\n        }\n\n        let mipTexture = texture;\n        const arrayLayerCount = texture.depthOrArrayLayers || 1; // Only valid for 2D textures.\n\n        // If the texture was created with RENDER_ATTACHMENT usage we can render directly between mip levels.\n        const renderToSource = texture.usage & GPUTextureUsage.RENDER_ATTACHMENT;\n\n        if (!renderToSource)\n        {\n            // Otherwise we have to use a separate texture to render into. It can be one mip level smaller than the source\n            // texture, since we already have the top level.\n            const mipTextureDescriptor = {\n                size: {\n                    width: Math.ceil(texture.width / 2),\n                    height: Math.ceil(texture.height / 2),\n                    depthOrArrayLayers: arrayLayerCount,\n                },\n                format: texture.format,\n                usage: GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,\n                mipLevelCount: texture.mipLevelCount - 1,\n            };\n\n            mipTexture = this.device.createTexture(mipTextureDescriptor);\n        }\n\n        const commandEncoder = this.device.createCommandEncoder({});\n        // TODO: Consider making this static.\n        const bindGroupLayout = pipeline.getBindGroupLayout(0);\n\n        for (let arrayLayer = 0; arrayLayer < arrayLayerCount; ++arrayLayer)\n        {\n            let srcView = texture.createView({\n                baseMipLevel: 0,\n                mipLevelCount: 1,\n                dimension: '2d',\n                baseArrayLayer: arrayLayer,\n                arrayLayerCount: 1,\n            });\n\n            let dstMipLevel = renderToSource ? 1 : 0;\n\n            for (let i = 1; i < texture.mipLevelCount; ++i)\n            {\n                const dstView = mipTexture.createView({\n                    baseMipLevel: dstMipLevel++,\n                    mipLevelCount: 1,\n                    dimension: '2d',\n                    baseArrayLayer: arrayLayer,\n                    arrayLayerCount: 1,\n                });\n\n                const passEncoder = commandEncoder.beginRenderPass({\n                    colorAttachments: [{\n                        view: dstView,\n                        storeOp: 'store',\n                        loadOp: 'clear',\n                        clearValue: { r: 0, g: 0, b: 0, a: 0 },\n                    }],\n                });\n\n                const bindGroup = this.device.createBindGroup({\n                    layout: bindGroupLayout,\n                    entries: [{\n                        binding: 0,\n                        resource: this.sampler,\n                    }, {\n                        binding: 1,\n                        resource: srcView,\n                    }],\n                });\n\n                passEncoder.setPipeline(pipeline);\n                passEncoder.setBindGroup(0, bindGroup);\n                passEncoder.draw(3, 1, 0, 0);\n\n                passEncoder.end();\n\n                srcView = dstView;\n            }\n        }\n\n        // If we didn't render to the source texture, finish by copying the mip results from the temporary mipmap texture\n        // to the source.\n        if (!renderToSource)\n        {\n            const mipLevelSize = {\n                width: Math.ceil(texture.width / 2),\n                height: Math.ceil(texture.height / 2),\n                depthOrArrayLayers: arrayLayerCount,\n            };\n\n            for (let i = 1; i < texture.mipLevelCount; ++i)\n            {\n                commandEncoder.copyTextureToTexture({\n                    texture: mipTexture,\n                    mipLevel: i - 1,\n                }, {\n                    texture,\n                    mipLevel: i,\n                }, mipLevelSize);\n\n                mipLevelSize.width = Math.ceil(mipLevelSize.width / 2);\n                mipLevelSize.height = Math.ceil(mipLevelSize.height / 2);\n            }\n        }\n\n        this.device.queue.submit([commandEncoder.finish()]);\n\n        if (!renderToSource)\n        {\n            mipTexture.destroy();\n        }\n\n        return texture;\n    }\n}\n"],"names":[],"mappings":"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"}