GMS 2.3+ Textures break when sprite_delete is included

RoanArts

Member
I'm working on an interactive fiction game engine, with a text parser I've written to be able to correctly draw text with different types of emphasis, as well as text colors, and so on. All of this is working perfectly. Because of the book-style layout, I use surfaces to let me draw sections of text within frames which can then be scrolled. In addition, because GMS2 doesn't have the text functions I need, the way my system works is to draw the parsed text to a surface, make a sprite out of that surface, and then draw the created sprite within the appropriate frame. I'm very careful to make sure that I use surface_free and sprite_delete to keep a handle on all of this, since I'm creating sprites for every page of text.

This is what it should look like, and it's what it correctly looked like up until runtime v2.3.1.406 was released.

memory leak.png

Since runtime v2.3.1.406, I've run into a problem where the visuals, for lack of a better term, just break.

texture corruption.png

I've spent days going line by line and trying to isolate what is causing this, and I finally found the culprit: If I call sprite_delete anywhere in my code (which I need to do in order to remove the sprites I'm creating of the page text), the visuals break as in the above image. If I do not call sprite_delete anywhere in my code, everything looks the way it should--however, obviously then I have a memory leak because I'm not deleting the sprites I'm creating.

Again, none of this was happening prior to runtime v2.3.1.406, and this problem came up as soon as I updated to the new runtime; I had not changed a single piece of code. I have tried the project cleanup button multiple times. I have tried flushing sprites and textures. I've tried everything I can think of, and the only thing that fixes it is to not call sprite_delete, which is something I can't do because of the memory leak it causes.

I thought I saw that runtime v2.3.1.406 made changes to the garbage cleanup part of GMS2? Could there be a problem with that? The problem, as near as I can tell from looking at the visuals, seems to be that surfaces become corrupted and no longer function properly. I'm at my wits end. I would be deeply grateful to anyone who has insight on this.
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Moderator
Could you post the code (or a sub-section) that is relevant to the issue? Ie: how are you creating the sprites and how (and when) are you deleting them?
 

RoanArts

Member
Absolutely! There are only two places where the sprites are created or deleted. Both are below, in annotated images.

sceneloader.png

In the above image, I know it appears odd to start with the [1] entry in the arrays instead of the [0] entry, but this is done so that the page number within a scene always references the matching array entries; i.e. page 1 references entry [1] in all relevant arrays. Entry [0] is never referenced or used. This works, and doesn't seem to have any impact on the problem, but I'm addressing it for the sake of thoroughness.

parser.png

What's interesting is that the sprites created aren't broken; it's something about them being deleted which breaks things--and what breaks is always the surfaces the game is using to draw the various parts of the page being displayed.

Thank you in advance for any help. I also appreciate this being moved to the correct forum.
 
Last edited:

Nocturne

Friendly Tyrant
Forum Staff
Admin
Moderator
Okay, on the surface the code looks sound, however there is one thing that could potentially be the issue here and that's re-using the same variable for different sprites, and creating multiple sprites and then deleting them all in the same event. Now, I know you say the code worked before, so I suspect this may be a bug, or YYG have changed something internally in the render pipeline, but there are a couple of things you can do to check...

The first thing I'd do is add a breakpoint to the final sprite_exists check in the function, then run the code in the debugger and step through it a line at a time. You can then check the value stored in the workhorse_sprite var, AND in the Graphics/Textures tab of the debugger, you can refresh it each step and see what the current textures and surfaces look like. This will permit you to see the reference stored in the var change each iteration of the funtion, as well as see the contents of the temp surface and the newly created sprite each step.

The second thing you can do is create a simple array for the workhorse_sprite and increment it as each new sprite is created, storing each sprite in a unique array slot. This can then be "garbage collected" manually in an alarm or something. It's a terrible solution, but it'll also help pinpoint whether the issue is caused by the re-use of the same variable.

After all that, you may have some further insight into the issue and be able to fix it or at least file a bug report about the change in behaviour. :)
 

Yal

šŸ§ *penguin noises*
GMC Elder
My guess is that the garbage collection that frees the sprite memory runs in a background thread now, and since you reuse the same sprite index, it ends up deleting the new sprite because we're not guaranteed atomicity anymore.

Here's an idea: reorder your code so that you first create the new sprite, then delete the old (you could store the old sprite index in a new var to allow them to coexist), this way the new sprite is guaranteed to have a new index (because the old is still in use at the time of its inception) and hopefully the deletion has finished the next time you need to create a new sprite.
 

RoanArts

Member
Thank you both so much for the input and suggestions, I really appreciate you taking the time to offer them! I'll try these ideas out and update this thread with the results.
 

chamaeleon

Member
My guess is that the garbage collection that frees the sprite memory runs in a background thread now, and since you reuse the same sprite index, it ends up deleting the new sprite because we're not guaranteed atomicity anymore.

Here's an idea: reorder your code so that you first create the new sprite, then delete the old (you could store the old sprite index in a new var to allow them to coexist), this way the new sprite is guaranteed to have a new index (because the old is still in use at the time of its inception) and hopefully the deletion has finished the next time you need to create a new sprite.
What a terrible state of affairs this would be if it is true (I have no idea one way or the other if it is).
 

RoanArts

Member
Okay, update time!

I tried out the ideas presented; the short version is that they didn't work, but they've introduced an interesting wrinkle. Using either suggestion, I can get through three 'cycles' of deleting the sprites and creating new ones, before the surface visuals break. To recap what I tried:

attempt1.png
^ This was the suggestion to save the last sprite ID to a temp var, create the new sprite so it'd use a new ID, then delete the old ID by referencing the temp var.

attempt2a.png
attempt2b.png
^ This was the suggestion to use workhorse_sprite as an array of IDs instead of a single variable, and manually clean up the array at a certain time. I put the cleanup in the function which cleans up all the other sprite arrays, since that part seemed stable with regard to this problem.

As stated above, neither method actually fixed the problem, but they both introduced the same wrinkle. When the game first starts, it looks correct. I can cycle through three sprite refreshes (delete and re-create), and on the fourth refresh, the problem returns.

I've reverted these code changes back to my initial code, to keep things simple while searching for a fix. My next step is to go frame by frame using a breakpoint as suggested, and see what the workhorse_sprite variable and the texture pages look like each time. Will update with the results of that.

Thank you for your continued help and advice!
 

RoanArts

Member
Another update!

I went through the debugger and kept an eye on the ID in the workhorse_sprite variable, as well as the texture pages, as things went. The ID seems to be fine; it increments the expected number of times for the sprites in play. However--and I'm not certain if this is expected behavior or not--but the texture pages corresponding to some of the created sprites seem to be... odd:

texture-pages.jpg

Any thoughts or insight on this would be appreciated; I can't tell if this is abnormal with how GMS2 uses texture memory / texture pages, or what it means in the case of this issue. Thank you!
 

Nocturne

Friendly Tyrant
Forum Staff
Admin
Moderator
That looks like what I'd expect to see if the surface being used isn't being cleared before being drawn to. Looks like the kind of VRAM noise you get from that... that said, I don't think this is relevant to your issue. At this stage, I'd file a bug report tbh... The fact that the behaviour has changed between versions strongly suggests a bug, and if it's NOT a bug but a change in how something works, then that needs to be documented and communicated to users.
 

RoanArts

Member
Awesome! I mean, obviously not awesome because bugs are frustrating, but awesome because I probably didn't screw something up! šŸ˜† Thank you again for all your help and insight; I'm off to file a bug report!
 
Top