On Creating a D3DX10 texture atlas

| | August 11, 2015

I have a DirectX10 texture (ID3D10Texture2D) that I load from disk with the following code:

CComPtr<ID3D10Device>   spD3D; // Initialized correctly elsewhere

hr = D3DX10CreateTextureFromFile( 
        spD3D.p, 
        wsFilename.c_str(), 
        NULL, NULL, 
        &spTextureResource, 
        NULL 
    );

if( FAILED(hr) || ! spTextureResource.p )
    return false;

spTextureResource->QueryInterface <ID3D10Texture2D> ( &m_spTexture );
if( ! m_spTexture.p )
    return false;

The texture loads fine, I can blit this onscreen.

Later, I want to use that texture as an atlas. To do so, the first thing I want to do is to call ID3D10Texture2D::Map() in order to get at the texels and parse them (to determine where my tiles are).

The following call fails with E_INVALIDARG:

D3D10_MAPPED_TEXTURE2D mapped;
HRESULT hr = spTexture->Map( 0, D3D10_MAP_READ, 0, &mapped );

So I’m thinking that this fails because the texture can’t be read from by the CPU. So in order to get a texture that is CPU readable, I tried setting the D3DX10_IMAGE_LOAD_INFO structure to include D3D10_CPU_ACCESS_READ. This fails on D3DX10CreateShaderResourceViewFromFile() with E_INVALIDARG.

So I guess I’ve given up trying to load in a texture and be able to read its data from on the CPU.

So I thought, what about making a texture with the following D3D10_TEXTURE2D_DESC flags:

    D3D10_TEXTURE2D_DESC    desc;
    ZeroMemory(&desc, sizeof(D3D10_TEXTURE2D_DESC));
    desc.CPUAccessFlags     = D3D10_CPU_ACCESS_READ | D3D10_CPU_ACCESS_WRITE;
    desc.Usage              = D3D10_USAGE_DYNAMIC;
    desc.BindFlags          = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;

Step 2 of my plan is to use CopyResource or CopyResourceSubregion to copy data from the disk file into my dynamic texture, so I can use the dynamic texture as a source to read texel data from.

Will this approach work? Am I barking up the wrong tree and missing an obvious simpler route?

I’m worried that CopyResource won’t work either, since the original texture was loaded from disk without the D3D10_CPU_ACCESS_READ flag.

God, I’ve got to write this for OpenGL too, I hope it’s easier with GL!

My plan is to parse this texture and determine where my tiles are, so I can use the texture as an atlas for generating sprites.

One Response to “On Creating a D3DX10 texture atlas”

  1. In order to read texel information from a texture, we need a texture with Usage = D3D10_USAGE_STAGING.

    This allows the D3D10_CPU_ACCESS_READ flag to be set.

    I loaded this texture a second time (as a temporary, or “staging” resource, so that I could read the texel information).

    So to get read access to a texture, we must fill in this IMAGE_LOAD_INFO structure, which is passed to the D3DX10CreateTextureFromFile() call.

        CComPtr<ID3D10Device>       spD3D       = RenderMgr::I()->D3DDev();
        CComPtr<ID3D10Texture2D>    spTexture;      
        CComPtr<ID3D10Resource>     spResource;
    
    
        D3DX10_IMAGE_LOAD_INFO  loadInfo;
        loadInfo.Width          = D3DX10_FROM_FILE;
        loadInfo.Height         = D3DX10_FROM_FILE;
        loadInfo.Depth          = D3DX10_FROM_FILE;
        loadInfo.FirstMipLevel  = 0;
        loadInfo.MipLevels      = D3DX10_FROM_FILE;
        loadInfo.Usage          = D3D10_USAGE_STAGING;
        loadInfo.BindFlags      = 0;
        loadInfo.CpuAccessFlags = D3D10_CPU_ACCESS_WRITE | D3D10_CPU_ACCESS_READ;
        loadInfo.MiscFlags      = 0;
        loadInfo.Format         = m_pTexAtlas->Desc().Format;
        loadInfo.Filter         = D3DX10_FILTER_NONE;
        loadInfo.MipFilter      = D3DX10_FILTER_NONE;
        loadInfo.pSrcInfo       = 0;                
    
        hr = D3DX10CreateTextureFromFile(
            spD3D,
            wsTexture.c_str(),
            &loadInfo,
            NULL,
            &spResource,
            NULL
        );
    
        if( ! SUCCEEDED(hr) )
            return false;
    
        spResource->QueryInterface <ID3D10Texture2D> (&spTexture);
        if( ! spTexture )
        {
            spResource.Release();
            return false;
        }
    
        spTexture->GetDesc( &desc );
    
        ZeroMemory(&mapped, sizeof(D3D10_MAPPED_TEXTURE2D));
    
        hr = spTexture->Map( D3D10CalcSubresource(0, 0, 1), D3D10_MAP_READ, 0, &mapped );
        if( ! SUCCEEDED(hr) )
            return false;
    

    Okay, so now the Map() call succeeds. I hope it helps someone out there !!! I wasted an entire day on this. I wish there was better documentation for dealing with textures. I was working from Luna, which like many sources, only deals with textures as they can be mapped to models :S

Leave a Reply