Skip to content

Latest commit

 

History

History
921 lines (693 loc) · 41.1 KB

USAGE_android.md

File metadata and controls

921 lines (693 loc) · 41.1 KB

LunarG

Creative Commons

Copyright © 2018-2023 LunarG, Inc.

GFXReconstruct API Capture and Replay - Android

This document describes the GFXReconstruct software for capturing and replaying Vulkan API calls on Android devices.

If you are looking for capturing/replaying on a different platform, please refer to one of these other documents:

Index

  1. Behavior on Android
    1. Android Writable Locations
  2. Capturing API Calls
    1. Before Use
    2. To Root or Not To Root
    3. Enabling the Layer with ADB
    4. Capture Options
    5. Capture Files
    6. Capture Limitations
    7. Troubleshooting Capturing of Applications
  3. Replaying API Calls
    1. Launch Script
    2. Install APK Command
    3. Replay Command
    4. Touch Controls
    5. Key Controls
    6. Limitations of Replay On Android
    7. Troubleshooting Replay of Applications
  4. Android Detailed Examples

Behavior on Android

The purpose of this section is to describe some of the software changes made to the GFXReconstruct software to add Android support. This section will not provide a comprehensive list of changes, but will instead highlight some of the primary adjustments required to adapt the GFXReconstruct software to the Android ecosystem.

Android Writable Locations

The contents of the traces should be written to external storage on the Android device. The final "external storage" result varies based on Android version but some locations that can be tried are:

  • /sdcard/Download
  • /storage/emulated/0/Download
  • /sdcard/Android/data/${Application Full Name}
  • /sdcard
  • /mnt/shell/emulated/0

Where ${Application Full Name} is the full name of the application, such as com.khronos.vulkand_samples.

Some devices won't allow access to those folders for certain applications. In those cases, the following folders can be used, but will require adb root access to retrieve the files:

  • /data/data/${Application Full Name}/
  • /data/user/0/${Application Full Name}/

NOTE: These directories may not be visible to other applications (including gfxrecon-replay, adb pull), so any capture files will need to be copied to a readable location with adb shell before they can be replayed.

Capturing API Calls

The GFXReconstruct capture layer is a Vulkan layer that intercepts Vulkan API calls and logs them to a GFXReconstruct capture file.

Before Use

Permissions

The GFXReconstruct layer can optionally read a configuration file from or write capture files to external storage. This requires that the application loading the layer have external storage permissions.

The read and write external storage permission may be requested in the application's manifest file. When installing the application, it may be necessary to ensure that the requested permissions are granted through one of the following actions:

When installing the application with adb install:

  • Specify the -g option: adb install -g

When deploying from Android Studio:

  • Click on "Run" in the menu
  • Choose "Edit Configurations..."
  • In the dialog box, look for the "Install Flags:" text box
  • Enter -g
  • Click "Apply"

It may also be possible to grant external storage permissions to an installed application through the device Settings.

Failure to enable the write external storage permission can cause the layer to return VK_ERROR_INITIALIZATION_FAILED from its vkCreateInstance function if it fails to create a capture file.

Understanding GFXReconstruct Layer Memory Capture

The Vulkan API allows Vulkan memory objects to be mapped by an application for direct modification. To successfully capture an application, the GFXReconstruct layer must be able to detect when the application modifies the mapped memory.

The layer can be configured to detect memory modifications by marking the mapped memory as write protected, triggering an access violation when the application writes to the memory. The layer then uses a signal handler to intercept the signal generated by the access violation, where it removes the write protection, marks the modified memory page as dirty, and allows the application to continue.

Disabling Debug Breaks Triggered by the GFXReconstruct Layer

When running an application in a debugger with the layer enabled, the access violations triggered by the layer's memory tracking behavior may cause the debugger to break. These debug breaks may be disabled for LLDB with the following command:

process handle SIGSEGV -n true -p true -s false

This command may be entered manually through the LLDB tab on Android Studio's Debug panel.

It may also be set as a post attach command in the project configuration:

  • Click on "Run" in the menu
  • Choose "Edit Configurations..."
  • In the dialog box, select the "Debugger" tab
  • In the "Debugger" tab, select the "LLDB Post Attach Commands" tab
  • Click the + to add the command to the command list
  • Enter the process handle SIGSEGV -n true -p true -s false command
  • Click "Apply"

