I’ve done a good deal of talking with some of the more adept Game Maker programmers the past couple of days, in fact much of what made my current iteration of GMD+ actually work was thanks to a signing swap code provided by Yourself, from the Yoyo Games forum. As I’ve talked to Yourself, as well as Xot, another moderator from the forums, It’s been suggested, briefly, by both of them, that I should look into the new buffer functions built into GameMaker Studio. Before I go too far into what exactly a buffer is, I’m going to talk a bit about what it takes to currently read variously sized integers from a binary file as is.
Firstly, the data that’s being loaded from a wad file at the current progress is as follows: the Header, which consists of a 4 byte string, followed by two unsigned integers, 4 bytes each; the Directory, which consists of two unsigned integers 4 bytes each and an 8 byte string multiplied by a couple thousand times for the number of lumps in the wad; and much more, too much at the moment for me to feel like typing it all out. What this basically means is that there are literally thousands of bytes of information being loaded into GMS even at this incomplete iteration. Currently E1M1 loads incomplete in 0.12 seconds, and E1M3 in 0.2 seconds. This may not seem like much time, but when combined with loading texture data, reconstructing the textures in game, constructing sounds, building the levels, and constructing the 3d models that make up the gl_sectors, loading time increases exponentially which is why its very important to shorten up the loading time at the beginning, before you even start assembling the textures, sounds, and meshes.
How can I speed up data reading? Let’s take a look at how you’d read the various data formats inside a binary file using the file_bin_read_byte() functions in GM. Let’s say you need to read an unsigned 16 bit integer, also known as a short. A short is made up of two bytes. As you can guess, you will have to call file_bin_read_byte() twice, once for each byte, then on top of that you have to convert both of those bits of information into the final product. This becomes even more tasking when you have to read 32 bit integers because you have now to read twice the number of bytes and perform even more calculations to convert the data into a usable format. Doing these once or twice won’t impact performance very much, but when you start reading hundreds and thousands of them it can really hinder development. What is the alternative?
Buffers are a new addition to the GameMaker core, and much of what the manuals mention in reference to buffers are creating file saves for games quickly, creating temporary checkpoints in games, and saving created data in game. One of the things mentioned in the manual is that you can load “the buffer data that was previously saved using the buffer_save functions” from a binary file. What the manual fails to mention is that the file you’re loading from doesn’t need to be created by using the buffer_save functions, but ANY binary file can be loaded into a buffer, including a classic Doom WAD. What makes a buffer so much quicker than all of the bytes loading and converting that I was doing prior is that buffers have built in functions for reading specific types of data, from unsigned 8bit integers to signed 64bit integers, it’s all hard coded and all I have to do is write buffer_read(buffer,data_type).
The downside? There’s only one problem that I’ve come across while exploring buffers and that is how you read strings stored in the buffer. GameMaker assumes that your strings are null terminated, meaning that the byte immediately following your string is 0. This does not work when loading a Doom WAD because strings are not null terminated, rather they are length dependent. Using buffer_read(buffer,buffer_string) to read a 4 byte string would possibly return something that consists of several more bytes, and completely offsetting the position in the buffer and will result in erroneous values loaded afterwards. To remedy this a custom script was created that essentially reads a byte at a time from the buffer dependent upon a length argument, and returns the resulting string. This is not ideal as I’m manually seeking through bytes as I was before, however, this happens much less often now.
I’ve just begun converting to buffers, but already data is loading properly from the buffer. After swapping out all of the binary file reading with buffer reading I will post the differences it makes in loading times.
Feel free to comment with any questions you may have and keep your eyes glued here for more progress!