GameMaker Changing the Draw order of the Asset Layer

J

Jaqueta

Guest
Hi, I've been dynamically adding sprites to an asset layer, to use it as Decals, the performance was very good from my tests (Over 3000 Sprites and no signs of slow down). But, when I add an Sprite, the sprite is drawn BELOW the sprites added previously, and I haven't found a way to change the draw order, with the only exception being, clearing the layer, and add all decals again in the correct order, needless to say, that this is terrible

This GIF explains what is happening, and what I am trying to do:
https://imgur.com/a/EAIik


SO, is there anyway to change the draw order of an asset layer? Or to add the new sprites at the bottom of the List, instead of the top? And is actually a good idea to use the Asset Layer for Decals, or I can get better performance by using another method?

Thanks.
 
Last edited by a moderator:

jo-thijs

Member
Hi, I've been dynamically adding sprites to an asset layer, to use it as Decals, the performance was very good from my tests (Over 3000 Sprites and no signs of slow down). But, when I add an Sprite, the sprite is drawn BELOW the sprites added previously, and I haven't found a way to change the draw order, with the only exception being, clearing the layer, and add all decals again in the correct order, needless to say, that this is terrible


If you look closely, the Enemy Bodies are turned into decals AFTER the blood splatter, making them being drawn below the blood, looks very weird.

SO, is there anyway to change the draw order of an asset layer? Or to add the new sprites at the bottom of the List, instead of the top? And is actually a good idea to use the Asset Layer for Decals, or I can get better performance by using another method?

Thanks.
Every layer has a depth.
Go to the room editor.
Right click on your asset layer in the "layers" menu.
Select "Layer properties".
Go to the "Layer properties" menu.
Click on the lock icon next to the depth field on the bottom of the menu.
Change the depth as you would have done in earlier versions of GM.
Create the blood spatters on a layer with a different depth from the layer on which you create the bodies.
 
K

kevins_office

Guest
Yes you can change the order of how objects are drawn on the asset layer. But you have to manually do this by getting all elements on the layer, disabling their default draw event with either instance invisible or layer invisible, sort them in a ds_* something or array, then manually run their draw events in the order you want them drawn in.

Or you could make a separate layer for blood under the dead enemy layer in the IDE, so the blood will always be under the dead guys. This would be an easier way.
 
J

Jaqueta

Guest
Create the blood spatters on a layer with a different depth from the layer on which you create the bodies.
Or you could make a separate layer for blood under the dead enemy layer in the IDE, so the blood will always be under the dead guys. This would be an easier way.
That won't work, I want every new sprite to be placed above everything else, so it looks like the sprites are stacking on top of each other.

You have to manually do this by getting all elements on the layer, disabling their default draw event with either instance invisible or layer invisible, sort them in a ds_* something or array, then manually run their draw events in the order you want them drawn in.
I think this solution will have a big impact on performance.

Does anyone knows how I can file a request to add a function for that? It could be simple as what we have for particle systems, "part_system_draw_order", or having a "layer_sprite_create_ext" function, where you can choose if the sprite would be added at the top, or the bottom of the layer (This one would allow for more flexibility, but either would be fine really).
 

jo-thijs

Member
That won't work, I want every new sprite to be placed above everything else, so it looks like the sprites are stacking on top of each other.
Well, there are still plenty of tricks with depth that you can apply to achieve that.
Use instance_create_depth with a depth that decreases every time.

I think this solution will have a big impact on performance.
Why do you think so?
It sounds like the best way to ensure control over the order in which things are drawn.

Does anyone knows how I can file a request to add a function for that? It could be simple as what we have for particle systems, "part_system_draw_order", or having a "layer_sprite_create_ext" function, where you can choose if the sprite would be added at the top, or the bottom of the layer (This one would allow for more flexibility, but either would be fine really).
I can't find it anymore.
I guess they don't want feature requests anymore.
 
K

kevins_office

Guest
That won't work, I want every new sprite to be placed above everything else, so it looks like the sprites are stacking on top of each other.
One idea: One layer for blood, one layer for bodies. Then treat bodies like real bodies and not card board cut outs. Don't let two bodies fall on top of each other, use collision methods when they die to move them over slightly so they are not laying on top of each other, like a body falls and rolls off another body so they are side by side to each other. Then you wont have bodies stacking on top of each other and blood will always be under the body on the other layer.


