Loading libs/gui/BLASTBufferQueue.cpp +35 −119 Original line number Diff line number Diff line Loading @@ -52,19 +52,18 @@ using namespace std::chrono_literals; namespace { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // RAII wrapper to defer arbitrary work until the Deferred instance is deleted. template <class F> class Deferred { template <class Mutex> class UnlockGuard { public: explicit Deferred(F f) : mF{std::move(f)} {} explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); } ~Deferred() { mF(); } ~UnlockGuard() { mLock.lock(); } Deferred(const Deferred&) = delete; Deferred& operator=(const Deferred&) = delete; UnlockGuard(const UnlockGuard&) = delete; UnlockGuard& operator=(const UnlockGuard&) = delete; private: F mF; Mutex& mLock; }; #endif Loading Loading @@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this)); #endif } void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, Loading @@ -297,11 +293,16 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, mSurfaceControl = surface; SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer); // SELinux policy may prevent this process from sending the BufferReleaseChannel's file // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. This // transaction is applied separately to ensure we don't lose the other updates. t.setApplyToken(mApplyToken) .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) .apply(false /* synchronous */, true /* oneWay */); #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); Loading @@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } if (applyTransaction) { // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction t.setApplyToken(mApplyToken).apply(false, true); t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */); } } Loading Loading @@ -419,7 +420,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.latchTime, stat.frameEventStats.dequeueReadyTime); } #if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { Loading @@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); } #endif } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } Loading Loading @@ -469,6 +468,9 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { return; } bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) bbq->drainBufferReleaseConsumer(); #endif }; } Loading Loading @@ -535,8 +537,6 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence) { auto it = mSubmitted.find(callbackId); if (it == mSubmitted.end()) { BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", callbackId.to_string().c_str()); return; } mNumAcquired--; Loading Loading @@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) ReleaseBufferCallback releaseBufferCallback = applyTransaction ? nullptr : makeReleaseBufferCallbackThunk(); #else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); #endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; Loading Loading @@ -1230,12 +1225,7 @@ public: // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. bbq->mBufferReleaseReader->clearInterrupts(); bbq->mThreadsBlockingOnDequeue++; bufferQueueLock.unlock(); Deferred cleanup{[&]() { bufferQueueLock.lock(); bbq->mThreadsBlockingOnDequeue--; }}; UnlockGuard unlockGuard{bufferQueueLock}; ATRACE_FORMAT("waiting for free buffer"); ReleaseCallbackId id; Loading Loading @@ -1345,6 +1335,21 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) void BLASTBufferQueue::drainBufferReleaseConsumer() { ATRACE_CALL(); while (true) { ReleaseCallbackId id; sp<Fence> fence; uint32_t maxAcquiredBufferCount; status_t status = mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount); if (status != OK) { return; } releaseBufferCallback(id, fence, maxAcquiredBufferCount); } } BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} { mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), Loading Loading @@ -1438,95 +1443,6 @@ void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() { } } BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) { android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!epollFd.ok(), "Failed to create buffer release background thread epoll file descriptor. " "errno=%d message='%s'", errno, strerror(errno)); epoll_event registerEndpointFd{}; registerEndpointFd.events = EPOLLIN; registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd(); status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(), ®isterEndpointFd); LOG_ALWAYS_FATAL_IF(status == -1, "Failed to register background thread buffer release consumer file " "descriptor with epoll. errno=%d message='%s'", errno, strerror(errno)); // EventFd is used to break the background thread's loop. android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)}; LOG_ALWAYS_FATAL_IF(!eventFd.ok(), "Failed to create background thread buffer release event file descriptor. " "errno=%d message='%s'", errno, strerror(errno)); epoll_event registerEventFd{}; registerEventFd.events = EPOLLIN; registerEventFd.data.fd = eventFd.get(); status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), ®isterEventFd); LOG_ALWAYS_FATAL_IF(status == -1, "Failed to register background thread event file descriptor with epoll. " "errno=%d message='%s'", errno, strerror(errno)); mEventFd = eventFd.get(); std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd), weakBbq = wp<BLASTBufferQueue>(bbq)]() { pthread_setname_np(pthread_self(), "BufferReleaseThread"); while (true) { epoll_event event{}; int eventCount; do { eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/); } while (eventCount == -1 && errno != EINTR); if (eventCount == -1) { ALOGE("epoll_wait error while waiting for buffer release in background thread. " "errno=%d message='%s'", errno, strerror(errno)); continue; } // EventFd is used to join this thread. if (event.data.fd == eventFd.get()) { return; } sp<BLASTBufferQueue> bbq = weakBbq.promote(); if (!bbq) { return; } // If there are threads blocking on dequeue, give those threads priority for handling // the release. if (bbq->mThreadsBlockingOnDequeue > 0) { std::this_thread::sleep_for(0ms); continue; } ReleaseCallbackId id; sp<Fence> fence; uint32_t maxAcquiredBufferCount; status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount); if (status != OK) { ALOGE("failed to read from buffer release consumer in background thread. errno=%d " "message='%s'", errno, strerror(errno)); continue; } bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); } }).detach(); } BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() { eventfd_write(mEventFd, 1); } #endif } // namespace android libs/gui/BufferReleaseChannel.cpp +27 −39 Original line number Diff line number Diff line Loading @@ -35,35 +35,35 @@ namespace android::gui { namespace { template <typename T> static void readAligned(const void*& buffer, size_t& size, T& value) { void readAligned(const void*& buffer, size_t& size, T& value) { size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::read(buffer, size, value); } template <typename T> static void writeAligned(void*& buffer, size_t& size, T value) { void writeAligned(void*& buffer, size_t& size, T value) { size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::write(buffer, size, value); } template <typename T> static void addAligned(size_t& size, T /* value */) { void addAligned(size_t& size, T /* value */) { size = FlattenableUtils::align<sizeof(T)>(size); size += sizeof(T); } template <typename T> static inline constexpr uint32_t low32(const T n) { inline constexpr uint32_t low32(const T n) { return static_cast<uint32_t>(static_cast<uint64_t>(n)); } template <typename T> static inline constexpr uint32_t high32(const T n) { inline constexpr uint32_t high32(const T n) { return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); } template <typename T> static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo); } Loading Loading @@ -139,19 +139,18 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( std::lock_guard lock{mMutex}; Message message; mFlattenedBuffer.resize(message.getFlattenedSize()); std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), }; msghdr msg{ .msg_iov = &iov, .msg_iovlen = 1, .msg_control = controlMessageBuffer.data(), .msg_controllen = controlMessageBuffer.size(), }; msghdr msg{}; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); ssize_t result; do { Loading @@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( if (errno == EWOULDBLOCK || errno == EAGAIN) { return WOULD_BLOCK; } ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno)); ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno)); return UNKNOWN_ERROR; } Loading Loading @@ -200,8 +199,8 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( return OK; } int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, const sp<Fence>& fence, status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence( const ReleaseCallbackId& callbackId, const sp<Fence>& fence, uint32_t maxAcquiredBufferCount) { Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; mFlattenedBuffer.resize(message.getFlattenedSize()); Loading @@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb size_t flattenedBufferSize = mFlattenedBuffer.size(); int* flattenedFdPtr = &flattenedFd; size_t flattenedFdCount = 1; if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, flattenedFdCount); err != OK) { ALOGE("Failed to flatten BufferReleaseChannel message."); return err; if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, flattenedFdCount); status != OK) { return status; } } iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), }; iovec iov{}; iov.iov_base = mFlattenedBuffer.data(); iov.iov_len = mFlattenedBuffer.size(); msghdr msg{ .msg_iov = &iov, .msg_iovlen = 1, }; msghdr msg{}; msg.msg_iov = &iov; msg.msg_iovlen = 1; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; if (fence && fence->isValid()) { msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); Loading @@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb result = sendmsg(mFd, &msg, 0); } while (result == -1 && errno == EINTR); if (result == -1) { ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); return -errno; } Loading Loading @@ -344,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name, return -errno; } // Make the producer write-only if (shutdown(producerFd.get(), SHUT_RD) == -1) { ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'", name.c_str(), errno, strerror(errno)); return -errno; } outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); return STATUS_OK; Loading libs/gui/include/gui/BLASTBufferQueue.h +2 −13 Original line number Diff line number Diff line Loading @@ -325,6 +325,8 @@ private: std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; void drainBufferReleaseConsumer(); class BufferReleaseReader { public: explicit BufferReleaseReader(BLASTBufferQueue&); Loading Loading @@ -353,19 +355,6 @@ private: }; std::optional<BufferReleaseReader> mBufferReleaseReader; std::atomic<int> mThreadsBlockingOnDequeue = 0; class BufferReleaseThread { public: BufferReleaseThread(const sp<BLASTBufferQueue>&); ~BufferReleaseThread(); private: int mEventFd; }; std::optional<BufferReleaseThread> mBufferReleaseThread; #endif }; Loading libs/gui/tests/BufferReleaseChannel_test.cpp +31 −16 Original line number Diff line number Diff line Loading @@ -29,11 +29,11 @@ namespace { // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { struct stat stat1; struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { return false; } struct stat stat2; struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { return false; } Loading @@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) { } // namespace TEST(BufferReleaseChannelTest, MessageFlattenable) { class BufferReleaseChannelTest : public testing::Test { protected: std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> mConsumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer; void SetUp() override { ASSERT_EQ(OK, BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer)); } }; TEST_F(BufferReleaseChannelTest, MessageFlattenable) { ReleaseCallbackId releaseCallbackId{1, 2}; sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; Loading Loading @@ -92,31 +103,23 @@ TEST(BufferReleaseChannelTest, MessageFlattenable) { // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { ReleaseCallbackId releaseCallbackId; sp<Fence> releaseFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); } // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. TEST(BufferReleaseChannelTest, ProduceAndConsume) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); } for (uint64_t i = 0; i < 64; i++) { Loading @@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> consumerFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); Loading @@ -135,4 +138,16 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { } } // Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to. TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) { uint64_t data = 0; ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t))); ASSERT_EQ(errno, EPIPE); } // Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from. TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) { ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t))); } } // namespace android No newline at end of file libs/gui/tests/EndToEndNativeInputTest.cpp +7 −7 Original line number Diff line number Diff line Loading @@ -294,7 +294,7 @@ public: transactionBody) { SurfaceComposerClient::Transaction t; transactionBody(t, mSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { Loading @@ -307,7 +307,7 @@ public: t.setAlpha(mSurfaceControl, 1); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); t.addWindowInfosReportedListener(reportedListener); t.apply(); t.apply(/*synchronously=*/true); reportedListener->wait(); } Loading @@ -319,7 +319,7 @@ public: request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); t.setFocusedWindow(request); t.apply(true); t.apply(/*synchronously=*/true); } public: Loading Loading @@ -363,7 +363,7 @@ public: transactionBody) override { SurfaceComposerClient::Transaction t; transactionBody(t, mParentSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override { Loading @@ -377,7 +377,7 @@ public: t.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setCrop(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); t.apply(true); t.apply(/*synchronously=*/true); } private: Loading Loading @@ -417,7 +417,7 @@ public: BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"); Transaction().setBuffer(layer, buffer).apply(true); Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true); usleep(mBufferPostDelay); } Loading Loading @@ -1207,7 +1207,7 @@ public: t.setDisplayLayerStack(token, layerStack); t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, {offsetX, offsetY, offsetX + width, offsetY + height}); t.apply(true); t.apply(/*synchronously=*/true); mVirtualDisplays.push_back(token); } Loading Loading
libs/gui/BLASTBufferQueue.cpp +35 −119 Original line number Diff line number Diff line Loading @@ -52,19 +52,18 @@ using namespace std::chrono_literals; namespace { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // RAII wrapper to defer arbitrary work until the Deferred instance is deleted. template <class F> class Deferred { template <class Mutex> class UnlockGuard { public: explicit Deferred(F f) : mF{std::move(f)} {} explicit UnlockGuard(Mutex& lock) : mLock{lock} { mLock.unlock(); } ~Deferred() { mF(); } ~UnlockGuard() { mLock.lock(); } Deferred(const Deferred&) = delete; Deferred& operator=(const Deferred&) = delete; UnlockGuard(const UnlockGuard&) = delete; UnlockGuard& operator=(const UnlockGuard&) = delete; private: F mF; Mutex& mLock; }; #endif Loading Loading @@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) mBufferReleaseThread.emplace(sp<BLASTBufferQueue>::fromExisting(this)); #endif } void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, Loading @@ -297,11 +293,16 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, mSurfaceControl = surface; SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) t.setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer); // SELinux policy may prevent this process from sending the BufferReleaseChannel's file // descriptor to SurfaceFlinger, causing the entire transaction to be dropped. This // transaction is applied separately to ensure we don't lose the other updates. t.setApplyToken(mApplyToken) .setBufferReleaseChannel(mSurfaceControl, mBufferReleaseProducer) .apply(false /* synchronous */, true /* oneWay */); #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); applyTransaction = true; } mTransformHint = mSurfaceControl->getTransformHint(); Loading @@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } if (applyTransaction) { // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction t.setApplyToken(mApplyToken).apply(false, true); t.setApplyToken(mApplyToken).apply(false /* synchronous */, true /* oneWay */); } } Loading Loading @@ -419,7 +420,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.latchTime, stat.frameEventStats.dequeueReadyTime); } #if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { Loading @@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); } #endif } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } Loading Loading @@ -469,6 +468,9 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { return; } bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) bbq->drainBufferReleaseConsumer(); #endif }; } Loading Loading @@ -535,8 +537,6 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence) { auto it = mSubmitted.find(callbackId); if (it == mSubmitted.end()) { BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", callbackId.to_string().c_str()); return; } mNumAcquired--; Loading Loading @@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) ReleaseBufferCallback releaseBufferCallback = applyTransaction ? nullptr : makeReleaseBufferCallbackThunk(); #else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); #endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; Loading Loading @@ -1230,12 +1225,7 @@ public: // we want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. bbq->mBufferReleaseReader->clearInterrupts(); bbq->mThreadsBlockingOnDequeue++; bufferQueueLock.unlock(); Deferred cleanup{[&]() { bufferQueueLock.lock(); bbq->mThreadsBlockingOnDequeue--; }}; UnlockGuard unlockGuard{bufferQueueLock}; ATRACE_FORMAT("waiting for free buffer"); ReleaseCallbackId id; Loading Loading @@ -1345,6 +1335,21 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) void BLASTBufferQueue::drainBufferReleaseConsumer() { ATRACE_CALL(); while (true) { ReleaseCallbackId id; sp<Fence> fence; uint32_t maxAcquiredBufferCount; status_t status = mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount); if (status != OK) { return; } releaseBufferCallback(id, fence, maxAcquiredBufferCount); } } BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} { mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), Loading Loading @@ -1438,95 +1443,6 @@ void BLASTBufferQueue::BufferReleaseReader::clearInterrupts() { } } BLASTBufferQueue::BufferReleaseThread::BufferReleaseThread(const sp<BLASTBufferQueue>& bbq) { android::base::unique_fd epollFd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!epollFd.ok(), "Failed to create buffer release background thread epoll file descriptor. " "errno=%d message='%s'", errno, strerror(errno)); epoll_event registerEndpointFd{}; registerEndpointFd.events = EPOLLIN; registerEndpointFd.data.fd = bbq->mBufferReleaseConsumer->getFd(); status_t status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, bbq->mBufferReleaseConsumer->getFd(), ®isterEndpointFd); LOG_ALWAYS_FATAL_IF(status == -1, "Failed to register background thread buffer release consumer file " "descriptor with epoll. errno=%d message='%s'", errno, strerror(errno)); // EventFd is used to break the background thread's loop. android::base::unique_fd eventFd{eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK)}; LOG_ALWAYS_FATAL_IF(!eventFd.ok(), "Failed to create background thread buffer release event file descriptor. " "errno=%d message='%s'", errno, strerror(errno)); epoll_event registerEventFd{}; registerEventFd.events = EPOLLIN; registerEventFd.data.fd = eventFd.get(); status = epoll_ctl(epollFd.get(), EPOLL_CTL_ADD, eventFd.get(), ®isterEventFd); LOG_ALWAYS_FATAL_IF(status == -1, "Failed to register background thread event file descriptor with epoll. " "errno=%d message='%s'", errno, strerror(errno)); mEventFd = eventFd.get(); std::thread([epollFd = std::move(epollFd), eventFd = std::move(eventFd), weakBbq = wp<BLASTBufferQueue>(bbq)]() { pthread_setname_np(pthread_self(), "BufferReleaseThread"); while (true) { epoll_event event{}; int eventCount; do { eventCount = epoll_wait(epollFd.get(), &event, 1 /*maxevents*/, -1 /*timeout*/); } while (eventCount == -1 && errno != EINTR); if (eventCount == -1) { ALOGE("epoll_wait error while waiting for buffer release in background thread. " "errno=%d message='%s'", errno, strerror(errno)); continue; } // EventFd is used to join this thread. if (event.data.fd == eventFd.get()) { return; } sp<BLASTBufferQueue> bbq = weakBbq.promote(); if (!bbq) { return; } // If there are threads blocking on dequeue, give those threads priority for handling // the release. if (bbq->mThreadsBlockingOnDequeue > 0) { std::this_thread::sleep_for(0ms); continue; } ReleaseCallbackId id; sp<Fence> fence; uint32_t maxAcquiredBufferCount; status_t status = bbq->mBufferReleaseConsumer->readReleaseFence(id, fence, maxAcquiredBufferCount); if (status != OK) { ALOGE("failed to read from buffer release consumer in background thread. errno=%d " "message='%s'", errno, strerror(errno)); continue; } bbq->releaseBufferCallback(id, fence, maxAcquiredBufferCount); } }).detach(); } BLASTBufferQueue::BufferReleaseThread::~BufferReleaseThread() { eventfd_write(mEventFd, 1); } #endif } // namespace android
libs/gui/BufferReleaseChannel.cpp +27 −39 Original line number Diff line number Diff line Loading @@ -35,35 +35,35 @@ namespace android::gui { namespace { template <typename T> static void readAligned(const void*& buffer, size_t& size, T& value) { void readAligned(const void*& buffer, size_t& size, T& value) { size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::read(buffer, size, value); } template <typename T> static void writeAligned(void*& buffer, size_t& size, T value) { void writeAligned(void*& buffer, size_t& size, T value) { size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::write(buffer, size, value); } template <typename T> static void addAligned(size_t& size, T /* value */) { void addAligned(size_t& size, T /* value */) { size = FlattenableUtils::align<sizeof(T)>(size); size += sizeof(T); } template <typename T> static inline constexpr uint32_t low32(const T n) { inline constexpr uint32_t low32(const T n) { return static_cast<uint32_t>(static_cast<uint64_t>(n)); } template <typename T> static inline constexpr uint32_t high32(const T n) { inline constexpr uint32_t high32(const T n) { return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); } template <typename T> static inline constexpr T to64(const uint32_t lo, const uint32_t hi) { inline constexpr T to64(const uint32_t lo, const uint32_t hi) { return static_cast<T>(static_cast<uint64_t>(hi) << 32 | lo); } Loading Loading @@ -139,19 +139,18 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( std::lock_guard lock{mMutex}; Message message; mFlattenedBuffer.resize(message.getFlattenedSize()); std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), }; msghdr msg{ .msg_iov = &iov, .msg_iovlen = 1, .msg_control = controlMessageBuffer.data(), .msg_controllen = controlMessageBuffer.size(), }; msghdr msg{}; msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); ssize_t result; do { Loading @@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( if (errno == EWOULDBLOCK || errno == EAGAIN) { return WOULD_BLOCK; } ALOGE("Error reading release fence from socket: error %#x (%s)", errno, strerror(errno)); ALOGE("Error reading release fence from socket: error %d (%s)", errno, strerror(errno)); return UNKNOWN_ERROR; } Loading Loading @@ -200,8 +199,8 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( return OK; } int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, const sp<Fence>& fence, status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence( const ReleaseCallbackId& callbackId, const sp<Fence>& fence, uint32_t maxAcquiredBufferCount) { Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; mFlattenedBuffer.resize(message.getFlattenedSize()); Loading @@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb size_t flattenedBufferSize = mFlattenedBuffer.size(); int* flattenedFdPtr = &flattenedFd; size_t flattenedFdCount = 1; if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, flattenedFdCount); err != OK) { ALOGE("Failed to flatten BufferReleaseChannel message."); return err; if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, flattenedFdCount); status != OK) { return status; } } iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), }; iovec iov{}; iov.iov_base = mFlattenedBuffer.data(); iov.iov_len = mFlattenedBuffer.size(); msghdr msg{ .msg_iov = &iov, .msg_iovlen = 1, }; msghdr msg{}; msg.msg_iov = &iov; msg.msg_iovlen = 1; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; if (fence && fence->isValid()) { msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); Loading @@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb result = sendmsg(mFd, &msg, 0); } while (result == -1 && errno == EINTR); if (result == -1) { ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); return -errno; } Loading Loading @@ -344,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name, return -errno; } // Make the producer write-only if (shutdown(producerFd.get(), SHUT_RD) == -1) { ALOGE("[%s] Failed to shutdown reading on producer socket. errno=%d message='%s'", name.c_str(), errno, strerror(errno)); return -errno; } outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); return STATUS_OK; Loading
libs/gui/include/gui/BLASTBufferQueue.h +2 −13 Original line number Diff line number Diff line Loading @@ -325,6 +325,8 @@ private: std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; void drainBufferReleaseConsumer(); class BufferReleaseReader { public: explicit BufferReleaseReader(BLASTBufferQueue&); Loading Loading @@ -353,19 +355,6 @@ private: }; std::optional<BufferReleaseReader> mBufferReleaseReader; std::atomic<int> mThreadsBlockingOnDequeue = 0; class BufferReleaseThread { public: BufferReleaseThread(const sp<BLASTBufferQueue>&); ~BufferReleaseThread(); private: int mEventFd; }; std::optional<BufferReleaseThread> mBufferReleaseThread; #endif }; Loading
libs/gui/tests/BufferReleaseChannel_test.cpp +31 −16 Original line number Diff line number Diff line Loading @@ -29,11 +29,11 @@ namespace { // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { struct stat stat1; struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { return false; } struct stat stat2; struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { return false; } Loading @@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) { } // namespace TEST(BufferReleaseChannelTest, MessageFlattenable) { class BufferReleaseChannelTest : public testing::Test { protected: std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> mConsumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> mProducer; void SetUp() override { ASSERT_EQ(OK, BufferReleaseChannel::open("BufferReleaseChannelTest"s, mConsumer, mProducer)); } }; TEST_F(BufferReleaseChannelTest, MessageFlattenable) { ReleaseCallbackId releaseCallbackId{1, 2}; sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; Loading Loading @@ -92,31 +103,23 @@ TEST(BufferReleaseChannelTest, MessageFlattenable) { // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { ReleaseCallbackId releaseCallbackId; sp<Fence> releaseFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, consumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); mConsumer->readReleaseFence(releaseCallbackId, releaseFence, maxAcquiredBufferCount)); } // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. TEST(BufferReleaseChannelTest, ProduceAndConsume) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; ASSERT_EQ(OK, producer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); ASSERT_EQ(OK, mProducer->writeReleaseFence(producerId, fence, maxAcquiredBufferCount)); } for (uint64_t i = 0; i < 64; i++) { Loading @@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> consumerFence; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); Loading @@ -135,4 +138,16 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { } } // Verify that BufferReleaseChannel::ConsumerEndpoint's socket can't be written to. TEST_F(BufferReleaseChannelTest, ConsumerSocketReadOnly) { uint64_t data = 0; ASSERT_EQ(-1, write(mConsumer->getFd().get(), &data, sizeof(uint64_t))); ASSERT_EQ(errno, EPIPE); } // Verify that BufferReleaseChannel::ProducerEndpoint's socket can't be read from. TEST_F(BufferReleaseChannelTest, ProducerSocketWriteOnly) { ASSERT_EQ(0, read(mProducer->getFd().get(), nullptr, sizeof(uint64_t))); } } // namespace android No newline at end of file
libs/gui/tests/EndToEndNativeInputTest.cpp +7 −7 Original line number Diff line number Diff line Loading @@ -294,7 +294,7 @@ public: transactionBody) { SurfaceComposerClient::Transaction t; transactionBody(t, mSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { Loading @@ -307,7 +307,7 @@ public: t.setAlpha(mSurfaceControl, 1); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); t.addWindowInfosReportedListener(reportedListener); t.apply(); t.apply(/*synchronously=*/true); reportedListener->wait(); } Loading @@ -319,7 +319,7 @@ public: request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); t.setFocusedWindow(request); t.apply(true); t.apply(/*synchronously=*/true); } public: Loading Loading @@ -363,7 +363,7 @@ public: transactionBody) override { SurfaceComposerClient::Transaction t; transactionBody(t, mParentSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override { Loading @@ -377,7 +377,7 @@ public: t.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setCrop(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); t.apply(true); t.apply(/*synchronously=*/true); } private: Loading Loading @@ -417,7 +417,7 @@ public: BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"); Transaction().setBuffer(layer, buffer).apply(true); Transaction().setBuffer(layer, buffer).apply(/*synchronously=*/true); usleep(mBufferPostDelay); } Loading Loading @@ -1207,7 +1207,7 @@ public: t.setDisplayLayerStack(token, layerStack); t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, {offsetX, offsetY, offsetX + width, offsetY + height}); t.apply(true); t.apply(/*synchronously=*/true); mVirtualDisplays.push_back(token); } Loading