From aae867b6912f6c211df36ae5eb3d2b99d55aa0ee Mon Sep 17 00:00:00 2001 From: Venya Vynohradov Date: Tue, 12 May 2026 19:35:52 +0300 Subject: [PATCH] Add new multiRender flavor to switch between vulkan and opengl libraries --- CMakeLists.txt | 80 +++++++++++++++++++ cmake/validate-backend-options.cmake | 7 +- .../android/MapLibreAndroid/build.gradle.kts | 24 +++++- .../MapLibreAndroid/src/cpp/CMakeLists.txt | 10 +++ .../maps/renderer/MapRendererFactory.java | 43 +++++----- .../src/multiBackend/AndroidManifest.xml | 8 ++ .../org/maplibre/android/RenderingEngine.java | 36 +++++++++ .../maps/renderer/RendererStrategy.java | 41 ++++++++++ .../loader/LibraryLoaderProviderImpl.java | 31 +++++++ .../org/maplibre/android/RenderingEngine.java | 50 ++++++++++++ .../maps/renderer/RendererStrategy.java | 32 ++++++++ .../loader/LibraryLoaderProviderImpl.java | 12 +-- .../maps/renderer/OpenGLRendererStrategy.java | 42 ++++++++++ .../maps/renderer/egl/EGLConfigChooser.java | 0 .../maps/renderer/egl/EGLContextFactory.java | 0 .../maps/renderer/egl/EGLLogWrapper.java | 0 .../renderer/egl/EGLWindowSurfaceFactory.java | 0 .../maps/renderer/egl/package-info.java | 0 .../surfaceview/GLSurfaceViewMapRenderer.java | 0 .../surfaceview/MapLibreGLSurfaceView.java | 0 .../GLTextureViewRenderThread.java | 0 .../maps/renderer/VulkanRendererStrategy.java | 42 ++++++++++ .../MapLibreVulkanSurfaceView.java | 0 .../VulkanSurfaceViewMapRenderer.java | 0 .../VulkanTextureViewRenderThread.java | 0 .../org/maplibre/android/RenderingEngine.java | 32 ++++++++ .../maps/renderer/MapRendererFactory.java | 49 ------------ .../maps/renderer/RendererStrategy.java | 32 ++++++++ .../loader/LibraryLoaderProviderImpl.java | 23 ++++++ 29 files changed, 508 insertions(+), 86 deletions(-) rename platform/android/MapLibreAndroid/src/{opengl => main}/java/org/maplibre/android/maps/renderer/MapRendererFactory.java (50%) create mode 100644 platform/android/MapLibreAndroid/src/multiBackend/AndroidManifest.xml create mode 100644 platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/RenderingEngine.java create mode 100644 platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/maps/renderer/RendererStrategy.java create mode 100644 platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java create mode 100644 platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/RenderingEngine.java create mode 100644 platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/RendererStrategy.java rename platform/android/MapLibreAndroid/src/{main => opengl}/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java (64%) create mode 100644 platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/OpenGLRendererStrategy.java rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/egl/EGLConfigChooser.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/egl/EGLLogWrapper.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/egl/EGLWindowSurfaceFactory.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/egl/package-info.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreGLSurfaceView.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/opengl/java/org/maplibre/android/maps/renderer/textureview/GLTextureViewRenderThread.java (100%) create mode 100644 platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/VulkanRendererStrategy.java rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/VulkanSurfaceViewMapRenderer.java (100%) rename platform/android/MapLibreAndroid/src/{ => sharedRenderer}/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java (100%) create mode 100644 platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/RenderingEngine.java delete mode 100644 platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/MapRendererFactory.java create mode 100644 platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/RendererStrategy.java create mode 100644 platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a680480d448..33fd67ddf314 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ option(MLN_USE_RUST "Use components in Rust" OFF) option(MLN_TEXT_SHAPING_HARFBUZZ "Use haffbuzz to shape complex text" ON) option(MLN_CREATE_AUTORELEASEPOOL "Create autoreleasepool in render loop" OFF) option(MLN_CREATE_AMALGAMATION "Create static amalgamation of core (requires amerge)" OFF) +option(MLN_ANDROID_MULTI_BACKEND "Android only: build both OpenGL and Vulkan versions" OFF) include(cmake/validate-backend-options.cmake) include(cmake/clang-tidy.cmake) @@ -35,6 +36,85 @@ endif() project("MapLibre Native" LANGUAGES CXX C) +if(MLN_WITH_MULTI_BACKEND) + if(NOT ANDROID) + message(FATAL_ERROR + "MLN_WITH_MULTI_BACKEND is only supported on Android. " + "On desktop, build mbgl-glfw with a single -DMLN_WITH_OPENGL=ON or -DMLN_WITH_VULKAN=ON.") + endif() + + # Android: invoke the Android NDK CMakeLists twice via ExternalProject_Add + # (once per backend) and surface the results to AGP as two distinct + # add_library(SHARED) targets so both .so files end up in the AAR. + # Reached via add_subdirectory(...) from the Android src/cpp CMakeLists. + include(ExternalProject) + + set(_multi_android_source_dir ${PROJECT_SOURCE_DIR}/platform/android/MapLibreAndroid/src/cpp) + + set(_multi_forward_args + -DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE} + -DANDROID_ABI=${ANDROID_ABI} + -DANDROID_PLATFORM=${ANDROID_PLATFORM} + -DANDROID_NDK=${ANDROID_NDK} + -DANDROID_TOOLCHAIN=${ANDROID_TOOLCHAIN} + -DANDROID_STL=${ANDROID_STL} + -DANDROID_CPP_FEATURES=${ANDROID_CPP_FEATURES} + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DMLN_ANDROID_MULTI_BACKEND=OFF) + + set(_multi_opengl_dir ${CMAKE_BINARY_DIR}/multi-opengl) + set(_multi_vulkan_dir ${CMAKE_BINARY_DIR}/multi-vulkan) + + ExternalProject_Add(maplibre-opengl-build + SOURCE_DIR ${_multi_android_source_dir} + BINARY_DIR ${_multi_opengl_dir} + CMAKE_ARGS + ${_multi_forward_args} + -DMLN_WITH_OPENGL=ON + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${_multi_opengl_dir}/lib + BUILD_COMMAND ${CMAKE_COMMAND} --build --target maplibre + INSTALL_COMMAND "" + BUILD_ALWAYS 1) + + ExternalProject_Add(maplibre-vulkan-build + SOURCE_DIR ${_multi_android_source_dir} + BINARY_DIR ${_multi_vulkan_dir} + CMAKE_ARGS + ${_multi_forward_args} + -DMLN_WITH_VULKAN=ON + -DCMAKE_LIBRARY_OUTPUT_DIRECTORY=${_multi_vulkan_dir}/lib + BUILD_COMMAND ${CMAKE_COMMAND} --build --target maplibre + INSTALL_COMMAND "" + BUILD_ALWAYS 1) + + set(_multi_stub ${CMAKE_BINARY_DIR}/maplibre_multi_stub.cpp) + file(WRITE ${_multi_stub} + "extern \"C\" void maplibre_multi_backend_stub() {}\n") + + add_library(maplibre-opengl SHARED ${_multi_stub}) + # Produce libmaplibre.so (not libmaplibre-opengl.so) so the OpenGL output + # matches the single-backend OpenGL AAR's library name. + set_target_properties(maplibre-opengl PROPERTIES OUTPUT_NAME maplibre) + add_dependencies(maplibre-opengl maplibre-opengl-build) + add_custom_command(TARGET maplibre-opengl POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${_multi_opengl_dir}/lib/libmaplibre.so + $ + VERBATIM) + + add_library(maplibre-vulkan SHARED ${_multi_stub}) + add_dependencies(maplibre-vulkan maplibre-vulkan-build) + add_custom_command(TARGET maplibre-vulkan POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy + ${_multi_vulkan_dir}/lib/libmaplibre.so + $ + VERBATIM) + + install(TARGETS maplibre-opengl maplibre-vulkan LIBRARY DESTINATION lib) + + return() +endif() + set_property(GLOBAL PROPERTY USE_FOLDERS ON) set_property(GLOBAL PROPERTY PREDEFINED_TARGETS_FOLDER MapLibre) diff --git a/cmake/validate-backend-options.cmake b/cmake/validate-backend-options.cmake index d439eced89f7..268da8a2f469 100644 --- a/cmake/validate-backend-options.cmake +++ b/cmake/validate-backend-options.cmake @@ -19,13 +19,16 @@ endif() if (MLN_WITH_WEBGPU) math(EXPR backend_count "${backend_count} + 1") endif() +if (MLN_ANDROID_MULTI_BACKEND) + math(EXPR backend_count "${backend_count} + 1") +endif() if (backend_count EQUAL 0) message(FATAL_ERROR "You need to set a rendering backend. " - "Set exactly one of: MLN_WITH_OPENGL, MLN_WITH_METAL, MLN_WITH_VULKAN, or MLN_WITH_WEBGPU.") + "Set exactly one of: MLN_WITH_OPENGL, MLN_WITH_METAL, MLN_WITH_VULKAN, MLN_WITH_WEBGPU, or MLN_ANDROID_MULTI_BACKEND.") elseif (backend_count GREATER 1) message(FATAL_ERROR "Multiple rendering backends selected. " - "Please enable only one of: MLN_WITH_OPENGL, MLN_WITH_METAL, MLN_WITH_VULKAN, or MLN_WITH_WEBGPU.") + "Please enable only one of: MLN_WITH_OPENGL, MLN_WITH_METAL, MLN_WITH_VULKAN, MLN_WITH_WEBGPU, or MLN_ANDROID_MULTI_BACKEND.") endif() diff --git a/platform/android/MapLibreAndroid/build.gradle.kts b/platform/android/MapLibreAndroid/build.gradle.kts index d5cb9b700801..bd1abdbc231e 100644 --- a/platform/android/MapLibreAndroid/build.gradle.kts +++ b/platform/android/MapLibreAndroid/build.gradle.kts @@ -108,18 +108,38 @@ android { } } } + create("multiBackend") { + dimension = "renderer" + externalNativeBuild { + cmake { + arguments("-DMLN_ANDROID_MULTI_BACKEND=ON") + targets("maplibre-opengl", "maplibre-vulkan") + } + } + } } sourceSets { getByName("opengl") { - java.srcDirs("src/opengl/java/") + java.srcDirs("src/opengl/java/", "src/sharedRenderer/opengl/java/") + } + getByName("vulkan") { + java.srcDirs("src/vulkan/java/", "src/sharedRenderer/vulkan/java/") } listOf("webgpuDawn", "webgpuWgpu").forEach { getByName(it) { - java.srcDirs("src/vulkan/java") + java.srcDirs("src/vulkan/java/", "src/sharedRenderer/vulkan/java/") manifest.srcFile("src/vulkan/AndroidManifest.xml") } } + getByName("multiBackend") { + java.srcDirs( + "src/multiBackend/java/", + "src/sharedRenderer/opengl/java/", + "src/sharedRenderer/vulkan/java/" + ) + manifest.srcFile("src/multiBackend/AndroidManifest.xml") + } } // Build native libraries diff --git a/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt b/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt index b78316ead435..b3bfef32d71a 100644 --- a/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt +++ b/platform/android/MapLibreAndroid/src/cpp/CMakeLists.txt @@ -2,9 +2,19 @@ cmake_minimum_required(VERSION 3.10) project(MapLibreAndroid) +# Always invoke the top-level CMakeLists. When MLN_ANDROID_MULTI_BACKEND=ON +# (set by the Gradle multiBackend flavor via -D), the top-level handles the +# dual-backend ExternalProject_Add setup, declares maplibre-opengl (output +# libmaplibre.so) and maplibre-vulkan (output libmaplibre-vulkan.so) SHARED +# targets, and returns from its scope. The matching `if` below then skips +# this file's normal single-backend maplibre target. add_subdirectory(../../../../../ ../../../../../${ANDROID_ABI}) +if(MLN_ANDROID_MULTI_BACKEND) + return() +endif() + set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/MapRendererFactory.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRendererFactory.java similarity index 50% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/MapRendererFactory.java rename to platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRendererFactory.java index 64320ecd3f07..fecbeddb8055 100644 --- a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/MapRendererFactory.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRendererFactory.java @@ -7,18 +7,25 @@ import androidx.annotation.Keep; import androidx.annotation.NonNull; -import org.maplibre.android.maps.renderer.surfaceview.GLSurfaceViewMapRenderer; -import org.maplibre.android.maps.renderer.surfaceview.MapLibreGLSurfaceView; import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; -import org.maplibre.android.maps.renderer.textureview.GLTextureViewRenderThread; import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; +/** + * Shared factory used by MapRenderer.create(). The shape of renderer construction + * (anonymous subclass wiring of initCallback, render-thread attachment) lives here; + * the backend-specific concrete-type instantiation is delegated to the + * flavor-provided {@link RendererStrategy}. + */ @Keep -public class MapRendererFactory { - public static TextureViewMapRenderer newTextureViewMapRenderer(@NonNull Context context, TextureView textureView, - String localFontFamily, boolean translucentSurface, - Runnable initCallback) { +public final class MapRendererFactory { + + private MapRendererFactory() {} + public static TextureViewMapRenderer newTextureViewMapRenderer(@NonNull Context context, + TextureView textureView, + String localFontFamily, + boolean translucentSurface, + Runnable initCallback) { TextureViewMapRenderer mapRenderer = new TextureViewMapRenderer(context, textureView, localFontFamily, translucentSurface) { @Override @@ -27,23 +34,15 @@ protected void onSurfaceCreated(Surface surface) { super.onSurfaceCreated(surface); } }; - - mapRenderer.setRenderThread(new GLTextureViewRenderThread(textureView, mapRenderer)); + RendererStrategy.attachTextureRenderThread(textureView, mapRenderer); return mapRenderer; } - public static SurfaceViewMapRenderer newSurfaceViewMapRenderer(@NonNull Context context, String localFontFamily, - boolean renderSurfaceOnTop, Runnable initCallback) { - - MapLibreGLSurfaceView surfaceView = new MapLibreGLSurfaceView(context); - surfaceView.setZOrderMediaOverlay(renderSurfaceOnTop); - - return new GLSurfaceViewMapRenderer(context, surfaceView, localFontFamily) { - @Override - public void onSurfaceCreated(Surface surface) { - initCallback.run(); - super.onSurfaceCreated(surface); - } - }; + public static SurfaceViewMapRenderer newSurfaceViewMapRenderer(@NonNull Context context, + String localFontFamily, + boolean renderSurfaceOnTop, + Runnable initCallback) { + return RendererStrategy.createSurfaceViewRenderer( + context, localFontFamily, renderSurfaceOnTop, initCallback); } } diff --git a/platform/android/MapLibreAndroid/src/multiBackend/AndroidManifest.xml b/platform/android/MapLibreAndroid/src/multiBackend/AndroidManifest.xml new file mode 100644 index 000000000000..5322b8f37ed0 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/multiBackend/AndroidManifest.xml @@ -0,0 +1,8 @@ + + + + diff --git a/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/RenderingEngine.java b/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/RenderingEngine.java new file mode 100644 index 000000000000..74b5b74da8e3 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/RenderingEngine.java @@ -0,0 +1,36 @@ +package org.maplibre.android; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; + +/** + * multiBackend flavor: the rendering engine is selectable at runtime. + * + *

