Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Inexplicable error

Started by hupsilardee Oct 20, 2011 at 6:44 PM 18 replies 2.4k views
Original Post
hupsilardee
hupsilardee
Hi, me again, this time with the weirdest error I have ever encountered.
Here's the code


struct Button
{
std::string text;
gml::Vector2 position;
Button(std::string t, gml::Vector2 pos) : text(t), position(pos) {}
};
class MainMenuControl : GameObject
{
std::vector<Button> buttons;
Font* font;
public:
void Create(Scene* scene)
{
font = new Font(scene->pGraphics);
font->Load("Arial Black", 32);
buttons.push_back(Button("Start Game", gml::Vector2(100, 100)));
buttons.push_back(Button("Options", gml::Vector2(100, 132)));
buttons.push_back(Button("Exit", gml::Vector2(100, 164)));
}
void Update(Scene* scene)
{
}
void Render(Scene* scene)
{
gml::Matrix view = gml::Matrix::CreateLookAt(gml::Vector3(512, 288, 10), gml::Vector3(512, 288, 0), gml::Vector3(0, 1, 0));
gml::Matrix proj = gml::Matrix::CreateOrtho(-512, 512, -288, 288, 1, 1024);
gml::Matrix world;

for (int i = 0; i < buttons.size(); i++)
{
world = gml::Matrix::CreateTranslation(buttons.position.x, buttons.position.z, 0);
font->DrawString(buttons.text, world * view * proj);
}
}
void Destroy(Scene* scene)
{
delete font;
}
};


Here's the effect file, loaded as an ID3DXEffect and used by Font::DrawString

float4x4 WorldViewProj;
sampler TexSamp : register(s0) = sampler_state { MinFilter = Point; MagFilter = Point; MipFilter = None; };

void VertexShaderFunc(in float3 iPos : POSITION0, in float2 iTex : TEXCOORD0,
out float4 oPos : POSITION0, out float2 oTex : TEXCOORD0)
{
oPos = mul(float4(iPos.xyz, 1), WorldViewProj);
oTex = iTex;
}
void PixelShaderFunc(in float2 iTex : TEXCOORD0,
out float4 oCol : COLOR0)
{
oCol = tex2D(TexSamp, iTex);
}

technique t0
{
pass p0
{
VertexShader = compile vs_2_0 VertexShaderFunc();
PixelShader = compile ps_2_0 PixelShaderFunc();
AlphaBlendEnable = true;
SrcBlend = SrcAlpha;
DestBlend = InvSrcAlpha;
}
}


And here's the result. (attached as PNG)
[attachment=5807:Capture.PNG]
How on earth is this happening? The "Options" and "Exit" buttons clearly have larger Y-coordinates than "Start Game"!
I have tested and tested and tested all my matrix creation functions, (the same thing happens with D3DXMATRIX).
Font is my own implementation, I will post it up if required.
hupsilardee
hupsilardee
I wrote the code that created the projection matrix, and I know 3D math. Trust me, this is not the problem. If you look at the order in which the buttons are inserted into the list, this could still not explain the order in which they are appearing onscreen.

I think the problem could be in the replacement of the worldViewProj matrix in the effect/vertex shader constants, but I don't know about how this works.
hupsilardee
hupsilardee
I looked at the constructor for that, no luck
gml::Vector2


struct Vector2
{
float x;
float y;

Vector2();
Vector2(Vector2& v);
Vector2(float _x, float _y);

Vector2 operator+(Vector2& v);
Vector2 operator-(Vector2& v);
Vector2 operator*(Vector2& v);
Vector2 operator*(float f);
Vector2 operator/(Vector2& v);
Vector2 operator/(float f);

Vector2 operator+=(Vector2& v);
Vector2 operator-=(Vector2& v);
Vector2 operator*=(Vector2& v);
Vector2 operator*=(float f);
Vector2 operator/=(Vector2& v);
Vector2 operator/=(float f);

float Length();
float LengthSquared();
Vector2 Lerp(Vector2& v, float f);
void Normalize();
};




