diff options
author | Łukasz Paczos <lukasz.paczos@mapbox.com> | 2019-09-06 14:22:30 +0200 |
---|---|---|
committer | Łukasz Paczos <lukasz.paczos@mapbox.com> | 2019-09-06 14:23:15 +0200 |
commit | 0dff7299e2546ec7828739dd6ee0a0b9f68fb0f6 (patch) | |
tree | 907fffbf869bbcad8cfe913e213f93e2b11b9df7 | |
parent | 641a7c242d8119b05f93d53ead9991008a39b431 (diff) | |
download | qtlocation-mapboxgl-0dff7299e2546ec7828739dd6ee0a0b9f68fb0f6.tar.gz |
[android][wip] use framework choreographer to sync camera animations
2 files changed, 130 insertions, 157 deletions
diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraTransition.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraTransition.java index 4a1828f9a6..cf0a9b825f 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraTransition.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/CameraTransition.java @@ -15,11 +15,11 @@ public abstract class CameraTransition<T> { public static final Interpolator INTERPOLATOR_EASING = PathInterpolatorCompat.create(0f, 0f, 0.25f, 1f); public static final int PROPERTY_CENTER = 0; - static final int PROPERTY_ZOOM = 1; - static final int PROPERTY_PITCH = 2; - static final int PROPERTY_BEARING = 3; - static final int PROPERTY_ANCHOR = 4; - static final int PROPERTY_PADDING = 5; + public static final int PROPERTY_ZOOM = 1; + public static final int PROPERTY_PITCH = 2; + public static final int PROPERTY_BEARING = 3; + public static final int PROPERTY_ANCHOR = 4; + public static final int PROPERTY_PADDING = 5; private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>(); @@ -28,10 +28,14 @@ public abstract class CameraTransition<T> { private T startValue; private final T endValue; - private double startTime; - private double endTime; - private final double duration; - private final double delay; + private double startTimeMillis; + private double startTimeNanos; + private double endTimeMillis; + private double endTimeNanos; + private final double durationMillis; + private final double durationNanos; + private final double delayMillis; + private final double delayNanos; private boolean isCanceled; private boolean isStarted; @@ -39,17 +43,21 @@ public abstract class CameraTransition<T> { private final Interpolator interpolator; - CameraTransition(int type, double duration, double delay, T endValue, Interpolator interpolator) { + CameraTransition(int type, double durationMillis, double delayMillis, T endValue, Interpolator interpolator) { this.type = type; - this.duration = duration; - this.delay = delay; + this.durationMillis = durationMillis; + this.durationNanos = durationMillis * 1E6; + this.delayMillis = delayMillis; + this.delayNanos = this.delayMillis * 1E6; this.endValue = endValue; this.interpolator = interpolator; } - void initTime(T startValue, double currentTime) { - this.startTime = currentTime + delay; - this.endTime = this.startTime + duration; + void initTime(T startValue, double currentTimeNanos) { + this.startTimeMillis = currentTimeNanos / 1E6 + delayMillis; + this.startTimeNanos = currentTimeNanos + delayNanos; + this.endTimeMillis = startTimeMillis + durationMillis; + this.endTimeNanos = startTimeNanos + durationNanos; this.startValue = startValue; this.isStarted = true; } @@ -82,25 +90,47 @@ public abstract class CameraTransition<T> { } public double getStartTime() { - if (startTime <= 0) { + if (startTimeMillis <= 0) { throw new RuntimeException("start or queue the transition first"); } - return startTime; + return startTimeMillis; + } + + double getStartTimeNanos() { + if (startTimeNanos <= 0) { + throw new RuntimeException("start or queue the transition first"); + } + return startTimeNanos; } public double getEndTime() { - if (endTime <= 0) { + if (endTimeMillis <= 0) { + throw new RuntimeException("start or queue the transition first"); + } + return endTimeMillis; + } + + double getEndTimeNanos() { + if (endTimeNanos <= 0) { throw new RuntimeException("start or queue the transition first"); } - return endTime; + return endTimeNanos; } public double getDuration() { - return duration; + return durationMillis; + } + + double getDurationNanos() { + return durationNanos; } public double getDelay() { - return delay; + return delayMillis; + } + + double getDelayNanos() { + return delayNanos; } public Interpolator getInterpolator() { @@ -115,8 +145,8 @@ public abstract class CameraTransition<T> { listeners.remove(listener); } - T onFrame(double currentTime) { - double animationPosition = (currentTime - startTime) / duration; + T onFrame(double currentTimeNanos) { + double animationPosition = (currentTimeNanos - startTimeNanos) / durationNanos; double fraction = interpolator.getInterpolation((float) animationPosition); return getAnimatedValue(fraction); } diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapCameraController.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapCameraController.java index ffbb19d23a..952cb4f20a 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapCameraController.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/MapCameraController.java @@ -1,17 +1,16 @@ package com.mapbox.mapboxsdk.maps; -import android.os.Handler; -import android.os.Looper; import android.support.annotation.NonNull; +import android.view.Choreographer; import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.geometry.LatLng; +import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.concurrent.locks.ReentrantLock; public class MapCameraController { @@ -19,159 +18,109 @@ public class MapCameraController { private final HashMap<Integer, LinkedList<CameraTransition>> queuedTransitions = new HashMap<>(5); @NonNull - private final ReentrantLock runningTransitionsLock = new ReentrantLock(); - - @NonNull private final HashMap<Integer, CameraTransition> runningTransitions = new HashMap<>(5); @NonNull private CameraBehavior behavior = new DefaultCameraBehavior(); @NonNull - private final Handler handler = new Handler(Looper.getMainLooper()); - - @NonNull private final Transform transform; private volatile boolean destroyed; - @NonNull - private final Thread thread = new Thread(new Runnable() { + private final Choreographer choreographer = Choreographer.getInstance(); - private static final short MIN_FRAME_INTERVAL = 10; + MapCameraController(@NonNull final Transform transform) { + this.transform = transform; + queuedTransitions.put(CameraTransition.PROPERTY_CENTER, new LinkedList<CameraTransition>()); + queuedTransitions.put(CameraTransition.PROPERTY_ZOOM, new LinkedList<CameraTransition>()); + queuedTransitions.put(CameraTransition.PROPERTY_PITCH, new LinkedList<CameraTransition>()); + queuedTransitions.put(CameraTransition.PROPERTY_BEARING, new LinkedList<CameraTransition>()); + queuedTransitions.put(CameraTransition.PROPERTY_ANCHOR, new LinkedList<CameraTransition>()); + queuedTransitions.put(CameraTransition.PROPERTY_PADDING, new LinkedList<CameraTransition>()); + choreographer.postFrameCallback(frameCallback); + } + private final Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() { @Override - public void run() { - CameraPosition cameraPosition = null; - while (true) { - if (destroyed) { - return; + public void doFrame(long frameTimeNanos) { + if (destroyed) { + return; + } + List<CameraTransition> canceled = new ArrayList<>(); + Iterator<CameraTransition> iterator = runningTransitions.values().iterator(); + while (iterator.hasNext()) { + CameraTransition transition = iterator.next(); + if (transition.isCanceled()) { + transition.onCancel(); + iterator.remove(); + canceled.add(transition); } + } - final CameraPosition update = cameraPosition; - if (update != null) { - handler.post(new Runnable() { - @Override - public void run() { - if (destroyed) { - return; - } - - CameraPosition finalUpdate = update; - - synchronized (runningTransitionsLock) { - Iterator<CameraTransition> iterator = runningTransitions.values().iterator(); - while (iterator.hasNext()) { - CameraTransition transition = iterator.next(); - if (transition.isCanceled()) { - transition.onCancel(); - iterator.remove(); - CameraPosition.Builder builder = new CameraPosition.Builder(finalUpdate); - setCameraProperty(builder, transition.getCameraProperty(), null); - finalUpdate = builder.build(); - - startNextTransition(transition.getCameraProperty()); - } - } - } - - // todo camera - check if update is noop, abort then - - transform.moveCamera(finalUpdate); - - synchronized (runningTransitionsLock) { - Iterator<CameraTransition> iterator = runningTransitions.values().iterator(); - while (iterator.hasNext()) { - CameraTransition transition = iterator.next(); - transition.onProgress(); - - if (transition.isFinishing()) { - transition.onFinish(); - iterator.remove(); - - startNextTransition(transition.getCameraProperty()); - } - } - } - } - }); - } + for (CameraTransition transition : canceled) { + startNextTransition(transition.getCameraProperty()); + } - // todo camera - what's the correct delay not to flood and block the main thread? - long nextFrameTime = System.currentTimeMillis() + MIN_FRAME_INTERVAL; - - synchronized (runningTransitionsLock) { - boolean willRun = false; - CameraPosition.Builder builder = new CameraPosition.Builder(); - for (final CameraTransition transition : runningTransitions.values()) { - if (transition.getStartTime() <= nextFrameTime) { - willRun = true; - if (nextFrameTime >= transition.getEndTime()) { - handler.post(new Runnable() { - @Override - public void run() { - transition.setFinishing(); - } - }); - } - - Object value = transition.isFinishing() ? transition.getEndValue() : transition.onFrame(nextFrameTime); - setCameraProperty(builder, transition.getCameraProperty(), value); - } + boolean shouldRun = false; + CameraPosition.Builder builder = new CameraPosition.Builder(); + for (final CameraTransition transition : runningTransitions.values()) { + if (transition.getStartTimeNanos() <= frameTimeNanos) { + shouldRun = true; + if (frameTimeNanos >= transition.getEndTimeNanos()) { + transition.setFinishing(); } - if (willRun) { - cameraPosition = builder.build(); - } else { - cameraPosition = null; - } + Object value = transition.isFinishing() ? transition.getEndValue() : transition.onFrame(frameTimeNanos); + setCameraProperty(builder, transition.getCameraProperty(), value); } + } - long sleepTime = nextFrameTime - System.currentTimeMillis(); + if (shouldRun) { + transform.moveCamera(builder.build()); + } - if (sleepTime >= 0) { - try { - Thread.sleep(sleepTime); - } catch (InterruptedException e) { - e.printStackTrace(); - } + + List<CameraTransition> finished = new ArrayList<>(); + iterator = runningTransitions.values().iterator(); + while (iterator.hasNext()) { + CameraTransition transition = iterator.next(); + transition.onProgress(); + + if (transition.isFinishing()) { + transition.onFinish(); + iterator.remove(); + finished.add(transition); } } - } - }, "MapCameraController"); - MapCameraController(@NonNull final Transform transform) { - this.transform = transform; - queuedTransitions.put(CameraTransition.PROPERTY_CENTER, new LinkedList<CameraTransition>()); - queuedTransitions.put(CameraTransition.PROPERTY_ZOOM, new LinkedList<CameraTransition>()); - queuedTransitions.put(CameraTransition.PROPERTY_PITCH, new LinkedList<CameraTransition>()); - queuedTransitions.put(CameraTransition.PROPERTY_BEARING, new LinkedList<CameraTransition>()); - queuedTransitions.put(CameraTransition.PROPERTY_ANCHOR, new LinkedList<CameraTransition>()); - queuedTransitions.put(CameraTransition.PROPERTY_PADDING, new LinkedList<CameraTransition>()); - thread.start(); - } + for (CameraTransition transition : finished) { + startNextTransition(transition.getCameraProperty()); + } + + choreographer.postFrameCallback(this); + } + }; public void startTransition(CameraTransition transition) { - synchronized (runningTransitionsLock) { - behavior.animationScheduled(this, transition); - - final CameraTransition runningTransition = runningTransitions.get(transition.getCameraProperty()); - if (runningTransition != null) { - CameraTransition resultingTransition = behavior.resolve(runningTransition, transition); - if (resultingTransition != transition && resultingTransition != runningTransition) { - throw new UnsupportedOperationException(); - } else if (resultingTransition != transition) { - transition.cancel(); - transition.onCancel(); - } else { - runningTransition.cancel(); - queuedTransitions.get(transition.getCameraProperty()).push(transition); - } + behavior.animationScheduled(this, transition); + + final CameraTransition runningTransition = runningTransitions.get(transition.getCameraProperty()); + if (runningTransition != null) { + CameraTransition resultingTransition = behavior.resolve(runningTransition, transition); + if (resultingTransition != transition && resultingTransition != runningTransition) { + throw new UnsupportedOperationException(); + } else if (resultingTransition != transition) { + transition.cancel(); + transition.onCancel(); } else { - long time = System.currentTimeMillis(); - transition.initTime(getCameraPropertyValue(transition.getCameraProperty()), time); - runningTransitions.put(transition.getCameraProperty(), transition); + runningTransition.cancel(); + queuedTransitions.get(transition.getCameraProperty()).push(transition); } + } else { + long time = System.nanoTime(); + transition.initTime(getCameraPropertyValue(transition.getCameraProperty()), time); + runningTransitions.put(transition.getCameraProperty(), transition); } } @@ -219,12 +168,6 @@ public class MapCameraController { transition.onCancel(); } runningTransitions.clear(); - - try { - thread.join(); - } catch (InterruptedException ex) { - ex.printStackTrace(); - } } private static void setCameraProperty(CameraPosition.Builder builder, int property, Object value) { |