HLSL reading outside array safe?

Started by
6 comments, last by SoldierOfLight 8 years ago

I have an ID3D10EffectVectorVariable in HLSL code it looks like this:


int4 array;

float4 VS()
{
   uint x = 0;
   //"x" is loaded from texture can be a value bigger than 3
   int grab = array[x];
   if(x > 3)
   {
      grab = 0;
   }

   //blablabla
}

is this code safe?

Advertisement
It's not going to crash, no. If you look at the assembly you can see what will happen -- in vs_4_0 it will do a bunch of masking nonsense and the final result will be sort of garbage with an out of bounds index.

It's not going to crash, no. If you look at the assembly you can see what will happen -- in vs_4_0 it will do a bunch of masking nonsense and the final result will be sort of garbage with an out of bounds index.

I have played around with DX12 with shader model 5.1, and some of my original working program (I ported them to DX12 with sm5.1) will cause GPU crashing. And later I found out it's actually caused by reading out of boundary of UAVs. So I am not sure in DX12 with sm5.1, whether the out of bound behavior will cause crash or not. Or it depends on your resource or UAV configuration.

Also I haven't tried this with just arrays.

But it will be great if somebody could verify that...

To be clear the example posted is a float4, not a buffer. So, the indexing is into the xyzw components in this case.

To be clear the example posted is a float4, not a buffer. So, the indexing is into the xyzw components in this case.

Sorry, I should start another thread about my concern: so basically I found in DX11 and before, out of bound access to UAV won't cause any trouble, it just return 0. But in DX12, my case, it will crash GPU. So any idea what changed in DX12 in term of UAV access?

Thanks

It's quite difficult to get that "just return 0" behavior that D3D11 guaranteed. I'm still not understanding the scenario. Are you reading beyond the end of the buffer (e.g. buffer is 100 bytes, you're reading byte 101), or are you reading beyond the end of the bound array (e.g. array declared to have N elements, but only M are bound, you're reading N+1 or M+1)?

In the first case, that should work as long as you're using descriptors from a table. Root descriptors don't contain size information, so out-of-bounds access can cause a page fault/crash.

In the second case, if you're reading M+1, you're reading from the descriptor that's M+1 past the start of the table. That descriptor needs to have valid contents in order to get defined behavior. If you're reading it as a UAV, there needs to be a UAV descriptor there. It actually needs to be even more specific depending on hardware - if you're reading a buffer UAV, there needs to be a buffer UAV descriptor there. It doesn't need to have a valid resource pointer, you can initialize it with a null resource to get 0s on read, but if the type doesn't match, you can cause a hang.

It's quite difficult to get that "just return 0" behavior that D3D11 guaranteed. I'm still not understanding the scenario. Are you reading beyond the end of the buffer (e.g. buffer is 100 bytes, you're reading byte 101), or are you reading beyond the end of the bound array (e.g. array declared to have N elements, but only M are bound, you're reading N+1 or M+1)?

In the first case, that should work as long as you're using descriptors from a table. Root descriptors don't contain size information, so out-of-bounds access can cause a page fault/crash.

In the second case, if you're reading M+1, you're reading from the descriptor that's M+1 past the start of the table. That descriptor needs to have valid contents in order to get defined behavior. If you're reading it as a UAV, there needs to be a UAV descriptor there. It actually needs to be even more specific depending on hardware - if you're reading a buffer UAV, there needs to be a buffer UAV descriptor there. It doesn't need to have a valid resource pointer, you can initialize it with a null resource to get 0s on read, but if the type doesn't match, you can cause a hang.

Thanks Jesse, that's very helpful.

In my case, I have a volume buffer for volume rendering, and have shader access SRV with indices. in some cases, when calculating the indices, rounding error will bring it actually out of bound (width*height*depth), D3D11 have no problem with it. But after I port it to dx12 (yes, I bound the volume buffer srv as root srv) the out of bound access on that buffer will cause crash....

But does that means in D3D11, the driver will do a boundary check when you read resource? So basically, how d3d11 can guarantee return 0 when access out of boundary (just curious)

It's frequently a function that the hardware does automatically, though sometimes it's implemented in the shader by the driver. Descriptor table bindings require that same out-of-bounds check that D3D11 had. As long as your descriptor has correct bounds, then out-of-bounds reads should return 0.

This topic is closed to new replies.

Advertisement