• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Draw order when using "with"

obscene

Member
If, in a draw event, I say...

with par_whatever
{
draw_self();
}

Will the objects draw in order according to their depths every time?

I'm guessing not... but I need to come up with a test for this. But someone maybe can tell me right off-hand and if so, how could I solve this conundrum efficiently?
 

jo-thijs

Member
No, the order is unspecified, it can change depending on the target module (windows, android, html, ...) and the version of GameMaker.

I'm not sure how performant a priority queue is in GameMaker, but you might be interested in taking a look at that.
 

obscene

Member
This was my suspicion. I've got a crepusucular ray effect that redraws every object within range of a light if the object is in front of it and it causes many more swaps that is logical. Even though I have a very regimented depth system in my game so make sure texture pages swaps are efficient, it comes unhinged with this effect on.

Basically what I would need to do is a much, much better version of this...

with par_prop
{
if depth>-20 draw_self();
}
with par_prop
{
if depth<=-20 && >-30 draw_self();
}
with par_prop
{
if depth<=-30 && >-40 draw_self();
}
etc...

Of course that would be horrible. I guess I could make a ds_grid and sort it as well. Probably not efficient but better than the texture swaps. Will try that first.

An option might be to grab the depth of every instance in a room when I enter it, sort it and then run through it. The problem is when objects are created on the fly. I dread the thought.
 
Last edited:

jo-thijs

Member
The sorting thing is what I hinted at when I suggested priority queues.

I would like to ask if there are any structures in your project that can be exploited?
Is it really necessary to draw instances through a with?
 

obscene

Member
The sorting thing is what I hinted at when I suggested priority queues.

I would like to ask if there are any structures in your project that can be exploited?
Is it really necessary to draw instances through a with?
Yes. It's actually a surface effect thats drawn in post, and many of my objects draw more than one sprites, so actually it's much more of a mess than my example above.

WIP:
Code:
                    // Draw props in order  
                    ds_grid_sort(grid,1,false);
                    for (i=0; i<row; i++)
                        {
                        with ds_grid_get(grid,1,i)
                            {
                            // Calculate surface coordinates, draw and move back
                            x-=view_xview
                            y-=view_yview
                            event_perform(ev_draw,0)
                            x+=view_xview
                            y+=view_yview                          
                            }                      
                        }
 

obscene

Member
Crepuscular rays (god rays) are made by... at the end of the draw cycle... redrawing the lights onto a surface, redrawing the objects which mask the light onto that surface and then blurring the whole thing in a shader.

When I redraw all the mask objects, if they aren't redrawn in the same order by their depth then I end up with a ton of extra swaps. For instance in my game the basic wall blocks are all the same depth and in one texture group. Then the grasses are all on a lower depth and all on a different texture group. So maybe 100 objects there on 2 depths and two texture groups. But if I say "with par_prop" and they get drawn in a jumbled up order, suddenly there might be anywhere from 2 to 100 texture swaps just to redraw stuff that I drew before in 2 swaps.

The ds_grid_sort is working for now. I've reduced swaps significantly. However, just my luck... the overall performance is only a tad bit better because of all the grid work. Will post my results later when I get it all squared.
 

Freddy Jones

Your Main Detective
This does pretty much exactly what you want.


Code:
// sets up the depth order 
var depthOrder = ds_priority_create();
with( obj_par ){
    ds_priority_add( depthOrder, id, depth );  
}

// essentially the same as subtracting view_xview and yview from each drawn x and y
d3d_transform_add_translation( -view_xview, -view_yview, 0);

// calls each instances draw event from the lowest depth to the max. 
while( !ds_priority_empty( depthOrder) ){
    with( ds_priority_delete_min( depthOrder ) ){
        event_perform( ev_draw, 0);
    }
}
// resets the draw matrix
d3d_transform_set_identity();
// free your data
ds_priority_destroy( depthOrder );
 

obscene

Member
Thanks Freddy, that's actually quite simpler than all the changing of xs and ys... I'm sure it's more efficient and actually it helped solve a bug I was dealing with related to this.

Thank you guys so much for the help. With your help from this topic as well as my other one about the lensflares I got some nice results. In this particular part of the game with all these effects I went from 61 texture swaps to 41 and 94 vertex batches to 60 and according to the profiler about a 7 percent perfomance increase!
 

Dragon47

Member
If most of your objects' depth remain constant over time, then Freddy's suggestion can be further optimized. I agree upon using a priority queue, but you don't have to recreate and fill it every step. Create it at the beginning of the level and place your instances together with the desired depth in the priority queue as shown in Freddy's code. Then later, if an instance changes its depth during the level, use ds_priority_change_priority to update that instance alone rather than every instance every step.
 

obscene

Member
I may try to implement that... but often I have objects created on the fly and I don't want to get into a system that I have to alter every object's creation code to make sure they are always add to the the queue. I'm thinking about it... The overall impact of the queue is pretty light as it is but every millisecond counts!
 
Top