Vector2::Vector2()
{
x = 0;
y = 0;
}
Vector2::Vector2(Vector2& v)
{
x = v.x;
y = v.y;
}
Vector2::Vector2(float _x, float _y)
{
x = _x;
y = _y;
}
//
Vector2 Vector2::operator+(Vector2& v)
{
return Vector2(x + v.x, y + v.y);
}
Vector2 Vector2::operator-(Vector2& v)
{
return Vector2(x - v.x, y - v.y);
}
Vector2 Vector2::operator*(Vector2& v)
{
return Vector2(x * v.x, y * v.y);
}
Vector2 Vector2::operator*(float f)
{
return Vector2(x * f, y * f);
}
Vector2 Vector2::operator/(Vector2& v)
{
return Vector2(x / v.x, y / v.y);
}
Vector2 Vector2::operator/(float f)
{
return Vector2(x / f, y / f);
}
//
Vector2 Vector2::operator+=(Vector2& v)
{
x += v.x;
y += v.y;
return *this;
}
Vector2 Vector2::operator-=(Vector2& v)
{
x -= v.x;
y -= v.y;
return *this;
}
Vector2 Vector2::operator*=(Vector2& v)
{
x *= v.x;
y *= v.y;
return *this;
}
Vector2 Vector2::operator*=(float f)
{
x *= f;
y *= f;
return *this;
}
Vector2 Vector2::operator/=(Vector2& v)
{
x /= v.x;
y /= v.y;
return *this;
}
Vector2 Vector2::operator/=(float f)
{
x /= f;
y /= f;
return *this;
}
//
float Vector2::Length()
{
return sqrtf(x*x+y*y);
}
float Vector2::LengthSquared()
{
return x*x + y*y;
}
Vector2 Vector2::Lerp(Vector2& v, float f)
{
return Vector2(x+(v.x-x)*f, y+(v.y-y)*f);
}
void Vector2::Normalize()
{
float l = Length();
x /= l;
y /= l;
}


All implementations present and correct. Perhaps not as fast as assembly or SSE2 or whatever it is the hardcore people use, but all working.
ApochPiQ
ApochPiQ
Something doesn't add up. You refer to buttons.position.z in your Render() code, but Vector2 doesn't even have a member called z.
hupsilardee
hupsilardee
Eh?
My bad, that should be y. It was just a typo. Same result with it fixed.
ApochPiQ
ApochPiQ
I assume you've run under a debugger and confirmed that (1) the numbers in the list are correct and (2) the numbers produced by your matrix math are correct?

Have you stripped the code down to a minimal example that reproduces the problem? Alternatively, can you reproduce this kind of error in any other situations?


[edit] And maybe at the risk of probing a bit too hard - since the code you posted is definitely not the code you're running (it shouldn't even compile with z instead of y), are you absolutely sure that there are no other differences between what you're actually running and what is posted?
XTAL256
XTAL256
Easiest thing would be to try adding more buttons, or changing their positions, to see how that affects the layout.
[Window Detective] - Windows UI spy utility for programmers
L. Spiro
L. Spiro
Explicitly define the = operator for your Button structure.
A sneaky suspicious tells me that will solve all of your problems and remove your invalid-access waiting to happen.


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
hupsilardee
hupsilardee
OK, I solved the problem, and I was right in thinking the ID3DXEffect was to blame.
Within Font::DrawString(), the call to ID3DXEffect::SetMatrix(worldviewproj) was INSIDE the BeginPass(0) / EndPass() pair.
When moved outside the BeginPass(0) / EndPass() pair, the buttons appear in the correct position.

So I assume that somehow the effect was retaining the old matrices from the previous draw call.
So the lesson to be learned is: dont set effect parameters if a pass has begun!

Thanks to everyone for trying to help.
ApochPiQ
ApochPiQ

Explicitly define the = operator for your Button structure.
A sneaky suspicious tells me that will solve all of your problems and remove your invalid-access waiting to happen.


L. Spiro




This is thoroughly off-topic, but... eh?

Button has two members, a std::string and a gml::Vector2. std::string is copy-safe already. gml::Vector2 is POD, and so the compiler-generated copy constructor and assignment operator will be fine.

I don't see what you are seeing as a problem here?
LancerSolurus
LancerSolurus
FYI, use effect->CommitChanges() to set stuff within a pass, you are correct in that it will use what is set before the pass before it begins if this command isn't used. Still it's better to set everything outside of the pass that you can to reduce cpu cycles used.
******************************************************************************************
Youtube Channel
hupsilardee
hupsilardee

FYI, use effect->CommitChanges() to set stuff within a pass, you are correct in that it will use what is set before the pass before it begins if this command isn't used. Still it's better to set everything outside of the pass that you can to reduce cpu cycles used.


