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

This reverts commit cca989f2b5.

Test: atest CtsUiRenderingTestCases --iterations 10 --armeabi-v7a
Change-Id: Iee19edeb489ed54b421ac8de37ee5a70b8f9756a
This commit is contained in:
Nader Jawad
2023-01-30 20:23:46 -08:00
parent cca989f2b5
commit a352185eca
30 changed files with 1205 additions and 157 deletions

View File

@@ -15310,6 +15310,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();

View File

@@ -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);
}
/**

View File

@@ -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);

View File

@@ -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);
}
}
}

View File

@@ -338,6 +338,7 @@ cc_defaults {
"jni/android_util_PathParser.cpp",
"jni/Bitmap.cpp",
"jni/HardwareBufferHelpers.cpp",
"jni/BitmapFactory.cpp",
"jni/ByteBufferStreamAdaptor.cpp",
"jni/Camera.cpp",
@@ -414,6 +415,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",

View File

@@ -84,6 +84,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 }
@@ -153,6 +154,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),
};

View File

@@ -2,49 +2,42 @@
#define LOG_TAG "Bitmap"
#include "Bitmap.h"
#include <hwui/Bitmap.h>
#include <hwui/Paint.h>
#include "CreateJavaOutputStreamAdaptor.h"
#include "Gainmap.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
@@ -1205,18 +1198,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()) {
@@ -1239,7 +1225,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
@@ -1347,18 +1334,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));
}

View File

@@ -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;
@@ -333,6 +332,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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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

View File

@@ -0,0 +1,183 @@
/*
* 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(jlong 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, jlong 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)));
nsecs_t vsync = systemTime(SYSTEM_TIME_MONOTONIC);
UiFrameInfoBuilder(proxy->frameInfo())
.setVsync(vsync, vsync, UiFrameInfoBuilder::INVALID_VSYNC_ID,
UiFrameInfoBuilder::UNKNOWN_DEADLINE,
UiFrameInfoBuilder::UNKNOWN_FRAME_INTERVAL)
.addFlag(FrameInfoFlags::SurfaceCanvas);
return proxy->syncAndDrawFrame();
}
static void android_graphics_HardwareBufferRenderer_setLightGeometry(JNIEnv*, jobject,
jlong 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,
jlong 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

View File

@@ -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;
}
}
@@ -449,26 +426,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 {
@@ -584,7 +541,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,
@@ -606,7 +563,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,
@@ -628,7 +585,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);
});
@@ -647,7 +604,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,
@@ -686,7 +643,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);
});
@@ -699,7 +656,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;
@@ -708,7 +665,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));
}

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -605,6 +605,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) {

View File

@@ -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,14 +75,22 @@ public:
mCaptureMode = callback ? CaptureMode::CallbackAPI : CaptureMode::None;
}
virtual void setHardwareBuffer(AHardwareBuffer* buffer) override;
bool hasHardwareBuffer() override { return mHardwareBuffer != nullptr; }
void setTargetSdrHdrRatio(float ratio) override;
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;

View File

@@ -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*/) {

View File

@@ -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;
@@ -66,7 +65,6 @@ protected:
private:
renderthread::VulkanManager& vulkanManager();
renderthread::VulkanSurface* mVkSurface = nullptr;
sp<ANativeWindow> mNativeWindow;
};

View File

@@ -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();
@@ -261,7 +275,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);
}
}
@@ -425,7 +439,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;
@@ -570,7 +584,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();

View File

@@ -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);
@@ -208,6 +209,10 @@ public:
mASurfaceTransactionCallback = callback;
}
void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) {
mBufferParams = params;
}
bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) {
@@ -262,6 +267,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

View File

@@ -26,6 +26,7 @@
#include "../Properties.h"
#include "../RenderNode.h"
#include "CanvasContext.h"
#include "HardwareBufferRenderParams.h"
#include "RenderThread.h"
namespace android {
@@ -92,6 +93,9 @@ void DrawFrameTask::run() {
mContext->setSyncDelayDuration(systemTime(SYSTEM_TIME_MONOTONIC) - mSyncQueued);
mContext->setTargetSdrHdrRatio(mRenderSdrHdrRatio);
auto hardwareBufferParams = mHardwareBufferParams;
mContext->setHardwareBufferRenderParams(hardwareBufferParams);
IRenderPipeline* pipeline = mContext->getRenderPipeline();
bool canUnblockUiThread;
bool canDrawThisFrame;
{
@@ -151,6 +155,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) {
@@ -176,8 +185,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

View File

@@ -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;
}
void setRenderSdrHdrRatio(float ratio) { mRenderSdrHdrRatio = ratio; }
private:
@@ -114,6 +126,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;

View File

@@ -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_

View File

@@ -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;

View File

@@ -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 {
@@ -340,6 +352,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(

View File

@@ -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);
float setColorMode(ColorMode mode);
void setRenderSdrHdrRatio(float ratio);

View File

@@ -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();
}

View File

@@ -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"

View File

@@ -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);
});
});
}
}