diff options
author | 2022-11-15 10:55:54 -0800 | |
---|---|---|
committer | 2023-01-05 19:33:58 -0800 | |
commit | 7612c613c9a5d41892a201eafd4949424497fb5f (patch) | |
tree | 04a7b51e7770bee35a4b57f827bf67742ad5747a | |
parent | 26af2c72fe35e8a3197e3d2189d97c8fad799e3f (diff) |
Created HardwareBufferRenderer to support rendering into
HardwareBuffer targets.
Relnote: "Created HardwareBufferRenderer API to handle
rendering a single frame into a HardwareBuffer target."
Refactored dlsym logic for dynamically resolving AHardwareBuffer
methods to be shared across multiple locations.
Bug: 255692581
Test: Created HardwareBufferRendererTests
Change-Id: I749b5d763a9ee580abc2d6cc87bd94a46b7abdd9
30 files changed, 1200 insertions, 157 deletions
diff --git a/core/api/current.txt b/core/api/current.txt index 5dd1b3938f40..31659e435915 100644 --- a/core/api/current.txt +++ b/core/api/current.txt @@ -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(); diff --git a/core/java/android/hardware/SyncFence.java b/core/java/android/hardware/SyncFence.java index 166001347bd4..d6052cd4c67f 100644 --- a/core/java/android/hardware/SyncFence.java +++ b/core/java/android/hardware/SyncFence.java @@ -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); } @@ -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); } /** diff --git a/core/jni/LayoutlibLoader.cpp b/core/jni/LayoutlibLoader.cpp index 93ba23bfdf84..d7cbf74d421a 100644 --- a/core/jni/LayoutlibLoader.cpp +++ b/core/jni/LayoutlibLoader.cpp @@ -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); diff --git a/graphics/java/android/graphics/HardwareBufferRenderer.java b/graphics/java/android/graphics/HardwareBufferRenderer.java new file mode 100644 index 000000000000..361dc594f2c6 --- /dev/null +++ b/graphics/java/android/graphics/HardwareBufferRenderer.java @@ -0,0 +1,390 @@ +/* + * 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); + } + } +} diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index 3e3d77b89a9a..59e4b7acdba7 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -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", @@ -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", diff --git a/libs/hwui/apex/jni_runtime.cpp b/libs/hwui/apex/jni_runtime.cpp index e6cfa7bcaf70..f57d80c496ad 100644 --- a/libs/hwui/apex/jni_runtime.cpp +++ b/libs/hwui/apex/jni_runtime.cpp @@ -83,6 +83,7 @@ extern int register_android_util_PathParser(JNIEnv* env); extern int register_android_view_DisplayListCanvas(JNIEnv* env); extern int register_android_view_RenderNode(JNIEnv* env); extern int register_android_view_ThreadedRenderer(JNIEnv* env); +extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env); #ifdef NDEBUG #define REG_JNI(name) { name } @@ -151,6 +152,8 @@ extern int register_android_view_ThreadedRenderer(JNIEnv* env); REG_JNI(register_android_util_PathParser), REG_JNI(register_android_view_RenderNode), REG_JNI(register_android_view_DisplayListCanvas), + REG_JNI(register_android_graphics_HardwareBufferRenderer), + REG_JNI(register_android_view_ThreadedRenderer), }; diff --git a/libs/hwui/jni/Bitmap.cpp b/libs/hwui/jni/Bitmap.cpp index 94cea65897cf..540abec38ebc 100755 --- a/libs/hwui/jni/Bitmap.cpp +++ b/libs/hwui/jni/Bitmap.cpp @@ -2,48 +2,41 @@ #define LOG_TAG "Bitmap" #include "Bitmap.h" +#include <hwui/Bitmap.h> +#include <hwui/Paint.h> + +#include "CreateJavaOutputStreamAdaptor.h" #include "GraphicsJNI.h" +#include "HardwareBufferHelpers.h" #include "SkBitmap.h" #include "SkBlendMode.h" #include "SkCanvas.h" #include "SkColor.h" #include "SkColorSpace.h" #include "SkData.h" -#include "SkImageEncoder.h" #include "SkImageInfo.h" #include "SkPaint.h" -#include "SkPixelRef.h" #include "SkPixmap.h" #include "SkPoint.h" #include "SkRefCnt.h" #include "SkStream.h" #include "SkTypes.h" -#include "SkWebpEncoder.h" - - #include "android_nio_utils.h" -#include "CreateJavaOutputStreamAdaptor.h" -#include <hwui/Paint.h> -#include <hwui/Bitmap.h> -#include <utils/Color.h> #ifdef __ANDROID__ // Layoutlib does not support graphic buffer, parcel or render thread #include <android-base/unique_fd.h> #include <android/binder_parcel.h> #include <android/binder_parcel_jni.h> #include <android/binder_parcel_platform.h> -#include <android/binder_parcel_utils.h> -#include <private/android/AHardwareBufferHelpers.h> #include <cutils/ashmem.h> -#include <dlfcn.h> #include <renderthread/RenderProxy.h> #include <sys/mman.h> #endif #include <inttypes.h> #include <string.h> + #include <memory> -#include <string> #define DEBUG_PARCEL 0 @@ -1197,18 +1190,11 @@ static jobject Bitmap_copyPreserveInternalConfig(JNIEnv* env, jobject, jlong bit return createBitmap(env, bitmap.release(), getPremulBitmapCreateFlags(false)); } -#ifdef __ANDROID__ // Layoutlib does not support graphic buffer -typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject); -AHB_from_HB AHardwareBuffer_fromHardwareBuffer; - -typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*); -AHB_to_HB AHardwareBuffer_toHardwareBuffer; -#endif - static jobject Bitmap_wrapHardwareBufferBitmap(JNIEnv* env, jobject, jobject hardwareBuffer, jlong colorSpacePtr) { #ifdef __ANDROID__ // Layoutlib does not support graphic buffer - AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer); + AHardwareBuffer* buffer = uirenderer::HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer( + env, hardwareBuffer); sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer, GraphicsJNI::getNativeColorSpace(colorSpacePtr)); if (!bitmap.get()) { @@ -1231,7 +1217,8 @@ static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) { } Bitmap& bitmap = bitmapHandle->bitmap(); - return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer()); + return uirenderer::HardwareBufferHelpers::AHardwareBuffer_toHardwareBuffer( + env, bitmap.hardwareBuffer()); #else return nullptr; #endif @@ -1329,18 +1316,7 @@ int register_android_graphics_Bitmap(JNIEnv* env) gBitmap_nativePtr = GetFieldIDOrDie(env, gBitmap_class, "mNativePtr", "J"); gBitmap_constructorMethodID = GetMethodIDOrDie(env, gBitmap_class, "<init>", "(JIIIZ[BLandroid/graphics/NinePatch$InsetStruct;Z)V"); gBitmap_reinitMethodID = GetMethodIDOrDie(env, gBitmap_class, "reinit", "(IIZ)V"); - -#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel - void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); - AHardwareBuffer_fromHardwareBuffer = - (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer"); - LOG_ALWAYS_FATAL_IF(AHardwareBuffer_fromHardwareBuffer == nullptr, - "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!"); - - AHardwareBuffer_toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); - LOG_ALWAYS_FATAL_IF(AHardwareBuffer_toHardwareBuffer == nullptr, - " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); -#endif + uirenderer::HardwareBufferHelpers::init(); return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods, NELEM(gBitmapMethods)); } diff --git a/libs/hwui/jni/GraphicsJNI.h b/libs/hwui/jni/GraphicsJNI.h index 085a905abaf8..c4a61ccd1e5f 100644 --- a/libs/hwui/jni/GraphicsJNI.h +++ b/libs/hwui/jni/GraphicsJNI.h @@ -2,19 +2,18 @@ #define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_ #include <cutils/compiler.h> +#include <hwui/Bitmap.h> +#include <hwui/Canvas.h> -#include "Bitmap.h" #include "BRDAllocator.h" +#include "Bitmap.h" #include "SkBitmap.h" #include "SkCodec.h" -#include "SkPixelRef.h" +#include "SkColorSpace.h" #include "SkMallocPixelRef.h" +#include "SkPixelRef.h" #include "SkPoint.h" #include "SkRect.h" -#include "SkColorSpace.h" -#include <hwui/Canvas.h> -#include <hwui/Bitmap.h> - #include "graphics_jni_helpers.h" class SkCanvas; @@ -335,6 +334,26 @@ private: int fLen; }; +class JGlobalRefHolder { +public: + JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} + + virtual ~JGlobalRefHolder() { + GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject); + mObject = nullptr; + } + + jobject object() { return mObject; } + JavaVM* vm() { return mVm; } + +private: + JGlobalRefHolder(const JGlobalRefHolder&) = delete; + void operator=(const JGlobalRefHolder&) = delete; + + JavaVM* mVm; + jobject mObject; +}; + void doThrowNPE(JNIEnv* env); void doThrowAIOOBE(JNIEnv* env); // Array Index Out Of Bounds Exception void doThrowIAE(JNIEnv* env, const char* msg = NULL); // Illegal Argument diff --git a/libs/hwui/jni/HardwareBufferHelpers.cpp b/libs/hwui/jni/HardwareBufferHelpers.cpp new file mode 100644 index 000000000000..7e3f771b6b3d --- /dev/null +++ b/libs/hwui/jni/HardwareBufferHelpers.cpp @@ -0,0 +1,68 @@ +/* + * 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. + */ + +#include "HardwareBufferHelpers.h" + +#include <dlfcn.h> +#include <log/log.h> + +#ifdef __ANDROID__ +typedef AHardwareBuffer* (*AHB_from_HB)(JNIEnv*, jobject); +typedef jobject (*AHB_to_HB)(JNIEnv*, AHardwareBuffer*); +static AHB_from_HB fromHardwareBuffer = nullptr; +static AHB_to_HB toHardwareBuffer = nullptr; +#endif + +void android::uirenderer::HardwareBufferHelpers::init() { +#ifdef __ANDROID__ // Layoutlib does not support graphic buffer or parcel + void* handle_ = dlopen("libandroid.so", RTLD_NOW | RTLD_NODELETE); + fromHardwareBuffer = (AHB_from_HB)dlsym(handle_, "AHardwareBuffer_fromHardwareBuffer"); + LOG_ALWAYS_FATAL_IF(fromHardwareBuffer == nullptr, + "Failed to find required symbol AHardwareBuffer_fromHardwareBuffer!"); + + toHardwareBuffer = (AHB_to_HB)dlsym(handle_, "AHardwareBuffer_toHardwareBuffer"); + LOG_ALWAYS_FATAL_IF(toHardwareBuffer == nullptr, + " Failed to find required symbol AHardwareBuffer_toHardwareBuffer!"); +#endif +} + +AHardwareBuffer* android::uirenderer::HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer( + JNIEnv* env, jobject hardwarebuffer) { +#ifdef __ANDROID__ + LOG_ALWAYS_FATAL_IF(fromHardwareBuffer == nullptr, + "Failed to find symbol AHardwareBuffer_fromHardwareBuffer, did you forget " + "to call HardwareBufferHelpers::init?"); + return fromHardwareBuffer(env, hardwarebuffer); +#else + ALOGE("ERROR attempting to invoke AHardwareBuffer_fromHardwareBuffer on non Android " + "configuration"); + return nullptr; +#endif +} + +jobject android::uirenderer::HardwareBufferHelpers::AHardwareBuffer_toHardwareBuffer( + JNIEnv* env, AHardwareBuffer* ahardwarebuffer) { +#ifdef __ANDROID__ + LOG_ALWAYS_FATAL_IF(toHardwareBuffer == nullptr, + "Failed to find symbol AHardwareBuffer_toHardwareBuffer, did you forget to " + "call HardwareBufferHelpers::init?"); + return toHardwareBuffer(env, ahardwarebuffer); +#else + ALOGE("ERROR attempting to invoke AHardwareBuffer_toHardwareBuffer on non Android " + "configuration"); + return nullptr; +#endif +}
\ No newline at end of file diff --git a/libs/hwui/jni/HardwareBufferHelpers.h b/libs/hwui/jni/HardwareBufferHelpers.h new file mode 100644 index 000000000000..326babfb0b34 --- /dev/null +++ b/libs/hwui/jni/HardwareBufferHelpers.h @@ -0,0 +1,38 @@ +/* + * 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. + */ +#ifndef HARDWAREBUFFER_JNI_HELPERS_H +#define HARDWAREBUFFER_JNI_HELPERS_H + +#include <android/bitmap.h> +#include <jni.h> + +namespace android { +namespace uirenderer { + +class HardwareBufferHelpers { +public: + static void init(); + static AHardwareBuffer* AHardwareBuffer_fromHardwareBuffer(JNIEnv*, jobject); + static jobject AHardwareBuffer_toHardwareBuffer(JNIEnv*, AHardwareBuffer*); + +private: + HardwareBufferHelpers() = default; // not to be instantiated +}; + +} // namespace uirenderer +} // namespace android + +#endif // HARDWAREBUFFER_JNI_HELPERS_H
\ No newline at end of file diff --git a/libs/hwui/jni/JvmErrorReporter.h b/libs/hwui/jni/JvmErrorReporter.h new file mode 100644 index 000000000000..5e10b9d93275 --- /dev/null +++ b/libs/hwui/jni/JvmErrorReporter.h @@ -0,0 +1,44 @@ +/* + * 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. + */ +#ifndef JVMERRORREPORTER_H +#define JVMERRORREPORTER_H + +#include <TreeInfo.h> +#include <jni.h> +#include <nativehelper/JNIHelp.h> + +#include "GraphicsJNI.h" + +namespace android { +namespace uirenderer { + +class JvmErrorReporter : public android::uirenderer::ErrorHandler { +public: + JvmErrorReporter(JNIEnv* env) { env->GetJavaVM(&mVm); } + + virtual void onError(const std::string& message) override { + JNIEnv* env = GraphicsJNI::getJNIEnv(); + jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); + } + +private: + JavaVM* mVm; +}; + +} // namespace uirenderer +} // namespace android + +#endif // JVMERRORREPORTER_H diff --git a/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp new file mode 100644 index 000000000000..4886fdd7ac67 --- /dev/null +++ b/libs/hwui/jni/android_graphics_HardwareBufferRenderer.cpp @@ -0,0 +1,177 @@ +/* + * Copyright (C) 2010 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. + */ + +#undef LOG_TAG +#define LOG_TAG "HardwareBufferRenderer" +#define ATRACE_TAG ATRACE_TAG_VIEW + +#include <GraphicsJNI.h> +#include <RootRenderNode.h> +#include <TreeInfo.h> +#include <android-base/unique_fd.h> +#include <android/native_window.h> +#include <nativehelper/JNIPlatformHelp.h> +#include <renderthread/CanvasContext.h> +#include <renderthread/RenderProxy.h> +#include <renderthread/RenderThread.h> + +#include "HardwareBufferHelpers.h" +#include "JvmErrorReporter.h" + +namespace android { + +using namespace android::uirenderer; +using namespace android::uirenderer::renderthread; + +struct { + jclass clazz; + jmethodID invokeRenderCallback; +} gHardwareBufferRendererClassInfo; + +static RenderCallback createRenderCallback(JNIEnv* env, jobject releaseCallback) { + if (releaseCallback == nullptr) return nullptr; + + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = + std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(releaseCallback)); + return [globalCallbackRef](android::base::unique_fd&& fd, int status) { + GraphicsJNI::getJNIEnv()->CallStaticVoidMethod( + gHardwareBufferRendererClassInfo.clazz, + gHardwareBufferRendererClassInfo.invokeRenderCallback, globalCallbackRef->object(), + reinterpret_cast<jint>(fd.release()), reinterpret_cast<jint>(status)); + }; +} + +static long android_graphics_HardwareBufferRenderer_createRootNode(JNIEnv* env, jobject) { + auto* node = new RootRenderNode(std::make_unique<JvmErrorReporter>(env)); + node->incStrong(nullptr); + node->setName("RootRenderNode"); + return reinterpret_cast<jlong>(node); +} + +static void android_graphics_hardwareBufferRenderer_destroyRootNode(JNIEnv*, jobject, + jlong renderNodePtr) { + auto* node = reinterpret_cast<RootRenderNode*>(renderNodePtr); + node->destroy(); +} + +static long android_graphics_HardwareBufferRenderer_create(JNIEnv* env, jobject, jobject buffer, + jlong renderNodePtr) { + auto* hardwareBuffer = HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer(env, buffer); + auto* rootRenderNode = reinterpret_cast<RootRenderNode*>(renderNodePtr); + ContextFactoryImpl factory(rootRenderNode); + auto* proxy = new RenderProxy(true, rootRenderNode, &factory); + proxy->setHardwareBuffer(hardwareBuffer); + return (jlong)proxy; +} + +static void HardwareBufferRenderer_destroy(jobject renderProxy) { + auto* proxy = reinterpret_cast<RenderProxy*>(renderProxy); + delete proxy; +} + +static SkMatrix createMatrixFromBufferTransform(SkScalar width, SkScalar height, int transform) { + auto matrix = SkMatrix(); + switch (transform) { + case ANATIVEWINDOW_TRANSFORM_ROTATE_90: + matrix.setRotate(90); + matrix.postTranslate(width, 0); + break; + case ANATIVEWINDOW_TRANSFORM_ROTATE_180: + matrix.setRotate(180); + matrix.postTranslate(width, height); + break; + case ANATIVEWINDOW_TRANSFORM_ROTATE_270: + matrix.setRotate(270); + matrix.postTranslate(0, width); + break; + default: + ALOGE("Invalid transform provided. Transform should be validated from" + "the java side. Leveraging identity transform as a fallback"); + [[fallthrough]]; + case ANATIVEWINDOW_TRANSFORM_IDENTITY: + break; + } + return matrix; +} + +static int android_graphics_HardwareBufferRenderer_render(JNIEnv* env, jobject, jobject renderProxy, + jint transform, jint width, jint height, + jlong colorspacePtr, jobject consumer) { + auto* proxy = reinterpret_cast<RenderProxy*>(renderProxy); + auto skWidth = static_cast<SkScalar>(width); + auto skHeight = static_cast<SkScalar>(height); + auto matrix = createMatrixFromBufferTransform(skWidth, skHeight, transform); + auto colorSpace = GraphicsJNI::getNativeColorSpace(colorspacePtr); + proxy->setHardwareBufferRenderParams( + HardwareBufferRenderParams(matrix, colorSpace, createRenderCallback(env, consumer))); + return proxy->syncAndDrawFrame(); +} + +static void android_graphics_HardwareBufferRenderer_setLightGeometry(JNIEnv*, jobject, + jobject renderProxyPtr, + jfloat lightX, jfloat lightY, + jfloat lightZ, + jfloat lightRadius) { + auto* proxy = reinterpret_cast<RenderProxy*>(renderProxyPtr); + proxy->setLightGeometry((Vector3){lightX, lightY, lightZ}, lightRadius); +} + +static void android_graphics_HardwareBufferRenderer_setLightAlpha(JNIEnv* env, jobject, + jobject renderProxyPtr, + jfloat ambientShadowAlpha, + jfloat spotShadowAlpha) { + auto* proxy = reinterpret_cast<RenderProxy*>(renderProxyPtr); + proxy->setLightAlpha((uint8_t)(255 * ambientShadowAlpha), (uint8_t)(255 * spotShadowAlpha)); +} + +static jlong android_graphics_HardwareBufferRenderer_getFinalizer() { + return static_cast<jlong>(reinterpret_cast<uintptr_t>(&HardwareBufferRenderer_destroy)); +} + +// ---------------------------------------------------------------------------- +// JNI Glue +// ---------------------------------------------------------------------------- + +const char* const kClassPathName = "android/graphics/HardwareBufferRenderer"; + +static const JNINativeMethod gMethods[] = { + {"nCreateHardwareBufferRenderer", "(Landroid/hardware/HardwareBuffer;J)J", + (void*)android_graphics_HardwareBufferRenderer_create}, + {"nRender", "(JIIIJLjava/util/function/Consumer;)I", + (void*)android_graphics_HardwareBufferRenderer_render}, + {"nCreateRootRenderNode", "()J", + (void*)android_graphics_HardwareBufferRenderer_createRootNode}, + {"nSetLightGeometry", "(JFFFF)V", + (void*)android_graphics_HardwareBufferRenderer_setLightGeometry}, + {"nSetLightAlpha", "(JFF)V", (void*)android_graphics_HardwareBufferRenderer_setLightAlpha}, + {"nGetFinalizer", "()J", (void*)android_graphics_HardwareBufferRenderer_getFinalizer}, + {"nDestroyRootRenderNode", "(J)V", + (void*)android_graphics_hardwareBufferRenderer_destroyRootNode}}; + +int register_android_graphics_HardwareBufferRenderer(JNIEnv* env) { + jclass hardwareBufferRendererClazz = + FindClassOrDie(env, "android/graphics/HardwareBufferRenderer"); + gHardwareBufferRendererClassInfo.clazz = hardwareBufferRendererClazz; + gHardwareBufferRendererClassInfo.invokeRenderCallback = + GetStaticMethodIDOrDie(env, hardwareBufferRendererClazz, "invokeRenderCallback", + "(Ljava/util/function/Consumer;II)V"); + HardwareBufferHelpers::init(); + return RegisterMethodsOrDie(env, kClassPathName, gMethods, NELEM(gMethods)); +} + +} // namespace android
\ No newline at end of file diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 0663121a4027..47e2edb2ed0f 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -56,6 +56,7 @@ #include <atomic> #include <vector> +#include "JvmErrorReporter.h" #include "android_graphics_HardwareRendererObserver.h" namespace android { @@ -93,35 +94,12 @@ struct { jmethodID getDestinationBitmap; } gCopyRequest; -static JNIEnv* getenv(JavaVM* vm) { - JNIEnv* env; - if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) { - LOG_ALWAYS_FATAL("Failed to get JNIEnv for JavaVM: %p", vm); - } - return env; -} - typedef ANativeWindow* (*ANW_fromSurface)(JNIEnv* env, jobject surface); ANW_fromSurface fromSurface; -class JvmErrorReporter : public ErrorHandler { -public: - JvmErrorReporter(JNIEnv* env) { - env->GetJavaVM(&mVm); - } - - virtual void onError(const std::string& message) override { - JNIEnv* env = getenv(mVm); - jniThrowException(env, "java/lang/IllegalStateException", message.c_str()); - } -private: - JavaVM* mVm; -}; - class FrameCommitWrapper : public LightRefBase<FrameCommitWrapper> { public: explicit FrameCommitWrapper(JNIEnv* env, jobject jobject) { - env->GetJavaVM(&mVm); mObject = env->NewGlobalRef(jobject); LOG_ALWAYS_FATAL_IF(!mObject, "Failed to make global ref"); } @@ -131,19 +109,18 @@ public: void onFrameCommit(bool didProduceBuffer) { if (mObject) { ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer); - getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit, - didProduceBuffer); + GraphicsJNI::getJNIEnv()->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit, + didProduceBuffer); releaseObject(); } } private: - JavaVM* mVm; jobject mObject; void releaseObject() { if (mObject) { - getenv(mVm)->DeleteGlobalRef(mObject); + GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject); mObject = nullptr; } } @@ -443,26 +420,6 @@ static void android_view_ThreadedRenderer_forceDrawNextFrame(JNIEnv* env, jobjec proxy->forceDrawNextFrame(); } -class JGlobalRefHolder { -public: - JGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm), mObject(object) {} - - virtual ~JGlobalRefHolder() { - getenv(mVm)->DeleteGlobalRef(mObject); - mObject = nullptr; - } - - jobject object() { return mObject; } - JavaVM* vm() { return mVm; } - -private: - JGlobalRefHolder(const JGlobalRefHolder&) = delete; - void operator=(const JGlobalRefHolder&) = delete; - - JavaVM* mVm; - jobject mObject; -}; - using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>; struct PictureCaptureState { @@ -578,7 +535,7 @@ static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv* auto pictureState = std::make_shared<PictureCaptureState>(); proxy->setPictureCapturedCallback([globalCallbackRef, pictureState](sk_sp<SkPicture>&& picture) { - JNIEnv* env = getenv(globalCallbackRef->vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); Picture* wrapper = new PictureWrapper{std::move(picture), pictureState}; env->CallStaticVoidMethod(gHardwareRenderer.clazz, gHardwareRenderer.invokePictureCapturedCallback, @@ -600,7 +557,7 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback( vm, env->NewGlobalRef(aSurfaceTransactionCallback)); proxy->setASurfaceTransactionCallback( [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) -> bool { - JNIEnv* env = getenv(globalCallbackRef->vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); jboolean ret = env->CallBooleanMethod( globalCallbackRef->object(), gASurfaceTransactionCallback.onMergeTransaction, @@ -622,7 +579,7 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback)); proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() { - JNIEnv* env = getenv(globalCallbackRef->vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); env->CallVoidMethod(globalCallbackRef->object(), gPrepareSurfaceControlForWebviewCallback.prepare); }); @@ -641,7 +598,7 @@ static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, env->NewGlobalRef(frameCallback)); proxy->setFrameCallback([globalCallbackRef](int32_t syncResult, int64_t frameNr) -> std::function<void(bool)> { - JNIEnv* env = getenv(globalCallbackRef->vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); ScopedLocalRef<jobject> frameCommitCallback( env, env->CallObjectMethod( globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw, @@ -680,7 +637,7 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env, auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback)); proxy->setFrameCompleteCallback([globalCallbackRef]() { - JNIEnv* env = getenv(globalCallbackRef->vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); env->CallVoidMethod(globalCallbackRef->object(), gFrameCompleteCallback.onFrameComplete); }); @@ -693,7 +650,7 @@ public: : CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {} virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override { - JNIEnv* env = getenv(mRefHolder.vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); jlong bitmapPtr = env->CallLongMethod( mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight); SkBitmap bitmap; @@ -702,7 +659,7 @@ public: } virtual void onCopyFinished(CopyResult result) override { - JNIEnv* env = getenv(mRefHolder.vm()); + JNIEnv* env = GraphicsJNI::getJNIEnv(); env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished, static_cast<jint>(result)); } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp index 19cd7bdd6fcb..202a62cf320c 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.cpp @@ -55,7 +55,9 @@ SkiaOpenGLPipeline::~SkiaOpenGLPipeline() { MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() { // In case the surface was destroyed (e.g. a previous trimMemory call) we // need to recreate it here. - if (!isSurfaceReady() && mNativeWindow) { + if (mHardwareBuffer) { + mRenderThread.requireGlContext(); + } else if (!isSurfaceReady() && mNativeWindow) { setSurface(mNativeWindow.get(), mSwapBehavior); } @@ -67,17 +69,24 @@ MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() { } Frame SkiaOpenGLPipeline::getFrame() { - LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, - "drawRenderNode called on a context with no surface!"); - return mEglManager.beginFrame(mEglSurface); + if (mHardwareBuffer) { + AHardwareBuffer_Desc description; + AHardwareBuffer_describe(mHardwareBuffer, &description); + return Frame(description.width, description.height, 0); + } else { + LOG_ALWAYS_FATAL_IF(mEglSurface == EGL_NO_SURFACE, + "drawRenderNode called on a context with no surface!"); + return mEglManager.beginFrame(mEglSurface); + } } IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, - const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { - if (!isCapturingSkp()) { + const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, + const HardwareBufferRenderParams& bufferParams) { + if (!isCapturingSkp() && !mHardwareBuffer) { mEglManager.damageFrame(frame, dirty); } @@ -104,13 +113,25 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw( SkSurfaceProps props(0, kUnknown_SkPixelGeometry); SkASSERT(mRenderThread.getGrContext() != nullptr); - sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget( - mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType, - mSurfaceColorSpace, &props)); + sk_sp<SkSurface> surface; + SkMatrix preTransform; + if (mHardwareBuffer) { + surface = getBufferSkSurface(bufferParams); + preTransform = bufferParams.getTransform(); + } else { + surface = SkSurface::MakeFromBackendRenderTarget(mRenderThread.getGrContext(), backendRT, + getSurfaceOrigin(), colorType, + mSurfaceColorSpace, &props); + preTransform = SkMatrix::I(); + } - LightingInfo::updateLighting(lightGeometry, lightInfo); + SkPoint lightCenter = preTransform.mapXY(lightGeometry.center.x, lightGeometry.center.y); + LightGeometry localGeometry = lightGeometry; + localGeometry.center.x = lightCenter.fX; + localGeometry.center.y = lightCenter.fY; + LightingInfo::updateLighting(localGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface, - SkMatrix::I()); + preTransform); // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || @@ -142,6 +163,10 @@ bool SkiaOpenGLPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect // metrics the frame was swapped at this point currentFrameInfo->markSwapBuffers(); + if (mHardwareBuffer) { + return false; + } + *requireSwap = drew || mEglManager.damageRequiresSwap(); if (*requireSwap && (CC_UNLIKELY(!mEglManager.swapBuffers(frame, screenDirty)))) { @@ -197,6 +222,26 @@ bool SkiaOpenGLPipeline::setSurface(ANativeWindow* surface, SwapBehavior swapBeh return false; } +[[nodiscard]] android::base::unique_fd SkiaOpenGLPipeline::flush() { + int fence = -1; + EGLSyncKHR sync = EGL_NO_SYNC_KHR; + mEglManager.createReleaseFence(true, &sync, &fence); + // If a sync object is returned here then the device does not support native + // fences, we block on the returned sync and return -1 as a file descriptor + if (sync != EGL_NO_SYNC_KHR) { + EGLDisplay display = mEglManager.eglDisplay(); + EGLint result = eglClientWaitSyncKHR(display, sync, 0, 1000000000); + if (result == EGL_FALSE) { + ALOGE("EglManager::createReleaseFence: error waiting for previous fence: %#x", + eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + ALOGE("EglManager::createReleaseFence: timeout waiting for previous fence"); + } + eglDestroySyncKHR(display, sync); + } + return android::base::unique_fd(fence); +} + bool SkiaOpenGLPipeline::isSurfaceReady() { return CC_UNLIKELY(mEglSurface != EGL_NO_SURFACE); } diff --git a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h index a80c613697f2..940d6bfdb83c 100644 --- a/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaOpenGLPipeline.h @@ -21,6 +21,7 @@ #include "SkiaPipeline.h" #include "renderstate/RenderState.h" +#include "renderthread/HardwareBufferRenderParams.h" namespace android { @@ -36,19 +37,18 @@ public: renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; - renderthread::IRenderPipeline::DrawResult draw(const renderthread::Frame& frame, - const SkRect& screenDirty, const SkRect& dirty, - const LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, - const LightInfo& lightInfo, - const std::vector<sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) override; + renderthread::IRenderPipeline::DrawResult draw( + const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, + const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler, + const renderthread::HardwareBufferRenderParams& bufferParams) override; GrSurfaceOrigin getSurfaceOrigin() override { return kBottomLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override; + [[nodiscard]] android::base::unique_fd flush() override; void onStop() override; bool isSurfaceReady() override; bool isContextReady() override; diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.cpp b/libs/hwui/pipeline/skia/SkiaPipeline.cpp index c546adaaf779..1a336c5855d9 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaPipeline.cpp @@ -604,6 +604,31 @@ void SkiaPipeline::dumpResourceCacheUsage() const { ALOGD("%s", log.c_str()); } +void SkiaPipeline::setHardwareBuffer(AHardwareBuffer* buffer) { + if (mHardwareBuffer) { + AHardwareBuffer_release(mHardwareBuffer); + mHardwareBuffer = nullptr; + } + + if (buffer) { + AHardwareBuffer_acquire(buffer); + mHardwareBuffer = buffer; + } +} + +sk_sp<SkSurface> SkiaPipeline::getBufferSkSurface( + const renderthread::HardwareBufferRenderParams& bufferParams) { + auto bufferColorSpace = bufferParams.getColorSpace(); + if (mBufferSurface == nullptr || mBufferColorSpace == nullptr || + !SkColorSpace::Equals(mBufferColorSpace.get(), bufferColorSpace.get())) { + mBufferSurface = SkSurface::MakeFromAHardwareBuffer( + mRenderThread.getGrContext(), mHardwareBuffer, kTopLeft_GrSurfaceOrigin, + bufferColorSpace, nullptr, true); + mBufferColorSpace = bufferColorSpace; + } + return mBufferSurface; +} + void SkiaPipeline::setSurfaceColorProperties(ColorMode colorMode) { mColorMode = colorMode; switch (colorMode) { diff --git a/libs/hwui/pipeline/skia/SkiaPipeline.h b/libs/hwui/pipeline/skia/SkiaPipeline.h index 7887d1ae2117..4f9334654c9b 100644 --- a/libs/hwui/pipeline/skia/SkiaPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaPipeline.h @@ -20,9 +20,11 @@ #include <SkDocument.h> #include <SkMultiPictureDocument.h> #include <SkSurface.h> + #include "Lighting.h" #include "hwui/AnimatedImageDrawable.h" #include "renderthread/CanvasContext.h" +#include "renderthread/HardwareBufferRenderParams.h" #include "renderthread/IRenderPipeline.h" class SkFILEWStream; @@ -73,11 +75,20 @@ public: mCaptureMode = callback ? CaptureMode::CallbackAPI : CaptureMode::None; } + virtual void setHardwareBuffer(AHardwareBuffer* buffer) override; + bool hasHardwareBuffer() override { return mHardwareBuffer != nullptr; } + protected: + sk_sp<SkSurface> getBufferSkSurface( + const renderthread::HardwareBufferRenderParams& bufferParams); void dumpResourceCacheUsage() const; renderthread::RenderThread& mRenderThread; + AHardwareBuffer* mHardwareBuffer = nullptr; + sk_sp<SkSurface> mBufferSurface = nullptr; + sk_sp<SkColorSpace> mBufferColorSpace = nullptr; + ColorMode mColorMode = ColorMode::Default; SkColorType mSurfaceColorType; sk_sp<SkColorSpace> mSurfaceColorSpace; diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp index f10bca675bf7..b94b6cf0546a 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.cpp @@ -57,37 +57,55 @@ VulkanManager& SkiaVulkanPipeline::vulkanManager() { MakeCurrentResult SkiaVulkanPipeline::makeCurrent() { // In case the surface was destroyed (e.g. a previous trimMemory call) we // need to recreate it here. - if (!isSurfaceReady() && mNativeWindow) { + if (mHardwareBuffer) { + mRenderThread.requireVkContext(); + } else if (!isSurfaceReady() && mNativeWindow) { setSurface(mNativeWindow.get(), SwapBehavior::kSwap_default); } return isContextReady() ? MakeCurrentResult::AlreadyCurrent : MakeCurrentResult::Failed; } Frame SkiaVulkanPipeline::getFrame() { - LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, "getFrame() called on a context with no surface!"); - return vulkanManager().dequeueNextBuffer(mVkSurface); + if (mHardwareBuffer) { + AHardwareBuffer_Desc description; + AHardwareBuffer_describe(mHardwareBuffer, &description); + return Frame(description.width, description.height, 0); + } else { + LOG_ALWAYS_FATAL_IF(mVkSurface == nullptr, + "getFrame() called on a context with no surface!"); + return vulkanManager().dequeueNextBuffer(mVkSurface); + } } IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( const Frame& frame, const SkRect& screenDirty, const SkRect& dirty, const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, - const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) { - sk_sp<SkSurface> backBuffer = mVkSurface->getCurrentSkSurface(); + const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler, + const HardwareBufferRenderParams& bufferParams) { + sk_sp<SkSurface> backBuffer; + SkMatrix preTransform; + if (mHardwareBuffer) { + backBuffer = getBufferSkSurface(bufferParams); + preTransform = bufferParams.getTransform(); + } else { + backBuffer = mVkSurface->getCurrentSkSurface(); + preTransform = mVkSurface->getCurrentPreTransform(); + } + if (backBuffer.get() == nullptr) { return {false, -1}; } // update the coordinates of the global light position based on surface rotation - SkPoint lightCenter = mVkSurface->getCurrentPreTransform().mapXY(lightGeometry.center.x, - lightGeometry.center.y); + SkPoint lightCenter = preTransform.mapXY(lightGeometry.center.x, lightGeometry.center.y); LightGeometry localGeometry = lightGeometry; localGeometry.center.x = lightCenter.fX; localGeometry.center.y = lightCenter.fY; LightingInfo::updateLighting(localGeometry, lightInfo); renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, backBuffer, - mVkSurface->getCurrentPreTransform()); + preTransform); // Draw visual debugging features if (CC_UNLIKELY(Properties::showDirtyRegions || @@ -116,12 +134,16 @@ IRenderPipeline::DrawResult SkiaVulkanPipeline::draw( bool SkiaVulkanPipeline::swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) { - *requireSwap = drew; - // Even if we decided to cancel the frame, from the perspective of jank // metrics the frame was swapped at this point currentFrameInfo->markSwapBuffers(); + if (mHardwareBuffer) { + return false; + } + + *requireSwap = drew; + if (*requireSwap) { vulkanManager().swapBuffers(mVkSurface, screenDirty); } @@ -137,6 +159,12 @@ DeferredLayerUpdater* SkiaVulkanPipeline::createTextureLayer() { void SkiaVulkanPipeline::onStop() {} +[[nodiscard]] android::base::unique_fd SkiaVulkanPipeline::flush() { + int fence = -1; + vulkanManager().createReleaseFence(&fence, mRenderThread.getGrContext()); + return android::base::unique_fd(fence); +} + // We can safely ignore the swap behavior because VkManager will always operate // in a mode equivalent to EGLManager::SwapBehavior::kBufferAge bool SkiaVulkanPipeline::setSurface(ANativeWindow* surface, SwapBehavior /*swapBehavior*/) { diff --git a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h index f3d36131a22f..2c7b268e8174 100644 --- a/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h +++ b/libs/hwui/pipeline/skia/SkiaVulkanPipeline.h @@ -16,14 +16,13 @@ #pragma once +#include "SkRefCnt.h" #include "SkiaPipeline.h" +#include "renderstate/RenderState.h" +#include "renderthread/HardwareBufferRenderParams.h" #include "renderthread/VulkanManager.h" #include "renderthread/VulkanSurface.h" -#include "renderstate/RenderState.h" - -#include "SkRefCnt.h" - class SkBitmap; struct SkRect; @@ -38,18 +37,18 @@ public: renderthread::MakeCurrentResult makeCurrent() override; renderthread::Frame getFrame() override; - renderthread::IRenderPipeline::DrawResult draw(const renderthread::Frame& frame, - const SkRect& screenDirty, const SkRect& dirty, - const LightGeometry& lightGeometry, - LayerUpdateQueue* layerUpdateQueue, - const Rect& contentDrawBounds, bool opaque, - const LightInfo& lightInfo, - const std::vector<sp<RenderNode> >& renderNodes, - FrameInfoVisualizer* profiler) override; + renderthread::IRenderPipeline::DrawResult draw( + const renderthread::Frame& frame, const SkRect& screenDirty, const SkRect& dirty, + const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, + const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, + const std::vector<sp<RenderNode> >& renderNodes, FrameInfoVisualizer* profiler, + const renderthread::HardwareBufferRenderParams& bufferParams) override; GrSurfaceOrigin getSurfaceOrigin() override { return kTopLeft_GrSurfaceOrigin; } bool swapBuffers(const renderthread::Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) override; DeferredLayerUpdater* createTextureLayer() override; + [[nodiscard]] android::base::unique_fd flush() override; + bool setSurface(ANativeWindow* surface, renderthread::SwapBehavior swapBehavior) override; void onStop() override; bool isSurfaceReady() override; @@ -64,7 +63,6 @@ protected: private: renderthread::VulkanManager& vulkanManager(); - renderthread::VulkanSurface* mVkSurface = nullptr; sp<ANativeWindow> mNativeWindow; }; diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 78ae5cf3d93b..b769f8d15044 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -153,6 +153,7 @@ void CanvasContext::removeRenderNode(RenderNode* node) { void CanvasContext::destroy() { stopDrawing(); + setHardwareBuffer(nullptr); setSurface(nullptr); setSurfaceControl(nullptr); freePrefetchedLayers(); @@ -176,6 +177,19 @@ static void setBufferCount(ANativeWindow* window) { native_window_set_buffer_count(window, bufferCount); } +void CanvasContext::setHardwareBuffer(AHardwareBuffer* buffer) { + if (mHardwareBuffer) { + AHardwareBuffer_release(mHardwareBuffer); + mHardwareBuffer = nullptr; + } + + if (buffer) { + AHardwareBuffer_acquire(buffer); + mHardwareBuffer = buffer; + } + mRenderPipeline->setHardwareBuffer(mHardwareBuffer); +} + void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); @@ -257,7 +271,7 @@ void CanvasContext::setStopped(bool stopped) { mRenderThread.removeFrameCallback(this); mRenderPipeline->onStop(); mRenderThread.cacheManager().onContextStopped(this); - } else if (mIsDirty && hasSurface()) { + } else if (mIsDirty && hasOutputTarget()) { mRenderThread.postFrameCallback(this); } } @@ -390,7 +404,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy mIsDirty = true; - if (CC_UNLIKELY(!hasSurface())) { + if (CC_UNLIKELY(!hasOutputTarget())) { mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame); info.out.canDrawThisFrame = false; return; @@ -535,7 +549,7 @@ void CanvasContext::draw() { std::scoped_lock lock(mFrameMetricsReporterMutex); drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry, &mLayerUpdateQueue, mContentDrawBounds, mOpaque, - mLightInfo, mRenderNodes, &(profiler())); + mLightInfo, mRenderNodes, &(profiler()), mBufferParams); } uint64_t frameCompleteNr = getFrameNumber(); diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index e875c42e9eba..3f796d9b7b65 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -125,12 +125,13 @@ public: // Won't take effect until next EGLSurface creation void setSwapBehavior(SwapBehavior swapBehavior); + void setHardwareBuffer(AHardwareBuffer* buffer); void setSurface(ANativeWindow* window, bool enableTimeout = true); void setSurfaceControl(ASurfaceControl* surfaceControl); bool pauseSurface(); void setStopped(bool stopped); - bool isStopped() { return mStopped || !hasSurface(); } - bool hasSurface() const { return mNativeSurface.get(); } + bool isStopped() { return mStopped || !hasOutputTarget(); } + bool hasOutputTarget() const { return mNativeSurface.get() || mHardwareBuffer; } void allocateBuffers(); void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); @@ -206,6 +207,10 @@ public: mASurfaceTransactionCallback = callback; } + void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) { + mBufferParams = params; + } + bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control); void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) { @@ -258,6 +263,9 @@ private: int32_t mLastFrameHeight = 0; RenderThread& mRenderThread; + + AHardwareBuffer* mHardwareBuffer = nullptr; + HardwareBufferRenderParams mBufferParams; std::unique_ptr<ReliableSurface> mNativeSurface; // The SurfaceControl reference is passed from ViewRootImpl, can be set to // NULL to remove the reference diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp index 1cc82fd0ff64..b06c5dd9ad36 100644 --- a/libs/hwui/renderthread/DrawFrameTask.cpp +++ b/libs/hwui/renderthread/DrawFrameTask.cpp @@ -26,6 +26,7 @@ #include "../Properties.h" #include "../RenderNode.h" #include "CanvasContext.h" +#include "HardwareBufferRenderParams.h" #include "RenderThread.h" namespace android { @@ -91,6 +92,9 @@ void DrawFrameTask::run() { mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued); + auto hardwareBufferParams = mHardwareBufferParams; + mContext->setHardwareBufferRenderParams(hardwareBufferParams); + IRenderPipeline* pipeline = mContext->getRenderPipeline(); bool canUnblockUiThread; bool canDrawThisFrame; { @@ -150,6 +154,11 @@ void DrawFrameTask::run() { if (!canUnblockUiThread) { unblockUiThread(); } + + if (pipeline->hasHardwareBuffer()) { + auto fence = pipeline->flush(); + hardwareBufferParams.invokeRenderCallback(std::move(fence), 0); + } } bool DrawFrameTask::syncFrameState(TreeInfo& info) { @@ -175,8 +184,9 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) { // This is after the prepareTree so that any pending operations // (RenderNode tree state, prefetched layers, etc...) will be flushed. - if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) { - if (!mContext->hasSurface()) { + bool hasTarget = mContext->hasOutputTarget(); + if (CC_UNLIKELY(!hasTarget || !canDraw)) { + if (!hasTarget) { mSyncResult |= SyncResult::LostSurfaceRewardIfFound; } else { // If we have a surface but can't draw we must be stopped diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h index fafab24cbce7..c5c5fe280743 100644 --- a/libs/hwui/renderthread/DrawFrameTask.h +++ b/libs/hwui/renderthread/DrawFrameTask.h @@ -27,8 +27,16 @@ #include "../Rect.h" #include "../TreeInfo.h" #include "RenderTask.h" +#include "SkColorSpace.h" +#include "SwapBehavior.h" +#include "utils/TimeUtils.h" +#ifdef __ANDROID__ // Layoutlib does not support hardware acceleration +#include <android/hardware_buffer.h> +#endif +#include "HardwareBufferRenderParams.h" namespace android { + namespace uirenderer { class DeferredLayerUpdater; @@ -88,6 +96,10 @@ public: void forceDrawNextFrame() { mForceDrawFrame = true; } + void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) { + mHardwareBufferParams = params; + } + private: void postAndWait(); bool syncFrameState(TreeInfo& info); @@ -111,6 +123,7 @@ private: int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE]; + HardwareBufferRenderParams mHardwareBufferParams; std::function<std::function<void(bool)>(int32_t, int64_t)> mFrameCallback; std::function<void(bool)> mFrameCommitCallback; std::function<void()> mFrameCompleteCallback; diff --git a/libs/hwui/renderthread/HardwareBufferRenderParams.h b/libs/hwui/renderthread/HardwareBufferRenderParams.h new file mode 100644 index 000000000000..91fe3f6cf273 --- /dev/null +++ b/libs/hwui/renderthread/HardwareBufferRenderParams.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2013 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. + */ +#ifndef HARDWAREBUFFERRENDERER_H_ +#define HARDWAREBUFFERRENDERER_H_ + +#include <android-base/unique_fd.h> +#include <android/hardware_buffer.h> + +#include "SkColorSpace.h" +#include "SkMatrix.h" +#include "SkSurface.h" + +namespace android { +namespace uirenderer { +namespace renderthread { + +using namespace android::uirenderer::renderthread; + +using RenderCallback = std::function<void(android::base::unique_fd&&, int)>; + +class RenderProxy; + +class HardwareBufferRenderParams { +public: + HardwareBufferRenderParams() = default; + HardwareBufferRenderParams(const SkMatrix& transform, const sk_sp<SkColorSpace>& colorSpace, + RenderCallback&& callback) + : mTransform(transform) + , mColorSpace(colorSpace) + , mRenderCallback(std::move(callback)) {} + const SkMatrix& getTransform() const { return mTransform; } + sk_sp<SkColorSpace> getColorSpace() const { return mColorSpace; } + + void invokeRenderCallback(android::base::unique_fd&& fenceFd, int status) { + if (mRenderCallback) { + std::invoke(mRenderCallback, std::move(fenceFd), status); + } + } + +private: + SkMatrix mTransform = SkMatrix::I(); + sk_sp<SkColorSpace> mColorSpace = SkColorSpace::MakeSRGB(); + RenderCallback mRenderCallback = nullptr; +}; + +} // namespace renderthread +} // namespace uirenderer +} // namespace android +#endif // HARDWAREBUFFERRENDERER_H_ diff --git a/libs/hwui/renderthread/IRenderPipeline.h b/libs/hwui/renderthread/IRenderPipeline.h index 35e370f52fda..715c17dfc895 100644 --- a/libs/hwui/renderthread/IRenderPipeline.h +++ b/libs/hwui/renderthread/IRenderPipeline.h @@ -16,17 +16,19 @@ #pragma once +#include <SkColorSpace.h> +#include <SkRect.h> +#include <android-base/unique_fd.h> +#include <utils/RefBase.h> + +#include "ColorMode.h" #include "DamageAccumulator.h" #include "FrameInfoVisualizer.h" +#include "HardwareBufferRenderParams.h" #include "LayerUpdateQueue.h" #include "Lighting.h" #include "SwapBehavior.h" #include "hwui/Bitmap.h" -#include "ColorMode.h" - -#include <SkColorSpace.h> -#include <SkRect.h> -#include <utils/RefBase.h> class GrDirectContext; @@ -64,10 +66,14 @@ public: const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue, const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo, const std::vector<sp<RenderNode>>& renderNodes, - FrameInfoVisualizer* profiler) = 0; + FrameInfoVisualizer* profiler, + const HardwareBufferRenderParams& bufferParams) = 0; virtual bool swapBuffers(const Frame& frame, bool drew, const SkRect& screenDirty, FrameInfo* currentFrameInfo, bool* requireSwap) = 0; virtual DeferredLayerUpdater* createTextureLayer() = 0; + [[nodiscard]] virtual android::base::unique_fd flush() = 0; + virtual void setHardwareBuffer(AHardwareBuffer* hardwareBuffer) = 0; + virtual bool hasHardwareBuffer() = 0; virtual bool setSurface(ANativeWindow* window, SwapBehavior swapBehavior) = 0; virtual void onStop() = 0; virtual bool isSurfaceReady() = 0; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 07f5a78fc1ec..ed01e322ffd9 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -85,6 +85,18 @@ void RenderProxy::setName(const char* name) { mRenderThread.queue().runSync([this, name]() { mContext->setName(std::string(name)); }); } +void RenderProxy::setHardwareBuffer(AHardwareBuffer* buffer) { + if (buffer) { + AHardwareBuffer_acquire(buffer); + } + mRenderThread.queue().post([this, hardwareBuffer = buffer]() mutable { + mContext->setHardwareBuffer(hardwareBuffer); + if (hardwareBuffer) { + AHardwareBuffer_release(hardwareBuffer); + } + }); +} + void RenderProxy::setSurface(ANativeWindow* window, bool enableTimeout) { if (window) { ANativeWindow_acquire(window); } mRenderThread.queue().post([this, win = window, enableTimeout]() mutable { @@ -324,6 +336,10 @@ void RenderProxy::setContentDrawBounds(int left, int top, int right, int bottom) mDrawFrameTask.setContentDrawBounds(left, top, right, bottom); } +void RenderProxy::setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) { + mDrawFrameTask.setHardwareBufferRenderParams(params); +} + void RenderProxy::setPictureCapturedCallback( const std::function<void(sk_sp<SkPicture>&&)>& callback) { mRenderThread.queue().post( diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index a21faa86ed0f..17cf6650f87d 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -18,6 +18,7 @@ #define RENDERPROXY_H_ #include <SkRefCnt.h> +#include <android/hardware_buffer.h> #include <android/native_window.h> #include <android/surface_control.h> #include <cutils/compiler.h> @@ -76,7 +77,7 @@ public: void setSwapBehavior(SwapBehavior swapBehavior); bool loadSystemProperties(); void setName(const char* name); - + void setHardwareBuffer(AHardwareBuffer* buffer); void setSurface(ANativeWindow* window, bool enableTimeout = true); void setSurfaceControl(ASurfaceControl* surfaceControl); void allocateBuffers(); @@ -84,6 +85,7 @@ public: void setStopped(bool stopped); void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha); void setLightGeometry(const Vector3& lightCenter, float lightRadius); + void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params); void setOpaque(bool opaque); void setColorMode(ColorMode mode); int64_t* frameInfo(); diff --git a/libs/hwui/tests/unit/CanvasContextTests.cpp b/libs/hwui/tests/unit/CanvasContextTests.cpp index 88420a5d5c23..9e376e32f8ea 100644 --- a/libs/hwui/tests/unit/CanvasContextTests.cpp +++ b/libs/hwui/tests/unit/CanvasContextTests.cpp @@ -38,7 +38,7 @@ RENDERTHREAD_TEST(CanvasContext, create) { std::unique_ptr<CanvasContext> canvasContext( CanvasContext::create(renderThread, false, rootNode.get(), &contextFactory, 0, 0)); - ASSERT_FALSE(canvasContext->hasSurface()); + ASSERT_FALSE(canvasContext->hasOutputTarget()); canvasContext->destroy(); } diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 7383d6ab4994..616f21cbe1a4 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -1136,6 +1136,15 @@ </intent-filter> </activity> + <activity android:name="HardwareBufferRendererActivity" + android:label="HardwareRenderer/HardwareBufferRenderer" + android:exported="true"> + <intent-filter> + <action android:name="android.intent.action.MAIN"/> + <category android:name="com.android.test.hwui.TEST"/> + </intent-filter> + </activity> + <activity android:name="MyLittleTextureView" android:label="HardwareRenderer/MyLittleTextureView" android:screenOrientation="fullSensor" diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java new file mode 100644 index 000000000000..e4de434f1ed2 --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/HardwareBufferRendererActivity.java @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2019 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 com.android.test.hwui; + +import android.app.Activity; +import android.graphics.Bitmap; +import android.graphics.Bitmap.Config; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorSpace; +import android.graphics.ColorSpace.Named; +import android.graphics.HardwareBufferRenderer; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.RenderNode; +import android.hardware.HardwareBuffer; +import android.os.Bundle; +import android.os.Handler; +import android.os.Looper; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; + +import java.time.Duration; +import java.util.concurrent.Executors; + +public class HardwareBufferRendererActivity extends Activity { + + private ImageView mImageView; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + mImageView = new ImageView(this); + mImageView.setBackgroundColor(Color.MAGENTA); + FrameLayout layout = new FrameLayout(this); + layout.setBackgroundColor(Color.CYAN); + layout.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT)); + layout.addView(mImageView, new FrameLayout.LayoutParams(100, 100)); + setContentView(layout); + + HardwareBuffer buffer = HardwareBuffer.create(100, 100, PixelFormat.RGBA_8888, 1, + HardwareBuffer.USAGE_GPU_SAMPLED_IMAGE | HardwareBuffer.USAGE_GPU_COLOR_OUTPUT); + HardwareBufferRenderer renderer = new HardwareBufferRenderer(buffer); + RenderNode node = new RenderNode("content"); + node.setPosition(0, 0, 100, 100); + + Canvas canvas = node.beginRecording(); + canvas.drawColor(Color.BLUE); + + Paint paint = new Paint(); + paint.setColor(Color.RED); + canvas.drawRect(0f, 0f, 50f, 50f, paint); + node.endRecording(); + + renderer.setContentRoot(node); + + ColorSpace colorSpace = ColorSpace.get(Named.SRGB); + Handler handler = new Handler(Looper.getMainLooper()); + renderer.obtainRenderRequest() + .setColorSpace(colorSpace) + .draw(Executors.newSingleThreadExecutor(), result -> { + result.getFence().await(Duration.ofMillis(3000)); + handler.post(() -> { + Bitmap bitmap = Bitmap.wrapHardwareBuffer(buffer, colorSpace); + Bitmap copy = bitmap.copy(Config.ARGB_8888, false); + mImageView.setImageBitmap(copy); + }); + }); + } +} |