Confound These Schedules
by Justin Proffitt · in Torque Game Builder · 09/22/2011 (12:17 pm) · 5 replies
They drive me to insanity! Here is the situation, I needed a way to safely delete t2dsceneobjectgroups and all their contents since they do not have their own "safeDelete" method, so I wrote a pseudo safeDelete function for simsets:
It basically just passes the "safeDelete" method down to what is inevitably t2dSceneObjects, then hard-deletes itself once it is certain there won't be any dangling calls below (like objects attempting to remove themselves from the simset). The issue is that when a scene gets loaded in the middle of this (such as often happens when switching screens), it all goes south. Understandably, if the first safeDelete() doesn't end up deleting the object, and a level begins loading or an intense script begins executing before the next call, that call doesn't get processed until some time after the loading/script finishes. So there is a gap of about 250 ms between the first and second calls, however, from there it goes downhill: It begins attempting to delete every millisecond or several times a millisecond, for hundreds of milliseconds. This causes performance issues that increase exponentially with the intensity of the script/function that interrupted the schedules
e.g. a level that takes 2 seconds to load, then takes 2.5, but a level that takes 8 seconds to load, then takes 30-40 seconds.
I haven't a clue exactly what is going wrong here, and actually I would appreciate just, if nothing else, understanding a bit better how schedules deal with delays and what not. Right now I feel a bit like I am throwing code at my game and hoping it sticks. So if anyone has any enlightenment on this issue or the subject in general, I would love to hear it.
function SimSet::safeDelete( %this ){
echo("Attempting delete of " @ %this @ ", at " @ getScriptTimer() @ " ms");
if(!%this.deleting){
%this.deleting = true;
for(%i = 0; %i < %this.getCount(); %i++){
%this.getObject(%i).safeDelete();
}
}
if(%this.getCount() == 0){
echo("Successfully deleted " @ %this @ " after " @ %this.attempts @ " attempts");
%this.delete();
}
else {
%this.schedule(50, "safeDelete");
%this.attempts += 1;
}
}It basically just passes the "safeDelete" method down to what is inevitably t2dSceneObjects, then hard-deletes itself once it is certain there won't be any dangling calls below (like objects attempting to remove themselves from the simset). The issue is that when a scene gets loaded in the middle of this (such as often happens when switching screens), it all goes south. Understandably, if the first safeDelete() doesn't end up deleting the object, and a level begins loading or an intense script begins executing before the next call, that call doesn't get processed until some time after the loading/script finishes. So there is a gap of about 250 ms between the first and second calls, however, from there it goes downhill: It begins attempting to delete every millisecond or several times a millisecond, for hundreds of milliseconds. This causes performance issues that increase exponentially with the intensity of the script/function that interrupted the schedules
e.g. a level that takes 2 seconds to load, then takes 2.5, but a level that takes 8 seconds to load, then takes 30-40 seconds.
I haven't a clue exactly what is going wrong here, and actually I would appreciate just, if nothing else, understanding a bit better how schedules deal with delays and what not. Right now I feel a bit like I am throwing code at my game and hoping it sticks. So if anyone has any enlightenment on this issue or the subject in general, I would love to hear it.
About the author
I am a college student majoring in physics. As it so happens I like programming, a hobby which extends my profession in web design.
#2
Original safeDelete is safe because it does not necessarily deletes object it is called for right in place, some objects are just marked for deletion. If an object is deleted, your for loop jumps over next one, thus the innatural need to reschedule your function. If an object is just marked however, then it will remain in simset and possibly processed again on next call.
Now, if your simset contains other simsets, it is possible that safeDelete called more than once for them, each call producing it's own chain of schedules. You can get yourself a nice avalanche this way.
09/23/2011 (1:08 am)
@Justin If you move getCount() outside of your for loop, and make it go from the end of the set towards it's zero index, you should be able to delete all objects in one call and avoid schedules altogether.Original safeDelete is safe because it does not necessarily deletes object it is called for right in place, some objects are just marked for deletion. If an object is deleted, your for loop jumps over next one, thus the innatural need to reschedule your function. If an object is just marked however, then it will remain in simset and possibly processed again on next call.
Now, if your simset contains other simsets, it is possible that safeDelete called more than once for them, each call producing it's own chain of schedules. You can get yourself a nice avalanche this way.
#3
The way I did a safeDelete-like system for my own game engine and libraries in C++ is to keep a list of objects (called the queue) that need to be removed and then while-loop through them like above and delete them and remove them from the list. They were added like IObject::addToDeletionQueue().
Maybe that can give you an idea.
09/23/2011 (8:07 am)
Or, better yet, you could do it like this:while(%this.getCount() > 0)
{
%this.getObject(0).safeDelete();
}The way I did a safeDelete-like system for my own game engine and libraries in C++ is to keep a list of objects (called the queue) that need to be removed and then while-loop through them like above and delete them and remove them from the list. They were added like IObject::addToDeletionQueue().
Maybe that can give you an idea.
#4
Better also remove deleted object from set there.
09/23/2011 (11:59 am)
while(%this.getCount() > 0)
{
%this.getObject(0).safeDelete();
}Better also remove deleted object from set there.
#5
In this case, I designed this specifically to delete trees of simsets containing objects at their lowest levels. I just realized I could forego the entire rescheduling procedure and remove the objects from the simset as I mark them for deletion, removing the need to keep the simset around while its children delete. So instead of deleting from the bottom up, it would dissolve the simsets from the top down. I'm thinking...
09/23/2011 (6:40 pm)
Thanks for pointing that out, I am embarrassed that I didn't account for that.In this case, I designed this specifically to delete trees of simsets containing objects at their lowest levels. I just realized I could forego the entire rescheduling procedure and remove the objects from the simset as I mark them for deletion, removing the need to keep the simset around while its children delete. So instead of deleting from the bottom up, it would dissolve the simsets from the top down. I'm thinking...
function SimSet::safeDelete(%this ){
while(%this.getCount() > 0){
%obj = %this.getObject(0);
%obj.safeDelete();
%this.remove(%obj);
}
%this.delete();
}
Torque Owner Andrew H