Skip to content

WICTextureLoader

Chuck Walbourn edited this page Apr 26, 2022 · 57 revisions
DirectXTK

A Direct3D 12 2D texture loader that uses Windows Imaging Component (WIC) to load a bitmap (BMP, JPEG, PNG, TIFF, GIF, HD Photo, or other WIC supported file container), resize if needed, format convert to a standard DXGI format if required, and then create a 2D texture. It can also optionally auto-generate mipmaps.

This loader does not support array textures, 1D textures, 3D volume textures, cubemaps, or cubemap arrays. For these scenarios, use the .DDS file format and DDSTextureLoader instead.

DDSTextureLoader is recommended for fully "precooked" textures for maximum performance and image quality, but this loader can be useful for creating simple 2D texture from standard image files at runtime.

On Windows 10, you can load simple 2D textures in BC1, BC2, or BC3 pixel format DDS files using WIC as there is a basic DDS built-in codec present. All other pixel formats and resource types will fail, and you'll get WINCODEC_ERR_COMPONENTNOTFOUND on older versions of Windows trying to use WICTextureLoader to load DDS files. Use DDSTextureLoader instead.

A standalone version is included in DirectXTex for Direct3D 9, Direct3D 11, and Direct3D 12. Be sure to add both the h and cpp file to your project.

Related tutorial: Sprites and textures

Header

#include <WICTextureLoader.h>

Initialization

The library assumes that the client code will have already called CoInitialize, CoInitializeEx, or Windows::Foundation::Initialize as needed by the application before calling any Windows Imaging Component functionality.

For a Universal Windows Platform (UWP) app using /ZW, the Windows Runtime and COM is initialized by the C/C++ Run-Time. For C++/WinRT applications, this is done by calling winrt::init_apartment();.

For a classic Windows desktop application you have to do this explicitly:

#if (_WIN32_WINNT >= 0x0A00 /*_WIN32_WINNT_WIN10*/)
  Microsoft::WRL::Wrappers::RoInitializeWrapper initialize(RO_INIT_MULTITHREADED);
  if (FAILED(initialize))
      // error
#else
  HRESULT hr = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
  if (FAILED(hr))
      // error
#endif

Functions

CreateWICTextureFromMemory

Loads a WIC-supported bitmap file from a memory buffer. It creates a Direct3D 12 resource from it. The texture data upload is queued to the provided ResourceUploadBatch.

HRESULT CreateWICTextureFromMemory(
    ID3D12Device* d3dDevice,
    ResourceUploadBatch& resourceUpload,
    const uint8_t* wicData,
    size_t wicDataSize,
    ID3D12Resource** texture,
    bool generateMips = false,
    size_t maxsize = 0);

CreateWICTextureFromFile

Loads a WIC-supported bitmap file from disk, creates a Direct3D 12 resource from it. The texture data upload is queued to the provided ResourceUploadBatch.

HRESULT CreateWICTextureFromFile(
    ID3D12Device* d3dDevice,
    ResourceUploadBatch& resourceUpload,
    const wchar_t* szFileName,
    ID3D12Resource** texture,
    bool generateMips = false,
    size_t maxsize = 0);

CreateWICTextureFromMemoryEx, CreateWICTextureFromFileEx

These versions provide explicit control over the created resource's flags (the standard version default to D3D12_RESOURCE_FLAG_NONE).

There is also a loadFlags parameter. The flags are WIC_LOADER_DEFAULT, WIC_LOADER_FORCE_SRGB, WIC_LOADER_IGNORE_SRGB, WIC_LOADER_SRGB_DEFAULT, WIC_LOADER_MIP_AUTOGEN, WIC_LOADER_MIP_RESERVE, WIC_LOADER_FIT_POW2, WIC_LOADER_MAKE_SQUARE, and/or WIC_LOADER_FORCE_RGBA32.

HRESULT CreateWICTextureFromMemoryEx(
    ID3D12Device* d3dDevice,
    ResourceUploadBatch& resourceUpload,
    const uint8_t* wicData,
    size_t wicDataSize,
    size_t maxsize,
    D3D12_RESOURCE_FLAGS resFlags,
    WIC_LOADER_FLAGS loadFlags,
    ID3D12Resource** texture);

HRESULT CreateWICTextureFromFileEx(
    ID3D12Device* d3dDevice,
    ResourceUploadBatch& resourceUpload,
    const wchar_t* szFileName,
    size_t maxsize,
    D3D12_RESOURCE_FLAGS resFlags,
    WIC_LOADER_FLAGS loadFlags,
    ID3D12Resource** texture);

The WIC_LOADER_FORCE_SRGB, WIC_LOADER_IGNORE_SRGB, and WIC_LOADER_SRGB_DEFAULT flags are detailed below under sRGB.

The WIC_LOADER_MIP_AUTOGEN is the same as the generateMips parameter for the non-Ex versions which creates space for the additional mipchains and then auto-generates them from the base image.

The WIC_LOADER_MIP_RESERVE flag is used to create the extra mipchains but leave them uninitialized. This is useful if your application will do the auto-generation through it's own implementation.