To Root Or Not To Root

Rooting the Android device for capture is not required if you are attempting to capture an application that is built in Debug mode. This is especially true if you can build in Debug mode and associate it with the GFXReconstruct layer during the build.

One of the ways to set the application to debug requires modifying the application Manifest file and setting the android:debuggable flag to true before rebuilding. Refer to the following info: https://developer.android.com/guide/topics/manifest/application-element#debug

Alternatively, if you are building using Gradle, then make sure that the application's build.gradle contains the debuggable flag under the appropriate "buildTypes" section:

debug {
   debuggable true
}

However, if you can not do that, your only option is to attempt to capture the image on a rooted device. Instructions for rooting Android devices are available on the internet.

If not building the application with the layer, several additional steps are required once the application is installed on a rooted Android device:

Start ADB Shell With Admin Privileges

adb shell
su

Locate the Application Data Directory

find /data/app -name *${Application Full Name}*

NOTE: Replace ${Application Full Name} with the full Android name of the application being captured, for example "com.khronos.vulkan_samples" is the full name of the Vulkan Samples. You can find out the application full name by watch Logcat output if you don't already know it.

Find the Appropriate Capture Layer .so

By default, the capture layer's shared object file will be written to:

./android/layer/build/intermediates/cmake/${Build Type}/obj/${Build Target}/libVkLayer_gfxreconstruct.so

${Build Type} is the build type used when compiling the layer: "debug" or "release".

${Build Target} is the platform target: "arm64-v8a", "armeabi-v7a", etc.

Copy the Capture Layer Onto the Device

The data folder returned by the instructions in Locate the Application Data Directory above can be used to store the layer, but it must be placed in the appropriate library path. However, you can't copy it directly, so you need to first push the file into either the /sdcard/Download or /storage/emulated/0/Download directory with an adb command:

adb push \
    ./android/layer/build/intermediates/cmake/${Build Type}/obj/${Build Target}/libVkLayer_gfxreconstruct.so \
    /sdcard/Download

For example, for a debug build of the ARM64 version of the library:

adb push \
    ./android/layer/build/intermediates/cmake/debug/obj/arm64-v8a/libVkLayer_gfxreconstruct.so \
    /sdcard/Download

Then move it to the application folder using the adb shell with Admin authority:

mv /sdcard/Download/libVkLayer_gfxreconstruct.so \
       ${Application Data Path}/lib/arm64/libVkLayer_gfxreconstruct.so

Make the layer copied over above writeable on the system using the ADB Admin shell:

chmod 777 ${Application Data Path}/lib/arm64/libVkLayer_gfxreconstruct.so

Give Application Write Permissions

You may also have to give the application permission to write to external storage using:

adb shell pm grant ${Application Full Name} android.permission.WRITE_EXTERNAL_STORAGE

Enabling the Layer with ADB

Enable the Layer a Specific Application

To enable the GFXReconstruct capture layer just for a specific application requires that 4 steps be taken:

  1. Make sure the application is debuggable
  2. Enable GPU Debug layers
  3. Indicate what app you are enabling GPU Debug on
  4. Identify the layers used to debug (in this case the layer is VK_LAYER_LUNARG_gfxreconstruct)

Together, the last 3 commands look like the following.

adb shell settings put global enable_gpu_debug_layers 1
adb shell settings put global gpu_debug_app ${Application Full Name}
adb shell settings put global gpu_debug_layers VK_LAYER_LUNARG_gfxreconstruct

NOTE: Replace ${Application Full Name} with the full Android name of the application being captured, for example "com.khronos.vulkan_samples" is the full name of the Vulkan Samples.

If you attempt to capture and nothing is happening, check the logcat output. A successful run of GFXReconstruct should show a message like the following:

I gfxrecon: Initializing GFXReconstruct capture layer

If that fails to show up, you may have to try enabling the layer for the entire system.

Enable the Layer for the Entire System

The layer can be enabled through a system property by executing the following ADB command:

adb shell "setprop debug.vulkan.layers 'VK_LAYER_LUNARG_gfxreconstruct'"

The following command will disable the layer:

adb shell "setprop debug.vulkan.layers ''"

NOTE: The downside to this alternative is that some Android systems use Vulkan to render the UI. In this scenario, enabling the layer may cause the desktop UI to either be recorded as well, or possibly become unresponsive. If this occurs, the UI can be forced to run in OpenGL mode on most Android devices using:

adb shell "setprop debug.hwui.renderer 'skiagl'"

Capture Options

The GFXReconstruct layer supports the following options, which may be enabled through Android system properties or a layer settings file. Each Android system property begins with the prefix debug.gfxrecon, and can be set through ADB with the following command syntax:

adb shell "setprop <option> '<value>'"

For example, to set the log_level to "warning", specify:

adb shell "setprop debug.gfxrecon.log_level 'warning'"

Supported Options

Options with the BOOL type accept the following values:

  • A case-insensitive string value 'true' or a non-zero integer value indicate true.
  • A case-insensitive string value 'false' or a zero integer value indicate false.

The capture layer will generate a warning message for unrecognized or invalid option values.

Option Property Type Description
Capture File Name debug.gfxrecon.capture_file STRING Path to use when creating the capture file. Default is: /sdcard/gfxrecon_capture.gfxr
Capture Specific Frames debug.gfxrecon.capture_frames STRING Specify one or more comma-separated frame ranges to capture. Each range will be written to its own file. A frame range can be specified as a single value, to specify a single frame to capture, or as two hyphenated values, to specify the first and last frame to capture. Frame ranges should be specified in ascending order and cannot overlap. Note that frame numbering is 1-based (i.e. the first frame is frame 1). Example: 200,301-305 will create two capture files, one containing a single frame and one containing five frames. Default is: Empty string (all frames are captured).
Capture trigger for Android debug.gfxrecon.capture_android_trigger BOOL Set during runtime to true to start capturing and to false to stop. If not set at all then it is disabled (non-trimmed capture). Default is not set.
Capture File Compression Type debug.gfxrecon.capture_compression_type STRING Compression format to use with the capture file. Valid values are: LZ4, ZLIB, ZSTD, and NONE. Default is: LZ4
Capture File Timestamp debug.gfxrecon.capture_file_timestamp BOOL Add a timestamp to the capture file as described by Timestamps. Default is: true
Capture File Flush After Write debug.gfxrecon.capture_file_flush BOOL Flush output stream after each packet is written to the capture file. Default is: false
Log Level debug.gfxrecon.log_level STRING Specify the highest level message to log. Options are: debug, info, warning, error, and fatal. The specified level and all levels listed after it will be enabled for logging. For example, choosing the warning level will also enable the error and fatal levels. Default is: info
Log Output to Console debug.gfxrecon.log_output_to_console BOOL Log messages will be written to Logcat. Default is: true
Log File debug.gfxrecon.log_file STRING When set, log messages will be written to a file at the specified path. Default is: Empty string (file logging disabled).
Log Detailed debug.gfxrecon.log_detailed BOOL Include name and line number from the file responsible for the log message. Default is: false
Log Allow Indents debug.gfxrecon.log_allow_indents BOOL Apply additional indentation formatting to log messages. Default is: false
Log Break on Error debug.gfxrecon.log_break_on_error BOOL Trigger a debug break when logging an error. Default is: false
Log File Create New debug.gfxrecon.log_file_create_new BOOL Specifies that log file initialization should overwrite an existing file when true, or append to an existing file when false. Default is: true
Log File Flush After Write debug.gfxrecon.log_file_flush_after_write BOOL Flush the log file to disk after each write when true. Default is: false
Log File Keep Open debug.gfxrecon.log_file_keep_open BOOL Keep the log file open between log messages when true, or close and reopen the log file for each message when false. Default is: true
Memory Tracking Mode debug.gfxrecon.memory_tracking_mode STRING Specifies the memory tracking mode to use for detecting modifications to mapped Vulkan memory objects. Available options are: page_guard, assisted, and unassisted. Default is page_guard
  • page_guard tracks modifications to individual memory pages, which are written to the capture file on calls to vkFlushMappedMemoryRanges, vkUnmapMemory, and vkQueueSubmit. Tracking modifications requires allocating shadow memory for all mapped memory and that the SIGSEGV signal is enabled in the thread's signal mask.
  • assisted expects the application to call vkFlushMappedMemoryRanges after memory is modified; the memory ranges specified to the vkFlushMappedMemoryRanges call will be written to the capture file during the call.
  • unassisted writes the full content of mapped memory to the capture file on calls to vkUnmapMemory and vkQueueSubmit. It is very inefficient and may be unusable with real-world applications that map large amounts of memory.
