2017-04-04 13 views
2

我正在開發一個基於虛幻的開源無人機模擬(Microsoft AirSim),我嘗試捕獲並保存附加到無人機。下面的圖像給出了遊戲外觀的一個概念。底部的最右側視圖是相機的實際視圖,另外兩個視圖僅是同一圖像的處理版本。虛幻引擎:在沒有阻塞遊戲線程的情況下訪問/保存遊戲中的圖像到磁盤

UAV simulation

現在它的成立這樣的方式:有一個攝像頭的資產,這是通過代碼捕獲組分閱讀。截圖中的三個視圖鏈接到此捕獲組件。隨着無人機在遊戲中飛行,觀看信息流沒有任何問題。但是在錄製屏幕截圖時,當前代碼從該捕獲組件創建一個TextureRenderTargetResource,隨後調用ReadPixels並將該數據保存爲圖像(請參閱下面的代碼流)。直接使用ReadPixels()會直接阻塞遊戲線程,並會大幅減慢整個遊戲速度:當我開始記錄時,從大約120 FPS下降到低於10 FPS。在this article

bool saveImage() { 
    USceneCaptureComponent2D* capture = getCaptureComponent(camera_type, true); 
    FTextureRenderTargetResource* RenderResource = capture->TextureTarget->GameThread_GetRenderTargetResource(); 
    width = capture->TextureTarget->GetSurfaceWidth(); 
    height = capture->TextureTarget->GetSurfaceHeight(); 

    TArray<FColor> imageColor; 
    imageColor.AddUninitialized(width * height); 
    RenderResource->ReadPixels(bmp); 
} 

來看,似乎顯而易見,ReadPixels()「將阻塞遊戲線程,直到渲染線程已經趕上了」。該文章包含讀取像素的「非阻塞」方法的示例代碼(通過去除FlushRenderingCommands()並使用RenderCommandFence標誌來確定任務何時完成),但它不會顯着提高性能:哪些圖像保存稍高,但遊戲線程仍然以大約20 FPS的速度運行,因此很難控制無人機。有沒有更有效的異步方法可以實現我想要做的事情,也許可以說,在一個單獨的線程中?我也有點困惑,爲什麼代碼在屏幕上儘可能快地傳輸這些圖像沒有問題,但保存圖像似乎更復雜。即使圖像只保存在15赫茲左右的磁盤上也沒問題,只要它不會太多幹擾遊戲原生FPS。

回答

0

也許你應該截圖存儲在TArray中,直到「遊戲」完成,然後處理所有存儲的數據?

還是看看從無人機攝像頭截圖,而不是操縱像素數據?我假設左側和中間的無人機相機圖像是基本相機圖像的材質修改版本。

當您保存圖像時,它是否正在寫圖像格式文件或.uasset?

最後 - 這是一個完整的黑暗中拍攝 - 創建一個自定義的UActorComponent,將其添加到無人機,並讓它觀看遊戲中的某些指令。當你想寫一個圖像時,將RenderTarget數據推送到自定義的UActorComponent。由於actor組件可以在任何線程上打勾(在構造函數中使用BCanTickOnAnyThread = true,我認爲這是變量名),actor組件的tick可以監視傳入的圖像數據並在那裏處理它。我從來沒有這樣做過,但我已經在任何線程上創建了一個演員組件(我正在檢查物理)。

+0

您能解釋一下更多關於您的意思嗎?「看看從無人機攝像頭截圖而不是操縱像素數據?」因爲我知道的唯一方法可以通過ReadPixels()命令將捕獲的圖像數據提取出來。而現在,它正在寫壓縮的PNG文件。如果我將它們存儲在TArray中,我猜你建議我在單獨的線程中執行它? – HighVoltage

+0

我剛剛添加了另一個想法,我的帖子:)你可以調用一個控制檯命令從玩家相機截圖。也許你可以找到該命令的來源,然後發現如何給它一個不同的源攝像頭。截圖命令保存了一個文件,targa我認爲。道歉,渲染不是我的專業領域。這裏的問題在於,您錄製的視頻不僅僅是拍攝單個圖像。也許看看添加第三方媒體庫? – JonS

+0

我也認爲在遊戲結束後保存圖像不會導致fps下降,如果這是你想要的,btw會嘗試使用虛幻的捕捉功能https://docs.unrealengine.com/latest/INT/引擎/基礎/屏幕截圖/,它也保存圖片本身,並且會比你現在正在做的更快。 – LumbusterTick

1

(...)似乎顯而易見,ReadPixels()「將阻塞遊戲線程,直到渲染線程已經趕上了」

使用,嘗試訪問和複製GPU紋理數據的功能總是很痛苦。我發現這些功能的完成時間取決於硬件或驅動程序版本。您可以嘗試在不同的GPU平臺上運行代碼(速度更快或更新)。

返回代碼:繞過單線程問題我會嘗試使用TextureRenderTarget2D::ConstructTexture2D方法將UTextureRenderTarget2D複製到UTexture2D。當你複製你的圖像(我希望它快得多)時,你可以按照15 FPS的比例將它推送到消費者線程。線程可以使用FRunnable或異步引擎模塊(使用所謂的任務圖)來創建。消費者線程可以通過使用紋理的PlatformData->Mips[0]->BulkData來訪問克隆的紋理。確保您正在讀取BulkData->Lock(...)BulkData->Unlock()調用之間的紋理數據。

我不確定,但它可以提供幫助,因爲您的複製紋理將不會被渲染線程使用,因此您可以在渲染線程上鎖定其數據而不會阻塞任何屏幕。我只擔心鎖定和解鎖操作的線程安全(沒有發現任何線索),應該可能,除非有引擎RHI調用。

Here是一些可能有用的相關代碼。

+0

感謝您的詳細評論:不幸的是,這種方法還會將我的遊戲內FPS降至10左右,而這僅僅是ConstructTexture2D()函數。是否有可能在一個單獨的線程中完成整個操作? ConstructTexture2D()函數本身在內部有一些調用,用於檢查它是否從遊戲線程被調用。 – HighVoltage

1

您可以將保存到磁盤操作從遊戲線程移動到其他線程。
請檢查Async.h查看詳細

線程的限制是,你不能修改/添加/在其他線程刪除uobject/uactor。 Multi-thread for UE4