diff options
author | 2023-10-24 20:43:22 -0400 | |
---|---|---|
committer | 2023-10-27 16:49:22 -0400 | |
commit | 1b152e712159c0b82831a7567ce2f3c49cdc11bf (patch) | |
tree | 74c107d65ae11b92af840bcd985910ed7ec4dc42 | |
parent | 57cca107540d12c63b10ba326483b9acc4fa7b45 (diff) |
Implement automatic SV clipping
Clip SV to its ancestor clipping bounds. This enables
Z-above SurfaceView + scrolling containers to work more naturally
Replaces the hidden API of setEnableSurfaceClipping
Fixes: 298621623
Test: Sample app
Change-Id: Iaa862598e37065677f5ba163a5ac7a6fab2739ea
-rw-r--r-- | core/java/android/view/SurfaceView.java | 70 | ||||
-rw-r--r-- | graphics/java/android/graphics/RenderNode.java | 37 | ||||
-rw-r--r-- | libs/hwui/Android.bp | 1 | ||||
-rw-r--r-- | libs/hwui/DamageAccumulator.cpp | 41 | ||||
-rw-r--r-- | libs/hwui/DamageAccumulator.h | 2 | ||||
-rw-r--r-- | libs/hwui/Properties.cpp | 24 | ||||
-rw-r--r-- | libs/hwui/Properties.h | 2 | ||||
-rw-r--r-- | libs/hwui/jni/android_graphics_RenderNode.cpp | 46 |
8 files changed, 200 insertions, 23 deletions
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 5b69d7fdeb35..0ae14a2fdb30 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -43,6 +43,7 @@ import android.os.IBinder; import android.os.Looper; import android.os.RemoteException; import android.os.SystemClock; +import android.text.TextUtils; import android.util.ArraySet; import android.util.AttributeSet; import android.util.Log; @@ -51,6 +52,7 @@ import android.view.accessibility.AccessibilityNodeInfo; import android.view.accessibility.IAccessibilityEmbeddedConnection; import android.window.SurfaceSyncGroup; +import com.android.graphics.hwui.flags.Flags; import com.android.internal.view.SurfaceCallbackHelper; import java.lang.annotation.Retention; @@ -326,6 +328,8 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } }; + private final boolean mRtDrivenClipping = Flags.clipSurfaceviews(); + public SurfaceView(Context context) { this(context, null); } @@ -572,6 +576,10 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall public void setClipBounds(Rect clipBounds) { super.setClipBounds(clipBounds); + if (mRtDrivenClipping && isHardwareAccelerated()) { + return; + } + if (!mClipSurfaceToBounds || mSurfaceControl == null) { return; } @@ -915,15 +923,17 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } if (sizeChanged || creating || !isHardwareAccelerated()) { - // Set a window crop when creating the surface or changing its size to - // crop the buffer to the surface size since the buffer producer may - // use SCALING_MODE_SCALE and submit a larger size than the surface - // size. - if (mClipSurfaceToBounds && mClipBounds != null) { - surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds); - } else { - surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, - mSurfaceHeight); + if (!mRtDrivenClipping || !isHardwareAccelerated()) { + // Set a window crop when creating the surface or changing its size to + // crop the buffer to the surface size since the buffer producer may + // use SCALING_MODE_SCALE and submit a larger size than the surface + // size. + if (mClipSurfaceToBounds && mClipBounds != null) { + surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mClipBounds); + } else { + surfaceUpdateTransaction.setWindowCrop(mSurfaceControl, mSurfaceWidth, + mSurfaceHeight); + } } surfaceUpdateTransaction.setDesintationFrame(mBlastSurfaceControl, mSurfaceWidth, @@ -941,7 +951,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall mScreenRect.height() / (float) mSurfaceHeight /*postScaleY*/); } if (DEBUG_POSITION) { - Log.d(TAG, String.format( + Log.d(TAG, TextUtils.formatSimple( "%d performSurfaceTransaction %s " + "position = [%d, %d, %d, %d] surfaceSize = %dx%d", System.identityHashCode(this), @@ -1453,6 +1463,7 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } private final Rect mRTLastReportedPosition = new Rect(); + private final Rect mRTLastSetCrop = new Rect(); private class SurfaceViewPositionUpdateListener implements RenderNode.PositionUpdateListener { private final int mRtSurfaceWidth; @@ -1496,6 +1507,45 @@ public class SurfaceView extends View implements ViewRootImpl.SurfaceChangedCall } @Override + public void positionChanged(long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + try { + if (DEBUG_POSITION) { + Log.d(TAG, String.format( + "%d updateSurfacePosition RenderWorker, frameNr = %d, " + + "position = [%d, %d, %d, %d] clip = [%d, %d, %d, %d] " + + "surfaceSize = %dx%d", + System.identityHashCode(SurfaceView.this), frameNumber, + left, top, right, bottom, clipLeft, clipTop, clipRight, clipBottom, + mRtSurfaceWidth, mRtSurfaceHeight)); + } + synchronized (mSurfaceControlLock) { + if (mSurfaceControl == null) return; + + mRTLastReportedPosition.set(left, top, right, bottom); + onSetSurfacePositionAndScale(mPositionChangedTransaction, mSurfaceControl, + mRTLastReportedPosition.left /*positionLeft*/, + mRTLastReportedPosition.top /*positionTop*/, + mRTLastReportedPosition.width() + / (float) mRtSurfaceWidth /*postScaleX*/, + mRTLastReportedPosition.height() + / (float) mRtSurfaceHeight /*postScaleY*/); + + mRTLastSetCrop.set(clipLeft, clipTop, clipRight, clipBottom); + mPositionChangedTransaction.setCrop(mSurfaceControl, mRTLastSetCrop); + if (mRTLastSetCrop.isEmpty()) { + mPositionChangedTransaction.hide(mSurfaceControl); + } else { + mPositionChangedTransaction.show(mSurfaceControl); + } + } + applyOrMergeTransaction(mPositionChangedTransaction, frameNumber); + } catch (Exception ex) { + Log.e(TAG, "Exception from repositionChild", ex); + } + } + + @Override public void applyStretch(long frameNumber, float width, float height, float vecX, float vecY, float maxStretchX, float maxStretchY, float childRelativeLeft, float childRelativeTop, float childRelativeRight, diff --git a/graphics/java/android/graphics/RenderNode.java b/graphics/java/android/graphics/RenderNode.java index 15d26ebe66f6..27325694073c 100644 --- a/graphics/java/android/graphics/RenderNode.java +++ b/graphics/java/android/graphics/RenderNode.java @@ -272,6 +272,17 @@ public final class RenderNode { void positionChanged(long frameNumber, int left, int top, int right, int bottom); /** + * Called by native by a Rendering Worker thread to update window position; includes + * the local rect that represents the clipped area of the RenderNode's bounds. + * + * @hide + */ + default void positionChanged(long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + positionChanged(frameNumber, left, top, right, bottom); + } + + /** * Called by JNI * * @hide */ @@ -287,6 +298,23 @@ public final class RenderNode { } /** + * Called by JNI + * + * @hide */ + static boolean callPositionChanged2(WeakReference<PositionUpdateListener> weakListener, + long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + final PositionUpdateListener listener = weakListener.get(); + if (listener != null) { + listener.positionChanged(frameNumber, left, top, right, bottom, clipLeft, + clipTop, clipRight, clipBottom); + return true; + } else { + return false; + } + } + + /** * Call to apply a stretch effect to any child SurfaceControl layers * * TODO: Fold this into positionChanged & have HWUI do the ASurfaceControl calls? @@ -371,6 +399,15 @@ public final class RenderNode { } @Override + public void positionChanged(long frameNumber, int left, int top, int right, int bottom, + int clipLeft, int clipTop, int clipRight, int clipBottom) { + for (PositionUpdateListener pul : mListeners) { + pul.positionChanged(frameNumber, left, top, right, bottom, clipLeft, clipTop, + clipRight, clipBottom); + } + } + + @Override public void positionLost(long frameNumber) { for (PositionUpdateListener pul : mListeners) { pul.positionLost(frameNumber); diff --git a/libs/hwui/Android.bp b/libs/hwui/Android.bp index ff1eedb8eacb..da728f90e8e0 100644 --- a/libs/hwui/Android.bp +++ b/libs/hwui/Android.bp @@ -144,6 +144,7 @@ cc_defaults { "libsync", "libui", "aconfig_text_flags_c_lib", + "server_configurable_flags", ], static_libs: [ "libEGL_blobCache", diff --git a/libs/hwui/DamageAccumulator.cpp b/libs/hwui/DamageAccumulator.cpp index a8d170d00ef7..fd276419f5e5 100644 --- a/libs/hwui/DamageAccumulator.cpp +++ b/libs/hwui/DamageAccumulator.cpp @@ -242,6 +242,47 @@ void DamageAccumulator::applyRenderNodeTransform(DirtyStack* frame) { } } +SkRect DamageAccumulator::computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const { + const DirtyStack* frame = mHead; + Matrix4 transform; + SkRect pretransformResult = bounds; + while (true) { + SkRect currentBounds = pretransformResult; + pretransformResult.setEmpty(); + switch (frame->type) { + case TransformRenderNode: { + const RenderProperties& props = frame->renderNode->properties(); + // Perform clipping + if (props.getClipDamageToBounds() && !currentBounds.isEmpty()) { + if (!currentBounds.intersect( + SkRect::MakeIWH(props.getWidth(), props.getHeight()))) { + currentBounds.setEmpty(); + } + } + + // apply all transforms + mapRect(props, currentBounds, &pretransformResult); + frame->renderNode->applyViewPropertyTransforms(transform); + } break; + case TransformMatrix4: + mapRect(frame->matrix4, currentBounds, &pretransformResult); + transform.multiply(*frame->matrix4); + break; + default: + pretransformResult = currentBounds; + break; + } + if (frame->prev == frame) break; + frame = frame->prev; + } + SkRect result; + Matrix4 globalToLocal; + globalToLocal.loadInverse(transform); + mapRect(&globalToLocal, pretransformResult, &result); + *outMatrix = transform; + return result; +} + void DamageAccumulator::dirty(float left, float top, float right, float bottom) { mHead->pendingDirty.join({left, top, right, bottom}); } diff --git a/libs/hwui/DamageAccumulator.h b/libs/hwui/DamageAccumulator.h index c4249af392d3..30bf7063a627 100644 --- a/libs/hwui/DamageAccumulator.h +++ b/libs/hwui/DamageAccumulator.h @@ -61,6 +61,8 @@ public: void computeCurrentTransform(Matrix4* outMatrix) const; + SkRect computeClipAndTransform(const SkRect& bounds, Matrix4* outMatrix) const; + void finish(SkRect* totalDirty); struct StretchResult { diff --git a/libs/hwui/Properties.cpp b/libs/hwui/Properties.cpp index 5e5eb4a51b35..ad600d0bab93 100644 --- a/libs/hwui/Properties.cpp +++ b/libs/hwui/Properties.cpp @@ -20,15 +20,26 @@ #ifdef __ANDROID__ #include "HWUIProperties.sysprop.h" #endif -#include "src/core/SkTraceEventCommon.h" +#include <android-base/properties.h> +#include <cutils/compiler.h> +#include <log/log.h> #include <algorithm> #include <cstdlib> #include <optional> -#include <android-base/properties.h> -#include <cutils/compiler.h> -#include <log/log.h> +#include "src/core/SkTraceEventCommon.h" + +#ifdef __ANDROID__ +#include <com_android_graphics_hwui_flags.h> +namespace hwui_flags = com::android::graphics::hwui::flags; +#else +namespace hwui_flags { +constexpr bool clip_surfaceviews() { + return false; +} +} // namespace hwui_flags +#endif namespace android { namespace uirenderer { @@ -92,6 +103,8 @@ bool Properties::isSystemOrPersistent = false; float Properties::maxHdrHeadroomOn8bit = 5.f; // TODO: Refine this number +bool Properties::clipSurfaceViews = false; + StretchEffectBehavior Properties::stretchEffectBehavior = StretchEffectBehavior::ShaderHWUI; DrawingEnabled Properties::drawingEnabled = DrawingEnabled::NotInitialized; @@ -159,6 +172,9 @@ bool Properties::load() { // call isDrawingEnabled to force loading of the property isDrawingEnabled(); + clipSurfaceViews = + base::GetBoolProperty("debug.hwui.clip_surfaceviews", hwui_flags::clip_surfaceviews()); + return (prevDebugLayersUpdates != debugLayersUpdates) || (prevDebugOverdraw != debugOverdraw); } diff --git a/libs/hwui/Properties.h b/libs/hwui/Properties.h index bb477449fff0..bca57e9e4678 100644 --- a/libs/hwui/Properties.h +++ b/libs/hwui/Properties.h @@ -325,6 +325,8 @@ public: static float maxHdrHeadroomOn8bit; + static bool clipSurfaceViews; + static StretchEffectBehavior getStretchEffectBehavior() { return stretchEffectBehavior; } diff --git a/libs/hwui/jni/android_graphics_RenderNode.cpp b/libs/hwui/jni/android_graphics_RenderNode.cpp index 2a218a25913d..a1b05c186ec0 100644 --- a/libs/hwui/jni/android_graphics_RenderNode.cpp +++ b/libs/hwui/jni/android_graphics_RenderNode.cpp @@ -568,6 +568,7 @@ static void android_view_RenderNode_forceEndAnimators(JNIEnv* env, jobject clazz struct { jclass clazz; jmethodID callPositionChanged; + jmethodID callPositionChanged2; jmethodID callApplyStretch; jmethodID callPositionLost; } gPositionListener; @@ -589,14 +590,25 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, virtual void onPositionUpdated(RenderNode& node, const TreeInfo& info) override { if (CC_UNLIKELY(!mListener || !info.updateWindowPositions)) return; - Matrix4 transform; - info.damageAccumulator->computeCurrentTransform(&transform); const RenderProperties& props = node.properties(); + const bool enableClip = Properties::clipSurfaceViews; - uirenderer::Rect bounds(props.getWidth(), props.getHeight()); + Matrix4 transform; + SkIRect clipBounds; + if (enableClip) { + uirenderer::Rect initialClipBounds; + props.getClippingRectForFlags(props.getClippingFlags(), &initialClipBounds); + clipBounds = + info.damageAccumulator + ->computeClipAndTransform(initialClipBounds.toSkRect(), &transform) + .roundOut(); + } else { + info.damageAccumulator->computeCurrentTransform(&transform); + } bool useStretchShader = Properties::getStretchEffectBehavior() != StretchEffectBehavior::UniformScale; // Compute the transform bounds first before calculating the stretch + uirenderer::Rect bounds(props.getWidth(), props.getHeight()); transform.mapRect(bounds); bool hasStretch = useStretchShader && info.stretchEffectCount; @@ -614,10 +626,11 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, bounds.roundOut(); } - if (mPreviousPosition == bounds) { + if (mPreviousPosition == bounds && mPreviousClip == clipBounds) { return; } mPreviousPosition = bounds; + mPreviousClip = clipBounds; ATRACE_NAME("Update SurfaceView position"); @@ -629,11 +642,23 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, // In particular if the app removes a view from the view tree before // this callback is dispatched, then we lose the position // information for this frame. - jboolean keepListening = env->CallStaticBooleanMethod( - gPositionListener.clazz, gPositionListener.callPositionChanged, mListener, - static_cast<jlong>(info.canvasContext.getFrameNumber()), - static_cast<jint>(bounds.left), static_cast<jint>(bounds.top), - static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom)); + jboolean keepListening; + if (!enableClip) { + keepListening = env->CallStaticBooleanMethod( + gPositionListener.clazz, gPositionListener.callPositionChanged, mListener, + static_cast<jlong>(info.canvasContext.getFrameNumber()), + static_cast<jint>(bounds.left), static_cast<jint>(bounds.top), + static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom)); + } else { + keepListening = env->CallStaticBooleanMethod( + gPositionListener.clazz, gPositionListener.callPositionChanged2, mListener, + static_cast<jlong>(info.canvasContext.getFrameNumber()), + static_cast<jint>(bounds.left), static_cast<jint>(bounds.top), + static_cast<jint>(bounds.right), static_cast<jint>(bounds.bottom), + static_cast<jint>(clipBounds.fLeft), static_cast<jint>(clipBounds.fTop), + static_cast<jint>(clipBounds.fRight), + static_cast<jint>(clipBounds.fBottom)); + } if (!keepListening) { env->DeleteGlobalRef(mListener); mListener = nullptr; @@ -738,6 +763,7 @@ static void android_view_RenderNode_requestPositionUpdates(JNIEnv* env, jobject, JavaVM* mVm; jobject mListener; uirenderer::Rect mPreviousPosition; + uirenderer::Rect mPreviousClip; }; RenderNode* renderNode = reinterpret_cast<RenderNode*>(renderNodePtr); @@ -866,6 +892,8 @@ int register_android_view_RenderNode(JNIEnv* env) { gPositionListener.clazz = MakeGlobalRefOrDie(env, clazz); gPositionListener.callPositionChanged = GetStaticMethodIDOrDie( env, clazz, "callPositionChanged", "(Ljava/lang/ref/WeakReference;JIIII)Z"); + gPositionListener.callPositionChanged2 = GetStaticMethodIDOrDie( + env, clazz, "callPositionChanged2", "(Ljava/lang/ref/WeakReference;JIIIIIIII)Z"); gPositionListener.callApplyStretch = GetStaticMethodIDOrDie( env, clazz, "callApplyStretch", "(Ljava/lang/ref/WeakReference;JFFFFFFFFFF)Z"); gPositionListener.callPositionLost = GetStaticMethodIDOrDie( |