Add a new screen for managing installed Adreno drivers

This commit is contained in:
Henrik Rydgård 2024-01-16 13:14:57 +01:00
parent 72c4d346d0
commit 9253bf9cb5
24 changed files with 408 additions and 159 deletions

View File

@ -1455,6 +1455,8 @@ list(APPEND NativeAppSource
UI/GameScreen.cpp
UI/GameSettingsScreen.h
UI/GameSettingsScreen.cpp
UI/DriverManagerScreen.h
UI/DriverManagerScreen.cpp
UI/GPUDriverTestScreen.h
UI/GPUDriverTestScreen.cpp
UI/TiltAnalogSettingsScreen.h

View File

@ -67,7 +67,7 @@ const JsonNode *JsonGet::get(const char *child_name, JsonTag type) const {
return nullptr;
}
const char *JsonGet::getStringOrDie(const char *child_name) const {
const char *JsonGet::getStringOrNull(const char *child_name) const {
const JsonNode *val = get(child_name, JSON_STRING);
if (val)
return val->value.toString();
@ -75,7 +75,16 @@ const char *JsonGet::getStringOrDie(const char *child_name) const {
return nullptr;
}
const char *JsonGet::getString(const char *child_name, const char *default_value) const {
bool JsonGet::getString(const char *child_name, std::string *output) const {
const JsonNode *val = get(child_name, JSON_STRING);
if (!val) {
return false;
}
*output = val->value.toString();
return true;
}
const char *JsonGet::getStringOr(const char *child_name, const char *default_value) const {
const JsonNode *val = get(child_name, JSON_STRING);
if (!val)
return default_value;

View File

@ -21,8 +21,9 @@ struct JsonGet {
const JsonGet getDict(const char *child_name) const {
return JsonGet(get(child_name, JSON_OBJECT)->value);
}
const char *getStringOrDie(const char *child_name) const;
const char *getString(const char *child_name, const char *default_value) const;
const char *getStringOrNull(const char *child_name) const;
const char *getStringOr(const char *child_name, const char *default_value) const;
bool getString(const char *child_name, std::string *output) const;
bool getStringVector(std::vector<std::string> *vec) const;
double getFloat(const char *child_name) const;
double getFloat(const char *child_name, double default_value) const;
@ -46,7 +47,7 @@ struct JsonGet {
class JsonReader {
public:
JsonReader(const std::string &filename);
// Makes a copy, after this returns you can free the input buffer.
// Makes a copy, after this returns you can free the input buffer. Zero termination is not necessary.
JsonReader(const char *data, size_t size) {
buffer_ = (char *)malloc(size + 1);
if (buffer_) {

View File

@ -62,6 +62,7 @@
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#include <unistd.h>
#endif
#if defined(__DragonFly__) || defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__NetBSD__)
@ -990,6 +991,19 @@ bool OpenFileInEditor(const Path &fileName) {
return true;
}
const Path GetCurDirectory() {
#ifdef _WIN32
wchar_t buffer[4096];
size_t len = GetCurrentDirectory(sizeof(buffer) / sizeof(wchar_t), buffer);
std::string curDir = ConvertWStringToUTF8(buffer);
return Path(curDir);
#else
char temp[4096]{};
getcwd(temp, 4096);
return Path(temp);
#endif
}
const Path &GetExeDirectory() {
static Path ExePath;

View File

@ -123,6 +123,8 @@ bool OpenFileInEditor(const Path &fileName);
// TODO: Belongs in System or something.
const Path &GetExeDirectory();
const Path GetCurDirectory();
// simple wrapper for cstdlib file functions to
// hopefully will make error checking easier
// and make forgetting an fclose() harder

View File

@ -133,7 +133,7 @@ VkResult VulkanContext::CreateInstance(const CreateInfo &info) {
#endif
#endif
if ((flags_ & VULKAN_FLAG_VALIDATE) && g_Config.customDriver.empty()) {
if ((flags_ & VULKAN_FLAG_VALIDATE) && g_Config.sCustomDriver.empty()) {
if (IsInstanceExtensionAvailable(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) {
// Enable the validation layers
for (size_t i = 0; i < ARRAY_SIZE(validationLayers); i++) {

View File

@ -300,40 +300,42 @@ static VulkanLibraryHandle VulkanLoadLibrary(std::string *errorString) {
void *lib = nullptr;
#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
if (!g_Config.customDriver.empty() && g_Config.customDriver != "Default") {
const Path driverPath = g_Config.internalDataDirectory / "drivers" / g_Config.customDriver;
if (!g_Config.sCustomDriver.empty() && g_Config.sCustomDriver != "Default") {
const Path driverPath = g_Config.internalDataDirectory / "drivers" / g_Config.sCustomDriver;
json::JsonReader meta = json::JsonReader((driverPath / "meta.json").c_str());
if (meta.ok()) {
std::string driverLibName = meta.root().get("libraryName")->value.toString();
json::JsonReader meta = json::JsonReader((driverPath / "meta.json").c_str());
if (meta.ok()) {
std::string driverLibName = meta.root().get("libraryName")->value.toString();
Path tempDir = g_Config.internalDataDirectory / "temp";
Path fileRedirectDir = g_Config.internalDataDirectory / "vk_file_redirect";
Path tempDir = g_Config.internalDataDirectory / "temp";
Path fileRedirectDir = g_Config.internalDataDirectory / "vk_file_redirect";
File::CreateDir(tempDir);
File::CreateDir(fileRedirectDir);
File::CreateDir(tempDir);
File::CreateDir(fileRedirectDir);
lib = adrenotools_open_libvulkan(
RTLD_NOW | RTLD_LOCAL, ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_CUSTOM,
(std::string(tempDir.c_str()) + "/").c_str(),g_nativeLibDir.c_str(),
(std::string(driverPath.c_str()) + "/").c_str(),driverLibName.c_str(),
(std::string(fileRedirectDir.c_str()) + "/").c_str(),nullptr);
if (!lib) {
ERROR_LOG(G3D, "Failed to load custom driver");
}
}
}
lib = adrenotools_open_libvulkan(
RTLD_NOW | RTLD_LOCAL, ADRENOTOOLS_DRIVER_FILE_REDIRECT | ADRENOTOOLS_DRIVER_CUSTOM,
(std::string(tempDir.c_str()) + "/").c_str(), g_nativeLibDir.c_str(),
(std::string(driverPath.c_str()) + "/").c_str(), driverLibName.c_str(),
(std::string(fileRedirectDir.c_str()) + "/").c_str(), nullptr);
if (!lib) {
ERROR_LOG(G3D, "Failed to load custom driver with AdrenoTools ('%s')", g_Config.sCustomDriver.c_str());
} else {
INFO_LOG(G3D, "Vulkan library loaded with AdrenoTools ('%s')", g_Config.sCustomDriver.c_str());
}
}
}
#endif
if (!lib) {
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
if (lib) {
INFO_LOG(G3D, "Vulkan library loaded with AdrenoTools ('%s')", so_names[i]);
break;
}
}
}
if (!lib) {
for (int i = 0; i < ARRAY_SIZE(so_names); i++) {
lib = dlopen(so_names[i], RTLD_NOW | RTLD_LOCAL);
if (lib) {
INFO_LOG(G3D, "Vulkan library loaded ('%s')", so_names[i]);
break;
}
}
}
return lib;
#endif
}

View File

@ -599,7 +599,7 @@ ItemHeader::ItemHeader(const std::string &text, LayoutParams *layoutParams)
}
void ItemHeader::Draw(UIContext &dc) {
dc.SetFontStyle(dc.theme->uiFontSmall);
dc.SetFontStyle(large_ ? dc.theme->uiFont : dc.theme->uiFontSmall);
dc.DrawText(text_.c_str(), bounds_.x + 4, bounds_.centerY(), dc.theme->headerStyle.fgColor, ALIGN_LEFT | ALIGN_VCENTER);
dc.Draw()->DrawImageCenterTexel(dc.theme->whiteImage, bounds_.x, bounds_.y2()-2, bounds_.x2(), bounds_.y2(), dc.theme->headerStyle.fgColor);
}

View File

@ -848,9 +848,10 @@ public:
void Draw(UIContext &dc) override;
std::string DescribeText() const override;
void GetContentDimensionsBySpec(const UIContext &dc, MeasureSpec horiz, MeasureSpec vert, float &w, float &h) const override;
void SetLarge(bool large) { large_ = large; }
private:
std::string text_;
bool large_ = false;
};
class PopupHeader : public Item {

View File

@ -592,7 +592,7 @@ static const ConfigSetting graphicsSettings[] = {
ConfigSetting("iShowStatusFlags", &g_Config.iShowStatusFlags, 0, CfgFlag::PER_GAME),
ConfigSetting("GraphicsBackend", &g_Config.iGPUBackend, &DefaultGPUBackend, &GPUBackendTranslator::To, &GPUBackendTranslator::From, CfgFlag::DEFAULT | CfgFlag::REPORT),
#if PPSSPP_PLATFORM(ANDROID) && PPSSPP_ARCH(ARM64)
ConfigSetting("CustomDriver", &g_Config.customDriver, "", CfgFlag::DEFAULT),
ConfigSetting("CustomDriver", &g_Config.sCustomDriver, "", CfgFlag::DEFAULT),
#endif
ConfigSetting("FailedGraphicsBackends", &g_Config.sFailedGPUBackends, "", CfgFlag::DEFAULT),
ConfigSetting("DisabledGraphicsBackends", &g_Config.sDisabledGPUBackends, "", CfgFlag::DEFAULT),
@ -1399,6 +1399,11 @@ void Config::PostLoadCleanup(bool gameSpecific) {
if (iTexScalingLevel <= 0) {
iTexScalingLevel = 1;
}
// Remove a legacy value.
if (g_Config.sCustomDriver == "Default") {
g_Config.sCustomDriver = "";
}
}
void Config::PreSaveCleanup(bool gameSpecific) {
@ -1448,7 +1453,8 @@ void Config::DownloadCompletedCallback(http::Request &download) {
return;
}
std::string version = root.getString("version", "");
std::string version;
root.getString("version", &version);
const char *gitVer = PPSSPP_GIT_VERSION;
Version installed(gitVer);

View File

@ -149,7 +149,7 @@ public:
// GFX
int iGPUBackend;
std::string customDriver;
std::string sCustomDriver;
std::string sFailedGPUBackends;
std::string sDisabledGPUBackends;
// We have separate device parameters for each backend so it doesn't get erased if you switch backends.

View File

@ -165,7 +165,7 @@ void HandleDebuggerRequest(const http::ServerRequest &request) {
}
const JsonGet root = reader.root();
const char *event = root ? root.getString("event", nullptr) : nullptr;
const char *event = root ? root.getStringOr("event", nullptr) : nullptr;
if (!event) {
ws->Send(DebuggerErrorEvent("Bad message: no event property", LogLevel::LERROR, root));
return;

View File

@ -219,7 +219,7 @@ static DebuggerRegType ValidateRegName(DebuggerRequest &req, const std::string &
}
static DebuggerRegType ValidateCatReg(DebuggerRequest &req, int *cat, int *reg) {
const char *name = req.data.getString("name", nullptr);
const char *name = req.data.getStringOr("name", nullptr);
if (name)
return ValidateRegName(req, name, cat, reg);

268
UI/DriverManagerScreen.cpp Normal file
View File

@ -0,0 +1,268 @@
#include "Common/File/VFS/ZipFileReader.h"
#include "Common/Data/Format/JSONReader.h"
#include "Common/System/OSD.h"
#include "Common/Log.h"
#include "Common/StringUtils.h"
#include "Core/Config.h"
#include "Core/System.h"
#include "UI/View.h"
#include "UI/DriverManagerScreen.h"
#include "UI/GameSettingsScreen.h" // for triggerrestart
#include "UI/OnScreenDisplay.h"
static Path GetDriverPath() {
if (g_Config.internalDataDirectory.empty()) {
Path curDir = File::GetCurDirectory();
// This is the case when testing on PC
return GetSysDirectory(DIRECTORY_PSP) / "drivers";
} else {
// On Android, this is set to something usable.
return g_Config.internalDataDirectory / "drivers";
}
}
// Example meta.json:
// {
// "schemaVersion": 1,
// "name" : "Turnip driver revision 14",
// "description" : "Compiled from Mesa source.",
// "author" : "KIMCHI",
// "packageVersion" : "1",
// "vendor" : "Mesa",
// "driverVersion" : "Vulkan 1.3.274",
// "minApi" : 27,
// "libraryName" : "vulkan.ad07XX.so"
// }
struct DriverMeta {
int minApi;
std::string name;
std::string description;
std::string vendor;
std::string driverVersion;
bool Read(std::string_view str, std::string *errorStr) {
// Validate the json file. TODO: Be a bit more detailed.
json::JsonReader meta = json::JsonReader((const char *)str.data(), str.size());
if (!meta.ok()) {
*errorStr = "meta.json not valid json";
return false;
}
if (!meta.root().getString("name", &name) || name.empty()) {
*errorStr = "missing driver name in json";
return false;
}
meta.root().getString("description", &description);
meta.root().getString("vendor", &vendor);
meta.root().getString("driverVersion", &driverVersion);
minApi = meta.root().getInt("minApi");
return true;
}
};
// Compound view, creating a FileChooserChoice inside.
class DriverChoice : public UI::LinearLayout {
public:
DriverChoice(const std::string &driverName, bool current, UI::LayoutParams *layoutParams = nullptr);
UI::Event OnUse;
UI::Event OnDelete;
std::string name_;
};
static constexpr UI::Size ITEM_HEIGHT = 64.f;
DriverChoice::DriverChoice(const std::string &driverName, bool current, UI::LayoutParams *layoutParams) : UI::LinearLayout(UI::ORIENT_VERTICAL, layoutParams), name_(driverName) {
using namespace UI;
SetSpacing(2.0f);
if (!layoutParams) {
layoutParams_->width = FILL_PARENT;
layoutParams_->height = 220;
}
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
// Read the meta data
DriverMeta meta{};
bool isDefault = driverName.empty();
if (isDefault) {
meta.description = "The default installed driver on your system";
}
Path metaPath = GetDriverPath() / driverName / "meta.json";
std::string metaJson;
if (File::ReadFileToString(true, metaPath, metaJson)) {
std::string errorStr;
meta.Read(metaJson, &errorStr);
}
Add(new Spacer(12.0));
#if PPSSPP_PLATFORM(ANDROID)
bool usable = isDefault || meta.minApi <= System_GetPropertyInt(SYSPROP_SYSTEMVERSION);
#else
// For testing only
bool usable = isDefault || true;
#endif
Add(new ItemHeader(driverName.empty() ? gr->T("Default driver") : driverName))->SetLarge(true);
if (current) {
Add(new NoticeView(NoticeLevel::SUCCESS, gr->T("Current GPU driver"), ""));
}
auto horizBar = Add(new UI::LinearLayout(UI::ORIENT_HORIZONTAL));
std::string desc = meta.description;
if (!desc.empty()) desc += "\n";
if (!isDefault)
desc += meta.vendor + ": " + meta.driverVersion;
horizBar->Add(new TextView(desc));
if (!current && !isDefault) {
horizBar->Add(new Choice(ImageID("I_TRASHCAN"), new LinearLayoutParams(ITEM_HEIGHT, ITEM_HEIGHT)))->OnClick.Add([=](UI::EventParams &) {
UI::EventParams e{};
e.s = name_;
OnDelete.Trigger(e);
return UI::EVENT_DONE;
});
}
if (usable) {
if (!current) {
Add(new Choice(gr->T("Use this driver")))->OnClick.Add([=](UI::EventParams &) {
UI::EventParams e{};
e.s = name_;
OnUse.Trigger(e);
return UI::EVENT_DONE;
});
}
} else {
Add(new NoticeView(NoticeLevel::WARN, ApplySafeSubstitutions(gr->T("Driver requires Android API version %1, current is %2"), meta.minApi, System_GetPropertyInt(SYSPROP_SYSTEMVERSION)),""));
}
}
DriverManagerScreen::DriverManagerScreen(const Path & gamePath) : TabbedUIDialogScreenWithGameBackground(gamePath) {}
void DriverManagerScreen::CreateTabs() {
using namespace UI;
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
LinearLayout *drivers = AddTab("DriverManagerDrivers", gr->T("Drivers"));
CreateDriverTab(drivers);
}
void DriverManagerScreen::CreateDriverTab(UI::ViewGroup *drivers) {
using namespace UI;
auto di = GetI18NCategory(I18NCat::DIALOG);
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
drivers->Add(new ItemHeader(gr->T("AdrenoTools driver manager")));
auto customDriverInstallChoice = drivers->Add(new Choice(gr->T("Install custom driver...")));
drivers->Add(new Choice(di->T("More information...")))->OnClick.Add([=](UI::EventParams &e) {
System_LaunchUrl(LaunchUrlType::BROWSER_URL, "https://www.ppsspp.org/docs/reference/custom-drivers/");
return UI::EVENT_DONE;
});
customDriverInstallChoice->OnClick.Handle(this, &DriverManagerScreen::OnCustomDriverInstall);
drivers->Add(new ItemHeader(gr->T("Drivers")));
bool isDefault = g_Config.sCustomDriver.empty();
drivers->Add(new DriverChoice("", isDefault))->OnUse.Handle(this, &DriverManagerScreen::OnCustomDriverChange);
const Path driverPath = GetDriverPath();
std::vector<File::FileInfo> listing;
if (File::GetFilesInDir(driverPath, &listing)) {
for (auto driver : listing) {
auto choice = drivers->Add(new DriverChoice(driver.name, g_Config.sCustomDriver == driver.name));
choice->OnUse.Handle(this, &DriverManagerScreen::OnCustomDriverChange);
choice->OnDelete.Handle(this, &DriverManagerScreen::OnCustomDriverUninstall);
}
}
drivers->Add(new Spacer(12.0));
}
UI::EventReturn DriverManagerScreen::OnCustomDriverChange(UI::EventParams &e) {
auto di = GetI18NCategory(I18NCat::DIALOG);
screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
if (yes) {
INFO_LOG(G3D, "Switching driver to '%s'", e.s.c_str());
g_Config.sCustomDriver = e.s;
TriggerRestart("GameSettingsScreen::CustomDriverYes", false, gamePath_);
}
}));
return UI::EVENT_DONE;
}
UI::EventReturn DriverManagerScreen::OnCustomDriverUninstall(UI::EventParams &e) {
INFO_LOG(G3D, "Uninstalling driver: %s", e.s.c_str());
Path folder = GetDriverPath() / e.s;
File::DeleteDirRecursively(folder);
RecreateViews();
return UI::EVENT_DONE;
}
UI::EventReturn DriverManagerScreen::OnCustomDriverInstall(UI::EventParams &e) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
System_BrowseForFile(gr->T("Install Custom Driver..."), BrowseFileType::ZIP, [this](const std::string &value, int) {
if (value.empty()) {
return;
}
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
Path zipPath = Path(value);
// Don't bother checking the file extension. Can't always do that with files from Download (they have paths like content://com.android.providers.downloads.documents/document/msf%3A1000001095).
// Though, it may be possible to get it in other ways.
std::unique_ptr<ZipFileReader> zipFileReader = std::unique_ptr<ZipFileReader>(ZipFileReader::Create(zipPath, "", true));
if (!zipFileReader) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver", "couldn't open zip"));
ERROR_LOG(SYSTEM, "Failed to open file '%s' as zip", zipPath.c_str());
return;
}
size_t metaDataSize;
uint8_t *metaData = zipFileReader->ReadFile("meta.json", &metaDataSize);
if (!metaData) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), "meta.json missing");
return;
}
DriverMeta meta;
std::string errorStr;
if (!meta.Read(std::string_view((const char *)metaData, metaDataSize), &errorStr)) {
delete[] metaData;
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), errorStr);
return;
}
delete[] metaData;
const Path newCustomDriver = GetDriverPath() / meta.name;
NOTICE_LOG(G3D, "Installing driver into '%s'", newCustomDriver.c_str());
File::CreateFullPath(newCustomDriver);
std::vector<File::FileInfo> zipListing;
zipFileReader->GetFileListing("", &zipListing, nullptr);
for (auto file : zipListing) {
File::CreateEmptyFile(newCustomDriver / file.name);
size_t size;
uint8_t *data = zipFileReader->ReadFile(file.name.c_str(), &size);
if (!data) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), file.name.c_str());
return;
}
File::WriteDataToFile(false, data, size, newCustomDriver / file.name);
delete[] data;
}
auto iz = GetI18NCategory(I18NCat::INSTALLZIP);
g_OSD.Show(OSDType::MESSAGE_SUCCESS, iz->T("Installed!"));
RecreateViews();
});
return UI::EVENT_DONE;
}

26
UI/DriverManagerScreen.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include "ppsspp_config.h"
#include "Common/UI/UIScreen.h"
#include "UI/MiscScreens.h"
#include "UI/TabbedDialogScreen.h"
// Per-game settings screen - enables you to configure graphic options, control options, etc
// per game.
class DriverManagerScreen : public TabbedUIDialogScreenWithGameBackground {
public:
DriverManagerScreen(const Path &gamePath);
const char *tag() const override { return "DriverManagerScreen"; }
protected:
void CreateTabs() override;
private:
UI::EventReturn OnCustomDriverInstall(UI::EventParams &e);
UI::EventReturn OnCustomDriverUninstall(UI::EventParams &e);
UI::EventReturn OnCustomDriverChange(UI::EventParams &e);
void CreateDriverTab(UI::ViewGroup *drivers);
};

View File

@ -39,6 +39,7 @@
#include "Common/Data/Text/I18n.h"
#include "Common/Data/Encoding/Utf8.h"
#include "UI/EmuScreen.h"
#include "UI/DriverManagerScreen.h"
#include "UI/GameSettingsScreen.h"
#include "UI/GameInfoCache.h"
#include "UI/GamepadEmu.h"
@ -57,8 +58,6 @@
#include "UI/RetroAchievementScreens.h"
#include "Common/File/FileUtil.h"
#include "Common/File/VFS/ZipFileReader.h"
#include "Common/Data/Format/JSONReader.h"
#include "Common/File/AndroidContentURI.h"
#include "Common/OSVersion.h"
#include "Common/TimeUtil.h"
@ -103,20 +102,22 @@ static bool CheckKgslPresent() {
return access(KgslPath, F_OK) == 0;
}
bool SupportsCustomDriver() {
static bool SupportsCustomDriver() {
return android_get_device_api_level() >= 28 && CheckKgslPresent();
}
#else
bool SupportsCustomDriver() {
static bool SupportsCustomDriver() {
#ifdef _DEBUG
return true;
#else
return false;
#endif
}
#endif
static void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath);
GameSettingsScreen::GameSettingsScreen(const Path &gamePath, std::string gameID, bool editThenRestore)
: TabbedUIDialogScreenWithGameBackground(gamePath), gameID_(gameID), editThenRestore_(editThenRestore) {
prevInflightFrames_ = g_Config.iInflightFrames;
@ -1254,93 +1255,6 @@ void GameSettingsScreen::CreateVRSettings(UI::ViewGroup *vrSettings) {
vrSettings->Add(new PopupMultiChoice(&g_Config.iCameraPitch, vr->T("Camera type"), cameraPitchModes, 0, 3, I18NCat::NONE, screenManager()));
}
UI::EventReturn DeveloperToolsScreen::OnCustomDriverChange(UI::EventParams &e) {
auto di = GetI18NCategory(I18NCat::DIALOG);
screenManager()->push(new PromptScreen(gamePath_, di->T("Changing this setting requires PPSSPP to restart."), di->T("Restart"), di->T("Cancel"), [=](bool yes) {
if (yes) {
TriggerRestart("GameSettingsScreen::CustomDriverYes", false, gamePath_);
}
}));
return UI::EVENT_DONE;
}
UI::EventReturn DeveloperToolsScreen::OnCustomDriverInstall(UI::EventParams &e) {
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
System_BrowseForFile(gr->T("Install Custom Driver..."), BrowseFileType::ZIP, [this](const std::string &value, int) {
if (value.empty()) {
return;
}
auto gr = GetI18NCategory(I18NCat::GRAPHICS);
Path zipPath = Path(value);
// Don't bother checking the file extension. Can't always do that with files from Download (they have paths like content://com.android.providers.downloads.documents/document/msf%3A1000001095).
// Though, it may be possible to get it in other ways.
std::unique_ptr<ZipFileReader> zipFileReader = std::unique_ptr<ZipFileReader>(ZipFileReader::Create(zipPath, "", true));
if (!zipFileReader) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver", "couldn't open zip"));
ERROR_LOG(SYSTEM, "Failed to open file '%s' as zip", zipPath.c_str());
return;
}
size_t metaDataSize;
uint8_t *metaData = zipFileReader->ReadFile("meta.json", &metaDataSize);
if (!metaData) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), "meta.json missing");
return;
}
// Validate the json file. TODO: Be a bit more detailed.
json::JsonReader meta = json::JsonReader((const char *)metaData, metaDataSize);
delete[] metaData;
if (!meta.ok()) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), "meta.json not valid json");
return;
}
const JsonNode *nameNode = meta.root().get("name");
if (!nameNode) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), "missing driver name in json");
return;
}
std::string driverName = nameNode->value.toString();
if (driverName.empty()) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), "driver name empty");
return;
}
const Path newCustomDriver = g_Config.internalDataDirectory / "drivers" / driverName;
NOTICE_LOG(G3D, "Installing driver into '%s'", newCustomDriver.c_str());
File::CreateFullPath(newCustomDriver);
std::vector<File::FileInfo> zipListing;
zipFileReader->GetFileListing("", &zipListing, nullptr);
for (auto file : zipListing) {
File::CreateEmptyFile(newCustomDriver / file.name);
size_t size;
uint8_t *data = zipFileReader->ReadFile(file.name.c_str(), &size);
if (!data) {
g_OSD.Show(OSDType::MESSAGE_ERROR, gr->T("The chosen ZIP file doesn't contain a valid driver"), file.name.c_str());
return;
}
File::WriteDataToFile(false, data, size, newCustomDriver / file.name);
delete[] data;
}
auto iz = GetI18NCategory(I18NCat::INSTALLZIP);
g_OSD.Show(OSDType::MESSAGE_SUCCESS, iz->T("Installed!"));
RecreateViews();
});
return UI::EVENT_DONE;
}
UI::EventReturn GameSettingsScreen::OnAutoFrameskip(UI::EventParams &e) {
g_Config.UpdateAfterSettingAutoFrameSkip();
return UI::EVENT_DONE;
@ -1562,7 +1476,7 @@ void GameSettingsScreen::CallbackMemstickFolder(bool yes) {
}
}
static void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath) {
void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath) {
// Extra save here to make sure the choice really gets saved even if there are shutdown bugs in
// the GPU backend code.
g_Config.Save(why);
@ -1835,22 +1749,11 @@ void DeveloperToolsScreen::CreateViews() {
}
if (GetGPUBackend() == GPUBackend::VULKAN && SupportsCustomDriver()) {
const Path driverPath = g_Config.internalDataDirectory / "drivers";
std::vector<File::FileInfo> listing;
File::GetFilesInDir(driverPath, &listing);
std::vector<std::string> availableDrivers;
availableDrivers.push_back("Default");
for (auto driver : listing) {
availableDrivers.push_back(driver.name);
}
auto driverChoice = list->Add(new PopupMultiChoiceDynamic(&g_Config.customDriver, gr->T("Current GPU Driver"), availableDrivers, I18NCat::NONE, screenManager()));
driverChoice->OnChoice.Handle(this, &DeveloperToolsScreen::OnCustomDriverChange);
auto customDriverInstallChoice = list->Add(new Choice(gr->T("Install Custom Driver...")));
customDriverInstallChoice->OnClick.Handle(this, &DeveloperToolsScreen::OnCustomDriverInstall);
auto driverChoice = list->Add(new Choice(gr->T("Adreno Driver Manager")));
driverChoice->OnClick.Add([=](UI::EventParams &e) {
screenManager()->push(new DriverManagerScreen(gamePath_));
return UI::EVENT_DONE;
});
}
// For now, we only implement GPU driver tests for Vulkan and OpenGL. This is simply

View File

@ -27,6 +27,8 @@
#include "UI/MiscScreens.h"
#include "UI/TabbedDialogScreen.h"
class Path;
// Per-game settings screen - enables you to configure graphic options, control options, etc
// per game.
class GameSettingsScreen : public TabbedUIDialogScreenWithGameBackground {
@ -151,8 +153,6 @@ private:
UI::EventReturn OnFramedumpTest(UI::EventParams &e);
UI::EventReturn OnTouchscreenTest(UI::EventParams &e);
UI::EventReturn OnCopyStatesToRoot(UI::EventParams &e);
UI::EventReturn OnCustomDriverChange(UI::EventParams &e);
UI::EventReturn OnCustomDriverInstall(UI::EventParams &e);
bool allowDebugger_ = false;
bool canAllowDebugger_ = true;
@ -240,3 +240,5 @@ private:
void OnCompleted(DialogResult result) override;
int restoreFlags_ = (int)(RestoreSettingsBits::SETTINGS); // RestoreSettingsBits enum
};
void TriggerRestart(const char *why, bool editThenRestore, const Path &gamePath);

View File

@ -234,7 +234,7 @@ bool RemoteISOConnectScreen::FindServer(std::string &resultHost, int &resultPort
if (scanCancelled)
return false;
const char *host = entry.getString("ip", "");
const char *host = entry.getStringOr("ip", "");
int port = entry.getInt("p", 0);
if (TryServer(host, port)) {

View File

@ -475,12 +475,12 @@ void StoreScreen::ParseListing(const std::string &json) {
e.type = ENTRY_PBPZIP;
e.name = GetTranslatedString(game, "name");
e.description = GetTranslatedString(game, "description", "");
e.author = game.getString("author", "?");
e.author = game.getStringOr("author", "?");
e.size = game.getInt("size");
e.downloadURL = game.getString("download-url", "");
e.iconURL = game.getString("icon-url", "");
e.downloadURL = game.getStringOr("download-url", "");
e.iconURL = game.getStringOr("icon-url", "");
e.hidden = false; // NOTE: Handling of the "hidden" flag is broken in old versions of PPSSPP. Do not use.
const char *file = game.getString("file", nullptr);
const char *file = game.getStringOr("file", nullptr);
if (!file)
continue;
e.file = file;
@ -596,7 +596,7 @@ std::string StoreScreen::GetTranslatedString(const json::JsonGet json, const std
}
const char *str = nullptr;
if (dict) {
str = dict.getString(key.c_str(), nullptr);
str = dict.getStringOr(key.c_str(), nullptr);
}
if (str) {
return std::string(str);

View File

@ -44,6 +44,7 @@
<ClCompile Include="DevScreens.cpp" />
<ClCompile Include="DiscordIntegration.cpp" />
<ClCompile Include="DisplayLayoutScreen.cpp" />
<ClCompile Include="DriverManagerScreen.cpp" />
<ClCompile Include="EmuScreen.cpp" />
<ClCompile Include="GameInfoCache.cpp" />
<ClCompile Include="GamepadEmu.cpp" />
@ -81,6 +82,7 @@
<ClInclude Include="DevScreens.h" />
<ClInclude Include="DiscordIntegration.h" />
<ClInclude Include="DisplayLayoutScreen.h" />
<ClInclude Include="DriverManagerScreen.h" />
<ClInclude Include="EmuScreen.h" />
<ClInclude Include="GameInfoCache.h" />
<ClInclude Include="GamepadEmu.h" />

View File

@ -92,6 +92,9 @@
<ClCompile Include="DebugOverlay.cpp">
<Filter>Screens</Filter>
</ClCompile>
<ClCompile Include="DriverManagerScreen.cpp">
<Filter>Screens</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="GameInfoCache.h" />
@ -184,6 +187,9 @@
<ClInclude Include="DebugOverlay.h">
<Filter>Screens</Filter>
</ClInclude>
<ClInclude Include="DriverManagerScreen.h">
<Filter>Screens</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Filter Include="Screens">

View File

@ -119,6 +119,7 @@
<ClInclude Include="..\..\UI\DevScreens.h" />
<ClInclude Include="..\..\UI\DiscordIntegration.h" />
<ClInclude Include="..\..\UI\DisplayLayoutScreen.h" />
<ClInclude Include="..\..\UI\DriverManagerScreen.h" />
<ClInclude Include="..\..\UI\EmuScreen.h" />
<ClInclude Include="..\..\UI\GameInfoCache.h" />
<ClInclude Include="..\..\UI\GamepadEmu.h" />
@ -157,6 +158,7 @@
<ClCompile Include="..\..\UI\DevScreens.cpp" />
<ClCompile Include="..\..\UI\DiscordIntegration.cpp" />
<ClCompile Include="..\..\UI\DisplayLayoutScreen.cpp" />
<ClCompile Include="..\..\UI\DriverManagerScreen.cpp" />
<ClCompile Include="..\..\UI\EmuScreen.cpp" />
<ClCompile Include="..\..\UI\GameInfoCache.cpp" />
<ClCompile Include="..\..\UI\GamepadEmu.cpp" />

View File

@ -37,6 +37,7 @@
<ClCompile Include="..\..\UI\TabbedDialogScreen.cpp" />
<ClCompile Include="..\..\UI\RetroAchievementScreens.cpp" />
<ClCompile Include="..\..\UI\DebugOverlay.cpp" />
<ClCompile Include="..\..\UI\DriverManagerScreen.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h" />
@ -75,5 +76,6 @@
<ClInclude Include="..\..\UI\TabbedDialogScreen.h" />
<ClInclude Include="..\..\UI\RetroAchievementScreens.h" />
<ClInclude Include="..\..\UI\DebugOverlay.h" />
<ClInclude Include="..\..\UI\DriverManagerScreen.h" />
</ItemGroup>
</Project>

View File

@ -812,6 +812,7 @@ LOCAL_SRC_FILES := \
$(SRC)/UI/ChatScreen.cpp \
$(SRC)/UI/DebugOverlay.cpp \
$(SRC)/UI/DevScreens.cpp \
$(SRC)/UI/DriverManagerScreen.cpp \
$(SRC)/UI/DisplayLayoutScreen.cpp \
$(SRC)/UI/EmuScreen.cpp \
$(SRC)/UI/MainScreen.cpp \