glTexStorage2D(GL_TEXTURE_2D, num_mipmaps, GL_RGBA8, width, height);

followed by

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, data);

we can ask OpenGL to generate storage for the first num_mipmaps many mipmaps levels, and to compute and store those mipmaps. The width and height need not be a power of 2.

According to this:

Khronos webpage on Texture Storage

the dimensions of each new mipmap level are computed by halving corresponding dimension of the previous level, and rounding down, and that this process stops when all dimensions are 1.

It seems to me that if this were 100% literally what happens, then in some cases we might actually reach an image one of whose dimensions was 1, and the other of which was greater than 1. If we were to halve and round down once more, then the smaller dimension would become 0. Is it more accurate to say that the function mapping a given dimension of the current level to the corresponding dimension of the next level, is actually \begin{equation} f(x) = \max(\lfloor x/2 \rfloor), 1) \end{equation} where $\lfloor \cdot \rfloor$ denotes the floor function ?

Since I am using automatic mipmap storage generation, I suppose that I do not need these two calls:


at least, so it seems according to this:

Khronos webpage on common mistakes


If indeed the dimensions were clamped below to 1, then I suppose that the function for computing the number of distinct mipmap levels would be \begin{equation} f(w, h) = \lfloor \log_2(\max(w, h)) \rfloor \end{equation} or else \begin{equation} f(w, h) = \lfloor \log_2(\min(w, h)) \rfloor \end{equation} but the language on the Khronos website seems to suggest it's the former, which would also make more sense, since one might have a triangle whose projection into texture space was very long and thin, and whose projection onto the screen occupied only a single pixel.


Whatever the answer to my question, a separate question is whether I need to set glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, f(w, h)); or whether the use of glGenerateMipmap(GL_TEXTURE_2D); takes care of this automatically.


1 Answer 1


As you surmised, the minimum dimension is 1, and for non-square images the smaller dimension will remain 1 until we get all the way down to 1x1.

I make the count of mip levels to be $\lfloor \log_2(\max(w,h)) \rfloor + 1$, while the index of the last level is the formula without the +1 (due to the first level being index 0).

Regarding GL_TEXTURE_MAX_LEVEL, I don't think that you need to set it at all; the documentation says that the default value is 1000, which is far more than the number of mip levels of any conceivable texture. So it seems like it only acts as an upper bound.

  • 1
    $\begingroup$ That's brilliant, thank you so much ! I think you are absolutely right about the +1. I suppose I was worried that the default value of 1000 meant that I was incurring some wasted memory, but with hindsight I suppose that memory is only set by, in this case, the command glTexStorage2D(GL_TEXTURE_2D, 8, GL_RGBA8, width, height), and this would be unaffected by that level of 1000. $\endgroup$
    – Simon
    Commented May 29, 2022 at 21:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.