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

Commit d2087e77 authored by Sally Qi's avatar Sally Qi
Browse files

Add fence support in ImageWriter class.

- Add SyncFence class that include FileDescriptor and related functions
- expose fence file descriptor setter/getter in the Image class

Bug: 210919185
Test: android.hardware.camera2.cts.ImageWriterTest
Change-Id: I7428db26e3e6ca84675d2ce2ef4def46ed0b1397
parent b41b25ff
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -17061,6 +17061,14 @@ package android.hardware {
    field public static final int MICROPHONE = 1; // 0x1
  }
  public final class SyncFence implements java.io.Closeable android.os.Parcelable {
    method public void close() throws java.io.IOException;
    method public int describeContents();
    method public boolean isValid();
    method public void writeToParcel(@NonNull android.os.Parcel, int);
    field @NonNull public static final android.os.Parcelable.Creator<android.hardware.SyncFence> CREATOR;
  }
  public final class TriggerEvent {
    field public android.hardware.Sensor sensor;
    field public long timestamp;
@@ -20773,6 +20781,7 @@ package android.media {
    method public abstract void close();
    method public android.graphics.Rect getCropRect();
    method public long getDataSpace();
    method @NonNull public android.hardware.SyncFence getFence() throws java.io.IOException;
    method public abstract int getFormat();
    method @Nullable public android.hardware.HardwareBuffer getHardwareBuffer();
    method public abstract int getHeight();
@@ -20781,6 +20790,7 @@ package android.media {
    method public abstract int getWidth();
    method public void setCropRect(android.graphics.Rect);
    method public void setDataSpace(long);
    method public void setFence(@NonNull android.hardware.SyncFence) throws java.io.IOException;
    method public void setTimestamp(long);
  }
+141 −0
Original line number Diff line number Diff line
/**
 * Copyright (C) 2022 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;

import android.annotation.NonNull;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;

import java.io.Closeable;
import java.io.IOException;

/**
 * A SyncFence represents a synchronization primitive which signals when hardware buffers have
 * completed work on a particular resource.
 *
 * <p>For example, a GPU rendering to a framebuffer may generate a synchronization fence,
 * e.g., a VkFence, which signals when rendering has completed.
 *
 * Once the fence signals, then the backing storage for the framebuffer may be safely read from,
 * such as for display or for media encoding.</p>
 *
 * @see <a href="https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/vkCreateFence.html">
 * VkFence</a>
 */
public final class SyncFence implements Closeable, Parcelable {
    private static final String TAG = "SyncFence";

    /**
     * Wrapped {@link android.os.ParcelFileDescriptor}.
     */
    private ParcelFileDescriptor mWrapped;

    private SyncFence(@NonNull ParcelFileDescriptor wrapped) {
        mWrapped = wrapped;
    }

    /***
     * Create an empty SyncFence
     *
     * @return a SyncFence with invalid fence
     * @hide
     */
    public static @NonNull SyncFence createEmpty() {
        return new SyncFence(ParcelFileDescriptor.adoptFd(-1));
    }

    /**
     * Create a new SyncFence wrapped around another descriptor. By default, all method calls are
     * delegated to the wrapped descriptor.
     *
     * @param wrapped The descriptor to be wrapped.
     * @hide
     */
    public static @NonNull SyncFence create(@NonNull ParcelFileDescriptor wrapped) {
        return new SyncFence(wrapped);
    }

    /**
     * Return a dup'd ParcelFileDescriptor from the SyncFence ParcelFileDescriptor.
     * @hide
     */
    public @NonNull ParcelFileDescriptor getFdDup() throws IOException {
        return mWrapped.dup();
    }

    /**
     * Checks if the SyncFile object is valid.
     *
     * @return {@code true} if the file descriptor represents a valid, open file;
     *         {@code false} otherwise.
     */
    public boolean isValid() {
        return mWrapped.getFileDescriptor().valid();
    }

    /**
     * Close the SyncFence. This implementation closes the underlying OS resources allocated
     * this stream.
     *
     * @throws IOException If an error occurs attempting to close this SyncFence.
     */
    @Override
    public void close() throws IOException {
        if (mWrapped != null) {
            try {
                mWrapped.close();
            } finally {
                // success
            }
        }
    }

    @Override
    public int describeContents() {
        return mWrapped.describeContents();
    }

    /**
     * Flatten this object into a Parcel.
     *
     * @param out The Parcel in which the object should be written.
     * @param flags Additional flags about how the object should be written.
     *              May be {@code 0} or {@link #PARCELABLE_WRITE_RETURN_VALUE}
     */
    @Override
    public void writeToParcel(@NonNull Parcel out, int flags) {
        try {
            mWrapped.writeToParcel(out, flags);
        } finally {
            // success
        }
    }

    public static final @NonNull Parcelable.Creator<SyncFence> CREATOR =
            new Parcelable.Creator<SyncFence>() {
        @Override
        public SyncFence createFromParcel(Parcel in) {
            return new SyncFence(ParcelFileDescriptor.CREATOR.createFromParcel(in));
        }

        @Override
        public SyncFence[] newArray(int size) {
            return new SyncFence[size];
        }
    };
}
+7 −6
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.annotation.RequiresPermission;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.SyncFence;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
@@ -58,7 +59,6 @@ import android.media.ImageReader;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import android.util.Size;
@@ -817,13 +817,14 @@ public final class CameraAdvancedExtensionSessionImpl extends CameraExtensionSes

            ParcelImage parcelImage = new ParcelImage();
            parcelImage.buffer = img.getHardwareBuffer();
            if (img.getFenceFd() >= 0) {
            try {
                    parcelImage.fence = ParcelFileDescriptor.fromFd(img.getFenceFd());
                SyncFence fd = img.getFence();
                if (fd.isValid()) {
                    parcelImage.fence = fd.getFdDup();
                }
            } catch (IOException e) {
                Log.e(TAG, "Failed to parcel buffer fence!");
            }
            }
            parcelImage.width = img.getWidth();
            parcelImage.height = img.getHeight();
            parcelImage.format = img.getFormat();
+11 −10
Original line number Diff line number Diff line
@@ -16,10 +16,14 @@

package android.hardware.camera2.impl;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.HardwareBuffer;
import android.hardware.SyncFence;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
@@ -30,6 +34,7 @@ import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureFailure;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.extension.CaptureBundle;
import android.hardware.camera2.extension.CaptureStageImpl;
import android.hardware.camera2.extension.ICaptureProcessorImpl;
@@ -38,7 +43,6 @@ import android.hardware.camera2.extension.IInitializeSessionCallback;
import android.hardware.camera2.extension.IPreviewExtenderImpl;
import android.hardware.camera2.extension.IRequestUpdateProcessorImpl;
import android.hardware.camera2.extension.ParcelImage;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.DynamicRangeProfiles;
import android.hardware.camera2.params.ExtensionSessionConfiguration;
import android.hardware.camera2.params.OutputConfiguration;
@@ -50,11 +54,7 @@ import android.media.ImageWriter;
import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.util.Log;
import android.util.LongSparseArray;
import android.util.Pair;
@@ -1656,13 +1656,14 @@ public final class CameraExtensionSessionImpl extends CameraExtensionSession {
    private static ParcelImage initializeParcelImage(Image img) {
        ParcelImage parcelImage = new ParcelImage();
        parcelImage.buffer = img.getHardwareBuffer();
        if (img.getFenceFd() >= 0) {
        try {
                parcelImage.fence = ParcelFileDescriptor.fromFd(img.getFenceFd());
            SyncFence fd = img.getFence();
            if (fd.isValid()) {
                parcelImage.fence = fd.getFdDup();
            }
        } catch (IOException e) {
            Log.e(TAG, "Failed to parcel buffer fence!");
        }
        }
        parcelImage.width = img.getWidth();
        parcelImage.height = img.getHeight();
        parcelImage.format = img.getFormat();
+24 −5
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.media;

import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.SuppressLint;
import android.annotation.TestApi;
@@ -24,7 +25,9 @@ import android.graphics.Rect;
import android.hardware.DataSpace;
import android.hardware.DataSpace.NamedDataSpace;
import android.hardware.HardwareBuffer;
import android.hardware.SyncFence;

import java.io.IOException;
import java.nio.ByteBuffer;

/**
@@ -223,12 +226,17 @@ public abstract class Image implements AutoCloseable {
    public abstract int getScalingMode();

    /**
     * Get the fence file descriptor associated with this frame.
     * @return The fence file descriptor for this frame.
     * @hide
     * Get the SyncFence object associated with this frame.
     *
     * <p>This function returns an invalid SyncFence after {@link #getPlanes()} on the image
     * dequeued from {@link ImageWriter} via {@link ImageWriter#dequeueInputImage()}.</p>
     *
     * @return The SyncFence for this frame.
     * @throws IOException if there is an error when a SyncFence object returns.
     * @see android.hardware.SyncFence
     */
    public int getFenceFd() {
        return -1;
    public @NonNull SyncFence getFence() throws IOException {
        return SyncFence.createEmpty();
    }

    /**
@@ -283,6 +291,17 @@ public abstract class Image implements AutoCloseable {
        return;
    }

    /**
     * Set the fence file descriptor with this frame.
     * @param fence The fence file descriptor to be set for this frame.
     * @throws IOException if there is an error when setting a SyncFence.
     * @see android.hardware.SyncFence
     */
    public void setFence(@NonNull SyncFence fence) throws IOException {
        throwISEIfImageIsInvalid();
        return;
    }

    private @NamedDataSpace long mDataSpace = DataSpace.DATASPACE_UNKNOWN;

    /**
Loading