Replacement for D3DX11FilterTexture() for Mipmap Generation

Apr 3, 2013 at 6:26 AM
Hey folks,

I recently needed to replace my use of D3DX11FilterTexture() for mipmap generation. The MSDN documentation suggests using GenerateMipMaps/3D() from DirectXTex, but this is not a direct replacement, since it doesn't work with D3D textures. I came up with what seems to be a good solution, which works on 2D, 3D, and cube textures. See the function below, which I added to my local copy of DirectXTexD3D11.cpp.

Do you see any issues with this implementation? Thanks!

— Mauricio
_Use_decl_annotations_
HRESULT GenerateMipMaps( _In_ ID3D11Device* pDevice, _In_ ID3D11DeviceContext* pContext, _In_ ID3D11Resource* pSource )
{
    HRESULT hr = S_OK;

    // Create a DirectXTex image from the D3D texture.
    ScratchImage image;
    hr = CaptureTexture(pDevice, pContext, pSource, image);
    if (FAILED(hr)) return hr;

    // Generate the mipmaps for the image, on the CPU.  Different functions are used for 3D and
    // everything else (1D, 2D, cube).
    ScratchImage result;
    if (image.GetMetadata().dimension == TEX_DIMENSION_TEXTURE3D)
    {
        hr = GenerateMipMaps3D(
            image.GetImages(), image.GetImageCount(), image.GetMetadata(),
            TEX_FILTER_FANT, 0, result);
    }
    else
    {
        hr = GenerateMipMaps(
            image.GetImages(), image.GetImageCount(), image.GetMetadata(),
            TEX_FILTER_FANT, 0, result);
    }

    // Return on any failure.
    if (FAILED(hr)) return hr;

    // Create a temporary D3D texture from the result image with mipmaps.
    ID3D11Resource* pTexTemp = NULL;
    hr = CreateTexture(
        pDevice, result.GetImages(), result.GetImageCount(), result.GetMetadata(), &pTexTemp);
    if (FAILED(hr)) return hr;

    // Copy the contents of the temporary texture to the original one, and then release the 
    // temporary one.
    // NOTE: It may be more efficient to copy directly from the result image to the original
    // texture, instead of using a temporary texture.  UpdateSubresource() might work for this.
    pContext->CopyResource(pSource, pTexTemp);
    pTexTemp->Release();

    return S_OK;
}
Coordinator
Apr 3, 2013 at 7:27 AM
Edited Apr 3, 2013 at 7:33 AM
I hope you only call this function after having tried to use auto-gen mipmaps which is a much more performant solution for generating mipmaps than this. Of course, ideally you'd just generate the mipmaps offline and write the out as .DDS files for loading at runtime, but I assume you have a reason for needing to generate mip-maps on-the-fly.
        UINT fmtSupport = 0;
        hr = d3dDevice->CheckFormatSupport( format, &fmtSupport );
        if ( SUCCEEDED(hr) && ( fmtSupport & D3D11_FORMAT_SUPPORT_MIP_AUTOGEN ) )
        {
            autogen = true;
        }
See WICTextureLoader.cpp for some additional details when using auto-gen mips. The biggest issue is you can't use initData and have to use CopySubresource.

The biggest potential issue in your CPU function would be if pSource is a block-compressed format. In that case, you need to call Decompress at the start and Compress at the end, although keep in mind that the second compress is lossy.

What formats exactly are you typically loading here without mips that you want to have mips generated for?

What is the source of your original texture pSource? If you are loading from a file, you could just load it using DirectXTex into a ScratchImage rather than the initial CaptureTexture().
Apr 3, 2013 at 8:08 AM
Chuck,

Thanks for the reply. This is for an authoring system (as opposed to a game), so input data can be quite arbitrary: the input texture could have any format, and it could come from an unknown source (local file, server, procedural, etc.). This is ultimately a fallback path: when nothing else will work, we need to have some answer.

That being said, D3DX11FilterTexture() is what "spoiled" us: if that wasn't previously available, we probably would not have offered that flexibility in our system. Now that we have offered it, it is difficult to take it away, hence the search for a suitable replacement.

I also vaguely recall that setting up a texture for auto-gen would make it slower to render with, e.g. because it must be a render target. That's another reason why we might take the slow mipmap generation path, in order to have a "faster" texture. But I may be operating on old information, like D3D9 requiring D3DPOOL_DEFAULT for auto-gen. Does that issue sound familiar to you?

Thanks again.

— Mauricio
Coordinator
Apr 3, 2013 at 7:40 PM
The solution above is a reasonable equivalent to D3DX11FilterTexture I think, but I'd encourage checking to see if it's overkill for your needs.

Check out the WICTextureLoader.cpp in the DirectXTex package. You can see how it uses auto-gen. It does have to be bindable as a render target to auto-gen mips in Direct3D 11. The only limitation here really is VRAM, so the question is what Feature Level of hardware you support for your app.
Apr 4, 2013 at 7:28 AM
Chuck,

I think I should clarify my previous statement:

"This is ultimately a fallback path: when nothing else will work, we need to have some answer."

What I mean is that we will only use CPU generation in the cases where GPU generation won't work, which should be rare in practice, as you say. Since the users may try to create textures that are ineligible for GPU generation, we want to keep that (slower) CPU fallback to generate mipmaps.

Thanks again!

— Mauricio