Search code examples
c#garbage-collectionsfml

SFML - C# Garbage Collector deletes object which is in use


I'm using SFML for C#. I want to create a BackgroundImage Sprite and then start drawing it with an Agent, represented as a Circle, on top of it like that:

    static void Main(string[] args)
    {
        Window = new RenderWindow(new VideoMode((uint)map.Size.X * 30, (uint)map.Size.Y * 30), map.Name + " - MAZE", Styles.Default);

        while (Window.IsOpen)
        {
            Update();
        }
    }
    static public RenderWindow Window { get; private set; }
    static Map map = new Map(string.Format(@"C:\Users\{0}\Desktop\Maze.png", Environment.UserName));

    static public void Update()
    {
        Window.Clear(Color.Blue);

        DrawBackground();
        DrawAgent();

        Window.Display();
    }

    static void DrawAgent()
    {
        using (CircleShape tempCircle = new CircleShape
        {
            FillColor = Color.Cyan,
            Radius = 15,
            Position = new Vector2f(30, 30),
            Origin = new Vector2f(30, 30),
            Scale = new Vector2f(.5f, .5f)
        })
        {
            Window.Draw(tempCircle);
        }

    }

    static private Sprite BackgroundImage { get; set; }
    static void DrawBackground()
    {
        if (BackgroundImage == null)
            BackgroundImage = GetBackground();

        Window.Draw(BackgroundImage);

    }

    static Sprite GetBackground()
    {
        RenderTexture render = new RenderTexture((uint)map.Size.X * 30, (uint)map.Size.Y * 30);
        foreach (var point in map.Grid.Points)
        {
            RectangleShape pointShape = new RectangleShape(new Vector2f(30, 30));
            switch (point.PointType)
            {
                case PointType.Walkable:
                    pointShape.FillColor = Color.White;
                    break;
                case PointType.NotWalkable:
                    pointShape.FillColor = Color.Black;
                    break;
                case PointType.Start:
                    pointShape.FillColor = Color.Red;
                    break;
                case PointType.Exit:
                    pointShape.FillColor = Color.Blue;
                    break;
            }
            pointShape.Position = new Vector2f(point.Position.X * 30, point.Position.Y * 30);
            render.Draw(pointShape);

        }
        Sprite result = new Sprite(render.Texture);
        result.Origin = new Vector2f(0, result.GetLocalBounds().Height);
        result.Scale = new Vector2f(1, -1);
        return result;
    }

Everything works as intended when I start it, but after a few seconds, around the time when process memory reaches 70MB, BackgroundImage turns into completely white sprite. If I change the type of BackgroundImage and GetBackground() to RenderTexture, return "render" object and then change DrawBackground() function like this

 void RenderBackground()
        {
            if (BackgroundImage == null)
                BackgroundImage = GetBackground();

            using (Sprite result = new Sprite(BackgroundImage.Texture))
            {
                result.Origin = new Vector2f(0, result.GetLocalBounds().Height);
                result.Scale = new Vector2f(1, -1);
                Window.Draw(result);
            }
        }

then the background sprite doesn't turn white, but storing entire RenderTexture, instead of Sprite and then constantly creating new Sprite objects every time we call RenderBackground() function seems like a bad idea. Is there any way for GetBackground() function to return a Sprite which won't turn white once the function's local "render" variable is destroyed?


Solution

  • It turned out the answer was simpler that I expected. All I had to to was create new Texture object and then make a Sprite out of it. So instead of

    Sprite result = new Sprite(render.Texture);
    

    I wrote

    Sprite result = new Sprite(new Texture(render.Texture));
    

    Now garbage collector doesn't dispose Sprite's texture