Didn't notice that little function there. So I could use it to batch models that share the shadow-map effect (a lot of them will), but have different world matrices, without needing to stop and restart the pass. Thanks for the heads-up!
LancerSolurus
LancerSolurus
You picked the exact thing I use it for mostly, when batching large amounts of models I use it to update the batching list since the shader has a limited number of array slots. Worked pretty well when I was playing around and batched 2 million rain drops in my precipitation shader.
******************************************************************************************
Youtube Channel
L. Spiro
L. Spiro

[quote name='YogurtEmperor' timestamp='1319186321' post='4874970']
Explicitly define the = operator for your Button structure.
A sneaky suspicious tells me that will solve all of your problems and remove your invalid-access waiting to happen.


L. Spiro




This is thoroughly off-topic, but... eh?

Button has two members, a std::string and a gml::Vector2. std::string is copy-safe already. gml::Vector2 is POD, and so the compiler-generated copy constructor and assignment operator will be fine.

I don't see what you are seeing as a problem here?[/quote]
Correct me unless I am wrong (been using game-ready classes instead of std::*), but the default copy constructor for that structure will simply copy all of the bytes of the structure, not call the copy operator of std::string.
And at the same time, since he is using std::vector, after the copy destructors will be called.

So instance A gets copied and still has a pointer to some address, then the original gets destroyed and releases that address, the copy still points there and the string remains there, etc.
Another instance gets made and because it is the next allocation being made it gets the address of the string that was previously destroyed and overwrites it.

Suddenly you have two buttons with the same name, and when both of them are destroyed they will double-free that same address. Of course I do not see duplicate names in his output, but I can imagine that is a side-effect of having 3 buttons and somehow names get switched around just so.


This is what I saw, but it has been many years since I used std::string rather than game-ready versions of it and std::vector.


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
Brother Bob
Brother Bob

[quote name='ApochPiQ' timestamp='1319223967' post='4875124']
[quote name='YogurtEmperor' timestamp='1319186321' post='4874970']
Explicitly define the = operator for your Button structure.
A sneaky suspicious tells me that will solve all of your problems and remove your invalid-access waiting to happen.


L. Spiro




This is thoroughly off-topic, but... eh?

Button has two members, a std::string and a gml::Vector2. std::string is copy-safe already. gml::Vector2 is POD, and so the compiler-generated copy constructor and assignment operator will be fine.

I don't see what you are seeing as a problem here?[/quote]
Correct me unless I am wrong (been using game-ready classes instead of std::*), but the default copy constructor for that structure will simply copy all of the bytes of the structure, not call the copy operator of std::string.
And at the same time, since he is using std::vector, after the copy destructors will be called.

So instance A gets copied and still has a pointer to some address, then the original gets destroyed and releases that address, the copy still points there and the string remains there, etc.
Another instance gets made and because it is the next allocation being made it gets the address of the string that was previously destroyed and overwrites it.

Suddenly you have two buttons with the same name, and when both of them are destroyed they will double-free that same address. Of course I do not see duplicate names in his output, but I can imagine that is a side-effect of having 3 buttons and somehow names get switched around just so.


This is what I saw, but it has been many years since I used std::string rather than game-ready versions of it and std::vector.


L. Spiro
[/quote]
You're wrong, so I'm correcting you. The default copy constructor calls the copy constructor for all members. Assuming, of course, that all of them can be called, but that's not an issue in this particular case.
L. Spiro
L. Spiro
Gosh, I have been so thorough in my practices that I have forgotten some of those kinds of details.
If I have a non-POD structure or class that will be copied, I always explicitly define the copy operator, “to be thorough”.
I wonder at which point did I forget why I was doing that and assume it was because copy constructors would not be called. Hmmm!


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
hupsilardee
hupsilardee
What does POD-ready mean?
And what did you mean by "i am using game-ready classes instead of std::"? Are the standard containers slow or something?
L. Spiro
L. Spiro
POD is plain-old-data. It means a type that has no members containing non-trivial constructors or destructors or copy operators.
A type that is just data and can be safely copied and moved around in RAM via nothing but bytewise copies.

The std:: set of containers are not necessary aimed at game development, and there are a few things that could be changed or improved to make them suitable for game development, especially in the area of memory allocation etc.
This explains in more detail.

I am using a special memory manager and set of containers that are made for games, and support features such as trashable heaps.


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.