Search code examples
c#opengltexturesaccess-violationopentk

c# opentk AccessViolationException


I am currently a student and programming a Game. For that, I load Textures at the beginning into the Program and let these Render, when I need them.
It does work so far, but sometimes I get the AccessViolationException. I already tried so much I found on the Internet, but nothing worked. This error is really frustrating, because it happenes unregularly. Sometimes it crashes after 20 sec and sometimes it stands 8 min.
Capping frames_per_second to 20 helped and I noticed it crashes, when there are many objects to render, so it doesnt crash that often, but it still does.

Here is my Code for loading the Textures in at the beginning:

        Dictionary<string, Texture> ddc = new Dictionary<string, Texture>();
        DirectoryInfo img_dir = new DirectoryInfo("./Resources/drawable/");
        foreach(FileInfo file in img_dir.GetFiles())
        {
            string name = file.Name.Substring(0, file.Name.Length - 4);
            if (name.StartsWith("spr_"))
            {
                Bitmap bmp = new Bitmap(file.FullName);
                int texture = GL.GenTexture();

                BitmapData bmp_data = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                GL.BindTexture(TextureTarget.Texture2D, texture);

                        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmp.Width, bmp.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmp_data.Scan0);
                bmp.UnlockBits(bmp_data);

                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapS, (int)TextureWrapMode.Clamp);
                GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureWrapT, (int)TextureWrapMode.Clamp);


                ddc.Add(name.Substring(4), new Texture()
                {
                    TextureID = texture,
                    Rows = (bmp.Height/64),
                    Columns = (bmp.Width/64)
                });
            }
        }
        return ddc;

It saves the ID with the name in ddc so I can use this to get the right Texture in the Game. And here is the render Class, which the game always goes throw when something has to be rendered:

public void init()
    {
        //starts at the beginning of the Gamestart

        textures = resourceHandler.loadTextures();

        textures["font"].Rows = 16;
        textures["font"].Columns = 16;

        GL.ClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        GL.MatrixMode(MatrixMode.Projection);
        GL.LoadIdentity();
        GL.Ortho(0.0f, Game.SCREEN_WIDTH, 0.0f, Game.SCREEN_HEIGHT, -1.0f, 1.0f);
        GL.MatrixMode(MatrixMode.Modelview);

        GL.Enable(EnableCap.Texture2D);
        GL.Enable(EnableCap.Blend);
        GL.BlendFunc(BlendingFactorSrc.SrcAlpha, BlendingFactorDest.OneMinusSrcAlpha);

        GL.ActiveTexture(TextureUnit.Texture0);
    }

    public void batchDraw(int x, int y, int w, int h, float tx, float ty, float tw, float th)
    {
        GL.VertexPointer(2, VertexPointerType.Float, 0, new float[] { x, y, x + w, y, x, y + h, x + w, y + h });
        GL.EnableClientState(ArrayCap.VertexArray);
        GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, new float[] { tx, ty + th, tx + tw, ty + th, tx , ty, tx + tw, ty });
        GL.EnableClientState(ArrayCap.TextureCoordArray);

        //Sometimes Error at DrawArrays: AccessViolationException
        GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);

        GL.DisableClientState(ArrayCap.VertexArray);
        GL.DisableClientState(ArrayCap.TextureCoordArray);
    }

I hope someone can help me


Solution

  • I'm not that familiar with C# and its scoping rules, but my first hunch would be that the in-situ arrays you create for VertexPointer and TexCoordPointer (new float[]{…}) are deallocated before the call of DrawArrays is reached. The …Pointer functions do not create an internal copy of the data you pass them; they store a pointer to some memory region you pass them and that pointer will be dereferenced only at the Draw… calls.

    If that's the case you have to keep the arrays in scope until the drawing happened:

    public void batchDraw(int x, int y, int w, int h, float tx, float ty, float tw, float th)
    {
        float[] vpos = new float[] { x, y, x + w, y, x, y + h, x + w, y + h };
        float[] vtxc = new float[] { tx, ty + th, tx + tw, ty + th, tx , ty, tx + tw, ty };
        GL.VertexPointer(2, VertexPointerType.Float, 0, vpos);
        GL.EnableClientState(ArrayCap.VertexArray);
        GL.TexCoordPointer(2, TexCoordPointerType.Float, 0, vtxc);
        GL.EnableClientState(ArrayCap.TextureCoordArray);
    
        //Sometimes Error at DrawArrays: AccessViolationException
        GL.DrawArrays(PrimitiveType.TriangleStrip, 0, 4);
    
        GL.DisableClientState(ArrayCap.VertexArray);
        GL.DisableClientState(ArrayCap.TextureCoordArray);
    }
    

    Note that the whole thing you do there uses the old fixed function pipeline and allocates quire small arrays. This is one of those cases where going straight through immediate mode calls (glTexCoord, glVertex) would probably perform better.

    Of course you shouldn't use that old style OpenGL in the first place.