Ok after some tests there seems to be something strange with some of the deconstructors and the OpenGL contexts. Lets start with some sample code.
This first example causes a memory leak. I also included the Textures decontructor to show which descontructor is being called.
static void Main
(string[] args
) { for (int c
= 0; c
< 10000; c
++) { SFML
.Graphics.Texture text
= new SFML
.Graphics.Texture(@"...file path..."); } } ~ObjectBase
() { Dispose
(false); } Now here is an example that does not have a memory leak. And the decontructor for reference.
static void Main
(string[] args
) { for (int c
= 0; c
< 10000; c
++) { SFML
.Graphics.Texture text
= new SFML
.Graphics.Texture(@"C:\Users\Zachariah Brown\Pictures\96134.jpg"); text
.Dispose(); } } public void Dispose
() { Dispose
(true); GC
.SuppressFinalize(this); } This code has the same memory leak regardless if you use a Texture, Font, or RenderTexture. The difference between the two decontructors is the
Dispose(bool) call. Now when we break into that Dispose call we will see the following.
private void Dispose(bool disposing)
{
if (myCPointer != IntPtr.Zero)
{
Destroy(disposing);
myCPointer = IntPtr.Zero;
}
}
protected abstract void Destroy(bool disposing);
The memory leak happens in the implementation of the
Destroy(bool) call. Lets look at the Textures's Destroy implementation.
protected override void Destroy(bool disposing)
{
if (!myExternal)
{
if (!disposing)
Context.Global.SetActive(true);
sfTexture_destroy(CPointer);
if (!disposing)
Context.Global.SetActive(false);
}
}
Look at that
Context.Global.SetActive(bool) call. Now if look back to the example that does not cause the memory link you will notice Destroy will be called with this.
Destroy(true);
Since Destroy is called with true that means we make 0 calls to the OpenGL context. The memory leak comes from calling
SetActive(bool) on the OpenGL context. More specifically when
Context.Global.SetActive(false); is called. As soon as you comment out the
SetActive(bool) call the first example that originally leaked memory no longer leaks.
Now this brings out a few questions (aimed at Laurent).
- Why is SetActive(false); leaking memory? I took from this thread that the OpenGL contexts are only supposed to leak memory when multiple threads come into play.
- Why are the deconstructors calling Dispose(bool) differently? Shouldn't the unmanaged objects be disposed in the same way regardless if they go out of scope or have the decontructor explicitly called?
On a side note Laurent, I also took a look at the way the .NET bindings handles contexts and all of the contexts use a single global context.
public static Context Global
{ get { if (ourGlobalContext
== null) ourGlobalContext
= new Context
(); return ourGlobalContext
; } }private static Context ourGlobalContext
= null; So if the bindings only ever use 1 context, why is there even a need to call
SetActive(bool) on the global context?