Is this not possible? (The shader from hell)

Started by
8 comments, last by EvilDecl81 19 years, 7 months ago
Using HLSL vs_1_1 is it not possible to compare the x component of a float4 vertex to any arbitrary number? Because this seems to be the case for me. And I can't find any documentation to verify this. For example, this expression will equate to false as expected. (1 > 2) // Works as it should, returns false However, if I compare any vertex data to an arbitrary number it'll *always* return true. (position.x > 1.0f) && (poisition.x < 1.0f) // returns true The above is impossible because a number will always be greater *or* less then that same number, never both at once. In my shader this will evaluate true, and I can't figure out why it would be doing this. If I set the value of position.x before I do the comepare then the statement works as it should. position.x = 5.0f; (position.x > 1.0f) && (poisition.x < 1.0f) // Works normally, returns false Applying transformations to the vertex in the shader as such: output.position = mul(mat,position); ..Work exactly as it should all the time. I just don't understand why I can't compare any of my vertex data with if statements?! I've setup the shader debugger and put watches on the variables and the data comes in as it should. This is driving me crazy and has pushed be a few notches closer to the brink of insanity as I've bashed my head on this for the last 4 days. If someone has any insight into this please let me know. Here's the shader: const uniform float4x4 mat; const uniform float index; struct Output { float4 position : POSITION; float2 uv : TEXCOORD0; }; Output main(float4 position : POSITION, float3 n : NORMAL0, float2 uv : TEXCOORD0) { Output output = (Output)0; if (position.x > 10.0f && position.x < 10.0f) { // Inside here *always* gets executed on each vertex, and it shouldn't because of the above line. Anyone know why? uv.x = cos(index*0.2f); uv.y = sin(index*0.2f); } output.position = mul(mat,position); output.uv = uv; return output; }
Advertisement
I don't have access to the fx compiler right now, but I suspect inspecting the assembly output would clarify the issue.
Quote:Original post by Anonymous Poster
I don't have access to the fx compiler right now, but I suspect inspecting the assembly output would clarify the issue.

That would be a good idea. The HLSL compiler for 1.1 seems to have a lot of quirks in it. Most people just write it in ASM, instead of having to deal with compiler issues (for 1.1 shaders).
Dustin Franklin ( circlesoft :: KBase :: Mystic GD :: ApolloNL )
In vs_1_1, both code paths are executed, and the compiler tries to make so that the result is what you expect through some trickery. For example, take a look at this shader:
const uniform float4x4 mat;const uniform float index;struct Output{	float4 position : POSITION;	float2 uv : TEXCOORD0;};Output main(float4 position : POSITION, float3 n : NORMAL0, float2 uv : TEXCOORD0){	Output output = (Output)0;	float someVal = 1.0f;		if (position.x > 10.0f && position.x <10.0f)	{		position.y = 5.0f;	}			output.position = position;	output.uv = uv;	return output;}


Compile this with Fxc and you get:
//// Generated by Microsoft (R) D3DX9 Shader Compiler 4.09.00.1126////   fxc /T vs_1_1 /Fc boo.txt test.txt//    vs_1_1    def c0, 10, 5, 0, 0    dcl_position v0    dcl_texcoord v1    slt r0.w, c0.x, v0.x    slt r1.w, v0.x, c0.x    mul r0.w, r0.w, r1.w    add r1.w, -v0.y, c0.y    mad oPos.y, r0.w, r1.w, v0.y    mov oPos.xzw, v0    mov oT0.xy, v1// approximately 7 instruction slots used


The key instruction here is the slt:
slt dst, src0, src1

Does this:
dest.x = (src0.x < src1.x) ? 1.0f : 0.0f;dest.y = (src0.y < src1.y) ? 1.0f : 0.0f;dest.z = (src0.z < src1.z) ? 1.0f : 0.0f;dest.w = (src0.w < src1.w) ? 1.0f : 0.0f;


