Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d1f113d0 authored by Ruben Brunk's avatar Ruben Brunk
Browse files

DO NOT MERGE: camera2: Performance potpourri.

Bug: 16208403
Bug: 15116722

Fixes many of the performance issues in the Camera2 API and
LEGACY mode CPU path, including:
- Only call getParameters/setParameters when the request has changed.
- Cache Request/Result objects.
- Cache object hashes for long-lived CameraMetadata, TypeReference,
  and Key objects with frequently used hash methods.
- Switch to Command pattern instead of repeated if/equals calls.
  in frequently hit CameraMetadata get/set methods.
- Move string construction for logging behind flags to avoid extra
  StringBuilder calls in frequently acquired lock methods.
- Cache results from frequently used JNI calls in object Builders.

Change-Id: I77bc4a023d4fe8bc46efcf771ff18ee268dcb9a9
parent ce50e7af
Loading
Loading
Loading
Loading
+13 −0
Original line number Diff line number Diff line
@@ -2423,6 +2423,19 @@ public class Camera {
            return Camera.this;
        }


        /**
         * Value equality check.
         *
         * @hide
         */
        public boolean same(Parameters other) {
            if (this == other) {
                return true;
            }
            return other != null && Parameters.this.mMap.equals(other.mMap);
        }

        /**
         * Writes the current Parameters to the log.
         * @hide
+158 −58
Original line number Diff line number Diff line
@@ -56,7 +56,6 @@ import android.location.LocationManager;
import android.os.Parcelable;
import android.os.Parcel;
import android.util.Log;
import android.util.Pair;
import android.util.Size;

import com.android.internal.util.Preconditions;
@@ -79,7 +78,7 @@ public class CameraMetadataNative implements Parcelable {
        private final Class<T> mType;
        private final TypeReference<T> mTypeReference;
        private final String mName;

        private final int mHash;
        /**
         * Visible for testing only.
         *
@@ -95,6 +94,7 @@ public class CameraMetadataNative implements Parcelable {
            mName = name;
            mType = type;
            mTypeReference = TypeReference.createSpecializedTypeReference(type);
            mHash = mName.hashCode() ^ mTypeReference.hashCode();
        }

        /**
@@ -113,6 +113,7 @@ public class CameraMetadataNative implements Parcelable {
            mName = name;
            mType = (Class<T>)typeReference.getRawType();
            mTypeReference = typeReference;
            mHash = mName.hashCode() ^ mTypeReference.hashCode();
        }

        /**
@@ -137,7 +138,7 @@ public class CameraMetadataNative implements Parcelable {
         */
        @Override
        public final int hashCode() {
            return mName.hashCode() ^ mTypeReference.hashCode();
            return mHash;
        }

        /**
@@ -156,6 +157,10 @@ public class CameraMetadataNative implements Parcelable {
                return true;
            }

            if (o == null || this.hashCode() != o.hashCode()) {
                return false;
            }

            Key<?> lhs;

            if (o instanceof CaptureResult.Key) {
@@ -337,11 +342,11 @@ public class CameraMetadataNative implements Parcelable {
    public <T> T get(Key<T> key) {
        Preconditions.checkNotNull(key, "key must not be null");

        Pair<T, Boolean> override = getOverride(key);
        if (override.second) {
            return override.first;
        // Check if key has been overridden to use a wrapper class on the java side.
        GetCommand g = sGetCommandMap.get(key);
        if (g != null) {
            return (T) g.getValue(this, key);
        }

        return getBase(key);
    }

@@ -371,7 +376,9 @@ public class CameraMetadataNative implements Parcelable {
     * type to the key.
     */
    public <T> void set(Key<T> key, T value) {
        if (setOverride(key, value)) {
        SetCommand s = sSetCommandMap.get(key);
        if (s != null) {
            s.setValue(this, value);
            return;
        }

@@ -449,44 +456,119 @@ public class CameraMetadataNative implements Parcelable {
        ByteBuffer buffer = ByteBuffer.wrap(values).order(ByteOrder.nativeOrder());
        return marshaler.unmarshal(buffer);
    }
    // Need overwrite some metadata that has different definitions between native
    // and managed sides.

    // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
    // metadata.
    private static final HashMap<Key<?>, GetCommand> sGetCommandMap =
            new HashMap<Key<?>, GetCommand>();
    static {
        sGetCommandMap.put(
                CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
    private <T> Pair<T, Boolean> getOverride(Key<T> key) {
        T value = null;
        boolean override = true;

        if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
            value = (T) getAvailableFormats();
        } else if (key.equals(CaptureResult.STATISTICS_FACES)) {
            value = (T) getFaces();
        } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
            value = (T) getFaceRectangles();
        } else if (key.equals(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)) {
            value = (T) getStreamConfigurationMap();
        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AE)) {
            value = (T) getMaxRegions(key);
        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AWB)) {
            value = (T) getMaxRegions(key);
        } else if (key.equals(CameraCharacteristics.CONTROL_MAX_REGIONS_AF)) {
            value = (T) getMaxRegions(key);
        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW)) {
            value = (T) getMaxNumOutputs(key);
        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC)) {
            value = (T) getMaxNumOutputs(key);
        } else if (key.equals(CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING)) {
            value = (T) getMaxNumOutputs(key);
        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
            value = (T) getTonemapCurve();
        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
            value = (T) getGpsLocation();
        } else if (key.equals(CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP)) {
            value = (T) getLensShadingMap();
        } else {
            override = false;
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getAvailableFormats();
                    }

        return Pair.create(value, override);
                });
        sGetCommandMap.put(
                CaptureResult.STATISTICS_FACES.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getFaceRectangles();
                    }
                });
        sGetCommandMap.put(
                CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getFaces();
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP.getNativeKey(),
                        new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getStreamConfigurationMap();
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.CONTROL_MAX_REGIONS_AE.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getMaxRegions(key);
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.CONTROL_MAX_REGIONS_AWB.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getMaxRegions(key);
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.CONTROL_MAX_REGIONS_AF.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getMaxRegions(key);
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_RAW.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getMaxNumOutputs(key);
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getMaxNumOutputs(key);
                    }
                });
        sGetCommandMap.put(
                CameraCharacteristics.REQUEST_MAX_NUM_OUTPUT_PROC_STALLING.getNativeKey(),
                        new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getMaxNumOutputs(key);
                    }
                });
        sGetCommandMap.put(
                CaptureRequest.TONEMAP_CURVE.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getTonemapCurve();
                    }
                });
        sGetCommandMap.put(
                CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getGpsLocation();
                    }
                });
        sGetCommandMap.put(
                CaptureResult.STATISTICS_LENS_SHADING_CORRECTION_MAP.getNativeKey(),
                        new GetCommand() {
                    @Override
                    @SuppressWarnings("unchecked")
                    public <T> T getValue(CameraMetadataNative metadata, Key<T> key) {
                        return (T) metadata.getLensShadingMap();
                    }
                });
    }

    private int[] getAvailableFormats() {
@@ -759,19 +841,37 @@ public class CameraMetadataNative implements Parcelable {
        writeValues(tag, values);
    }

    // Set the camera metadata override.
    private <T> boolean setOverride(Key<T> key, T value) {
        if (key.equals(CameraCharacteristics.SCALER_AVAILABLE_FORMATS)) {
            return setAvailableFormats((int[]) value);
        } else if (key.equals(CaptureResult.STATISTICS_FACE_RECTANGLES)) {
            return setFaceRectangles((Rect[]) value);
        } else if (key.equals(CaptureRequest.TONEMAP_CURVE)) {
            return setTonemapCurve((TonemapCurve) value);
        } else if (key.equals(CaptureResult.JPEG_GPS_LOCATION)) {
            return setGpsLocation((Location) value);
    // Use Command pattern here to avoid lots of expensive if/equals checks in get for overridden
    // metadata.
    private static final HashMap<Key<?>, SetCommand> sSetCommandMap =
            new HashMap<Key<?>, SetCommand>();
    static {
        sSetCommandMap.put(CameraCharacteristics.SCALER_AVAILABLE_FORMATS.getNativeKey(),
                new SetCommand() {
            @Override
            public <T> void setValue(CameraMetadataNative metadata, T value) {
                metadata.setAvailableFormats((int[]) value);
            }
        // For other keys, set() falls back to setBase().
        return false;
        });
        sSetCommandMap.put(CaptureResult.STATISTICS_FACE_RECTANGLES.getNativeKey(),
                new SetCommand() {
            @Override
            public <T> void setValue(CameraMetadataNative metadata, T value) {
                metadata.setFaceRectangles((Rect[]) value);
            }
        });
        sSetCommandMap.put(CaptureRequest.TONEMAP_CURVE.getNativeKey(), new SetCommand() {
            @Override
            public <T> void setValue(CameraMetadataNative metadata, T value) {
                metadata.setTonemapCurve((TonemapCurve) value);
            }
        });
        sSetCommandMap.put(CaptureResult.JPEG_GPS_LOCATION.getNativeKey(), new SetCommand() {
            @Override
            public <T> void setValue(CameraMetadataNative metadata, T value) {
                metadata.setGpsLocation((Location) value);
            }
        });
    }

    private boolean setAvailableFormats(int[] value) {
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.hardware.camera2.impl;

/**
 * Getter interface for use with Command pattern metadata value getters.
 */
public interface GetCommand {

    /**
     * Get the value from the given {@link CameraMetadataNative} object.
     *
     * @param metadata the {@link CameraMetadataNative} object to get the value from.
     * @param key the {@link CameraMetadataNative.Key} to look up.
     * @param <T> the type of the value.
     * @return the value for a given {@link CameraMetadataNative.Key}.
     */
    public <T> T getValue(CameraMetadataNative metadata, CameraMetadataNative.Key<T> key);
}
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.hardware.camera2.impl;

/**
 * Setter interface for use with Command pattern metadata value setters.
 */
public interface SetCommand {

    /**
     * Set the value in the given metadata.
     *
     * @param metadata {@link CameraMetadataNative} to set value in.
     * @param value value to set.
     * @param <T> type of the value to set.
     */
    public <T> void setValue(/*inout*/CameraMetadataNative metadata,
                             T value);
}
+15 −6
Original line number Diff line number Diff line
@@ -17,6 +17,9 @@
package android.hardware.camera2.legacy;

import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.util.Log;
import android.view.Surface;

import java.util.ArrayList;
import java.util.List;
@@ -25,8 +28,8 @@ import java.util.List;
 * Immutable container for a burst of capture results.
 */
public class BurstHolder {

    private final ArrayList<CaptureRequest> mRequests;
    private static final String TAG = "BurstHolder";
    private final ArrayList<RequestHolder.Builder> mRequestBuilders;
    private final boolean mRepeating;
    private final int mRequestId;

@@ -38,7 +41,13 @@ public class BurstHolder {
     * @param requests a {@link java.util.List} of {@link CaptureRequest}s in this burst.
     */
    public BurstHolder(int requestId, boolean repeating, List<CaptureRequest> requests) {
        mRequests = new ArrayList<CaptureRequest>(requests);
        mRequestBuilders = new ArrayList<RequestHolder.Builder>();
        int i = 0;
        for (CaptureRequest r : requests) {
            mRequestBuilders.add(new RequestHolder.Builder(requestId, /*subsequenceId*/i,
                    /*request*/r, repeating));
            ++i;
        }
        mRepeating = repeating;
        mRequestId = requestId;
    }
@@ -61,7 +70,7 @@ public class BurstHolder {
     * Return the number of requests in this burst sequence.
     */
    public int getNumberOfRequests() {
        return mRequests.size();
        return mRequestBuilders.size();
    }

    /**
@@ -73,8 +82,8 @@ public class BurstHolder {
    public List<RequestHolder> produceRequestHolders(long frameNumber) {
        ArrayList<RequestHolder> holders = new ArrayList<RequestHolder>();
        int i = 0;
        for (CaptureRequest r : mRequests) {
            holders.add(new RequestHolder(mRequestId, i, r, mRepeating, frameNumber + i));
        for (RequestHolder.Builder b : mRequestBuilders) {
            holders.add(b.build(frameNumber + i));
            ++i;
        }
        return holders;
Loading