DXGI_USAGE_UNORDERED_ACCESS and RWTexture2D in pixel shader?

Started by
10 comments, last by MJP 9 years, 5 months ago

Hello

I'm trying to do some dynamic blending in a pixel shader. This is on DX 11. I created the swap chain with DXGI_USAGE_UNORDERED_ACCESS and DXGI_FORMAT_R8G8B8A8_UNORM format. The idea is to have a pixel shader with no declared output but instead read read/write to a RWTexture2D inside the shader.

However I'm having a bit of a problem to declare the RWTexture2D. What type should it be, uint doesn't work? I get the error message:

The resource return type for component 0 declared in the shader code (UINT) is not compatible with the resource type bound to Unordered Access View slot 0 of the Pixel Shader unit (UNORM). This mismatch is invalid if the shader actually uses the view (e.g. it is not skipped due to shader code branching). [ EXECUTION ERROR #2097372: DEVICE_UNORDEREDACCESSVIEW_RETURN_TYPE_MISMATCH]

And if I try to create the UAV with a different format I get errors like this:

The Format (0x1e, R8G8B8A8_UINT) is invalid, when creating a View; the Resource was already created with a fully qualified Format, which is not castable (0x1c, R8G8B8A8_UNORM). [ STATE_CREATION ERROR #2097343: CREATEUNORDEREDACCESSVIEW_INVALIDFORMAT]

So how can I resolve this? Is there a correct way to declare the texture in the shader or should handle the setup on the CPU in a different way?

Thanks!

Advertisement

If you have a format with the "UNORM" suffix, this basically means "the raw data is stored as unsigned integers, but the hardware will interpret it as a [0.0, 1.0] floating point value". So if you have a Texture2D or RWTexture2D, you should use the "float4" type with UNORM formats.

As an example of how it works, let's say a single texel of your texture has the integer values (255, 127, 63, 0). To perform the UNORM conversion, the hardware will divide all of those by 255.0. This produces a float4 with the values (1.0, 0.5, 0.25, 0.0). SNORM works in a very similar way, except that it interprets that data as a signed integer and converts to floating point values of the range [-1.0, 1.0].

If you're interested, the relevent documentation on how the format conversions work can be found here.

If you have a format with the "UNORM" suffix, this basically means "the raw data is stored as unsigned integers, but the hardware will interpret it as a [0.0, 1.0] floating point value". So if you have a Texture2D or RWTexture2D, you should use the "float4" type with UNORM formats.

As an example of how it works, let's say a single texel of your texture has the integer values (255, 127, 63, 0). To perform the UNORM conversion, the hardware will divide all of those by 255.0. This produces a float4 with the values (1.0, 0.5, 0.25, 0.0). SNORM works in a very similar way, except that it interprets that data as a signed integer and converts to floating point values of the range [-1.0, 1.0].

If you're interested, the relevent documentation on how the format conversions work can be found here.

Thanks! That worked fine but I now have a different issue.

First I want to read the back buffer do some computations on the data and then write back to the RWTexture resource. So I declared the texture as float4 to be able to write to it but then when I try to read from it, it fails to compile with the following error:

error X3676: typed UAV loads are only allowed for single-component 32-bit element types

How can I get around this? Is there a way to declare it so it's both valid for read and write in the pixel shader?

Yeah, that's a really annoying limitation of RWTexture2D: they can only read from R32_FLOAT, R32_UINT, and R32_INT formats. It comes from early D3D11 hardware that had bad support for UAV's.

For the case of an R8B8G8A8_UNORM texture, you can work around this by aliasing the texture as R32_UINT and then manually performing the UNORM conversion in the shader. There's documentation on how to do this here.

Yeah, that's a really annoying limitation of RWTexture2D: they can only read from R32_FLOAT, R32_UINT, and R32_INT formats. It comes from early D3D11 hardware that had bad support for UAV's.

For the case of an R8B8G8A8_UNORM texture, you can work around this by aliasing the texture as R32_UINT and then manually performing the UNORM conversion in the shader. There's documentation on how to do this here.

I was thinking about that but how do I alias the texture? The swap chain is created as a typed format by default.

Thanks!

You can (sometimes) alias if you create the resource/texture with the _TYPELESS suffix. The views (SRV, RTV, UAV) then explicitly define a type. You probably have to experiment a bit to find out what combination works. E.g. I don't know if you can alias R32 and RGBA8. I also wonder if the swap-chain texture can be typeless.

If this doesn't work the alternative is rendering to an off-screen texture and later draw it with a full-screen quad reading from said texture.

 

unbird, on 28 Oct 2014 - 10:48 AM, said:

You can (sometimes) alias if you create the resource/texture with the _TYPELESS suffix. The views (SRV, RTV, UAV) then explicitly define a type. You probably have to experiment a bit to find out what combination works. E.g. I don't know if you can alias R32 and RGBA8. I also wonder if the swap-chain texture can be typeless.

If this doesn't work the alternative is rendering to an off-screen texture and later draw it with a full-screen quad reading from said texture.

According to this document it's not possible to use a typeless swap chain, http://msdn.microsoft.com/en-us/library/windows/desktop/bb173064(v=vs.85).aspx.

Would it be possible to manage the resource as RWByteAddressBuffer in the shader somehow without copying into a whole new resource?

I believe that for UAV's that allow for a special case, where you can create the UAV with DXGI_FORMAT_R32_UINT even though that does not match the original format that the texture was created with.

I believe that for UAV's that allow for a special case, where you can create the UAV with DXGI_FORMAT_R32_UINT even though that does not match the original format that the texture was created with.

I tried that but I'll get:

D3D11 ERROR: ID3D11Device::CreateUnorderedAccessView: The Format (0x2a, R32_UINT) is invalid, when creating a View; the Resource was already created with a fully qualified Format, which is not castable (0x1c, R8G8B8A8_UNORM). [ STATE_CREATION ERROR #2097343: CREATEUNORDEREDACCESSVIEW_INVALIDFORMAT]

Would it be possible to manage the resource as RWByteAddressBuffer in the shader somehow without copying into a whole new resource?


To my knowledge you can't alias a texture as a buffer (or vice versa). If so, you'd need the D3D11_RESOURCE_MISC_BUFFER_ALLOW_RAW_VIEWS flag. I doubt you can do this with textures, and the swap chain description doesn't have such a flag anyway. Looks you need to go for the indirect approach.sad.png

Hmmmm, now I remember: Humus has an example of GPU texture compression where he copies from a R32G32_UInt to BC4 with CopyResource. Maybe you can do something similar. Still indirect, but doesn't involve another draw call.

This topic is closed to new replies.

Advertisement