So what the 2 slt's up there do is simple. Each one represents one of the conditional expressions in the 'if'. So the first slt will evaluate r0.w to 1 if position.x > 10.0f, and the second will evaluate r1.w to 1 if position.x < 10.0f

The conditional composed of the 2 sub-conditionals "anded" together is true only if both are true (1), and thus the 'w's are multiplied together into r0.w

Now the problem is both paths will be executed. i.e. position.y will be set to 5.0f, no matter what you do (for vs1.1). So the compiler works around it like this. It stores (5 - y) in r1.w

Next, it does:
mad oPos.y, r0.w, r1.w, v0.y

which is:
position.y = conditional_result * (5 - y ) + y


If the conditional evaluated to true, you'd get:
position.y = 5 - y + y = 5


Else, you'd get:
position.y = y


If you try more complex examples, you'll find that the compiler does similar trickery. In general, it'll try to find a way to execute both paths, but make the result what you expect. Sometimes, it might fail.

Quote:Original post by circlesoft
Quote:Original post by Anonymous Poster
I don't have access to the fx compiler right now, but I suspect inspecting the assembly output would clarify the issue.

That would be a good idea. The HLSL compiler for 1.1 seems to have a lot of quirks in it. Most people just write it in ASM, instead of having to deal with compiler issues (for 1.1 shaders).

The problems people face with the 1.1 model isn't much due to the compilter, as much as it is attributed to the limitations of the model itself. One has to understand the model well in order to be able to - sometimes - walk the compiler by hand.

Awesome, makes total sence to me now. Coder, thanks a mil for that rundown, it helped out wonders. And thanks everyone too =)
Quote:Original post by Coder
The problems people face with the 1.1 model isn't much due to the compilter, as much as it is attributed to the limitations of the model itself. One has to understand the model well in order to be able to - sometimes - walk the compiler by hand.
It seems that most people who are experienced with 1.1 (and actually use it) know ASM anyways, so it's not that big of a deal.
Dustin Franklin ( circlesoft :: KBase :: Mystic GD :: ApolloNL )
For 1.1, I use macros and compile multiple versions of the shaders, and then select the appropriate one at at run time. Works really well.
Quote:
If you try more complex examples, you'll find that the compiler does similar trickery. In general, it'll try to find a way to execute both paths, but make the result what you expect. Sometimes, it might fail.


For targets without branching, (most of them), the compiler emits both sides of the branch and patches the output based on the conditional.

The only case where this DOESN'T sometimes work is for NAN Propogation - for instance:


if(x > 0)
{
y = y/x;
}
else
y = 0;

then the code would look something like:

rcp r0.w, v0.x
mul r1.w, r0.w, v1.x
slt r0.w, c0.x, v0.x
mul r0.w, r1.w, r0.w

However, if x == 0, y will == NAN no matter what you do - algebraic fixups don't work. Since there are no conditional movs or dynamic branches for most of vs targets, there isn't really a way to avoid this.
Quote:Original post by Anonymous Poster
Quote:
If you try more complex examples, you'll find that the compiler does similar trickery. In general, it'll try to find a way to execute both paths, but make the result what you expect. Sometimes, it might fail.


For targets without branching, (most of them), the compiler emits both sides of the branch and patches the output based on the conditional.

The only case where this DOESN'T sometimes work is for NAN Propogation - for instance:


if(x > 0)
{
y = y/x;
}
else
y = 0;

then the code would look something like:

rcp r0.w, v0.x
mul r1.w, r0.w, v1.x
slt r0.w, c0.x, v0.x
mul r0.w, r1.w, r0.w

However, if x == 0, y will == NAN no matter what you do - algebraic fixups don't work. Since there are no conditional movs or dynamic branches for most of vs targets, there isn't really a way to avoid this.


Didn't mean to be anonymous...
EvilDecl81

This topic is closed to new replies.

Advertisement