From time to time we collaborate with partner developers around the world, so that we can share even more VR development learnings with our community. Today, we hear from Brock Heinz of Turtle Rock Studios, as he provides a number of recommendations for using UE4 to develop mobile VR applications. Be sure to check out the latest UE4 Integration Release, we hope you find this latest post insightful!
If you’re new to working on the Quest, Go or GearVR, then you may not know how to use some of the standard development tools that you would rely on when developing a UE4 game. In this blog post, we’ll show you how to get those tools back in to your toolbox when developing for these Android based mobile VR devices. We used these same tools to help supercharge our development team while shipping
Journey of the Gods and
Face Your Fears 2 for the Oculus Quest!
Using ADB to control the device
You’ll be using the
Android Debug Bridge (adb) command line tool to work with your mobile VR device. To use it, open a Command Prompt and type "adb" followed by the command you want to use:
C:\Temp>adb devices
List of devices attached
e6555e96 device
Retrieving Logs
You can use the adb "pull" command to retrieve files from the device. This can be used to pull logs and other files as long as you know the full path:
adb pull /sdcard/UE4Game/ProjectName/ProjectName/Saved/Logs
You can take a screenshot from the command line, and then pull it from the device to your PC.
adb shell screencap -p /sdcard/screencap.png
adb pull /sdcard/screencap.png
Installing an application
UE4’s package building process will make .bat files that you can use to install / uninstall an application, but you can do it manually via adb if you need to. You can install them with the filename, and supply the "-g" flag to grant all permissions to the application. You install by specifying the APK filename:
adb install -g ProjectName-armv7-es2.apk
Uninstalling an application
You uninstall by specifying the package name:
adb uninstall com.yourcompany.yourproject
You can launch an application by issuing a command to the application manager and providing your application's package name:
adb shell am start -n
com.yourcompany.yourproject/com.epicgames.ue4.SplashActivity
adb shell am force-stop com.yourcompany.yourproject
Real Time Logging
You can get a lot of useful information by using android’s real time logging feature, commonly called “logcat”. You can see this by opening a command prompt and running:
You’ll see something like the following:
This will often include information that is not relevant to your current focus. You can use filters to only show the information you need. For example, these logcat filters will show you the
VrApi performance stats:
adb logcat VrApi:I --regex="FPS="
We find it easier to use the
Android Device Monitor utility to get real time logging data from the Quest. In addition to having an easy to use UI, it contains a number of other useful development tools.
To access Android Device Monitor make a shortcut that points at this batch file:
C:\NVPACK\android-sdk-windows\tools\monitor.bat
When you launch it, you’ll see a window like this one. To see logcat messages from your Quest, you’ll need to select it in the Devices list.
The first thing that you’ll want to do is go into the preferences and increase the size of the message buffer:
Now make a few filters. When you add the filter below, you’ll only see log messages from UE4 applications running on your Quest!
UE4 provides a way to send console commands to games on Android devices via broadcast intents:
adb shell "am broadcast -a android.intent.action.RUN -e cmd 'CONSOLE COMMAND GOES HERE'"
For example, this command will turn on an in-game frametime display:
adb shell "am broadcast -a android.intent.action.RUN -e cmd 'stat unit'"
However, this can get cumbersome to use. Luckily, you can use the “doskey” command to create macros that make this easier:
doskey com=adb shell "am broadcast -a android.intent.action.RUN -e cmd '$*'"
com stat unit
Diagnosing Crashes
If you need to investigate a crash, you have a few options. If it is easy to reproduce, you can use Android Device Monitor to get real time logging, then trigger the crash. As long as you have installed a development or debug build with symbols, you’ll see a callstack leading to the function that crashed and some other relevant info:
*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
Build fingerprint: 'oculus/vr_monterey/monterey:7.1.1/NGI77B/256550.6170.5:user/release-keys'
Revision: '0'
ABI: 'arm'
pid: 2571, tid: 2586, name: GameThread >>> com.yourcompany.yourproject <<<
signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr 0x3
r0 00000003 r1 0000007b r2 00000000 r3 00000a1a
r4 cf926570 r5 cf926570 r6 ffffffff r7 000f4240
r8 fc000000 r9 d11c5aa0 sl 000f4240 fp d1091868
ip fcf45380 sp d10915f0 lr ccef6c6c pc ccef6c74 cpsr 80070010
backtrace:
#00 pc 068dec74 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN7UEngine12PerformErrorEPKDsR13FOutputDevice+1260)
#01 pc 068c31a0 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN7UEngine4ExecEP6UWorldPKDsR13FOutputDevice+4388)
#02 pc 0602b480 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN11UGameEngine4ExecEP6UWorldPKDsR13FOutputDevice+1068)
#03 pc 06086628 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN19UGameViewportClient4ExecEP6UWorldPKDsR13FOutputDevice+1576)
#04 pc 061f4b0c /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN12ULocalPlayer4ExecEP6UWorldPKDsR13FOutputDevice+2264)
#05 pc 068b80d4 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN7UEngine20TickDeferredCommandsEv+932)
#06 pc 03359d50 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_ZN11FEngineLoop4TickEv+42496)
#07 pc 0334d51c /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (_Z11AndroidMainP11android_app+4348)
#08 pc 0335c400 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so (android_main+144)
#09 pc 0337df28 /data/app/com.yourcompany.yourproject-1/lib/arm/libUE4.so
#10 pc 000478d3 /system/lib/libc.so (_ZL15__pthread_startPv+22)
#11 pc 00019fe5 /system/lib/libc.so (__start_thread+6)
If you don’t have Android Device Monitor running when the game crashes, or if you need crash data from a headset that your QA team is using, you can use adb to retrieve bug reporting info from the device:
adb bugreport
/data/user_de/0/com.android.shell/files/bugreports/bugrepo...39.zip: 1 file pulled. 37.2 MB/s (1393690 bytes in 0.036s)
If you open that zip file, you’ll find a ton logs and other data about the state of the device. The information we want is in the “data/tombstones” folder. Look for a tombstone file with time and date stamp that corresponds when the crash occurred, and open it in a text editor. As long as you had symbols in the build, you should see a backtrace with the function calls leading to the crash, similar to the output above from Android Device Monitor.
Passing Command Line Switches
Out of the box, UE4 doesn’t have a way to pass a command line switch to the application, however, you can add this feature with a few code changes. These changes were made in UE 4.21, but should be easy to adapt to other engine versions.
GameActivity.java.template
// TRS CHANGE BEGIN
public String AndroidThunkJava_GetExtraCommandLineArg()
{
String extraArg = "";
try
{
if (_extrasBundle != null)
{
extraArg = _extrasBundle.getString("extraArg");
}
}
catch (Exception e)
{
Log.debug("[JAVA] - AndroidThunkJava_GetExtraCommandLineArg exception " + e.toString());
}
//Log.debug("AndroidThunkJava_GetExtraCommandLineArg returning " + extraArg);
return extraArg;
}
// TRS CHANGE END
class FJavaWrapper
{
public:
<SNIP> - around line 140
// TRS CHANGE BEGIN
static jmethodID AndroidThunkJava_GetExtraCommandLineArg;
// TRS CHANGE END
};
void FJavaWrapper::FindClassesAndMethods(JNIEnv* Env)
{
<SNIP> - around line 150
// TRS CHANGE BEGIN
AndroidThunkJava_GetExtraCommandLineArg = FindMethod(Env, GameActivityClassID, "AndroidThunkJava_GetExtraCommandLineArg", "()Ljava/lang/String;", bIsOptional);
// TRS CHANGE END
}
<SNIP> - around line 450
// TRS CHANGE BEGIN - Brock H. - 01/24/2019
jmethodID FJavaWrapper::AndroidThunkJava_GetExtraCommandLineArg;
// TRS CHANGE END - Brock H. - 01/24/2019
<SNIP> - around line 1600
// TRS CHANGE BEGIN
FString AndroidThunkCpp_GetExtraCommandLineArg()
{
FString ExtraArg;
if (JNIEnv* Env = FAndroidApplication::GetJavaEnv())
{
jstring extraArg = (jstring)FJavaWrapper::CallObjectMethod(Env, FJavaWrapper::GameActivityThis, FJavaWrapper::AndroidThunkJava_GetExtraCommandLineArg);
if (!Env->IsSameObject(extraArg, NULL))
{
const char *nativeandroidextraArgString = Env->GetStringUTFChars(extraArg, 0);
ExtraArg = FString(nativeandroidextraArgString);
Env->ReleaseStringUTFChars(extraArg, nativeandroidextraArgString);
Env->DeleteLocalRef(extraArg);
}
}
return ExtraArg;
}
// TRS CHANGE END
static void InitCommandLine()
{
<SNIP> - around line 300
// TRS CHANGE BEGIN
#if !UE_BUILD_SHIPPING
{
extern FString AndroidThunkCpp_GetExtraCommandLineArg();
FString ExtraArg = AndroidThunkCpp_GetExtraCommandLineArg();
if (!ExtraArg.IsEmpty())
{
FCommandLine::Append(TEXT(" "));
FCommandLine::Append(*ExtraArg);
FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Extra Command Line Arg: %s"), *ExtraArg);
UE_LOG(LogAndroid, Log, TEXT("Extra Command Line Arg: %"), *ExtraArg);
}
}
#endif
// TRS CHANGE END
}
Once you have this code in place, you can pass a command line argument to the game when you launch it via adb. For example, the code below will launch the game with garbage collection verification disabled, which helps reduce hitches in development builds:
adb shell am start -e extraArg -NoVerifyGC -n gg.trs.porter/com.epicgames.ue4.GameActivity
We have simplified this a bit by having the UE4 packaging process generate a “Launch.bat” file when it generates the install and uninstall batch files. It is an easy change in AndroidPlatform.Automation.cs if you want to make the same change for your project.
Basic Profiling
We can start to use the following tools to get some basic profiling information from the game while it’s running on a Quest or Go.
Real time performance stats
You can use the ‘stat unit’ command to get an onscreen display of real time performance statistics:
- Frame: Total frame time, in milliseconds. You want this at 16 for 60fps, 13 for 72 fps.
- Game: Main gameplay thread CPU frame time
- Draw: Render thread CPU time
- GPU: GPU frame time.
- RHIT: If you’ve enabled the OpenGL RHI Thread, this will be the time spent in that thread each frame.
- Memory: Memory used by the game. This isn’t 100% accurate, since some memory allocated by the OS, video driver, etc. won’t be reflected.
- Draws: Number of objects drawn per frame.
- Prims: Number of triangles drawn per frame.
Memory Usage
You can generate a memory report at any time. We’ll discuss this further in a future post:
com memreport -full
adb pull /sdcard/UE4Game/YourProject/YourProject/Saved/
CPU Profiling
The
Profiler Tool enables you to capture profiling stats on your device, and bring them in to the editor to be analyzed. You can turn on stats recording, play a bit of the game, then turn stats recording off and pull it with the following:
com stat startfile
< play the game for a bit >
com stat stopfile
adb pull /sdcard/UE4Game/YourProject/YourProject/Saved/
Enabling Logging in Shipping Builds
UE4 disables logs for shipping builds. Generally, this is a good policy, but there are times when you need logs from a build configured for distribution. Luckily, you can enable logging in shipping builds with just a few changes.
First, you’ll need to open the YourProject.Target.cs file, and add this line:
bUseLoggingInShipping = true;
Second, you’ll need to open AndroidMisc.cpp, and make this change:
void FAndroidMisc::LocalPrint(const TCHAR *Message)
{
// TRS CHANGE BEGIN
#if !UE_BUILD_SHIPPING || (USE_LOGGING_IN_SHIPPING==1)
// TRS CHANGE END
This will re-enable logging functionality for your Windows and Android builds. Note that if you want to find the logs from a shipping build on Windows, they will be in a different location:
C:\Users\UserName\AppData\Local\YourProject\Saved\Logs
Go Make Awesome Games!
Hopefully this post will help you get off to a good start during the early phases of developing mobile VR games with UE4. In my next post, I’ll discuss a few things we learned to help improve loading times and memory usage in
Journey of the Gods and
Face Your Fears 2.