Jump to content
  • Advertisement
Sign in to follow this  
Schnozzinkobenstein

DX10: Passing in an Array of Classes

This topic is 2826 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

This should be easily answered by anyone familiar with using HLSL.

Quick version: How do I pass an array into the shader if it's an array of classes?

Less-quick version (i.e. what my problem is):

I have a class Light. The following is how I get it into the shader.
App:
Light light;
ID3D10EffectVariable* mFXLightVar;
mFXLightVar->SetRawValue(&light,0,sizeof(Light));
mFXLightVar = mFX->GetVariableByName("gLight");


Shader:
cbuffer cbPerFrame
{
Light gLight;
}


The problem comes when I try to do this with an array of lights. The one at "address" 0 works fine, but the others are corrupted. This what I'm currently trying:
App:
Light light[NUM_LIGHTS];
ID3D10EffectVariable* mFXLightVar;
mFXLightVar->SetRawValue(&light,0,sizeof(Light)*NUM_LIGHTS);
mFXLightVar = mFX->GetVariableByName("gLight");


Shader:
cbuffer cbPerFrame
{
Light gLight[NUM_LIGHTS];
}



What do I need to change? (The lights all work fine when I try to use just one, btw.)

Share this post


Link to post
Share on other sites
Advertisement
This is a common problem people have. Most likely, your c++ class/struct is not created on a 16 byte alignment, which is a requirement for video cards. What does that mean? Here are some examples . . .

c/c++ struct

struct cLight{
D3DXVECTOR3 pos;
float Specular;
D3DXVECTOR2 otherInfo;
};

If you attempt to send this to the video card, this is where your data will be sotred/accessed

HLSL struct

struct cLight{
float4 data1;
float2 data2;
};

data1.xyz will contain your D3DXVECTOR3 pos variable from your c++ struct,
data1.w will be your specular, and data2.xy will be your otherinfo.

So, either account for how the data is packed in the Video card, or change your c++ struct to mirror the way your HLSL struct is, like this

c/c++ struct
struct cLight{
D3DXVECTOR3 pos;
float padd1;
float Specular;
float padd2[3];
D3DXVECTOR2 otherInfo;
float padd3[2];
};

Now, you could create your HLSL struct like this

struct cLight{
D3DXVECTOR3 pos;
float Specular;
D3DXVECTOR2 otherInfo;
};

and you the data is exactly where you would think it is, pos, specular and otherinfo can be accessed normally in HLSL. Hope this helps....

Share this post


Link to post
Share on other sites
I know about the padding, but thank you for the suggestions. However, I stumbled upon the solution merely by serendipity. (Probably because there's something I don't understand.) Can you tell me why this seems to work?

App:
struct Light
{
float spotPow;
D3DXVECTOR3 pos;
//16
D3DXVECTOR3 dir;
float range;
//16
D3DXCOLOR ambient;
//16
D3DXCOLOR diffuse;
//16
D3DXCOLOR specular;
//16
D3DXVECTOR3 att;
int on; // -1 (off), 1 (on), -2 (turning off), 2 (turning on)
//16
float switchLength; //how long it takes the light to turn on/off in seconds
float switchTime; //what point ( 0 - 1 ) the light is at being turned on or off
int type; // 0 (parallel), 1 (point), 2 (spot)
//12
float a,b,c,d,e;
//20

Light();
Light(D3DXVECTOR3 pos, D3DXVECTOR3 dir, D3DXCOLOR ambient, D3DXCOLOR diffuse,
D3DXCOLOR specular, D3DXVECTOR3 att, float spotPow, float range, int type);
void update(bool switched,float dt);
};

Shader:
struct Light
{
float spotPower;
float3 pos;
//16 bytes
float3 dir;
float range;
//16/32
float4 ambient;
//16/48
float4 diffuse;
//16/64
float4 specular;
//16/80
float3 att;
int on; // -1 (off), 1 (on), -2 (turning off), 2 (turning on)
//16/96
float switchLength; //how long it takes the light to turn on/off in seconds
float switchTime; //what point ( 0 - 1 ) the light is at being turned on or off
int type; // 0 (parallel), 1 (point), 2 (spot)
float pad[1];
//16/112
};


Note that I have what seems to me 16 extra bytes of pad in the application. I also couldn't use "float pad[5]" in the application with "float pad[1]" or "float pad" in the shader with "float a,b,c,d,e" in the application. I'm so confused right now.

Share this post


Link to post
Share on other sites
your shader struct is incorrect. If it works, it is out of luck. You do not need padding in your shader struct either. The way your c++ struct is set up and your HLSL struct setup does not equal each other. Take a look at the code I posted. If you want your c++ struct like this

stuct cLight {
float x;
D3DXVECTOR3 pos;
};

