C# 桌面屏幕到Unity3D中的平面(RAM溢出)

C# 桌面屏幕到Unity3D中的平面(RAM溢出),c#,unity3d,garbage-collection,screen-capture,C#,Unity3d,Garbage Collection,Screen Capture,我尝试在Unity3D中将计算机的桌面ingame流到一个平面上(稍后再与之交互)。我开始抓屏,但一分钟后unity游戏就填满了我电脑的内存(~30GB) 我已经尝试通过手动调用垃圾收集器来清理它,但是调用它似乎并没有改变任何事情 我还使用Windows窗体在Visual Studio中用本机C#编写了一个测试应用程序。在C#test应用程序中,代码表现良好,不会淹没RAM 有没有可能,Unity不知道如何释放RAM?为了让这段代码正常工作,我必须将System.Drawing.dll添加到我的

我尝试在Unity3D中将计算机的桌面ingame流到一个平面上(稍后再与之交互)。我开始抓屏,但一分钟后unity游戏就填满了我电脑的内存(~30GB)

我已经尝试通过手动调用垃圾收集器来清理它,但是调用它似乎并没有改变任何事情

我还使用Windows窗体在Visual Studio中用本机C#编写了一个测试应用程序。在C#test应用程序中,代码表现良好,不会淹没RAM

有没有可能,Unity不知道如何释放RAM?为了让这段代码正常工作,我必须将
System.Drawing.dll
添加到我的Unity游戏中

如果这种方法不起作用,是否还有其他选项来流式处理桌面并在Unity的平面上显示它

这是我目前的代码:

using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Threading;
using UnityEngine;

public class ScreenCap : MonoBehaviour {

    public Renderer renderer;
    public MemoryStream stream;
    // Use this for initialization
    void Start () {
        renderer = GetComponent<Renderer>();
        startScreenCaptureThread();
    }

    // Update is called once per frame
    void Update () {

        Texture2D tex = new Texture2D(200, 300, TextureFormat.RGB24, false);

        if (stream != null)
        {
            tex.LoadImage(stream.ToArray());

            renderer.material.mainTexture = tex;
            stream.Dispose();
            System.GC.Collect();
            System.GC.WaitForPendingFinalizers();
        }
    }

    public void startScreenCaptureThread()
    {
        Thread t = new Thread(ScreenCapture);
        t.Start();
    }

    public void ScreenCapture()
    {
        Rectangle screenSize;
        Bitmap target;
        MemoryStream tempStream;
        while (true)
        {

            System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();
            Debug.Log(proc.PrivateMemorySize64);
            screenSize = System.Windows.Forms.Screen.PrimaryScreen.Bounds;
            target = new Bitmap(screenSize.Width, screenSize.Height);
            using (System.Drawing.Graphics g = System.Drawing.Graphics.FromImage(target))
            {
                g.CopyFromScreen(0, 0, 0, 0, new Size(screenSize.Width, screenSize.Height));
            }
            tempStream = new MemoryStream();
            target.Save(tempStream, ImageFormat.Png);
            tempStream.Seek(0, SeekOrigin.Begin);
            stream = tempStream;
            target.Dispose();
            tempStream.Dispose();
            System.GC.Collect();

            System.GC.WaitForPendingFinalizers();
        }
    }
}
使用系统集合;
使用System.Collections.Generic;
使用系统图;
使用系统、绘图、成像;
使用System.IO;
使用系统线程;
使用UnityEngine;
公共类屏幕上限:MonoBehavior{
公共服务;
公共记忆流;
//用于初始化
无效开始(){
renderer=GetComponent();
startScreenCaptUratRead();
}
//每帧调用一次更新
无效更新(){
Texture2D tex=新的Texture2D(200300,TextureFormat.RGB24,false);
if(流!=null)
{
tex.LoadImage(stream.ToArray());
renderer.material.mainTexture=tex;
stream.Dispose();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
}
}
公共无效StartScreenCaptUratRead()
{
线程t=新线程(屏幕截图);
t、 Start();
}
公共屏幕截图()
{
矩形屏幕尺寸;
位图目标;
内存流tempStream;
while(true)
{
System.Diagnostics.Process proc=System.Diagnostics.Process.GetCurrentProcess();
Debug.Log(proc.privatemorysize64);
screenSize=System.Windows.Forms.Screen.PrimaryScreen.Bounds;
目标=新位图(screenSize.Width、screenSize.Height);
使用(System.Drawing.Graphics g=System.Drawing.Graphics.FromImage(目标))
{
g、 CopyFromScreen(0,0,0,0,新大小(screenSize.Width,screenSize.Height));
}
tempStream=newmemoryStream();
Save(tempStream,ImageFormat.Png);
tempStream.Seek(0,SeekOrigin.Begin);
stream=tempStream;
target.Dispose();
tempStream.Dispose();
System.GC.Collect();
System.GC.WaitForPendingFinalizers();
}
}
}

创建新的
纹理2D
时,在加载另一个场景之前,它不会被销毁。Unity随后将清理
Texture2D
使用的资源

从代码中,您将创建新的
Texture2D
每帧。这确实应该是代码中最大的问题

快走

Texture2D tex = new Texture2D(200, 300, TextureFormat.RGB24, false);
进入
Start
函数,然后将
tex
变量设置为公共变量,以便在更新函数中使用。这样,您就不会在每一帧创建新的
Texture2D

Texture2D tex;

void Start () 
{
    tex = new Texture2D(200, 300, TextureFormat.RGB24, false);
}

void Update () 
{
    if (stream != null)
    {
        tex.LoadImage(stream.ToArray());
        ....
        ...
    }
}
注意

您的代码中存在大量不相关的问题:

1。您的代码无论如何都不是线程安全的。您应该使用
Thread.MemoryBarrier
lock
关键字。这是一个很长的话题。你可以在网上找到很多资源

2。在不知道是否有新屏幕截图的情况下盲目更新
Texture2D
。您可以通过从
screenscapture()
函数调用
Update
函数中的加载代码来修复此问题

当然,你会得到

xxx只能从主线程调用

例外。在此类中使用
UnityThread.executeInUpdate
函数

将下面的代码放在
循环的末尾,而
循环:

UnityThread.executeInUpdate(() =>
{
    if (stream != null)
    {
        tex.LoadImage(stream.ToArray());

        renderer.material.mainTexture = tex;
        stream.Dispose();
        System.GC.Collect();
        System.GC.WaitForPendingFinalizers();
    }
});

您仍然需要执行#1以确保线程安全。我将把这个留给你。您只需使
变量线程安全,因为它是从不同的线程使用的。

您可以尝试将Texture2D、矩形、位图和MemoryStream变量的初始化移出方法。我也不确定Unity中的线程。我认为使用协同程序会更好。我试图在我的VR项目中添加一个虚拟桌面,发现使用CopyFromsRen在性能上不太好,最高可达30fps,充分利用了1个CPU核心。看起来更好的方法是使用像SharpDX这样的DirectX LIB,或者像smth这样的(还不知道它是如何工作的)谢谢你的帮助,这对我帮助很大。