Jump to content
  • Advertisement
Sign in to follow this  
SaraJ

How to retrieve pass or fail info from HLSL shader

This topic is 3518 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, I hope this is the right place for posting a question on HLSL (using DirectX/Direct3D and C#). I am doing some image processing using HLSL and at one point I do "test" in a pixel shader and want to return information to my C# code if the test passed or not. The test being to subtract one texture from another, and passing the test means that all pixels in texture 2 is larger or equal to corresponding pixels in texture1. That is, if texture1-texture2>0, the test failed. Reading texture values from the textures and subtracting them is no problem, but I want to know if anyone knows of an efficient way of returning if the test passed or not to the c#-code. Of course I can return the subtracted value for each pixel (rendering to a texture), and then read all texture values in the c# code using SurfaceLoader.FromSurface to copy the rendersurface and then locking it. However, the texture copying is far to slow for this being an realistic option (the test might be run hundreds of times). So, does anyone know another way of doing it? The optimum thing would be to send some sort of "break" message to the c# code as soon as the subtracted value is above zero, telling the test has failed, but I don't know how to do this. I know there is a discard option in hlsl, but I don't know of any way of sending info to the c# code that the shader has been discarded. This is what my shader do so far (I use shader version 3.0):
PixelToFrame FirstPixelShader(VertexToPixel PSIn) 
{ 
    PixelToFrame Output = (PixelToFrame)0; 
 
    float tex1 = tex2D(TextureSampler1, PSIn.TexCoords).r; 
    float tex2 = tex2D(TextureSampler2, PSIn.TexCoords).r; 
        
    if((tex1-tex2)>0) 
    { 
        Output.Color.r=0; 
    } 
    else 
    { 
        Output.Color.r=1; 
    } 
    return Output; 
} 


Any help is very appreciated, and from experience I know people here are usually very helpful. Thanks!

Share this post


Link to post
Share on other sites
Advertisement
I don't see the reason why you should do this test in pixel shader and then read values in render target. If both textures have the same dimensions, you might just lock their surfaces and do this test in one loop directly in C# code.
But if you want to use pixel shader, I would create some global variable in effect file and set it to true before testing and if tex1 < tex2 then set it to false. And then you would just read it back in your C# code, using for ex. GetParameterByName and GetBool (if it's that, I do D3D in C++, but those function should be there too).

Share this post


Link to post
Share on other sites
I have an extremely limited knowledge of HLSL, so this might be a long shot.

Could you not declare a global in your shader, and then set it where you do your test;


uniform extern bool g_Test;
//...
if((tex1-tex2)>0)
{
Output.Color.r=0;
g_Test = true;
}
else
{
Output.Color.r=1;
g_Test = false;
}

Then in code obtain a handle to the global, and use that to test what it is and act accordingly?

LPD3DXEFFECT fx;
D3DXHANDLE handle = fx->GetParameterByName(0, "g_Test");
bool test;
if( fx->GetBool( handle, &test ) )
{
//Do Something
}


Its a wild stab in the dark but it might help.

Share this post


Link to post
Share on other sites
Quote:
Original post by 0rcus
I don't see the reason why you should do this test in pixel shader and then read values in render target. If both textures have the same dimensions, you might just lock their surfaces and do this test in one loop directly in C# code.
But if you want to use pixel shader, I would create some global variable in effect file and set it to true before testing and if tex1 < tex2 then set it to false. And then you would just read it back in your C# code, using for ex. GetParameterByName and GetBool (if it's that, I do D3D in C++, but those function should be there too).


Thanks for the tip! There is a GetParameter function in c#, so I will look into that. I will test doing it on the CPU as well, but the textures to be subtracted are render target textures (hence, need to be copied to another texture prior to reading/subtacting values) and quite large, so this will probably be much to slow.

Share this post


Link to post
Share on other sites
GetParameter() isn't going to work - it will only return the value you set yourself via SetParameter().

Here's some options that should work:

1. As mentioned before do the check on the CPU side. Unless you're comparing lots of huge textures it should be quicker and it's much easier to do.

2. After subtracting the textures, downsize the result by summing the pixel values. Once you get to a 1x1 texture, lock that on the CPU. If that pixel is black then they were equal.

3. Use a hardware occlusion query. To use that you'll need the pixel shader to output a different z value depending on the result of your comparison.

Share this post


Link to post
Share on other sites
Quote:
Original post by Adam_42
GetParameter() isn't going to work - it will only return the value you set yourself via SetParameter().


Thanks for that info, I was just trying to use that one but could (obviously) not get it to work properly. You saved me a lot of time :)

Quote:
Original post by Adam_42
1. As mentioned before do the check on the CPU side. Unless you're comparing lots of huge textures it should be quicker and it's much easier to do.


This is of course the most straight forward method. However, as I mentioned previousley, both textures are render targets (and have to be that) and as far as I have understood you can't read any values from a render target texture (at least not using DirectX+C#). So the only way I can see to compare the two textures on the CPU would be to copy them to new (non render target) surfaces, lock them and then do the comparison. I would need to do this about some hundred or actually probably some thousand times, and the copying is very slow... Do you know of any other way it would be possible to read the texture values?

Thanks!

Share this post


Link to post
Share on other sites
Quote:
Original post by Adam_42
GetParameter() isn't going to work - it will only return the value you set yourself via SetParameter().


I am not sure how about C#, but GetParameterXXX functions in C++ return handle of effect parameter which is used to set/get value of that parameter. And if you change this value in shader, it will persist changed, so you can set it in shader and then read it in C#. For ex. in C++ I would do:

D3DXHANDLE hPassed = pEffect->GetParameterByName(NULL, TEXT("bPassed"));
pEffect->SetBool(hPassed, TRUE);
//execute shader here
BOOL bPassed;
pEffect->GetBool(hPassed, &bPassed);
if (bPassed)
{
//do something if test passed
}

Assuming bPassed is global boolean variable declared in effect (extern and uniform storage classes are default for global variables)

Share this post


Link to post
Share on other sites
Quote:
Original post by 0rcus
I am not sure how about C#, but GetParameterXXX functions in C++ return handle of effect parameter which is used to set/get value of that parameter. And if you change this value in shader, it will persist changed

No, it won't. These are shader constants. The shader can't change them. Suppose it could, which of the shader runs (the shader would run for each pixel) would store the value there? See, it doesn't make sense.

Adam_42 suggested two very reasonable options (2 and 3) for doing this on the GPU without reading everything back.

Share this post


Link to post
Share on other sites
Quote:
Original post by Adam_42
2. After subtracting the textures, downsize the result by summing the pixel values. Once you get to a 1x1 texture, lock that on the CPU. If that pixel is black then they were equal.

3. Use a hardware occlusion query. To use that you'll need the pixel shader to output a different z value depending on the result of your comparison.


Seems like I have to look into the above suggestions. Did some tests on doing the comparison on the CPU (copying rendertarget textures), and it was way to slow. It is to be used in an interactive tool, and the comparison step took several minutes, which is not an acceptable time.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!