diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp index 885a6f88..e736e0a4 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.cpp @@ -2631,7 +2631,13 @@ bool VulkanRenderer::AcquireNextSwapchainImage(bool mainWindow) m_padCloseReadySemaphore.notify(); return false; } - +#if __ANDROID__ + std::unique_lock lock(m_surfaceMutex); + m_surfaceCondVar.wait(lock,[&](){ + auto& chainInfo = GetChainInfoPtr(mainWindow); + return chainInfo && chainInfo->surface; + }); +#endif // __ANDROID__ auto& chainInfo = GetChainInfo(mainWindow); if (chainInfo.swapchainImageIndex != -1) @@ -3770,7 +3776,29 @@ void VulkanRenderer::AppendOverlayDebugInfo() ImGui::Text("--- Tex heaps ---"); memoryManager->appendOverlayHeapDebugInfo(); } - +#if __ANDROID__ +void VulkanRenderer::ClearSurface(bool mainWindow) +{ + std::lock_guard lock(m_surfaceMutex); + auto& chainInfo = GetChainInfoPtr(mainWindow); + if(!chainInfo || !chainInfo->surface) + return; + vkDestroySurfaceKHR(m_instance, chainInfo->surface, nullptr); + chainInfo->surface = nullptr; +} +void VulkanRenderer::NotifySurfaceChanged(bool mainWindow) +{ + std::lock_guard lock(m_surfaceMutex); + auto& chainInfo = GetChainInfoPtr(mainWindow); + if(!chainInfo) + return; + if(mainWindow) + chainInfo->surface = CreateFramebufferSurface(m_instance, gui_getWindowInfo().canvas_main); + else + chainInfo->surface = CreateFramebufferSurface(m_instance, gui_getWindowInfo().canvas_pad); + m_surfaceCondVar.notify_one(); +} +#endif // __ANDROID__ void VKRDestructibleObject::flagForCurrentCommandBuffer() { m_lastCmdBufferId = VulkanRenderer::GetInstance()->GetCurrentCommandBufferId(); diff --git a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h index 93e582e7..07b4b25a 100644 --- a/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h +++ b/src/Cafe/HW/Latte/Renderer/Vulkan/VulkanRenderer.h @@ -183,7 +183,10 @@ public: void GetDeviceFeatures(); void DetermineVendor(); void InitializeSurface(const Vector2i& size, bool mainWindow); - +#if __ANDROID__ + void ClearSurface(bool mainWindow); + void NotifySurfaceChanged(bool mainWindow); +#endif // __ANDROID const std::unique_ptr& GetChainInfoPtr(bool mainWindow) const; SwapchainInfoVk& GetChainInfo(bool mainWindow) const; @@ -610,6 +613,11 @@ private: VkPipelineLayout m_pipelineLayout{nullptr}; VkCommandPool m_commandPool{ nullptr }; +#if __ANDROID__ + std::mutex m_surfaceMutex; + std::condition_variable m_surfaceCondVar; +#endif // __ANDROID__ + // buffer to cache uniform vars VkBuffer m_uniformVarBuffer = VK_NULL_HANDLE; VkDeviceMemory m_uniformVarBufferMemory = VK_NULL_HANDLE; diff --git a/src/android/app/src/main/cpp/native-lib.cpp b/src/android/app/src/main/cpp/native-lib.cpp index 4133aff4..7a60c9e4 100644 --- a/src/android/app/src/main/cpp/native-lib.cpp +++ b/src/android/app/src/main/cpp/native-lib.cpp @@ -180,10 +180,23 @@ Java_info_cemu_Cemu_NativeLibrary_initializeRendererSurface(JNIEnv *env, jclass VulkanRenderer::GetInstance()->InitializeSurface( {width, height}, is_main_canvas); + } extern "C" JNIEXPORT void JNICALL Java_info_cemu_Cemu_NativeLibrary_setDPI(JNIEnv *env, jclass clazz, jfloat dpi) { - auto& windowInfo = gui_getWindowInfo(); + auto &windowInfo = gui_getWindowInfo(); windowInfo.dpi_scale = windowInfo.pad_dpi_scale = dpi; } + +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_clearSurface(JNIEnv *env, jclass clazz, jboolean is_main_canvas) { + VulkanRenderer::GetInstance()->ClearSurface(is_main_canvas); +} +extern "C" +JNIEXPORT void JNICALL +Java_info_cemu_Cemu_NativeLibrary_recreateRenderSurface(JNIEnv *env, jclass clazz, + jboolean is_main_canvas) { + VulkanRenderer::GetInstance()->NotifySurfaceChanged(is_main_canvas); +} \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java b/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java index 93355862..19a0adb9 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/EmulationFragment.java @@ -1,24 +1,16 @@ package info.cemu.Cemu; -import android.content.Intent; import android.os.Bundle; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.recyclerview.widget.LinearLayoutManager; -import androidx.recyclerview.widget.RecyclerView; - import android.view.LayoutInflater; import android.view.SurfaceHolder; import android.view.SurfaceView; import android.view.View; import android.view.ViewGroup; -import java.util.Objects; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import info.cemu.Cemu.databinding.FragmentEmulationBinding; -import info.cemu.Cemu.databinding.FragmentGamesBinding; public class EmulationFragment extends Fragment implements SurfaceHolder.Callback { FragmentEmulationBinding binding; @@ -35,7 +27,7 @@ public class EmulationFragment extends Fragment implements SurfaceHolder.Callbac } long gameTitleId; - boolean isRunning; + boolean isGameRunning; @Override public void onCreate(Bundle savedInstanceState) { @@ -55,15 +47,6 @@ public class EmulationFragment extends Fragment implements SurfaceHolder.Callbac return binding.getRoot(); } - private void startGame() { - if (!isRunning) { - isRunning = true; - NativeLibrary.initializerRenderer(); - NativeLibrary.initializeRendererSurface(true); - NativeLibrary.startGame(gameTitleId); - } - } - @Override public void surfaceCreated(@NonNull SurfaceHolder surfaceHolder) { @@ -73,11 +56,18 @@ public class EmulationFragment extends Fragment implements SurfaceHolder.Callbac public void surfaceChanged(@NonNull SurfaceHolder surfaceHolder, int format, int width, int height) { NativeLibrary.setSurface(surfaceHolder.getSurface(), true); NativeLibrary.setSurfaceSize(width, height, true); - startGame(); + if (!isGameRunning) { + isGameRunning = true; + NativeLibrary.initializerRenderer(); + NativeLibrary.initializeRendererSurface(true); + NativeLibrary.startGame(gameTitleId); + }else{ + NativeLibrary.recreateRenderSurface(true); + } } @Override public void surfaceDestroyed(@NonNull SurfaceHolder surfaceHolder) { - + NativeLibrary.clearSurface(true); } } \ No newline at end of file diff --git a/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java b/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java index 4251faf2..17a8b1d9 100644 --- a/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java +++ b/src/android/app/src/main/java/info/cemu/Cemu/NativeLibrary.java @@ -11,6 +11,8 @@ public class NativeLibrary { public static native void setSurface(Surface surface, boolean isMainCanvas); + public static native void clearSurface(boolean isMainCanvas); + public static native void setSurfaceSize(int width, int height, boolean isMainCanvas); public static native void initializerRenderer(); @@ -19,6 +21,8 @@ public class NativeLibrary { public static native void startGame(long titleId); + public static native void recreateRenderSurface(boolean isMainCanvas); + public interface GameTitleLoadedCallback { void onGameTitleLoaded(long titleId, String title);