Unreal Engine: accessing/saving in-game images to disk without blocking game thread - unreal-engine4

I am working on an Unreal based open-source UAV simulation (Microsoft AirSim) where I am trying to capture and save images from a camera that's attached to the drone. The image underneath gives an idea of how the game looks like. The rightmost view on the bottom is the actual view from the camera, the other two are just processed versions of the same image.
Right now the way it's set up in this way: There's a camera asset, which is read through the code as a capture component. The three views in the screenshot are linked to this capture component. The views are streamed without any problem as the drone is flown around in-game. But when it comes to recording screenshots, the current code sets up a TextureRenderTargetResource from this capture component, and subsequently calls ReadPixels and saves that data as an image (please see below for code flow). Using ReadPixels() as it is, is blocking the game thread directly and slowing down the whole game by a lot: drops from ~120 FPS to less than 10 FPS when I start recording.
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);
}
Looking at this article, it seems evident that ReadPixels() "will block the game thread until the rendering thread has caught up". The article contains sample code for a 'non-blocking' method of reading pixels (through removal of FlushRenderingCommands() and using a RenderCommandFence flag to determine when the task is done), but it doesn't significantly improve the performance: the rate at which images are saved is slightly higher, but the game thread still runs at about only 20 FPS, thus making it really hard to control the UAV. Are there any more efficient asynchronous methods that can achieve what I am trying to do, perhaps, say, in a separate thread? I am also a little confused as to why the code has no trouble streaming those images on-screen as fast as possible, but saving the images seems way more complicated. It's fine even if the images are saved to disk only at 15 Hz or so, as long as it doesn't interfere with the game's native FPS too much.

You can move the save-to-disk operation from Game thread to other thread.
Please check Async.h for detail
The limitation for thread is that you could not modify/add/delete uobject/uactor in other thread.
Multi-thread for UE4

I'm very late to the party, though in case anybody is still having issues with this: I built a non-blocking version inspired by code of AirSim's and UnrealCV's plugins because I had some serious compilation and versioning issues with those.. but needed a smoothly running camera while capturing images myself. This is also based on the async.h class of unreal API provides as the accepted answer suggested.
I setup a tutorial repo for this:
https://github.com/TimmHess/UnrealImageCapture.
Hoping it may help.

(...) it seems evident that ReadPixels() "will block the game thread until the rendering thread has caught up"
Using functions that try to access and copy GPU texture data is always a pain. I found that completion time of these functions depends on hardware or driver version. You could try to run your code on different (preferably faster or newer) GPU platform.
Back to code: to bypass single thread issue I would try to copy UTextureRenderTarget2D to UTexture2D using TextureRenderTarget2D::ConstructTexture2D method. When you get your image copied (I hope it take much faster) you could push it to consumer thread at 15 FPS ratio. Thread can be created using FRunnable or Async engine module (using so called Task Graph). Consumer thread could access cloned texture by using texture's PlatformData->Mips[0]->BulkData. Make sure that you are reading texture data between BulkData->Lock(...) and BulkData->Unlock() calls.
I'm not sure, but it could help because your copied texture will be not used by render thread so you could lock it's data without any blocking on render thread. I'm only worried about thread safety of Lock and Unlock operation (didn't found any clue), should be possible unless there are engine RHI calls.
Here is some related code that could be helpful.

