Add multiBackend Android flavor for runtime OpenGL/Vulkan switching (#4288)
This commit is contained in:
committed by
GitHub
parent
ccb9af119e
commit
b0388d186d
@@ -0,0 +1,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<!-- Vulkan support is optional in the multiBackend flavor; the app falls back
|
||||
to OpenGL when Vulkan is unavailable. -->
|
||||
<uses-feature
|
||||
android:name="android.hardware.vulkan.version"
|
||||
android:version="0x400003"
|
||||
android:required="false" />
|
||||
</manifest>
|
||||
@@ -0,0 +1,70 @@
|
||||
package org.maplibre.android;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* multiBackend flavor: the rendering engine is selectable at runtime.
|
||||
*
|
||||
* <p>Call {@link #setCurrentType(Type)} before the first call to
|
||||
* {@link MapLibre#getInstance(android.content.Context)} (or any other path that
|
||||
* triggers native library loading). {@link #setCurrentType(Type)} throws once
|
||||
* {@link MapLibre#getInstance(android.content.Context)} has been called, since the
|
||||
* native library is loaded by then and the selection can no longer take effect for
|
||||
* the current process.</p>
|
||||
*/
|
||||
@Keep
|
||||
public final class RenderingEngine {
|
||||
|
||||
/**
|
||||
* Matches the android.hardware.vulkan.version `uses-feature` declared for this flavor.
|
||||
*/
|
||||
private static final int REQUIRED_VULKAN_VERSION = 0x400003;
|
||||
|
||||
public enum Type {
|
||||
OPENGL,
|
||||
VULKAN
|
||||
}
|
||||
|
||||
private static volatile Type currentType = Type.VULKAN;
|
||||
|
||||
private RenderingEngine() {}
|
||||
|
||||
@NonNull
|
||||
public static Type getCurrentType() {
|
||||
return currentType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws IllegalStateException if {@link MapLibre#getInstance(android.content.Context)}
|
||||
* has already been called in this process.
|
||||
*/
|
||||
static void setCurrentType(@NonNull Type type) {
|
||||
if (MapLibre.hasInstance()) {
|
||||
throw new IllegalStateException(
|
||||
"RenderingEngine.setCurrentType() must be called before MapLibre.getInstance().");
|
||||
}
|
||||
currentType = type;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines which rendering engine to use when none was explicitly requested:
|
||||
* Vulkan if this device's hardware supports it, OpenGL otherwise.
|
||||
*/
|
||||
@NonNull
|
||||
static Type getDefaultRenderingEngine(@NonNull Context context) {
|
||||
return isVulkanSupported(context) ? Type.VULKAN : Type.OPENGL;
|
||||
}
|
||||
|
||||
private static boolean isVulkanSupported(@NonNull Context context) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
|
||||
return false;
|
||||
}
|
||||
return context.getPackageManager().hasSystemFeature(
|
||||
PackageManager.FEATURE_VULKAN_HARDWARE_VERSION, REQUIRED_VULKAN_VERSION);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 (Vulkan, the default name) or libmaplibre-opengl.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.OPENGL) {
|
||||
System.loadLibrary("maplibre-opengl");
|
||||
} else {
|
||||
System.loadLibrary(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user