Skip to main content

Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
TagsGame Engines

Any Optimization Tips When Writing Thousands of Models to Static Buffer?

A topic by WideWorldGames created Apr 15, 2024 Views: 151 Replies: 3
Viewing posts 1 to 4

My game rooms can have up to a few thousand models, which, to my understanding, shouldn't be a problem for the Fauxton engine. When writing to the static buffer, performance sits around 10-12fps for 30+ seconds every time you enter a new room.  Once the static buffer is loaded, game performance returns to normal. For a game with small rooms, that amount of loading would become a QOL problem very quickly.

It seems like most of the performance issues are being caused by the many iterations of buffer_write() - around 80-90% of the time taken to reach full 60fps is spent on buffer_write(). Is this normal and unavoidable given the amount of sprites I have in my room?

Thanks in advance.

Hey! Yes, saving vertex buffers after they are initially written and loading only those VBuffs would save an enormous amount of time. GML is not the fastest Lagrange to read/write with so you may want to implement a caching system yourself using the static buffers that you could easily load using buffer_load() and vertex_create_buffer_from_buffer()

(2 edits)

Thanks for the response! I'm a bit of a beginner to buffers so I'm trying to understand - is this to say if I have 1000 instances of the same tree, I really only need to create the model (write the VBuffs) once and then create a system that can save and load this information on each subsequent iteration of the tree? I guess I'm asking how certain videos of this system can handle so many models at once without spending an initial 1+ minute on loading each room.

Any tips on where to get started would be really appreciated!

Edit: Also, oddly, when I change the models to 2d billboarded sprites, the game's slowdown is still very significant due to calling so many iterations of matrix_build each frame. Is this normal? I feel like I might be doing something wrong that's causing all this slowdown

Hey! If you're still having trouble with this, maybe the solution will be what I've implemented for myself. In the load_buffer_maps() function, I've implemented the option of saving static buffers to disk and loading them. My maps have a lot of models because they're quite classically modular platformers, each block is one model. Without static buffers, the game maintains 50-60 frames, but with static buffers it jumps to ~130. After creating a map, it doesn't modify it, so the option of saving buffers and loading them from disk when loading the map makes perfect sense. It could probably be further optimized or the buffers could be compressed to take up less space, but for now it works for me. When you want to load buffers using this method, all you have to do is create static buffers and delete the original models as you would when creating static buffers normally, and the rest will be done by the code I'm posting below. The path to buffers for me consists of the level name so that the game knows when to load which buffers and the buffer name. If you already have the code responsible for creating static buffers, the code I'm sending is ready to go, all you need to do is change the path to the files and create two variables: global.loadStaticBuffers and global.saveStaticBuffers. I hope I helped, and even if not, maybe if someone comes across this thread, they will find some usefulness in it :D


function load_buffer_maps() 
{
    if (!global.loadStaticBuffers)
    {
        // Load all buffer maps
        for ( var i=ds_map_find_first(BUFFER_MAPS); !is_undefined(i); i = ds_map_find_next(BUFFER_MAPS, i) )
        {
            var b = BUFFER_MAPS[? i];
            if ( !b.loaded )
            {
                // Load and write to buffers 
                var l = b.load_pos + b.load_chunk;
                l = clamp(l, 0, ds_list_size(b.load_queue));
                for ( var j = b.load_pos; j<l; j++ ) 
                {
                    b.load_pos++;
                    var m = b.load_queue[| j];
                    __FauxtonWriteStaticSpriteStack( b.buffer, m[0], m[1], m[2], m[3], m[4], m[5], m[6], m[7], m[8], m[9]);
                }
                // Complete loading
                if ( b.load_pos == ds_list_size(b.load_queue) )
                {
                    show_debug_message("Buffer '"+string(i)+"': Succesfully Loaded! ");
                    b.loaded = true;
                    ds_list_destroy(b.load_queue);
                    if ( b.buffer < 0 ) { buffer_delete(b.buffer); b.buffer = -1; exit; }
                    b.vertex_buffer = vertex_create_buffer_from_buffer(b.buffer, SYSTEM_VERTEX_FORMAT);
                    if (global.saveStaticBuffers)
                    {
                        buffer_save(b.buffer, get_project_path() + "content/buffers/" + string("{0}_{1}.sav", o_debugController.gameLevelName, i));
                    }
                    buffer_delete(b.buffer);
                    vertex_freeze(b.vertex_buffer);
                }
            }
        }
    }
    else 
    {
        for ( var i=ds_map_find_first(BUFFER_MAPS); !is_undefined(i); i = ds_map_find_next(BUFFER_MAPS, i) )
        {
            var b = BUFFER_MAPS[? i];
            if ( !b.loaded )
            {
                b.buffer = buffer_load(get_project_path() + "content/buffers/" + string("{0}_{1}.sav", o_debugController.gameLevelName, i));
                b.loaded = true;
                ds_list_destroy(b.load_queue);
                if ( b.buffer < 0 ) { buffer_delete(b.buffer); b.buffer = -1; exit; }
                b.vertex_buffer = vertex_create_buffer_from_buffer(b.buffer, SYSTEM_VERTEX_FORMAT);
                buffer_delete(b.buffer); 
                vertex_freeze(b.vertex_buffer);
            }
        }
    }
}