Vertex Declaration Design

Started by
7 comments, last by jollyjeffers 15 years, 9 months ago
My application currently only uses 4 different IDirect3DVertexDeclaration9 pointers. Right now, I define them globally in a namespace VertexDeclarations. Is this bad design? What are other people doing?
-----Quat
Advertisement
It's not the best by sure but if it works it's ok.
In the future you may want to build those algorithmically to allow loading of arbitrary meshes with arbitrary vertex formats.
Note that your approach isn't necessarly wrong: back in quake or quake2 era for example, most if not all geometry was just pos+norm+color+tex. In those cases, dynamically inferred vertex formats were just not needed.

Be sure that the hardcoding approach will by sure break as the level of complexity starts getting out of your hands.

Previously "Krohm"

Just from the top of my head:

If you use .x-files and you have access to the vertexformat maybe you can parce it through a function that builds the vertex-declaration for you?

Perhaps store the vertex-declarations in a vector-list as pointer and returning a index to the pointer and then reference the pointer to the correct model?

Just how many formats will the engine support? Will there be a need for a lot of different formats and do these need to be stored at runtime? If the engine is small, maybe a fixed number of formats will do?
I could just about accept a global array of D3DVERTEXELEMENT9's as they're just POD, but a constructed and valid IDirect3DVertexDeclaration9 really shouldn't be global. It's just got "hack" written all over it, especially with regard to ownership and ensuring that it's properly reference counted and cleaned up...

Building a geometry<->Effect mapping layer isn't too complex and will allow you to have much more elegant code.

I only use 10.0 and above these days which tends to enforce a better cohesion in this regard and I'd be inclined to follow it for D3D9 as well [smile]


hth
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

Quote:
but a constructed and valid IDirect3DVertexDeclaration9 really shouldn't be global. It's just got "hack" written all over it, especially with regard to ownership and ensuring that it's properly reference counted and cleaned up...


How is it hacky? I basically create them all in one function at application initialization, and destroy them all in one function when the application ends. I mean, technically they don't have to be global, and I could pass them as parameters, but if I at least wrap them in a namespace, I don't really see too much of a problem.

For example, .NET has a Pens class where you can access different pre-created Pen objects. So it is similar where I just access references to pre-created vertex declarations.

Right now the code works fine and is not hacky at all. It just calls SetVertexDeclaration() with the desired pointer when it needs to.

Anyway, I'm not trying to sound defensive because I wanted criticism, I just want you to elaborate on why you think it seems "hacky".
-----Quat
Your counter-arguments are fair enough [smile]

A lot of it comes from maintainability and good quality engineering being easy to fix as well as develop.

Globals, unless they are constants, are just a bad thing in my eyes - they say "everyone and no one owns them" and most importantly it says no one is really responsible for them.

You can create these super-duper CreateEverything() and DestroyEverything() and match them up in a global try{}catch{} block inside main() which will work but its not elegant.

Problems really start to arise when your codebase starts to get bigger - you're up to the 250,000 lines and 500 files realm or you're working with multiple developers on the same codebase.

Sooner or later someone makes a change that corrupts the global IDirect3DVertexDeclaration9* pointer - they accidentally call SAFE_RELEASE() but forget to call AddRef() for example. Then a completely seperate and seemingly unrelated piece of code starts falling over on ::Draw**() calls. You then look into the debug spew and eventually track it down to a NULL vertex declaration being set - simple!

But then you've got this problem whereby any piece of code can access these globals and you've got a massive codebase worked on my many developers spanning back many months or even years of work. Tools like ReSharper (admittedly for .NET) will only help you so much with the "Find Usages" feature along with the "watches" feature in a debug session.

Turns out after 5 days of debugging that it was a guy who used to work on your team who wrote some shoddy file loading code that grabbed the vertex declaration to try and match the incoming data stream. That code was broken for ages but its only showed up because someone else made a change that uses the resource in a subtly different (and correct) way that now causes the bug to expose itself.

The flipside is that you have no globals and everything is wrapped up neatly and access is concisely and well controlled. No code that doesn't need that variable can see it and there is a very well defined responsibility for whom owns the declaration and who is responsible for ensuring it gets correctly cleaned up. You can then isolate the exact pieces of code for debugging very quickly.

Also, by following cohesion and encapsulation ideals you usually boost the testability of your code. Having a well defined interface into a set of owned resources allows you to unit test the entire surface area and have confidence about what conditions it will and wont work in. Another useful bit of information when you're trying to debug a complex solution.


That sound enough for you? [grin]

Quote:For example, .NET has a Pens class where you can access different pre-created Pen objects. So it is similar where I just access references to pre-created vertex declarations
Yes, I can see the similarity here. I'd argue that its a difference with C/C++ versus managed languages - with the internal reference checks and garbage collection its much harder for one part of the code to blow another bit out of the water in the way I described above.

Note that the Pens class also effectively implements const-correctness by making each pen a read-only property. That is an extra guarantee that I can't make Pens.Red actually refer to a green pen by changing it's state... to get the same protection in C/C++ you have to be quite careful - I could change your vertex declaration and you'd be none the wiser [evil]



Cheers,
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

As a semi-related aside, most of the DirectX SDK tutorials and samples are a mess of globals.

*gasp* Even the venerable HDRPipeline sample! For shame! [grin]
NextWar: The Quest for Earth available now for Windows Phone 7.
Quote:Original post by Sc4Freak
As a semi-related aside, most of the DirectX SDK tutorials and samples are a mess of globals.

*gasp* Even the venerable HDRPipeline sample! For shame! [grin]


Well it is Jacks coding, i looked at the code and was like WTF! [grin]. I Jest.

In my present project i have the vertex declaration created upon model load and stored in my custom Model format. This works nicely.
DXUT is fine with its use of globals - there are so damned many of them and they're so chaotic you wouldn't be able to write any code that trashed them simply because you'd never know which one to mess with [lol]

I had to conform to their standards with my HDRPipeline code. I take no responsibility and entirely blame The Man™ [razz]


Cheers,
Jack

<hr align="left" width="25%" />
Jack Hoxley <small>[</small><small> Forum FAQ | Revised FAQ | MVP Profile | Developer Journal ]</small>

This topic is closed to new replies.

Advertisement