Loading libs/gui/BLASTBufferQueue.cpp +35 −119 Original line number Original line Diff line number Diff line Loading @@ -52,19 +52,18 @@ using namespace std::chrono_literals; namespace { namespace { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // RAII wrapper to defer arbitrary work until the Deferred instance is deleted. template <class Mutex> template <class F> class UnlockGuard { class Deferred { public: 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; UnlockGuard(const UnlockGuard&) = delete; Deferred& operator=(const Deferred&) = delete; UnlockGuard& operator=(const UnlockGuard&) = delete; private: private: F mF; Mutex& mLock; }; }; #endif #endif Loading Loading @@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); 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, 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; mSurfaceControl = surface; SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) #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 #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); applyTransaction = true; applyTransaction = true; } } mTransformHint = mSurfaceControl->getTransformHint(); mTransformHint = mSurfaceControl->getTransformHint(); Loading @@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } } if (applyTransaction) { if (applyTransaction) { // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction // 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.latchTime, stat.frameEventStats.dequeueReadyTime); stat.frameEventStats.dequeueReadyTime); } } #if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { for (const auto& [key, value]: mSubmitted) { Loading @@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); true /* fakeRelease */); } } #endif } else { } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } } Loading Loading @@ -469,6 +468,9 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { return; return; } } bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); 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) { const sp<Fence>& releaseFence) { auto it = mSubmitted.find(callbackId); auto it = mSubmitted.find(callbackId); if (it == mSubmitted.end()) { if (it == mSubmitted.end()) { BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", callbackId.to_string().c_str()); return; return; } } mNumAcquired--; mNumAcquired--; Loading Loading @@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); bufferItem.mScalingMode, crop); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) ReleaseBufferCallback releaseBufferCallback = applyTransaction ? nullptr : makeReleaseBufferCallbackThunk(); #else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); #endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; 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 want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. // we don't miss an interrupt. bbq->mBufferReleaseReader->clearInterrupts(); bbq->mBufferReleaseReader->clearInterrupts(); bbq->mThreadsBlockingOnDequeue++; UnlockGuard unlockGuard{bufferQueueLock}; bufferQueueLock.unlock(); Deferred cleanup{[&]() { bufferQueueLock.lock(); bbq->mThreadsBlockingOnDequeue--; }}; ATRACE_FORMAT("waiting for free buffer"); ATRACE_FORMAT("waiting for free buffer"); ReleaseCallbackId id; ReleaseCallbackId id; Loading Loading @@ -1345,6 +1335,21 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) #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} { BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} { mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), 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 #endif } // namespace android } // namespace android libs/gui/BufferReleaseChannel.cpp +27 −39 Original line number Original line Diff line number Diff line Loading @@ -35,35 +35,35 @@ namespace android::gui { namespace { namespace { template <typename T> 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); size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::read(buffer, size, value); FlattenableUtils::read(buffer, size, value); } } template <typename T> 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); size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::write(buffer, size, value); FlattenableUtils::write(buffer, size, value); } } template <typename T> 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 = FlattenableUtils::align<sizeof(T)>(size); size += sizeof(T); size += sizeof(T); } } template <typename 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)); return static_cast<uint32_t>(static_cast<uint64_t>(n)); } } template <typename T> 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); return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); } } template <typename T> 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); 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}; std::lock_guard lock{mMutex}; Message message; Message message; mFlattenedBuffer.resize(message.getFlattenedSize()); mFlattenedBuffer.resize(message.getFlattenedSize()); std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; iovec iov{ iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), .iov_len = mFlattenedBuffer.size(), }; }; msghdr msg{ msghdr msg{}; .msg_iov = &iov, msg.msg_iov = &iov; .msg_iovlen = 1, msg.msg_iovlen = 1; .msg_control = controlMessageBuffer.data(), msg.msg_control = controlMessageBuffer.data(); .msg_controllen = controlMessageBuffer.size(), msg.msg_controllen = controlMessageBuffer.size(); }; ssize_t result; ssize_t result; do { do { Loading @@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( if (errno == EWOULDBLOCK || errno == EAGAIN) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return WOULD_BLOCK; 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; return UNKNOWN_ERROR; } } Loading Loading @@ -200,8 +199,8 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( return OK; return OK; } } int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence( const sp<Fence>& fence, const ReleaseCallbackId& callbackId, const sp<Fence>& fence, uint32_t maxAcquiredBufferCount) { uint32_t maxAcquiredBufferCount) { Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; mFlattenedBuffer.resize(message.getFlattenedSize()); mFlattenedBuffer.resize(message.getFlattenedSize()); Loading @@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb size_t flattenedBufferSize = mFlattenedBuffer.size(); size_t flattenedBufferSize = mFlattenedBuffer.size(); int* flattenedFdPtr = &flattenedFd; int* flattenedFdPtr = &flattenedFd; size_t flattenedFdCount = 1; size_t flattenedFdCount = 1; if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdCount); flattenedFdPtr, flattenedFdCount); err != OK) { status != OK) { ALOGE("Failed to flatten BufferReleaseChannel message."); return status; return err; } } } } iovec iov{ iovec iov{}; .iov_base = mFlattenedBuffer.data(), iov.iov_base = mFlattenedBuffer.data(); .iov_len = mFlattenedBuffer.size(), iov.iov_len = mFlattenedBuffer.size(); }; msghdr msg{ msghdr msg{}; .msg_iov = &iov, msg.msg_iov = &iov; .msg_iovlen = 1, 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()) { if (fence && fence->isValid()) { msg.msg_control = controlMessageBuffer.data(); msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); msg.msg_controllen = controlMessageBuffer.size(); Loading @@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb result = sendmsg(mFd, &msg, 0); result = sendmsg(mFd, &msg, 0); } while (result == -1 && errno == EINTR); } while (result == -1 && errno == EINTR); if (result == -1) { if (result == -1) { ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); return -errno; return -errno; } } Loading Loading @@ -344,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name, return -errno; 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)); outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); return STATUS_OK; return STATUS_OK; Loading libs/gui/include/gui/BLASTBufferQueue.h +2 −13 Original line number Original line Diff line number Diff line Loading @@ -325,6 +325,8 @@ private: std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; void drainBufferReleaseConsumer(); class BufferReleaseReader { class BufferReleaseReader { public: public: explicit BufferReleaseReader(BLASTBufferQueue&); explicit BufferReleaseReader(BLASTBufferQueue&); Loading Loading @@ -353,19 +355,6 @@ private: }; }; std::optional<BufferReleaseReader> mBufferReleaseReader; 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 #endif }; }; Loading libs/gui/tests/BufferReleaseChannel_test.cpp +31 −16 Original line number Original line Diff line number Diff line Loading @@ -29,11 +29,11 @@ namespace { // Helper function to check if two file descriptors point to the same file. // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { bool is_same_file(int fd1, int fd2) { struct stat stat1; struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { if (fstat(fd1, &stat1) != 0) { return false; return false; } } struct stat stat2; struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { if (fstat(fd2, &stat2) != 0) { return false; return false; } } Loading @@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) { } // namespace } // 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}; ReleaseCallbackId releaseCallbackId{1, 2}; sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; 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 // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. // available. TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); ReleaseCallbackId releaseCallbackId; ReleaseCallbackId releaseCallbackId; sp<Fence> releaseFence; sp<Fence> releaseFence; uint32_t maxAcquiredBufferCount; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, 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 // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. // using the BufferReleaseChannel consumer. TEST(BufferReleaseChannelTest, ProduceAndConsume) { TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; 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++) { for (uint64_t i = 0; i < 64; i++) { Loading @@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> consumerFence; sp<Fence> consumerFence; uint32_t maxAcquiredBufferCount; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, ASSERT_EQ(OK, consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); 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 } // namespace android No newline at end of file libs/gui/tests/EndToEndNativeInputTest.cpp +7 −7 Original line number Original line Diff line number Diff line Loading @@ -294,7 +294,7 @@ public: transactionBody) { transactionBody) { SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t; transactionBody(t, mSurfaceControl); transactionBody(t, mSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } } virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { Loading @@ -307,7 +307,7 @@ public: t.setAlpha(mSurfaceControl, 1); t.setAlpha(mSurfaceControl, 1); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); t.addWindowInfosReportedListener(reportedListener); t.addWindowInfosReportedListener(reportedListener); t.apply(); t.apply(/*synchronously=*/true); reportedListener->wait(); reportedListener->wait(); } } Loading @@ -319,7 +319,7 @@ public: request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); request.displayId = displayId.val(); t.setFocusedWindow(request); t.setFocusedWindow(request); t.apply(true); t.apply(/*synchronously=*/true); } } public: public: Loading Loading @@ -363,7 +363,7 @@ public: transactionBody) override { transactionBody) override { SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t; transactionBody(t, mParentSurfaceControl); transactionBody(t, mParentSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } } void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override { 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.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setCrop(mSurfaceControl, crop); t.setCrop(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); t.setAlpha(mSurfaceControl, 1); t.apply(true); t.apply(/*synchronously=*/true); } } private: private: Loading Loading @@ -417,7 +417,7 @@ public: BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"); 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); usleep(mBufferPostDelay); } } Loading Loading @@ -1207,7 +1207,7 @@ public: t.setDisplayLayerStack(token, layerStack); t.setDisplayLayerStack(token, layerStack); t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, {offsetX, offsetY, offsetX + width, offsetY + height}); {offsetX, offsetY, offsetX + width, offsetY + height}); t.apply(true); t.apply(/*synchronously=*/true); mVirtualDisplays.push_back(token); mVirtualDisplays.push_back(token); } } Loading Loading
libs/gui/BLASTBufferQueue.cpp +35 −119 Original line number Original line Diff line number Diff line Loading @@ -52,19 +52,18 @@ using namespace std::chrono_literals; namespace { namespace { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) // RAII wrapper to defer arbitrary work until the Deferred instance is deleted. template <class Mutex> template <class F> class UnlockGuard { class Deferred { public: 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; UnlockGuard(const UnlockGuard&) = delete; Deferred& operator=(const Deferred&) = delete; UnlockGuard& operator=(const UnlockGuard&) = delete; private: private: F mF; Mutex& mLock; }; }; #endif #endif Loading Loading @@ -271,9 +270,6 @@ BLASTBufferQueue::~BLASTBufferQueue() { void BLASTBufferQueue::onFirstRef() { void BLASTBufferQueue::onFirstRef() { // safe default, most producers are expected to override this // safe default, most producers are expected to override this mProducer->setMaxDequeuedBufferCount(2); 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, 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; mSurfaceControl = surface; SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t; if (surfaceControlChanged) { if (surfaceControlChanged) { t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) #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 #endif t.setFlags(mSurfaceControl, layer_state_t::eEnableBackpressure, layer_state_t::eEnableBackpressure); applyTransaction = true; applyTransaction = true; } } mTransformHint = mSurfaceControl->getTransformHint(); mTransformHint = mSurfaceControl->getTransformHint(); Loading @@ -325,7 +326,7 @@ void BLASTBufferQueue::update(const sp<SurfaceControl>& surface, uint32_t width, } } if (applyTransaction) { if (applyTransaction) { // All transactions on our apply token are one-way. See comment on mAppliedLastTransaction // 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.latchTime, stat.frameEventStats.dequeueReadyTime); stat.frameEventStats.dequeueReadyTime); } } #if !COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) auto currFrameNumber = stat.frameEventStats.frameNumber; auto currFrameNumber = stat.frameEventStats.frameNumber; std::vector<ReleaseCallbackId> staleReleases; std::vector<ReleaseCallbackId> staleReleases; for (const auto& [key, value]: mSubmitted) { for (const auto& [key, value]: mSubmitted) { Loading @@ -435,7 +435,6 @@ void BLASTBufferQueue::transactionCallback(nsecs_t /*latchTime*/, const sp<Fence stat.currentMaxAcquiredBufferCount, stat.currentMaxAcquiredBufferCount, true /* fakeRelease */); true /* fakeRelease */); } } #endif } else { } else { BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); BQA_LOGE("Failed to find matching SurfaceControl in transactionCallback"); } } Loading Loading @@ -469,6 +468,9 @@ ReleaseBufferCallback BLASTBufferQueue::makeReleaseBufferCallbackThunk() { return; return; } } bbq->releaseBufferCallback(id, releaseFence, currentMaxAcquiredBufferCount); 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) { const sp<Fence>& releaseFence) { auto it = mSubmitted.find(callbackId); auto it = mSubmitted.find(callbackId); if (it == mSubmitted.end()) { if (it == mSubmitted.end()) { BQA_LOGE("ERROR: releaseBufferCallback without corresponding submitted buffer %s", callbackId.to_string().c_str()); return; return; } } mNumAcquired--; mNumAcquired--; Loading Loading @@ -646,12 +646,7 @@ status_t BLASTBufferQueue::acquireNextBufferLocked( bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mGraphicBuffer->getHeight(), bufferItem.mTransform, bufferItem.mScalingMode, crop); bufferItem.mScalingMode, crop); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) ReleaseBufferCallback releaseBufferCallback = applyTransaction ? nullptr : makeReleaseBufferCallbackThunk(); #else auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); auto releaseBufferCallback = makeReleaseBufferCallbackThunk(); #endif sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE; nsecs_t dequeueTime = -1; 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 want to ignore it. This must be done before unlocking the BufferQueue lock to ensure // we don't miss an interrupt. // we don't miss an interrupt. bbq->mBufferReleaseReader->clearInterrupts(); bbq->mBufferReleaseReader->clearInterrupts(); bbq->mThreadsBlockingOnDequeue++; UnlockGuard unlockGuard{bufferQueueLock}; bufferQueueLock.unlock(); Deferred cleanup{[&]() { bufferQueueLock.lock(); bbq->mThreadsBlockingOnDequeue--; }}; ATRACE_FORMAT("waiting for free buffer"); ATRACE_FORMAT("waiting for free buffer"); ReleaseCallbackId id; ReleaseCallbackId id; Loading Loading @@ -1345,6 +1335,21 @@ void BLASTBufferQueue::setApplyToken(sp<IBinder> applyToken) { #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BUFFER_RELEASE_CHANNEL) #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} { BLASTBufferQueue::BufferReleaseReader::BufferReleaseReader(BLASTBufferQueue& bbq) : mBbq{bbq} { mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; mEpollFd = android::base::unique_fd{epoll_create1(EPOLL_CLOEXEC)}; LOG_ALWAYS_FATAL_IF(!mEpollFd.ok(), 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 #endif } // namespace android } // namespace android
libs/gui/BufferReleaseChannel.cpp +27 −39 Original line number Original line Diff line number Diff line Loading @@ -35,35 +35,35 @@ namespace android::gui { namespace { namespace { template <typename T> 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); size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::read(buffer, size, value); FlattenableUtils::read(buffer, size, value); } } template <typename T> 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); size -= FlattenableUtils::align<alignof(T)>(buffer); FlattenableUtils::write(buffer, size, value); FlattenableUtils::write(buffer, size, value); } } template <typename T> 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 = FlattenableUtils::align<sizeof(T)>(size); size += sizeof(T); size += sizeof(T); } } template <typename 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)); return static_cast<uint32_t>(static_cast<uint64_t>(n)); } } template <typename T> 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); return static_cast<uint32_t>(static_cast<uint64_t>(n) >> 32); } } template <typename T> 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); 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}; std::lock_guard lock{mMutex}; Message message; Message message; mFlattenedBuffer.resize(message.getFlattenedSize()); mFlattenedBuffer.resize(message.getFlattenedSize()); std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer; std::array<uint8_t, CMSG_SPACE(sizeof(int))> controlMessageBuffer{}; iovec iov{ iovec iov{ .iov_base = mFlattenedBuffer.data(), .iov_base = mFlattenedBuffer.data(), .iov_len = mFlattenedBuffer.size(), .iov_len = mFlattenedBuffer.size(), }; }; msghdr msg{ msghdr msg{}; .msg_iov = &iov, msg.msg_iov = &iov; .msg_iovlen = 1, msg.msg_iovlen = 1; .msg_control = controlMessageBuffer.data(), msg.msg_control = controlMessageBuffer.data(); .msg_controllen = controlMessageBuffer.size(), msg.msg_controllen = controlMessageBuffer.size(); }; ssize_t result; ssize_t result; do { do { Loading @@ -161,7 +160,7 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( if (errno == EWOULDBLOCK || errno == EAGAIN) { if (errno == EWOULDBLOCK || errno == EAGAIN) { return WOULD_BLOCK; 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; return UNKNOWN_ERROR; } } Loading Loading @@ -200,8 +199,8 @@ status_t BufferReleaseChannel::ConsumerEndpoint::readReleaseFence( return OK; return OK; } } int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallbackId& callbackId, status_t BufferReleaseChannel::ProducerEndpoint::writeReleaseFence( const sp<Fence>& fence, const ReleaseCallbackId& callbackId, const sp<Fence>& fence, uint32_t maxAcquiredBufferCount) { uint32_t maxAcquiredBufferCount) { Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; Message message{callbackId, fence ? fence : Fence::NO_FENCE, maxAcquiredBufferCount}; mFlattenedBuffer.resize(message.getFlattenedSize()); mFlattenedBuffer.resize(message.getFlattenedSize()); Loading @@ -213,25 +212,22 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb size_t flattenedBufferSize = mFlattenedBuffer.size(); size_t flattenedBufferSize = mFlattenedBuffer.size(); int* flattenedFdPtr = &flattenedFd; int* flattenedFdPtr = &flattenedFd; size_t flattenedFdCount = 1; size_t flattenedFdCount = 1; if (status_t err = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdPtr, if (status_t status = message.flatten(flattenedBufferPtr, flattenedBufferSize, flattenedFdCount); flattenedFdPtr, flattenedFdCount); err != OK) { status != OK) { ALOGE("Failed to flatten BufferReleaseChannel message."); return status; return err; } } } } iovec iov{ iovec iov{}; .iov_base = mFlattenedBuffer.data(), iov.iov_base = mFlattenedBuffer.data(); .iov_len = mFlattenedBuffer.size(), iov.iov_len = mFlattenedBuffer.size(); }; msghdr msg{ msghdr msg{}; .msg_iov = &iov, msg.msg_iov = &iov; .msg_iovlen = 1, 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()) { if (fence && fence->isValid()) { msg.msg_control = controlMessageBuffer.data(); msg.msg_control = controlMessageBuffer.data(); msg.msg_controllen = controlMessageBuffer.size(); msg.msg_controllen = controlMessageBuffer.size(); Loading @@ -248,7 +244,6 @@ int BufferReleaseChannel::ProducerEndpoint::writeReleaseFence(const ReleaseCallb result = sendmsg(mFd, &msg, 0); result = sendmsg(mFd, &msg, 0); } while (result == -1 && errno == EINTR); } while (result == -1 && errno == EINTR); if (result == -1) { if (result == -1) { ALOGD("Error writing release fence to socket: error %#x (%s)", errno, strerror(errno)); return -errno; return -errno; } } Loading Loading @@ -344,13 +339,6 @@ status_t BufferReleaseChannel::open(std::string name, return -errno; 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)); outConsumer = std::make_unique<ConsumerEndpoint>(name, std::move(consumerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); outProducer = std::make_shared<ProducerEndpoint>(std::move(name), std::move(producerFd)); return STATUS_OK; return STATUS_OK; Loading
libs/gui/include/gui/BLASTBufferQueue.h +2 −13 Original line number Original line Diff line number Diff line Loading @@ -325,6 +325,8 @@ private: std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::unique_ptr<gui::BufferReleaseChannel::ConsumerEndpoint> mBufferReleaseConsumer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; std::shared_ptr<gui::BufferReleaseChannel::ProducerEndpoint> mBufferReleaseProducer; void drainBufferReleaseConsumer(); class BufferReleaseReader { class BufferReleaseReader { public: public: explicit BufferReleaseReader(BLASTBufferQueue&); explicit BufferReleaseReader(BLASTBufferQueue&); Loading Loading @@ -353,19 +355,6 @@ private: }; }; std::optional<BufferReleaseReader> mBufferReleaseReader; 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 #endif }; }; Loading
libs/gui/tests/BufferReleaseChannel_test.cpp +31 −16 Original line number Original line Diff line number Diff line Loading @@ -29,11 +29,11 @@ namespace { // Helper function to check if two file descriptors point to the same file. // Helper function to check if two file descriptors point to the same file. bool is_same_file(int fd1, int fd2) { bool is_same_file(int fd1, int fd2) { struct stat stat1; struct stat stat1 {}; if (fstat(fd1, &stat1) != 0) { if (fstat(fd1, &stat1) != 0) { return false; return false; } } struct stat stat2; struct stat stat2 {}; if (fstat(fd2, &stat2) != 0) { if (fstat(fd2, &stat2) != 0) { return false; return false; } } Loading @@ -42,7 +42,18 @@ bool is_same_file(int fd1, int fd2) { } // namespace } // 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}; ReleaseCallbackId releaseCallbackId{1, 2}; sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); sp<Fence> releaseFence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); uint32_t maxAcquiredBufferCount = 5; 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 // Verify that the BufferReleaseChannel consume returns WOULD_BLOCK when there's no message // available. // available. TEST(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { TEST_F(BufferReleaseChannelTest, ConsumerEndpointIsNonBlocking) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); ReleaseCallbackId releaseCallbackId; ReleaseCallbackId releaseCallbackId; sp<Fence> releaseFence; sp<Fence> releaseFence; uint32_t maxAcquiredBufferCount; uint32_t maxAcquiredBufferCount; ASSERT_EQ(WOULD_BLOCK, 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 // Verify that we can write a message to the BufferReleaseChannel producer and read that message // using the BufferReleaseChannel consumer. // using the BufferReleaseChannel consumer. TEST(BufferReleaseChannelTest, ProduceAndConsume) { TEST_F(BufferReleaseChannelTest, ProduceAndConsume) { std::unique_ptr<BufferReleaseChannel::ConsumerEndpoint> consumer; std::shared_ptr<BufferReleaseChannel::ProducerEndpoint> producer; ASSERT_EQ(OK, BufferReleaseChannel::open("test-channel"s, consumer, producer)); sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); sp<Fence> fence = sp<Fence>::make(memfd_create("fake-fence-fd", 0)); for (uint64_t i = 0; i < 64; i++) { for (uint64_t i = 0; i < 64; i++) { ReleaseCallbackId producerId{i, i + 1}; ReleaseCallbackId producerId{i, i + 1}; uint32_t maxAcquiredBufferCount = i + 2; 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++) { for (uint64_t i = 0; i < 64; i++) { Loading @@ -127,7 +130,7 @@ TEST(BufferReleaseChannelTest, ProduceAndConsume) { sp<Fence> consumerFence; sp<Fence> consumerFence; uint32_t maxAcquiredBufferCount; uint32_t maxAcquiredBufferCount; ASSERT_EQ(OK, ASSERT_EQ(OK, consumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); mConsumer->readReleaseFence(consumerId, consumerFence, maxAcquiredBufferCount)); ASSERT_EQ(expectedId, consumerId); ASSERT_EQ(expectedId, consumerId); ASSERT_TRUE(is_same_file(fence->get(), consumerFence->get())); 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 } // namespace android No newline at end of file
libs/gui/tests/EndToEndNativeInputTest.cpp +7 −7 Original line number Original line Diff line number Diff line Loading @@ -294,7 +294,7 @@ public: transactionBody) { transactionBody) { SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t; transactionBody(t, mSurfaceControl); transactionBody(t, mSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } } virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { virtual void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) { Loading @@ -307,7 +307,7 @@ public: t.setAlpha(mSurfaceControl, 1); t.setAlpha(mSurfaceControl, 1); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); auto reportedListener = sp<SynchronousWindowInfosReportedListener>::make(); t.addWindowInfosReportedListener(reportedListener); t.addWindowInfosReportedListener(reportedListener); t.apply(); t.apply(/*synchronously=*/true); reportedListener->wait(); reportedListener->wait(); } } Loading @@ -319,7 +319,7 @@ public: request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.timestamp = systemTime(SYSTEM_TIME_MONOTONIC); request.displayId = displayId.val(); request.displayId = displayId.val(); t.setFocusedWindow(request); t.setFocusedWindow(request); t.apply(true); t.apply(/*synchronously=*/true); } } public: public: Loading Loading @@ -363,7 +363,7 @@ public: transactionBody) override { transactionBody) override { SurfaceComposerClient::Transaction t; SurfaceComposerClient::Transaction t; transactionBody(t, mParentSurfaceControl); transactionBody(t, mParentSurfaceControl); t.apply(true); t.apply(/*synchronously=*/true); } } void showAt(int x, int y, Rect crop = Rect(0, 0, 100, 100)) override { 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.setInputWindowInfo(mSurfaceControl, mInputInfo); t.setCrop(mSurfaceControl, crop); t.setCrop(mSurfaceControl, crop); t.setAlpha(mSurfaceControl, 1); t.setAlpha(mSurfaceControl, 1); t.apply(true); t.apply(/*synchronously=*/true); } } private: private: Loading Loading @@ -417,7 +417,7 @@ public: BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; BufferUsage::COMPOSER_OVERLAY | BufferUsage::GPU_TEXTURE; sp<GraphicBuffer> buffer = sp<GraphicBuffer> buffer = new GraphicBuffer(w, h, PIXEL_FORMAT_RGBA_8888, 1, usageFlags, "test"); 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); usleep(mBufferPostDelay); } } Loading Loading @@ -1207,7 +1207,7 @@ public: t.setDisplayLayerStack(token, layerStack); t.setDisplayLayerStack(token, layerStack); t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, t.setDisplayProjection(token, ui::ROTATION_0, {0, 0, width, height}, {offsetX, offsetY, offsetX + width, offsetY + height}); {offsetX, offsetY, offsetX + width, offsetY + height}); t.apply(true); t.apply(/*synchronously=*/true); mVirtualDisplays.push_back(token); mVirtualDisplays.push_back(token); } } Loading