Perhaps you should store screenshots in a TArray until the "game" is done, then process all stored data?
Or look at taking a screenshot from the drone camera instead of manipulating the pixel data? I assume the left and middle drone camera images are material-modified versions of the base camera image.
When you save an image, is it writing an image-format file or a .uasset?
Lastly - and this is a complete shot-in-the-dark - create a custom UActorComponent, add it to the drone, and make it watch for some instruction from the game. When you want to write an image, push the RenderTarget data to the custom UActorComponent. Since actor components can tick on any thread (using BCanTickOnAnyThread=true in the constructor, I think that's the variable name), the actor component's tick can watch for the incoming image data and process it there. I've never done it, but I have made an actor component tick on any thread (mine was checking physics).

Related

Unity - Texture2D.Loadimage resulting in MemoryWarning

I currently work on a 2D project on Unity (for mobiles/tablets), where I download a few heavy pictures by using UnityWebRequestTexture.GetTexture(url), which I then store using File.WriteAllBytes(filePath, webRequest.downloadHandler.data).
Later in the project, I need to show the downloaded picture on a RawImage element. To avoid blocking the app I read the bytes via FileStream on a separate Thread, which works well.
But when I call (from the main thread) ((Texture2D) myRawImage.texture).LoadImage(loadedBytes), my Profiler shows a huge peak, and if most devices handle it correctly, on iPad Mini I often get a MemoryWarning at this very point. Which, when repeated, leads to the app being forced close by iOS.
For info, the downloaded image's size vary depending on the device asking it. We initially intended to display 2048x1536 pictures that way on iPad Mini, but we were getting MemoryWarning on each display. We're now using 750x1334, but we still get MemoryWarning from time to time.
My question then is : is there a better way to do the "LoadImage" step ? I believe that the conversion from byte[] to Texture2D is where my memory problem lies, but I didn't find any workaround to this.
Or eventually: Is there a better way to download/store the image? If the image was in the app's resources from the beginning, I don't seem to have any trouble displaying it on the same device. Maybe there is a way to download the texture while applying it the same compression algorithm that assets receive when building an app ?
Using Texture2D.Compress() after loading the texture compresses the texture by at least 90%.
Reference

How Can I Record the Screen with Acceptable Performance While Keeping the UI Responsive?

I'm looking for help with a performance issue in an Objective-C based iOS app.
I have an iOS application that captures the screen's contents using CALayer's renderInContext method. It attempts to capture enough screen frames to create a video using AVFoundation. The screen recording is then combined with other elements for research purposes on usability. While the screen is being captured, the app may also be displaying the contents of a UIWebView, going out over the network to fetch data, etc... The content of the Web view is not under my control - it is arbitrary content from the Web.
This setup is working but as you might imagine, it's not buttery smooth. Since the layer must be rendered on the main thread, there's more UI contention than I'd like. What I'd like to do is to have a setup where the responsiveness of the UI is prioritized over the screen capture. For instance, if the user is scrolling the Web view, I'd rather drop frames on the recording than have a terrible scrolling experience.
I've experimented with several techniques, from dispatch_source coalescing to submitting the frame capture requests as blocks to the main queue to CADisplayLink. So far they all seem to perform about the same. The frame capture is currently being triggered in the drawRect of the screen's main view.
What I'm asking here is: given the above, what techniques would you suggest I try to achieve my goals? I realize the answer may be that there is no great answer... but I'd like to try anything, however wacky it might sound.
NOTE: Whatever techniques need to be App Store friendly. Can't use something like the CoreSurface hack that Display Recorder used/uses.
Thanks for your help!
"Since the layer must be rendered on the main thread" this is not true, as long as you don't touch UIKit.
Please see https://stackoverflow.com/a/12844171/136305
Maybe you can record at half resolution to speed up things, if that fits the requirements?

Reduced quality OpenGL ES screenshots (iPhone)

I'm currently using this method from Apple to take screenshots of my OpenGL ES iPhone game. The screenshots look great. However taking a screenshot causes a small stutter in the game play (which otherwise runs smoothly at 60 fps). How can I modify the method from Apple to take lower quality screenshots (hence eliminating the stutter caused by taking the screenshot)?
Edit #1: the end goal is to create a video of the game play using AVAssetWriter. Perhaps there's a more efficient way to generate the CVPixelBuffers referenced in this SO post.
What is the purpose of the recording?
If you want to replay a sequence on the device you can look into saving the object positions etc instead and redraw the sequence in 3D. This also makes it possible to replay sequences from other view positions.
If you want to show the game play on i.e. youtube or other you can look into recording the game play with another device/camera or record some game play running in the simulator using some screen capture software as ScreenFlow.
The Apple method uses glReadPixels() which just pulls all the data across from the display buffer, and probably triggers sync barriers, etc, between GPU and CPU. You can't make that part faster or lower resolution.
Are you doing this to create a one-off video? Or are you wanting the user to be able to trigger this behavior in the production code? If the former, you could do all sorts of trickery to speed it up-- render to a smaller size for everything, don't present at all and just capture frames based on a recording of the input data running into the game, or other such tricks, or going even further run that whole simulation at half speed to get all the frames.
I'm less helpful if you need an actual in-game function for this. Perhaps someone else will be.
If all else fails.
Get one of these
http://store.apple.com/us/product/MC748ZM/A
And then convert that composite video to digital through some sort of external device.
I've done this when I converted vhs movies to dvd a long time ago.

iOS frame by frame animation, by script

There are a few SO questions regarding frame by frame animation (such as frame by frame animation and other similar questions), however I feel mine is different so here goes.
This is partially a design question from someone with very little ios experience.
I'm not sure "frame by frame" is the correct description of what I want to do so let me describe that. Basically, I have a "script" of an animated movie and I'd like to play this script.
This script is a json file which describes a set of scenes. In each scene there are a few elements such as a background image, a list of actors with their positions and a background sound clip. Further, for each actor and background there's an image file that represents it. (it's a bit more complex - each actor has a "behavior" such as how it blinks, how he talks etc). So my job is to follow the given script referencing actors and background and with every frame, place the actors in their designated position, draw the correct background and play the sound file.
The movie may be paused, scrubbed forward or backward similar to youtube's movie player functionality.
Most of the questions I've seen which refer to frame-by-frame animation have different requirements than I do (I'll list some more requirements later). They usually suggest to use animationImages property of a UIImageView. This is fine for animating a button or a checkbox but they all assume there's a short and predefined set of images that need to be played.
If I were to go with animationImages I'd have to pre-create all the images up front and my pure guess is that it won't scale (think about 30fps for one minute, you get 60*30=1800 images. Plus the scrub and pause/play abilities seem challenging in this case).
So I'm looking for the right way to do this. My instinct, and I'm learning more as I go, is that there are probably three or four main ways to achieve this.
By using Core Animations and defining "keypoints" and animated transitions b/w those keypoints. For example if an actor needs to be at point A at time t1 and point B at time t2 then all I need to do it animate what's in between. I've done something similar in ActionScript in the past and it was nice but was particularly challenging to implement the scrub action and keep everytyhing in sync so I'm not a big fan of the approach. Imagine that you have to implement a pause in the middle of an animation or scrub to a middle of an animation. It's doable but not pleasant.
Set a timer for, say 30 times a second and on every tick consult the model (the model is the script json file along with the description of the actors and the backgrounds) and draw what needs to be drawn at this time. Use Quartz 2D's API and drawRect. This is probably the simple approach and but I don't have enough experience to tell how well it's going to work on different devices, probably CPU wise, it all depends on the amount of calculations I need to make on each tick and the amount of effort it takes ios to draw everything. I don't have a hunch.
Similar to 2, but use OpenGL to draw. I prefer 2 b/c the API is easier but perhaps resource wise OpenGL is more suitable.
Use a game framework such as cocos2d which I'd never used before but seems to be solving more or less similar problems. They seem to have a nice API so I'd be happy if I could find all my requirements answered by them.
Atop of the requirements I'd just described (play a movie given it's "script" file and a description of the actors, backgrounds and sounds), there's another set of requirements -
The movie needs to be played in full screen mode or partial screen mode (where the rest of the screen is dedicated to other controls)
I'm starting with the iphone by naturally an ipad should follow.
I'd like to be able to create a thumbnail of this movie for local phone use (display it in a gallery in my application). The thumbnail may just be the first frame of the movie.
I want to be able to "export" the result as a movie, something that could be easily uploaded to youtube or facebook.
So the big question here is whether any of the suggested 1-4 implementations I have in mind (or others you might suggest) can somehow export such a movie.
If all four fail on the movie export task then I have an alternative in mind. The alternative is to use a server which runs ffmpeg and which accept a bundle of all the movie images (I'd have to draw them in the phone and upload them to the sever by their sequence) and then the server would compile all the images with their soundtrack to a single movie.
Obviously to keep things simple I'd prefer to do this server-less, i.e. be able to export the movie from the iphone but if that's too much to ask then the last requirement would be to at least be able to export the set of all images (keyframes in the movie) so I can bundle them and upload to a server.
The length of the movie is supposed to be a one or two minutes. I hope the question wasn't too long and that it's clear...
Thanks!
well written question. for your video export needs check out AVFoundation (available as of iOS 4). If I were going to implement this, I'd try #1 or #4. I think #1 might be the quickest to just try out, but that's probably because I don't have any experience with cocos2d. I think you will be able to pause and scrub CoreAnimation: check out the CAMediaTiming protocol it adopts.
Ran, you do have a number of options. You are not going to find a "complete solution" but it will be possible to make use of existing libraries in order to skip a bunch of implementation and performance issues. You can of course try to build this whole thing in OpenGL, but my advice is that you go with another approach. What I suggest is that you render the entire "video" frame by frame on the device based on your json settings. That basically comes down to setting up your scene elements and then determining the positions of each element for times [0, 1, 2] where each number indicates a frame at some framerate (15, 20, or 24 FPS would be more than enough). First off, please have a look at my library for non-trivial iOS animations, in it you will find a class named AVOfflineComposition that does the "comp items and save to a file on disk" step. Obviously, this class does not do everything you need, but it is a good starting point for the basic logic of creating a comp of N elements and writing the results out to a video file. The point of creating a comp is that all of your code that reads settings and places objects at a specific spot in the comp can be run in an offline mode and the result you get at the end is a video file. Compare this to all the details involved with maintaining all theses elements in memory and then going forward more quickly or slowly depending on how quickly everything is running.
The next step will be to create 1 audio file that is the length that the "movie" of all the comped frames and have it include any sounds at specific times. This basically means mixing the audio at runtime and saving the results to an output file so that the results are easy to play with AVAudioPlayer. You can have a look at some very simple PCM mixer code that I wrote for this type of thing. But, you might want to consider a more complete audio engine like theamazingaudioengine.
Once you have an audio file and a movie file, these can be played together and kept in sync quite easily using the AVAnimatorMedia class. Take a look at this AVSync example for source code that shows a tightly synced example of playing a video and showing a movie.
Your last requirement can be implemented with the AVAssetWriterConvertFromMaxvid class, it implements logic that will read a .mvid movie file and write it as a h.264 encoded video using the h.264 encoder hardware on the iPhone or iPad. With this code, you will not need to write a ffmpeg based server module. Also, that would not work anyway because it would take too long to upload all the uncompressed video to your server. You need to compress the video to h.264 before it can be uploaded or emailed from the app.

OpenGL ES on iPhone: timer-based painting problems, jitteryness

I have an OpenGL ES 1.1 project on iPhone, based heavily on the empty OpenGL ES application given here:
http://iphonedevelopment.blogspot.com/2009/06/empty-opengl-es-application-project.html
I'm testing on a 3G (not 3GS) device, 8GB.
The paint loop in my app which does openGL operations is doing quite a lot each time it renders the screen. However, in situations with it doing the same thing each paint cycle, I'm seeing variable frame rates. I have set the NSTimer which fires the paint code to fire 30 times a second -- i.e. every 0.0333 of a second. The main problem is that whereas my actual paint code often takes approximately that amount of time to execute one paint (in terms of wall time), it varies, and sometimes it takes far longer, for no apparent reason. Using careful logging to report maximum time intervals when they occur, I can see that sometimes my paint has taken as long as 0.23 sec to complete - that's like 4FPS, which compared to 30FPS is like skipping 5 frames of animation/ user interaction, which isn't very acceptable.
At first I suspected my paint loop was snagging on some lock (there aren't many in there, because the GL render stuff in on the main thread (which is necessary AFAIK), as is incoming event handling) but some logging with finer granularity revealed that, in one paint code execution cycle, a large time elapsing over a bit of code that was doing basically hardly anything, and certainly not a GL operation.
So it seems a bit like my GL drawing thread (i.e. the main thread) just takes longer sometimes for no good reason. I have comms in my application and I disabled comms to see if that was the problem -- but I still see some "spikes" in execution time of my painting, when it's doing the same painting each time.
It's seems like another thread is just being switched to, mid-paint code, for ages, before returning to my paint code, on occaison.
Any ideas with how to analyse further what is going on? I know NSTimers aren't perfect and aren't at a guaranteed frequency, but the main issue here is that my actual paint cycle sometimes just takes forever, presumably because some other thread gets switched to....
Keep in mind that your application can seem to "hang" for no reason that has nothing to do with your "main loop". That's because you are multitasking... and in paticular, something as simple as your phone checking email can cause this sort of problem. One big cause, on the iPhone anyway, is when you move through different cell sites (like if you are on a subway or in a car) you can sometimes get spikes as it does... whatever it does.
If you are on an iPhone, try airplane mode and see if the problems go away.
-- David