I think this solution will have a big impact on performance.
No its not a big impact and is the new official recommended sorting method by YYG. Look at their Dungeon demo and they are using a crude (IMO) method of this.
There are some youtube videos on doing this (friendly astronaut) and some forum post here talking about it.
Sure its not as easy as just doing depth = -1 but it is doable and you will not notice any performance impact on your game if done correctly.

If your game is small enough, you might be able to get away with the old depth = -y method.
YYG doesn't recommend this anymore but it still does work in GMS2 if you pay attention to what the rest of your layers are doing.
 

CMAllen

Member
A simple approach might be to have 2 asset layers. A 'below' asset layer for assets that are always underneath. And an 'above' asset layer, for assets that go on top of the below layer. In the case of bodies and blood, blood goes on the below layer, and bodies go on the above layer. Quick, simple, no sorting needed.
 
J

Jaqueta

Guest
.
A simple approach might be to have 2 asset layers.
As said earlier, won't provide the effect I desire.

Then you wont have bodies stacking on top of each other and blood will always be under the body on the other layer.
But I want to blood to be spit over the bodies of the enemies.

Why do you think so?
It sounds like the best way to ensure control over the order in which things are drawn.
Because I'll use a LOT of decals, blood, bullet casings, corpses, explosion markings, debris, the list goes on... and I want to make it so there's a sense of permanence of the player's action.
The amount of decals can go up to 1000, 2000 or even more.

Here, I made a "test":
3000 Bullet casings drawn through the asset layer: Average Framerate 1000 FPS
upload_2018-3-7_19-47-17.png

3000 draw_sprite_ext Bullet casings drawn through a for loop. Average Framerate: 200 FPS
upload_2018-3-7_19-41-21.png

Of course, this is not the best way to draw it.

One thing that I am considering, is using this asset by GMWolf: https://marketplace.yoyogames.com/assets/1794/optimized-decals
Wonder if I can achieve the results I want with it.
 

jo-thijs

Member
.
As said earlier, won't provide the effect I desire.


But I want to blood to be spit over the bodies of the enemies.


Because I'll use a LOT of decals, blood, bullet casings, corpses, explosion markings, debris, the list goes on... and I want to make it so there's a sense of permanence of the player's action.
The amount of decals can go up to 1000, 2000 or even more.

Here, I made a "test":
3000 Bullet casings drawn through the asset layer: Average Framerate 1000 FPS
View attachment 16971

3000 draw_sprite_ext Bullet casings drawn through a for loop. Average Framerate: 200 FPS
View attachment 16970

Of course, this is not the best way to draw it.

One thing that I am considering, is using this asset by GMWolf: https://marketplace.yoyogames.com/assets/1794/optimized-decals
Wonder if I can achieve the results I want with it.
Interesting.

I would then advice one of the following 2 methods:
1) Create a vertex buffer and draw that to the screen instead of individual sprites.
You could also use multiple vertex buffers that are grouped together according to a grid so that only the vertices that are inside the view get drawn.
2) Use surfaces to maintain a part (a bit larger than the screen) of the room and redraw it using the loop every time the surface gets destroyed or the player moves far enough to require a surface update.
You can group sprites together, based on a grid so that you don't need to draw every sprite, only the ones inside.

GMWolf knows what he's doing and his asset is probably worth concidering, but it looks like it was created for GM:S 1.4.
It's probably just fine in GM:S 2, but it might be worth to PM him and ask if he thinks it would work for you and if it would solve your issue efficiently.
 

CMAllen

Member
But I want to blood to be spit over the bodies of the enemies.
Then afaik what you want is exactly how it already functions. It's not first in, last out. It's first in, first out. Add assets to an asset layer. New ones are always drawn over older ones. If it works differently in code than from the editor, then whoever wrote those functions messed up and that's a bug. Please report it so it can be fixed. Consistency in functionality is critical.

That being said, we can't see the code you're using to create your effects. Are you actually sure you're adding the assets version of your enemies after you add the blood? Because there's a clear delay between the switch over.
 

jo-thijs

Member
Then afaik what you want is exactly how it already functions. It's not first in, last out. It's first in, first out. Add assets to an asset layer. New ones are always drawn over older ones. If it works differently in code than from the editor, then whoever wrote those functions messed up and that's a bug. Please report it so it can be fixed. Consistency in functionality is critical.
That's indeed how it goes.
I'm not certain it is a bug, but it is inconvenient.

@Jaqueta, I just messed around a bit and found a possible solution.
If you're adding instances to the asset layer, you can also do the following.
Create the instances using instance_create_depth at a depth higher than the depth of your asset layer.
Then set their depth variable to the depth of the layer's depth.
You've now got the sprites in your requested order!