Notice.. No padding added. The sizeof(cLight) == 16 bytes
When you set the veriable in your shader
ID3D10EffectVariable* mFXLightVar;
mFXLightVar->SetRawValue(&light,0,sizeof(cLight));

You will send 16 bytes of data to your HLSL.

For each variable declared in a HLSL struct, it takes 16 bytes of data at a MINIMUM unless you do register packing --which you are not.

This might help a little. If you could do a sizeof the following HLSL struct, your answer would be 16* 3 = 48. That is because the x variable takes 16 bytes of data regardless of the fact it is a float. The same for the rest.. . .

struct cLight{
float x;
float y;
float pd[2];
};

So, change your c++ struct, or your HLSL struct.

Share this post


Link to post
Share on other sites
My app struct is 16-byte aligned, and the variables between the two structs are in order. As I said in my last post, I know that the pads are weird. As you wished, I tried what you suggested in your first post. It doesn't work.
app:
struct Light
{
float spotPow;
float pad1[3];

D3DXVECTOR3 pos;
float pad2[1];

D3DXVECTOR3 dir;
float pad3[1];

float range;
float pad4[3];

D3DXCOLOR ambient;
// float pad5[];

D3DXCOLOR diffuse;
// float pad6[];

D3DXCOLOR specular;
// float pad7[];

D3DXVECTOR3 att;
float pad8[1];

int on;
float pad9[3];

float switchLength;
float pad10[3];

float switchTime;
float pad11[3];

int type;
float pad12[3];

Light();
Light(D3DXVECTOR3 pos, D3DXVECTOR3 dir, D3DXCOLOR ambient, D3DXCOLOR diffuse,
D3DXCOLOR specular, D3DXVECTOR3 att, float spotPow, float range, int type);
void update(bool switched,float dt);
};

shader:
struct Light
{
float spotPower;
float3 pos;
float3 dir;
float range;
float4 ambient;
float4 diffuse;
float4 specular;
float3 att;
int on;
float switchLength;
float switchTime;
int type;
};


I've seen Frank Luna set up his structures the way I did in my last post (minus the pads that seem not only weird to you, but also to me). Since neither of you have pointed me to documentation that corroborates your view, I'm more inclined to believe the guy who has published several books on DirectX.

I can't really respond to your post other than, "I know." I would appreciate a suggestion of an explicit snippet of code to try, or an explanation as to why my weird solution works, or anything other than just restating your first post.

Share this post


Link to post
Share on other sites
Awsome, I liked franks books. I have read both his directx 9c and direct x 10 books. They are good. So, to use your authors own code. . . ., the code can be found here http://www.d3dcoder.net/d3d10.aspx Download Part II, unzip it, chapter6/Lighting

You can view his lighthelper.fx file where his light struct is defined as such

HLSL struct
struct Light{
float3 pos;
float3 dir;
float4 ambient;
float4 diffuse;
float4 spec;
float3 att;
float spotPower;
float range;
};

Then, his c++ struct is defined as such in Light.h

struct Light{
Light(){
ZeroMemory(this, sizeof(Light));
}
D3DXVECTOR3 pos;
float pad1; // not used
D3DXVECTOR3 dir;
float pad2; // not used
D3DXCOLOR ambient;
D3DXCOLOR diffuse;
D3DXCOLOR specular;
D3DXVECTOR3 att;
float spotPow;
float range;
};


As you can see, there is an error in this authors code. He uses padding for the first variables pos and dir, but forgets to add it after att. Hey, he wrote a whole book, not everyone can be 100% perfect.

I checked out your code you posted, it looks correct for padding and alignment. Found an error you will hit yourself over the head about

Light light;
ID3D10EffectVariable* mFXLightVar;<----------- Not initialized
mFXLightVar->SetRawValue(&light,0,sizeof(Light));<-------- Used a pointer to junk memory
mFXLightVar = mFX->GetVariableByName("gLight");<------ Now, the pointer is initalized

Share this post


Link to post
Share on other sites
Quote:
Original post by smasherprogFound an error you will hit yourself over the head about

Light light;
ID3D10EffectVariable* mFXLightVar;<----------- Not initialized
mFXLightVar->SetRawValue(&light,0,sizeof(Light));<-------- Used a pointer to junk memory
mFXLightVar = mFX->GetVariableByName("gLight");<------ Now, the pointer is initalized

That was my bad in copying it down for the forum post. My code actually hits the GetVariable line before the SetValue line.

I would love to see some working examples of passing a struct/class into the shader. I haven't found anything from googling and I couldn't figure out where to go on MSDN. The only one of Frank's I could find was his light. I tried wading through the horrendous samples that come with the DX SDK, but from what I've seen they do all of their variable passing on a per-variable basis even if it's for an entire struct.

I've done somewhat thorough testing of my Light struct, and all of my information is going where it needs to go when I have more than one light. Any more ideas? Thank you for trying.

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!