Objects render themselves or does something render them?

Started by
13 comments, last by Namethatnobodyelsetook 19 years, 8 months ago
I'm (still) working on my game engine and I've to come to rendering. Right now I'm working with 2D surfaces that can have as many vertices that they want. Should each surface render itself or should I somehow combine its vertices into a vertexbuffer? I'm pretty sure that each object rendering itself (about 100 objects) would be VERY slow, but I don't know how to do it any other way. Any help would be appreciated! Thanks PS: I'm using DirectX9
Advertisement
I use openGL but this shouldnt matter in the design sense.

each object loads the vertex info, sends it to a "renderer"
and the renderer manages the data internaly and returns a
unique id to the object (so data can be addressed from outside
the renderer)

here is my Renderer.h file
(with some closed source stuff removed)
namespace render {using namespace std;template<class type>struct Polygon {   struct {      type a,b,c;   } poly, tangentSpace;   types::word shader;   types::word ownerBufferId;};struct VertexBuffer {   vector< math::Vector3f > verts;   vector< math::Vector3f > norms;      math::Vector2f tc[ constants::render::MaxMultiTextureUnits ];};struct PolygonBuffer {   vector< Polygon<types::uint32> > polygons;   VertexBuffer *vb;}; class Renderer : public IRenderDevice {public:      pushPolygons( Polygon *p, int count );      pushVerticies( int pbId, int count, int tcOnly, math::Vector3f *v = NULL, math::Vector3f *n = NULL, math::Vector2f *tc = NULL);      int createPolybuffer();      // called in this order...      void prepareRender();  // sorts shader state etc.      void flushBuffers();   // does rendering to screen      void finish();         // clean-up    }; // Renderer}; // Namespace


obviously thats a heavily cut down version, but you should
be able to see what im doing.
"I am a donut! Ask not how many tris/batch, but rather how many batches/frame!" -- Matthias Wloka & Richard Huddy, (GDC, DirectX 9 Performance)

http://www.silvermace.com/ -- My personal website
something along the lines of what silvermace said. It's always best to batch thigns up and render them all at once then render many small things individually. Instead of each object rendering themselves, you can make each object take a pointer to your renderer. Your renderer can have a render queue. Each object can put itself on this queue. A queuable object would need to give certain information about itself to the render queue - ie: What texture ID is it using, the vertex/pixel shader ID it uses, it's material ID and etc any kind of information you need to render any object in your world. The render queue can then sort the objects by textureID/vsID/psID/whatever and can render the objects in batches.
[size=2]aliak.net
Okay guys, thanks. I guess its time for me to experiment some more before I go any further with my engine.

Thanks again!
Okay, I'm not quite understanding what is going on, so I'm going to try and make up a situation.

I have 2 quads
One is at (0,0)
Two is at (64,0)
Both are 32x32
8 vertices (using tri-strips)

One and Two are instances of the Quad class.

I have a Renderer class, which contains a list of Quad's.
When I call the Renderer's DrawAllQuads() function, what do I do?

Do I combine all of the Quads vertices into a single vertex buffer, then make calls to DrawPrimitive(vertexBuffer, StartingVertex) increasing StartingVertex by 4 each time?

Or do I make a vertex-buffer for each quad?

Sorry if I'm being a pain, and thanks!
Typically you don't create vertex buffers during the frame, only at init. It may take a while.

Data which remains constant, and only changes as a result of World/View/Proj transforms, or shader work should go into static vertex buffers. This uploads the data to the card once, and refers to the data on the card there-after. This is fast.

Data which changes often via CPU calculation should go into a dynamic vertex buffer. This uploads the data as it's needed, each frame. This is slower, but is necessary is the data is changing.

Draw calls are slow. Drawing less than 1000 polygons at a time is wasteful. Sometimes you need to, other times you can work around it. For example. If I load a tree mesh, and it's 100 polygons, I put 10 copys into the vertex buffer. When I render, I can render upto 10 trees at once using a vertex shader to select a different transform matrix.

Quads are special. Even if you batch them with a shader, you're still not rendering many polys. Often it's better to world transform them on the CPU, set the D3DTS_WORLD to identity, copy them all (all that share the same texture, etc.) into a dynamic VB, then render that. This allows you to render hundreds or thousands of quads without killing your framerate.

Objects can setup their render properties (textures, texture stages, render states). Actual draw calls either need something else, higher up, to handle it, or to have an interface in your objects to allow optimal batching. Either way will work.
Ok, I've figured out that I don't want to use tri-strips for anything and that they're whats causing me headaches.

@Namethatnobodyelsetook: "Quads are special. Even if you batch them with a shader, you're still not rendering many polys. Often it's better to world transform them on the CPU, set the D3DTS_WORLD to identity, copy them all (all that share the same texture, etc.) into a dynamic VB, then render that. This allows you to render hundreds or thousands of quads without killing your framerate."

By this, do you mean render them in 3D? if so, then don't I have to worry about my other objects walking "through" my quads?

Thanks for all the help guys!!!
Bad advice:

I figure any sort of buffer and rendering orginization I do is going to suck. As my current project is my first engine, and my first use of DirectX even I'm doing it the naivest way possible that works. So far, everything is going perfectly fine; if terribly inefficient.

One quad drawing class has a static vertex buffer per quad and switches textures per frame per quad. [because naively, changing memory locations since the textures are loaded in memory should be fast, and creating buffers should be slower than changing them]

Another has a dynamic vertex buffer [created/destroyed per frame] but has static texture and many many quads.

So far the 2nd viariant gets me around 20,000 polys before becoming noticably slow. I've not seen the first become noticably slow. More than enough for my turn based 2d game. And when it stops being enough, I'll look into fixing some things, now that I know better.
You've still got your Z buffer handling depth issues for solid objects. All solid quads can be rendered "whenever".

For semi-transparent objects you need to maintain back to front ordering, as with non-quads. If you're just rendering quads that all have the same texture, just ensure you've Z sorted them before going into the dynamic buffer. If you have a mix of different objects, or objects with varying textures, you might not be able to batch them efficiently. Whether it's worth the trouble of trying to batch at all, while maintaining strict back to front Z ordering, really depends on your app and your art.

I'm just pointing out that one-size-fits-all doesn't work... and batching is critical if you're dealing with many small objects, like quads. How you batch, and how you render really depends on what you're doing. An engine doing a tiled 2D sidescroller will be designed "a bit" different than an engine doing Doom 3. If you're 2D, whether you're doing lots of alphablending, only alphatest, or just solid squares changes things somewhat. What's your art like? Do you have texture sheets, or a texture per frame/tile? Texture sheets really help you build a good batching system.

What I can tell you is that each object having it's own VB isn't good. There is overhead in making them. There is overhead in switching between them. The choice of static and dynamic buffers really depends on the app. The choice of how to do batching depends on the app. We don't have enough information to recommend handling things in any specific way... but with what we've said you can hopefully see how your choices will affect things.
Okay, here's some info before I ask any more questions:

I plan on using the engine for simple 3D models (including levels), a particle engine, and a GUI.
The GUI and particle engine are going to allow for transparency.
The particle engine will use a single texture for every particle, while the GUI is going to use a texture-sheet.

I'm just having a little problem understanding the logic behind some of this. Using what you said earlier (about dynamic and static VBs) if I had 4 characters on the screen and 1 left (dies and is never coming back), would I have to recreate the entire vertex-buffer (thus making it a dynamic VB)?

Do I sort all of the same type of objects into a VB (i.e. all quads in one VB, all character models in another, the level in its own, etc)?

To learn more about this, should I buy one of the following books:
3D Game Engine Programming
3D Game Engine Design
Real-Time Rendering
Or is there another book that would help?

Thanks for your patience and for helping!

This topic is closed to new replies.

Advertisement