1
\$\begingroup\$

I need to read back a GPU texture (stored in the GPU as D3D11_USAGE_DEFAULT). I am doing this via creating a staging ID3D11Texture. The whole application is running in a Microsoft Store application.

I am starting from a D3D11ShaderResourceView defined in the cResourceShaderView variable below. Code follows:

ID3D11Texture2D* pTextureInterface = 0;
ID3D11Resource* res;
HRESULT hr = 0;

ID3D11Texture2D* tx = 0;
    
ID3D11Device* dv;
deviceContext->GetDevice(&dv);

cResourceShaderView->GetResource(&res);
hr = res->QueryInterface<ID3D11Texture2D>(&pTextureInterface);
D3D11_TEXTURE2D_DESC desc;
pTextureInterface->GetDesc(&desc);

desc.Usage = D3D11_USAGE_STAGING;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
desc.BindFlags = 0;

hr = dv->CreateTexture2D(&desc, nullptr, &tx);
deviceContext->CopyResource(tx, pTextureInterface);

D3D11_MAPPED_SUBRESOURCE mapped = { 0 };
hr = deviceContext->Map(tx, 0, D3D11_MAP_READ, 0, &mapped);

The Map() function fails with an E_INVALIDARG.

I have turned on Debug Layers and there are no specific warnings nor errors in the Visual Studio Window for all these code above.

If I try to save this texture using the DirectX::SaveWICTextureToFile function which uses this same method in the sample, inside this function the Map() function call here fails as well with an E_INVALIDARG and without any debug warnings nor errors.

The GetDesc() function gives me a valid D3D11_TEXTURE2D_DESC for that texture.

enter image description here

What could be wrong?

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I don't see anything obviously wrong with what you're doing. There are some things that you may want to look for: 1. Is your context a deferred context? 2. Is the original texture a part of a swap chain? If either of these is true, then you can't do what you're doing. Also, does this happen on other computers as well? Does it work on the software rasterizer? \$\endgroup\$ Aug 4 at 13:19
  • \$\begingroup\$ Thanks Panda Pajama. The context D3D11_DEVICE_CONTEXT_DEFERRED. I switched to a D3D11_DEVICE_CONTEXT_IMMEDIATE and worked. Thanks so much. \$\endgroup\$
    – fred26
    Aug 4 at 14:21
  • \$\begingroup\$ Glad to know you resolved the issue. I posted an answer going into greater detail of why this is the case, and what your options are. \$\endgroup\$ Aug 4 at 14:30
3
\$\begingroup\$

You can't read back from a texture using Map() on a deferred context:

If you call Map on a deferred context, you can only pass D3D11_MAP_WRITE_DISCARD, D3D11_MAP_WRITE_NO_OVERWRITE, or both to the MapType parameter. Other D3D11_MAP-typed values are not supported for a deferred context.

As to why, you have to keep in mind how the copy and map operations work inside the system. When you call CopyResource(), you are not copying the resource. You are just telling Direct3D to record a command that issues a copy operation from one memory area to another. When you call Map(), Direct3D inserts a read fence in the command buffer.

Now, the difference between immediate and deferred contexts is as to when the commands are submitted to the GPU.

With an immediate (non-deferred) context, Direct3D submits commands as you record them, so the GPU is running in parallel with your CPU program, and it is possible for the CPU program to wait on a fence to read from some memory.

With a deferred context, as its name suggests, you are only recording the command buffer contents. They are later submitted if/when you call ExecuteCommandList, so waiting on the fence while the command buffer is merely being created doesn't make any sense. Therefore it's forbidden by the API.

So in general, you have two choices:

  • Do this in the immediate context instead. You can still use secondary deferred contexts, but this type of operations can only be done on the immediate context.
  • If you definitely must do this from another thread, use Direct3D 12 instead, where you have full control over command buffer creation, submission, you can put whatever fences you want wherever you want, at the cost of dramatically increased complexity. There's no immediate, nor deferred context. In fact, the concept of "context" no longer even exists.

Direct3D 11 deferred contexts are in my opinion, an inelegant hack. If you need simplicity, use Direct3D 11 immediate contexts. If you need full control, use Direct3D 12.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.