Using non-POD struct for vertex data

Started by
7 comments, last by Koen 13 years, 7 months ago
I've been using a simple C style POD Vector3D struct for vertex data. I really wanted to change this to use a C++ template instead to make calculations on vectors easier.

The new class I have written is here (posted on pastebin)

One of the optimizations for using Power VR's OpenGL implementation on the iPhone is to use aligned vertex data when sending to the GPU.

I'm concerned now that using my new class will effect performance of the game. Is it worth using my new class which is much more convenient to use or go back to the old struct and write C functions for vertex multiplication/addition etc.

Also I noticed using offsetof gives me an "invalid access to non-static" warning now. Is there any way like in C# to "pack" the data? I've noticed that the game still works fine and I'm using -Wno-invalid-offsetof to suppress the warning. Seems GCC handles the data fine.

Should I be worried, or should I just go with my new non-POD class?
Advertisement
Quote:A POD type in C++ is defined as either a scalar type or a POD class. A POD class has no user-defined copy assignment operator, no user-defined destructor, and no non-static data members that are not themselves PODs. Moreover, a POD class must be an aggregate, meaning it has no user-declared constructors, no private nor protected non-static data, no bases and no virtual functions. The standard includes statements about how PODs must behave in C++.
As far as I can tell, your class meets these requirements, assuming T is POD.
Quote:I'm concerned now that using my new class will effect performance of the game. Is it worth using my new class which is much more convenient to use or go back to the old struct and write C functions for vertex multiplication/addition etc.
They should both be equivalent.
Quote:Is there any way like in C# to "pack" the data?
There's no standard way - it depends on the compiler.
I'm pretty sure both GCC and MSVC support:
#pragma pack(push,1)//set packing alignment to 1-byte (tight packing)class Foo{... };#pragma pack(pop)//reset packing alignment
Thanks for the info!
Quote:Original post by Headkaze
I've been using a simple C style POD Vector3D struct for vertex data. I really wanted to change this to use a C++ template instead to make calculations on vectors easier.


The templated-ness of that class is a red herring. You are talking about whether operators are more convenient than C funcs. This is a highly debatable topic, and largely speaking there are a few things worth considring:

1. If you are DLL exporting your classes (which I doubt in this case), then using inline C functions is always better than inline member functions (which will not be inlined since the class is DLL exported).
2. Operators frequently lead to confusion. I absolutely hate them these days for vector ops for the following reasons:
- does Vec3 * Vec3 mean component multiply, dot product, cross product?
- does Matrix * Vec3 mean transform or rotate?
- If you SIMD optimise the classes it's faster, but with less precision.
There's no way to easily specify an additional precision flag.
3. For statically linked objects, there isn't any difference between an inlined operator, and inlined member function, or an inlined C function. It's a question of personal taste.

Increasingly I personally favour inlined C functions. That's just me though.....

Quote:Original post by Headkaze
One of the optimizations for using Power VR's OpenGL implementation on the iPhone is to use aligned vertex data when sending to the GPU.

That depends what the alignment requirements are.... I'm not familiar with the requirements, so can't comment......

Quote:Original post by Headkaze
I'm concerned now that using my new class will effect performance of the game. Is it worth using my new class which is much more convenient to use or go back to the old struct and write C functions for vertex multiplication/addition etc.


I'd argue that the usage of templates in this case is more debatable than the use of classes per say. Are you ever going to use anything other than float in your game? Are you sure you want to be using those short and byte versions of the Vector3 class? I'm hoping you have already realised there are some serious bugs implementation regarding overflows. eg:

Vertex3DB a(16,0,0);a *= 16;


and:

Vertex3DB a(127,0,0);Vertex3DB b(1,0,0);Vertex3DB c = a+b ;


And countless others.

This would imply that the result of Vector<char> * char should be Vector<short>. And similarly, the result of Vector<short> * short should be Vector<int>. Once you specialised all of the template methods to handle those overflows, you'll find that very few (if any) template methods will be shared between the data types. In this case then, I'd argue that creating specific classes (with inline member funcs) would be better than using a template in this case. Your short/char types are far too dangerous at the moment imho.

Quote:Should I be worried, or should I just go with my new non-POD class?


It's still a POD type. It's only non pod when it has either a virtual function, or it contains dynamic data (eg an array, string, etc). Personally, if I were you I'd ditch the template and write the Vector3D, Vector3DS and Vector3DB as separate types without the use of templates. My 2 cents....
Quote:Original post by Hodgman
Quote:...meaning it has no user-declared constructors...
As far as I can tell, your class meets these requirements, assuming T is POD.

Aren't his constructors user-declared? So I would assume that violates the requirements?

Quote:Original post by Koen
Quote:Original post by Hodgman
Quote:...meaning it has no user-declared constructors...
As far as I can tell, your class meets these requirements, assuming T is POD.

Aren't his constructors user-declared? So I would assume that violates the requirements?


Maybe in the strictest dictionary definition sense, but for practical purposes if the class can be moved with memcpy safely, it's a POD.
Quote:Original post by RobTheBloke
Maybe in the strictest dictionary definition sense

I have always been wondering why that constructor limitation was in there. I don't see how a constructor would ever need to influence the physical layout of a class. Maybe the commentary at the end of this note is an explanation. POD "classes" are supposed to mimic C structs, which is broader than only memory layout. The expression 'new T' should leave the POD object members un-initialized, which might not be the case if you implement a constructor.
Quote:Original post by Koen
I have always been wondering why that constructor limitation was in there.
The most important part of that limitation is the copy constructor - if you don't use the default one, then you're saying that copying the object involves more work than just copying it's members. Also, if you provide any constructors, you're also saying that creating one of these objects is more complex than just filling in the members.

One way to get around this is to make a base class that's POD, then put all the non-POD niceties (like constructors) into a derived class. You can use the derived class to construct an object, and then slice it down to the POD base type.
struct Pod{  int a, b, c;};struct NotPod : public Pod{  NotPod( int a, int b, int c ) : a(a), b(b), c(c) {}  NotPod( const Pod& base ) : a(base.a), b(base.b), c(base.c) {}  NotPod& operator=( const Pod& base ) { a=base.a; b=base.b; c=base.c; }  void Print() { printf( "%d, %d, %d", a, b, c ); }};Pod x = { 1, 2, 3 };//POD constructor.Pod y = NotPod( 1, 2, 3 );//not-POD constructor, sliced down to POD type.NotPod(x).Print();//Creating a non-POD from POD works too, if needed
That's an elegant solution. (You can't use the baseclass members in a deriving class' constructor initializer list, so you'll have to assign in the body.)

This topic is closed to new replies.

Advertisement