Skip to content

Commit

Permalink
android: Switch to GLES layer on Android Q
Browse files Browse the repository at this point in the history
  • Loading branch information
cnorthrop authored and baldurk committed Sep 10, 2019
1 parent b2ef488 commit 24fb676
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 53 deletions.
54 changes: 45 additions & 9 deletions renderdoc/android/android.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -403,6 +403,12 @@ bool RemoveRenderDocAndroidServer(const std::string &deviceID)
void ResetCaptureSettings(const std::string &deviceID) void ResetCaptureSettings(const std::string &deviceID)
{ {
Android::adbExecCommand(deviceID, "shell setprop debug.vulkan.layers :", ".", true); Android::adbExecCommand(deviceID, "shell setprop debug.vulkan.layers :", ".", true);
Android::adbExecCommand(deviceID, "shell settings delete global enable_gpu_debug_layers", ".",
true);
Android::adbExecCommand(deviceID, "shell settings delete global gpu_debug_app", ".", true);
Android::adbExecCommand(deviceID, "shell settings delete global gpu_debug_layer_app", ".", true);
Android::adbExecCommand(deviceID, "shell settings delete global gpu_debug_layers", ".", true);
Android::adbExecCommand(deviceID, "shell settings delete global gpu_debug_layers_gles", ".", true);
} }


rdcarray<rdcstr> EnumerateDevices() rdcarray<rdcstr> EnumerateDevices()
Expand Down Expand Up @@ -937,6 +943,7 @@ struct AndroidController : public IDeviceProtocolHandler
return &m_Inst; return &m_Inst;
}; };
}; };

ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w, const char *c, ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w, const char *c,
const rdcarray<EnvironmentModification> &env, const rdcarray<EnvironmentModification> &env,
const CaptureOptions &opts) const CaptureOptions &opts)
Expand Down Expand Up @@ -977,9 +984,41 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w
Android::adbExecCommand(m_deviceID, StringFormat::Fmt("forward --remove tcp:%i", jdwpPort)); Android::adbExecCommand(m_deviceID, StringFormat::Fmt("forward --remove tcp:%i", jdwpPort));
// force stop the package if it was running before // force stop the package if it was running before
Android::adbExecCommand(m_deviceID, "shell am force-stop " + packageName); Android::adbExecCommand(m_deviceID, "shell am force-stop " + packageName);
// enable the vulkan layer (will only be used by vulkan programs)
Android::adbExecCommand(m_deviceID, bool hookWithJDWP = true;
"shell setprop debug.vulkan.layers " RENDERDOC_VULKAN_LAYER_NAME);
if(Android::SupportsNativeLayers(m_deviceID))
{
RDCLOG("Using Android 10 native GPU layering");

// if we have Android 10 native layering, don't use JDWP hooking
hookWithJDWP = false;

// set up environment variables for the package, and point to ourselves for vulkan and GLES
// layers
std::string installedABI = Android::DetermineInstalledABI(m_deviceID, packageName);
std::string layerPackage = GetRenderDocPackageForABI(Android::GetABI(installedABI));
Android::adbExecCommand(m_deviceID, "shell settings put global enable_gpu_debug_layers 1");
Android::adbExecCommand(m_deviceID, "shell settings put global gpu_debug_app " + packageName);
Android::adbExecCommand(m_deviceID,
"shell settings put global gpu_debug_layer_app " + layerPackage);
Android::adbExecCommand(
m_deviceID, "shell settings put global gpu_debug_layers " RENDERDOC_VULKAN_LAYER_NAME);
Android::adbExecCommand(
m_deviceID, "shell settings put global gpu_debug_layers_gles " RENDERDOC_ANDROID_LIBRARY);
}
else
{
RDCLOG("Using pre-Android 10 Vulkan layering and JDWP injection");

// use JDWP hooking to inject our library for GLES
hookWithJDWP = true;

// enable the vulkan layer (will only be used by vulkan programs)
Android::adbExecCommand(m_deviceID,
"shell setprop debug.vulkan.layers " RENDERDOC_VULKAN_LAYER_NAME);
}

