如何使用C#

时间:2017-12-20 13:19:20

标签: c# opengl memory-management gpu opentk

为了在Fullscreen中以60 FPS和稳定的帧速率显示图像,我在学习如何使用C#(OpenTK库)在OpenGL中进行操作。

我有一个运行GameWindow类的基本项目,它可以将图像作为纹理上传到gpu内存并以60 FPS显示。我的问题是,当进程完成时,永远不会释放gpu内存。只有当我关闭应用程序时,它才会被释放。

我尝试了最常见的建议,如GL.DeleteTexture并手动调用GC几次,但似乎没有任何效果。每次从Windows窗体启动GameWindow时,gpu内存使用量都会增加。

有关如何以更好的方式管理GPU内存的任何建议吗?我的应用程序对于gpu内存至关重要,因为我想在gpu内存中加载大量图像(> 1GB)并以60 FPS显示它们并具有良好的时序,其中CPU在稳定帧率方面非常不可靠。稳定的帧率对此至关重要。

这是我的示例GameWindow类:

using System;
using System.Collections.Generic;
using OpenTK.Graphics.OpenGL;
using OpenTK;
using System.Diagnostics;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Windows.Forms;

namespace OpenGLAnimation
{
    class OpenGLShow : GameWindow
    {
    #region --- Global Variables -------------------------------------
    public  bool            bRunning      = true;
    public  bool            bFinishedLoad = false;
    public  int             WorkTime      = 0;
    private Screen          Scrn;
    private List<int>       TextureList = new List<int>();
    private Thread          MainThread;
    #endregion

    #region --- Constructor ------------------------------------------
    /// <summary>
    /// Initializes the Class
    /// </summary>
    /// <param name="FilesPath">Directory path with all the images, that will be shown.</param>
    /// <param name="_Screen">Information about the screen, on which the images will be shown.</param>
    /// <param name="ScreenIndex">Index of the screen (first monitor = 0; second monitor = 1 ..</param>
    public OpenGLShow (Screen _Screen, DisplayDevice disp, int ScreenIndex = 1) : 
        base(_Screen.WorkingArea.Width, _Screen.WorkingArea.Height, OpenTK.Graphics.GraphicsMode.Default, "FBSScreen", GameWindowFlags.Fullscreen, disp)
    {
        Scrn         = _Screen;

        bFinishedLoad = false;
    }
    #endregion

    #region --- Event triggered functions ----------------------------
    protected override void OnLoad(EventArgs EvAr)
    {
        WindowBorder = WindowBorder.Hidden;
        WindowState  = WindowState.Fullscreen;
        VSync        = VSyncMode.On;

        GL.Viewport(0, 0, Scrn.WorkingArea.Width, Scrn.WorkingArea.Height);
        GL.ClearColor(Color.MidnightBlue);
        GL.Enable(EnableCap.Texture2D);

        Context.MakeCurrent(null);
        MainThread = new Thread(DoWork);
        MainThread.Start();
    }
    #endregion

    private void DoWork()
    {
        while (!bFinishedLoad) { Thread.Sleep(500); Console.WriteLine("Textures are loaded. Waiting.."); }

        MakeCurrent();
        int         index     = 0;
        int         MaxIndex  = TextureList.Count;
        Stopwatch   swRender  = new Stopwatch();
        Stopwatch   swSumTime = new Stopwatch();

        swSumTime.Restart();
        while (bRunning && TextureList.Count > 0)
        {
            swRender.Restart();

            if (index >= MaxIndex) { index = 0; }

            GL.ClearColor(Color.MidnightBlue);
            GL.BindTexture(TextureTarget.Texture2D, TextureList[index++]);
            GL.Begin(PrimitiveType.Polygon);
            GL.Color4(Color.White);
            GL.TexCoord2(0, 1); GL.Vertex2(-1, -1);
            GL.TexCoord2(0, 0); GL.Vertex2(-1,  1);
            GL.TexCoord2(1, 0); GL.Vertex2( 1,  1);
            GL.TexCoord2(1, 1); GL.Vertex2( 1, -1);
            GL.End();
            SwapBuffers();
            GL.Finish();

            Title = "FPS: " + Math.Round((decimal)1000 / swRender.ElapsedMilliseconds, 2) + "  |  Frame: " + index + "/" + MaxIndex;

            if (WorkTime > 0 && WorkTime < swSumTime.ElapsedMilliseconds) { Abort(); }
        }
    }

    public void Abort()
    {
        bRunning = false;
        Thread.Sleep(500);

        GL.BindTexture(TextureTarget.Texture2D, 0);     // unbind textures
        //foreach (int i in TextureList)                // doesn't free GPU memory
        //{
        //    GL.DeleteTexture(i);
        //    GL.DeleteBuffer(i);
        //}

        TextureList.Clear();
        Close();

        GC.Collect();
        GC.WaitForPendingFinalizers();
    }

    public void Start()
    {
        MainThread.Start();
    }

    private void AddTexture(string sPath)
    {
        int     tmpTexture = 0;
        Bitmap  bmp        = new Bitmap(sPath);

        GL.GenTextures(1, out tmpTexture);
        GL.BindTexture(TextureTarget.Texture2D, tmpTexture);

        BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
        GL.TexImage2D(TextureTarget.Texture2D, 0, PixelInternalFormat.Rgba, bmpData.Width, bmpData.Height, 0, OpenTK.Graphics.OpenGL.PixelFormat.Bgra, PixelType.UnsignedByte, bmpData.Scan0);

        bmp.UnlockBits(bmpData);

        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMinFilter, (int)TextureMinFilter.Linear);
        GL.TexParameter(TextureTarget.Texture2D, TextureParameterName.TextureMagFilter, (int)TextureMagFilter.Linear);

        TextureList.Add(tmpTexture);
    }

    public void LoadImages(string sPath)
    {
        List<string> Files = new List<string>();
        TextureList.Clear();

        if (File.Exists(sPath))
        {
            Files.Add(sPath);
        }
        else if (Directory.Exists(sPath))
        {
            Files = new List<string>(Directory.GetFiles(sPath));
        }

        foreach (string s in Files)
        {
            if (s.Contains(".png") || s.Contains(".bmp") || s.Contains(".jpg"))
            {
                AddTexture(s);
            }
        }

        bFinishedLoad = true;
    }
}
}

要测试班级,您可以使用NuGet下载OpenTK refs,只需使用按钮创建一个新表格,即可执行以下操作

string filesPath = @"C:\Users\Public\Pictures\Sample Pictures";
Screen[] scr = Screen.AllScreens;
OpenTK.DisplayDevice display = OpenTK.DisplayDevice.GetDisplay(OpenTK.DisplayIndex.First);

OpenGLAnimation.OpenGLShow gwForm = new OpenGLAnimation.OpenGLShow(scr[0], display, 0);
gwForm.WorkTime = 2000;            // if worktime = 0 then the images will be looped forever
gwForm.LoadImages(filesPath);   // load images as textures to GPU memory. Can be a png/bmp file or a directory with such files
gwForm.Run();

基本部分基本上是AddTexture(),DoWork()和Abort()函数,我将图像上传到gpu内存,在显示器上显示它们并使用Abort()来停止&amp;完成这个过程。

0 个答案:

没有答案