Экран рабочего стола для Plane в Unity3D (переполнение ОЗУ)
Я пытаюсь транслировать рабочий стол компьютера в игре на плоскость в Unity3D (и позже взаимодействую с ним). Я приступил к съемке экрана, но уже через минуту игра Unity просто заполняет оперативную память моего компьютера (~30 ГБ).
Я уже пытаюсь очистить его, вызвав сборщик мусора вручную, но вызов этого, похоже, ничего не меняет.
Я также написал тестовое приложение на родном C# в Visual Studio с Windows Forms. В тестовом приложении C# код ведет себя хорошо и не загружает оперативную память.
Есть ли вероятность, что Unity не знает, как освободить оперативную память? Чтобы этот код работал, мне нужно было добавить 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();
}
}
}
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());
....
...
}
}
Примечание:
В вашем коде есть большие проблемы:
1. Ваш код в любом случае не является потокобезопасным. Вы должны использовать Thread.MemoryBarrier
или lock
ключевое слово. Это длинная тема для обсуждения здесь. Вы можете найти много ресурсов в Интернете.
2. Вы слепо обновляете Texture2D
не зная, есть ли новый скриншот или нет. Вы можете это исправить, вызвав коды загрузки, которые находятся внутри Update
функция от ScreenCapture()
функция.
Конечно, вы получите
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
переменная Thread-safe, так как она используется из разных потоков.