diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java index d041ad762c5..4c62ff47ec9 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapView.java @@ -140,10 +140,10 @@ protected void initialize(@NonNull final Context context, @NonNull final MapLibr // add accessibility support setContentDescription(context.getString(R.string.maplibre_mapActionDescription)); setWillNotDraw(false); - initialiseDrawingSurface(options); + initializeDrawingSurface(options); } - private void initialiseMap() { + private void initializeMap() { Context context = getContext(); // callback for focal point invalidation @@ -305,7 +305,7 @@ public void onCreate(@Nullable Bundle savedInstanceState) { } } - private void initialiseDrawingSurface(MapLibreMapOptions options) { + private void initializeDrawingSurface(MapLibreMapOptions options) { mapRenderer = MapRenderer.create(options, getContext(), () -> MapView.this.onSurfaceCreated()); renderView = mapRenderer.getView(); @@ -321,9 +321,9 @@ private void onSurfaceCreated() { post(new Runnable() { @Override public void run() { - // Initialise only when not destroyed and only once + // Initialize only when not destroyed and only once if (!destroyed && maplibreMap == null) { - MapView.this.initialiseMap(); + MapView.this.initializeMap(); maplibreMap.onStart(); } } @@ -465,6 +465,34 @@ public void setMaximumFps(int maximumFps) { } } + /** + * Set the rendering refresh mode and wake up the render thread if it is sleeping. + * + * @param mode can be: + * {@link MapRenderer.RenderingRefreshMode#CONTINUOUS} or {@link MapRenderer.RenderingRefreshMode#WHEN_DIRTY} + * default is {@link MapRenderer.RenderingRefreshMode#WHEN_DIRTY} + */ + public void setRenderingRefreshMode(MapRenderer.RenderingRefreshMode mode) { + if (mapRenderer != null) { + mapRenderer.setRenderingRefreshMode(mode); + } else { + throw new IllegalStateException("Calling MapView#setRenderingRefreshMode before mapRenderer is created."); + } + } + + /** + * Get the rendering refresh mode + * + * @return one of the MapRenderer.RenderingRefreshMode modes + * @see #setRenderingRefreshMode + */ + public MapRenderer.RenderingRefreshMode getRenderingRefreshMode() { + if (mapRenderer == null) { + throw new IllegalStateException("Calling MapView#getRenderingRefreshMode before mapRenderer is created."); + } + return mapRenderer.getRenderingRefreshMode(); + } + /** * Returns if the map has been destroyed. *
diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRenderer.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRenderer.java index 992e07b6df2..24ca1af3170 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRenderer.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/MapRenderer.java @@ -30,7 +30,24 @@ public abstract class MapRenderer implements MapRendererScheduler { private static final String TAG = "Mbgl-MapRenderer"; - // Holds the pointer to the native peer after initialisation + /** + * Rendering presentation refresh mode. + */ + public enum RenderingRefreshMode { + /** + * The map is rendered only in response to an event that affects the rendering of the map. + * This mode is preferred to improve battery life and overall system performance + */ + WHEN_DIRTY, + + /** + * The map is repeatedly re-rendered at the refresh rate of the display. + * This mode is preferred when benchmarking the rendering + */ + CONTINUOUS, + } + + // Holds the pointer to the native peer after initialization private long nativePtr = 0; private double expectedRenderTime = 0; private MapLibreMap.OnFpsChangedListener onFpsChangedListener; @@ -57,7 +74,7 @@ public static MapRenderer create(MapLibreMapOptions options, @NonNull Context co public MapRenderer(@NonNull Context context, String localIdeographFontFamily) { float pixelRatio = context.getResources().getDisplayMetrics().density; - // Initialise native peer + // Initialize native peer nativeInitialize(this, pixelRatio, localIdeographFontFamily); } @@ -83,6 +100,10 @@ public void onDestroy() { // Implement if needed } + public abstract void setRenderingRefreshMode(RenderingRefreshMode mode); + + public abstract RenderingRefreshMode getRenderingRefreshMode(); + public void setOnFpsChangedListener(MapLibreMap.OnFpsChangedListener listener) { onFpsChangedListener = listener; } diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreSurfaceView.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreSurfaceView.java index d69baa0b12b..b30c0642b91 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreSurfaceView.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/MapLibreSurfaceView.java @@ -5,6 +5,7 @@ import android.util.AttributeSet; import android.view.SurfaceHolder; import android.view.SurfaceView; +import org.maplibre.android.maps.renderer.MapRenderer; import androidx.annotation.NonNull; @@ -21,24 +22,6 @@ public abstract class MapLibreSurfaceView extends SurfaceView implements Surface protected boolean detached; - /** - * The renderer only renders - * when the surface is created, or when {@link #requestRender} is called. - * - * @see #getRenderMode() - * @see #setRenderMode(int) - * @see #requestRender() - */ - public static final int RENDERMODE_WHEN_DIRTY = 0; - /** - * The renderer is called - * continuously to re-render the scene. - * - * @see #getRenderMode() - * @see #setRenderMode(int) - */ - public static final int RENDERMODE_CONTINUOUSLY = 1; - /** * Standard View constructor. In order to render something, you * must call {@link #setRenderer} to register a renderer. @@ -104,7 +87,7 @@ public void setDetachedListener(@NonNull OnSurfaceViewDetachedListener detachedL *
- * Using RENDERMODE_WHEN_DIRTY can improve battery life and overall system performance + * Using WHEN_DIRTY can improve battery life and overall system performance * by allowing the GPU and CPU to idle when the view does not need to be updated. *
* This method can only be called after {@link #setRenderer(SurfaceViewMapRenderer)}
*
- * @param renderMode one of the RENDERMODE_X constants
+ * @param mode one of the MapRenderer.RenderingRefreshMode constants
*/
- public void setRenderMode(int renderMode) {
- renderThread.setRenderMode(renderMode);
+ public void setRenderingRefreshMode(MapRenderer.RenderingRefreshMode mode) {
+ renderThread.setRenderingRefreshMode(mode);
}
/**
@@ -141,14 +123,14 @@ public void setRenderMode(int renderMode) {
*
* @return the current rendering mode.
*/
- public int getRenderMode() {
- return renderThread.getRenderMode();
+ public MapRenderer.RenderingRefreshMode getRenderingRefreshMode() {
+ return renderThread.getRenderingRefreshMode();
}
/**
* Request that the renderer render a frame.
* This method is typically used when the render mode has been set to
- * RENDERMODE_WHEN_DIRTY, so that frames are only rendered on demand.
+ * MapRenderer.RenderingRefreshMode.WHEN_DIRTY, so that frames are only rendered on demand.
* May be called
* from any thread. Must not be called before a renderer has been set.
*/
@@ -249,13 +231,13 @@ public void waitForEmpty() {
protected void onAttachedToWindow() {
super.onAttachedToWindow();
if (detached && (renderer != null)) {
- int renderMode = RENDERMODE_CONTINUOUSLY;
+ MapRenderer.RenderingRefreshMode renderMode = MapRenderer.RenderingRefreshMode.WHEN_DIRTY;
if (renderThread != null) {
- renderMode = renderThread.getRenderMode();
+ renderMode = renderThread.getRenderingRefreshMode();
}
createRenderThread();
- if (renderMode != RENDERMODE_CONTINUOUSLY) {
- renderThread.setRenderMode(renderMode);
+ if (renderMode != MapRenderer.RenderingRefreshMode.WHEN_DIRTY) {
+ renderThread.setRenderingRefreshMode(renderMode);
}
renderThread.start();
}
@@ -289,7 +271,7 @@ abstract static class RenderThread extends Thread {
width = 0;
height = 0;
requestRender = true;
- renderMode = RENDERMODE_CONTINUOUSLY;
+ renderMode = MapRenderer.RenderingRefreshMode.WHEN_DIRTY;
wantRenderNotification = false;
}
@@ -311,24 +293,21 @@ public void run() {
protected boolean readyToDraw() {
return (!paused) && hasSurface
&& (width > 0) && (height > 0)
- && (requestRender || (renderMode == RENDERMODE_CONTINUOUSLY));
+ && (requestRender || (renderMode == MapRenderer.RenderingRefreshMode.CONTINUOUS));
}
public boolean ableToDraw() {
return readyToDraw();
}
- public void setRenderMode(int renderMode) {
- if ( !((RENDERMODE_WHEN_DIRTY <= renderMode) && (renderMode <= RENDERMODE_CONTINUOUSLY)) ) {
- throw new IllegalArgumentException("renderMode");
- }
+ public void setRenderingRefreshMode(MapRenderer.RenderingRefreshMode mode) {
synchronized (renderThreadManager) {
- this.renderMode = renderMode;
+ this.renderMode = mode;
renderThreadManager.notifyAll();
}
}
- public int getRenderMode() {
+ public MapRenderer.RenderingRefreshMode getRenderingRefreshMode() {
synchronized (renderThreadManager) {
return renderMode;
}
@@ -503,7 +482,7 @@ public void waitForEmpty() {
protected boolean waitingForSurface;
protected int width;
protected int height;
- protected int renderMode;
+ protected MapRenderer.RenderingRefreshMode renderMode;
protected boolean requestRender;
protected boolean wantRenderNotification;
protected boolean renderComplete;
diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/SurfaceViewMapRenderer.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/SurfaceViewMapRenderer.java
index 468f7f6cdfe..99445e8e14a 100644
--- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/SurfaceViewMapRenderer.java
+++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/surfaceview/SurfaceViewMapRenderer.java
@@ -112,4 +112,20 @@ public void queueEvent(Runnable runnable) {
public void waitForEmpty() {
surfaceView.waitForEmpty();
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setRenderingRefreshMode(MapRenderer.RenderingRefreshMode mode) {
+ surfaceView.setRenderingRefreshMode(mode);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MapRenderer.RenderingRefreshMode getRenderingRefreshMode() {
+ return surfaceView.getRenderingRefreshMode();
+ }
}
\ No newline at end of file
diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/textureview/TextureViewMapRenderer.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/textureview/TextureViewMapRenderer.java
index 1ec1b0b9fd3..64623ea3acf 100644
--- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/textureview/TextureViewMapRenderer.java
+++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/renderer/textureview/TextureViewMapRenderer.java
@@ -132,4 +132,22 @@ public void onDestroy() {
public boolean isTranslucentSurface() {
return translucentSurface;
}
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setRenderingRefreshMode(MapRenderer.RenderingRefreshMode mode) {
+ throw new RuntimeException("setRenderingRefreshMode is not supported for TextureViewMapRenderer. "
+ + "Use SurfaceViewMapRenderer to set the rendering refresh mode.");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public MapRenderer.RenderingRefreshMode getRenderingRefreshMode() {
+ throw new RuntimeException("getRenderingRefreshMode is not supported for TextureViewMapRenderer. "
+ + "Use SurfaceViewMapRenderer to set the rendering refresh mode.");
+ }
}
diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java
index 0a8f1a0d7d1..9d05530a912 100644
--- a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java
+++ b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/surfaceview/GLSurfaceViewMapRenderer.java
@@ -6,8 +6,7 @@
import org.maplibre.android.maps.renderer.egl.EGLConfigChooser;
import org.maplibre.android.maps.renderer.egl.EGLContextFactory;
import org.maplibre.android.maps.renderer.egl.EGLWindowSurfaceFactory;
-
-import static android.opengl.GLSurfaceView.RENDERMODE_WHEN_DIRTY;
+import org.maplibre.android.maps.renderer.MapRenderer;
public class GLSurfaceViewMapRenderer extends SurfaceViewMapRenderer {
@@ -20,7 +19,7 @@ public GLSurfaceViewMapRenderer(Context context,
surfaceView.setEGLWindowSurfaceFactory(new EGLWindowSurfaceFactory());
surfaceView.setEGLConfigChooser(new EGLConfigChooser());
surfaceView.setRenderer(this);
- surfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
+ surfaceView.setRenderingRefreshMode(MapRenderer.RenderingRefreshMode.WHEN_DIRTY);
surfaceView.setPreserveEGLContextOnPause(true);
}
}
\ No newline at end of file
diff --git a/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt b/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt
index 48a9334f90d..38e2467c931 100644
--- a/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt
+++ b/platform/android/MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/maps/MapLibreMapTest.kt
@@ -19,6 +19,7 @@ import org.maplibre.android.annotations.PolylineOptions
import org.maplibre.android.exceptions.InvalidMarkerPositionException
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.maps.MapLibreMap.InfoWindowAdapter
+import org.maplibre.android.maps.renderer.MapRenderer.RenderingRefreshMode
import org.maplibre.android.testapp.R
import org.maplibre.android.testapp.activity.EspressoTest
@@ -480,6 +481,19 @@ class MapLibreMapTest : EspressoTest() {
}
}
+ @Test
+ fun testRenderingRefreshMode() {
+ validateTestSetup()
+ rule.runOnUiThread {
+ mapView = rule.getActivity().findViewById(R.id.mapView)
+ // Default RenderingRefreshMode is WHEN_DIRTY
+ assertTrue(mapView.getRenderingRefreshMode() == RenderingRefreshMode.WHEN_DIRTY)
+ // Switch to CONTINUOUS rendering
+ mapView.setRenderingRefreshMode(RenderingRefreshMode.CONTINUOUS)
+ assertTrue(mapView.getRenderingRefreshMode() == RenderingRefreshMode.CONTINUOUS)
+ }
+ }
+
@Deprecated("remove this class when removing deprecated annotations")
inner class MapLibreMapAction internal constructor(private val invokeViewAction: InvokeViewAction) : ViewAction {
override fun getConstraints(): Matcher