diff --git a/qrenderdoc/Code/Interface/PersistantConfig.cpp b/qrenderdoc/Code/Interface/PersistantConfig.cpp index 7dab18a0c..ede918b96 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.cpp +++ b/qrenderdoc/Code/Interface/PersistantConfig.cpp @@ -188,6 +188,9 @@ void PersistantConfig::AddAndroidHosts() SetConfigSetting(lit("MaxConnectTimeout"), QString::number(Android_MaxConnectTimeout)); + SetConfigSetting(lit("Android_AutoPushLayerToApp"), + Android_AutoPushLayerToApp ? lit("1") : lit("0")); + rdctype::str androidHosts; RENDERDOC_EnumerateAndroidDevices(&androidHosts); for(const QString &hostName : ToQStr(androidHosts).split(QLatin1Char(','), QString::SkipEmptyParts)) diff --git a/qrenderdoc/Code/Interface/PersistantConfig.h b/qrenderdoc/Code/Interface/PersistantConfig.h index b9129e85e..506bfc5d1 100644 --- a/qrenderdoc/Code/Interface/PersistantConfig.h +++ b/qrenderdoc/Code/Interface/PersistantConfig.h @@ -109,6 +109,8 @@ DECLARE_REFLECTION_STRUCT(SPIRVDisassembler); \ CONFIG_SETTING_VAL(public, int, int, Android_MaxConnectTimeout, 30) \ \ + CONFIG_SETTING_VAL(public, bool, bool, Android_AutoPushLayerToApp, false) \ + \ CONFIG_SETTING_VAL(public, bool, bool, CheckUpdate_AllowChecks, true) \ \ CONFIG_SETTING_VAL(public, bool, bool, CheckUpdate_UpdateAvailable, false) \ @@ -347,6 +349,12 @@ For more information about some of these settings that are user-facing see Defaults to ``30``. +.. data:: Android_AutoPushLayerToApp + + Whether to automatically push the RenderDoc layer to the application's lib directory when running + on a device with root access. This can enable debugging of Vulkan applications that didn't already + package the layer in the APK. + .. data:: CheckUpdate_AllowChecks ``True`` if when colouring marker regions in the :class:`EventBrowser`, the whole row should be diff --git a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp index e960b222c..164392ad3 100644 --- a/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/CaptureDialog.cpp @@ -423,8 +423,8 @@ void CaptureDialog::vulkanLayerWarn_mouseClick() void CaptureDialog::CheckAndroidSetup(QString &filename) { - ui->androidWarn->setVisible(false); ui->androidScan->setVisible(true); + ui->androidWarn->setVisible(false); LambdaThread *scan = new LambdaThread([this, filename]() { @@ -466,6 +466,7 @@ void CaptureDialog::androidWarn_mouseClick() bool missingPermissions = bool(m_AndroidFlags & AndroidFlags::MissingPermissions); bool missingLibrary = bool(m_AndroidFlags & AndroidFlags::MissingLibrary); + bool rootAccess = bool(m_AndroidFlags & AndroidFlags::RootAccess); if(missingPermissions) { @@ -481,21 +482,107 @@ void CaptureDialog::androidWarn_mouseClick() msg += tr("Missing library
" "The RenderDoc library must be present in the " - "installed application.

" - "To fix this, you should repackage the APK following guidelines on the " - "" - "RenderDoc Wiki

"); + "installed application.

"); } if(missingPermissions) { - // Don't prompt for patching if anything other than library is missing + // Don't prompt for patching if permissions are wrong - we can't fix that RDDialog::critical(this, caption, msg); + return; } - else + + // Track whether we tried to push layer directly, to influence text + bool triedPush = false; + + // Track whether to continue with push suggestion dialogue, in case user clicked Cancel + bool suggestPatch = true; + + if(rootAccess) { + // Check whether user has requested automatic pushing + bool autoPushConfig = m_Ctx.Config().Android_AutoPushLayerToApp; + + // Separately, track whether the persistent checkBox is selected + bool autoPushCheckBox = autoPushConfig; + + QMessageBox::StandardButton prompt = QMessageBox::No; + + // Only display initial prompt if user has not chosen to push automatically + if(!autoPushConfig) + { + QString rootmsg = msg; + rootmsg += + tr("Your device appears to have root access. If you are only targeting Vulkan, " + "RenderDoc can try to push the layer directly to your application.

" + "Would you like RenderDoc to push the layer?
"); + + QString checkMsg(lit("Automatically push the layer on rooted devices")); + QCheckBox *cb = new QCheckBox(checkMsg, this); + cb->setChecked(autoPushCheckBox); + prompt = RDDialog::questionChecked(this, caption, rootmsg, cb, autoPushCheckBox, + RDDialog::YesNoCancel); + } + + if(autoPushConfig || prompt == QMessageBox::Yes) + { + bool pushSucceeded = false; + triedPush = true; + + // Only update the autoPush setting if Yes was clicked + if(autoPushCheckBox != m_Ctx.Config().Android_AutoPushLayerToApp) + { + m_Ctx.Config().Android_AutoPushLayerToApp = autoPushCheckBox; + m_Ctx.Config().Save(); + } + + // Call into layer push routine, then continue + LambdaThread *push = new LambdaThread([this, exe, &pushSucceeded]() { + QByteArray hostnameBytes = m_Ctx.Replay().CurrentRemote()->Hostname.toUtf8(); + if(RENDERDOC_PushLayerToAndroidApp(hostnameBytes.data(), exe.toUtf8().data())) + { + // Sucess! + pushSucceeded = true; + + RDDialog::information( + this, tr("Push succeeded!"), + tr("The push attempt succeeded and %1 now contains the RenderDoc layer").arg(exe)); + } + }); + + push->start(); + if(push->isRunning()) + { + ShowProgressDialog(this, tr("Pushing layer to %1, please wait...").arg(exe), + [push]() { return !push->isRunning(); }); + } + push->deleteLater(); + + if(pushSucceeded) + { + // We should be good from here, no futher prompts + suggestPatch = false; + ui->androidWarn->setVisible(false); + } + } + else if(prompt == QMessageBox::Cancel) + { + // Cancel skips any other fix prompts + suggestPatch = false; + } + } + + if(suggestPatch) + { + if(triedPush) + msg.insert(0, tr("The push attempt failed, so other methods must be used to fix the missing " + "layer.

")); + msg += - tr("If you are only targeting Vulkan, RenderDoc can try to add the layer for you, " + tr("To fix this, you should repackage the APK following guidelines on the " + "" + "RenderDoc Wiki

" + "If you are only targeting Vulkan, RenderDoc can try to add the layer for you, " "which requires pulling the APK, patching it, uninstalling the original, and " "installing the modified version with a debug key. " "This works for many debuggable applications, but not all, especially those that " @@ -506,7 +593,8 @@ void CaptureDialog::androidWarn_mouseClick() " to get them.

" "Would you like RenderDoc to try patching your APK?"); - QMessageBox::StandardButton prompt = RDDialog::question(this, caption, msg); + QMessageBox::StandardButton prompt = + RDDialog::question(this, caption, msg, RDDialog::YesNoCancel); if(prompt == QMessageBox::Yes) { diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp index 817e89f70..d4caba724 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.cpp @@ -63,6 +63,7 @@ SettingsDialog::SettingsDialog(ICaptureContext &ctx, QWidget *parent) } ui->Android_AdbExecutablePath->setText(m_Ctx.Config().Android_AdbExecutablePath); ui->Android_MaxConnectTimeout->setValue(m_Ctx.Config().Android_MaxConnectTimeout); + ui->Android_AutoPushLayerToApp->setChecked(m_Ctx.Config().Android_AutoPushLayerToApp); ui->TextureViewer_ResetRange->setChecked(m_Ctx.Config().TextureViewer_ResetRange); ui->TextureViewer_PerTexSettings->setChecked(m_Ctx.Config().TextureViewer_PerTexSettings); @@ -378,3 +379,10 @@ void SettingsDialog::on_Android_AdbExecutablePath_textEdited(const QString &adb) m_Ctx.Config().Save(); } + +void SettingsDialog::on_Android_AutoPushLayerToApp_toggled(bool checked) +{ + m_Ctx.Config().Android_AutoPushLayerToApp = ui->Android_AutoPushLayerToApp->isChecked(); + + m_Ctx.Config().Save(); +} diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.h b/qrenderdoc/Windows/Dialogs/SettingsDialog.h index 327bc0ed9..d4d94fee4 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.h +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.h @@ -86,6 +86,7 @@ private slots: void on_browseAdbPath_clicked(); void on_Android_MaxConnectTimeout_valueChanged(double timeout); void on_Android_AdbExecutablePath_textEdited(const QString &path); + void on_Android_AutoPushLayerToApp_toggled(bool checked); // manual slots void formatter_valueChanged(int value); diff --git a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui index a75052968..730fbe065 100644 --- a/qrenderdoc/Windows/Dialogs/SettingsDialog.ui +++ b/qrenderdoc/Windows/Dialogs/SettingsDialog.ui @@ -874,7 +874,7 @@ If {spv_disas} is not used, the tool is expected to output the disassembly on st - + Qt::Vertical @@ -887,6 +887,25 @@ If {spv_disas} is not used, the tool is expected to output the disassembly on st + + + + Automatically push the RenderDoc layer to applications that need it when running on a device with root access. +This can enable debugging of Vulkan apps that don't already contain the layer. + + + Automatically push RenderDoc layer for Vulkan on devices with root access. + + + + + + + Automatically push the RenderDoc layer to applications that need it when running on a device with root access. +This can enable debugging of Vulkan apps that don't already contain the layer. + + + diff --git a/renderdoc/api/replay/replay_enums.h b/renderdoc/api/replay/replay_enums.h index c5c470014..ae3fc7f5c 100644 --- a/renderdoc/api/replay/replay_enums.h +++ b/renderdoc/api/replay/replay_enums.h @@ -3325,6 +3325,10 @@ DOCUMENT(R"(A set of flags giving details of the current status of Android traca The application is not debuggable. +.. data:: RootAccess + + The device being targeted has root access. + .. data:: Unfixable The current situation is not fixable automatically and requires user intervention/disambiguation. @@ -3335,7 +3339,8 @@ enum class AndroidFlags : uint32_t MissingLibrary = 0x1, MissingPermissions = 0x2, NotDebuggable = 0x4, - Unfixable = 0x8, + RootAccess = 0x8, + Unfixable = 0x10, }; BITMASK_OPERATORS(AndroidFlags); \ No newline at end of file diff --git a/renderdoc/replay/entry_points.cpp b/renderdoc/replay/entry_points.cpp index 3cbb346ee..84fe26c62 100644 --- a/renderdoc/replay/entry_points.cpp +++ b/renderdoc/replay/entry_points.cpp @@ -1298,6 +1298,12 @@ extern "C" RENDERDOC_API void RENDERDOC_CC RENDERDOC_CheckAndroidPackage(const c *flags |= AndroidFlags::MissingPermissions; } + if(CheckRootAccess(deviceID)) + { + RDCLOG("Root access detected"); + *flags |= AndroidFlags::RootAccess; + } + return; }