How to load a ScratchImage from an array of DDS images in memory?

Jul 16, 2012 at 4:14 AM

Hello,

The high level API appears to cleanly support the concept of a 2D texture array, but the DDS loader does not seem to offer the same ease of use.

I'd prefer to not locally modify the API or write wrapper code to do messy pointer passing from the single image loaders, so I'm hoping that I'm either missing an existing API feature, or that this feature could be added in an upcoming release.

ScratchImage::InitializeArrayFromImages could take an array of Image pointers and construct a single ScratchImage that could create a 2D texture array resource. However, LoadFromDDSMemory appears to be the only way I can see of populating a ScratchImage easily, and it only loads in a single image.

Am I missing something, or how could this feature be cleanly implemented in the existing code?

 

Thanks!

Graham

Jul 17, 2012 at 5:22 AM

Currently I have texture array creation working by creating an array of textures (loaded through DirectXTex) and then calling UpdateSubresource to bring over all the mip slices. This approach works, but just wondering if there's a better way. I did have to expose the concept of a "staging" texture in the API, so that I can properly set the usage\bind flags (would be good to expose that in my opinion).

 

Cheers,

Graham

Aug 25, 2012 at 8:29 AM

I agree that exposing some of the params for CreateTexture2D would be nice.  As it stands now, I used the library for loading the images, but am creating the DX Texture resources in my own code due to these not being exposed.  Not that is is terribly difficult to create them, but if the goal is provide a general purpose library for creating DX Textures then it isn't quite meeting it in this area.

I do like the library much better than D3DX though.  The intermediate step with ScratchImage is great.

For loading an array you can avoid using an UpdateSubresource if you want.  I did something similar to what CreateTexture is doing to create my texture array.  I used what they did as an example but based it off an array of ScratchImages.

1) Allocate an array of ScratchImage for however many images you want in the texture array.

2) Use LoadFromDDSFile/LoadFromWICFile to load all the images.

3) Do whatever validation you need on your images since they have to all be the same for the array.

4) Now you can allocate an array of D3D11_SUBRESOURCE_DATA for all the images * mips (just like CreateTexture does) and iterate through them all to fill in the structures.

unique_ptr<D3D11_SUBRESOURCE_DATA[]> initData(new D3D11_SUBRESOURCE_DATA[imageCount * firstMeta.mipLevels]);
		
size_t mipCount;
const Image* mips;
int index = 0;
for (int i=0;i<imageCount;++i)
{
	mipCount = imageList[i].GetImageCount();
	mips = imageList[i].GetImages();

	for (size_t mipIndex=0;mipIndex<mipCount;++mipIndex)
	{
		initData[index].pSysMem = mips[mipIndex].pixels;
		initData[index].SysMemPitch = static_cast<DWORD>(mips[mipIndex].rowPitch);
		initData[index].SysMemSlicePitch = static_cast<DWORD>(mips[mipIndex].slicePitch);

		++index;
	}
}

5) Now you can call CreateTexture2D just like the library does without having to use UpdateSubresource.  

Whether this is actually a better way (your original question) I dunno.  Less API calls, but I suppose if you have a lot of large images this might not be the best approach due to having to load them all at the same time.  In that case your UpdateSubresource is probably better since you can do it in smaller chunks.  For my case, I have a lot of small images and loading them all up at once before creating the array is not a big deal.

I agree it would be nice to see a way to create a texture arrays directly in the library.  To make use of that though, I would need the D3D11_TEXTURE2D_DESC data accessible in some fashion.

Coordinator
Aug 25, 2012 at 7:20 PM
Edited Aug 25, 2012 at 7:33 PM

The general model for DirectXTex is to use ScratchImage as a workspace object which understands the various layout requirements for the memory. It holds it's memory in exactly the layout that is required by .DDS files. Remember that Image is a structure which describes the surface but it doesn't actually own the memory. ScratchImage's purpose is to own the memory for the various outputs so they can be cleaned up, but in fact the DirectXTex library doesn't really care who owns the memory. It only operates in terms of taking data pointed to by an Image or multiple Image instances and then creating it's results in a ScratchImage.