EDIT:
I can't seem to do it for sprites though.
 
Last edited:

CMAllen

Member
That's indeed how it goes.
I'm not certain it is a bug, but it is inconvenient.

@Jaqueta, I just messed around a bit and found a possible solution.
If you're adding instances to the asset layer, you can also do the following.
Create the instances using instance_create_depth at a depth higher than the depth of your asset layer.
Then set their depth variable to the depth of the layer's depth.
You've now got the sprites in your requested order!

EDIT:
I can't seem to do it for sprites though.
What I don't get is why drawing assets is so much quicker than drawing sprites. It's clearly just a dynamic linked list of drawing properties. 1000 assets shouldn't be drawing any faster than 1000 sprites. So why are assets in the area of 5x faster? What kind of code mess exists under the hood with standard draw functions that doesn't exist with assets? Assets use all the same properties to be drawn. They can be animated. It just doesn't make sense to me.
 
J

Jaqueta

Guest
Are you actually sure you're adding the assets version of your enemies after you add the blood? Because there's a clear delay between the switch over.
Yes! In fact a body, is separated in 5 Parts, the Head, Torso, Arms, Hands and Legs, and in order to get the body to be drawn correctly, I have to add the HEAD FIRST (Which should be draw at the top of everything else), take a look:

Code:
    body_spr[0]=spr_corpse_legs;
    body_ind[0]=legs_index;
    body_col[0]=char_legs_colour;

    body_spr[1]=spr_corpse_torso;
    body_ind[1]=torso_index;
    body_col[1]=char_torso_colour;

    body_spr[2]=spr_corpse_arms;
    body_ind[2]=torso_index;
    body_col[2]=char_arms_colour;

    body_spr[3]=spr_corpse_hands;
    body_ind[3]=torso_index;
    body_col[3]=char_hands_colour;

    body_spr[4]=char_skin_head_corpse;
    body_ind[4]=head_index;
    body_col[4]=c_white;
Code:
//Turning the body into a decal
    for (i=4; i>=0; i--)
    {
        var sprite=layer_sprite_create("Decals",x,y,body_spr[i]);
        layer_sprite_index(sprite,body_ind[i]);
        layer_sprite_xscale(sprite,image_xscale);
        layer_sprite_yscale(sprite,image_yscale);
        layer_sprite_angle(sprite,image_angle);
        layer_sprite_blend(sprite,merge_color(body_col[i],c_black,0.25));
        layer_sprite_alpha(sprite,image_alpha);
        layer_sprite_speed(sprite,0);
    };
Also, if I shoot, and a bullet casing ends up being at the same place of other decals, it will be added below everything else.

Yea, it appears to be a bug, I'll report to YoYo.

What I don't get is why drawing assets is so much quicker than drawing sprites.
Not sure too, but MAYBE this has to do with Draw Calls.
If I ain't mistaken, each time you use a draw function, you're essentially making a Draw Call, maybe it's more optimized on asset layers.
 

CMAllen

Member
Yea, it appears to be a bug, I'll report to YoYo.
That is bizarre. Why would adding assets by code work in the opposite order of assets added via the room editor?

