Sign in to follow this  
RobMaddison

Vertex Shader [SOLVED]

Recommended Posts

I thought I'd start a new post as my previous one got a bit confusing. I've hunted high and low on the net and still can't find a way to do what I'm trying to do. Here's what I'm using and trying to do: DirectX 9, vs_3_0, c++ I have a vertex buffer full of single floats (y values) and one full of single float pairs (x and z values). The second buffer, having pairs of floats is twice as big as the first. I'd like to combine these two buffers as input into the vertex shader in order to create the position vector. This is essentially to save memory for my terrain which has an overall size of 4096x4096 with about 5 levels of detail. Could someone please show me how the vertex declaration should look for this setup? I've tried loads of different configurations and DirectX complains at most of them. If I use D3DDECLUSAGE_POSITION, it complains that the D3DDECLTYPE_ should be a FLOAT3 which won't work in my case. If I don't use D3DDECLUSAGE_POSITION for the usage (e.g. using something like D3DDECLUSAGE_TEXCOORD), it complains with this error: "Declaration can't map to fixed function FVF because position field is missing. Usage: D3DDECLUSAGE_POSITION, with usage index 0 is required for fixed function" This is confusing for a start, because I thought I was trying to use the programmable pipleline - both my vertex buffers are created with zero as the FVF parameter specifying them to be NON-FVF buffers. If I were to use D3DDECLUSAGE_POSITION and D3DDECLTYPE_FLOAT3, is there some way that, for each vertex, I can some how only pass in one float for the first stream and 2 floats for the second stream? I don't mind if I get two FLOAT3s into my vertex shader like this: first stream input: FLOAT3 - x = <actual correct y value> y = 0 z = 0 second stream input: FLOAT3 - x = <actual correct x value> y = <actual correct z value> z = 0 I can then just extract the floats out and create a new vector in the vertex shader. I'm 100% sure I've heard of this being done before, but I can't seem to find a way to do it. If anyone can shed any more light, I'd be eternally grateful - I've been banging my head about this for over a week now. I did have a couple of useful replies about this before, but they didn't work. Thanks in advance [Edited by - RobMaddison on July 31, 2008 2:38:38 PM]

Share this post


Link to post
Share on other sites
I've not seen/read your previous thread, but I've definitely implemented nearly this exact same code myself at some point in the distant past.

Quote:
"Declaration can't map to fixed function FVF because position field is missing. Usage: D3DDECLUSAGE_POSITION, with usage index 0 is required for fixed function"

This is confusing for a start, because I thought I was trying to use the programmable pipleline
Yes, I wouldn't be expecting that message. Maybe dig into PIX to see what state the pipeline is in - it may well be that with your order of pipeline configuration that it thinks you're using FF and later tell it otherwise. Or something silly like that.

