GameMaker ds_grid memory management

F

Finn

Guest
I am working on a simple version of a depth system (mostly drawing objects in order of y coordinate).
I am using a grid to manage the objects, sort them and then draw them in the right order.

I was careful setting up the data structure and managing it but encountered some strange memory leak with it nevertheless that I would really like to understand. Any advise is appreciated!

Code below. Roughly the simple depth system works like this:
  1. Write all instance ids of relevant objects and their y-values into a grid
  2. Sort that grid by y-value
  3. Iterate through the sorted grid and execute the draw event of each object in the grid
Code:

Code:
// --------------------------------------------------------------------------------

    // CREATE EVENT

    //    D S   G R I D   S T R U C T U R E
        ds_depthgridW   = 2;    if (DEBUG_MODE_ENABLED) { ds_depthgridW = 3;}    // number of columns
        ds_depthgrid      = ds_grid_create(ds_depthgridW, 1);
        ds_depthgridH   = 0;                                                    // number of rows
        ds_depthgrid_pointer    = 0;                                                    // pointer to last row that was populated

        
// --------------------------------------------------------------------------------


//   STEP EVENT

    var _grid = ds_depthgrid;

    //    Reset depth grid
        ds_depthgrid_pointer    = 0;
        ds_depthgridH        = 0;

    //    Get total number of instances to draw this step
            ds_depthgridH = instance_number(obj_depthSystem)     
             if (ds_depthgridH <=0) {exit;}   


//    Setup grid: resize & clear (!)
    ds_grid_resize(_grid, ds_depthgridW, ds_depthgridH);
    ds_grid_clear(_grid, noone);


    //    Populate Grid 
with(obj_depthSystem)  { sc_registerInst_with_depthGrid(_grid); }

// --------------------------------------------------------------------------------