Not sure too, but MAYBE this has to do with Draw Calls.
If I ain't mistaken, each time you use a draw function, you're essentially making a Draw Call, maybe it's more optimized on asset layers.
I thought about this a bit and wondered if an asset layer is drawn using a single, large vertex batch (being that assets don't much move), where as sprite draw calls are individual vertex batches. I know that vertex batches take more work to create and define than just drawing them, so that could explain why a larger, single vertex batch for an entire asset layer would be faster than lots of individual vertex batches for sprites.
 

Mike

nobody important
GMC Elder
If it works differently in code than from the editor, then whoever wrote those functions messed up and that's a bug.
This is not a bug. We have added a specific list order to the IDE to ensure that what you see is what you get, to allow users simple editing. But this is pre-compile step.

One code is running, the original rule applies. if you want something above add ti to a layer that is above. If you want it below, then add to the layer below.
if you want blood created below, make a blood layer. if you want bodies crated above, then create a body layer.

How big are the levels? Could you use a surface and just render in order to it? Then you don't have the expense of drawing all these things over and over, you just have one surface. Doesn't even have to be the same resolution I guess....
 

jo-thijs

Member
This is not a bug. We have added a specific list order to the IDE to ensure that what you see is what you get, to allow users simple editing. But this is pre-compile step.

One code is running, the original rule applies. if you want something above add ti to a layer that is above. If you want it below, then add to the layer below.
if you want blood created below, make a blood layer. if you want bodies crated above, then create a body layer.
Maybe it's not a bug, but it is inconvenient that there is no way to decide the drawing order of sprites within the same asset layer, other than removing every sprite on it first.

The issue isn't so much to get the blood below the bodies.
It's to make new bodies appear on top of pevious bodies.

I guess you could solve this by creating a new layer per sprite, but I don't know the implications of that on performance and such.
Is having thousands of asset layers with 1 sprite in each asset layer a good idea?

How big are the levels? Could you use a surface and just render in order to it? Then you don't have the expense of drawing all these things over and over, you just have one surface. Doesn't even have to be the same resolution I guess....
Judging by the gif Jaqueta posted, they're pretty large.

If a surface is used as a solution here, some datastructure keeping track of every sprite has to be maintained, in case the surface gets freed by the OS.
This datastructure could be a grid that keeps track of the sprites per screen size of the level.
The surface can then just be slightly larger than screen size and redraw a bunch of sprites every time it moves sufficiently.
Seems like it'd become independent of level size then.
 

CMAllen

Member
This is not a bug. We have added a specific list order to the IDE to ensure that what you see is what you get, to allow users simple editing. But this is pre-compile step.
That doesn't make any sense. Why would one function different from the other? What possible advantage is gained by having it ordered one way in the room editor and the complete opposite in code?
 
J

Jaqueta

Guest
if you want blood created below, make a blood layer. if you want bodies crated above, then create a body layer.
As I mentioned multiple times, this is not what I am trying to achieve, I want sprites to stack on top of each other as they are created.
This gif should explain: https://imgur.com/a/EAIik

How big are the levels?
Large, some can go all the way up to 2560x8640.

That doesn't make any sense.
Exactly! xD
The sad part is that the code doesn't allow for flexibility. @Mike, please consider improving the functions.

I'm buying GMWolf's Asset, I'll let you guys know about my results when I finish up implementing the asset.
 

Mike

nobody important
GMC Elder
This isn't new or different from 1.x, the same "rule" applied there as well. If you wanted something above or below you had to set the depth appropriately. Anything at the same depth did not have a guaranteed order.

We added a feature to the IDE to make things easier for editing, but after that, each platform will optimise each layer how it sees fit. This can be sorting by texture, shader - whatever.
 

CMAllen

Member
This isn't new or different from 1.x, the same "rule" applied there as well. If you wanted something above or below you had to set the depth appropriately. Anything at the same depth did not have a guaranteed order.

We added a feature to the IDE to make things easier for editing, but after that, each platform will optimise each layer how it sees fit. This can be sorting by texture, shader - whatever.
Actually, it always had a specific order -- the order in which object/instances are created/added. This is something I've seen and dealt with first hand. It is 100% consistent. But for some inexplicable reason this behavior is the exact opposite of what adding assets is doing when done via code, hence my question -- Why is it behaving like that, when no other part of GM does? Every other part is first in, first out. Even the asset layer in the room editor is first in, first out. But assets added via code are first in, LAST out.
 

Mike

nobody important
GMC Elder
No, it didn't. Here it is from the 1.x manual, under the "Depth" section.

http://docs.yoyogames.com/index.html?page=source/dadiospice/000_using gamemaker/008_defining objects.html

It should be noted here that instances of the same depth that overlap each other in the room editor (or in game) have no guaranteed priority over which one will be "on top" of the other and. If you want to guarantee that an object lies on top of the others then you must give it a negative (or low) depth. Conversely If you want to make sure it lies below other instances then you have to give it a positive (high) depth. Depth can also be changed in-game as it has its own variable that can be changed and manipulated by actions or in code.
So this rule has been there for about 6 or 7 years now....
 

CMAllen

Member
No, it didn't. Here it is from the 1.x manual, under the "Depth" section.

http://docs.yoyogames.com/index.html?page=source/dadiospice/000_using gamemaker/008_defining objects.html


So this rule has been there for about 6 or 7 years now....
This not true and easily demonstrated. There is a specific, set order in which draw calls populate the list the engine uses to execute a complete draw loop. There is also a set, specific method by which new draw calls are added to that list. The list is not random. It will behave in a consistent, predictable manner, from session to session. This list is also not something the user controls unless they assert control over it. But this isn't about taking control of how the engine handles that list. It's about why the behavior is different between the room editor and code functions.

And just to be sure, I went back into GMS2 before posting this to test for myself, and everything added at runtime populates that list in the reverse order that the room editor uses. Run-time additions use first in, last out -- the latest addition are the first to be drawn. Always. Room editor additions use first in, first out -- latest additions are the last to be drawn. Always. What is the cause of this inconsistency in behavior? If you can insert a new entry at either the top or the bottom of list, why would run-time behavior be one way while room editor behavior operates in the opposite way? Clearly the functionality exists to determine which end of the list receives new additions, so I'm trying to understand why this is not a bug but a deliberate decision to be inconsistent.
 

Mike

nobody important
GMC Elder
Just because this is what currently happens, does not mean that method was protected. This rule was in the manual for a reason.

The layer stuff is basically a linked list of items - per layer, of things to render, so I suspect that new items are added to the start of that appropriate chunk. This is again a performance gain as we don't need to search to the end of the list of a specific block of items.

As we move from platform to platform, allowing reorganisation of things on the same layer is a potential performance boost. Also, if we'd discovered that we could get a speed boost by grouping like-wise instances together at a single depth, we were then free to do so. Similarly, if we wanted to batch shader callers, or texture changes this would also give a speed boost. These are still on our todo list for some platforms.

Lastly.... if you need "utter" control over rendering, then you always have vertex buffers. You can build up whatever you like - however you like, with these, and they will render incredibly quickly. Far faster than rendering instances. Then it's up to you how you add things to them. I'd suggest having one for blood and one for bodies, then the order you want will be preserved.
 

CMAllen

Member
Just because this is what currently happens, does not mean that method was protected. This rule was in the manual for a reason.

The layer stuff is basically a linked list of items - per layer, of things to render, so I suspect that new items are added to the start of that appropriate chunk. This is again a performance gain as we don't need to search to the end of the list of a specific block of items.

As we move from platform to platform, allowing reorganisation of things on the same layer is a potential performance boost. Also, if we'd discovered that we could get a speed boost by grouping like-wise instances together at a single depth, we were then free to do so. Similarly, if we wanted to batch shader callers, or texture changes this would also give a speed boost. These are still on our todo list for some platforms.

Lastly.... if you need "utter" control over rendering, then you always have vertex buffers. You can build up whatever you like - however you like, with these, and they will render incredibly quickly. Far faster than rendering instances. Then it's up to you how you add things to them. I'd suggest having one for blood and one for bodies, then the order you want will be preserved.
Okay, so that makes a little more sense, but if you have a pointer to where your linked list begins, is really that big of an impact to have a pointer to the last entry as well? Or, wait. Let me go out on a limb and guess that the linked list isn't traversable in both directions. It can only be read from start to finish. So if you delete the last entry in the linked list, you have no way of updating last entry pointer to the previous entry in the list without iterating through the entire list again. If that's the case, then I get it. There's definitely going to be an increase in the size of the linked list if you need all entries to keep track of both the previous and next entries. But still, even with hundreds of thousands of entries, that's less than a 1mb increase in memory allocation. Which, I guess, could cause issues for the mobile platforms that GMS supports, so the list needs to be kept as lightweight as possible.
 

Mike

nobody important
GMC Elder
As I've said. We have specifically reserved this rule for years so we can optimise rendering more where and when we need to. Allowing sequenced addition would break this rule.

Vertex Buffers let you do exactly what you want to do, and will be faster than any other method anyway.

We are not looking to change this.
 

GMWolf

aka fel666
Vertex Buffers let you do exactly what you want to do, and will be faster than any other method anyway.
not unless you don't know what you are doing. I have used them naively for quite a while, totally ignoring batching, etc.

This not true and easily demonstrated.
As I've said. We have specifically reserved this rule for years so we can optimise rendering more where and when we need to.
There is a reason as to why I often comment on the use of undefined behavior in GM. (like referencing variables through objects rather than instances. What instance will be accessed? who knows?!)

I will plug my asset: https://marketplace.yoyogames.com/assets/1794/optimized-decals
Uses vertex buffers to draw things like bodies and blood splatters really, really fast.
Supports a few extra things, like having them fade, over time, etc.
Optimized to use very few draw calls.
 

CMAllen

Member
We have specifically reserved this rule for years so we can optimise rendering more where and when we need to.
Okay. I get that now. Perhaps the warning could be be worded better, but it gets the point across. "Any kind of automatic sorting behavior you may observe is subject to change at any time due to new methods or optimizations made to the drawing pipeline, and as such, should not be relied upon or built around."
 
Top