Re: [AD] held drawing and primitives

[ Thread Index | Date Index | More lists.liballeg.org/allegro-developers Archives ]


On Mon, Nov 29, 2010 at 8:22 AM, Thomas Fjellstrom <tfjellstrom@xxxxxxxxxx> wrote:
I know I've brought this up in the past, but I think this would be REALLY
handy. If held drawing also worked with the primitives.

I can see how it would be nice - but not sure it's really worth the effort. If you do something where it actually brings an improvement, like a scene with 1000 thick lines - you might just as well replace al_draw_line with al_draw_prim. Only 5 lines of extra code. Changing our graphics API to effectively support full deferred drawing on the other hand sounds like a major undertaking - need to take into account changes to blending, clipping, transformation and probably something else I can't think of right now. I've recently looked a bit at the XNA API (to figure out how they handle alpha) and there Shawn opted to not implement this either as of XNA 4.0. Instead he only added a "SpriteBatch" class which is more or less identical to our held drawing. Only SiegeLord likely knows how hard it would be to change this, but still, my impression is it might not be worth it.
 
SiegeLord has said that it is possible but requires a rewrite, which is
unfortunate.. I am willing to help with such a thing, because one of my
projects either needs it, or has to re-implement everything from
al_draw_bitmap, al_draw_text, to every single primitive.

Since the ttf addon doesn't seem to export its internal render methods, I'd
essentially have to re-implement the entire addon in my own project. Which I'd
rather not do if I don't have to.

I already started on designing the necessary infrastructure for my project to
handle full deferred drawing:

struct PaintEngineVert {
       static const int VERT_STRIDE = 10;
       double d[VERT_STRIDE];
};

struct PaintEngineStoreRange {
       DynArray<int> idx;
       int texture;
};

struct PaintEngineStore {
       PtrArray<PaintEngineStoreRange> range;
       DynArray<PaintEngineVert> vert;
};


Though I'm not sure if its missing anything important.

Basically it stores a big blob of vertices in interleaved format (xyz+rgba+uv)
for use with something like glDrawElemets, each drawing command will append
three or more vertices to make up one or more triangles (using triangle
lists), and append three or more indices to the current StoreRange structure.
When the texture is changed, either a new StoreRange object is pushed onto the
stack, or an existing one is selected from the stack which contains the same
texture id, where any subsequent drawing operations are added. Also, every
vertex's z is set to the current "Depth", that is to say, some value that
places it behind any items that were drawn after them, so things actually look
correct once everything is drawn. This is important, because the structure
stores objects out of drawing order, and without it, drawing operations would
layer wrong.

With this structure, all held drawing operations should be sorted by texture,
and an absolute minimum of texture switching will need to be done. One
simplification could be made to just not look for any existing StoreRange in
the stack, and just allocate a new StoreRange when the texture is switched,
that may also allow the code to skip dealing with the z value, but I think in
some cases the performance improvement may be worth it, and the overhead isn't
that large.

Do you think its worth it?


The z value won't help as OpenGL never does any sorting. If you happen to have a depth buffer and one of the per-pixel depth tests enabled then the z value will help for opaque textures but not for translucent ones. So I'd say the order must be preserved for overlapping primitives in any case. In the end I think the simple algorithm would be like this:

When drawing a primitive, check the stack of held operations. If the type (e.g. TRIANGLE_LIST) and texture are both identical, add it to the stack. Otherwise draw the complete stack and start a new stack with the new primitive. The change to right now would be that also things like drawing a line would somehow be allowed to go to a global vertex array.

The more complex variant would be to add all primitives to the stack, then when drawing them, generate vertex arrays by grouping them by type and texture. And while doing so check them all for overlap and make sure order is preserved for any but fully opaque primitives.

Somewhat related, I've seen a neat idea in XNA. It has a sort mode for its SpriteBatch which must be explicitely specified [1] - the documentation doesn't tell the possible values buy my guess is that it sorts either not at all or sorts all sprites by texture. So it's up to the user to simply draw a SpriteBatch in the same order in which sprites were added, or actually group them by texture (in case they do not overlap). For drawing large amount of texts this can help a lot, as glyphs usually do not overlap and having to change texture after each letter would get very slow. (I think XNA in fact can draw text as a SpriteBatch.)

[1] http://msdn.microsoft.com/en-us/library/ff433701.aspx


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/