// SCRIPT sc_registerInst_with_depthGrid()
   
 //    R E G I S T E R   I N S T A N C E   W I T H   D E P T H   G R I D 
        var _grid        = argument[0]; 
        var _pointer    = other.ds_depthgrid_pointer;
       
        //var _grid        = other.ds_depthgrid; 
        _grid[# 0, _pointer] = id;                                    // instance ID
        _grid[# 1, _pointer] = yDepth;                                // y-coordinate
        if (DEBUG_MODE_ENABLED) {
            _grid[# 2, _pointer] = object_get_name(object_index);        // object name
            }
        // increment draw counter for next instance in depthgrid within obj_masterDraw
        other.ds_depthgrid_pointer++;


// --------------------------------------------------------------------------------

//     DRAW EVENT

    //    D R A W   O B J E C T S   I N   O R D E R   O F    y D E P T H 
        //    A C T I V A T E   D R A W   E V E N T S
            var _grid    = ds_depthgrid;
            var _i        = 0;
           
            repeat(ds_depthgridH){
                var _inst = _grid[# 0, _i];
                with(_inst){
                    drawingActive = true; 
                    event_perform(ev_draw, 0);        // This will (exclusively) perform main draw event of respective object; 
                    drawingActive = false; 
                    }
              

                _i++;
            }

Now the problem is with the step event at (!) above:
If I first resize the grid and then clear it; memory usage will keep increasing constantly every step. (This becomes more dramatic if I write some extra long strings into the grid for debugging.)
First resizing and then clearing sounds like a good choice and it is done the same way in the GML documendation for ds_grid_resize();

Solution (?) : If I first clear and then resize the grid, memory usage will stay stable and not increase every step. I dont know if that is actually stable or just seems like it is fixed because when clearing I just write a simple integer into the grid which takes less memory than the original complex and heterogeneous data.

Code:
// "SOLUTION"?

//    Setup grid: resize & clear
ds_grid_clear(_grid, noone);
ds_grid_resize(_grid, ds_depthgridW, ds_depthgridH);

PS: I destroy the grid in the clean_up event of the object. I am also additionally destroying it at every room_end and creating a new grid at every room_start for safety now :S

PPS: The script above that writes each instance into ds_depthgrid; I am grateful for any recommendations for performance improvement.
 
T

Taddio

Guest
You're creating a grid every step and not destroying it. You create a temporary variable every step, which creates a grid every step (line 1)
If you just made it an instance variable (by removing var), you wouldn't have a leak anymore, provided you destroy it in the clean up event.
 
F

Finn

Guest
You're creating a grid every step and not destroying it. You create a temporary variable every step, which creates a grid every step (line 1)
If you just made it an instance variable (by removing var), you wouldn't have a leak anymore, provided you destroy it in the clean up event.
  • The purpose of the local variable is to reference a data structure in an efficient way. As far as I know the temporary variable will only carry the index of the grid but not duplicate it: Data structures are stored "in a scope of their own" and therefore every time you reference them it will look up the index. With the temporary variable this can be avoided. I am no expert in this topic but that is how I understood it.
  • The memory problem is indepentent of the use of the temporary variable.
  • I tested it and the introduction of the temporary variable gave a slight performance boost in terms of CPU (-2% usage) but made no change to memory.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Tbh, this sounds like it may be a bug with the ds_grid_resize function... it seems like it may be keeping the data from the resized grid in memory, which would explain why clearing it and then resizing removes the issue. Could you please file a bug report and link to this topic and also include a download link to a sample project that shows the issue? Thanks!
 

Ido-f

Member
I also don't see any reason for a memory leak but a bug.
Can I ask how do you make all of the instances' draw events not run by themselves, but only by the loop that iterates the depth grid?
Or is it that instances' draw events are performed twice per step?

Edit: oh, should have seen it, you have a "drawingActive" variable that controls it, kk
 
Last edited:

Nocturne

Friendly Tyrant
Forum Staff
Admin
What I do is simply add a comment into the Draw Event of the instances that you don't want to draw... this prevents them from default drawing and then you can draw them how you wish elsewhere.
 
T

Taddio

Guest
  • The purpose of the local variable is to reference a data structure in an efficient way. As far as I know the temporary variable will only carry the index of the grid but not duplicate it: Data structures are stored "in a scope of their own" and therefore every time you reference them it will look up the index. With the temporary variable this can be avoided. I am no expert in this topic but that is how I understood it.
I first went "Hmmm?!" reading that, but I did a test in a fresh session, and you're totally right! No idea what it can be, then, and sorry if I misled you, it wasn't my intention at all, bro!
 

TheouAegis

Member
It actually makes a lot of sense to me, the memory leak I mean. If you think back to what data structures rendered as strings look like, you essentially have a series of address pointers. So if you have 20 addresses that take up 64 bytes and 10 addresses that take up 128 bytes, when you upsize the grid (not sure about downsizing), it's going to set the address to the end of that home group of bytes. However if you first clear the grid, thensomething inside the grid structure tells it that all of those variables now only need to take up 32 bytes or whatever and so upsizing is only going to add to those. And then I would guess if the variable needs to take up any more bytes, it restructures the grid accordingly...? LOL I dunno.
 
F

Finn

Guest
It actually makes a lot of sense to me, the memory leak I mean. If you think back to what data structures rendered as strings look like, you essentially have a series of address pointers. So if you have 20 addresses that take up 64 bytes and 10 addresses that take up 128 bytes, when you upsize the grid (not sure about downsizing), it's going to set the address to the end of that home group of bytes. However if you first clear the grid, thensomething inside the grid structure tells it that all of those variables now only need to take up 32 bytes or whatever and so upsizing is only going to add to those. And then I would guess if the variable needs to take up any more bytes, it restructures the grid accordingly...? LOL I dunno.
I think you are right (but I cant proof it). I did some testing and the problem seems only related to grids that store strings.
 
F

Finn

Guest
I first went "Hmmm?!" reading that, but I did a test in a fresh session, and you're totally right! No idea what it can be, then, and sorry if I misled you, it wasn't my intention at all, bro!
No worries mate; appreciate your response!
 
F

Finn

Guest
Tbh, this sounds like it may be a bug with the ds_grid_resize function... it seems like it may be keeping the data from the resized grid in memory, which would explain why clearing it and then resizing removes the issue. Could you please file a bug report and link to this topic and also include a download link to a sample project that shows the issue? Thanks!
Thanks, I filed a bug report following your suggestion.
 
Top