Off the top of my head (don't have my old code to hand) the trick was creating two streams, one with a <D3DDECLUSAGE_POSITION, usage=0, size=float> and a <D3DDECLUSAGE_POSITION, usage=1, size=float2> and then the declaration in the VS can be something like:

struct vsi
{
float y : POSITION0;
float2 xz : POSITION1;
// ...
};

vso vs( in vsi vertex )
{
float3 pos = float3( vertex.xz.x, vertex.y, vertex.xz.y );
// ...
}



If that fragment doesn't yield anything, can you post your declarations - both app and shader side?

hth
Jack

Share this post


Link to post
Share on other sites
Thanks for the quick responses guys.

Quote:

Off the top of my head (don't have my old code to hand) the trick was creating two streams, one with a <D3DDECLUSAGE_POSITION, usage=0, size=float> and a <D3DDECLUSAGE_POSITION, usage=1, size=float2> and then the declaration in the VS can be something like:


Jack, that doesn't quite work in my configuration as when using POSITION as a usage, it requires a size of FLOAT3. Here is my exact usage at the moment:


D3DVERTEXELEMENT9 declTerrainXYZ[]=
{
{0, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 1},
D3DECL_END()
};


When I call the DrawIndexPrimitive method using this declaration, I get

"Declaration can't map to fixed function FVF because position must use D3DDECLTYPE_FLOAT3". Here is the very simple shader fx file I'm using:


struct V_To_P
{
float4 Position : POSITION;
float4 Color : COLOR0;
};

float4x4 xViewProjection;

V_To_P SimpleVertexShader( float1 y : POSITION0, float2 xz : POSITION1)
{
V_To_P Output = (V_To_P)0;

....

return Output;
}

technique Simplest
{
pass Pass0
{
VertexShader = compile vs_3_0 SimpleVertexShader();
PixelShader = NULL;
}
}


Here is the code to draw the primitives:


D3DXHandle technique = effect->GetTechniqueByName("Simplest");
D3DXHandle matrix = effect->GetParameterByName(0, "xViewProjection");

hr = effect->SetTechnique(technique");
hr = effect->SetMatrix(...);

hr = DXUTGetD3DDevice()->SetVertexDeclaration(<as above>);
hr = DXUTGetD3DDevice()->SetStreamSource(0, <vertexbufferwithsinglefloats>, 0, sizeof(float));
hr = DXUTGetD3DDevice()->SetStreamSource(0, <vertexbufferwithfloatpairs>, 0, sizeof(float) * 2);
hr = DXUTGetD3DDevice()->SetIndices(<regular indices>);

// then simple beginpass, drawindexprimitive, endpass, etc



Tim, if I specify FLOAT3 for both elements and use usagetypes of POSITION, it doesn't error, but how would I specify that my first FLOAT3 is only (x, y, 0) and the second is only (x, 0 ,0)?

Thanks guys, I'd really love to get to the bottom of this.

Share this post


Link to post
Share on other sites
When you create the VBs you need to fill them with 3 floats, either using three seperate floats or using a D3DXVECTOR3. Not quite sure how to describe it, this isn't my dev machine and I don't have any code here. I'm geussing you define a struct for your vertex data, fill an array of them and then memcopy into your VB. In this struct you need the three floats instead of one or two, when you fill in the instances of this struct in the array you set the unused values as 0.

Also you don't necessarily need to use FLOAT3 for texcoord, which is why i suggested it instead of position1, you can definately use FLOAT2, and i think you might be able to use just FLOAT.

Lastly, in your set stream source calls, the first parameter of the first call should be 1, not 0.

Share this post


Link to post
Share on other sites
Quote:
Original post by RobMaddison
Jack, that doesn't quite work in my configuration as when using POSITION as a usage, it requires a size of FLOAT3.
I did a bit of reading around on MSDN and I must be remembering my code wrong for exactly the reason you state. I am left wondering whether I just used TEXCOORD fields instead - they're about the only 'free form' field you can use with D3D9 as the rest all have quite strictly defined semantics.

It's been quite a while since I did any D3D9 work, but can you try your code using TEXCOORD instead of POSITION? A straight up substitution should suffice and the test will be whether the runtime throws a wobbly over not having a POSITION...

Quote:
if I specify FLOAT3 for both elements and use usagetypes of POSITION, it doesn't error, but how would I specify that my first FLOAT3 is only (x, y, 0) and the second is only (x, 0 ,0)?
I'm pretty sure you can't - the IA stage would just lump through two float3's and you'd just burn the extra storage and bandwidth.

hth
Jack

Share this post


Link to post
Share on other sites
Hi Tim

Thanks for spotting the error in the SetStreamSource line, although that was a typo as my code is on my dev machine.

I see what you're saying about putting FLOAT3's (or vectors) into the vertex buffer, but the whole point of this exercise for me is to cut down on memory by reusing the x and z coordinates of each terrain patch as they are regular grids. The heights are individual and need to be stored in a buffer on their own. If I store 3 float vectors in the buffer, then I may as well store the x and z too in the unused parts. This would take up too much memory though, as my terrain at the highest LOD level is made up of 4096 65x65 patches.

Using the method I'm aiming at, the y values for the entire terrain would take up:

65x65 (verts) = 4225 total verts per patch
4225 x 4 bytes (y value float) = 16,900 bytes
16,900 x 4096 (patches) = 69.2mb

the x and z coordinates would only need to be stored for one patch - when drawing, they'll just be translated to the correct positon.

This is a totally acceptable amount of memory for what I need, but if I have to add the x and z into the mix, my vertex buffer(s) grow to over 200mb which I can't afford. Does that help to explain what I'm trying to achieve better?

Thanks

Share this post


Link to post
Share on other sites
Hi Jack

I tried using TEXCOORD instead of position and yes, it complains that it should have a POSITION:

"Declaration can't map to fixed function FVF because position field is missing. Usage: D3DDECLUSAGE_POSITION, with usage index 0 is required for fixed function"

(I had also changed the shader file to use TEXCOORD0 and TEXCOORD1 as the inputs).

It would appear to me that my engine somehow things I'm tryin to use the fixed function pipeline. Is there a way to tell which I'm using? I'm currently using the DXUT libraries just to get things up and running. If there's a "SetDeviceToUseFVF" or something similar I can just do a search for it in my code.

Thanks again for your input, it is much appreciated.

Share this post


Link to post
Share on other sites
OK sorry about that then, I thought there was some other reason you needed them seperated over two buffers. In that case I think you might want to look into hw instancing, if your target hardware supports it. I can't remember how to do it without looking at my old code, but the DX SDK has some pretty good info about it. The basic premise would be to still use FLOAT3 for the position with an empty value, also still use texcoord for the y's. However, you should be able to send one patch of position data and multiple patches of height data and tell d3d to draw multiple copies of the position VB. I'm not 100% sure this will be possible in this case. The way I did it was to have stream 0 contain the position VB, stream one contain a second position which was an offset for the whole patch and then store the height information as a texture using VTF. The problem is you will need three streams, each with a different stride, one for (x,z,0), one for patcht offset and one for height data. I know its designed for two, but I'm not sure about three, the docs will probably mention it.

Share this post


Link to post
Share on other sites
Tim

I believe you might be onto something here. If I put the FLOAT3 POSITION value first, that satisfies DX's need for it and my repeated xz values can just have a dummy 0 value added for the y - I can then hopefully make the second stream just use TEXCOORD with FLOAT1.

I'll let you know how I get on, thanks.

Rob

Share this post


Link to post
Share on other sites
Tim's idea for using instancing is a good one - I should imagine it'll work nicely on paper, but do be careful about performance testing it. A bit outdated now, but I know there were some earlier SM2/SM3 GPU's that weren't quite so instancing-friendly, especially with unaligned strides or something like that.

Quote:
...This would take up too much memory though, as my terrain at the highest LOD level is made up of 4096 65x65 patches.
Have you considered a streaming architecture? I've had success with a crude implementation years back and am currently toying with some code to take advantage of multithreading for this. You can potentially reduce the in-memory working set down to an acceptable level whilst effectively making the landscape unbounded in size...

Quote:
Original post by RobMaddison
It would appear to me that my engine somehow things I'm tryin to use the fixed function pipeline. Is there a way to tell which I'm using? I'm currently using the DXUT libraries just to get things up and running. If there's a "SetDeviceToUseFVF" or something similar I can just do a search for it in my code.
There is no such call unfortunately - that'd just make things far too easy [cool]

I'd recommend grabbing a single-frame capture using PIX and stepping through it to the offending Draw**() call. You should be able to pull up the device state at that point in time and find out what its basing that message on.

It's common enough in complex D3D apps to think you're setting pipeline state in a certain way only to find a leaked state ruining the party. PIX will tell you exactly what state the device is in and if this isn't what you expect then you've got an application bug - you could be lucky and find that the message is due to something like this.

The flipside of course is that the state matches what you're expecting and you've either proven that your app isn't setting the correct state or you simply cant do what you're trying to do - any of the 3 outcomes being quite useful to know!

hth
Jack

Share this post


Link to post
Share on other sites
Well, I'm getting close...

My vertex declaration looks like this:


D3DVERTEXELEMENT9 declTerrainXYZ[]=
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{1, 0, D3DDECLTYPE_FLOAT2, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DECL_END()
};


The inputs to my vertex shader are:


V_To_P SimpleVertexShader( float3 Position : POSITION, float2 yy : TEXCOORD)
{
V_To_P Output = (V_To_P)0;

float4 newPos = float4(Position.x, yy.x, Position.z, 1.0);
Output.Position = mul(newPos, xViewProjection);

...

return Output;
}



and a snippet of my c++ code:


hr = DXUTGetD3DDevice()->SetVertexDeclaration(<as above>);
hr = DXUTGetD3DDevice()->SetStreamSource(0, <VertexBufferWithD2DXVECTOR3Verts>, 0, sizeof(D3DXVECTOR3));
hr = DXUTGetD3DDevice()->SetStreamSource(1, <VertexBufferWithHeightFloatPairs>, 0, sizeof(float) * 2);
hr = DXUTGetD3DDevice()->SetIndices(<regular indices>);



I am getting the correct triangles drawn, but the heights are not being picked up by the second stream, i.e. in the shader yy.x (and yy.y) is coming through as zero and the vertex buffer definitely contains heights.

Any ideas folks?

Thanks

Share this post


Link to post
Share on other sites
Hi Tim

I did try that one and it didn't work. What I did do is pad the second stream out to D3DXVECTORS and supply it as a POSITION1 field which worked perfectly, but defeats the object of my exercise. So I know the shader and drawing code is correct, it's just I need to find a way to pass in 1 float as the second input.

Thanks
Rob

Share this post


Link to post
Share on other sites
Hmm, thats odd. I can't see anything obviously wrong with the code you posted, and the method works as I've used it myself, I'm not really sure what to suggest, sorry.

One thing I did notice is that you are using D3DDECLTYPE_FLOAT2 when the docs say that you should be able to use D3DDECLTYPE_FLOAT1 with D3DDECLUSAGE_TEXCOORD, which would halve the memory you need. Of course thats only true if it actually works.

Share this post


Link to post
Share on other sites
Hi Tim

Yes, I've tried it with just about every float including D3DDECLTYPE_FLOAT1 and nothing seems to work other than D3DDECLTYPE_FLOAT3 and POSITION(1). I'm now reinvestigating my vertex buffer creation just to be 100% sure it contains the height values. It's something I've checked and analyised twice before, but I'm running out of ideas!

Thanks

Share this post


Link to post
Share on other sites
Don't pass an FVF to CreateVertexBuffer, pass 0. This may make D3D complain if the FVF and your declaration are different. Other than that, the only reason I could see it complaining is that you haven't actually activated your effect before you draw. I have several shaders where my position is short2, and it works fine.

If you aren't using a shader, and have fixed pipeline vertex processing, your inputs have to be what the fixed pipe wants. For a shader you can input whatever you want. Since you have a shader, all I can think of is that it's not actually being used, and the fixed pipeline is complaining at you.

Share this post


Link to post
Share on other sites
Does the card that you're trying to run this on have hardware vertex processing? Did you enable it with D3DCREATE_HARDWARE_VERTEXPROCESSING?

If I remember correctly software vertex processing is much more restrictive on what you can put in the declarations.

Share this post


Link to post
Share on other sites
Quote:
Does the card that you're trying to run this on have hardware vertex processing? Did you enable it with D3DCREATE_HARDWARE_VERTEXPROCESSING?

If I remember correctly software vertex processing is much more restrictive on what you can put in the declarations.


I haven't explicitly set that flag (D3DCREATE_HARDWARE_VERTEXPROCESSING), I'll check - it certainly looks to be using hardware vertex processing (it's extremely fast).

Quote:

Don't pass an FVF to CreateVertexBuffer, pass 0. This may make D3D complain if the FVF and your declaration are different.


I do pass zero in the FVF param of CreateVertexBuffer.

Quote:
Other than that, the only reason I could see it complaining is that you haven't actually activated your effect before you draw. I have several shaders where my position is short2, and it works fine.


The actual drawing of the vertices is working fine with regard to the effect/shader. I am passing in untransformed world terrain vertices and the shader is transforming them to my viewport perfectly. The following line in my shader:


Output.Position = mul(newPos, xViewProjection);



Proves that theory. The terrain grid draws perfectly well, it's just flat.

One thing I've been musing over for the last few minutes is whether the elements I put into my vertex buffer need to be in a struct with an x accessor. It works with two streams of D3DXVECTOR3 types - I'm wondering whether my second vertex buffer should be filled with something like:

struct oneFloatVertex
{
float x;
};

At the moment, I just fill it with float types. I'm wondering if the vertex shader needs something with an accessor in order to use the yy.x accessor I've used in my shader.

Thanks for all the help so far guys.

Share this post


Link to post
Share on other sites
Thanks to everyone for their help on this, but I've figured it out (and how annoyingly simple is the fix!).

Essentially, the vertex buffers are fine but this declaration:


D3DVERTEXELEMENT9 declTerrainXYZ[]=
{
{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
{1, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
D3DECL_END()
};


Needs to be this in the vertex shader signature:


V_To_P SimpleVertexShader( float3 Position : POSITION, float4 Ypos : TEXCOORD)
{
V_To_P Output = (V_To_P)0;

float4 newPos = float4(Position.x, Ypos.x, Position.z, 1.0);
Output.Position = mul(newPos, xViewProjection);

...

return Output;
}


Notice that the Ypos TEXCOORD input type is a float4, not a float1 as you'd expect. I read somewhere that a D3DDECLTYPE_FLOAT1 expands to float4(x, 0.0, 0.0, 1.0).

Now working perfectly.

Thanks again

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this