The WIC_LOADER_FIT_POW2 and/or WIC_LOADER_MAKE_SQUARE flags can be used to force the image to be resized to a power-of-2 size and/or be made a square texture that has width == height. These flags are most useful for the Direct3D 9 legacy version of WICTextureLoader, but are supported in all versions.

The WIC_LOADER_FORCE_RGBA32 flag is explained under Pixel format conversions.

The loadFlags parameter was previously bool forceSRGB and bool generateMips.

Non-upload versions

The Create functions rely on the ResourceUploadBatch class to manage the GPU upload. For applications where you want to handle the upload directly, you can make use of:

  • LoadWICTextureFromMemory
  • LoadWICTextureFromFile
  • LoadWICTextureFromMemoryEx
  • LoadWICTextureFromFileEx

These functions create the texture resource and return the initialization data in decodedData which is pointed into by subresource. They don't support mipmaps generation, but do support reserving space for mipmaps.

The Create functions are not included in the stand-alone copy of WICTextureLoader12 that is included with DirectXTex, only the Load functions.

Parameters

To automatically generate mipmaps, set the generateMips parameter to true or set loadFlags to WIC_LOADER_MIP_AUTOGEN. Note that automatic mipmap generation for Direct3D 12 is not supported by the runtime or driver, and is implemented in ResourceUploadBatch.

For all these functions above, the maxsize parameter provides an upper limit on the size of the resulting texture. If given a 0, the functions assume a maximum size of D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION (16384). If the bitmap file contains a larger image, it will be resized using WIC at load-time to provide scaling.

Resource states

For the Create functions that utilize ResourceUploadBatch, the texture resources will be in the D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE state if using a Direct command queue.

If using a Compute or Copy command queue, the resource state will be left in D3D12_RESOURCE_STATE_COPY_DEST.

Requests to generate mips are ignored if the upload batch is set to use a Copy command queue since that does not support the required compute shader functionality.

To render all resources need to be in the proper state. With Windows PC, common state promotion will typically fix this up. For Xbox One where this feature is optional or for other usage scenarios, you need to insert resource barriers for transitioning the resource state.

TransitionResource(commandList, tex.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

Examples

This loads a bitmap, and ResourceUploadBatch performs the upload as well as generates mipmaps using a compute shader--non-DDS file formats do not include mipmap chains.

ComPtr<ID3D12Resource> tex;

ResourceUploadBatch resourceUpload(device);

resourceUpload.Begin();

DX::ThrowIfFailed(
    CreateWICTextureFromFile(device, resourceUpload, L"texture.bmp",
        tex.ReleaseAndGetAddressOf(), true)
);

// Upload the resources to the GPU.
auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue());

// Wait for the upload thread to terminate
uploadResourcesFinished.wait();

If handling your own upload, use the Load functions:

std::unique_ptr<uint8_t[]> decodedData;
D3D12_SUBRESOURCE_DATA subresource;
DX::ThrowIfFailed(
    LoadWICTextureFromFile(device, L"texture.bmp", tex.ReleaseAndGetAddressOf(),
        decodedData, subresource));

const UINT64 uploadBufferSize = GetRequiredIntermediateSize(tex.Get(), 0, 1);

CD3DX12_HEAP_PROPERTIES heapProps(D3D12_HEAP_TYPE_UPLOAD);

auto desc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);

// Create the GPU upload buffer.
ComPtr<ID3D12Resource> uploadRes;
DX::ThrowIfFailed(
    device->CreateCommittedResource(
        &heapProps,
        D3D12_HEAP_FLAG_NONE,
        &desc,
        D3D12_RESOURCE_STATE_GENERIC_READ,
        nullptr,
        IID_PPV_ARGS(uploadRes.GetAddressOf())));

UpdateSubresources(commandList, tex.Get(), uploadRes.Get(),
    0, 0, 1, &subresource);

auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(tex.Get(),
    D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
commandList->ResourceBarrier(1, &barrier);

DX::ThrowIfFailed(commandList->Close());
m_deviceResources->GetCommandQueue()->ExecuteCommandLists(1,
    CommandListCast(&commandList));

In order to actually render with this texture, you must create a texture heap descriptor and associate it with the loaded resource. Often this is done using the DirectXHelpers function CreateShaderResourceView:

CreateShaderResourceView(device, tex.Get(),
    resourceDescriptors->GetCpuHandle(Descriptors::MyTexture));

Then to draw you use resourceDescriptors->GetGpuHandle(Descriptors::MyTexture) and set the descriptor heap with SetDescriptorHeaps. See DescriptorHeap for more information.

Here's a snippet for loading a texture forcing use of DXGI_FORMAT_*_SRGB via a loader flag, but is otherwise the same as the call above:

DX::ThrowIfFailed(
    CreateWICTextureFromFileEx(device, resourceUpload, L"texture.bmp",
        0,
        D3D12_RESOURCE_FLAG_NONE,
        WIC_LOADER_FORCE_SRGB | WIC_LOADER_MIP_AUTOGEN,
        tex.ReleaseAndGetAddressOf())
);

sRGB

  • While there is no explicit 'sRGB' pixel format defined for WIC, the load functions will check for known metadata tags and may return DXGI_FORMAT_*_SRGB formats if there are equivalents of the same size and channel configuration available. Setting loadFlags to WIC_LOADER_IGNORE_SRGB will ignore this metadata.

    • For PNG, this is indicated by /sRGB/RenderingIntent set to 1.
    • For JPG this is /app1/ifd/exif/{ushort=40961} set to 1.
    • For TIFF this is /ifd/exif/{ushort=40961} set to 1.
  • Setting loadFlags to WIC_LOADER_FORCE_SRGB will force conversion of the DXGI_FORMAT to one of DXGI_FORMAT_*_SRGB formats if it exist. This is useful for loading sRGB content for linearly-correct rendering that lacks the metadata tags to indicate sRGB colorspace. Note that no pixel data conversion takes place.

  • gAMA chunks in PNGs are ignored. If the sRGB chunk is found, it is assumed to be gamma 2.2.

  • By default, the lack of explicit 'sRGB' metadata implies the format is linear (i.e. not sRGB). Setting loadFlags to WIC_LOADER_SRGB_DEFAULT will reverse this assumption.

Pixel format conversions

WIC has built-in support for doing pixel format conversions.

If the loadFlags include WIC_LOADER_FORCE_RGBA32, then the result is always a DXGI_FORMAT_R8G8B8A8_UNORM texture by converting to GUID_WICPixelFormat32bppRGBA. If WIC_LOADER_FORCE_SRGB is also set, the result with be set to DXGI_FORMAT_R8G8B8A8_UNORM_SRGB.

The conversion tables are designed so that they prefer to convert to RGB if a conversion is required as a general preferance for DXGI 1.0 supporting formats supported by WDDM 1.0 drivers. The majority of Direct3D 11 devices actually support BGR DXGI 1.1 formats so we use them when they are the best match. For example, GUID_WICPixelFormat32bppBGRA loads directly as DXGI_FORMAT_B8G8R8A8_UNORM, but GUID_WICPixelFormat32bppPBGRA converts to DXGI_FORMAT_R8G8B8A8_UNORM.

GUID_WICPixelFormatBlackWhite is always converted to a greyscale DXGI_FORMAT_R8_UNORM since DXGI_FORMAT_R1_UNORM is not supported by Direct3D.

GUID_WICPixelFormat32bppRGBE is an 8:8:8:8 format, which does not match DXGI_FORMAT_R9G9B9E5_SHAREDEXP. This WIC pixel format is therefore converted to GUID_WICPixelFormat128bppRGBAFloat and returns as DXGI_FORMAT_R32G32B32A32_FLOAT.

Remarks

If the format is unsupported for auto-gen mipmaps by the device or if a copy command queue was provided to the ResourceUploadBatch, then a request for autogen mips via generateMips or WIC_LOADER_MIP_AUTOGEN will be ignored.

WICTextureLoader cannot load .TGA/.HDR files unless the system has a 3rd party WIC codec installed. You must use the DirectXTex library for TGA/HDR file format support without relying on an add-on WIC codec.

WIC2

Since WIC2 is available on Windows 10, it is always used by DirectX Tool Kit for DirectX 12.

See Windows Imaging Component and Windows 8

Threading model

The Create functions require a ResourceUploadBatch so they only support submitting content from one thread at a time. You can load on multiple threads if you create a separate ResourceUploadBatch instance per command-list.

The Load functions are fully asynchronous.

Work Submission in Direct3D 12

Windows Store apps

For information on using WICTextureLoader from a Windows Store app or a Universal Windows Platform (UWP) app, see here

Debugging texture loading

If the HRESULT is a success, but you are still having problems using the texture, the next step in debugging is to examine details about the loaded texture. You can do this easily with the following code:

ComPtr<ID3D12Resource> res;
HRESULT hr = CreateWICTextureFromFile( d3dDevice.Get(), resourceUpload, L"LOGO.BMP",
    res.GetAddressOf());
DX::ThrowIfFailed(hr);

auto desc = res->GetDesc();

desc.Width is the texture width in pixels (of the top most mip level)
desc.Height is the texture height in pixels (of the top most mip level)
desc.MipLevels is the number of mip levels (or 1 if there are no mips)
desc.DepthOrArraySize is the number of textures in the array (or 1 if this not an array)
desc.Format is the DXGI format of the texture resource

Further reading

Textures

Uploading Texture Data Through Buffers

Linear-Space Lighting (i.e. Gamma)

Chapter 24. The Importance of Being Linear, GPU Gems 3

Gamma-correct rendering

For Use

  • Universal Windows Platform apps
  • Windows desktop apps
  • Windows 11
  • Windows 10
  • Xbox One
  • Xbox Series X|S

For Development

  • Visual Studio 2022
  • Visual Studio 2019 (16.11)
  • clang/LLVM v12 - v18
  • MinGW 12.2, 13.2
  • CMake 3.20

Related Projects

DirectX Tool Kit for DirectX 11

DirectXMesh

DirectXTex

DirectXMath

Tools

Test Suite

Model Viewer

Content Exporter

DxCapsViewer

See also

DirectX Landing Page

Clone this wiki locally