Chaos Project

Featured Projects => Advanced RPG Creator => ARC Reactor Engine => Topic started by: Blizzard on March 23, 2011, 02:48:06 pm

Title: Hardcode GC coding
Post by: Blizzard on March 23, 2011, 02:48:06 pm
Alright, it's time for the next step. I have successfully managed to implement a Bitmap that loads a file properly. I have also made the part in Sprite work that lets it draw the Bitmap. Here comes the tricky part and you have to be careful when you code. I will explain everything.



1. Let me first explain what gc_mark and gc_free functions will be used for. The GC (garbage collector) works like this: It has a mark-phase and a collect-phase. During the mark phase it marks all objects accessible from Ruby. We have to mark C++ objects manually so the GC doesn't destroy them. Basically when an object is not marked during the mark-phase, the GC assumes that all references are lost and that it's time to delete that object. The collect-phase then simply deletes all inaccessible objects. At that moment the gc_free function is called to clean up additional stuff if necessary.



2. This is what we will use the ClassName::gc_mark function for; we will mark C++ objects that are referenced by this object. Here is a simple example:

// Sprite::rb_bitmap is of type VALUE! The reason is explained later.
void Sprite::gc_mark(Sprite* sprite)
{
   if (!NIL_P(sprite->rb_bitmap))
   {
       rb_gc_mark(sprite->rb_bitmap);
   }
}


This makes sure that the bitmap is no immediately destroyed when Ruby loses all references to that object.



3. gm_free won't be necessary in most cases. A rare case is the Bitmap class that allocates an external object. It allocates an april::Texture instance and has to get rid of it once the bitmap itself has been destroyed.



4. One of the first things I noticed is that there are problems during the GC's mark phase if you create and kind of new variable. Even though using Data_Wrap_Struct does not actually create a new object but only wraps a C++ object into a Ruby object, it seems that on the Ruby side it is considered a new object. Basically gc_mark functions crash the GC (and therefore the whole game) if Data_Wrap_Struct is called during the gc_mark call. This is one thing to keep in mind.



5. I have figured out a simple workaround for the previous problem, but it will require us to be extra careful with the code. Basically we will have to have a Ruby object reference to C++ objects in our objects. The Sprite class with the Bitmap class is a good example for that.

class Sprite
{
   Bitmap* bitmap; // C++ Bitmap object
   VALUE rb_bitmap; // Ruby Bitmap object
}


As you noticed under 2., I have been using the rb_bitmap when I was marking it for the GC. Sadly we will have to do this. I remember that RMXP was able to crash if you call GC.start at some points. That made me think a bit. I think that Enterbrain didn't do that part actually right. It even makes quite a bit of sense. I remember when I tried to implement a cache emptying functionality in CP and it would crash with an unusual error message even though all Ruby references to the bitmaps have been removed.



I'm going to post a new task after I have finalized the last few things and committed everything.

EDIT: I have also added section separations to the Color and Sprite class' source code. When you work on another class, feel free to do the same.
Title: Re: Hardcode GC coding
Post by: Blizzard on April 07, 2011, 10:00:45 am
I have gotten rid of the wrap functions, they are not needed anymore.

What are we going to do about garbage collection? I can theoretically run it every time Graphics.update is called, but I think this is unwise. How long does it take RGSS to destroy a dereferenced Sprite that wasn't disposed previously?

EDIT: How often does ti take for the game to figure out a dereferenced sprite anyway?
Title: Re: Hardcode GC coding
Post by: Ryex on April 07, 2011, 06:12:09 pm
supposedly ruby runs GC automatically when it detects dereferenced objects. perhaps we could have sprite increments a static class variable that stored how many sprites are disposed but not garbage collected. and the gc_free method could deincrement it. we could have the graphics update methods check if that variable was large enough and run the GC if it was.
Title: Re: Hardcode GC coding
Post by: ForeverZer0 on April 07, 2011, 07:01:29 pm
I think it takes less than 5 seconds. I found this out by creating a module with a singleton method to create a window in a local variable. When the method was called in a call script, it would only show for about the length of time then disappear, presumably when the GC found that it was unreferenced and deleted it.
Title: Re: Hardcode GC coding
Post by: Blizzard on April 08, 2011, 02:35:22 am
I know that our Ruby doesn't ever run the GC automatically as far as I have noticed. If I dereference a sprite in the test script and run the main loop, the sprite stays there the whole time. If I run GC.start via Ruby occasionally, it gets removed, though.
Title: Re: Hardcode GC coding
Post by: Ryex on April 08, 2011, 03:11:08 am
so perhaps a call to GC.start every 1-2 seconds would work.
Title: Re: Hardcode GC coding
Post by: Blizzard on April 08, 2011, 03:57:34 am
I did that already. I put in Graphics::rb_update a call every 200 frames.
Title: Re: Hardcode GC coding
Post by: Ryex on December 15, 2012, 05:06:31 am
I cant remember if we were still having problems with the GC or not but I found this.
http://stackoverflow.com/questions/12452069/embedded-ruby-gc-and-wrapped-structs
Title: Re: Hardcode GC coding
Post by: Blizzard on December 15, 2012, 06:10:08 am
Yeah, I fixed it months before the beta release of the engine already.

The problem when I use xmalloc (like suggested in the post) is that it won't create proper C++ objects with virtual function tables.
Title: Re: Hardcode GC coding
Post by: Ryex on December 15, 2012, 06:14:58 am
ah, well, what do you know. shows what I know.