From 057117f0096a47b07f9070d48a0dbd952ab0522e Mon Sep 17 00:00:00 2001 From: bunnei Date: Thu, 1 Jun 2023 17:57:49 -0700 Subject: [PATCH] android: Fix presentation layout on foldable and tablet devices. --- .../yuzu_emu/activities/EmulationActivity.kt | 24 ++++++++ .../features/settings/ui/SettingsActivity.kt | 6 -- .../org/yuzu/yuzu_emu/overlay/InputOverlay.kt | 10 ++-- src/android/app/src/main/jni/native.cpp | 21 ++++++- .../renderer_vulkan/vk_blit_screen.cpp | 55 ++++++++++++++++--- 5 files changed, 94 insertions(+), 22 deletions(-) diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt index abeb019957..81474b8246 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/activities/EmulationActivity.kt @@ -7,6 +7,7 @@ import android.app.Activity import android.content.Context import android.content.DialogInterface import android.content.Intent +import android.content.res.Configuration import android.graphics.Rect import android.hardware.Sensor import android.hardware.SensorEvent @@ -31,6 +32,7 @@ import org.yuzu.yuzu_emu.features.settings.model.Settings import org.yuzu.yuzu_emu.fragments.EmulationFragment import org.yuzu.yuzu_emu.model.Game import org.yuzu.yuzu_emu.utils.ControllerMappingHelper +import org.yuzu.yuzu_emu.utils.EmulationMenuSettings import org.yuzu.yuzu_emu.utils.ForegroundService import org.yuzu.yuzu_emu.utils.InputHandler import org.yuzu.yuzu_emu.utils.NfcReader @@ -128,6 +130,11 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { super.onResume() nfcReader.startScanning() startMotionSensorListener() + + NativeLibrary.notifyOrientationChange( + EmulationMenuSettings.landscapeScreenLayout, + getAdjustedRotation() + ) } override fun onPause() { @@ -233,6 +240,23 @@ class EmulationActivity : AppCompatActivity(), SensorEventListener { override fun onAccuracyChanged(sensor: Sensor, i: Int) {} + private fun getAdjustedRotation():Int { + val rotation = windowManager.defaultDisplay.rotation; + val config: Configuration = resources.configuration + + if ((config.screenLayout and Configuration.SCREENLAYOUT_LONG_YES) != 0 || + (config.screenLayout and Configuration.SCREENLAYOUT_LONG_NO) == 0) { + return rotation; + } + when (rotation) { + Surface.ROTATION_0 -> return Surface.ROTATION_90; + Surface.ROTATION_90 -> return Surface.ROTATION_0; + Surface.ROTATION_180 -> return Surface.ROTATION_270; + Surface.ROTATION_270 -> return Surface.ROTATION_180; + } + return rotation; + } + private fun restoreState(savedInstanceState: Bundle) { game = savedInstanceState.parcelable(EXTRA_SELECTED_GAME)!! } diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt index 7831228609..72e2cce2ac 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/features/settings/ui/SettingsActivity.kt @@ -118,12 +118,6 @@ class SettingsActivity : AppCompatActivity(), SettingsActivityView { override fun onStop() { super.onStop() presenter.onStop(isFinishing) - - // Update framebuffer layout when closing the settings - NativeLibrary.notifyOrientationChange( - EmulationMenuSettings.landscapeScreenLayout, - windowManager.defaultDisplay.rotation - ) } override fun showSettingsFragment(menuTag: String, addToStack: Boolean, gameId: String) { diff --git a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt index 693e709732..c9f5797acd 100644 --- a/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt +++ b/src/android/app/src/main/java/org/yuzu/yuzu_emu/overlay/InputOverlay.kt @@ -767,10 +767,11 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context var cutoutBottom = 0 val insets = context.windowManager.currentWindowMetrics.windowInsets.displayCutout if (insets != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - maxY = - if (insets.boundingRectTop.bottom != 0) insets.boundingRectTop.bottom.toFloat() else maxY - maxX = - if (insets.boundingRectRight.left != 0) insets.boundingRectRight.left.toFloat() else maxX + if (insets.boundingRectTop.bottom != 0 && insets.boundingRectTop.bottom > maxY / 2) + insets.boundingRectTop.bottom.toFloat() else maxY + if (insets.boundingRectRight.left != 0 && insets.boundingRectRight.left > maxX / 2) + insets.boundingRectRight.left.toFloat() else maxX + minX = insets.boundingRectLeft.right - insets.boundingRectLeft.left minY = insets.boundingRectBottom.top - insets.boundingRectBottom.bottom @@ -778,7 +779,6 @@ class InputOverlay(context: Context, attrs: AttributeSet?) : SurfaceView(context cutoutBottom = insets.boundingRectTop.top - insets.boundingRectTop.bottom } - // This makes sure that if we have an inset on one side of the screen, we mirror it on // the other side. Since removing space from one of the max values messes with the scale, // we also have to account for it using our min values. diff --git a/src/android/app/src/main/jni/native.cpp b/src/android/app/src/main/jni/native.cpp index bbe6abdb0a..d503ef61d0 100644 --- a/src/android/app/src/main/jni/native.cpp +++ b/src/android/app/src/main/jni/native.cpp @@ -89,8 +89,16 @@ public: return m_native_window; } - void SetNativeWindow(ANativeWindow* m_native_window_) { - m_native_window = m_native_window_; + void SetNativeWindow(ANativeWindow* native_window) { + m_native_window = native_window; + } + + u32 ScreenRotation() const { + return m_screen_rotation; + } + + void SetScreenRotation(u32 screen_rotation) { + m_screen_rotation = screen_rotation; } void InitializeGpuDriver(const std::string& hook_lib_dir, const std::string& custom_driver_dir, @@ -379,6 +387,7 @@ private: // Window management std::unique_ptr m_window; ANativeWindow* m_native_window{}; + u32 m_screen_rotation{}; // Core emulation Core::System m_system; @@ -404,6 +413,10 @@ private: } // Anonymous namespace +u32 GetAndroidScreenRotation() { + return EmulationSession::GetInstance().ScreenRotation(); +} + static Core::SystemResultStatus RunEmulation(const std::string& filepath) { Common::Log::Initialize(); Common::Log::SetColorConsoleBackendEnabled(true); @@ -450,7 +463,9 @@ void Java_org_yuzu_yuzu_1emu_NativeLibrary_surfaceDestroyed(JNIEnv* env, void Java_org_yuzu_yuzu_1emu_NativeLibrary_notifyOrientationChange(JNIEnv* env, [[maybe_unused]] jclass clazz, jint layout_option, - jint rotation) {} + jint rotation) { + return EmulationSession::GetInstance().SetScreenRotation(static_cast(rotation)); +} void Java_org_yuzu_yuzu_1emu_NativeLibrary_setAppDirectory(JNIEnv* env, [[maybe_unused]] jclass clazz, diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp index e4c581a283..7cdde992b9 100644 --- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp +++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp @@ -37,6 +37,10 @@ #include "video_core/vulkan_common/vulkan_memory_allocator.h" #include "video_core/vulkan_common/vulkan_wrapper.h" +#ifdef ANDROID +extern u32 GetAndroidScreenRotation(); +#endif + namespace Vulkan { namespace { @@ -74,23 +78,58 @@ struct ScreenRectVertex { } }; -constexpr std::array MakeOrthographicMatrix(f32 width, f32 height) { - // clang-format off #ifdef ANDROID - // Android renders in portrait, so rotate the matrix. - return { 0.f, 2.f / width, 0.f, 0.f, - -2.f / height, 0.f, 0.f, 0.f, - 0.f, 0.f, 1.f, 0.f, - 1.f, -1.f, 0.f, 1.f}; + +std::array MakeOrthographicMatrix(f32 width, f32 height) { + constexpr u32 ROTATION_0 = 0; + constexpr u32 ROTATION_90 = 1; + constexpr u32 ROTATION_180 = 2; + constexpr u32 ROTATION_270 = 3; + + // clang-format off + switch (GetAndroidScreenRotation()) { + case ROTATION_0: + // Desktop + return { 2.f / width, 0.f, 0.f, 0.f, + 0.f, 2.f / height, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, -1.f, 0.f, 1.f}; + case ROTATION_180: + // Reverse desktop + return {-2.f / width, 0.f, 0.f, 0.f, + 0.f, -2.f / height, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 1.f, 1.f, 0.f, 1.f}; + case ROTATION_270: + // Reverse landscape + return { 0.f, -2.f / width, 0.f, 0.f, + 2.f / height, 0.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + -1.f, 1.f, 0.f, 1.f}; + case ROTATION_90: + default: + // Landscape + return { 0.f, 2.f / width, 0.f, 0.f, + -2.f / height, 0.f, 0.f, 0.f, + 0.f, 0.f, 1.f, 0.f, + 1.f, -1.f, 0.f, 1.f}; + } + // clang-format on +} + #else + +std::array MakeOrthographicMatrix(f32 width, f32 height) { + // clang-format off return { 2.f / width, 0.f, 0.f, 0.f, 0.f, 2.f / height, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, -1.f, -1.f, 0.f, 1.f}; -#endif // ANDROID // clang-format on } +#endif + u32 GetBytesPerPixel(const Tegra::FramebufferConfig& framebuffer) { using namespace VideoCore::Surface; return BytesPerBlock(PixelFormatFromGPUPixelFormat(framebuffer.pixel_format));