Repository: flutter
Issue: [Impeller] New Gaussian blur optimization: Add a utility for generating specific mip levels.
Labels: engine,P3,e: impeller,team-engine,triaged-engine

This is an optimization idea for reducing the overhead of mipmap generation for `EntityPass` textures. When using the new Gaussian blur, we need to generate mipmaps for `EntityPass` textures which are blurred (by way of backdrop filters or image filters) in order to render with acceptable quality. See also https://github.com/flutter/flutter/issues/140949.

This optimization builds on https://github.com/flutter/flutter/issues/140949 and relies on `EntityPass` backdrop textures receiving an appropriate `mip_count` when allocated.

Listed below are a couple of possible routes we could take for generating only the specific mip levels we need to sample from (in stead of generating _all_ mip levels via a blit pass with `pass->GenerateMipmap()`).
For guidance on how to calculate the specific mip levels that need to be generated for a given texture input, see the "How to compute the max mip level for a given Gaussian blur invocation" section of https://github.com/flutter/flutter/issues/140949.

## Possible approaches for generating mip levels

> [!Note]
> I haven't experimentally verified whether these techniques would use less energy than the regular blit pass route to generate a full mipmap. Results will almost certainly vary across different drivers and hardware, so we should profile against some representative set of real hardware before concluding that these approaches are faster.

### Use a raster pipeline

We can create a raster pipeline designed to read from mip level 0 and output a higher mip level.

The fragment shader should average together all texels in a rectangular kernel with a radius of ~ `{ floor(width / 2^miplevel), floor(height / 2^miplevel) }`.

#### Reading from mip level 0

Bind the texture as normal and explicitly sample from mip level 0. In GLSL terms, this can be done with `textureLod`, for example. Sampling from specific LoDs is supported by Vulkan, Metal, and OpenGLES 3.0. However, I'm not sure if there's a viable route when the device is limited to OpenGLES 2.0 & GLSL ES 1.0.

#### Output to a higher mip level

We can add support for outputting to a specified mip level by adding the mip level to the `RenderTarget` color attachment state:

* OpenGLES 3.0 supports this via the `level` parameter of `glFramebufferTexture2D`. OpenGLES 2.0 also has a `level` parameter, but the spec says it must always be set to 0. Not sure what's going on there.
* Metal supports this via `MTLRenderPassAttachmentDescriptor::level`.
* Vulkan supports this. I think the correct way is to set `VkFramebufferCreateInfo::layers` to 1 and `VkImageViewCreateInfo::subresourceRange.baseMipLevel` to the desired mip level (for the framebuffer attachment handed to `VkFramebufferCreateInfo::pAttachments`). The framebuffer is part of the state for beginning a render pass `vkCmdBeginRenderPass`.

> [!Note]
> Even though we're technically reading from the attached (output) texture resource, I don't think the `SupportsReadFromResolve` capability restriction applies to this case, since we're actually reading from and writing to different mip levels and thus different memory.

### Use a compute pipeline

Metal and Vulkan compute shaders support sampling and writing to bound texture mip levels. So it's possible to perform the same work as the downsample raster pipeline described above with a compute shader.