Expose RENDERMODE_CONTINUOUSLY and RENDERMODE_WHEN_DIRTY (#2801)
This commit is contained in:
@@ -140,10 +140,10 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
// 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 class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ public class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
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 class MapView extends FrameLayout implements NativeMapView.ViewCallback {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
|
||||
@@ -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 abstract class MapRenderer implements MapRendererScheduler {
|
||||
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 abstract class MapRenderer implements MapRendererScheduler {
|
||||
// Implement if needed
|
||||
}
|
||||
|
||||
public abstract void setRenderingRefreshMode(RenderingRefreshMode mode);
|
||||
|
||||
public abstract RenderingRefreshMode getRenderingRefreshMode();
|
||||
|
||||
public void setOnFpsChangedListener(MapLibreMap.OnFpsChangedListener listener) {
|
||||
onFpsChangedListener = listener;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.graphics.PixelFormat;
|
||||
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 abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
* <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.
|
||||
@@ -118,21 +101,20 @@ public abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -141,14 +123,14 @@ public abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
*
|
||||
* @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 abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
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 @@ public abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
width = 0;
|
||||
height = 0;
|
||||
requestRender = true;
|
||||
renderMode = RENDERMODE_CONTINUOUSLY;
|
||||
renderMode = MapRenderer.RenderingRefreshMode.WHEN_DIRTY;
|
||||
wantRenderNotification = false;
|
||||
}
|
||||
|
||||
@@ -311,24 +293,21 @@ public abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
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 abstract class MapLibreSurfaceView extends SurfaceView implements Surface
|
||||
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;
|
||||
|
||||
@@ -112,4 +112,20 @@ public class SurfaceViewMapRenderer extends MapRenderer {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -132,4 +132,22 @@ public class TextureViewMapRenderer extends MapRenderer {
|
||||
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.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,7 @@ import androidx.annotation.NonNull;
|
||||
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 class GLSurfaceViewMapRenderer extends SurfaceViewMapRenderer {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<View> {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,7 +50,7 @@ public abstract class BaseTest extends AppCenter {
|
||||
@Before
|
||||
@CallSuper
|
||||
public void beforeTest() {
|
||||
initialiseMap();
|
||||
initializeMap();
|
||||
holdTestRunnerForStyleLoad();
|
||||
}
|
||||
|
||||
@@ -71,15 +71,15 @@ public abstract class BaseTest extends AppCenter {
|
||||
if (!MapLibre.isConnected()) {
|
||||
Timber.e("Not connected to the internet while running test");
|
||||
}
|
||||
assertNotNull("MapView isn't initialised", mapView);
|
||||
assertNotNull("MapLibreMap isn't initialised", maplibreMap);
|
||||
assertNotNull("Style isn't initialised", maplibreMap.getStyle());
|
||||
assertNotNull("MapView isn't initialized", mapView);
|
||||
assertNotNull("MapLibreMap isn't initialized", maplibreMap);
|
||||
assertNotNull("Style isn't initialized", maplibreMap.getStyle());
|
||||
assertTrue("Style isn't fully loaded", maplibreMap.getStyle().isFullyLoaded());
|
||||
}
|
||||
|
||||
protected abstract Class getActivityClass();
|
||||
|
||||
private void initialiseMap() {
|
||||
private void initializeMap() {
|
||||
try {
|
||||
rule.runOnUiThread(() -> {
|
||||
mapView = rule.getActivity().findViewById(R.id.mapView);
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import org.maplibre.android.maps.*
|
||||
import org.maplibre.android.maps.MapLibreMap.OnCameraMoveListener
|
||||
import org.maplibre.android.maps.MapLibreMap.OnFpsChangedListener
|
||||
import org.maplibre.android.maps.renderer.MapRenderer
|
||||
import org.maplibre.android.style.layers.Layer
|
||||
import org.maplibre.android.style.layers.Property
|
||||
import org.maplibre.android.style.layers.PropertyFactory
|
||||
@@ -29,6 +30,7 @@ open class DebugModeActivity : AppCompatActivity(), OnMapReadyCallback, OnFpsCha
|
||||
private var actionBarDrawerToggle: ActionBarDrawerToggle? = null
|
||||
private var currentStyleIndex = 0
|
||||
private var isReportFps = true
|
||||
private var isContinuousRendering = false
|
||||
private var fpsView: TextView? = null
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -167,6 +169,13 @@ open class DebugModeActivity : AppCompatActivity(), OnMapReadyCallback, OnFpsCha
|
||||
mapView.setMaximumFps(30)
|
||||
} else if (itemId == R.id.menu_action_limit_to_60_fps) {
|
||||
mapView.setMaximumFps(60)
|
||||
} else if (itemId == R.id.menu_action_toggle_continuous_rendering) {
|
||||
isContinuousRendering = !isContinuousRendering
|
||||
if (isContinuousRendering) {
|
||||
mapView.setRenderingRefreshMode(MapRenderer.RenderingRefreshMode.CONTINUOUS)
|
||||
} else {
|
||||
mapView.setRenderingRefreshMode(MapRenderer.RenderingRefreshMode.WHEN_DIRTY)
|
||||
}
|
||||
}
|
||||
return actionBarDrawerToggle!!.onOptionsItemSelected(item) || super.onOptionsItemSelected(
|
||||
item
|
||||
|
||||
@@ -13,5 +13,9 @@
|
||||
android:id="@+id/menu_action_limit_to_60_fps"
|
||||
android:title="Limit FPS to 60"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/menu_action_toggle_continuous_rendering"
|
||||
android:title="Toggle continuous rendering"
|
||||
app:showAsAction="never" />
|
||||
|
||||
</menu>
|
||||
|
||||
Reference in New Issue
Block a user