Send syncStatus and return FrameCommitCallback in FrameCallback
FrameCallback sends back the syncStatus and allows the caller to return
a FrameCommitCallback. This is so the caller can evaluate the results of
the sync and determine if it wants to continue waiting for a commit
callback. In cases, where the sync has failed, there will never be a
commit callback so the caller can avoid waiting.
This is partically helpful for VRI because it wants to sync with a frame
that will draw, but also doesn't want to get stuck indefinitely waiting
on the callback. VRI can check the syncStatus and only handle blast sync
and/or reportDraw when its determined that a draw will happen.
This allows VRI to remove frameCompleteCallback since it can now wait
until a buffer has drawn instead of when the vsync has completed. This makes
sure it waits on FrameDropped since that result indicates HWUI will draw on
the next vsync
Test: BlastSync
Bug: 200284684
Change-Id: Ic6e2f08ea21ac8a1634a3389c927fcb68ede3f7b
diff --git a/core/java/android/view/ThreadedRenderer.java b/core/java/android/view/ThreadedRenderer.java
index 00754af..1566f9e 100644
--- a/core/java/android/view/ThreadedRenderer.java
+++ b/core/java/android/view/ThreadedRenderer.java
@@ -478,6 +478,19 @@
}
/**
+ * Remove a frame drawing callback that was added via
+ * {@link #registerRtFrameCallback(FrameDrawingCallback)}
+ *
+ * @param callback The callback to unregister.
+ */
+ void unregisterRtFrameCallback(@NonNull FrameDrawingCallback callback) {
+ if (mNextRtFrameCallbacks == null) {
+ return;
+ }
+ mNextRtFrameCallbacks.remove(callback);
+ }
+
+ /**
* Destroys all hardware rendering resources associated with the specified
* view hierarchy.
*
@@ -679,9 +692,31 @@
if (mNextRtFrameCallbacks != null) {
final ArrayList<FrameDrawingCallback> frameCallbacks = mNextRtFrameCallbacks;
mNextRtFrameCallbacks = null;
- setFrameCallback(frame -> {
- for (int i = 0; i < frameCallbacks.size(); ++i) {
- frameCallbacks.get(i).onFrameDraw(frame);
+ setFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ ArrayList<FrameCommitCallback> frameCommitCallbacks = new ArrayList<>();
+ for (int i = 0; i < frameCallbacks.size(); ++i) {
+ FrameCommitCallback frameCommitCallback = frameCallbacks.get(i)
+ .onFrameDraw(syncResult, frame);
+ if (frameCommitCallback != null) {
+ frameCommitCallbacks.add(frameCommitCallback);
+ }
+ }
+
+ if (frameCommitCallbacks.isEmpty()) {
+ return null;
+ }
+
+ return didProduceBuffer -> {
+ for (int i = 0; i < frameCommitCallbacks.size(); ++i) {
+ frameCommitCallbacks.get(i).onFrameCommit(didProduceBuffer);
+ }
+ };
}
});
}
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 8109197..12e914f 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -16,6 +16,8 @@
package android.view;
+import static android.graphics.HardwareRenderer.SYNC_CONTEXT_IS_STOPPED;
+import static android.graphics.HardwareRenderer.SYNC_LOST_SURFACE_REWARD_IF_FOUND;
import static android.os.IInputConstants.INVALID_INPUT_EVENT_ID;
import static android.view.Display.DEFAULT_DISPLAY;
import static android.view.Display.INVALID_DISPLAY;
@@ -1395,11 +1397,20 @@
*/
public void registerRtFrameCallback(@NonNull FrameDrawingCallback callback) {
if (mAttachInfo.mThreadedRenderer != null) {
- mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frame -> {
- try {
- callback.onFrameDraw(frame);
- } catch (Exception e) {
- Log.e(TAG, "Exception while executing onFrameDraw", e);
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
+ }
+
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult,
+ long frame) {
+ try {
+ return callback.onFrameDraw(syncResult, frame);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception while executing onFrameDraw", e);
+ }
+ return null;
}
});
}
@@ -4026,61 +4037,6 @@
return mAttachInfo.mThreadedRenderer != null && mAttachInfo.mThreadedRenderer.isEnabled();
}
- private boolean addFrameCompleteCallbackIfNeeded(boolean useBlastSync,
- boolean reportNextDraw) {
- if (!isHardwareEnabled()) {
- return false;
- }
-
- if (!useBlastSync && !reportNextDraw) {
- return false;
- }
-
- if (DEBUG_BLAST) {
- Log.d(mTag, "Creating frameCompleteCallback");
- }
-
- final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
- mBLASTDrawConsumer = null;
-
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(() -> {
- long frameNr = mBlastBufferQueue.getLastAcquiredFrameNum();
- if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameCompleteCallback "
- + " lastAcquiredFrameNum=" + frameNr
- + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum);
- }
-
- boolean frameWasNotDrawn = frameNr != mRtLastAttemptedDrawFrameNum;
- // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
- // draw attempt. The next transaction and transaction complete callback were only set
- // for the current draw attempt.
- if (frameWasNotDrawn) {
- mBlastBufferQueue.setSyncTransaction(null);
- // Apply the transactions that were sent to mergeWithNextTransaction since the
- // frame didn't draw on this vsync. It's possible the frame will draw later, but
- // it's better to not be sync than to block on a frame that may never come.
- mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
- }
-
- Transaction tmpTransaction = new Transaction();
- tmpTransaction.merge(mRtBLASTSyncTransaction);
- mHandler.postAtFrontOfQueue(() -> {
- if (useBlastSync) {
- mSurfaceChangedTransaction.merge(tmpTransaction);
- if (blastSyncConsumer != null) {
- blastSyncConsumer.accept(mSurfaceChangedTransaction);
- }
- }
-
- if (reportNextDraw) {
- pendingDrawFinished();
- }
- });
- });
- return true;
- }
-
private void addFrameCommitCallbackIfNeeded() {
if (!isHardwareEnabled()) {
return;
@@ -4111,51 +4067,131 @@
});
}
- private void addFrameCallbackIfNeeded(boolean useBlastSync) {
+ private HardwareRenderer.FrameCommitCallback createFrameCommitCallbackForSync(
+ boolean useBlastSync, boolean reportNextDraw, Consumer<Transaction> blastSyncConsumer) {
+ return didProduceBuffer -> {
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Received frameCommittedCallback "
+ + " lastAttemptedDrawFrameNum=" + mRtLastAttemptedDrawFrameNum
+ + " didProduceBuffer=" + didProduceBuffer);
+ }
+
+ // If frame wasn't drawn, clear out the next transaction so it doesn't affect the next
+ // draw attempt. The next transaction and transaction complete callback were only set
+ // for the current draw attempt.
+ if (!didProduceBuffer) {
+ mBlastBufferQueue.setSyncTransaction(null);
+ // Apply the transactions that were sent to mergeWithNextTransaction since the
+ // frame didn't draw on this vsync. It's possible the frame will draw later, but
+ // it's better to not be sync than to block on a frame that may never come.
+ mBlastBufferQueue.applyPendingTransactions(mRtLastAttemptedDrawFrameNum);
+ }
+
+ Transaction tmpTransaction = new Transaction();
+ tmpTransaction.merge(mRtBLASTSyncTransaction);
+ // Post at front of queue so the buffer can be processed immediately and allow RT
+ // to continue processing new buffers. If RT tries to process buffers before the sync
+ // buffer is applied, the new buffers will not get acquired and could result in a
+ // deadlock. UI thread would wait on RT, but RT would be blocked waiting for a free
+ // buffer.
+ mHandler.postAtFrontOfQueue(() -> {
+ if (useBlastSync) {
+ mSurfaceChangedTransaction.merge(tmpTransaction);
+ if (blastSyncConsumer != null) {
+ blastSyncConsumer.accept(mSurfaceChangedTransaction);
+ }
+ }
+
+ if (reportNextDraw) {
+ pendingDrawFinished();
+ }
+ });
+ };
+ }
+
+ @Nullable
+ private FrameDrawingCallback createFrameDrawingCallbackIfNeeded(boolean useBlastSync,
+ boolean reportNextDraw) {
+ if (!isHardwareEnabled()) {
+ return null;
+ }
final boolean hasBlurUpdates = mBlurRegionAggregator.hasUpdates();
final boolean needsCallbackForBlur = hasBlurUpdates || mBlurRegionAggregator.hasRegions();
- if (!useBlastSync && !needsCallbackForBlur) {
- return;
+ if (!useBlastSync && !needsCallbackForBlur && !reportNextDraw) {
+ return null;
}
+ final Consumer<SurfaceControl.Transaction> blastSyncConsumer = mBLASTDrawConsumer;
+ mBLASTDrawConsumer = null;
+
if (DEBUG_BLAST) {
Log.d(mTag, "Creating frameDrawingCallback"
+ " nextDrawUseBlastSync=" + useBlastSync
- + " hasBlurUpdates=" + hasBlurUpdates);
+ + " reportNextDraw=" + reportNextDraw
+ + " hasBlurUpdates=" + hasBlurUpdates
+ + " hasBlastSyncConsumer=" + (blastSyncConsumer != null));
}
+
final BackgroundBlurDrawable.BlurRegion[] blurRegionsForFrame =
needsCallbackForBlur ? mBlurRegionAggregator.getBlurRegionsCopyForRT() : null;
// The callback will run on the render thread.
- HardwareRenderer.FrameDrawingCallback frameDrawingCallback = frame -> {
- if (DEBUG_BLAST) {
- Log.d(mTag, "Received frameDrawingCallback frameNum=" + frame + "."
- + " Creating transactionCompleteCallback=" + useBlastSync);
+ return new FrameDrawingCallback() {
+ @Override
+ public void onFrameDraw(long frame) {
}
- mRtLastAttemptedDrawFrameNum = frame;
+ @Override
+ public HardwareRenderer.FrameCommitCallback onFrameDraw(int syncResult, long frame) {
+ if (DEBUG_BLAST) {
+ Log.d(mTag,
+ "Received frameDrawingCallback syncResult=" + syncResult + " frameNum="
+ + frame + ".");
+ }
- if (needsCallbackForBlur) {
- mBlurRegionAggregator
- .dispatchBlurTransactionIfNeeded(frame, blurRegionsForFrame, hasBlurUpdates);
- }
+ mRtLastAttemptedDrawFrameNum = frame;
- if (mBlastBufferQueue == null) {
- return;
- }
+ if (needsCallbackForBlur) {
+ mBlurRegionAggregator.dispatchBlurTransactionIfNeeded(frame,
+ blurRegionsForFrame, hasBlurUpdates);
+ }
- if (useBlastSync) {
- // Frame callbacks will always occur after submitting draw requests and before
- // the draw actually occurs. This will ensure that we set the next transaction
- // for the frame that's about to get drawn and not on a previous frame that.
+ if (mBlastBufferQueue == null) {
+ return null;
+ }
- // We don't need to synchronize mRtBLASTSyncTransaction here since it's not
- // being modified and only sent to BlastBufferQueue.
- mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
+ if (!useBlastSync && !reportNextDraw) {
+ return null;
+ }
+
+ // If the syncResults are SYNC_LOST_SURFACE_REWARD_IF_FOUND or
+ // SYNC_CONTEXT_IS_STOPPED it means nothing will draw. There's no need to set up
+ // any blast sync or commit callback, and the code should directly call
+ // pendingDrawFinished.
+ if ((syncResult
+ & (SYNC_LOST_SURFACE_REWARD_IF_FOUND | SYNC_CONTEXT_IS_STOPPED)) != 0) {
+ if (reportNextDraw) {
+ mHandler.postAtFrontOfQueue(() -> pendingDrawFinished());
+ }
+ return null;
+ }
+
+ if (DEBUG_BLAST) {
+ Log.d(mTag, "Setting up sync and frameCommitCallback");
+ }
+
+ if (useBlastSync) {
+ // Frame callbacks will always occur after submitting draw requests and before
+ // the draw actually occurs. This will ensure that we set the next transaction
+ // for the frame that's about to get drawn and not on a previous frame.
+ mBlastBufferQueue.setSyncTransaction(mRtBLASTSyncTransaction);
+ }
+
+ return createFrameCommitCallbackForSync(useBlastSync, reportNextDraw,
+ blastSyncConsumer);
}
};
- registerRtFrameCallback(frameDrawingCallback);
}
private void performDraw(boolean useBlastSync) {
@@ -4171,15 +4207,20 @@
mIsDrawing = true;
Trace.traceBegin(Trace.TRACE_TAG_VIEW, "draw");
- addFrameCallbackIfNeeded(useBlastSync);
+ FrameDrawingCallback frameDrawingCallback = createFrameDrawingCallbackIfNeeded(useBlastSync,
+ mReportNextDraw);
+ if (frameDrawingCallback != null) {
+ mAttachInfo.mThreadedRenderer.registerRtFrameCallback(frameDrawingCallback);
+ }
addFrameCommitCallbackIfNeeded();
- boolean usingAsyncReport = addFrameCompleteCallbackIfNeeded(useBlastSync, mReportNextDraw);
+ boolean usingAsyncReport = isHardwareEnabled() && (useBlastSync || mReportNextDraw);
try {
boolean canUseAsync = draw(fullRedrawNeeded);
if (usingAsyncReport && !canUseAsync) {
- mAttachInfo.mThreadedRenderer.setFrameCompleteCallback(null);
+ mAttachInfo.mThreadedRenderer.setFrameCallback(null);
usingAsyncReport = false;
+ mAttachInfo.mThreadedRenderer.unregisterRtFrameCallback(frameDrawingCallback);
}
} finally {
mIsDrawing = false;
diff --git a/graphics/java/android/graphics/HardwareRenderer.java b/graphics/java/android/graphics/HardwareRenderer.java
index 2b18350..fd4bed1 100644
--- a/graphics/java/android/graphics/HardwareRenderer.java
+++ b/graphics/java/android/graphics/HardwareRenderer.java
@@ -902,6 +902,20 @@
* @param frame The id of the frame being drawn.
*/
void onFrameDraw(long frame);
+
+ /**
+ * Invoked during a frame drawing.
+ *
+ * @param syncResult The result of the draw. Should be a value or a combination of values
+ * from {@link SyncAndDrawResult}
+ * @param frame The id of the frame being drawn.
+ *
+ * @return A {@link FrameCommitCallback} that will report back if the current vsync draws.
+ */
+ default FrameCommitCallback onFrameDraw(@SyncAndDrawResult int syncResult, long frame) {
+ onFrameDraw(frame);
+ return null;
+ }
}
/**
diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
index bd93a4f..27865b3 100644
--- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
+++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp
@@ -609,10 +609,19 @@
LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM");
auto globalCallbackRef = std::make_shared<JGlobalRefHolder>(vm,
env->NewGlobalRef(frameCallback));
- proxy->setFrameCallback([globalCallbackRef](int64_t frameNr) {
+ proxy->setFrameCallback([globalCallbackRef](int32_t syncResult,
+ int64_t frameNr) -> std::function<void(bool)> {
JNIEnv* env = getenv(globalCallbackRef->vm());
- env->CallVoidMethod(globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
- static_cast<jlong>(frameNr));
+ ScopedLocalRef<jobject> frameCommitCallback(
+ env, env->CallObjectMethod(
+ globalCallbackRef->object(), gFrameDrawingCallback.onFrameDraw,
+ static_cast<jint>(syncResult), static_cast<jlong>(frameNr)));
+ if (frameCommitCallback == nullptr) {
+ return nullptr;
+ }
+ sp<FrameCommitWrapper> wrapper =
+ sp<FrameCommitWrapper>::make(env, frameCommitCallback.get());
+ return [wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); };
});
}
}
@@ -623,7 +632,7 @@
if (!callback) {
proxy->setFrameCommitCallback(nullptr);
} else {
- sp<FrameCommitWrapper> wrapper = new FrameCommitWrapper{env, callback};
+ sp<FrameCommitWrapper> wrapper = sp<FrameCommitWrapper>::make(env, callback);
proxy->setFrameCommitCallback(
[wrapper](bool didProduceBuffer) { wrapper->onFrameCommit(didProduceBuffer); });
}
@@ -1003,8 +1012,9 @@
jclass frameCallbackClass = FindClassOrDie(env,
"android/graphics/HardwareRenderer$FrameDrawingCallback");
- gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass,
- "onFrameDraw", "(J)V");
+ gFrameDrawingCallback.onFrameDraw =
+ GetMethodIDOrDie(env, frameCallbackClass, "onFrameDraw",
+ "(IJ)Landroid/graphics/HardwareRenderer$FrameCommitCallback;");
jclass frameCommitClass =
FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameCommitCallback");
diff --git a/libs/hwui/renderthread/DrawFrameTask.cpp b/libs/hwui/renderthread/DrawFrameTask.cpp
index 94aedd0..8c98c72 100644
--- a/libs/hwui/renderthread/DrawFrameTask.cpp
+++ b/libs/hwui/renderthread/DrawFrameTask.cpp
@@ -158,7 +158,8 @@
// Grab a copy of everything we need
CanvasContext* context = mContext;
- std::function<void(int64_t)> frameCallback = std::move(mFrameCallback);
+ std::function<std::function<void(bool)>(int32_t, int64_t)> frameCallback =
+ std::move(mFrameCallback);
std::function<void()> frameCompleteCallback = std::move(mFrameCompleteCallback);
mFrameCallback = nullptr;
mFrameCompleteCallback = nullptr;
@@ -173,8 +174,13 @@
// Even if we aren't drawing this vsync pulse the next frame number will still be accurate
if (CC_UNLIKELY(frameCallback)) {
- context->enqueueFrameWork(
- [frameCallback, frameNr = context->getFrameNumber()]() { frameCallback(frameNr); });
+ context->enqueueFrameWork([frameCallback, context, syncResult = mSyncResult,
+ frameNr = context->getFrameNumber()]() {
+ auto frameCommitCallback = std::move(frameCallback(syncResult, frameNr));
+ if (frameCommitCallback) {
+ context->addFrameCommitListener(std::move(frameCommitCallback));
+ }
+ });
}
nsecs_t dequeueBufferDuration = 0;
diff --git a/libs/hwui/renderthread/DrawFrameTask.h b/libs/hwui/renderthread/DrawFrameTask.h
index e3ea802..8ad8abc 100644
--- a/libs/hwui/renderthread/DrawFrameTask.h
+++ b/libs/hwui/renderthread/DrawFrameTask.h
@@ -77,7 +77,7 @@
void run();
- void setFrameCallback(std::function<void(int64_t)>&& callback) {
+ void setFrameCallback(std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback) {
mFrameCallback = std::move(callback);
}
@@ -126,7 +126,7 @@
int64_t mFrameInfo[UI_THREAD_FRAME_INFO_SIZE];
- std::function<void(int64_t)> mFrameCallback;
+ std::function<std::function<void(bool)>(int32_t, int64_t)> mFrameCallback;
std::function<void(bool)> mFrameCommitCallback;
std::function<void()> mFrameCompleteCallback;
diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp
index 430c4d3..026699c 100644
--- a/libs/hwui/renderthread/RenderProxy.cpp
+++ b/libs/hwui/renderthread/RenderProxy.cpp
@@ -327,7 +327,8 @@
[this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); });
}
-void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) {
+void RenderProxy::setFrameCallback(
+ std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback) {
mDrawFrameTask.setFrameCallback(std::move(callback));
}
diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h
index 6d46be4..491dbd7 100644
--- a/libs/hwui/renderthread/RenderProxy.h
+++ b/libs/hwui/renderthread/RenderProxy.h
@@ -124,7 +124,7 @@
void setASurfaceTransactionCallback(
const std::function<bool(int64_t, int64_t, int64_t)>& callback);
void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback);
- void setFrameCallback(std::function<void(int64_t)>&& callback);
+ void setFrameCallback(std::function<std::function<void(bool)>(int32_t, int64_t)>&& callback);
void setFrameCommitCallback(std::function<void(bool)>&& callback);
void setFrameCompleteCallback(std::function<void()>&& callback);