diff options
| -rw-r--r-- | libs/gui/LayerState.cpp | 2 | ||||
| -rw-r--r-- | libs/gui/SurfaceComposerClient.cpp | 11 | ||||
| -rw-r--r-- | libs/gui/include/gui/LayerState.h | 5 | ||||
| -rw-r--r-- | libs/gui/include/gui/SurfaceComposerClient.h | 6 | ||||
| -rw-r--r-- | services/surfaceflinger/BufferStateLayer.cpp | 16 | ||||
| -rw-r--r-- | services/surfaceflinger/BufferStateLayer.h | 3 | ||||
| -rw-r--r-- | services/surfaceflinger/Layer.h | 5 | ||||
| -rw-r--r-- | services/surfaceflinger/RefreshRateOverlay.cpp | 4 | ||||
| -rw-r--r-- | services/surfaceflinger/SurfaceFlinger.cpp | 2 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp | 60 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp | 2 | ||||
| -rw-r--r-- | services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp | 35 |
12 files changed, 121 insertions, 30 deletions
diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 837e5b35f7..5bed69cfc7 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -174,6 +174,7 @@ status_t layer_state_t::write(Parcel& output) const SAFE_PARCEL(output.write, destinationFrame); SAFE_PARCEL(output.writeBool, isTrustedOverlay); + SAFE_PARCEL(output.writeStrongBinder, releaseBufferEndpoint); return NO_ERROR; } @@ -303,6 +304,7 @@ status_t layer_state_t::read(const Parcel& input) SAFE_PARCEL(input.read, destinationFrame); SAFE_PARCEL(input.readBool, &isTrustedOverlay); + SAFE_PARCEL(input.readNullableStrongBinder, &releaseBufferEndpoint); return NO_ERROR; } diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index ce9a1f7918..dc71b6a212 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -147,8 +147,16 @@ int64_t TransactionCompletedListener::getNextIdLocked() { return mCallbackIdCounter++; } +sp<TransactionCompletedListener> TransactionCompletedListener::sInstance = nullptr; + +void TransactionCompletedListener::setInstance(const sp<TransactionCompletedListener>& listener) { + sInstance = listener; +} + sp<TransactionCompletedListener> TransactionCompletedListener::getInstance() { - static sp<TransactionCompletedListener> sInstance = new TransactionCompletedListener; + if (sInstance == nullptr) { + sInstance = new TransactionCompletedListener; + } return sInstance; } @@ -1274,6 +1282,7 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setBuffe removeReleaseBufferCallback(s); s->what |= layer_state_t::eBufferChanged; s->buffer = buffer; + s->releaseBufferEndpoint = IInterface::asBinder(TransactionCompletedListener::getIInstance()); if (mIsAutoTimestamp) { mDesiredPresentTime = systemTime(); } diff --git a/libs/gui/include/gui/LayerState.h b/libs/gui/include/gui/LayerState.h index b27d9cf2bc..8dc8b9a87b 100644 --- a/libs/gui/include/gui/LayerState.h +++ b/libs/gui/include/gui/LayerState.h @@ -242,6 +242,11 @@ struct layer_state_t { // is used to remove the old callback from the client process map if it is // overwritten by another setBuffer call. ReleaseCallbackId releaseCallbackId; + + // Stores which endpoint the release information should be sent to. We don't want to send the + // releaseCallbackId and release fence to all listeners so we store which listener the setBuffer + // was called with. + sp<IBinder> releaseBufferEndpoint; }; struct ComposerState { diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 8993891398..1d758e0dde 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -655,8 +655,10 @@ public: }; class TransactionCompletedListener : public BnTransactionCompletedListener { +public: TransactionCompletedListener(); +protected: int64_t getNextIdLocked() REQUIRES(mMutex); std::mutex mMutex; @@ -734,8 +736,12 @@ public: void onReleaseBuffer(ReleaseCallbackId, sp<Fence> releaseFence, uint32_t transformHint, uint32_t currentMaxAcquiredBufferCount) override; + // For Testing Only + static void setInstance(const sp<TransactionCompletedListener>&); + private: ReleaseBufferCallback popReleaseBufferCallbackLocked(const ReleaseCallbackId&); + static sp<TransactionCompletedListener> sInstance; }; } // namespace android diff --git a/services/surfaceflinger/BufferStateLayer.cpp b/services/surfaceflinger/BufferStateLayer.cpp index 6253036c41..5a59b67e83 100644 --- a/services/surfaceflinger/BufferStateLayer.cpp +++ b/services/surfaceflinger/BufferStateLayer.cpp @@ -158,7 +158,8 @@ void BufferStateLayer::onLayerDisplayed(const sp<Fence>& releaseFence) { // transaction doesn't need a previous release fence. sp<CallbackHandle> ch; for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer) { + if (handle->releasePreviousBuffer && + mDrawingState.releaseBufferEndpoint == handle->listener) { ch = handle; break; } @@ -199,14 +200,9 @@ void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) { mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid); } - // If there are multiple transactions in this frame, set the previous id on the earliest - // transacton. We don't need to pass in the released buffer id to multiple transactions. - // The buffer id does not have to correspond to any particular transaction as long as the - // listening end point is the same but the client expects the first transaction callback that - // replaces the presented buffer to contain the release fence. This follows the same logic. - // see BufferStateLayer::onLayerDisplayed. for (auto& handle : mDrawingState.callbackHandles) { - if (handle->releasePreviousBuffer) { + if (handle->releasePreviousBuffer && + mDrawingState.releaseBufferEndpoint == handle->listener) { handle->previousReleaseCallbackId = mPreviousReleaseCallbackId; break; } @@ -420,7 +416,8 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex nsecs_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info, - const sp<ITransactionCompletedListener>& releaseBufferListener) { + const sp<ITransactionCompletedListener>& releaseBufferListener, + const sp<IBinder>& releaseBufferEndpoint) { ATRACE_CALL(); if (mDrawingState.buffer) { @@ -484,6 +481,7 @@ bool BufferStateLayer::setBuffer(const std::shared_ptr<renderengine::ExternalTex mDrawingState.width = mDrawingState.buffer->getBuffer()->getWidth(); mDrawingState.height = mDrawingState.buffer->getBuffer()->getHeight(); + mDrawingState.releaseBufferEndpoint = releaseBufferEndpoint; return true; } diff --git a/services/surfaceflinger/BufferStateLayer.h b/services/surfaceflinger/BufferStateLayer.h index e5674785af..dbc0b052e8 100644 --- a/services/surfaceflinger/BufferStateLayer.h +++ b/services/surfaceflinger/BufferStateLayer.h @@ -59,7 +59,8 @@ public: const sp<Fence>& acquireFence, nsecs_t postTime, nsecs_t desiredPresentTime, bool isAutoTimestamp, const client_cache_t& clientCacheId, uint64_t frameNumber, std::optional<nsecs_t> dequeueTime, const FrameTimelineInfo& info, - const sp<ITransactionCompletedListener>& transactionListener) override; + const sp<ITransactionCompletedListener>& transactionListener, + const sp<IBinder>& releaseBufferEndpoint) override; bool setAcquireFence(const sp<Fence>& fence) override; bool setDataspace(ui::Dataspace dataspace) override; bool setHdrMetadata(const HdrMetadata& hdrMetadata) override; diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index d9ab0a6026..a53762cf64 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -280,6 +280,8 @@ public: Rect bufferCrop; Rect destinationFrame; + + sp<IBinder> releaseBufferEndpoint; }; /* @@ -417,7 +419,8 @@ public: const client_cache_t& /*clientCacheId*/, uint64_t /* frameNumber */, std::optional<nsecs_t> /* dequeueTime */, const FrameTimelineInfo& /*info*/, - const sp<ITransactionCompletedListener>& /* releaseBufferListener */) { + const sp<ITransactionCompletedListener>& /* releaseBufferListener */, + const sp<IBinder>& /* releaseBufferEndpoint */) { return false; }; virtual bool setAcquireFence(const sp<Fence>& /*fence*/) { return false; }; diff --git a/services/surfaceflinger/RefreshRateOverlay.cpp b/services/surfaceflinger/RefreshRateOverlay.cpp index 8704c88a75..bc32a1d66c 100644 --- a/services/surfaceflinger/RefreshRateOverlay.cpp +++ b/services/surfaceflinger/RefreshRateOverlay.cpp @@ -271,7 +271,7 @@ void RefreshRateOverlay::changeRefreshRate(const Fps& fps) { mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), std::nullopt /* dequeueTime */, FrameTimelineInfo{}, - nullptr /* releaseBufferListener */); + nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } @@ -285,7 +285,7 @@ void RefreshRateOverlay::onInvalidate() { mLayer->setBuffer(buffer, Fence::NO_FENCE, 0, 0, true, {}, mLayer->getHeadFrameNumber(-1 /* expectedPresentTime */), std::nullopt /* dequeueTime */, FrameTimelineInfo{}, - nullptr /* releaseBufferListener */); + nullptr /* releaseBufferListener */, nullptr /* releaseBufferEndpoint */); mFlinger.mTransactionFlags.fetch_or(eTransactionMask); } diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 361624044d..395091bb7f 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -4202,7 +4202,7 @@ uint32_t SurfaceFlinger::setClientStateLocked( if (layer->setBuffer(buffer, s.acquireFence, postTime, desiredPresentTime, isAutoTimestamp, s.cachedBuffer, frameNumber, dequeueBufferTimestamp, frameTimelineInfo, - s.releaseBufferListener)) { + s.releaseBufferListener, s.releaseBufferEndpoint)) { flags |= eTraversalNeeded; } } else if (frameTimelineInfo.vsyncId != FrameTimelineInfo::INVALID_VSYNC_ID) { diff --git a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp index 579a26ebf4..c4d42fad36 100644 --- a/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp +++ b/services/surfaceflinger/tests/ReleaseBufferCallback_test.cpp @@ -99,10 +99,10 @@ public: } static void waitForReleaseBufferCallback(ReleaseBufferCallbackHelper& releaseCallback, - const ReleaseCallbackId& expectedCallbackId) { + const ReleaseCallbackId& expectedReleaseBufferId) { ReleaseCallbackId actualReleaseBufferId; releaseCallback.getCallbackData(&actualReleaseBufferId); - EXPECT_EQ(expectedCallbackId, actualReleaseBufferId); + EXPECT_EQ(expectedReleaseBufferId, actualReleaseBufferId); releaseCallback.verifyNoCallbacks(); } static ReleaseBufferCallbackHelper* getReleaseBufferCallbackHelper() { @@ -333,4 +333,60 @@ TEST_F(ReleaseBufferCallbackTest, DISABLED_FrameDropping) { ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); } +TEST_F(ReleaseBufferCallbackTest, DISABLED_Merge_Different_Processes) { + sp<TransactionCompletedListener> firstCompletedListener = new TransactionCompletedListener(); + sp<TransactionCompletedListener> secondCompletedListener = new TransactionCompletedListener(); + + CallbackHelper callback1, callback2; + + TransactionCompletedListener::setInstance(firstCompletedListener); + + sp<SurfaceControl> layer = createBufferStateLayer(); + ReleaseBufferCallbackHelper* releaseCallback = getReleaseBufferCallbackHelper(); + + sp<GraphicBuffer> firstBuffer = getBuffer(); + ReleaseCallbackId firstBufferCallbackId(firstBuffer->getId(), generateFrameNumber()); + + // Send initial buffer for the layer + submitBuffer(layer, firstBuffer, Fence::NO_FENCE, callback1, firstBufferCallbackId, + *releaseCallback); + + ExpectedResult expected; + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED); + ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); + + // Sent a second buffer to allow the first buffer to get released. + sp<GraphicBuffer> secondBuffer = getBuffer(); + ReleaseCallbackId secondBufferCallbackId(secondBuffer->getId(), generateFrameNumber()); + + Transaction transaction1; + transaction1.setFrameNumber(layer, secondBufferCallbackId.framenumber); + transaction1.setBuffer(layer, secondBuffer, secondBufferCallbackId, + releaseCallback->getCallback()); + transaction1.setAcquireFence(layer, Fence::NO_FENCE); + transaction1.addTransactionCompletedCallback(callback1.function, callback1.getContext()); + + // Set a different TransactionCompletedListener to mimic a second process + TransactionCompletedListener::setInstance(secondCompletedListener); + + // Make sure the second "process" has a callback set up. + Transaction transaction2; + transaction2.addTransactionCompletedCallback(callback2.function, callback2.getContext()); + + // This merging order, merge transaction1 first then transaction2, seems to ensure the listener + // for transaction2 is ordered first. This makes sure the wrong process is added first to the + // layer's vector of listeners. With the bug, only the secondCompletedListener will get the + // release callback id, since it's ordered first. Then firstCompletedListener would fail to get + // the release callback id and not invoke the release callback. + Transaction().merge(std::move(transaction1)).merge(std::move(transaction2)).apply(); + + expected = ExpectedResult(); + expected.addSurface(ExpectedResult::Transaction::PRESENTED, layer, + ExpectedResult::Buffer::NOT_ACQUIRED, + ExpectedResult::PreviousBuffer::RELEASED); + ASSERT_NO_FATAL_FAILURE(waitForCallback(callback1, expected)); + ASSERT_NO_FATAL_FAILURE(waitForReleaseBufferCallback(*releaseCallback, firstBufferCallbackId)); +} + } // namespace android diff --git a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp index 2845d0ab14..a749ece835 100644 --- a/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionFrameTracerTest.cpp @@ -119,7 +119,7 @@ public: FrameTracer::FrameEvent::QUEUE, /*duration*/ 0)); layer->setBuffer(buffer, fence, postTime, /*desiredPresentTime*/ 30, false, mClientCache, frameNumber, dequeueTime, FrameTimelineInfo{}, - nullptr /* releaseBufferCallback */); + nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint*/); commitTransaction(layer.get()); bool computeVisisbleRegions; diff --git a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp index 7bf224d557..2a7921f661 100644 --- a/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp +++ b/services/surfaceflinger/tests/unittests/TransactionSurfaceFrameTest.cpp @@ -119,7 +119,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); acquireFence->signalForTest(12); commitTransaction(layer.get()); @@ -147,7 +148,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -160,7 +162,8 @@ public: mRenderEngine, false); nsecs_t start = systemTime(); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); nsecs_t end = systemTime(); acquireFence2->signalForTest(12); @@ -200,7 +203,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); acquireFence->signalForTest(12); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); @@ -228,7 +232,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -260,7 +265,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer, fence, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 3, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(2u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto bufferSurfaceFrameTX = layer->mDrawingState.bufferSurfaceFrameTX; @@ -298,7 +304,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame = layer->mDrawingState.bufferSurfaceFrameTX; @@ -309,7 +316,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); acquireFence2->signalForTest(12); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -339,7 +347,8 @@ public: 1, 0), mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 1, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); const auto droppedSurfaceFrame1 = layer->mDrawingState.bufferSurfaceFrameTX; @@ -353,7 +362,7 @@ public: auto dropStartTime1 = systemTime(); layer->setBuffer(buffer2, fence2, 10, 20, false, mClientCache, 1, std::nullopt, {/*vsyncId*/ FrameTimelineInfo::INVALID_VSYNC_ID, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */); + nullptr /* releaseBufferCallback */, nullptr /* releaseBufferEndpoint */); auto dropEndTime1 = systemTime(); EXPECT_EQ(0u, layer->mDrawingState.bufferlessSurfaceFramesTX.size()); ASSERT_NE(nullptr, layer->mDrawingState.bufferSurfaceFrameTX); @@ -367,7 +376,8 @@ public: mRenderEngine, false); auto dropStartTime2 = systemTime(); layer->setBuffer(buffer3, fence3, 10, 20, false, mClientCache, 1, std::nullopt, - {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */); + {/*vsyncId*/ 2, /*inputEventId*/ 0}, nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); auto dropEndTime2 = systemTime(); acquireFence3->signalForTest(12); @@ -411,7 +421,8 @@ public: mRenderEngine, false); layer->setBuffer(buffer1, fence1, 10, 20, false, mClientCache, 1, std::nullopt, {/*vsyncId*/ 1, /*inputEventId*/ 0}, - nullptr /* releaseBufferCallback */); + nullptr /* releaseBufferCallback */, + nullptr /* releaseBufferEndpoint */); layer->setFrameTimelineVsyncForBufferlessTransaction({/*vsyncId*/ 2, /*inputEventId*/ 0}, 10); |