Page Guard Copy on Map debug.gfxrecon.page_guard_copy_on_map BOOL When the page_guard memory tracking mode is enabled, copies the content of the mapped memory to the shadow memory immediately after the memory is mapped. Default is: true
Page Guard Separate Read Tracking debug.gfxrecon.page_guard_separate_read BOOL When the page_guard memory tracking mode is enabled, copies the content of pages accessed for read from mapped memory to shadow memory on each read. Can overwrite unprocessed shadow memory content when an application is reading from and writing to the same page. Default is: true
Page Guard Persistent Memory debug.gfxrecon.page_guard_persistent_memory BOOL When the page_guard memory tracking mode is enabled, this option changes the way that the shadow memory used to detect modifications to mapped memory is allocated. The default behavior is to allocate and copy the mapped memory range on map and free the allocation on unmap. When this option is enabled, an allocation with a size equal to that of the object being mapped is made once on the first map and is not freed until the object is destroyed. This option is intended to be used with applications that frequently map and unmap large memory ranges, to avoid frequent allocation and copy operations that can have a negative impact on performance. This option is ignored when GFXRECON_PAGE_GUARD_EXTERNAL_MEMORY is enabled. Default is false
Page Guard Align Buffer Sizes debug.gfxrecon.page_guard_align_buffer_sizes BOOL When the page_guard memory tracking mode is enabled, this option overrides the Vulkan API calls that report buffer memory properties to report that buffer sizes and alignments must be a multiple of the system page size. This option is intended to be used with applications that perform CPU writes and GPU writes/copies to different buffers that are bound to the same page of mapped memory, which may result in data being lost when copying pages from the page_guard shadow allocation to the real allocation. This data loss can result in visible corruption during capture. Forcing buffer sizes and alignments to a multiple of the system page size prevents multiple buffers from being bound to the same page, avoiding data loss from simultaneous CPU writes to the shadow allocation and GPU writes to the real allocation for different buffers bound to the same page. This option is only available for the Vulkan API. Default is false
Omit calls with NULL AHardwareBuffer* debug.gfxrecon.omit_null_hardware_buffers BOOL Some GFXReconstruct capture files may replay with a NULL AHardwareBuffer* parameter, for example, vkGetAndroidHardwareBufferPropertiesANDROID. Although this is invalid Vulkan usage, some drivers may ignore these calls and some may not. This option causes replay to omit Vulkan calls for which the AHardwareBuffer* would be NULL. Default is false
Page guard unblock SIGSEGV debug.gfxrecon.page_guard_unblock_sigsegv BOOL When the page_guard memory tracking mode is enabled and in the case that SIGSEGV has been marked as blocked in thread's signal mask, setting this enviroment variable to true will forcibly re-enable the signal in the thread's signal mask. Default is false
Page guard signal handler watcher debug.gfxrecon.page_guard_signal_handler_watcher BOOL When the page_guard memory tracking mode is enabled, setting this enviroment variable to true will spawn a thread which will periodically reinstall the SIGSEGV handler if it has been replaced by the application being traced. Default is false
Page guard signal handler watcher max restores debug.gfxrecon.page_guard_signal_handler_watcher_max_restores INTEGER Sets the number of times the watcher will attempt to restore the signal handler. Setting it to a negative value will make the watcher thread run indefinitely. Default is 1

Settings File

Capture options may also be specified through a layer settings file. The layer settings file will be loaded before the Android system properties are processed, allowing system properties to override individual settings file entries.

The debug.gfxrecon.settings_path Android system property is used to enable a settings file:

adb shell "setprop debug.gfxrecon.settings_path /sdcard/vk_layer_settings.txt"

The system property may be set as either the path to the folder containing a file named vk_layer_settings.txt or the full path to a file with a custom name. When set to a folder, the capture layer will try to open a file in that folder named vk_layer_settings.txt. When set to a file, the capture layer will try to open a file with the specified name.

The settings file may be combined with settings files for other layers. The capture layer will ignore entries that do not start with the 'lunarg_gfxreconstruct.' prefix.

A sample layer settings file, documenting each available setting, can be found in the GFXReconstruct GitHub repository at layer/vk_layer_settings.txt. Most binary distributions of the GFXReconstruct software will also include a sample settings file.

Selecting Settings for the page_guard Memory Tracking Mode

The default settings selected for the page_guard memory tracking mode are the settings that are most likely to work on a given platform, but may not provide the best performance for all cases.

If capture performs poorly with the the default settings, try setting debug.gfxrecon.page_guard_persistent_memory to true.

If corruption is observed during capture, try setting debug.gfxrecon.page_guard_align_buffer_sizes to true. If this does not help, try setting debug.gfxrecon.page_guard_separate_read to false.

Capture Files

Capture files are created on the first call to vkCreateInstance, when the Vulkan loader loads the capture layer, and are closed on vkDestroyInstance, when the last active instance is destroyed and the layer is unloaded.

If multiple instances are active concurrently, only one capture file will be created. If multiple instances are active consecutively (i.e. an instance is created and destroyed before the next instance is created), the creation of each instance will generate a new file. For applications that create multiple instances consecutively, it will be necessary to enable capture file timestamps to prevent each new instance from overwriting the file created by the previous instance.

If the layer fails to open the capture file, it will make the call to vkCreateInstance fail, returning VK_ERROR_INITIALIZATION_FAILED.

Specifying Capture File Location

The capture file's save location can be specified by setting the debug.gfxrecon.capture_file system property, described above in the Layer Options section.

Please note that only some directories are writable. See Android Writable Locations for more info.

Timestamps

When capture file timestamps are enabled, a timestamp with an ISO 8601-based format will be added to the name of every file created by the layer. The timestamp is generated when the capture file is created by the layer's vkCreateInstance function and is added to the base filename specified through the debug.gfxrecon.capture_file system property. Timestamps have the form:

_yyyymmddThhmmss

where the lower-case letters stand for: Year, Month, Day, Hours, Minutes, Seconds. The T is a designator that separates the date and time components. Time is reported for the local timezone and is specified with the 24-hour format.

The following example shows a timestamp that was added to a file that was originally named gfxrecon_capture.gfxr and was created at 2:35 PM on November 25, 2018: gfxrecon_capture_20181125T143527.gfxr

Retrieving a Trace

First check the directory to make sure a trace exists:

adb shell ls ${location}

For example:

adb shell ls /sdcard/Download

If the trace exists there, for example as "gfxrecon_trace.gfxr", it can be retrieved from the Android device using the adb pull command:

adb pull ${location}/${filename}

Such as:

adb pull /sdcard/Download/gfxrecon_trace.gfxr

This will download the file to the current directory.

Capture Limitations

Conflicts With Crash Detection Libraries

As described in Understanding GFXReconstruct Layer Memory Capture, the capture layer uses a signal handler to detect modifications to mapped memory. Only one signal handler for that signal can be registered at a time, which can lead to a potential conflict with crash detection libraries that will also register a signal handler.

Conflict between the capture layer and crash detection libraries depends on the order with which each component registers its signal handler. The capture layer will not register its signal handler until the first call to vkMapMemory. As long as the application initializes the crash detection library before calling vkMapMemory, there should be no conflict.

The conflict occurs when the application initializes its Vulkan component and its crash detection library concurrently. Applications have been observed to initialize Vulkan and begin uploading resources with one or more threads, while at the same time initializing a crash detection library from another thread. For this scenario, the crash detection library sets its signal handler after the first call to vkMapMemory, while a resource upload thread is actively writing to the mapped memory. After the crash detection library sets its signal handler, it immediately receives a SIGSEGV event generated by the concurrent write to mapped memory, which it detects as a crash and terminates the application.

