MipMap count : counter or math is better ?

Started by
6 comments, last by Hodgman 8 years, 10 months ago

Hi,

There is two methods to count mipmaps : counter and math.

Here the math method :


1 + floor(log2(max(width, height)))

Here the counter method :


int m = max(width, height);
int i = 0;
while(m > 0)
{
  m >>= 1;
  i++;
}

What is the best method ? Is the math method reliable ?

Thanks

Advertisement
Those floating-point operations are very expensive. You are generally better off doing manipulations with integers, although there are faster methods than your loop.

32 - __builtin_clz(max(height, width)); // gcc only; but I am sure other compilers have similar constructs

If counting the number of miplevels in a texture is a bottleneck then you're doing something wrong.

For which method is best, both are reliable so use whichever you prefer. I'd incline towards the math-based method primarily because it's the method used by the specification for GL_ARB_texture_storage:


    If the <levels> parameter to TexStorage* is greater than the
    <target>-specific value listed below then the error
    INVALID_OPERATION is generated:
        [PROXY_]TEXTURE_{1D,1D_ARRAY}:
            floor(log_2(width)) + 1
        [PROXY_]TEXTURE_{2D,2D_ARRAY,CUBE_MAP,CUBE_MAP_ARRAY}:
            floor(log_2(max(width, height))) + 1
        [PROXY_]TEXTURE_3D:
            floor(log_2(max(width, height, depth))) + 1
        [PROXY_]TEXTURE_RECTANGLE:
            1

Other APIs (i.e D3D) allow you to specify 0 as the number of miplevels in order to create a full mipmap chain, and in practice you don't need to know the number of miplevels created using that convention.

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

I just use the dumb loop method because it's easy to read/understand, and determining the maximum mip-chain length is not done often enough to warrant micro-optimizing :)

Those floating-point operations are very expensive. You are generally better off doing manipulations with integers, although there are faster methods than your loop.

32 - __builtin_clz(max(height, width)); // gcc only; but I am sure other compilers have similar constructs
clz/ctz are called BitscanReverse/BitscanForward on MSVC, and yep, these intrinsics map to a nice single x86 instruction :)

However, that method doesnt work for counting mips of textures that arent a power of 2 (without some extra bit twiddling to detect that more bits than the MSB are set, and to add 1 to the result).

Thanks for all answer.

My question was more about reliability of the math method than performance because yes, it's only on the editor or when build for platforms.

clz/ctz are called BitscanReverse/BitscanForward on MSVC, and yep, these intrinsics map to a nice single x86 instruction smile.png

However, that method doesnt work for counting mips of textures that arent a power of 2 (without some extra bit twiddling to detect that more bits than the MSB are set, and to add 1 to the result).

Why would that not work for NPOT textures? It's the exact same thing as the dumb loop method.


for(int i = 1; i <= MAX_TEXTURE_SIZE; i++)
{
   assert(MipCount(i) == MipMath(i));
}

Why would that not work for NPOT textures? It's the exact same thing as the dumb loop method.

I had a brainfart and thought that you rounded up when halving sizes for the next mip, not rounded down.
In reality rounding down makes all the methods work for POT and NPOT.

This topic is closed to new replies.

Advertisement