Copyright © 2023 LunarG, Inc.
- Purpose
- Building Vulkan Samples with the Capture Layer
- Capturing Vulkan Samples
- Capturing Released Application on Rooted System
The purpose of this doc is to show several examples in using the GFXReconstruct tools. These examples are intended to help work through issues you may discover while attempting to perform the same process on your own application.
NOTE: Many of these examples are given based on using a Linux system as the build and/or host, but should be fairly easy to replicate behavior on a Windows system with very little changes.
Let's look at adding the GFXReconstruct layer as a requirement of the Khronos Vulkan Samples project. These steps assume that you have all the dependencies necessary to build that project, so refer to that repo to ensure those are met before continuing.
If you haven't already, pull down the GFXReconstruct source from GitHub so that it also updates any necessary submodules as part of the clone operation:
git clone --recurse-submodules https://github.com/LunarG/gfxreconstruct.git
The full location of the gfxreconstruct
folder generated from this step
is to be used in all cases below for {gfxreconstruct_root}
.
git clone --recurse-submodules https://github.com/KhronosGroup/Vulkan-Samples.git
cd Vulkan-Samples
./bldsys/scripts/generate_android_gradle.sh
(./build/android_gradle/settings.gradle) and add the following lines to the end
include(':app',':VkLayer_gfxreconstruct')
project(':VkLayer_gfxreconstruct').projectDir = file('{gfxreconstruct_root}/android/layer')
NOTE Replacing {gfxreconstruct_root} with the location of the source you cloned from the GFXReconstruct repo in step 1.
Open ./build/android_gradle/app/build.gradle and add the following line at the
top of the dependencies
block at the end of the file:
implementation project(':VkLayer_gfxreconstruct')
It should look something like this when you are done:
dependencies {
implementation project(':VkLayer_gfxreconstruct')
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
}
From the top-level samples folder you now need to enter the following sub- folder to build
cd build/android_gradle
Some common targets are assembleDebug
and assembleRelease
.
We'll build for debug in this scenario:
gradle assembleDebug
This example follows using the Khronos Vulkan Samples built above and assumes that you built the debug version. The benefit of building the application in this way is that it will automatically incorporate the GFXReconstruct capture layer into the .APK file for the built application.
adb logcat -s gfxrecon,vulkan
Install the debug version of the samples we just built with the
-g
option to request storage access:
adb install -g ./build/android_gradle/app/build/outputs/apk/debug/vulkan_samples-debug.apk
This should guarantee that the application (and any associated layers) is allowed to write out anything generated.
Enable the GFXReconstruct layer just for the application using the global settings Android provides:
adb shell settings put global enable_gpu_debug_layers 1
adb shell settings put global gpu_debug_app com.khronos.vulkan_samples
adb shell settings put global gpu_debug_layers "VK_LAYER_LUNARG_gfxreconstruct"
Now, set the command to capture frames 500-700 so we can see what's happening
and write the file to the /sdcard/Download
folder:
adb shell "setprop debug.gfxrecon.capture_file '/sdcard/Download/samples_capture.gfxr'"
adb shell "setprop debug.gfxrecon.capture_frames '500-700'"
More capture options can be found in the USAGE_android.md under the Capture Options section.
Keep the logcat output in a separate window/terminal because you can see useful information there.
To run the application, I looked at the application list on the Android device, then I clicked it to launch. Android may ask you to "Allow access to manage all files". When it asks you that, enable the option and click the back arrow to get to the application.
Once launched, you will see something similar to the following in the logcat
window/terminal:
D vulkan : added global layer 'VK_LAYER_LUNARG_gfxreconstruct' from library '/data/app/~~3mMLAga5cfTsFt19iAcISQ==/com.khronos.vulkan_samples-cwdD6xdzmqmyo6QTZDsjng==/base.apk!/lib/arm64-v8a/libVkLayer_gfxreconstruct.so'
This message states that it found the GFXReconstruct capture layer in the base.apk file which we built earlier, meaning there's no need to add an additional external layer to any folders.
Now, click on one of the samples. In this scenario, let's click on the "Dynamic uniform buffers" test.
Logcat should show:
gfxrecon: Finished recording graphics API capture
Exit the application.
Running certain Vulkan samples tests results in corruption and require additional changes to capture properly. For example, if you attempted to capture the "Instancing" sample, it will render without the asteroids. This is because the GFXReconstruct layer needs some additional settings to properly align and track the memory.
First, we will enable page guard on the memory:
adb shell "debug.gfxrecon.page_guard_persistent_memory true"
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.
Next, we will force page guard buffers are aligned to pages:
adb shell "debug.gfxrecon.page_guard_align_buffer_sizes true"
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.
After making these changes, re-open the application and re-attempt recapture.
I noticed in my capture adb logcat
output that the file was recorded to
/sdcard/Download
.
NOTE: It actually lists the full file in the message like the following:
gfxrecon: Recording graphics API capture to /sdcard/Download/gfxrecon_capture_frames_500_through_700_20221211T130328.gfxr
But, let's say you didn't know, so double check that folder:
adb shell ls /sdcard/Download
The output might look like this:
gfxrecon_capture_frames_500_through_700_20221211T130328.gfxr
There's our file right there.
We want to make sure that the layer is not present for any further work we are going to do. In this case, we're going to check our replay, so disable any active debug layers so they don't interfere with the tool.
adb shell settings put global enable_gpu_debug_layers 0
adb shell settings put global gpu_debug_layers ""
Now, we need to install the Replay application that we built as part of the
GFXReconstruct source.
From the top of the source pulled down from the repo by using the
gfxrecon.py
script:
./android/scripts/gfxrecon.py install-apk android/tools/replay/build/outputs/apk/debug/replay-debug.apk
Try running the replay using the gfxrecon.py
script:
./android/scripts/gfxrecon.py replay /sdcard/Download/gfxrecon_capture_frames_500_through_700_20221211T130328.gfxr
Voila! It worked, the replay executes properly.
If you need to share your replay file, make sure to pull it off of the device for sharing:
adb pull /sdcard/Download/gfxrecon_capture_frames_500_through_700_20221211T130328.gfxr
In this example, we'll capture a set of frames from the GPUScore benchmark. This set of instructions assume that you have already have properly rooted your device, reinstalled the proper Android image, and installed the target application (in this case the benchmark).
adb shell
su
We'll refer to this as the admin ADB shell from here on out.
In another terminal/command window, run logcat
to see application messages:
adb logcat -s vulkan
Then run the application and watch for message with the full name.
In my case, I see that the full application name is
com.basemark.gpuscore.sacredpath
.
Alternatively, you can brows the list of installed applications:
adb shell cmd package list packages -3
In the admin adb shell, run the following:
find /data/app -name *${Full Application Name}*
Where ${Full Application Name}
is the name we discovered in step 2 above.
On my system, running find /data/app -name *com.basemark.gpuscore.sacredpath*
returned:
/data/app/~~CklMw7KUY3NvpJa1VxdzCg==/com.basemark.gpuscore.sacredpath-y4DeeR7lnUlvifTr1xSRWA==
In another terminal/command window, change directory to the location of your GFXReconstruct source. After building, perform the following command:
adb push \
./android/layer/build/intermediates/cmake/debug/obj/arm64-v8a/libVkLayer_gfxreconstruct.so \
/storage/emulated/0/Download
Now, copy it into the application folder discovered in step 3 above using the admin ADB shell:
mv /storage/emulated/0/Download/libVkLayer_gfxreconstruct.so \
${Application Data Path}/lib/arm64/libVkLayer_gfxreconstruct.so
Again, my full command was:
mv /storage/emulated/0/Download/libVkLayer_gfxreconstruct.so \
/data/app/~~CklMw7KUY3NvpJa1VxdzCg==/com.basemark.gpuscore.sacredpath-y4DeeR7lnUlvifTr1xSRWA==/lib/arm64/libVkLayer_gfxreconstruct.so
Finally, make the capture layer executable:
chmod 777 /data/app/~~CklMw7KUY3NvpJa1VxdzCg==/com.basemark.gpuscore.sacredpath-y4DeeR7lnUlvifTr1xSRWA==/lib/arm64/libVkLayer_gfxreconstruct.so
I had to enable the layer system wide in this configuration and I also had to disable the Vulkan usage for the UI or it would interfere with my capture:
adb shell "setprop debug.vulkan.layers 'VK_LAYER_LUNARG_gfxreconstruct'"
adb shell "setprop debug.hwui.renderer 'skiagl'"
Now, set the command to capture frames 100-200 so we can see what's happening:
adb shell "setprop debug.gfxrecon.capture_frames '100-200'"
And set the output filename to the proper location:
adb shell "setprop debug.gfxrecon.capture_file /storage/emulated/0/Download/sacredpath_capture.gfxr"
We will enable page guard on the memory:
adb shell "setprop debug.gfxrecon.page_guard_persistent_memory true"
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.
Next, we will force page guard buffers are aligned to pages:
adb shell "setprop debug.gfxrecon.page_guard_align_buffer_sizes true"
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.
More capture options can be found in the USAGE_android.md under the Capture Options section.
adb shell pm grant com.basemark.gpuscore.sacredpath android.permission.WRITE_EXTERNAL_STORAGE
In my case, I opened up the menu, found the GPUScore application and chose the official "Sacred Path" benchmark.
Watching the log output in the above adb logcat
terminal, I waited until
the benchmark completed and then exited the application.
I made sure that I saw the following to make sure the recording started:
I gfxrecon: Recording graphics API capture to /storage/emulated/0/Download/sacredpath_capture_frames_100_through_200_20221215T174939.gfxr
Check that the above file exists:
adb shell ls /storage/emulated/0/Download/sacredpath_capture_frames*.gfxr
Disable the capture layer globally and also restore the Vulkan usage of the HWUI:
adb shell "setprop debug.vulkan.layers ''"
adb shell "setprop debug.hwui.renderer 'skiavk'"
Now, we need to install the Replay application that we built as part of the
GFXReconstruct source.
Install the replay APK from the root of the built source tree by using the
gfxrecon.py
script:
./android/scripts/gfxrecon.py install-apk android/tools/replay/build/outputs/apk/debug/replay-debug.apk
Try running the replay using the gfxrecon.py
script:
./android/scripts/gfxrecon.py replay /storage/emulated/0/Download/sacredpath_capture_frames_100_through_200_20221215T174939.gfxr