Use Texture2DArray in D3D12

Started by
9 comments, last by Range 4 years, 6 months ago

Good day. I am find for information on how to create texture arrays in d3d12. In d3d11, I calmly figured out how to create a texture array from several textures:

  • I upload several dds - textures of the same resolution
  • in the engine I create ID3D11Texture2D* and map date into it all loaded textures
  • in shader I use texture2darray in pixel shader:

Texture2DArray textures : register(t0);

Now I am looking for something like that for d3d12. For load the textures, I use DDSTextureLoader.h/.cpp from microsoft. So I'm loading a few textures. How can I create a texture array from them and pass it to a pixel shader? I will be glad to some example. Thank.

PS: I searched the forum, but I didn’t find anything.

Advertisement

If you were familiar with DDSTextureLoader for DirectX 11, then you could take a look at the same tool in DirectXTK12.

47 minutes ago, zhangdoa said:

If you were familiar with DDSTextureLoader for DirectX 11, then you could take a look at the same tool in DirectXTK12.

Sorry, I meant creating a texture array in the code:)

Well, this did not really change between D3D10, D3D11 and D3D12.

You create your ID3D12Resource and upload the individual array slices using ID3D12Resource::Map+WriteToSubresource+Unmap. I hope.

The HLSL shaders are exactly the same.

 

19 minutes ago, pcmaster said:

Well, this did not really change between D3D10, D3D11 and D3D12.

You create your ID3D12Resource and upload the individual array slices using ID3D12Resource::Map+WriteToSubresource+Unmap. I hope.

The HLSL shaders are exactly the same.

 

Thanks, I will try it. But if you can show me an example it will be great, because I don't know so good d3d12

Sorry for my misinterpreting answer?.

 

In D3D12 creating a texture has more steps than D3D11, you basically need:

1. Reserve a heap memory for the resource. It could be your main memory or your dedicated video card memory, depends on the target platform memory architecture (UDMA?/DMA?) and the creation info you specified; Different heap type has different CPU/GPU accessibility;

2. Create a resource handle. You will get an ID3D12Resource* as similar as ID3D11Texture2D* for further binding or other operations;

3. Upload the texture data to the reserved heap;

4. Transit the resource barrier of your texture resource handle to the final usage stage;

5. Create the SRV or UAV by your usage case.

 

You have 2 or more choices to implement the 1st and 2nd steps:

A. Using ID3D12Device::CreateHeap for the 1st step, and using ID3D12Device::CreatePlacedResource for the 2nd step;

B. Using ID3D12Device::CreateCommittedResource for a combined result of 1st and 2nd steps.

 

When implementing the 3rd step:

 As @pcmaster mentioned, you could map-write-unmap, but your resources must stay in a heap that CPU is writable (the D3D12_CPU_PAGE_PROPERTY is not D3D12_CPU_PAGE_PROPERTY_NOT_AVAILABLE), so it should be an Upload heap or a Readback heap.

Then the better solution is, create an Upload heap, upload your resource to it and then issue an ID3D12GraphicsCommandList::CopyResource command to copy it to the Default heap in order to get the best GPU accessibility. You would need another temporary ID3D12Resource* for the resource inside the upload heap, it would be created by the same processes in the 1st and 2nd steps.

You have 2 or more choices to create and upload the texture data to the Upload heap resource:

A. Create the Upload heap and the Upload heap resource handle by your own, and then map-write-unmap;

B. Create the Upload heap by your own, then use UpdateSubresources method provided by d3dx12.h to upload.

 

The 4th step is easy: ID3D12GraphicsCommandList::ResourceBarrier.

 

The 5th step requires you to create SRV or UAV in a Descriptor Heap, this is another topic but generally speaking, if you could survive from the texture creation process above, then that won't be a problem.

 

Also, all command execution need you to take explicit care about synchronization.

 

I suggest you take a look at the DirectX 12 Graphics samples, there should be some real code examples.

If anyone found any mistakes please point out, thanks! (MESSY D3D12?)

3 hours ago, pcmaster said:

You create your ID3D12Resource and upload the individual array slices using ID3D12Resource::Map+WriteToSubresource+Unmap. I hope.

While you can do this, I'd not recommend it in general practice. This can be a great optimization for UMA (unified memory architecture, i.e. integrated) GPUs, but for discrete GPUs, this requires putting your resource in L0 memory, i.e. RAM and not VRAM, which will probably have catastrophic results on performance. As zhangdoa mentioned, the general-purpose approach is to create your resources with HEAP_TYPE_DEFAULT and use HEAP_TYPE_UPLOAD buffers to get data into them. This is much better for discrete architectures, without really harming integrated.

Thank you all for your help!

You actually don't need to do step 4 (issue a transition barrier) if you're uploading using a COPY queue, since the resource will implicitly decay to COMMON and get promoted to a readable state on first access by a DIRECT queue. This is getting into more advanced territory (I would get things working the most basic way possible first), but using a COPY queue is the recommended way to upload resources on discrete GPUs. This is because the DMA units are optimized for pulling data over PCI-e, and also because the transfer can overlap with graphics work that's happening simultaneously on the GPU. For integrated parts you instead want to exploit unified memory and skip the upload step entirely.

I have some code here that you can look at if you ever want a full example of loading and uploading a texture. It basically does the following:

  1. Uses DirectXTex to load and process the texture data
  2. Creates a committed texture resource in a DEFAULT heap
  3. Creates an SRV
  4. Grabs some temporary memory from a large ring buffer located in an UPLOAD heap
  5. Grabs a temporary COPY command list
  6. Copies the texture data into the temporary UPLOAD buffer
  7. Calls CopyTextureRegion for each subresource on the COPY command list
  8. Kicks off the COPY command list to execute on a COPY queue, and has it signal a fence when completed

Then you just need make sure that any queues using your newly-uploaded resource waits for the fence to be signaled before executing, that way you can be sure that the copies are finished before trying to read from the data. What I have is not a perfect implementation by any means, in particular you generally want to batch up a bunch of copies into single call to ExecuteCommandList so that you don't have lots of tiny submissions and signals to fences. But perhaps you can use it as a reference at some point once you're further along with everything.

Thanks MJP :)

This topic is closed to new replies.

Advertisement