So the problem you are having is you want to create a texture array from a series of images. This can be fairly easily accomplished in the existing library.

  1. Load each of your individual images for the array using LoadXXXFromFile to create a number of ScratchImage instances (i.e. ScratchImage src[6]; )
  2. Use Resize and Convert to make sure that all the images meet the requirements for the array: all the same size, all the same format. This will result in some new ScratchImage instances ( i.e. ScratchImage tmp[6];  }
  3. Create a temporary array of Image structures the size of your target array texture ( i.e. Image itemp[6]; )
  4. Copy the Image information from the source ScratchImage into your temporary Image array ( for(i=0; i < 6; ++i) { itemp[i] = tmp[i].GetImages()[0]; )
  5. Call ScratchImage::InitializeArrayFromImages using itemp as the input (be sure to keep the tmp array in scope until after this step since that is where the data is still residing)

The resulting ScratchImage is an array texture containing the six source images. At this point you can use GenerateMipMaps and /or Compress on the array and then SaveXXXToFile.

Note that if you changed step 5 to a call to ScratchImage::InitializeCubeFromImages you'd have made a cube map where each image is a face (it requires a multiply of six faces). If you change step 5 to a call to ScratchImage::Initialize3DFromImages you'd have made a volume map where each image is a slice of depth.

For more complicated image compositing you can make use of CopyRectangle to move the data from one Image (or ScratchImage) into a particular location in a new ScratchImage. For example, you could:

  1. Load source images using LoadXXXFromFile to get ScratchImage instances
  2. Use Convert to make sure they are all the same format
  3. Create a ScratchImage to meet your final requirements using ScratchImage::Initialize2D, ScratchImage::Initialize3D, or ScratchImage::InitializeCube.
  4. Use CopyRectangle using source Image references obtained from ScratchImage::GetImage() on your source images and destination Image references from the ScratchImage you created in Step 3.

The resulting ScratchImage is an composite texture containing the various images placed as you wanted. At this point you can use GenerateMipMaps[3D] and /or Compress on the array and then SaveXXXToFile.

As a reminder, the Direct3D 11 interaction parts of the DirectXTex library are really there for viewers and level editors and the like. The general assumption of DirectXTex is that for efficent runtime loading in an application, you would do all your processing with DirectXTex offline and then create the final format, size, and type of textures as .DDS files. You would then use DDSTextureLoader (in the DirectXTex package or in DirectXTK) to actually create the resources with minimal cost in your performance application. In other words you *can* use DirectXTex in a game, but probably it should be used mostly for tools instead. The ease and flexibility of DirectXTex come at the expense of a fair amount of copying and many individual steps, but the end result is usually something that can be directly serialized to disk as a .DDS file and then loaded later in one step as a complete resource without multiple memory copies.

Aug 25, 2012 at 9:18 PM

Thanks for the info.  I didn't realize you could save it out as an array like that.  I admit I have a pretty shallow understanding of image format saving in general; which is why I'm using your library.

For the array in this case I allow users to add content so I will have to stick with this method, or something similar, for generating it.  However, you have given me some new ideas on some of my other resources.

Great library by the way.  Easy to use and the code is easy to read.  

Oct 17, 2012 at 2:27 PM
Edited Oct 17, 2012 at 3:01 PM

i am trying to create texture array and i got crash in last line of _CopyScanline: 

size_t size = std::min<size_t>( outSize, inSize );
memcpy_s( pDestination, outSize, pSource, size );)

can you tell me what's wrong with my code, am i missing something?

 

    HRESULT hr;

    std::vector<std::wstring> paths;
    paths.push_back(L"c:\\box0.dds");
    paths.push_back(L"c:\\box1.dds");
    paths.push_back(L"c:\\box2.dds");
    paths.push_back(L"c:\\box3.dds");
    paths.push_back(L"c:\\box4.dds");
    paths.push_back(L"c:\\box5.dds");

    ScratchImage** originalImages = new ScratchImage*[6];

    for(int i = 0; i < 6; i++)
    {
        TexMetadata metaData;

        originalImages[i] = new ScratchImage();
        hr = LoadFromDDSFile(paths[i].c_str(), DDS_FLAGS_NONE, &metaData, *originalImages[i]);
        if(FAILED(hr)) assert(false);

        metaData.miscFlags &= ~TEX_MISC_TEXTURECUBE;
    }

    ///// TODO: check if textures have same size and format and if not convert them

    Image tmpImages[6];
    for(int i = 0; i < 6; ++i)
    {
        Image img = originalImages[i]->GetImages()[0];
        tmpImages[i] = img;
    }
           
    ScratchImage outputImage;

    hr = outputImage.InitializeArrayFromImages(tmpImages, 6);
    if(FAILED(hr)) assert(false);

Coordinator
Oct 18, 2012 at 7:55 PM

What is the ''crash' exactly?

Oct 19, 2012 at 9:45 AM

access violation, anyway the solution was to use a8r8g8b8 dds not dxt 3, now everything is fine, i suppose i could decompress it also

Coordinator
Oct 22, 2012 at 2:57 AM

Hmm. What's the size of those image exactly? What compiler are you using?