2017-05-26 126 views
2

我嘗試在Unity3D(以後與它進行交互)中將計算機遊戲中的桌面流式傳輸到平面上。我開始了屏幕截圖,但是一分鐘後,統一遊戲就已經滿足了我個人電腦的內存(〜30GB)。在Unity3D平面的桌面屏幕(RAM溢出)

我已經嘗試通過手動調用垃圾收集器來清除它,但調用它似乎沒有改變任何東西。

我還在Visual Studio中用Windows窗體編寫了本地C#中的測試應用程序。在C#測試應用程序中,代碼行爲良好,不會氾濫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(); 
     } 
    } 
} 
+0

您可以嘗試將Texture2D,Rectangle,Bitmap和MemoryStream變量的初始化移出方法。 我也不確定Unity中的Threading。我認爲使用協程會更好。 – Woltus

+0

我正在嘗試在我的VR項目中添加一個虛擬桌面,並發現使用CopyFromSreen在性能方面遜色,並且在30fps的條件下完全利用1個CPU內核。看起來更好的方法是使用像SharpDX這樣的DirectX庫,或者像https://github.com/Phylliida/UnityWindowsCapture(不知道它是如何工作的)不要運行 –

回答

1

在創建新的Texture2D,這是從來沒有,直到您裝入另一個場景破壞。 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()); 
     .... 
     ... 
    } 
} 

注意

有在你的代碼的大無關的問題:

。你的代碼不是線程安全的,無論如何。您應該使用Thread.MemoryBarrierlock關鍵字。這是一個很長的話題在這裏討論。你可以在互聯網上找到很多資源。

。您正在盲目更新Texture2D而不知道是否有新的屏幕截圖。您可以通過調用ScreenCapture()函數中的Update函數內的加載代碼來修復該問題。

中,當然,你會得到

xxx can only be called from the main thread

例外。使用UnityThread.executeInUpdate功能這個UnityThread類。

將下面的代碼到while循環結束:

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

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

你還要做#1,使其線程安全的。我會把它留給你。你只需要使stream變量成爲線程安全的,因爲它是從不同的線程使用的。

+1

感謝您的幫助,這對我幫助很大。 – xStream