2017-04-12 4 views
3

Ich versuche auf Pixeldaten zuzugreifen und Bilder von einer In-Game-Kamera auf der Festplatte zu speichern. Zu Beginn bestand der einfache Ansatz darin, ein Renderziel und anschließend RenderTarget-> ReadPixels() zu verwenden. Da jedoch die native Implementierung von ReadPixels() einen Aufruf von FlushRenderingCommands() enthält, würde der Spielthread blockiert, bis das Bild gespeichert wird. Da es sich um eine rechenintensive Operation handelt, hat dies meine FPS viel zu sehr gesenkt.Unreal Engine 4: Anpassung von ReadPixels() an ein Multithread-Framework

Um dieses Problem zu lösen, versuche ich einen dedizierten Thread zu erstellen, der als CaptureComponent auf die Kamera zugreifen kann und dann einen ähnlichen Ansatz verfolgt. Aber da der FlushRenderingCommands() -Baustein nur aus einem Spielthread aufgerufen werden kann, musste ich ReadPixels() ohne diesen Aufruf (in einer nicht blockierenden Art und Weise, inspiriert durch das Tutorial unter https://wiki.unrealengine.com/Render_Target_Lookup) neu schreiben: aber selbst dann bin ich es Das Problem ist, dass mein In-Game-FPS immer dann ruckelt, wenn ein Bild gespeichert wird (ich bestätige, dass dies nicht auf den tatsächlichen Speichervorgang, sondern auf den Zugriff auf Pixeldaten zurückzuführen ist). Meine neu geschriebene ReadPixels() - Funktion sieht wie folgt aus, ich hatte gehofft, einige Vorschläge zu bekommen, was hier schief gehen könnte. Ich bin mir nicht sicher, ob ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER von einem Nicht-Spiel-Thread aufgerufen werden kann und ob das ein Teil meines Problems ist.

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 
       ); 
      }); 
     } 
    } 
} 

EDIT: Eine weitere Sache, die ich bemerkt habe ist, dass das Stottern weg geht, wenn ich HDR in meiner deaktivieren Zielsystemeinstellung machen (aber dies führt zu einer geringen Bildqualität): so scheint es plausibel, dass die Größe des Bildes Vielleicht blockiert es immer noch einen der Kern-Threads wegen der Art, wie ich es implementiere.

Antwort

0

Es sollte möglich sein, ENQUEUE_UNIQUE_RENDER_COMMAND_ONEPARAMETER aus einem beliebigen Thread aufzurufen, da es einen zugrunde liegenden Aufruf von Task Graph gibt. Sie können es sehen, wenn Sie analize, welcher Code das Makro erzeugt:

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

Sie sollten vorsichtig sein UObjects (wie USceneCaptureComponent2D) von verschiedenen Threads verursachen diese werden verwaltet von Garbage Collector und eigene von Spiel Thread über den Zugriff.

(...), aber auch dann ein Problem mit meiner im Spiel FPS sein abgehackt ich bin vor, wenn ein Bild gespeichert wird

Haben Sie überprüfen, welcher Thread verursachen FPS Drop mit stat unit oder stat unitgraph Befehl? Sie können auch profiling tools verwenden, um detailliertere Einblicke zu erhalten und sicherzustellen, dass es keine anderen Ursachen für Verzögerungen gibt.

Bearbeiten: Ich habe noch eine weitere method of accessing pixel data gefunden. Versuchen Sie dies, ohne Daten tatsächlich in for Schleife zu kopieren und zu überprüfen, ob es eine Verbesserung in FPS gibt. Dies könnte ein wenig schneller sein, da es keine Pixelmanipulation/Umwandlung dazwischen gibt.

+0

Ich habe auch versucht, es (Einzelkamera HDR Fall) durch die Profiling-Tools: die meisten Ereignisse, die die größten Erfolge in Bezug auf die Zeit waren hatte "CPU-Stillstand: Warten auf Ereignis", "CPU stall: Schlaf" auf sie geschrieben, was, denke ich, anzeigt, dass sie darauf warteten, dass die GPU aufholte? – HighVoltage

+0

Vielen Dank für den Kommentar: Wenn ich Stat Unitgraph ausführen, ist das, was ich finde: Kein HDR: Alle Threads mit einer anständigen Rate. HDR: Render-Thread-Spikes steigen stark an und verursachen Spikes im Frame-Timing. Gelegentliche GPU-Thread-Verzögerung. i.imgur.com/S1YVGaz.png – HighVoltage

+0

Es sieht so aus, als würde die ReadSurfaceData-Methode selbst im Render-Thread zu viel Zeit in Anspruch nehmen. Angenommen, Sie haben keine anderen Fehler gemacht (wie zum Beispiel die Lesefunktion mehrere Male aufgerufen), macht sie die Multithreading-Optimierung unmöglich, indem diese spezielle Methode zum Lesen von Pixeldaten verwendet wird. Haben Sie über die Verwendung von Raw DirectX nachgedacht?Sie können direkt von RHI-Objekten darauf zugreifen, es macht Ihr Projekt jedoch sehr plattformabhängig. – JKovalsky

Verwandte Themen