Fix damage tracking for RenderNode drawn twice

Bug: 127866048
Test: CustomRenderer demo
Change-Id: I431a7284b1d0a026e06500a78f41830a268235a5
This commit is contained in:
John Reck
2019-03-07 17:01:08 -08:00
parent 4348a4d707
commit f1aa7909e6
5 changed files with 81 additions and 18 deletions

View File

@@ -134,6 +134,7 @@ void RenderNode::removeAnimator(const sp<BaseRenderNodeAnimator>& animator) {
void RenderNode::damageSelf(TreeInfo& info) {
if (isRenderable()) {
mDamageGenerationId = info.damageGenerationId;
if (properties().getClipDamageToBounds()) {
info.damageAccumulator->dirty(0, 0, properties().getWidth(), properties().getHeight());
} else {
@@ -199,6 +200,12 @@ void RenderNode::pushLayerUpdate(TreeInfo& info) {
* stencil buffer may be needed. Views that use a functor to draw will be forced onto a layer.
*/
void RenderNode::prepareTreeImpl(TreeObserver& observer, TreeInfo& info, bool functorsNeedLayer) {
if (mDamageGenerationId == info.damageGenerationId) {
// We hit the same node a second time in the same tree. We don't know the minimal
// damage rect anymore, so just push the biggest we can onto our parent's transform
// We push directly onto parent in case we are clipped to bounds but have moved position.
info.damageAccumulator->dirty(DIRTY_MIN, DIRTY_MIN, DIRTY_MAX, DIRTY_MAX);
}
info.damageAccumulator->pushTransform(this);
if (info.mode == TreeInfo::MODE_FULL) {

View File

@@ -255,6 +255,8 @@ private:
DisplayList* mDisplayList;
DisplayList* mStagingDisplayList;
int64_t mDamageGenerationId;
friend class AnimatorManager;
AnimatorManager mAnimatorManager;

View File

@@ -24,6 +24,7 @@ TreeInfo::TreeInfo(TraversalMode mode, renderthread::CanvasContext& canvasContex
: mode(mode)
, prepareTextures(mode == MODE_FULL)
, canvasContext(canvasContext)
, damageGenerationId(canvasContext.getFrameNumber())
, disableForceDark(canvasContext.useForceDark() ? 0 : 1) {}
} // namespace android::uirenderer

View File

@@ -87,6 +87,7 @@ public:
// Must not be null during actual usage
DamageAccumulator* damageAccumulator = nullptr;
int64_t damageGenerationId = 0;
LayerUpdateQueue* layerUpdateQueue = nullptr;
ErrorHandler* errorHandler = nullptr;

View File

@@ -16,6 +16,7 @@
package com.android.test.hwui;
import android.animation.ObjectAnimator;
import android.app.Activity;
import android.graphics.Color;
import android.graphics.HardwareRenderer;
@@ -23,12 +24,15 @@ import android.graphics.Paint;
import android.graphics.RecordingCanvas;
import android.graphics.RenderNode;
import android.os.Bundle;
import android.util.Log;
import android.os.Handler;
import android.view.SurfaceHolder;
public class CustomRenderer extends Activity {
private RenderNode mContent = new RenderNode("CustomRenderer");
private RenderNode mRootNode = new RenderNode("CustomRenderer");
private RenderNode mChildNode = new RenderNode("RedBox");
private HardwareRenderer mRenderer = new HardwareRenderer();
private ObjectAnimator mAnimator;
private Handler mRedrawHandler = new Handler(true);
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -36,6 +40,64 @@ public class CustomRenderer extends Activity {
getWindow().takeSurface(mSurfaceCallbacks);
}
@Override
protected void onStart() {
super.onStart();
mAnimator = ObjectAnimator.ofFloat(mChildNode, "translationY", 0, 300);
mAnimator.setRepeatMode(ObjectAnimator.REVERSE);
mAnimator.setRepeatCount(ObjectAnimator.INFINITE);
final Runnable redraw = this::draw;
mAnimator.addUpdateListener(animation -> {
mRedrawHandler.post(redraw);
});
}
@Override
protected void onStop() {
super.onStop();
mAnimator.end();
mAnimator = null;
}
private void setupRoot(int width, int height) {
mRootNode.setPosition(0, 0, width, height);
RecordingCanvas canvas = mRootNode.beginRecording();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setTextAlign(Paint.Align.CENTER);
float textSize = Math.min(width, height) * .05f;
paint.setTextSize(textSize);
canvas.drawText("Hello custom renderer!", width / 2, textSize * 2, paint);
canvas.translate(0, height / 4);
canvas.drawRenderNode(mChildNode);
canvas.translate(width / 2, 0);
canvas.drawRenderNode(mChildNode);
mRootNode.endRecording();
setupChild(width / 2, height / 2);
}
private void setupChild(int width, int height) {
mChildNode.setPosition(0, 0, width, height);
mChildNode.setScaleX(.5f);
mChildNode.setScaleY(.5f);
RecordingCanvas canvas = mChildNode.beginRecording();
canvas.drawColor(Color.RED);
mChildNode.endRecording();
}
private void draw() {
// Since we are constantly pumping frames between onStart & onStop we don't really
// care about any errors that may happen. They will self-correct.
mRenderer.createRenderRequest()
.setVsyncTime(System.nanoTime())
.syncAndDraw();
}
private SurfaceHolder.Callback2 mSurfaceCallbacks = new SurfaceHolder.Callback2() {
@Override
@@ -48,24 +110,14 @@ public class CustomRenderer extends Activity {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mContent.setLeftTopRightBottom(0, 0, width, height);
RecordingCanvas canvas = mContent.beginRecording();
canvas.drawColor(Color.WHITE);
Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.BLACK);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(Math.min(width, height) * .05f);
canvas.drawText("Hello custom renderer!", width / 2, height / 2, paint);
mContent.endRecording();
setupRoot(width, height);
mRenderer.setContentRoot(mContent);
mRenderer.setContentRoot(mRootNode);
mRenderer.setSurface(holder.getSurface());
mRenderer.createRenderRequest()
.setVsyncTime(System.nanoTime())
.setFrameCommitCallback(Runnable::run, () -> {
Log.d("CustomRenderer", "Frame committed!");
})
.syncAndDraw();
draw();
if (!mAnimator.isStarted()) {
mAnimator.start();
}
}
@Override