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

Commit 69e586c6 authored by John Reck's avatar John Reck Committed by Android (Google) Code Review
Browse files

Merge "Revert "Created HardwareBufferRenderer to support rendering into...""

parents 41f9aa41 cca989f2
Loading
Loading
Loading
Loading
+0 −23
Original line number Diff line number Diff line
@@ -15312,29 +15312,6 @@ package android.graphics {
    ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
  }
  public class HardwareBufferRenderer implements java.lang.AutoCloseable {
    ctor public HardwareBufferRenderer(@NonNull android.hardware.HardwareBuffer);
    method public void close();
    method public boolean isClosed();
    method @NonNull public android.graphics.HardwareBufferRenderer.RenderRequest obtainRenderRequest();
    method public void setContentRoot(@Nullable android.graphics.RenderNode);
    method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
    method public void setLightSourceGeometry(float, float, @FloatRange(from=0.0f) float, @FloatRange(from=0.0f) float);
  }
  public final class HardwareBufferRenderer.RenderRequest {
    method public void draw(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.HardwareBufferRenderer.RenderResult>);
    method @NonNull public android.graphics.HardwareBufferRenderer.RenderRequest setBufferTransform(int);
    method @NonNull public android.graphics.HardwareBufferRenderer.RenderRequest setColorSpace(@Nullable android.graphics.ColorSpace);
  }
  public static final class HardwareBufferRenderer.RenderResult {
    method @NonNull public android.hardware.SyncFence getFence();
    method public int getStatus();
    field public static final int ERROR_UNKNOWN = 1; // 0x1
    field public static final int SUCCESS = 0; // 0x0
  }
  public class HardwareRenderer {
    ctor public HardwareRenderer();
    method public void clearContent();
+5 −17
Original line number Diff line number Diff line
@@ -87,8 +87,8 @@ public final class SyncFence implements AutoCloseable, Parcelable {
    // is well worth making.
    private final Runnable mCloser;

    private SyncFence(int fileDescriptor) {
        mNativePtr = nCreate(fileDescriptor);
    private SyncFence(@NonNull ParcelFileDescriptor wrapped) {
        mNativePtr = nCreate(wrapped.detachFd());
        mCloser = sRegistry.registerNativeAllocation(this, mNativePtr);
    }

@@ -136,26 +136,14 @@ public final class SyncFence implements AutoCloseable, Parcelable {
    }

    /**
     * Create a new SyncFence wrapped around another {@link ParcelFileDescriptor}. By default, all
     * method calls are delegated to the wrapped descriptor. This takes ownership of the
     * {@link ParcelFileDescriptor}.
     * 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.detachFd());
    }

    /**
     * Create a new SyncFence wrapped around another descriptor. The returned {@link SyncFence}
     * instance takes ownership of the file descriptor.
     *
     * @param fileDescriptor The descriptor to be wrapped.
     * @hide
     */
    public static @NonNull SyncFence adopt(int fileDescriptor) {
        return new SyncFence(fileDescriptor);
        return new SyncFence(wrapped);
    }

    /**
+0 −1
Original line number Diff line number Diff line
@@ -102,7 +102,6 @@ extern int register_android_view_KeyCharacterMap(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env);
extern int register_android_view_VelocityTracker(JNIEnv* env);
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);

+0 −390
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.graphics;

import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.graphics.ColorSpace.Named;
import android.hardware.HardwareBuffer;
import android.hardware.SyncFence;
import android.view.SurfaceControl;

import libcore.util.NativeAllocationRegistry;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.concurrent.Executor;
import java.util.function.Consumer;

/**
 * <p>Creates an instance of a hardware-accelerated renderer. This is used to render a scene built
 * from {@link RenderNode}s to an output {@link HardwareBuffer}. There can be as many
 * HardwareBufferRenderer instances as desired.</p>
 *
 * <h3>Resources & lifecycle</h3>
 *
 * <p>All HardwareBufferRenderer and {@link HardwareRenderer} instances share a common render
 * thread. Therefore HardwareBufferRenderer will share common resources and GPU utilization with
 * hardware accelerated rendering initiated by the UI thread of an application.
 * The render thread contains the GPU context & resources necessary to do GPU-accelerated
 * rendering. As such, the first HardwareBufferRenderer created comes with the cost of also creating
 * the associated GPU contexts, however each incremental HardwareBufferRenderer thereafter is fairly
 * cheap. The expected usage is to have a HardwareBufferRenderer instance for every active {@link
 * HardwareBuffer}.</p>
 *
 * This is useful in situations where a scene built with {@link RenderNode}s can be consumed
 * directly by the system compositor through
 * {@link SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)}.
 *
 * HardwareBufferRenderer will never clear contents before each draw invocation so previous contents
 * in the {@link HardwareBuffer} target will be preserved across renders.
 */
public class HardwareBufferRenderer implements AutoCloseable {

    private static final ColorSpace DEFAULT_COLORSPACE = ColorSpace.get(Named.SRGB);

    private static class HardwareBufferRendererHolder {
        public static final NativeAllocationRegistry REGISTRY =
                NativeAllocationRegistry.createMalloced(
                    HardwareBufferRenderer.class.getClassLoader(), nGetFinalizer());
    }

    private final HardwareBuffer mHardwareBuffer;
    private final RenderRequest mRenderRequest;
    private final RenderNode mRootNode;
    private final Runnable mCleaner;

    private long mProxy;

    /**
     * Creates a new instance of {@link HardwareBufferRenderer} with the provided {@link
     * HardwareBuffer} as the output of the rendered scene.
     */
    public HardwareBufferRenderer(@NonNull HardwareBuffer buffer) {
        RenderNode rootNode = RenderNode.adopt(nCreateRootRenderNode());
        rootNode.setClipToBounds(false);
        mProxy = nCreateHardwareBufferRenderer(buffer, rootNode.mNativeRenderNode);
        mCleaner = HardwareBufferRendererHolder.REGISTRY.registerNativeAllocation(this, mProxy);
        mRenderRequest = new RenderRequest();
        mRootNode = rootNode;
        mHardwareBuffer = buffer;
    }

    /**
     * Sets the content root to render. It is not necessary to call this whenever the content
     * recording changes. Any mutations to the RenderNode content, or any of the RenderNodes
     * contained within the content node, will be applied whenever a new {@link RenderRequest} is
     * issued via {@link #obtainRenderRequest()} and {@link RenderRequest#draw(Executor,
     * Consumer)}.
     *
     * @param content The content to set as the root RenderNode. If null the content root is removed
     * and the renderer will draw nothing.
     */
    public void setContentRoot(@Nullable RenderNode content) {
        RecordingCanvas canvas = mRootNode.beginRecording();
        if (content != null) {
            canvas.drawRenderNode(content);
        }
        mRootNode.endRecording();
    }

    /**
     * Returns a {@link RenderRequest} that can be used to render into the provided {@link
     * HardwareBuffer}. This is used to synchronize the RenderNode content provided by {@link
     * #setContentRoot(RenderNode)}.
     *
     * @return An instance of {@link RenderRequest}. The instance may be reused for every frame, so
     * the caller should not hold onto it for longer than a single render request.
     */
    @NonNull
    public RenderRequest obtainRenderRequest() {
        mRenderRequest.reset();
        return mRenderRequest;
    }

    /**
     * Returns if the {@link HardwareBufferRenderer} has already been closed. That is
     * {@link HardwareBufferRenderer#close()} has been invoked.
     * @return True if the {@link HardwareBufferRenderer} has been closed, false otherwise.
     */
    public boolean isClosed() {
        return mProxy == 0L;
    }

    /**
     * Releases the resources associated with this {@link HardwareBufferRenderer} instance. **Note**
     * this does not call {@link HardwareBuffer#close()} on the provided {@link HardwareBuffer}
     * instance
     */
    @Override
    public void close() {
        // Note we explicitly call this only here to clean-up potential animator state
        // This is not done as part of the NativeAllocationRegistry as it would invoke animator
        // callbacks on the wrong thread
        nDestroyRootRenderNode(mRootNode.mNativeRenderNode);
        if (mProxy != 0L) {
            mCleaner.run();
            mProxy = 0L;
        }
    }

    /**
     * Sets the center of the light source. The light source point controls the directionality and
     * shape of shadows rendered by RenderNode Z & elevation.
     *
     * <p>The light source should be setup both as part of initial configuration, and whenever
     * the window moves to ensure the light source stays anchored in display space instead of in
     * window space.
     *
     * <p>This must be set at least once along with {@link #setLightSourceAlpha(float, float)}
     * before shadows will work.
     *
     * @param lightX The X position of the light source. If unsure, a reasonable default
     * is 'displayWidth / 2f - windowLeft'.
     * @param lightY The Y position of the light source. If unsure, a reasonable default
     * is '0 - windowTop'
     * @param lightZ The Z position of the light source. Must be >= 0. If unsure, a reasonable
     * default is 600dp.
     * @param lightRadius The radius of the light source. Smaller radius will have sharper edges,
     * larger radius will have softer shadows. If unsure, a reasonable default is 800 dp.
     */
    public void setLightSourceGeometry(
            float lightX,
            float lightY,
            @FloatRange(from = 0f) float lightZ,
            @FloatRange(from = 0f) float lightRadius
    ) {
        validateFinite(lightX, "lightX");
        validateFinite(lightY, "lightY");
        validatePositive(lightZ, "lightZ");
        validatePositive(lightRadius, "lightRadius");
        nSetLightGeometry(mProxy, lightX, lightY, lightZ, lightRadius);
    }

    /**
     * Configures the ambient & spot shadow alphas. This is the alpha used when the shadow has max
     * alpha, and ramps down from the values provided to zero.
     *
     * <p>These values are typically provided by the current theme, see
     * {@link android.R.attr#spotShadowAlpha} and {@link android.R.attr#ambientShadowAlpha}.
     *
     * <p>This must be set at least once along with
     * {@link #setLightSourceGeometry(float, float, float, float)} before shadows will work.
     *
     * @param ambientShadowAlpha The alpha for the ambient shadow. If unsure, a reasonable default
     * is 0.039f.
     * @param spotShadowAlpha The alpha for the spot shadow. If unsure, a reasonable default is
     * 0.19f.
     */
    public void setLightSourceAlpha(@FloatRange(from = 0.0f, to = 1.0f) float ambientShadowAlpha,
            @FloatRange(from = 0.0f, to = 1.0f) float spotShadowAlpha) {
        validateAlpha(ambientShadowAlpha, "ambientShadowAlpha");
        validateAlpha(spotShadowAlpha, "spotShadowAlpha");
        nSetLightAlpha(mProxy, ambientShadowAlpha, spotShadowAlpha);
    }

    /**
     * Class that contains data regarding the result of the render request.
     * Consumers are to wait on the provided {@link SyncFence} before consuming the HardwareBuffer
     * provided to {@link HardwareBufferRenderer} as well as verify that the status returned by
     * {@link RenderResult#getStatus()} returns {@link RenderResult#SUCCESS}.
     */
    public static final class RenderResult {

        /**
         * Render request was completed successfully
         */
        public static final int SUCCESS = 0;

        /**
         * Render request failed with an unknown error
         */
        public static final int ERROR_UNKNOWN = 1;

        /** @hide **/
        @IntDef(value = {SUCCESS, ERROR_UNKNOWN})
        @Retention(RetentionPolicy.SOURCE)
        public @interface RenderResultStatus{}

        private final SyncFence mFence;
        private final int mResultStatus;

        private RenderResult(@NonNull SyncFence fence, @RenderResultStatus int resultStatus) {
            mFence = fence;
            mResultStatus = resultStatus;
        }

        @NonNull
        public SyncFence getFence() {
            return mFence;
        }

        @RenderResultStatus
        public int getStatus() {
            return mResultStatus;
        }
    }

    /**
     * Sets the parameters that can be used to control a render request for a {@link
     * HardwareBufferRenderer}. This is not thread-safe and must not be held on to for longer than a
     * single request.
     */
    public final class RenderRequest {

        private ColorSpace mColorSpace = DEFAULT_COLORSPACE;
        private int mTransform = SurfaceControl.BUFFER_TRANSFORM_IDENTITY;

        private RenderRequest() { }

        /**
         * Syncs the RenderNode tree to the render thread and requests content to be drawn. This
         * {@link RenderRequest} instance should no longer be used after calling this method. The
         * system internally may reuse instances of {@link RenderRequest} to reduce allocation
         * churn.
         *
         * @param executor Executor used to deliver callbacks
         * @param renderCallback Callback invoked when rendering is complete. This includes a
         * {@link RenderResult} that provides a {@link SyncFence} that should be waited upon for
         * completion before consuming the rendered output in the provided {@link HardwareBuffer}
         * instance.
         *
         * @throws IllegalStateException if attempt to draw is made when
         * {@link HardwareBufferRenderer#isClosed()} returns true
         */
        public void draw(
                @NonNull Executor executor,
                @NonNull Consumer<RenderResult> renderCallback
        ) {
            Consumer<RenderResult> wrapped = consumable -> executor.execute(
                    () -> renderCallback.accept(consumable));
            if (!isClosed()) {
                nRender(
                        mProxy,
                        mTransform,
                        mHardwareBuffer.getWidth(),
                        mHardwareBuffer.getHeight(),
                        mColorSpace.getNativeInstance(),
                        wrapped);
            } else {
                throw new IllegalStateException("Attempt to draw with a HardwareBufferRenderer "
                    + "instance that has already been closed");
            }
        }

        private void reset() {
            mColorSpace = DEFAULT_COLORSPACE;
            mTransform = SurfaceControl.BUFFER_TRANSFORM_IDENTITY;
        }

        /**
         * Configures the color space which the content should be rendered in. This affects
         * how the framework will interpret the color at each pixel. The color space provided here
         * must be non-null, RGB based and leverage an ICC parametric curve. The min/max values
         * of the components should not reduce the numerical range compared to the previously
         * assigned color space. If left unspecified, the default color space of SRGB will be used.
         *
         * @param colorSpace The color space the content should be rendered in. If null is provided
         * the default of SRGB will be used.
         */
        @NonNull
        public RenderRequest setColorSpace(@Nullable ColorSpace colorSpace) {
            if (colorSpace == null) {
                mColorSpace = DEFAULT_COLORSPACE;
            } else {
                mColorSpace = colorSpace;
            }
            return this;
        }

        /**
         * Specifies a transform to be applied before content is rendered. This is useful
         * for pre-rotating content for the current display orientation to increase performance
         * of displaying the associated buffer. This transformation will also adjust the light
         * source position for the specified rotation.
         * @see SurfaceControl.Transaction#setBufferTransform(SurfaceControl, int)
         */
        @NonNull
        public RenderRequest setBufferTransform(
                @SurfaceControl.BufferTransform int bufferTransform) {
            boolean validTransform = bufferTransform == SurfaceControl.BUFFER_TRANSFORM_IDENTITY
                    || bufferTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_90
                    || bufferTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_180
                    || bufferTransform == SurfaceControl.BUFFER_TRANSFORM_ROTATE_270;
            if (validTransform) {
                mTransform = bufferTransform;
            } else {
                throw new IllegalArgumentException("Invalid transform provided, must be one of"
                    + "the SurfaceControl.BufferTransform values");
            }
            return this;
        }
    }

    /**
     * @hide
     */
    /* package */
    static native int nRender(long renderer, int transform, int width, int height, long colorSpace,
            Consumer<RenderResult> callback);

    private static native long nCreateRootRenderNode();

    private static native void nDestroyRootRenderNode(long rootRenderNode);

    private static native long nCreateHardwareBufferRenderer(HardwareBuffer buffer,
            long rootRenderNode);

    private static native void nSetLightGeometry(long bufferRenderer, float lightX, float lightY,
            float lightZ, float radius);

    private static native void nSetLightAlpha(long nativeProxy, float ambientShadowAlpha,
            float spotShadowAlpha);

    private static native long nGetFinalizer();

    // Called by native
    private static void invokeRenderCallback(
            @NonNull Consumer<RenderResult> callback,
            int fd,
            int status
    ) {
        callback.accept(new RenderResult(SyncFence.adopt(fd), status));
    }

    private static void validateAlpha(float alpha, String argumentName) {
        if (!(alpha >= 0.0f && alpha <= 1.0f)) {
            throw new IllegalArgumentException(argumentName + " must be a valid alpha, "
                + alpha + " is not in the range of 0.0f to 1.0f");
        }
    }

    private static void validateFinite(float f, String argumentName) {
        if (!Float.isFinite(f)) {
            throw new IllegalArgumentException(argumentName + " must be finite, given=" + f);
        }
    }

    private static void validatePositive(float f, String argumentName) {
        if (!(Float.isFinite(f) && f >= 0.0f)) {
            throw new IllegalArgumentException(argumentName
                + " must be a finite positive, given=" + f);
        }
    }
}
+0 −2
Original line number Diff line number Diff line
@@ -338,7 +338,6 @@ cc_defaults {
        "jni/android_util_PathParser.cpp",

        "jni/Bitmap.cpp",
        "jni/HardwareBufferHelpers.cpp",
        "jni/BitmapFactory.cpp",
        "jni/ByteBufferStreamAdaptor.cpp",
        "jni/Camera.cpp",
@@ -415,7 +414,6 @@ cc_defaults {
                "jni/AnimatedImageDrawable.cpp",
                "jni/android_graphics_TextureLayer.cpp",
                "jni/android_graphics_HardwareRenderer.cpp",
                "jni/android_graphics_HardwareBufferRenderer.cpp",
                "jni/BitmapRegionDecoder.cpp",
                "jni/GIFMovie.cpp",
                "jni/GraphicsStatsService.cpp",
Loading