Call {@link #setCurrentType(Type)} before the first call to + * {@link MapLibre#getInstance(android.content.Context)} (or any other path that + * triggers native library loading). Once the native library is loaded the + * selection is effectively locked for the process lifetime — subsequent + * {@code setCurrentType} calls update this field, but the loaded .so does + * not change.

+ */ +@Keep +public final class RenderingEngine { + + public enum Type { + OPENGL, + VULKAN + } + + private static volatile Type currentType = Type.OPENGL; + + private RenderingEngine() {} + + @NonNull + public static Type getCurrentType() { + return currentType; + } + + public static void setCurrentType(@NonNull Type type) { + currentType = type; + } +} diff --git a/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/maps/renderer/RendererStrategy.java b/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/maps/renderer/RendererStrategy.java new file mode 100644 index 000000000000..884dde3fa33e --- /dev/null +++ b/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/maps/renderer/RendererStrategy.java @@ -0,0 +1,41 @@ +package org.maplibre.android.maps.renderer; + +import android.content.Context; +import android.view.TextureView; + +import androidx.annotation.NonNull; + +import org.maplibre.android.RenderingEngine; +import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; + +/** + * multiBackend flavor glue. Dispatches to {@link OpenGLRendererStrategy} or + * {@link VulkanRendererStrategy} based on {@link RenderingEngine#getCurrentType()} + * at the moment the renderer is constructed. + */ +final class RendererStrategy { + + private RendererStrategy() {} + + static void attachTextureRenderThread(@NonNull TextureView textureView, + @NonNull TextureViewMapRenderer renderer) { + if (RenderingEngine.getCurrentType() == RenderingEngine.Type.VULKAN) { + VulkanRendererStrategy.attachTextureRenderThread(textureView, renderer); + } else { + OpenGLRendererStrategy.attachTextureRenderThread(textureView, renderer); + } + } + + static SurfaceViewMapRenderer createSurfaceViewRenderer(@NonNull Context context, + String localFontFamily, + boolean renderSurfaceOnTop, + Runnable initCallback) { + if (RenderingEngine.getCurrentType() == RenderingEngine.Type.VULKAN) { + return VulkanRendererStrategy.createSurfaceViewRenderer( + context, localFontFamily, renderSurfaceOnTop, initCallback); + } + return OpenGLRendererStrategy.createSurfaceViewRenderer( + context, localFontFamily, renderSurfaceOnTop, initCallback); + } +} diff --git a/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java b/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java new file mode 100644 index 000000000000..abad206d4073 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/multiBackend/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java @@ -0,0 +1,31 @@ +package org.maplibre.android.module.loader; + +import org.maplibre.android.LibraryLoader; +import org.maplibre.android.LibraryLoaderProvider; +import org.maplibre.android.RenderingEngine; + +/** + * multiBackend flavor: when asked to load "maplibre", routes to either + * libmaplibre.so (OpenGL, the default name) or libmaplibre-vulkan.so based on + * {@link RenderingEngine#getCurrentType()}. Any other library name is loaded + * verbatim. + */ +public class LibraryLoaderProviderImpl implements LibraryLoaderProvider { + + @Override + public LibraryLoader getDefaultLibraryLoader() { + return new MultiBackendLibraryLoader(); + } + + private static class MultiBackendLibraryLoader extends LibraryLoader { + @Override + public void load(String name) { + if ("maplibre".equals(name) + && RenderingEngine.getCurrentType() == RenderingEngine.Type.VULKAN) { + System.loadLibrary("maplibre-vulkan"); + } else { + System.loadLibrary(name); + } + } + } +} diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/RenderingEngine.java b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/RenderingEngine.java new file mode 100644 index 000000000000..506429c73de8 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/RenderingEngine.java @@ -0,0 +1,50 @@ +package org.maplibre.android; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; + +/** + * The rendering engine baked into this build of the SDK. + * + *

In single-backend flavors (opengl, vulkan, ...) the engine is fixed at build + * time and {@link #setCurrentType(Type)} only accepts the matching value. In the + * multiBackend flavor this class is mutable and {@code setCurrentType(...)} must + * be called before {@link MapLibre#getInstance(android.content.Context)} to pick + * a backend for the process lifetime.

+ */ +@Keep +public final class RenderingEngine { + + /** Supported rendering backends. WebGPU flavors report the closest match. */ + public enum Type { + OPENGL, + VULKAN + } + + private RenderingEngine() {} + + /** + * @return the rendering backend used by the currently loaded library. + */ + @NonNull + public static Type getCurrentType() { + return Type.OPENGL; + } + + /** + * Single-backend flavor: this is a no-op when {@code type} matches the + * compiled-in backend, and throws otherwise. Use the multiBackend flavor of + * the SDK if you need to switch backends at runtime. + * + * @param type the desired backend + * @throws UnsupportedOperationException if {@code type} differs from the + * compiled-in backend + */ + public static void setCurrentType(@NonNull Type type) { + if (type != Type.OPENGL) { + throw new UnsupportedOperationException( + "This MapLibre Android build supports only " + Type.OPENGL + + ". Use the multiBackend flavor to switch backends at runtime."); + } + } +} diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/RendererStrategy.java b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/RendererStrategy.java new file mode 100644 index 000000000000..ddd02dd1022b --- /dev/null +++ b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/RendererStrategy.java @@ -0,0 +1,32 @@ +package org.maplibre.android.maps.renderer; + +import android.content.Context; +import android.view.TextureView; + +import androidx.annotation.NonNull; + +import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; + +/** + * OpenGL flavor glue. Delegates straight to {@link OpenGLRendererStrategy}. + * Exists only so the shared {@link MapRendererFactory} in main can call + * {@code RendererStrategy.X(...)} without a per-flavor import. + */ +final class RendererStrategy { + + private RendererStrategy() {} + + static void attachTextureRenderThread(@NonNull TextureView textureView, + @NonNull TextureViewMapRenderer renderer) { + OpenGLRendererStrategy.attachTextureRenderThread(textureView, renderer); + } + + static SurfaceViewMapRenderer createSurfaceViewRenderer(@NonNull Context context, + String localFontFamily, + boolean renderSurfaceOnTop, + Runnable initCallback) { + return OpenGLRendererStrategy.createSurfaceViewRenderer( + context, localFontFamily, renderSurfaceOnTop, initCallback); + } +} diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java similarity index 64% rename from platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java rename to platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java index 02ea2b843b55..d6a340c95b56 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java +++ b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java @@ -4,25 +4,15 @@ import org.maplibre.android.LibraryLoaderProvider; /** - * Concrete implementation of a native library loader. - *

- *

+ * OpenGL flavor: loads the single-backend libmaplibre.so via System.loadLibrary. */ public class LibraryLoaderProviderImpl implements LibraryLoaderProvider { - /** - * Creates and returns a the default Library Loader. - * - * @return the default library loader - */ @Override public LibraryLoader getDefaultLibraryLoader() { return new SystemLibraryLoader(); } - /** - * Concrete implementation of a LibraryLoader using System.loadLibrary. - */ private static class SystemLibraryLoader extends LibraryLoader { @Override public void load(String name) { diff --git a/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/OpenGLRendererStrategy.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/OpenGLRendererStrategy.java new file mode 100644 index 000000000000..1b9b167b5699 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/OpenGLRendererStrategy.java @@ -0,0 +1,42 @@ +package org.maplibre.android.maps.renderer; + +import android.content.Context; +import android.view.Surface; +import android.view.TextureView; + +import androidx.annotation.NonNull; + +import org.maplibre.android.maps.renderer.surfaceview.GLSurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.surfaceview.MapLibreGLSurfaceView; +import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.textureview.GLTextureViewRenderThread; +import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; + +/** + * OpenGL concrete impl behind {@link MapRendererFactory}. Lives alongside the + * other GL renderer helpers in src/sharedRenderer/opengl/. + */ +final class OpenGLRendererStrategy { + + private OpenGLRendererStrategy() {} + + static void attachTextureRenderThread(@NonNull TextureView textureView, + @NonNull TextureViewMapRenderer renderer) { + renderer.setRenderThread(new GLTextureViewRenderThread(textureView, renderer)); + } + + static SurfaceViewMapRenderer createSurfaceViewRenderer(@NonNull Context context, + String localFontFamily, + boolean renderSurfaceOnTop, + Runnable initCallback) { + MapLibreGLSurfaceView surfaceView = new MapLibreGLSurfaceView(context); + surfaceView.setZOrderMediaOverlay(renderSurfaceOnTop); + return new GLSurfaceViewMapRenderer(context, surfaceView, localFontFamily) { + @Override + public void onSurfaceCreated(Surface surface) { + initCallback.run(); + super.onSurfaceCreated(surface); + } + }; + } +} diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLConfigChooser.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLConfigChooser.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLConfigChooser.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLConfigChooser.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLLogWrapper.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLLogWrapper.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLLogWrapper.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLLogWrapper.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLWindowSurfaceFactory.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLWindowSurfaceFactory.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLWindowSurfaceFactory.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/EGLWindowSurfaceFactory.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/package-info.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/package-info.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/package-info.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/egl/package-info.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreGLSurfaceView.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreGLSurfaceView.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreGLSurfaceView.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreGLSurfaceView.java diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/textureview/GLTextureViewRenderThread.java b/platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/textureview/GLTextureViewRenderThread.java similarity index 100% rename from platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/textureview/GLTextureViewRenderThread.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/opengl/java/org/maplibre/android/maps/renderer/textureview/GLTextureViewRenderThread.java diff --git a/platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/VulkanRendererStrategy.java b/platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/VulkanRendererStrategy.java new file mode 100644 index 000000000000..8d9c8c58f260 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/VulkanRendererStrategy.java @@ -0,0 +1,42 @@ +package org.maplibre.android.maps.renderer; + +import android.content.Context; +import android.view.Surface; +import android.view.TextureView; + +import androidx.annotation.NonNull; + +import org.maplibre.android.maps.renderer.surfaceview.MapLibreVulkanSurfaceView; +import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.surfaceview.VulkanSurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; +import org.maplibre.android.maps.renderer.textureview.VulkanTextureViewRenderThread; + +/** + * Vulkan concrete impl behind {@link MapRendererFactory}. Lives alongside the + * other Vulkan renderer helpers in src/sharedRenderer/vulkan/. + */ +final class VulkanRendererStrategy { + + private VulkanRendererStrategy() {} + + static void attachTextureRenderThread(@NonNull TextureView textureView, + @NonNull TextureViewMapRenderer renderer) { + renderer.setRenderThread(new VulkanTextureViewRenderThread(textureView, renderer)); + } + + static SurfaceViewMapRenderer createSurfaceViewRenderer(@NonNull Context context, + String localFontFamily, + boolean renderSurfaceOnTop, + Runnable initCallback) { + MapLibreVulkanSurfaceView surfaceView = new MapLibreVulkanSurfaceView(context); + surfaceView.setZOrderMediaOverlay(renderSurfaceOnTop); + return new VulkanSurfaceViewMapRenderer(context, surfaceView, localFontFamily) { + @Override + public void onSurfaceCreated(Surface surface) { + initCallback.run(); + super.onSurfaceCreated(surface); + } + }; + } +} diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java b/platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java similarity index 100% rename from platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreVulkanSurfaceView.java diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/VulkanSurfaceViewMapRenderer.java b/platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/VulkanSurfaceViewMapRenderer.java similarity index 100% rename from platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/VulkanSurfaceViewMapRenderer.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/surfaceview/VulkanSurfaceViewMapRenderer.java diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java b/platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java similarity index 100% rename from platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java rename to platform/android/MapLibreAndroid/src/sharedRenderer/vulkan/java/org/maplibre/android/maps/renderer/textureview/VulkanTextureViewRenderThread.java diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/RenderingEngine.java b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/RenderingEngine.java new file mode 100644 index 000000000000..15dc23dcac0f --- /dev/null +++ b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/RenderingEngine.java @@ -0,0 +1,32 @@ +package org.maplibre.android; + +import androidx.annotation.Keep; +import androidx.annotation.NonNull; + +/** + * Vulkan (and WebGPU) flavor: the rendering engine is fixed at build time. + * See the opengl flavor's javadoc for the multiBackend contrast. + */ +@Keep +public final class RenderingEngine { + + public enum Type { + OPENGL, + VULKAN + } + + private RenderingEngine() {} + + @NonNull + public static Type getCurrentType() { + return Type.VULKAN; + } + + public static void setCurrentType(@NonNull Type type) { + if (type != Type.VULKAN) { + throw new UnsupportedOperationException( + "This MapLibre Android build supports only " + Type.VULKAN + + ". Use the multiBackend flavor to switch backends at runtime."); + } + } +} diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/MapRendererFactory.java b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/MapRendererFactory.java deleted file mode 100644 index 2ac787831942..000000000000 --- a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/MapRendererFactory.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.maplibre.android.maps.renderer; - -import android.content.Context; -import android.view.Surface; -import android.view.TextureView; - -import androidx.annotation.Keep; -import androidx.annotation.NonNull; - -import org.maplibre.android.maps.renderer.surfaceview.MapLibreVulkanSurfaceView; -import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; -import org.maplibre.android.maps.renderer.surfaceview.VulkanSurfaceViewMapRenderer; -import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; -import org.maplibre.android.maps.renderer.textureview.VulkanTextureViewRenderThread; - -@Keep -public class MapRendererFactory { - public static TextureViewMapRenderer newTextureViewMapRenderer(@NonNull Context context, TextureView textureView, - String localFontFamily, boolean translucentSurface, - Runnable initCallback) { - - TextureViewMapRenderer mapRenderer = new TextureViewMapRenderer(context, textureView, - localFontFamily, translucentSurface) { - @Override - protected void onSurfaceCreated(Surface surface) { - initCallback.run(); - super.onSurfaceCreated(surface); - } - }; - - mapRenderer.setRenderThread(new VulkanTextureViewRenderThread(textureView, mapRenderer)); - return mapRenderer; - } - - public static SurfaceViewMapRenderer newSurfaceViewMapRenderer(@NonNull Context context, String localFontFamily, - boolean renderSurfaceOnTop, Runnable initCallback) { - - MapLibreVulkanSurfaceView surfaceView = new MapLibreVulkanSurfaceView(context); - surfaceView.setZOrderMediaOverlay(renderSurfaceOnTop); - - return new VulkanSurfaceViewMapRenderer(context, surfaceView, localFontFamily) { - @Override - public void onSurfaceCreated(Surface surface) { - initCallback.run(); - super.onSurfaceCreated(surface); - } - }; - } -} diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/RendererStrategy.java b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/RendererStrategy.java new file mode 100644 index 000000000000..4d8213f77af7 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/maps/renderer/RendererStrategy.java @@ -0,0 +1,32 @@ +package org.maplibre.android.maps.renderer; + +import android.content.Context; +import android.view.TextureView; + +import androidx.annotation.NonNull; + +import org.maplibre.android.maps.renderer.surfaceview.SurfaceViewMapRenderer; +import org.maplibre.android.maps.renderer.textureview.TextureViewMapRenderer; + +/** + * Vulkan flavor glue. Delegates straight to {@link VulkanRendererStrategy}. + * Exists only so the shared {@link MapRendererFactory} in main can call + * {@code RendererStrategy.X(...)} without a per-flavor import. + */ +final class RendererStrategy { + + private RendererStrategy() {} + + static void attachTextureRenderThread(@NonNull TextureView textureView, + @NonNull TextureViewMapRenderer renderer) { + VulkanRendererStrategy.attachTextureRenderThread(textureView, renderer); + } + + static SurfaceViewMapRenderer createSurfaceViewRenderer(@NonNull Context context, + String localFontFamily, + boolean renderSurfaceOnTop, + Runnable initCallback) { + return VulkanRendererStrategy.createSurfaceViewRenderer( + context, localFontFamily, renderSurfaceOnTop, initCallback); + } +} diff --git a/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java new file mode 100644 index 000000000000..c6da8917ded7 --- /dev/null +++ b/platform/android/MapLibreAndroid/src/vulkan/java/org/maplibre/android/module/loader/LibraryLoaderProviderImpl.java @@ -0,0 +1,23 @@ +package org.maplibre.android.module.loader; + +import org.maplibre.android.LibraryLoader; +import org.maplibre.android.LibraryLoaderProvider; + +/** + * Vulkan and WebGPU flavors: loads the single-backend libmaplibre.so via + * System.loadLibrary. + */ +public class LibraryLoaderProviderImpl implements LibraryLoaderProvider { + + @Override + public LibraryLoader getDefaultLibraryLoader() { + return new SystemLibraryLoader(); + } + + private static class SystemLibraryLoader extends LibraryLoader { + @Override + public void load(String name) { + System.loadLibrary(name); + } + } +}