diff --git a/Common/System/Request.h b/Common/System/Request.h index d0e67c78e6cc..547b073878e0 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -111,6 +111,10 @@ inline void System_RestartApp(const std::string ¶ms) { g_requestManager.MakeSystemRequest(SystemRequestType::RESTART_APP, nullptr, nullptr, params, "", 0); } +inline void System_RecreateActivity() { + g_requestManager.MakeSystemRequest(SystemRequestType::RECREATE_ACTIVITY, nullptr, nullptr, "", "", 0); +} + // The design is a little weird, just a holdover from the old message. Can either toggle or set to on or off. inline void System_ToggleFullscreenState(const std::string ¶m) { g_requestManager.MakeSystemRequest(SystemRequestType::TOGGLE_FULLSCREEN_STATE, nullptr, nullptr, param, "", 0); diff --git a/Common/System/System.h b/Common/System/System.h index 002bdf633a4b..d00324d9b65f 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -63,6 +63,7 @@ enum class SystemRequestType { EXIT_APP, RESTART_APP, // For graphics backend changes + RECREATE_ACTIVITY, // Android COPY_TO_CLIPBOARD, SHARE_TEXT, SET_WINDOW_TITLE, diff --git a/Core/Config.cpp b/Core/Config.cpp index 4a687683dcef..8a3b0f36f7b6 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -300,6 +300,34 @@ static int DefaultFastForwardMode() { #endif } +static int DefaultAndroidHwScale() { +#ifdef __ANDROID__ + if (System_GetPropertyInt(SYSPROP_SYSTEMVERSION) >= 19 || System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) { + // Arbitrary cutoff at Kitkat - modern devices are usually powerful enough that hw scaling + // doesn't really help very much and mostly causes problems. See #11151 + return 0; + } + + // Get the real resolution as passed in during startup, not dp_xres and stuff + int xres = System_GetPropertyInt(SYSPROP_DISPLAY_XRES); + int yres = System_GetPropertyInt(SYSPROP_DISPLAY_YRES); + + if (xres <= 960) { + // Smaller than the PSP*2, let's go native. + return 0; + } else if (xres <= 480 * 3) { // 720p xres + // Small-ish screen, we should default to 2x + return 2 + 1; + } else { + // Large or very large screen. Default to 3x psp resolution. + return 3 + 1; + } + return 0; +#else + return 1; +#endif +} + // See issue 14439. Should possibly even block these devices from selecting VK. const char * const vulkanDefaultBlacklist[] = { "Sony:BRAVIA VH1", @@ -498,6 +526,7 @@ static const ConfigSetting graphicsSettings[] = { ConfigSetting("SoftwareSkinning", &g_Config.bSoftwareSkinning, true, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("TextureFiltering", &g_Config.iTexFiltering, 1, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("InternalResolution", &g_Config.iInternalResolution, &DefaultInternalResolution, CfgFlag::PER_GAME | CfgFlag::REPORT), + ConfigSetting("AndroidHwScale", &g_Config.iAndroidHwScale, &DefaultAndroidHwScale, CfgFlag::DEFAULT), ConfigSetting("HighQualityDepth", &g_Config.bHighQualityDepth, true, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("FrameSkip", &g_Config.iFrameSkip, 0, CfgFlag::PER_GAME | CfgFlag::REPORT), ConfigSetting("FrameSkipType", &g_Config.iFrameSkipType, 0, CfgFlag::PER_GAME | CfgFlag::REPORT), diff --git a/Core/Config.h b/Core/Config.h index e871c14ebce2..1d36130b4966 100644 --- a/Core/Config.h +++ b/Core/Config.h @@ -398,6 +398,9 @@ struct Config { bool bSystemControls; + // Use the hardware scaler to scale up the image to save fillrate. Similar to Windows' window size, really. + int iAndroidHwScale; // 0 = device resolution. 1 = 480x272 (extended to correct aspect), 2 = 960x544 etc. + // Risky JIT optimizations bool bDiscardRegsOnJRRA; diff --git a/UI/ControlMappingScreen.cpp b/UI/ControlMappingScreen.cpp index 0579ddbbffc2..0747181e1c63 100644 --- a/UI/ControlMappingScreen.cpp +++ b/UI/ControlMappingScreen.cpp @@ -772,6 +772,9 @@ void RecreateActivity() { UI::EventReturn TouchTestScreen::OnImmersiveModeChange(UI::EventParams &e) { System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE); + if (g_Config.iAndroidHwScale != 0) { + RecreateActivity(); + } return UI::EVENT_DONE; } diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index b77512dc7157..ab15e6137d89 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -368,6 +368,18 @@ void GameSettingsScreen::CreateGraphicsSettings(UI::ViewGroup *graphicsSettings) } } +#if PPSSPP_PLATFORM(ANDROID) + if ((deviceType != DEVICE_TYPE_TV) && (deviceType != DEVICE_TYPE_VR)) { + static const char *deviceResolutions[] = { "Native device resolution", "Auto (same as Rendering)", "1x PSP", "2x PSP", "3x PSP", "4x PSP", "5x PSP" }; + int max_res_temp = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 2; + if (max_res_temp == 3) + max_res_temp = 4; // At least allow 2x + int max_res = std::min(max_res_temp, (int)ARRAY_SIZE(deviceResolutions)); + UI::PopupMultiChoice *hwscale = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iAndroidHwScale, gr->T("Display Resolution (HW scaler)"), deviceResolutions, 0, max_res, I18NCat::GRAPHICS, screenManager())); + hwscale->OnChoice.Handle(this, &GameSettingsScreen::OnHwScaleChange); // To refresh the display mode + } +#endif + if (deviceType != DEVICE_TYPE_VR) { #if !defined(MOBILE_DEVICE) graphicsSettings->Add(new CheckBox(&g_Config.bFullScreen, gr->T("FullScreen", "Full Screen")))->OnClick.Handle(this, &GameSettingsScreen::OnFullscreenChange); @@ -1206,6 +1218,9 @@ UI::EventReturn GameSettingsScreen::OnAdhocGuides(UI::EventParams &e) { UI::EventReturn GameSettingsScreen::OnImmersiveModeChange(UI::EventParams &e) { System_Notify(SystemNotification::IMMERSIVE_MODE_CHANGE); + if (g_Config.iAndroidHwScale != 0) { + System_RecreateActivity(); + } return UI::EVENT_DONE; } @@ -1327,11 +1342,19 @@ UI::EventReturn GameSettingsScreen::OnFullscreenMultiChange(UI::EventParams &e) } UI::EventReturn GameSettingsScreen::OnResolutionChange(UI::EventParams &e) { + if (g_Config.iAndroidHwScale == 1) { + System_RecreateActivity(); + } Reporting::UpdateConfig(); NativeMessageReceived("gpu_renderResized", ""); return UI::EVENT_DONE; } +UI::EventReturn GameSettingsScreen::OnHwScaleChange(UI::EventParams &e) { + System_RecreateActivity(); + return UI::EVENT_DONE; +} + void GameSettingsScreen::onFinish(DialogResult result) { Reporting::Enable(enableReports_, "report.ppsspp.org"); Reporting::UpdateConfig(); diff --git a/UI/GameSettingsScreen.h b/UI/GameSettingsScreen.h index 89f1ce23ad06..882b60263ce1 100644 --- a/UI/GameSettingsScreen.h +++ b/UI/GameSettingsScreen.h @@ -108,6 +108,7 @@ class GameSettingsScreen : public UIDialogScreenWithGameBackground { UI::EventReturn OnFullscreenChange(UI::EventParams &e); UI::EventReturn OnFullscreenMultiChange(UI::EventParams &e); UI::EventReturn OnResolutionChange(UI::EventParams &e); + UI::EventReturn OnHwScaleChange(UI::EventParams &e); UI::EventReturn OnRestoreDefaultSettings(UI::EventParams &e); UI::EventReturn OnRenderingMode(UI::EventParams &e); UI::EventReturn OnRenderingBackend(UI::EventParams &e); diff --git a/UI/NativeApp.cpp b/UI/NativeApp.cpp index 1985583e5dd8..22c8543d683d 100644 --- a/UI/NativeApp.cpp +++ b/UI/NativeApp.cpp @@ -211,6 +211,22 @@ std::string NativeQueryConfig(std::string query) { return std::string(temp); } else if (query == "immersiveMode") { return std::string(g_Config.bImmersiveMode ? "1" : "0"); + } else if (query == "hwScale") { + int scale = g_Config.iAndroidHwScale; + // Override hw scale for TV type devices. + if (System_GetPropertyInt(SYSPROP_DEVICE_TYPE) == DEVICE_TYPE_TV) + scale = 0; + + if (scale == 1) { + // If g_Config.iInternalResolution is also set to Auto (1), we fall back to "Device resolution" (0). It works out. + scale = g_Config.iInternalResolution; + } else if (scale >= 2) { + scale -= 1; + } + + int max_res = std::max(System_GetPropertyInt(SYSPROP_DISPLAY_XRES), System_GetPropertyInt(SYSPROP_DISPLAY_YRES)) / 480 + 1; + snprintf(temp, sizeof(temp), "%d", std::min(scale, max_res)); + return std::string(temp); } else if (query == "sustainedPerformanceMode") { return std::string(g_Config.bSustainedPerformanceMode ? "1" : "0"); } else if (query == "androidJavaGL") { diff --git a/android/jni/app-android.cpp b/android/jni/app-android.cpp index 76e99cb1aa2f..1084252a14f1 100644 --- a/android/jni/app-android.cpp +++ b/android/jni/app-android.cpp @@ -1046,6 +1046,9 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string case SystemRequestType::RESTART_APP: PushCommand("graphics_restart", param1); return true; + case SystemRequestType::RECREATE_ACTIVITY: + PushCommand("recreate", param1); + return true; case SystemRequestType::INPUT_TEXT_MODAL: { std::string serialized = StringFromFormat("%d:@:%s:@:%s", requestId, param1.c_str(), param2.c_str()); @@ -1283,6 +1286,50 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeActivity_requestExitVulkanR } } +void correctRatio(int &sz_x, int &sz_y, float scale) { + float x = (float)sz_x; + float y = (float)sz_y; + float ratio = x / y; + INFO_LOG(G3D, "CorrectRatio: Considering size: %0.2f/%0.2f=%0.2f for scale %f", x, y, ratio, scale); + float targetRatio; + + // Try to get the longest dimension to match scale*PSP resolution. + if (x >= y) { + targetRatio = 480.0f / 272.0f; + x = 480.f * scale; + y = 272.f * scale; + } else { + targetRatio = 272.0f / 480.0f; + x = 272.0f * scale; + y = 480.0f * scale; + } + + float correction = targetRatio / ratio; + INFO_LOG(G3D, "Target ratio: %0.2f ratio: %0.2f correction: %0.2f", targetRatio, ratio, correction); + if (ratio < targetRatio) { + y *= correction; + } else { + x /= correction; + } + + sz_x = x; + sz_y = y; + INFO_LOG(G3D, "Corrected ratio: %dx%d", sz_x, sz_y); +} + +void getDesiredBackbufferSize(int &sz_x, int &sz_y) { + sz_x = display_xres; + sz_y = display_yres; + std::string config = NativeQueryConfig("hwScale"); + int scale; + if (1 == sscanf(config.c_str(), "%d", &scale) && scale > 0) { + correctRatio(sz_x, sz_y, scale); + } else { + sz_x = 0; + sz_y = 0; + } +} + extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JNIEnv *, jclass, jint xres, jint yres, jint dpi, jfloat refreshRate) { INFO_LOG(G3D, "NativeApp.setDisplayParameters(%d x %d, dpi=%d, refresh=%0.2f)", xres, yres, dpi, refreshRate); @@ -1311,6 +1358,18 @@ extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_setDisplayParameters(JN } } +extern "C" void JNICALL Java_org_ppsspp_ppsspp_NativeApp_computeDesiredBackbufferDimensions() { + getDesiredBackbufferSize(desiredBackbufferSizeX, desiredBackbufferSizeY); +} + +extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferWidth(JNIEnv *, jclass) { + return desiredBackbufferSizeX; +} + +extern "C" jint JNICALL Java_org_ppsspp_ppsspp_NativeApp_getDesiredBackbufferHeight(JNIEnv *, jclass) { + return desiredBackbufferSizeY; +} + std::vector System_GetCameraDeviceList() { jclass cameraClass = findClass("org/ppsspp/ppsspp/CameraHelper"); jmethodID deviceListMethod = getEnv()->GetStaticMethodID(cameraClass, "getDeviceList", "()Ljava/util/ArrayList;"); diff --git a/android/src/org/ppsspp/ppsspp/SizeManager.java b/android/src/org/ppsspp/ppsspp/SizeManager.java index 92a8dbc44f77..174396f86300 100644 --- a/android/src/org/ppsspp/ppsspp/SizeManager.java +++ b/android/src/org/ppsspp/ppsspp/SizeManager.java @@ -85,6 +85,11 @@ public void surfaceCreated(SurfaceHolder holder) { Log.d(TAG, "Surface created. pixelWidth=" + pixelWidth + ", pixelHeight=" + pixelHeight + " holder: " + holder.toString() + " or: " + requestedOr); NativeApp.setDisplayParameters(pixelWidth, pixelHeight, (int)densityDpi, refreshRate); + getDesiredBackbufferSize(desiredSize); + + // Note that desiredSize might be 0,0 here - but that's fine when calling setFixedSize! It means auto. + Log.d(TAG, "Setting fixed size " + desiredSize.x + " x " + desiredSize.y); + holder.setFixedSize(desiredSize.x, desiredSize.y); } @Override