Description
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 #140949.
This optimization builds on #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 #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 ofglFramebufferTexture2D
. OpenGLES 2.0 also has alevel
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 andVkImageViewCreateInfo::subresourceRange.baseMipLevel
to the desired mip level (for the framebuffer attachment handed toVkFramebufferCreateInfo::pAttachments
). The framebuffer is part of the state for beginning a render passvkCmdBeginRenderPass
.
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.