Memory Tracking Limitations

There is a limitation with the page guard memory tracking method used by the GFXReconstruct capture layer. The logic behind that method is to apply a memory protection to the guarded/shadowed regions so that accesses made by the user to trigger a segmentation fault which is handled by GFXReconstruct. If the access is made by a system call (like fread()) then there won't be a segmentation fault generated and the function will fail. As a result the mapped region will not be updated.

Android-Specific Limitations

The current version of the Android capture layer has the following limitations:

  • The capture layer must be deployed and enabled manually. Future work may provide a tool to automate the process of launching a debuggable app, injecting the trace layer with the Java Debug Wire Protocol (JDWP), and enabling the layer.
  • Writing files to external storage requires the external storage permission. If the app to be captured does not already have this permission, it can be granted manually through the Applications panel from device Settings or granted through adb using the pm command as listed later.
  • Adding a timestamp to the filename does not guarantee a unique filename. Some applications will query for Vulkan support by creating an instance, retrieving instance properties, and then destroying the instance. After the initial support query, the application will create a second instance to do its real work. Each of these Vulkan sessions will generate a capture file. If the time elapsed between these two sessions is less than a second, both sessions will use the same file name, with the second session overwriting the file produced by the first session.

Troubleshooting Capturing of Applications

There are several steps to take to determine why a capture may not be working. Attempt each of these and re-run after the suggested modifications to see if the problem is resolved.

Enable LogCat

First, run logcat showing only messages for GFXReconstruct:

adb logcat -s gfxrecon

This should report messages that may be useful in determining the source of the capture error.

An extended option would be to look for both gfxrecon and vulkan messages using this command:

adb logcat -s gfxrecon,vulkan

Stop Your App Before Capture

Sometimes, the application could still be running in the background prior to enabling the capture layer. In this case, you must force stop the application and restart it in order for the capture to work properly.

adb shell am force-stop ${Application Full Name}

NOTE: Replace ${Application Full Name} with the full Android name of the application being captured, for example "com.khronos.vulkan_samples" is the full name of the Vulkan Samples.

Forcing Aligned Pages

In some cases, the application may write to buffers that are bound in the same mapped page of memory resulting in data being lost when copying pages from the page_guard shadow allocation to the real allocation. This data loss can result in visible corruption during capture.

To avoid this issue when using page_guard, enable page aligned memory:

adb shell "setprop debug.gfxrecon.page_guard_align_buffer_sizes 'true'"
Application Write Access Issues

debug.gfxrecon.capture_file is the most important as the capture layer will fail if the location isn’t writable by the target app.

See locations list in Android Writable Locations above for ideas of where to store your capture file.

If logcat output shows a line like the following:

E gfxrecon: fopen(/storage/emulated/0/Download/gfxrecon_capture_20220607T132930.gfxr, wb) failed (errno = 13)

Then you may need to set the “Files and Media” permission on the application. That may be possible through the application’s settings in the Android interface, or you can try “granting” the permission through the pm command:

adb shell pm grant ${Application Full Name} android.permission.WRITE_EXTERNAL_STORAGE

NOTE: Replace ${Application Full Name} with the full Android name of the application being captured, for example "com.khronos.vulkan_samples" is the full name of the Vulkan Samples.

Refer to the other settings in Capture Options.

Replaying API Calls

Launch Script

The gfxrecon.py script, located in android/scripts directory of the gfxreconstruct git repository is provided as a convenient method for deploying and launching the Android replay tool. The script currently supports the following commands:

Command Description
install-apk Install the specified APK file with the -g -t -r options.
replay Launch the replay tool with the specified command line options.

Install APK Command

The gfxrecon.py install-apk command has the following usage:

usage: gfxrecon.py install-apk [-h] <file>

positional arguments:
  file        APK file to install

optional arguments:
  -h, --help  show this help message and exit

The command is equivalent to:

adb install -g -t -r <file>

The install-apk option of the gfxrecon.py script with the install-apk option is is a convenient way to install the gfxrecon replay tool.

For example, starting at the root directory of the GFXReconstruct repo after building, install the replay tool using the following commands:

pushd android
python scripts/gfxrecon.py install-apk tools/replay/build/outputs/apk/debug/replay-debug.apk
popd

Replay Command

The gfxrecon.py replay command has the following usage:

usage: gfxrecon.py replay [-h] [--push-file LOCAL_FILE] [--version] [--pause-frame N]
                          [--paused] [--screenshot-all] [--screenshots RANGES]
                          [--screenshot-format FORMAT] [--screenshot-dir DIR]
                          [--screenshot-prefix PREFIX] [--sfa] [--opcd]
                          [--surface-index N] [--sync] [--remove-unsupported]
                          [-m MODE] [--use-captured-swapchain-indices]
                          [file]

Launch the replay tool.

positional arguments:
  file                  File on device to play (forwarded to replay tool)

optional arguments:
  -h, --help            show this help message and exit
  --version             Print version information and exit (forwarded to
                        replay tool)
  --pause-frame N       Pause after replaying frame number N (forwarded to
                        replay tool)
  --paused              Pause after replaying the first frame (same as "--
                        pause-frame 1"; forwarded to replay tool)
  -p LOCAL_FILE, --push-file LOCAL_FILE
                        Local file to push to the location on device specified
                        by <file>
  --screenshot-all      Generate screenshots for all frames. When this option
                        is specified, --screenshots is ignored (forwarded to
                        replay tool)
  --screenshots RANGES  Generate screenshots for the specified frames. Target
                        frames are specified as a comma separated list of
                        frame ranges. A frame range can be specified as a
                        single value, to specify a single frame, or as two
                        hyphenated values, to specify the first and last
                        frames to process. Frame ranges should be specified in
                        ascending order and cannot overlap. Note that frame
                        numbering is 1-based (i.e. the first frame is frame
                        1). Example: 200,301-305 will generate six screenshots
                        (forwarded to replay tool)
  --screenshot-format FORMAT
                        Image file format to use for screenshot generation.
                        Available formats are: bmp (forwarded to replay tool)
  --screenshot-dir DIR  Directory to write screenshots. Default is "/sdcard"
                        (forwarded to replay tool)
  --screenshot-prefix PREFIX
                        Prefix to apply to the screenshot file name. Default
                        is "screenshot" (forwarded to replay tool)
  --sfa, --skip-failed-allocations
                        Skip vkAllocateMemory, vkAllocateCommandBuffers, and
                        vkAllocateDescriptorSets calls that failed during
                        capture (forwarded to replay tool)
  --opcd, --omit-pipeline-cache-data
                        Omit pipeline cache data from calls to
                        vkCreatePipelineCache and skip calls to
                        vkGetPipelineCacheData (forwarded to replay tool)
  --surface-index N     Restrict rendering to the Nth surface object created.
                        Used with captures that include multiple surfaces.
                        Default is -1 (render to all surfaces; forwarded to
                        replay tool)
  --sync                Synchronize after each queue submission with
                        vkQueueWaitIdle (forwarded to replay tool)
  --remove-unsupported  Remove unsupported extensions and features from
                        instance and device creation parameters (forwarded to
                        replay tool)
  -m MODE, --memory-translation MODE
                        Enable memory translation for replay on GPUs with
                        memory types that are not compatible with the capture
                        GPU's memory types. Available modes are: none, remap,
                        realign, rebind (forwarded to replay tool)
  --onhb, --omit-null-hardware-buffers
                        Omit Vulkan API calls which would pass a NULL
                        AHardwareBuffer*.  (forwarded to replay tool)
  --use-captured-swapchain-indices
                        Use the swapchain indices stored in the capture directly on the swapchain
                        setup for replay. The default without this option is to use a Virtual Swapchain
                        of images which match the swapchain in effect at capture time and which are
                        copied to the underlying swapchain of the implementation being replayed on.

The command will force-stop an active replay process before starting the replay activity with the following:

adb shell am force-stop com.lunarg.gfxreconstruct.replay
adb shell am start -n "com.lunarg.gfxreconstruct.replay/android.app.NativeActivity" \ 
                   -a android.intent.action.MAIN \ 
                   -c android.intent.category.LAUNCHER \ 
                   --es "args" \ 
                   "<arg-list>"

If gfxrecon-replay was built with Vulkan Validation Layer support, VK_LAYER_KHRONOS_validation can be enabled and disabled in the same manner as VK_LAYER_LUNARG_gfxreconstruct

Using the Replay Command

To replay a captured trace, find the location of the trace and replay it using the built gfxrecon-replay tool:

./android/scripts/gfxrecon.py replay ${capture file path}

For example, if the capture was saved to the /sdcard/Download folder as "gfxrecon_capture_20221210T120558.gfxr", the command would look like:

./android/scripts/gfxrecon.py replay /sdcard/Download/gfxrecon_capture_20221210T120558.gfxr
Pushing a Replay File from the Host

The Android GFXReconstruct replay script allows pushing a file from the host to the Android device using the -p or --push-file option.

If a capture was pulled down onto the host, it could be pushed up to the Android device using the following:

./android/scripts/gfxrecon.py replay --push-file ${HOST_FILE} ${RESULTING_ANDROID_FILE}

For example, if the capture file was found at /home/user/test/android_capture.gfxr on the local host, and needed to be pushed up to the Android device into the /sdcard/Download folder, then the command would be:

./android/scripts/gfxrecon.py replay --push-file /home/user/test/android_capture.gfxr /sdcard/Download/android_capture.gfxr

This would result in the gfxrecon.py Python script first pushing up the file to the requested location and then starting the replay.

Touch Controls

The gfxrecon-replay tool for Android supports the following touch controls:

Key(s) Action
Tap Toggle pause/play.
Swipe left Advance to the next frame when paused.

Key Controls

The gfxrecon-replay tool for Android supports the following key controls. Key events can be simulated through adb with the adb shell input keyevent <key-code> command:

Key(s) Key code(s) Action
Space, p Space = 62, p = 44 Toggle pause/play.
D-pad right, n D-pad right = 22, n = 42 Advance to the next frame when paused.

Limitations of Replay On Android

The current version of the Android replay layer has the following limitations:

  • Creation of more than one swapchain at a time is not guaranteed to work. The replay tool has a single ANativeActivity with a single ANativeWindow. Android may enforce a limitation of one Vulkan surface per-window.
    • NOTE:The --surface-index option may improve the chances of GFXReconstruct properly capturing the necessary content from the correct surface.
  • Permission to access external storage must be granted manually.

Troubleshooting Replay of Applications

There are several steps to take to determine why a replay of a capture may not be working. Attempt each of these and re-run after the suggested modifications to see if the problem is resolved.

Removing Unsupported Extensions

When capturing an application, the GFXReconstruct layer exposes the VK_EXT_tooling_info extension. If the application or OS uses this extension, it will be stored in the capture. However, when replaying the capture, the extension may not be exposed by other Vulkan components on that system and may fail invkCreateDevice with VK_ERROR_EXTENSION_NOT_PRESENT. This is just one example of a scenario where an extension is present during the capture process, but not during the replay later on.

To remedy this problem, use the --remove-unsupported argument to the gfxrecon.py replay command.

For example (modifying the example above):

./android/scripts/gfxrecon.py replay --remove-unsupported /sdcard/Download/gfxrecon_capture_20221210T120558.gfxr

Virtual Swapchain

During replay, swapchain indices for present can be different from captured indices. Causes for this can include the swapchain image count differing between capture and replay, and vkAcquireNextImageKHR returning a different pImageIndex at replay to the one that was captured. These issues can cause unexpected rendering or even crashes.

Virtual Swapchain insulates higher layers in the Vulkan stack from these problems by creating a set of images, exactly matching the swapchain configuration at capture time, which it exposes for them to render into. Before a present, it copies the virtual image to a target swapchain image for display. Since this issue can happen in many situations, virtual swapchain is the default setup. If the user wants to bypass the feature and use the captured indices to present directly on the swapchain of the replay implementation, they should add the --use-captured-swapchain-indices option when invoking gfxrecon-replay.

Android Detailed Examples

For more information and detailed examples on using GFXReconstruct on Android can be found in the HOWTO_android.md document.