Skip to content

Commit

Permalink
Expose RENDERMODE_CONTINUOUSLY and RENDERMODE_WHEN_DIRTY (#2801)
Browse files Browse the repository at this point in the history
  • Loading branch information
alasram authored Sep 17, 2024
1 parent 0e0c8da commit 77a464d
Show file tree
Hide file tree
Showing 11 changed files with 157 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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();

Expand All @@ -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();
}
}
Expand Down Expand Up @@ -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.
* <p>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
}

Expand All @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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.
Expand Down Expand Up @@ -104,7 +87,7 @@ public void setDetachedListener(@NonNull OnSurfaceViewDetachedListener detachedL
* <li>{@link #onResume()}
* <li>{@link #queueEvent(Runnable)}
* <li>{@link #requestRender()}
* <li>{@link #setRenderMode(int)}
* <li>{@link #setRenderingRefreshMode(MapRenderer.RenderingRefreshMode)}
* </ul>
*
* @param renderer the renderer to use to perform drawing.
Expand All @@ -118,21 +101,20 @@ public void setRenderer(SurfaceViewMapRenderer renderer) {
}

/**
* Set the rendering mode. When renderMode is
* RENDERMODE_CONTINUOUSLY, the renderer is called
* repeatedly to re-render the scene. When renderMode
* is RENDERMODE_WHEN_DIRTY, the renderer only rendered when the surface
* is created, or when {@link #requestRender} is called. Defaults to RENDERMODE_CONTINUOUSLY.
* Set the rendering refresh mode to CONTINUOUS or WHEN_DIRTY.
* Defaults to MapRenderer.RenderingRefreshMode.WHEN_DIRTY.
* The renderer is called repeatedly to re-render the scene in continuous mode otherwise
* the renderer is called when the surface is created, or when {@link #requestRender} is called.
* <p>
* 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.
* <p>
* 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);
}

/**
Expand All @@ -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.
*/
Expand Down Expand Up @@ -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();
}
Expand Down Expand Up @@ -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;
}

Expand All @@ -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;
}
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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<View> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,8 @@ class NativeMapViewTest : AppCenter() {

class DummyRenderer(context: Context) : MapRenderer(context, null) {

private var renderingRefreshMode: RenderingRefreshMode = RenderingRefreshMode.WHEN_DIRTY

override fun requestRender() {
// no-op
}
Expand All @@ -452,5 +454,13 @@ class NativeMapViewTest : AppCenter() {
override fun getView(): View? {
return null;
}

override fun setRenderingRefreshMode(mode : RenderingRefreshMode) {
renderingRefreshMode = mode
}

override fun getRenderingRefreshMode() : RenderingRefreshMode{
return renderingRefreshMode
}
}
}
Loading

0 comments on commit 77a464d

Please sign in to comment.