Bug 109816 - glGenerateMipmap makes deepest level far from average texel value for NPOT textures
Summary: glGenerateMipmap makes deepest level far from average texel value for NPOT te...
Status: RESOLVED MOVED
Alias: None
Product: Mesa
Classification: Unclassified
Component: Mesa core (show other bugs)
Version: 18.2
Hardware: Other All
: medium normal
Assignee: mesa-dev
QA Contact: mesa-dev
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2019-03-02 14:03 UTC by Ruslan Kabatsayev
Modified: 2019-09-18 20:27 UTC (History)
0 users

See Also:
i915 platform:
i915 features:


Attachments
Test project (21.48 KB, application/x-xz)
2019-03-02 14:03 UTC, Ruslan Kabatsayev
Details

Description Ruslan Kabatsayev 2019-03-02 14:03:33 UTC
Created attachment 143513 [details]
Test project

Trying to get an average of the pixels I rendered in the scene, I've come across a problem with the way Mesa's implementation of glGenerateMipmap works with NPOT textures.
To estimate the average, I render the scene to an FBO texture, and then use the following C++ code (full compilable test project is attached to this bug report):

// BEGIN code
void getMeanPixelValue(int texW, int texH)
{
    // Get average value of the rendered pixels as the value of the deepest mipmap level
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texFBO);
    glGenerateMipmap(GL_TEXTURE_2D);

    using namespace std;
    // Formula from the glspec, "Mipmapping" subsection in section 3.8.11 Texture Minification
    const auto totalMipmapLevels = 1+floor(log2(max(texW,texH)));
    const auto deepestLevel=totalMipmapLevels-1;

    // Sanity check
    int deepestMipmapLevelWidth=-1, deepestMipmapLevelHeight=-1;
    glGetTexLevelParameteriv(GL_TEXTURE_2D, deepestLevel,
        GL_TEXTURE_WIDTH, &deepestMipmapLevelWidth);
    glGetTexLevelParameteriv(GL_TEXTURE_2D, deepestLevel,
        GL_TEXTURE_HEIGHT, &deepestMipmapLevelHeight);
    assert(deepestMipmapLevelWidth==1);
    assert(deepestMipmapLevelHeight==1);

    vec4 pixel;
    glGetTexImage(GL_TEXTURE_2D, deepestLevel, GL_RGBA, GL_FLOAT, &pixel[0]);

    // Get average value in an actual summing loop over all the pixels
    std::vector<vec4> data(texW*texH);
    glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_FLOAT, data.data());
    vec4 avg(0,0,0,0);
    for(auto const& v : data)
        avg+=v;
    avg/=texW*texH;

    std::cerr << "Mipmap value: " << pixel[0] << ", " << pixel[1] 
              << ", " << pixel[2] << ", " << pixel[3] << "\n";
    std::cerr << "True average: " << avg[0] << ", " << avg[1]
              << ", " << avg[2] << ", " << avg[3] << "\n";
}
// END code

What I get from this code on Mesa Software renderer (using LIBGL_ALWAYS_SOFTWARE=1):

New size: 512x512
Mipmap value: 0.195312, 0.390625, 0.585938, 0.78125
True average: 0.195312, 0.390625, 0.585938, 0.78125
New size: 512x511
Mipmap value: 0, 1.19209e-07, 1, 1
True average: 0.195695, 0.391389, 0.587084, 0.782779

With Intel Haswell (Core i7-4765T) hardware acceleration, the 1.19209e-07 value becomes simply 0.

For comparison, below is what I get on an NVidia GPU with the proprietary "nvidia" driver. This is what I would expect from Mesa and any other decent OpenGL implementation.

New size: 512x512
Mipmap value: 0.195312, 0.390625, 0.585938, 0.78125
True average: 0.195312, 0.390625, 0.585938, 0.78125
New size: 512x511
Mipmap value: 0.195606, 0.39101, 0.586946, 0.782537
True average: 0.195695, 0.391389, 0.587084, 0.782779
Comment 1 Roland Scheidegger 2019-03-02 20:22:42 UTC
This sounds like an interesting issue (although I'm pretty sure it doesn't qualify as a bug).
GL states that
"The contents of the derived images are computed by repeated, filtered reduction of the levelbase image. No particular filter algorithm is required, though a box filter is recommended as the default filter."

I believe this is generally how all drivers in mesa implement this (they don't all use the same helpers for it, but I think all end up in a blit with bilinear filter essentially).

But you are quite right that the results won't be all that great for npot textures - not even all src texels will be actually considered in the filter (e.g. from a 3x3 to a 1x1 mipmap, only 4 of the 9 texels will actually be used).

So presumably a different (more complex, maybe bicubic or even more advanced one) filter used for generating npot mipmaps, at least for mips which round the size down from the next higher up level, could give better results.

I have no idea though what kind of filter nvidia uses (and I would not be surprised if other non-mesa blob drivers also simply use bilinear filtering).
Comment 2 Ruslan Kabatsayev 2019-03-02 20:27:55 UTC
(In reply to Roland Scheidegger from comment #1)
> I have no idea though what kind of filter nvidia uses (and I would not be
> surprised if other non-mesa blob drivers also simply use bilinear filtering).

I think they use something like they describe in their paper "Non-Power-of-Two Mipmapping":

http://download.nvidia.com/developer/Papers/2005/NP2_Mipmapping/NP2_Mipmap_Creation.pdf
Comment 3 Roland Scheidegger 2019-03-02 22:46:36 UTC
(In reply to Ruslan Kabatsayev from comment #2)
> (In reply to Roland Scheidegger from comment #1)
> > I have no idea though what kind of filter nvidia uses (and I would not be
> > surprised if other non-mesa blob drivers also simply use bilinear filtering).
> 
> I think they use something like they describe in their paper
> "Non-Power-of-Two Mipmapping":
> 
> http://download.nvidia.com/developer/Papers/2005/NP2_Mipmapping/
> NP2_Mipmap_Creation.pdf

Ah yes that would make sense (although I highly doubt you'd want to do the two-pass approach with the hw of today...). So I guess nvidia is using this approach since ages, interesting. All the apis kind of seemed to have ignored the issues with the box filter with npot textures.
Comment 4 GitLab Migration User 2019-09-18 20:27:39 UTC
-- GitLab Migration Automatic Message --

This bug has been migrated to freedesktop.org's GitLab instance and has been closed from further activity.

You can subscribe and participate further through the new bug through this link to our GitLab instance: https://gitlab.freedesktop.org/mesa/mesa/issues/1037.


Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.