# How is anisotropic filtering typically implemented in modern GPUs?

Anisotropic filtering "retains the sharpness of a texture normally lost by MIP map texture's attempts to avoid aliasing". The Wikipedia article gives hints about how it can be implemented ("probe the texture (...) for any orientation of anisotropy"), but it does't read very clear to me.

There seem to be various implementations, as suggested by the tests illustrated in the notes of the presentation Approximate Models For Physically Based Rendering:

What are the concrete computations performed by (modern) GPUs to choose the correct MIP level when using anisotropic filtering?

The texture filtering hardware takes several samples of the various mipmap levels (the maximum amount of samples is indicated by the anisotropic filtering level, though the exact amount of samples taken in a given filtering operation will depend on the proportion between the derivatives on the fragment.) If you project the cone viewing a surface at an oblique angle onto the texture space, it will result in approximately an oval shaped projection, which is more elongated for more oblique angles. Extra samples are taken along the axis of this oval (from the correct mip levels, to take advantage of the pre-filtering they offer) and combined to give a sharper texture sample.

Another technique know as rip-mapping (mentioned in the Wikipedia article on Mipmapping), which is not commonly found in contemporary GPUs, uses prefiltering of textures. In contrast to mips, the texture is not scaled down uniformly but using various height-width-ratios (up to a ratio dependent on your chosen anisotropic filtering level). The variant - or maybe two variants if using trilinear filtering - of the texture is then chosen based on the angle of the surface to minimize distortion. Pixel values are fetched using default filtering techniques (bilinear or trilinear). Rip-maps are not used in any hardware that I know of due to their prohibitive size: while mipmaps use additional 33% storage, ripmaps use 300%. This can be verified by noting that texture usage requirements don't increase when using AF, rather, only bandwidth does.

For futher reading, you might want to take a look at the specification for the EXT_texture_filter_anisotropic OpenGL extension. It details the formulas used to calculate samples and how to combine them when using anisotropic filtering.

• RIP maps also probably aren't used because they don't help on the, rather common, diagonal case. FWIW, if you can find the code for the Microsoft Refrast, the anistropic filter implementation in that is probably a good reference for how today's HW does it. – Simon F Sep 4 '15 at 9:43
• "This can be verified by noting that texture usage requirements don't increase when using AF, rather, only bandwidth does." Killer argument. Good answer! – David Kuri Sep 4 '15 at 10:22
• The "High-Performance Software Rasterization on GPUs" link only mentions anisotropic filtering in passing once, and gives no mention of any details. So I'm going to edit it out of the answer because I don't think it's relevant in a helpful way. – yuriks Sep 9 '15 at 18:30
• @SimonF also we can add that the additional bandwidth requirement is pretty scary. – v.oddou Sep 11 '15 at 2:06

The API requirements can be found in any of the specs or extensions. Here is one: https://www.opengl.org/registry/specs/EXT/texture_filter_anisotropic.txt

All GPU vendors likely deviate from the spec because AF-quality used to be a part of many benchmarks. And current implementations will continue to keep on evolving as new workloads stress the existing approximations. Unfortunately, to know exactly what either does, you will need to be a part of one of the companies. But you can gauge the spectrum of possibilities from the following papers, listed in increasing order of quality and implementation cost:

Quoting from the spec:

 Anisotropic texture filtering substantially changes Section 3.8.5.
Previously a single scale factor P was determined based on the
pixel's projection into texture space.  Now two scale factors,
Px and Py, are computed.

Px = sqrt(dudx^2 + dvdx^2)
Py = sqrt(dudy^2 + dvdy^2)

Pmax = max(Px,Py)
Pmin = min(Px,Py)

N = min(ceil(Pmax/Pmin),maxAniso)
Lamda' = log2(Pmax/N)

where maxAniso is the smaller of the texture's value of
TEXTURE_MAX_ANISOTROPY_EXT or the implementation-defined value of
MAX_TEXTURE_MAX_ANISOTROPY_EXT.

It is acceptable for implementation to round 'N' up to the nearest
supported sampling rate.  For example an implementation may only
support power-of-two sampling rates.

It is also acceptable for an implementation to approximate the ideal
functions Px and Py with functions Fx and Fy subject to the following
conditions:

1.  Fx is continuous and monotonically increasing in |du/dx| and |dv/dx|.
Fy is continuous and monotonically increasing in |du/dy| and |dv/dy|.

2.  max(|du/dx|,|dv/dx|} <= Fx <= |du/dx| + |dv/dx|.
max(|du/dy|,|dv/dy|} <= Fy <= |du/dy| + |dv/dy|.

Instead of a single sample, Tau, at (u,v,Lamda), 'N' locations in the mipmap
at LOD Lamda, are sampled within the texture footprint of the pixel.

Instead of a single sample, Tau, at (u,v,lambda), 'N' locations in
the mipmap at LOD Lamda are sampled within the texture footprint of
the pixel.  This sum TauAniso is defined using the single sample Tau.
When the texture's value of TEXTURE_MAX_ANISOTROPHY_EXT is greater
than 1.0, use TauAniso instead of Tau to determine the fragment's
texture value.

i=N
---
TauAniso = 1/N \ Tau(u(x - 1/2 + i/(N+1), y), v(x - 1/2 + i/(N+1), y)),  Px > Py
/
---
i=1

i=N
---
TauAniso = 1/N \ Tau(u(x, y - 1/2 + i/(N+1)), v(x, y - 1/2 + i/(N+1))),  Py >= Px
/
---
i=1

It is acceptable to approximate the u and v functions with equally spaced
samples in texture space at LOD Lamda:

i=N
---
TauAniso = 1/N \ Tau(u(x,y)+dudx(i/(N+1)-1/2), v(x,y)+dvdx(i/(N+1)-1/2)), Px > Py
/
---
i=1

i=N
---
TauAniso = 1/N \ Tau(u(x,y)+dudy(i/(N+1)-1/2), v(x,y)+dvdy(i/(N+1)-1/2)), Py >= Px
/
---
i=1