<p>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.</p>
<p>I make the <em>count</em> of mip levels to be <span class="math-container">$\lfloor \log_2(\max(w,h)) \rfloor + 1$</span>, while the <em>index</em> of the last level is the formula without the +1 (due to the first level being index 0).</p>
<p>Regarding <code>GL_TEXTURE_MAX_LEVEL</code>, I don't think that you need to set it at all; the <a href="https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml" rel="nofollow noreferrer">documentation</a> 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.</p>

CC BY-SA 4.0
2022-05-29T21:33:36.337Z
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.
Simon
CC BY-SA 4.0
2022-05-29T21:14:55.883Z
2022-05-29T21:14:55.883Z
Nathan Reed
<p>Using</p>
<pre><code>glTexStorage2D(GL_TEXTURE_2D, num_mipmaps, GL_RGBA8, width, height);
</code></pre>
<p>followed by</p>
<pre><code>glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
</code></pre>
<p>we can ask OpenGL to generate storage for the first <code>num_mipmaps</code> many mipmaps levels, and to compute and store those mipmaps. The width and height need not be a power of 2.</p>
<p>According to this:</p>
<p><a href="https://www.khronos.org/opengl/wiki/Texture_Storage#Image_sizes" rel="nofollow noreferrer">Khronos webpage on Texture Storage</a></p>
<p>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.</p>
<p>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
<span class="math-container">\begin{equation}
f(x) = \max(\lfloor x/2 \rfloor), 1)
\end{equation}</span>
where <span class="math-container">$\lfloor \cdot \rfloor$</span> denotes the floor function ?</p>
<p>Since I am using automatic mipmap storage generation, I suppose that I do not need these two calls:</p>
<pre><code>glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
</code></pre>
<p>at least, so it seems according to this:</p>
<p><a href="https://www.khronos.org/opengl/wiki/Common_Mistakes#Automatic_mipmap_generation" rel="nofollow noreferrer">Khronos webpage on common mistakes</a></p>
<p><strong>EDIT 1</strong></p>
<p>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
<span class="math-container">\begin{equation}
f(w, h) = \lfloor \log_2(\max(w, h)) \rfloor
\end{equation}</span>
or else
<span class="math-container">\begin{equation}
f(w, h) = \lfloor \log_2(\min(w, h)) \rfloor
\end{equation}</span>
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.</p>
<p><strong>EDIT 2</strong></p>
<p>Whatever the answer to my question, a separate question is whether I need to set <code>glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, f(w, h));</code> or whether the use of <code>glGenerateMipmap(GL_TEXTURE_2D);</code> takes care of this automatically.</p>

<opengl><texture><mipmap>
Calculating maximum number of mipmap levels for OpenGL automatic mipmap storage generation
Simon
# Calculating maximum number of mipmap levels for OpenGL automatic mipmap storage generation

(Asked by: username_0 on 2022-05-29)

Using

```
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);
glGenerateMipmap(GL_TEXTURE_2D);

```

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](https://www.khronos.org/opengl/wiki/Texture_Storage#Image_sizes)

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:

```
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);

```

at least, so it seems according to this:

[Khronos webpage on common mistakes](https://www.khronos.org/opengl/wiki/Common_Mistakes#Automatic_mipmap_generation)

**EDIT 1**

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.

**EDIT 2**

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.

**Tags:** opengl, texture, mipmap


---

## 1 answers

### Answer by username_1 on 2022-05-29. Score: 1 | Accepted answer

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](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glTexParameter.xhtml) 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.

> **Comments:**
> **username_0**: 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.  

---

