From 067ac4fe018da0d12b77c167823795cc30b95110 Mon Sep 17 00:00:00 2001 From: Benau Date: Sat, 2 Apr 2022 13:33:08 +0800 Subject: [PATCH] Implement resume behaviour for mobile stk --- .../src/main/java/SuperTuxKartActivity.java | 4 + .../include/ge_vulkan_driver.hpp | 9 +- .../src/ge_vulkan_2d_renderer.cpp | 8 +- .../src/ge_vulkan_2d_renderer.hpp | 2 + lib/graphics_engine/src/ge_vulkan_driver.cpp | 145 +++++++++++++++--- lib/irrlicht/include/IVideoDriver.h | 2 + .../source/Irrlicht/CIrrDeviceSDL.cpp | 4 + src/main_android.cpp | 4 +- src/main_loop.cpp | 5 + 9 files changed, 160 insertions(+), 23 deletions(-) diff --git a/android/src/main/java/SuperTuxKartActivity.java b/android/src/main/java/SuperTuxKartActivity.java index 7b2210697..9862948ff 100644 --- a/android/src/main/java/SuperTuxKartActivity.java +++ b/android/src/main/java/SuperTuxKartActivity.java @@ -84,6 +84,8 @@ public class SuperTuxKartActivity extends SDLActivity // ------------------------------------------------------------------------ private native static void addDNSSrvRecords(String name, int weight); // ------------------------------------------------------------------------ + private native static void pauseRenderingJNI(); + // ------------------------------------------------------------------------ private void showExtractProgressPrivate() { WindowManager wm = @@ -283,6 +285,8 @@ public class SuperTuxKartActivity extends SDLActivity { super.onPause(); hideKeyboardNative(false/*clear_text*/); + if (SDLActivity.mSDLThread != null) + pauseRenderingJNI(); } // ------------------------------------------------------------------------ /* SDL manually dlopen main to allow unload after main thread exit. */ diff --git a/lib/graphics_engine/include/ge_vulkan_driver.hpp b/lib/graphics_engine/include/ge_vulkan_driver.hpp index 3d40aae98..af83f839a 100644 --- a/lib/graphics_engine/include/ge_vulkan_driver.hpp +++ b/lib/graphics_engine/include/ge_vulkan_driver.hpp @@ -318,6 +318,8 @@ namespace GE void getRotatedViewport(VkViewport* vp); const core::matrix4& getPreRotationMatrix() { return m_pre_rotation_matrix; } + virtual void pauseRendering(); + virtual void unpauseRendering(); private: struct SwapChainSupportDetails { @@ -383,7 +385,8 @@ namespace GE { for (VkFramebuffer& framebuffer : swap_chain_framebuffers) vkDestroyFramebuffer(device, framebuffer, NULL); - vkDestroyRenderPass(device, render_pass, NULL); + if (render_pass != VK_NULL_HANDLE) + vkDestroyRenderPass(device, render_pass, NULL); for (unsigned i = 0; i < GVS_COUNT; i++) vkDestroySampler(device, samplers[i], NULL); if (!command_buffers.empty()) @@ -438,6 +441,8 @@ namespace GE video::ITexture* m_white_texture; video::ITexture* m_transparent_texture; + SDL_Window* m_window; + void createInstance(SDL_Window* window); void findPhysicalDevice(); bool checkDeviceExtensions(VkPhysicalDevice device); @@ -458,6 +463,8 @@ namespace GE void initPreRotationMatrix(); std::string getVulkanVersionString() const; std::string getDriverVersionString() const; + void destroySwapChainRelated(bool handle_surface); + void createSwapChainRelated(bool handle_surface); }; } diff --git a/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp b/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp index 726991572..ecec49c9a 100644 --- a/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp +++ b/lib/graphics_engine/src/ge_vulkan_2d_renderer.cpp @@ -545,11 +545,17 @@ end_cmd: vkEndCommandBuffer(g_vk->getCurrentCommandBuffer()); end: + clear(); +} // render + +// ---------------------------------------------------------------------------- +void GEVulkan2dRenderer::clear() +{ g_tex_map.clear(); g_tris_queue.clear(); g_tris_index_queue.clear(); g_tris_clip.clear(); -} // render +} // clear // ---------------------------------------------------------------------------- void GEVulkan2dRenderer::addVerticesIndices(irr::video::S3DVertex* vertices, diff --git a/lib/graphics_engine/src/ge_vulkan_2d_renderer.hpp b/lib/graphics_engine/src/ge_vulkan_2d_renderer.hpp index 2eb966aff..5152cfd17 100644 --- a/lib/graphics_engine/src/ge_vulkan_2d_renderer.hpp +++ b/lib/graphics_engine/src/ge_vulkan_2d_renderer.hpp @@ -30,6 +30,8 @@ void createTrisBuffers(); // ---------------------------------------------------------------------------- void render(); // ---------------------------------------------------------------------------- +void clear(); +// ---------------------------------------------------------------------------- void addVerticesIndices(irr::video::S3DVertex* vertices, unsigned vertices_count, uint16_t* indices, unsigned indices_count, diff --git a/lib/graphics_engine/src/ge_vulkan_driver.cpp b/lib/graphics_engine/src/ge_vulkan_driver.cpp index d537c4a93..6f609e53a 100644 --- a/lib/graphics_engine/src/ge_vulkan_driver.cpp +++ b/lib/graphics_engine/src/ge_vulkan_driver.cpp @@ -9,11 +9,11 @@ #ifdef _IRR_COMPILE_WITH_VULKAN_ #include "SDL_vulkan.h" #include +#include #include #include #include #include - #include "../source/Irrlicht/os.h" #if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) @@ -443,6 +443,10 @@ extern "C" PFN_vkVoidFunction loader(void* user_ptr, const char* name) namespace GE { +std::atomic_bool g_device_created(false); +std::atomic_bool g_schedule_pausing_rendering(false); +std::atomic_bool g_paused_rendering(false); + GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, io::IFileSystem* io, SDL_Window* window) : CNullDriver(io, core::dimension2d(0, 0)), @@ -463,6 +467,11 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, m_transparent_texture = NULL; m_pre_rotation_matrix = core::matrix4(core::matrix4::EM4CONST_IDENTITY); + m_window = window; + g_schedule_pausing_rendering.store(false); + g_paused_rendering.store(false); + g_device_created.store(true); + createInstance(window); #if !defined(__APPLE__) || defined(DLOPEN_MOLTENVK) @@ -523,6 +532,7 @@ GEVulkanDriver::GEVulkanDriver(const SIrrlichtCreationParameters& params, // ---------------------------------------------------------------------------- GEVulkanDriver::~GEVulkanDriver() { + g_device_created.store(false); } // ~GEVulkanDriver // ---------------------------------------------------------------------------- @@ -923,22 +933,6 @@ void GEVulkanDriver::createSwapChain() } found_mode: - VkExtent2D image_extent = m_surface_capabilities.currentExtent; - if (m_surface_capabilities.currentExtent.width == std::numeric_limits::max()) - { - VkExtent2D max_extent = m_surface_capabilities.maxImageExtent; - VkExtent2D min_extent = m_surface_capabilities.minImageExtent; - - VkExtent2D actual_extent = - { - std::max( - std::min(ScreenSize.Width, max_extent.width), min_extent.width), - std::max( - std::min(ScreenSize.Height, max_extent.height), min_extent.height) - }; - image_extent = actual_extent; - } - // Try to get triple buffering by default // https://vulkan-tutorial.com/Drawing_a_triangle/Presentation/Swap_chain uint32_t swap_chain_images_count = m_surface_capabilities.minImageCount + 1; @@ -948,13 +942,25 @@ found_mode: swap_chain_images_count = m_surface_capabilities.maxImageCount; } + int w, h = 0; + SDL_Vulkan_GetDrawableSize(m_window, &w, &h); + VkExtent2D max_extent = m_surface_capabilities.maxImageExtent; + VkExtent2D min_extent = m_surface_capabilities.minImageExtent; + VkExtent2D actual_extent = + { + std::max( + std::min((unsigned)w, max_extent.width), min_extent.width), + std::max( + std::min((unsigned)h, max_extent.height), min_extent.height) + }; + VkSwapchainCreateInfoKHR create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; create_info.surface = m_vk->surface; create_info.minImageCount = swap_chain_images_count; create_info.imageFormat = surface_format.format; create_info.imageColorSpace = surface_format.colorSpace; - create_info.imageExtent = image_extent; + create_info.imageExtent = actual_extent; create_info.imageArrayLayers = 1; create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; @@ -979,7 +985,7 @@ found_mode: create_info.clipped = VK_TRUE; create_info.oldSwapchain = VK_NULL_HANDLE; - m_swap_chain_extent = image_extent; + m_swap_chain_extent = actual_extent; ScreenSize.Width = m_swap_chain_extent.width; ScreenSize.Height = m_swap_chain_extent.height; m_clip = getFullscreenClip(); @@ -1325,6 +1331,11 @@ void GEVulkanDriver::endSingleTimeCommands(VkCommandBuffer command_buffer) void GEVulkanDriver::OnResize(const core::dimension2d& size) { CNullDriver::OnResize(size); + if (g_paused_rendering.load() == false) + { + destroySwapChainRelated(false/*handle_surface*/); + createSwapChainRelated(false/*handle_surface*/); + } } // OnResize // ---------------------------------------------------------------------------- @@ -1332,7 +1343,14 @@ bool GEVulkanDriver::beginScene(bool backBuffer, bool zBuffer, SColor color, const SExposedVideoData& videoData, core::rect* sourceRect) { - if (!video::CNullDriver::beginScene(backBuffer, zBuffer, color, videoData, + if (g_schedule_pausing_rendering.load()) + { + pauseRendering(); + g_schedule_pausing_rendering.store(false); + } + + if (g_paused_rendering.load() || + !video::CNullDriver::beginScene(backBuffer, zBuffer, color, videoData, sourceRect)) return false; @@ -1353,6 +1371,12 @@ bool GEVulkanDriver::beginScene(bool backBuffer, bool zBuffer, SColor color, // ---------------------------------------------------------------------------- bool GEVulkanDriver::endScene() { + if (g_paused_rendering.load()) + { + GEVulkan2dRenderer::clear(); + return false; + } + GEVulkan2dRenderer::render(); VkSemaphore wait_semaphores[] = {m_vk->image_available_semaphores[m_current_frame]}; @@ -1827,6 +1851,66 @@ void GEVulkanDriver::initPreRotationMatrix() m_pre_rotation_matrix.setRotationAxisRadians(270.0 * pi / 180., core::vector3df(0.0f, 0.0f, 1.0f)); } // initPreRotationMatrix +// ---------------------------------------------------------------------------- +void GEVulkanDriver::pauseRendering() +{ + if (g_paused_rendering.load() != false) + return; + + destroySwapChainRelated(true/*handle_surface*/); + g_paused_rendering.store(true); +} // pauseRendering + +// ---------------------------------------------------------------------------- +void GEVulkanDriver::unpauseRendering() +{ + if (g_paused_rendering.load() != true) + return; + + createSwapChainRelated(true/*handle_surface*/); + g_paused_rendering.store(false); +} // unpauseRendering + +// ---------------------------------------------------------------------------- +void GEVulkanDriver::destroySwapChainRelated(bool handle_surface) +{ + vkDeviceWaitIdle(m_vk->device); + for (VkFramebuffer& framebuffer : m_vk->swap_chain_framebuffers) + vkDestroyFramebuffer(m_vk->device, framebuffer, NULL); + m_vk->swap_chain_framebuffers.clear(); + if (m_vk->render_pass != VK_NULL_HANDLE) + vkDestroyRenderPass(m_vk->device, m_vk->render_pass, NULL); + m_vk->render_pass = VK_NULL_HANDLE; + for (VkImageView& image_view : m_vk->swap_chain_image_views) + vkDestroyImageView(m_vk->device, image_view, NULL); + m_vk->swap_chain_image_views.clear(); + if (m_vk->swap_chain != VK_NULL_HANDLE) + vkDestroySwapchainKHR(m_vk->device, m_vk->swap_chain, NULL); + m_vk->swap_chain = VK_NULL_HANDLE; + if (handle_surface) + { + if (m_vk->surface != VK_NULL_HANDLE) + vkDestroySurfaceKHR(m_vk->instance, m_vk->surface, NULL); + m_vk->surface = VK_NULL_HANDLE; + } +} // destroySwapChainRelated + +// ---------------------------------------------------------------------------- +void GEVulkanDriver::createSwapChainRelated(bool handle_surface) +{ + vkDeviceWaitIdle(m_vk->device); + if (handle_surface) + { + if (SDL_Vulkan_CreateSurface(m_window, m_vk->instance, &m_vk->surface) == SDL_FALSE) + throw std::runtime_error("SDL_Vulkan_CreateSurface failed"); + } + updateSurfaceInformation(m_physical_device, &m_surface_capabilities, + &m_surface_formats, &m_present_modes); + createSwapChain(); + createRenderPass(); + createFramebuffers(); +} // createSwapChainRelated + } namespace irr @@ -1840,4 +1924,25 @@ namespace video } // createVulkanDriver } } + +#ifdef ANDROID +#include +extern "C" JNIEXPORT void JNICALL pauseRenderingJNI(JNIEnv* env, jclass cls) +{ + using namespace GE; + if (g_device_created.load() == false || g_schedule_pausing_rendering.load() == true) + return; + g_schedule_pausing_rendering.store(true); + if (g_paused_rendering.load() == false) + { + while (true) + { + if (g_device_created.load() == false || g_paused_rendering.load() == true) + break; + } + } +} // pauseRenderingJNI + +#endif + #endif diff --git a/lib/irrlicht/include/IVideoDriver.h b/lib/irrlicht/include/IVideoDriver.h index 976bf70d4..2f23505df 100644 --- a/lib/irrlicht/include/IVideoDriver.h +++ b/lib/irrlicht/include/IVideoDriver.h @@ -1481,6 +1481,8 @@ namespace video virtual u32 getDefaultFramebuffer() const =0; virtual void enableScissorTest(const core::rect& r) {} virtual void disableScissorTest() {} + virtual void pauseRendering() {} + virtual void unpauseRendering() {} }; } // end namespace video diff --git a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp index 8b4ec6dbb..928f97be4 100644 --- a/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp +++ b/lib/irrlicht/source/Irrlicht/CIrrDeviceSDL.cpp @@ -909,6 +909,10 @@ bool CIrrDeviceSDL::run() { WindowHasFocus = true; reset_network_body(); +#ifdef ANDROID + if (VideoDriver) + VideoDriver->unpauseRendering(); +#endif } else if (SDL_event.window.event == SDL_WINDOWEVENT_FOCUS_LOST) { diff --git a/src/main_android.cpp b/src/main_android.cpp index 1862ea34d..38fba5b08 100644 --- a/src/main_android.cpp +++ b/src/main_android.cpp @@ -33,6 +33,7 @@ extern int android_main(int argc, char *argv[]); extern "C" JNIEXPORT void JNICALL debugMsg(JNIEnv* env, jclass cls, jstring msg); extern "C" JNIEXPORT void JNICALL handlePadding(JNIEnv* env, jclass cls, jboolean val); extern "C" JNIEXPORT void JNICALL addDNSSrvRecords(JNIEnv* env, jclass cls, jstring name, jint weight); +extern "C" JNIEXPORT void JNICALL pauseRenderingJNI(JNIEnv* env, jclass cls); extern "C" JNIEXPORT void JNICALL editText2STKEditbox(JNIEnv* env, jclass cls, jint widget_id, jstring text, jint start, jint end, jint composing_start, jint composing_end); extern "C" JNIEXPORT void JNICALL handleActionNext(JNIEnv* env, jclass cls, jint widget_id); @@ -48,7 +49,8 @@ void registering_natives() { { "debugMsg", "(Ljava/lang/String;)V", (void*)&debugMsg }, { "handlePadding", "(Z)V", (void*)&handlePadding }, - { "addDNSSrvRecords", "(Ljava/lang/String;I)V", (void*)&addDNSSrvRecords } + { "addDNSSrvRecords", "(Ljava/lang/String;I)V", (void*)&addDNSSrvRecords }, + { "pauseRenderingJNI", "()V", (void*)&pauseRenderingJNI } }; JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv(); assert(env); diff --git a/src/main_loop.cpp b/src/main_loop.cpp index 3a413f3fe..dffb01bb1 100644 --- a/src/main_loop.cpp +++ b/src/main_loop.cpp @@ -800,12 +800,17 @@ extern "C" int handle_app_event(void* userdata, SDL_Event* event) { if (!main_loop) return 1; + IrrlichtDevice* dev = irr_driver->getDevice(); switch (event->type) { case SDL_APP_WILLENTERBACKGROUND: + if (dev && dev->getVideoDriver()) + dev->getVideoDriver()->pauseRendering(); main_loop->setPaused(true); break; case SDL_APP_DIDENTERFOREGROUND: + if (dev && dev->getVideoDriver()) + dev->getVideoDriver()->unpauseRendering(); main_loop->setPaused(false); break; default: