From 269d1bc9101bcad0a5b61ab9ead4581287d4c091 Mon Sep 17 00:00:00 2001 From: Adrian Cojocaru Date: Fri, 5 Jun 2026 19:59:36 +0300 Subject: [PATCH] Improve Vulkan Android surface lifecycle (#4323) --- .../src/cpp/android_renderer_backend.hpp | 3 ++ .../cpp/android_vulkan_renderer_backend.cpp | 15 +++++++++ .../cpp/android_vulkan_renderer_backend.hpp | 3 ++ .../MapLibreAndroid/src/cpp/map_renderer.cpp | 33 ++++++++++++------- .../MapLibreAndroid/src/cpp/map_renderer.hpp | 4 ++- .../MapLibreVulkanSurfaceView.java | 16 ++------- .../VulkanTextureViewRenderThread.java | 15 ++------- 7 files changed, 52 insertions(+), 37 deletions(-) diff --git a/platform/android/MapLibreAndroid/src/cpp/android_renderer_backend.hpp b/platform/android/MapLibreAndroid/src/cpp/android_renderer_backend.hpp index 3f538c100..64fac445f 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_renderer_backend.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_renderer_backend.hpp @@ -27,6 +27,9 @@ public: // Ensures the current context is not cleaned up when destroyed virtual void markContextLost(); + virtual bool createSurface(ANativeWindow*) { return false; } + virtual void destroySurface() {} + virtual void resizeFramebuffer(int width, int height); virtual PremultipliedImage readFramebuffer(); diff --git a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp index 3101843ba..17ce25b0c 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.cpp @@ -55,6 +55,21 @@ AndroidVulkanRendererBackend::AndroidVulkanRendererBackend(ANativeWindow* window AndroidVulkanRendererBackend::~AndroidVulkanRendererBackend() = default; +bool AndroidVulkanRendererBackend::createSurface(ANativeWindow* window_) { + window = window_; + setResource(std::make_unique(*this)); + + initSurface(); + initSwapchain(); + + return true; +} + +void AndroidVulkanRendererBackend::destroySurface() { + window = nullptr; + setResource(nullptr); +} + std::vector AndroidVulkanRendererBackend::getInstanceExtensions() { auto extensions = mbgl::vulkan::RendererBackend::getInstanceExtensions(); extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME); diff --git a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp index 1d6f988ca..d18ec0fa0 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_vulkan_renderer_backend.hpp @@ -16,6 +16,9 @@ public: ~AndroidVulkanRendererBackend() override; ANativeWindow* getWindow() { return window; } + bool createSurface(ANativeWindow* window) override; + void destroySurface() override; + mbgl::gfx::RendererBackend& getImpl() override { return *this; } std::vector getInstanceExtensions() override; diff --git a/platform/android/MapLibreAndroid/src/cpp/map_renderer.cpp b/platform/android/MapLibreAndroid/src/cpp/map_renderer.cpp index a5696c3b4..2fbdb4343 100644 --- a/platform/android/MapLibreAndroid/src/cpp/map_renderer.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/map_renderer.cpp @@ -249,10 +249,23 @@ void MapRenderer::onSurfaceCreated(JNIEnv& env, const jni::Object>( + ANativeWindow_fromSurface(&env, reinterpret_cast(surface.get())), + [](ANativeWindow* window_) { ANativeWindow_release(window_); }); + } + if (backend) { + if (backend->createSurface(window_.get())) { + window = std::move(window_); + return; + } + + // The android system will have already destroyed the underlying + // GL resources if this is not the first initialization and an + // attempt to clean them up will fail gfx::BackendScope backendGuard{backend->getImpl()}; backend->markContextLost(); } @@ -264,13 +277,7 @@ void MapRenderer::onSurfaceCreated(JNIEnv& env, const jni::Object>( - ANativeWindow_fromSurface(&env, reinterpret_cast(surface.get())), - [](ANativeWindow* window_) { ANativeWindow_release(window_); }); - } + window = std::move(window_); // Create the new backend and renderer backend = AndroidRendererBackend::Create(window.get()); @@ -316,7 +323,11 @@ void MapRenderer::onRendererReset(JNIEnv&) { // needs to be called on GL thread void MapRenderer::onSurfaceDestroyed(JNIEnv&) { - resetRenderer(); + if (backend) { + backend->destroySurface(); + } + + window.reset(); } void MapRenderer::setSwapBehaviorFlush(JNIEnv&, jboolean flush) { diff --git a/platform/android/MapLibreAndroid/src/cpp/map_renderer.hpp b/platform/android/MapLibreAndroid/src/cpp/map_renderer.hpp index c6500359b..76a873057 100644 --- a/platform/android/MapLibreAndroid/src/cpp/map_renderer.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/map_renderer.hpp @@ -147,7 +147,9 @@ private: std::unique_ptr backend; std::unique_ptr renderer; std::unique_ptr> rendererRef; - std::unique_ptr> window; + + using UniqueANativeWindow = std::unique_ptr>; + UniqueANativeWindow window; std::shared_ptr updateParameters; std::mutex updateMutex; diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java index a4382fea5..756223889 100644 --- a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java +++ b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java @@ -35,7 +35,6 @@ public class MapLibreVulkanSurfaceView extends MapLibreSurfaceView { boolean sizeChanged = false; boolean initSurface = false; - boolean destroySurface = false; boolean wantRenderNotification = false; boolean doRenderNotification = false; int w = 0; @@ -64,10 +63,10 @@ public class MapLibreVulkanSurfaceView extends MapLibreSurfaceView { // lost surface if (!hasSurface && !waitingForSurface) { MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get(); - if (view != null) { - destroySurface = true; - graphicsSurfaceCreated = false; + if (view != null && graphicsSurfaceCreated) { + view.renderer.onSurfaceDestroyed(); } + graphicsSurfaceCreated = false; waitingForSurface = true; renderThreadManager.notifyAll(); } @@ -129,15 +128,6 @@ public class MapLibreVulkanSurfaceView extends MapLibreSurfaceView { continue; } - if (destroySurface) { - MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get(); - if (view != null) { - view.renderer.onSurfaceDestroyed(); - destroySurface = false; - continue; - } - } - if (initSurface) { MapLibreVulkanSurfaceView view = mSurfaceViewWeakRef.get(); if (view != null) { diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java index 061dd39d6..9ee245563 100644 --- a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java +++ b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java @@ -34,7 +34,6 @@ public class VulkanTextureViewRenderThread extends TextureViewRenderThread { while (true) { Runnable event = null; boolean createSurface = false; - boolean destroySurface = false; boolean sizeChanged = false; int w = -1; int h = -1; @@ -54,13 +53,15 @@ public class VulkanTextureViewRenderThread extends TextureViewRenderThread { } if (this.destroySurface) { - destroySurface = true; if (surface != null) { + mapRenderer.onSurfaceDestroyed(); surface.release(); surface = null; } + this.hasNativeSurface = false; this.destroySurface = false; + lock.notifyAll(); break; } @@ -108,16 +109,6 @@ public class VulkanTextureViewRenderThread extends TextureViewRenderThread { continue; } - if (destroySurface) { - mapRenderer.onSurfaceDestroyed(); - synchronized (lock) { - this.hasNativeSurface = false; - lock.notifyAll(); - } - destroySurface = false; - continue; - } - // If the surface size has changed inform the map renderer. if (sizeChanged) { mapRenderer.onSurfaceChanged(w, h);