2017-04-12 307 views
3

我試圖從一個遊戲攝像頭像素訪問數據和圖像保存到磁盤。最初,簡單的方法是使用一個渲染目標,隨後RenderTarget-> ReadPixels(),但由於本機實現ReadPixels的()包含)調用FlushRenderingCommands(,直到圖像保存它會阻止遊戲線程。作爲一個計算密集型操作,這是降低我的FPS方式太多。虛幻引擎4:適應ReadPixels(),以多線程框架

爲了解決這個問題,我想創建一個專用的線程可以訪問相機作爲CaptureComponent,然後按照類似的方法。但隨着FlushRenderingCommands()塊只能從遊戲線程調用的,我有沒有那個電話重寫ReadPixels(),(在各種各樣的非阻塞的方式,通過在https://wiki.unrealengine.com/Render_Target_Lookup教程啓發):但即使如此,我在保存圖像時(我確認這不是因爲實際保存到磁盤操作,而是因爲像素數據訪問),我的遊戲內FPS出現問題。我重寫的ReadPixels()函數如下所示,我希望得到一些關於這裏可能會出錯的一些建議。我不確定是否可以從非遊戲線程調用ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER,並且這是我的問題的一部分。

APIPCamera* cam = GameThread->CameraDirector->getCamera(0); 
USceneCaptureComponent2D* capture = cam->getCaptureComponent(EPIPCameraType::PIP_CAMERA_TYPE_SCENE, true); 
if (capture != nullptr) { 
    if (capture->TextureTarget != nullptr) { 
     FTextureRenderTargetResource* RenderResource = capture->TextureTarget->GetRenderTargetResource(); 
     if (RenderResource != nullptr) { 
      width = capture->TextureTarget->GetSurfaceWidth(); 
      height = capture->TextureTarget->GetSurfaceHeight(); 
      // Read the render target surface data back.  
      struct FReadSurfaceContext 
      { 
       FRenderTarget* SrcRenderTarget; 
       TArray<FColor>* OutData; 
       FIntRect Rect; 
       FReadSurfaceDataFlags Flags; 
      }; 

      bmp.Reset(); 
      FReadSurfaceContext ReadSurfaceContext = 
      { 
       RenderResource, 
       &bmp, 
       FIntRect(0, 0, RenderResource->GetSizeXY().X, RenderResource->GetSizeXY().Y), 
       FReadSurfaceDataFlags(RCM_UNorm, CubeFace_MAX) 
      }; 
      ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER(
       ReadSurfaceCommand, 
       FReadSurfaceContext, Context, ReadSurfaceContext, 
       { 
        RHICmdList.ReadSurfaceData(
        Context.SrcRenderTarget->GetRenderTargetTexture(), 
        Context.Rect, 
        *Context.OutData, 
        Context.Flags 
       ); 
      }); 
     } 
    } 
} 

編輯:我注意到一件事是,口吃消失,如果我在我的渲染目標設置禁用HDR(但這會導致低質量的圖像):所以它似乎是合理的圖像的大小或許,由於我正在實施它的方式,它仍然阻塞了一個核心線程。

回答

0

應該可以調用ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER從任何線程,因爲有任務圖的基礎調用。你可以看到它,當你analize這個宏生成什麼樣的代碼:

if(ShouldExecuteOnRenderThread()) 
{ 
    CheckNotBlockedOnRenderThread(); 
    TGraphTask<EURCMacro_##TypeName>::CreateTask().ConstructAndDispatchWhenReady(ParamValue1); 
} 

您應該謹慎訪問UObjects(如USceneCaptureComponent2D)從不同的線程導致這些由垃圾收集管理,並通過遊戲線程自己。

(...),但即使如此,我面臨着與我在遊戲中FPS是生澀的問題時的圖像保存

你檢查什麼的線程導致FPS下降與stat unitstat unitgraph命令?您也可以使用profiling tools來執行更詳細的洞察,並確保沒有其他原因滯後。

編輯: 我發現了另一個method of accessing pixel data。嘗試這樣做,而不是實際複製for循環中的數據,並檢查FPS是否有任何改進。這可能會更快一點,因爲它們之間沒有像素操作/轉換。

+0

我也試圖通過分析工具看着它(單臺攝像機HDR的情況下):大部分是最大的安打所花費的時間方面的事件有「CPU攤位:等待事件」,「CPU攤主:睡眠」寫在他們,我猜表明他們正在等待GPU趕上? – HighVoltage

+0

感謝您的評論:當我運行統計單位圖時,這是我發現:沒有人類發展報告:所有線程在一個不錯的利率。 HDR:渲染線程尖峯了很多,導致幀定時的尖峯。偶爾的GPU線程滯後。 i.imgur.com/S1YVGaz.png – HighVoltage

+0

即使在渲染線程中,它看起來像'ReadSurfaceData'方法花費了太多時間。假設你沒有犯任何其他錯誤(如多次調用read函數),它使得多線程優化不可能使用這種特殊的讀取像素數據的方法。你有沒有想過使用原始的DirectX?您可以直接從RHI對象訪問它,但它使您的項目高度依賴於平臺。 – JKovalsky