{"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 * @category 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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"}