Loading core/api/current.txt +23 −0 Original line number Diff line number Diff line Loading @@ -15080,6 +15080,29 @@ 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(); core/java/android/hardware/SyncFence.java +17 −5 Original line number Diff line number Diff line Loading @@ -87,8 +87,8 @@ public final class SyncFence implements AutoCloseable, Parcelable { // is well worth making. private final Runnable mCloser; private SyncFence(@NonNull ParcelFileDescriptor wrapped) { mNativePtr = nCreate(wrapped.detachFd()); private SyncFence(int fileDescriptor) { mNativePtr = nCreate(fileDescriptor); mCloser = sRegistry.registerNativeAllocation(this, mNativePtr); } Loading Loading @@ -136,14 +136,26 @@ public final class SyncFence implements AutoCloseable, Parcelable { } /** * Create a new SyncFence wrapped around another descriptor. By default, all method calls are * delegated to the wrapped descriptor. * 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}. * * @param wrapped The descriptor to be wrapped. * @hide */ public static @NonNull SyncFence create(@NonNull ParcelFileDescriptor wrapped) { return new SyncFence(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); } /** Loading core/jni/LayoutlibLoader.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,7 @@ 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); Loading graphics/java/android/graphics/HardwareBufferRenderer.java 0 → 100644 +390 −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.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); } } } libs/hwui/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -329,6 +329,7 @@ cc_defaults { "jni/android_util_PathParser.cpp", "jni/Bitmap.cpp", "jni/HardwareBufferHelpers.cpp", "jni/BitmapFactory.cpp", "jni/ByteBufferStreamAdaptor.cpp", "jni/Camera.cpp", Loading Loading @@ -397,6 +398,7 @@ 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 Loading
core/api/current.txt +23 −0 Original line number Diff line number Diff line Loading @@ -15080,6 +15080,29 @@ 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();
core/java/android/hardware/SyncFence.java +17 −5 Original line number Diff line number Diff line Loading @@ -87,8 +87,8 @@ public final class SyncFence implements AutoCloseable, Parcelable { // is well worth making. private final Runnable mCloser; private SyncFence(@NonNull ParcelFileDescriptor wrapped) { mNativePtr = nCreate(wrapped.detachFd()); private SyncFence(int fileDescriptor) { mNativePtr = nCreate(fileDescriptor); mCloser = sRegistry.registerNativeAllocation(this, mNativePtr); } Loading Loading @@ -136,14 +136,26 @@ public final class SyncFence implements AutoCloseable, Parcelable { } /** * Create a new SyncFence wrapped around another descriptor. By default, all method calls are * delegated to the wrapped descriptor. * 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}. * * @param wrapped The descriptor to be wrapped. * @hide */ public static @NonNull SyncFence create(@NonNull ParcelFileDescriptor wrapped) { return new SyncFence(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); } /** Loading
core/jni/LayoutlibLoader.cpp +1 −0 Original line number Diff line number Diff line Loading @@ -102,6 +102,7 @@ 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); Loading
graphics/java/android/graphics/HardwareBufferRenderer.java 0 → 100644 +390 −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.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); } } }
libs/hwui/Android.bp +2 −0 Original line number Diff line number Diff line Loading @@ -329,6 +329,7 @@ cc_defaults { "jni/android_util_PathParser.cpp", "jni/Bitmap.cpp", "jni/HardwareBufferHelpers.cpp", "jni/BitmapFactory.cpp", "jni/ByteBufferStreamAdaptor.cpp", "jni/Camera.cpp", Loading Loading @@ -397,6 +398,7 @@ 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