// if in VR mode, enable frame delimiter markers // if in VR mode, enable frame delimiter markers
Android::adbExecCommand(m_deviceID, "shell setprop debug.vr.profiler 1"); Android::adbExecCommand(m_deviceID, "shell setprop debug.vr.profiler 1");
// create the data directory we will use for storing, in case the application doesn't // create the data directory we will use for storing, in case the application doesn't
Expand Down Expand Up @@ -1007,8 +1046,6 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w
if(RDCLib.find("/lib/*/" RENDERDOC_ANDROID_LIBRARY) != std::string::npos) if(RDCLib.find("/lib/*/" RENDERDOC_ANDROID_LIBRARY) != std::string::npos)
RDCLib.clear(); RDCLib.clear();


bool injectLibraries = true;

if(RDCLib.empty()) if(RDCLib.empty())
{ {
RDCLOG("No library found in %s/lib/*/" RENDERDOC_ANDROID_LIBRARY RDCLOG("No library found in %s/lib/*/" RENDERDOC_ANDROID_LIBRARY
Expand All @@ -1017,15 +1054,15 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w
} }
else else
{ {
injectLibraries = false; hookWithJDWP = false;
RDCLOG("Library found, no injection required: %s", RDCLib.c_str()); RDCLOG("Library found, no injection required: %s", RDCLib.c_str());
} }


int pid = 0; int pid = 0;


RDCLOG("Launching package '%s' with activity '%s'", packageName.c_str(), activityName.c_str()); RDCLOG("Launching package '%s' with activity '%s'", packageName.c_str(), activityName.c_str());


if(injectLibraries) if(hookWithJDWP)
{ {
RDCLOG("Setting up to launch the application as a debugger to inject."); RDCLOG("Setting up to launch the application as a debugger to inject.");


Expand All @@ -1043,8 +1080,7 @@ ExecuteResult AndroidRemoteServer::ExecuteAndInject(const char *a, const char *w
} }
else else
{ {
RDCLOG( RDCLOG("Launching APK with no debugger or direct injection.");
"Not doing any injection - assuming APK is pre-loaded with RenderDoc capture library.");


// start the activity in this package with debugging enabled and force-stop after starting // start the activity in this package with debugging enabled and force-stop after starting
Android::adbExecCommand(m_deviceID, Android::adbExecCommand(m_deviceID,
Expand Down
31 changes: 0 additions & 31 deletions renderdoc/android/android_patch.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -390,37 +390,6 @@ bool CheckPatchingRequirements()
return true; return true;
} }


std::string DetermineInstalledABI(const std::string &deviceID, const std::string &packageName)
{
RDCLOG("Checking installed ABI for %s", packageName.c_str());
std::string abi;

std::string dump = adbExecCommand(deviceID, "shell pm dump " + packageName).strStdout;
if(dump.empty())
RDCERR("Unable to pm dump %s", packageName.c_str());

// Walk through the output and look for primaryCpuAbi
std::istringstream contents(dump);
std::string line;
std::string prefix("primaryCpuAbi=");
while(std::getline(contents, line))
{
line = trim(line);
if(line.compare(0, prefix.size(), prefix) == 0)
{
// Extract the abi
abi = line.substr(line.find_last_of("=") + 1);
RDCLOG("primaryCpuAbi found: %s", abi.c_str());
break;
}
}

if(abi.empty())
RDCERR("Unable to determine installed abi for: %s", packageName.c_str());

return abi;
}

bool PullAPK(const std::string &deviceID, const std::string &pkgPath, const std::string &apk) bool PullAPK(const std::string &deviceID, const std::string &pkgPath, const std::string &apk)
{ {
RDCLOG("Pulling APK to patch"); RDCLOG("Pulling APK to patch");
Expand Down
46 changes: 46 additions & 0 deletions renderdoc/android/android_utils.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@


#include "android_utils.h" #include "android_utils.h"
#include <algorithm> #include <algorithm>
#include <sstream>
#include "core/core.h" #include "core/core.h"
#include "strings/string_utils.h" #include "strings/string_utils.h"


Expand Down Expand Up @@ -183,6 +184,51 @@ bool IsSupported(std::string deviceID)
return true; return true;
} }


bool SupportsNativeLayers(const rdcstr &deviceID)
{
std::string api =
trim(Android::adbExecCommand(deviceID, "shell getprop ro.build.version.sdk").strStdout);

int apiVersion = atoi(api.c_str());

// SDK 29 == Android 10.0, the first version that included layering
if(apiVersion >= 29)
return true;

return false;
}

std::string DetermineInstalledABI(const std::string &deviceID, const std::string &packageName)
{
RDCLOG("Checking installed ABI for %s", packageName.c_str());
std::string abi;

std::string dump = adbExecCommand(deviceID, "shell pm dump " + packageName).strStdout;
if(dump.empty())
RDCERR("Unable to pm dump %s", packageName.c_str());

// Walk through the output and look for primaryCpuAbi
std::istringstream contents(dump);
std::string line;
std::string prefix("primaryCpuAbi=");
while(std::getline(contents, line))
{
line = trim(line);
if(line.compare(0, prefix.size(), prefix) == 0)
{
// Extract the abi
abi = line.substr(line.find_last_of("=") + 1);
RDCLOG("primaryCpuAbi found: %s", abi.c_str());
break;
}
}

if(abi.empty())
RDCERR("Unable to determine installed abi for: %s", packageName.c_str());

return abi;
}

rdcstr GetFriendlyName(const rdcstr &deviceID) rdcstr GetFriendlyName(const rdcstr &deviceID)
{ {
// run adb root now, so we hit any disconnection that we're going to before trying to connect. // run adb root now, so we hit any disconnection that we're going to before trying to connect.
Expand Down
2 changes: 2 additions & 0 deletions renderdoc/android/android_utils.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ bool toolExists(const std::string &path);
std::string GetFirstMatchingLine(const std::string &haystack, const std::string &needle); std::string GetFirstMatchingLine(const std::string &haystack, const std::string &needle);


bool IsSupported(std::string deviceID); bool IsSupported(std::string deviceID);
bool SupportsNativeLayers(const rdcstr &deviceID);
std::string DetermineInstalledABI(const std::string &deviceID, const std::string &packageName);
rdcstr GetFriendlyName(const rdcstr &deviceID); rdcstr GetFriendlyName(const rdcstr &deviceID);


// supported ABIs // supported ABIs
Expand Down
88 changes: 83 additions & 5 deletions renderdoc/driver/gl/egl_hooks.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -901,27 +901,59 @@ static void EGLHooked(void *handle)
} }


#if ENABLED(RDOC_WIN32) #if ENABLED(RDOC_WIN32)

bool ShouldHookEGL() bool ShouldHookEGL()
{ {
const char *toggle = Process::GetEnvVariable("RENDERDOC_HOOK_EGL"); const char *toggle = Process::GetEnvVariable("RENDERDOC_HOOK_EGL");


// if the var is set to 0, then don't hook EGL // if the var is set to 0, then don't hook EGL
if(toggle && toggle[0] == '0') if(toggle && toggle[0] == '0')
{
RDCLOG(
"EGL hooks disabled by RENDERDOC_HOOK_EGL environment variable - "
"if GLES emulator is in use, underlying API will be captured");
return false; return false;
}


return true; return true;
} }

#elif ENABLED(RDOC_ANDROID)

bool ShouldHookEGL()
{
void *egl_handle = dlopen("libEGL.so", RTLD_LAZY);
PFN_eglQueryString query_string = (PFN_eglQueryString)dlsym(egl_handle, "eglQueryString");
if(!query_string)
{
RDCERR("Unable to find eglQueryString entry point, enabling EGL hooking");
return true;
}

const char *eglExts = query_string(EGL_NO_DISPLAY, EGL_EXTENSIONS);

if(eglExts && strstr(eglExts, "EGL_ANDROID_GLES_layers"))
{
RDCLOG("EGL_ANDROID_GLES_layers detected, disabling EGL hooks - GLES layering in effect");
return false;
}

return true;
}

#else

bool ShouldHookEGL()
{
return true;
}

#endif #endif


void EGLHook::RegisterHooks() void EGLHook::RegisterHooks()
{ {
#if ENABLED(RDOC_WIN32)
if(!ShouldHookEGL()) if(!ShouldHookEGL())
{
RDCLOG("EGL hooks disabled - if GLES emulator is in use, underlying API will be captured");
return; return;
}
#endif


RDCLOG("Registering EGL hooks"); RDCLOG("Registering EGL hooks");


Expand Down Expand Up @@ -960,3 +992,49 @@ void EGLHook::RegisterHooks()
EGL_HOOKED_SYMBOLS(EGL_REGISTER) EGL_HOOKED_SYMBOLS(EGL_REGISTER)
#undef EGL_REGISTER #undef EGL_REGISTER
} }

// Android GLES layering support
#if ENABLED(RDOC_ANDROID)

typedef __eglMustCastToProperFunctionPointerType(EGLAPIENTRY *PFNEGLGETNEXTLAYERPROCADDRESSPROC)(
void *, const char *funcName);

HOOK_EXPORT void AndroidGLESLayer_Initialize(void *layer_id,
PFNEGLGETNEXTLAYERPROCADDRESSPROC next_gpa)
{
RDCLOG("Initialising Android GLES layer with ID %p", layer_id);

// as a hook callback this is only called while capturing
RDCASSERT(!RenderDoc::Inst().IsReplayApp());

// populate EGL dispatch table with the next layer's function pointers. Fetch all 'hooked' and
// non-hooked functions
#define EGL_FETCH(func, isext) \
EGL.func = (CONCAT(PFN_egl, func))next_gpa(layer_id, "egl" STRINGIZE(func)); \
if(!EGL.func) \
RDCWARN("Couldn't fetch function pointer for egl" STRINGIZE(func));
EGL_HOOKED_SYMBOLS(EGL_FETCH)
EGL_NONHOOKED_SYMBOLS(EGL_FETCH)
#undef EGL_FETCH

// populate GL dispatch table with the next layer's function pointers
GL.PopulateWithCallback(
[layer_id, next_gpa](const char *f) { return (void *)next_gpa(layer_id, f); });
}

HOOK_EXPORT void *AndroidGLESLayer_GetProcAddress(const char *funcName,
__eglMustCastToProperFunctionPointerType next)
{
// return our egl hooks
#define GPA_FUNCTION(name, isext) \
if(!strcmp(funcName, "egl" STRINGIZE(name))) \
return (void *)&CONCAT(egl, CONCAT(name, _renderdoc_hooked));
EGL_HOOKED_SYMBOLS(GPA_FUNCTION)
#undef GPA_FUNCTION

// otherwise, consult our database of hooks
// Android GLES layer spec expects us to return next unmodified for functions we don't support
return HookedGetProcAddress(funcName, (void *)next);
}

#endif // ENABLED(RDOC_ANDROID)
26 changes: 26 additions & 0 deletions renderdoc/driver/gl/egl_layer_android.cpp
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,26 @@
/******************************************************************************
* The MIT License (MIT)
*
* Copyright (c) 2019 Baldur Karlsson
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
******************************************************************************/

#include "driver/gl/egl_dispatch_table.h"
#include "driver/gl/gl_driver.h"
13 changes: 5 additions & 8 deletions renderdoc/driver/gl/gl_common.h
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -49,20 +49,17 @@ DECLARE_REFLECTION_ENUM(RDCGLenum);
#define RENDERDOC_SUPPORT_GL #define RENDERDOC_SUPPORT_GL
#define RENDERDOC_SUPPORT_GLES #define RENDERDOC_SUPPORT_GLES


// checks a runtime opt-out option to disallow hooking EGL on windows. This means that the
// underlying GL or D3D calls will be captured instead.
bool ShouldHookEGL();

#else #else


// other cases are defined by build-time configuration // other cases are defined by build-time configuration


// for consistency, we always enable this on other platforms - if GLES support is disabled at
// compile time then it does nothing.
#define RENDERDOC_HOOK_EGL OPTION_ON

#endif #endif


// checks a runtime opt-out option to disallow hooking EGL. On Windows with no EGL support then the
// EGL library is an emulator, so this means we'll capture the underlying calls it makes to the
// native API. On Android this is useful if we detect that GLES layering is supported.
bool ShouldHookEGL();

#define GL_APICALL #define GL_APICALL


// official headers // official headers
Expand Down
9 changes: 9 additions & 0 deletions renderdoc/driver/gl/gl_hooks.cpp
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -243,6 +243,15 @@ static void GLHooked(void *handle)


void GLHook::RegisterHooks() void GLHook::RegisterHooks()
{ {
#if ENABLED(RDOC_ANDROID)
// on android if EGL hooking is disabled we're using GLES layering, don't register any GL hooks
if(!ShouldHookEGL())
{
RDCLOG("Not registering OpenGL hooks for Android");
return;
}
#endif

RDCLOG("Registering OpenGL hooks"); RDCLOG("Registering OpenGL hooks");


// pick the 'primary' library we consider GL functions to come from. This is mostly important on // pick the 'primary' library we consider GL functions to come from. This is mostly important on
Expand Down
Loading

0 comments on commit 24fb676

Please sign in to comment.