dsimon's picture

Free memory after change texture

Hello,
I'm currently working with OpenTK and i have sort of memory leak problem.
Basically, I'm rendering planes (4 vetices) with textures (loaded from bitmaps), then I want dynamically change transparancy (modifying the "alpha" of the bitmaps, pixel-based) and reload the textures.

My plane is create given a location on the Z-axis and the path to the bitmap used as texture (PNG file with alpha).
My "ChangeImageOpacity" method cycles thru all planes and all pixels, and change transparency (or opacity)
Then actualyze() should reload the texture.

But at each iteration, a large memory comsupsion is reported. Why ???
Is there a way to modify texture (alpha component) and reload this texture ? Should I destroy old textures and regenerate texture from modified Bitmaps (how ?)

public class Square:Frame
    {
        private float Lambda;
        private int idtexture;
        static Bitmap Texture;
        private const int bytesPerPixel = 4;
        static byte[] _argbValues = new byte[512 * 384 * 4];
 
        public Square(float lambda, string file)
        {
            this.Lambda = lambda;
            idtexture = LoadTexture(file);
 
        }
        static int LoadTexture(string filename)
        {
            if (String.IsNullOrEmpty(filename))
                throw new ArgumentException(filename);
            GL.Enable(EnableCap.Texture2D);
            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            int id = GL.GenTexture();
            GL.BindTexture(TextureTarget.Texture2D, id);
 
            Texture = loadbitmap(filename);
            BitmapData bmp_data = Texture.LockBits(new Rectangle(0, 0, Texture.Width, Texture.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmp_data.Width, bmp_data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmp_data.Scan0);
 
            Texture.UnlockBits(bmp_data);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
 
            return id;
        }
        public void  Actualize()
        {
            GL.Enable(EnableCap.Texture2D);
            GL.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            idtexture = GL.GenTexture();
            BitmapData bmp_data = Texture.LockBits(new Rectangle(0, 0, Texture.Width, Texture.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
 
            GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmp_data.Width, bmp_data.Height, 0,
                OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmp_data.Scan0);
 
            Texture.UnlockBits(bmp_data);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
            GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
 
        }
        public static Bitmap loadbitmap(string path)
        {
            using (Bitmap original = new Bitmap(path))
            {
                return new Bitmap(original);
            }
 
        }
        public void ChangeImageOpacity(double opacity)
        {
            if ((Texture.PixelFormat & System.Drawing.Imaging.PixelFormat.Indexed) == System.Drawing.Imaging.PixelFormat.Indexed)
            {
 
            }
            Bitmap bmp = (Bitmap)Texture.Clone();
            System.Drawing.Imaging.PixelFormat pxf = System.Drawing.Imaging.PixelFormat.Format32bppArgb;
 
            int numBytes = Texture.Width * Texture.Height * bytesPerPixel;
            Rectangle rect = new Rectangle(0, 0, Texture.Width, Texture.Height);
            BitmapData bmpData = Texture.LockBits(rect, ImageLockMode.ReadWrite, pxf);
 
            IntPtr ptr = bmpData.Scan0;
            {
                System.Runtime.InteropServices.Marshal.Copy(ptr, _argbValues, 0, numBytes);
                for (int counter = 0; counter < _argbValues.Length; counter += bytesPerPixel)
                {
                    if (_argbValues[counter + bytesPerPixel - 1] == 0)
                        continue;
 
                    int pos = 0;
                    pos++; // skip B value
                    pos++; // skip G value
                    pos++; // skip R value
                    _argbValues[counter + pos] = (byte)(_argbValues[counter + pos] * opacity);  // take care, multiplicative alpha
                }
                System.Runtime.InteropServices.Marshal.Copy(_argbValues, 0, ptr, numBytes);
            }
            Texture.UnlockBits(bmpData);
        }
        public override void Render(double time)
        {
            GL.PushMatrix();
 
            base.Render(time);
            GL.Enable(EnableCap.Blend);
            GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);
            GL.BindTexture(TextureTarget.Texture2D, idtexture);
            GL.Begin(BeginMode.Quads);
            GL.TexCoord2(0.0f, 1.0f);
            GL.Vertex3(-4.0f, -4.0f, Lambda);
            GL.TexCoord2(1.0f, 1.0f);
            GL.Vertex3(4.0f, -4.0f, Lambda);
            GL.TexCoord2(1.0f, 0.0f);
            GL.Vertex3(4.0f, 4.0f, Lambda);
            GL.TexCoord2(0.0f, 0.0f);
            GL.Vertex3(-4.0f, 4.0f, Lambda);
            GL.End();
 
            GL.PopMatrix();
        }
    }

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
AltSoftLab's picture

Just try AltSketch for loading textures and be happy without any marshalling & unsafe code

richardjmoss's picture
AltSoftLab wrote:

Just try AltSketch for loading textures and be happy without any marshalling & unsafe code

Do you have to reply to almost every post with an advert for your product?

AltSoftLab's picture
richardjmoss wrote:

Do you have to reply to almost every post with an advert for your product?

Not to every post, only where our product may be useful (not everyone read every blog post). We are creating product that we wished to have in the past. If someone likes it, it will be good for him and for us.

dsimon's picture

Thx for you answer!
I realize this project for people who want to detach to a maximum of libraries. If others have ideas?
Thank you!

winterhell's picture

Hey mate.
Yes, it is annoying to be advertised with a whole library when the solution to your problem may require less lines of code than to integrate the library.

Onto the memory leak- for every GL.GenTexture(); you need a corresponding GL.DeleteTexture(id). You can start with that. Be sure to call GC.Collect like every frame when benchmarking as memory can fluctuate quite much, I've seen cases where it takes 137MB before the GC activates on its own to bring the memory down to 97MB.
Lastly, the whole standart Bitmap class and its locking bits, marshalling etc generates some garbage. It is also several times slower than if you load from your own format, be it raw RGBA or zipped.

Hope it helps.