Revert "Created HardwareBufferRenderer to support rendering into..."
Revert submission 20579518-hardware_buffer_renderer Reason for revert: The submission timing tracks with a major regression in CtsUiRenderingTestCases stability: https://screenshot.googleplex.com/3TxXCSP4xCZq7Zy.png and also some crash bugs, eg: https://b.corp.google.com/issues/264889058 Reverting to re-stabilize the tree Reverted changes: /q/submissionid:20579518-hardware_buffer_renderer Change-Id: I29f47da097257bdeaa963fccb9ad0dbe39ead063
This commit is contained in:
@@ -15310,29 +15310,6 @@ package android.graphics {
|
||||
ctor @Deprecated public EmbossMaskFilter(float[], float, float, float);
|
||||
}
|
||||
|
||||
public class HardwareBufferRenderer implements java.lang.AutoCloseable {
|
||||
ctor public HardwareBufferRenderer(@NonNull android.hardware.HardwareBuffer);
|
||||
method public void close();
|
||||
method public boolean isClosed();
|
||||
method @NonNull public android.graphics.HardwareBufferRenderer.RenderRequest obtainRenderRequest();
|
||||
method public void setContentRoot(@Nullable android.graphics.RenderNode);
|
||||
method public void setLightSourceAlpha(@FloatRange(from=0.0f, to=1.0f) float, @FloatRange(from=0.0f, to=1.0f) float);
|
||||
method public void setLightSourceGeometry(float, float, @FloatRange(from=0.0f) float, @FloatRange(from=0.0f) float);
|
||||
}
|
||||
|
||||
public final class HardwareBufferRenderer.RenderRequest {
|
||||
method public void draw(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<android.graphics.HardwareBufferRenderer.RenderResult>);
|
||||
method @NonNull public android.graphics.HardwareBufferRenderer.RenderRequest setBufferTransform(int);
|
||||
method @NonNull public android.graphics.HardwareBufferRenderer.RenderRequest setColorSpace(@Nullable android.graphics.ColorSpace);
|
||||
}
|
||||
|
||||
public static final class HardwareBufferRenderer.RenderResult {
|
||||
method @NonNull public android.hardware.SyncFence getFence();
|
||||
method public int getStatus();
|
||||
field public static final int ERROR_UNKNOWN = 1; // 0x1
|
||||
field public static final int SUCCESS = 0; // 0x0
|
||||
}
|
||||
|
||||
public class HardwareRenderer {
|
||||
ctor public HardwareRenderer();
|
||||
method public void clearContent();
|
||||
|
||||
@@ -87,8 +87,8 @@ public final class SyncFence implements AutoCloseable, Parcelable {
|
||||
// is well worth making.
|
||||
private final Runnable mCloser;
|
||||
|
||||
private SyncFence(int fileDescriptor) {
|
||||
mNativePtr = nCreate(fileDescriptor);
|
||||
private SyncFence(@NonNull ParcelFileDescriptor wrapped) {
|
||||
mNativePtr = nCreate(wrapped.detachFd());
|
||||
mCloser = sRegistry.registerNativeAllocation(this, mNativePtr);
|
||||
}
|
||||
|
||||
@@ -136,26 +136,14 @@ public final class SyncFence implements AutoCloseable, Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SyncFence wrapped around another {@link ParcelFileDescriptor}. By default, all
|
||||
* method calls are delegated to the wrapped descriptor. This takes ownership of the
|
||||
* {@link ParcelFileDescriptor}.
|
||||
* Create a new SyncFence wrapped around another descriptor. By default, all method calls are
|
||||
* delegated to the wrapped descriptor.
|
||||
*
|
||||
* @param wrapped The descriptor to be wrapped.
|
||||
* @hide
|
||||
*/
|
||||
public static @NonNull SyncFence create(@NonNull ParcelFileDescriptor wrapped) {
|
||||
return new SyncFence(wrapped.detachFd());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SyncFence wrapped around another descriptor. The returned {@link SyncFence}
|
||||
* instance takes ownership of the file descriptor.
|
||||
*
|
||||
* @param fileDescriptor The descriptor to be wrapped.
|
||||
* @hide
|
||||
*/
|
||||
public static @NonNull SyncFence adopt(int fileDescriptor) {
|
||||
return new SyncFence(fileDescriptor);
|
||||
return new SyncFence(wrapped);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -102,7 +102,6 @@ extern int register_android_view_KeyCharacterMap(JNIEnv* env);
|
||||
extern int register_android_view_KeyEvent(JNIEnv* env);
|
||||
extern int register_android_view_MotionEvent(JNIEnv* env);
|
||||
extern int register_android_view_ThreadedRenderer(JNIEnv* env);
|
||||
extern int register_android_graphics_HardwareBufferRenderer(JNIEnv* env);
|
||||
extern int register_android_view_VelocityTracker(JNIEnv* env);
|
||||
extern int register_com_android_internal_util_VirtualRefBasePtr(JNIEnv *env);
|
||||
|
||||
|
||||
@@ -1,390 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -338,7 +338,6 @@ cc_defaults {
|
||||
"jni/android_util_PathParser.cpp",
|
||||
|
||||
"jni/Bitmap.cpp",
|
||||
"jni/HardwareBufferHelpers.cpp",
|
||||
"jni/BitmapFactory.cpp",
|
||||
"jni/ByteBufferStreamAdaptor.cpp",
|
||||
"jni/Camera.cpp",
|
||||
@@ -415,7 +414,6 @@ cc_defaults {
|
||||
"jni/AnimatedImageDrawable.cpp",
|
||||
"jni/android_graphics_TextureLayer.cpp",
|
||||
"jni/android_graphics_HardwareRenderer.cpp",
|
||||
"jni/android_graphics_HardwareBufferRenderer.cpp",
|
||||
"jni/BitmapRegionDecoder.cpp",
|
||||
"jni/GIFMovie.cpp",
|
||||
"jni/GraphicsStatsService.cpp",
|
||||
|
||||
@@ -84,7 +84,6 @@ 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 }
|
||||
@@ -154,8 +153,6 @@ extern int register_android_graphics_HardwareBufferRenderer(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),
|
||||
};
|
||||
|
||||
|
||||
@@ -2,42 +2,49 @@
|
||||
#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
|
||||
|
||||
@@ -1198,11 +1205,18 @@ 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 = uirenderer::HardwareBufferHelpers::AHardwareBuffer_fromHardwareBuffer(
|
||||
env, hardwareBuffer);
|
||||
AHardwareBuffer* buffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBuffer);
|
||||
sk_sp<Bitmap> bitmap = Bitmap::createFrom(buffer,
|
||||
GraphicsJNI::getNativeColorSpace(colorSpacePtr));
|
||||
if (!bitmap.get()) {
|
||||
@@ -1225,8 +1239,7 @@ static jobject Bitmap_getHardwareBuffer(JNIEnv* env, jobject, jlong bitmapPtr) {
|
||||
}
|
||||
|
||||
Bitmap& bitmap = bitmapHandle->bitmap();
|
||||
return uirenderer::HardwareBufferHelpers::AHardwareBuffer_toHardwareBuffer(
|
||||
env, bitmap.hardwareBuffer());
|
||||
return AHardwareBuffer_toHardwareBuffer(env, bitmap.hardwareBuffer());
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
@@ -1334,7 +1347,18 @@ 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");
|
||||
uirenderer::HardwareBufferHelpers::init();
|
||||
|
||||
#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
|
||||
return android::RegisterMethodsOrDie(env, "android/graphics/Bitmap", gBitmapMethods,
|
||||
NELEM(gBitmapMethods));
|
||||
}
|
||||
|
||||
@@ -2,18 +2,19 @@
|
||||
#define _ANDROID_GRAPHICS_GRAPHICS_JNI_H_
|
||||
|
||||
#include <cutils/compiler.h>
|
||||
#include <hwui/Bitmap.h>
|
||||
#include <hwui/Canvas.h>
|
||||
|
||||
#include "BRDAllocator.h"
|
||||
#include "Bitmap.h"
|
||||
#include "BRDAllocator.h"
|
||||
#include "SkBitmap.h"
|
||||
#include "SkCodec.h"
|
||||
#include "SkColorSpace.h"
|
||||
#include "SkMallocPixelRef.h"
|
||||
#include "SkPixelRef.h"
|
||||
#include "SkMallocPixelRef.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;
|
||||
@@ -332,26 +333,6 @@ 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
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* 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
|
||||
@@ -56,7 +56,6 @@
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
#include "JvmErrorReporter.h"
|
||||
#include "android_graphics_HardwareRendererObserver.h"
|
||||
|
||||
namespace android {
|
||||
@@ -94,12 +93,35 @@ 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");
|
||||
}
|
||||
@@ -109,18 +131,19 @@ public:
|
||||
void onFrameCommit(bool didProduceBuffer) {
|
||||
if (mObject) {
|
||||
ATRACE_FORMAT("frameCommit success=%d", didProduceBuffer);
|
||||
GraphicsJNI::getJNIEnv()->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
|
||||
didProduceBuffer);
|
||||
getenv(mVm)->CallVoidMethod(mObject, gFrameCommitCallback.onFrameCommit,
|
||||
didProduceBuffer);
|
||||
releaseObject();
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
JavaVM* mVm;
|
||||
jobject mObject;
|
||||
|
||||
void releaseObject() {
|
||||
if (mObject) {
|
||||
GraphicsJNI::getJNIEnv()->DeleteGlobalRef(mObject);
|
||||
getenv(mVm)->DeleteGlobalRef(mObject);
|
||||
mObject = nullptr;
|
||||
}
|
||||
}
|
||||
@@ -426,6 +449,26 @@ 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 {
|
||||
@@ -541,7 +584,7 @@ static void android_view_ThreadedRenderer_setPictureCapturedCallbackJNI(JNIEnv*
|
||||
auto pictureState = std::make_shared<PictureCaptureState>();
|
||||
proxy->setPictureCapturedCallback([globalCallbackRef,
|
||||
pictureState](sk_sp<SkPicture>&& picture) {
|
||||
JNIEnv* env = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(globalCallbackRef->vm());
|
||||
Picture* wrapper = new PictureWrapper{std::move(picture), pictureState};
|
||||
env->CallStaticVoidMethod(gHardwareRenderer.clazz,
|
||||
gHardwareRenderer.invokePictureCapturedCallback,
|
||||
@@ -563,7 +606,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 = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(globalCallbackRef->vm());
|
||||
jboolean ret = env->CallBooleanMethod(
|
||||
globalCallbackRef->object(),
|
||||
gASurfaceTransactionCallback.onMergeTransaction,
|
||||
@@ -585,7 +628,7 @@ static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCall
|
||||
auto globalCallbackRef =
|
||||
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
|
||||
proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() {
|
||||
JNIEnv* env = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(globalCallbackRef->vm());
|
||||
env->CallVoidMethod(globalCallbackRef->object(),
|
||||
gPrepareSurfaceControlForWebviewCallback.prepare);
|
||||
});
|
||||
@@ -604,7 +647,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 = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(globalCallbackRef->vm());
|
||||
ScopedLocalRef<jobject> frameCommitCallback(
|
||||
env, env->CallObjectMethod(
|
||||
globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
|
||||
@@ -643,7 +686,7 @@ static void android_view_ThreadedRenderer_setFrameCompleteCallback(JNIEnv* env,
|
||||
auto globalCallbackRef =
|
||||
std::make_shared<JGlobalRefHolder>(vm, env->NewGlobalRef(callback));
|
||||
proxy->setFrameCompleteCallback([globalCallbackRef]() {
|
||||
JNIEnv* env = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(globalCallbackRef->vm());
|
||||
env->CallVoidMethod(globalCallbackRef->object(),
|
||||
gFrameCompleteCallback.onFrameComplete);
|
||||
});
|
||||
@@ -656,7 +699,7 @@ public:
|
||||
: CopyRequest(srcRect), mRefHolder(vm, jCopyRequest) {}
|
||||
|
||||
virtual SkBitmap getDestinationBitmap(int srcWidth, int srcHeight) override {
|
||||
JNIEnv* env = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(mRefHolder.vm());
|
||||
jlong bitmapPtr = env->CallLongMethod(
|
||||
mRefHolder.object(), gCopyRequest.getDestinationBitmap, srcWidth, srcHeight);
|
||||
SkBitmap bitmap;
|
||||
@@ -665,7 +708,7 @@ public:
|
||||
}
|
||||
|
||||
virtual void onCopyFinished(CopyResult result) override {
|
||||
JNIEnv* env = GraphicsJNI::getJNIEnv();
|
||||
JNIEnv* env = getenv(mRefHolder.vm());
|
||||
env->CallVoidMethod(mRefHolder.object(), gCopyRequest.onCopyFinished,
|
||||
static_cast<jint>(result));
|
||||
}
|
||||
|
||||
@@ -55,9 +55,7 @@ SkiaOpenGLPipeline::~SkiaOpenGLPipeline() {
|
||||
MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
|
||||
// In case the surface was destroyed (e.g. a previous trimMemory call) we
|
||||
// need to recreate it here.
|
||||
if (mHardwareBuffer) {
|
||||
mRenderThread.requireGlContext();
|
||||
} else if (!isSurfaceReady() && mNativeWindow) {
|
||||
if (!isSurfaceReady() && mNativeWindow) {
|
||||
setSurface(mNativeWindow.get(), mSwapBehavior);
|
||||
}
|
||||
|
||||
@@ -69,24 +67,17 @@ MakeCurrentResult SkiaOpenGLPipeline::makeCurrent() {
|
||||
}
|
||||
|
||||
Frame SkiaOpenGLPipeline::getFrame() {
|
||||
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);
|
||||
}
|
||||
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,
|
||||
const HardwareBufferRenderParams& bufferParams) {
|
||||
if (!isCapturingSkp() && !mHardwareBuffer) {
|
||||
const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) {
|
||||
if (!isCapturingSkp()) {
|
||||
mEglManager.damageFrame(frame, dirty);
|
||||
}
|
||||
|
||||
@@ -113,25 +104,13 @@ IRenderPipeline::DrawResult SkiaOpenGLPipeline::draw(
|
||||
SkSurfaceProps props(0, kUnknown_SkPixelGeometry);
|
||||
|
||||
SkASSERT(mRenderThread.getGrContext() != nullptr);
|
||||
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();
|
||||
}
|
||||
sk_sp<SkSurface> surface(SkSurface::MakeFromBackendRenderTarget(
|
||||
mRenderThread.getGrContext(), backendRT, this->getSurfaceOrigin(), colorType,
|
||||
mSurfaceColorSpace, &props));
|
||||
|
||||
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);
|
||||
LightingInfo::updateLighting(lightGeometry, lightInfo);
|
||||
renderFrame(*layerUpdateQueue, dirty, renderNodes, opaque, contentDrawBounds, surface,
|
||||
preTransform);
|
||||
SkMatrix::I());
|
||||
|
||||
// Draw visual debugging features
|
||||
if (CC_UNLIKELY(Properties::showDirtyRegions ||
|
||||
@@ -163,10 +142,6 @@ 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)))) {
|
||||
@@ -222,26 +197,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
|
||||
#include "SkiaPipeline.h"
|
||||
#include "renderstate/RenderState.h"
|
||||
#include "renderthread/HardwareBufferRenderParams.h"
|
||||
|
||||
namespace android {
|
||||
|
||||
@@ -37,18 +36,19 @@ 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,
|
||||
const renderthread::HardwareBufferRenderParams& bufferParams) 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;
|
||||
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;
|
||||
|
||||
@@ -605,31 +605,6 @@ 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) {
|
||||
|
||||
@@ -20,11 +20,9 @@
|
||||
#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;
|
||||
@@ -75,22 +73,14 @@ 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;
|
||||
|
||||
@@ -57,55 +57,37 @@ 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 (mHardwareBuffer) {
|
||||
mRenderThread.requireVkContext();
|
||||
} else if (!isSurfaceReady() && mNativeWindow) {
|
||||
if (!isSurfaceReady() && mNativeWindow) {
|
||||
setSurface(mNativeWindow.get(), SwapBehavior::kSwap_default);
|
||||
}
|
||||
return isContextReady() ? MakeCurrentResult::AlreadyCurrent : MakeCurrentResult::Failed;
|
||||
}
|
||||
|
||||
Frame SkiaVulkanPipeline::getFrame() {
|
||||
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);
|
||||
}
|
||||
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,
|
||||
const HardwareBufferRenderParams& bufferParams) {
|
||||
sk_sp<SkSurface> backBuffer;
|
||||
SkMatrix preTransform;
|
||||
if (mHardwareBuffer) {
|
||||
backBuffer = getBufferSkSurface(bufferParams);
|
||||
preTransform = bufferParams.getTransform();
|
||||
} else {
|
||||
backBuffer = mVkSurface->getCurrentSkSurface();
|
||||
preTransform = mVkSurface->getCurrentPreTransform();
|
||||
}
|
||||
|
||||
const std::vector<sp<RenderNode>>& renderNodes, FrameInfoVisualizer* profiler) {
|
||||
sk_sp<SkSurface> backBuffer = mVkSurface->getCurrentSkSurface();
|
||||
if (backBuffer.get() == nullptr) {
|
||||
return {false, -1};
|
||||
}
|
||||
|
||||
// update the coordinates of the global light position based on surface rotation
|
||||
SkPoint lightCenter = preTransform.mapXY(lightGeometry.center.x, lightGeometry.center.y);
|
||||
SkPoint lightCenter = mVkSurface->getCurrentPreTransform().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,
|
||||
preTransform);
|
||||
mVkSurface->getCurrentPreTransform());
|
||||
|
||||
// Draw visual debugging features
|
||||
if (CC_UNLIKELY(Properties::showDirtyRegions ||
|
||||
@@ -134,16 +116,12 @@ 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);
|
||||
}
|
||||
@@ -159,12 +137,6 @@ 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*/) {
|
||||
|
||||
@@ -16,13 +16,14 @@
|
||||
|
||||
#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;
|
||||
|
||||
@@ -37,18 +38,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,
|
||||
const renderthread::HardwareBufferRenderParams& bufferParams) 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;
|
||||
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;
|
||||
@@ -65,6 +66,7 @@ protected:
|
||||
|
||||
private:
|
||||
renderthread::VulkanManager& vulkanManager();
|
||||
|
||||
renderthread::VulkanSurface* mVkSurface = nullptr;
|
||||
sp<ANativeWindow> mNativeWindow;
|
||||
};
|
||||
|
||||
@@ -153,7 +153,6 @@ void CanvasContext::removeRenderNode(RenderNode* node) {
|
||||
|
||||
void CanvasContext::destroy() {
|
||||
stopDrawing();
|
||||
setHardwareBuffer(nullptr);
|
||||
setSurface(nullptr);
|
||||
setSurfaceControl(nullptr);
|
||||
freePrefetchedLayers();
|
||||
@@ -177,19 +176,6 @@ 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();
|
||||
|
||||
@@ -275,7 +261,7 @@ void CanvasContext::setStopped(bool stopped) {
|
||||
mRenderThread.removeFrameCallback(this);
|
||||
mRenderPipeline->onStop();
|
||||
mRenderThread.cacheManager().onContextStopped(this);
|
||||
} else if (mIsDirty && hasOutputTarget()) {
|
||||
} else if (mIsDirty && hasSurface()) {
|
||||
mRenderThread.postFrameCallback(this);
|
||||
}
|
||||
}
|
||||
@@ -439,7 +425,7 @@ void CanvasContext::prepareTree(TreeInfo& info, int64_t* uiFrameInfo, int64_t sy
|
||||
|
||||
mIsDirty = true;
|
||||
|
||||
if (CC_UNLIKELY(!hasOutputTarget())) {
|
||||
if (CC_UNLIKELY(!hasSurface())) {
|
||||
mCurrentFrameInfo->addFlag(FrameInfoFlags::SkippedFrame);
|
||||
info.out.canDrawThisFrame = false;
|
||||
return;
|
||||
@@ -584,7 +570,7 @@ void CanvasContext::draw() {
|
||||
std::scoped_lock lock(mFrameMetricsReporterMutex);
|
||||
drawResult = mRenderPipeline->draw(frame, windowDirty, dirty, mLightGeometry,
|
||||
&mLayerUpdateQueue, mContentDrawBounds, mOpaque,
|
||||
mLightInfo, mRenderNodes, &(profiler()), mBufferParams);
|
||||
mLightInfo, mRenderNodes, &(profiler()));
|
||||
}
|
||||
|
||||
uint64_t frameCompleteNr = getFrameNumber();
|
||||
|
||||
@@ -125,13 +125,12 @@ 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 || !hasOutputTarget(); }
|
||||
bool hasOutputTarget() const { return mNativeSurface.get() || mHardwareBuffer; }
|
||||
bool isStopped() { return mStopped || !hasSurface(); }
|
||||
bool hasSurface() const { return mNativeSurface.get(); }
|
||||
void allocateBuffers();
|
||||
|
||||
void setLightAlpha(uint8_t ambientShadowAlpha, uint8_t spotShadowAlpha);
|
||||
@@ -209,10 +208,6 @@ public:
|
||||
mASurfaceTransactionCallback = callback;
|
||||
}
|
||||
|
||||
void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) {
|
||||
mBufferParams = params;
|
||||
}
|
||||
|
||||
bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control);
|
||||
|
||||
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) {
|
||||
@@ -267,9 +262,6 @@ 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
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
#include "../Properties.h"
|
||||
#include "../RenderNode.h"
|
||||
#include "CanvasContext.h"
|
||||
#include "HardwareBufferRenderParams.h"
|
||||
#include "RenderThread.h"
|
||||
|
||||
namespace android {
|
||||
@@ -93,9 +92,6 @@ 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;
|
||||
{
|
||||
@@ -155,11 +151,6 @@ void DrawFrameTask::run() {
|
||||
if (!canUnblockUiThread) {
|
||||
unblockUiThread();
|
||||
}
|
||||
|
||||
if (pipeline->hasHardwareBuffer()) {
|
||||
auto fence = pipeline->flush();
|
||||
hardwareBufferParams.invokeRenderCallback(std::move(fence), 0);
|
||||
}
|
||||
}
|
||||
|
||||
bool DrawFrameTask::syncFrameState(TreeInfo& info) {
|
||||
@@ -185,9 +176,8 @@ bool DrawFrameTask::syncFrameState(TreeInfo& info) {
|
||||
|
||||
// This is after the prepareTree so that any pending operations
|
||||
// (RenderNode tree state, prefetched layers, etc...) will be flushed.
|
||||
bool hasTarget = mContext->hasOutputTarget();
|
||||
if (CC_UNLIKELY(!hasTarget || !canDraw)) {
|
||||
if (!hasTarget) {
|
||||
if (CC_UNLIKELY(!mContext->hasSurface() || !canDraw)) {
|
||||
if (!mContext->hasSurface()) {
|
||||
mSyncResult |= SyncResult::LostSurfaceRewardIfFound;
|
||||
} else {
|
||||
// If we have a surface but can't draw we must be stopped
|
||||
|
||||
@@ -27,16 +27,8 @@
|
||||
#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;
|
||||
@@ -96,10 +88,6 @@ public:
|
||||
|
||||
void forceDrawNextFrame() { mForceDrawFrame = true; }
|
||||
|
||||
void setHardwareBufferRenderParams(const HardwareBufferRenderParams& params) {
|
||||
mHardwareBufferParams = params;
|
||||
}
|
||||
|
||||
void setRenderSdrHdrRatio(float ratio) { mRenderSdrHdrRatio = ratio; }
|
||||
|
||||
private:
|
||||
@@ -126,7 +114,6 @@ 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;
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* 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_
|
||||
@@ -16,19 +16,17 @@
|
||||
|
||||
#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;
|
||||
|
||||
@@ -66,14 +64,10 @@ public:
|
||||
const LightGeometry& lightGeometry, LayerUpdateQueue* layerUpdateQueue,
|
||||
const Rect& contentDrawBounds, bool opaque, const LightInfo& lightInfo,
|
||||
const std::vector<sp<RenderNode>>& renderNodes,
|
||||
FrameInfoVisualizer* profiler,
|
||||
const HardwareBufferRenderParams& bufferParams) = 0;
|
||||
FrameInfoVisualizer* profiler) = 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;
|
||||
|
||||
@@ -85,18 +85,6 @@ 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 {
|
||||
@@ -352,10 +340,6 @@ 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(
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
#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>
|
||||
@@ -77,7 +76,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();
|
||||
@@ -85,7 +84,6 @@ 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);
|
||||
|
||||
@@ -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->hasOutputTarget());
|
||||
ASSERT_FALSE(canvasContext->hasSurface());
|
||||
|
||||
canvasContext->destroy();
|
||||
}
|
||||
|
||||
@@ -1136,15 +1136,6 @@
|
||||
</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"
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* 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);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user