Loading libs/gui/BLASTBufferQueue.cpp +105 −1 Original line number Diff line number Diff line Loading @@ -22,8 +22,13 @@ #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> #include <gui/GLConsumer.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> #include <utils/Singleton.h> #include <utils/Trace.h> Loading Loading @@ -118,7 +123,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont mSize(width, height), mRequestedSize(mSize), mNextTransaction(nullptr) { BufferQueue::createBufferQueue(&mProducer, &mConsumer); createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max()); Loading Loading @@ -454,4 +459,103 @@ sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { return new BBQSurface(mProducer, true, scHandle, this); } // Maintains a single worker thread per process that services a list of runnables. class AsyncWorker : public Singleton<AsyncWorker> { private: std::thread mThread; bool mDone = false; std::deque<std::function<void()>> mRunnables; std::mutex mMutex; std::condition_variable mCv; void run() { std::unique_lock<std::mutex> lock(mMutex); while (!mDone) { mCv.wait(lock); while (!mRunnables.empty()) { std::function<void()> runnable = mRunnables.front(); mRunnables.pop_front(); runnable(); } } } public: AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); } ~AsyncWorker() { mDone = true; mCv.notify_all(); if (mThread.joinable()) { mThread.join(); } } void post(std::function<void()> runnable) { std::unique_lock<std::mutex> lock(mMutex); mRunnables.emplace_back(std::move(runnable)); mCv.notify_one(); } }; ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker); // Asynchronously calls ProducerListener functions so we can emulate one way binder calls. class AsyncProducerListener : public BnProducerListener { private: const sp<IProducerListener> mListener; public: AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {} void onBufferReleased() override { AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); }); } void onBuffersDiscarded(const std::vector<int32_t>& slots) override { AsyncWorker::getInstance().post( [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); }); } }; // Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls // can be non-blocking when the producer is in the client process. class BBQBufferQueueProducer : public BufferQueueProducer { public: BBQBufferQueueProducer(const sp<BufferQueueCore>& core) : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {} status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override { if (!listener) { return BufferQueueProducer::connect(listener, api, producerControlledByApp, output); } return BufferQueueProducer::connect(new AsyncProducerListener(listener), api, producerControlledByApp, output); } }; // Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer. // This BQP allows invoking client specified ProducerListeners and invoke them asynchronously, // emulating one way binder call behavior. Without this, if the listener calls back into the queue, // we can deadlock. void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer) { LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL"); LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL"); sp<BufferQueueCore> core(new BufferQueueCore()); LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore"); sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core)); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BLASTBufferQueue: failed to create BBQBufferQueueProducer"); sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BLASTBufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; } } // namespace android libs/gui/include/gui/BLASTBufferQueue.h +2 −0 Original line number Diff line number Diff line Loading @@ -97,6 +97,8 @@ private: // can't be copied BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs); BLASTBufferQueue(const BLASTBufferQueue& rhs); void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer); void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); Loading libs/gui/tests/BLASTBufferQueue_test.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -475,6 +475,46 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { /*border*/ 0, /*outsideRegion*/ true)); } class TestProducerListener : public BnProducerListener { public: sp<IGraphicBufferProducer> mIgbp; TestProducerListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {} void onBufferReleased() override { sp<GraphicBuffer> buffer; sp<Fence> fence; mIgbp->detachNextBuffer(&buffer, &fence); } }; TEST_F(BLASTBufferQueueTest, CustomProducerListener) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer(); ASSERT_NE(nullptr, igbProducer.get()); ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); IGraphicBufferProducer::QueueBufferOutput qbOutput; ASSERT_EQ(NO_ERROR, igbProducer->connect(new TestProducerListener(igbProducer), NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); for (int i = 0; i < 3; i++) { int slot; sp<Fence> fence; sp<GraphicBuffer> buf; auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); IGraphicBufferProducer::QueueBufferOutput qbOutput; IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN, Rect(mDisplayWidth, mDisplayHeight), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); } adapter.waitForCallbacks(); } class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest { public: void test(uint32_t tr) { Loading Loading
libs/gui/BLASTBufferQueue.cpp +105 −1 Original line number Diff line number Diff line Loading @@ -22,8 +22,13 @@ #include <gui/BLASTBufferQueue.h> #include <gui/BufferItemConsumer.h> #include <gui/BufferQueueConsumer.h> #include <gui/BufferQueueCore.h> #include <gui/BufferQueueProducer.h> #include <gui/GLConsumer.h> #include <gui/IProducerListener.h> #include <gui/Surface.h> #include <utils/Singleton.h> #include <utils/Trace.h> Loading Loading @@ -118,7 +123,7 @@ BLASTBufferQueue::BLASTBufferQueue(const std::string& name, const sp<SurfaceCont mSize(width, height), mRequestedSize(mSize), mNextTransaction(nullptr) { BufferQueue::createBufferQueue(&mProducer, &mConsumer); createBufferQueue(&mProducer, &mConsumer); // since the adapter is in the client process, set dequeue timeout // explicitly so that dequeueBuffer will block mProducer->setDequeueTimeout(std::numeric_limits<int64_t>::max()); Loading Loading @@ -454,4 +459,103 @@ sp<Surface> BLASTBufferQueue::getSurface(bool includeSurfaceControlHandle) { return new BBQSurface(mProducer, true, scHandle, this); } // Maintains a single worker thread per process that services a list of runnables. class AsyncWorker : public Singleton<AsyncWorker> { private: std::thread mThread; bool mDone = false; std::deque<std::function<void()>> mRunnables; std::mutex mMutex; std::condition_variable mCv; void run() { std::unique_lock<std::mutex> lock(mMutex); while (!mDone) { mCv.wait(lock); while (!mRunnables.empty()) { std::function<void()> runnable = mRunnables.front(); mRunnables.pop_front(); runnable(); } } } public: AsyncWorker() : Singleton<AsyncWorker>() { mThread = std::thread(&AsyncWorker::run, this); } ~AsyncWorker() { mDone = true; mCv.notify_all(); if (mThread.joinable()) { mThread.join(); } } void post(std::function<void()> runnable) { std::unique_lock<std::mutex> lock(mMutex); mRunnables.emplace_back(std::move(runnable)); mCv.notify_one(); } }; ANDROID_SINGLETON_STATIC_INSTANCE(AsyncWorker); // Asynchronously calls ProducerListener functions so we can emulate one way binder calls. class AsyncProducerListener : public BnProducerListener { private: const sp<IProducerListener> mListener; public: AsyncProducerListener(const sp<IProducerListener>& listener) : mListener(listener) {} void onBufferReleased() override { AsyncWorker::getInstance().post([listener = mListener]() { listener->onBufferReleased(); }); } void onBuffersDiscarded(const std::vector<int32_t>& slots) override { AsyncWorker::getInstance().post( [listener = mListener, slots = slots]() { listener->onBuffersDiscarded(slots); }); } }; // Extends the BufferQueueProducer to create a wrapper around the listener so the listener calls // can be non-blocking when the producer is in the client process. class BBQBufferQueueProducer : public BufferQueueProducer { public: BBQBufferQueueProducer(const sp<BufferQueueCore>& core) : BufferQueueProducer(core, false /* consumerIsSurfaceFlinger*/) {} status_t connect(const sp<IProducerListener>& listener, int api, bool producerControlledByApp, QueueBufferOutput* output) override { if (!listener) { return BufferQueueProducer::connect(listener, api, producerControlledByApp, output); } return BufferQueueProducer::connect(new AsyncProducerListener(listener), api, producerControlledByApp, output); } }; // Similar to BufferQueue::createBufferQueue but creates an adapter specific bufferqueue producer. // This BQP allows invoking client specified ProducerListeners and invoke them asynchronously, // emulating one way binder call behavior. Without this, if the listener calls back into the queue, // we can deadlock. void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer) { LOG_ALWAYS_FATAL_IF(outProducer == nullptr, "BLASTBufferQueue: outProducer must not be NULL"); LOG_ALWAYS_FATAL_IF(outConsumer == nullptr, "BLASTBufferQueue: outConsumer must not be NULL"); sp<BufferQueueCore> core(new BufferQueueCore()); LOG_ALWAYS_FATAL_IF(core == nullptr, "BLASTBufferQueue: failed to create BufferQueueCore"); sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core)); LOG_ALWAYS_FATAL_IF(producer == nullptr, "BLASTBufferQueue: failed to create BBQBufferQueueProducer"); sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core)); LOG_ALWAYS_FATAL_IF(consumer == nullptr, "BLASTBufferQueue: failed to create BufferQueueConsumer"); *outProducer = producer; *outConsumer = consumer; } } // namespace android
libs/gui/include/gui/BLASTBufferQueue.h +2 −0 Original line number Diff line number Diff line Loading @@ -97,6 +97,8 @@ private: // can't be copied BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs); BLASTBufferQueue(const BLASTBufferQueue& rhs); void createBufferQueue(sp<IGraphicBufferProducer>* outProducer, sp<IGraphicBufferConsumer>* outConsumer); void processNextBufferLocked(bool useNextTransaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); Loading
libs/gui/tests/BLASTBufferQueue_test.cpp +40 −0 Original line number Diff line number Diff line Loading @@ -475,6 +475,46 @@ TEST_F(BLASTBufferQueueTest, SetCrop_ScalingModeScaleCrop) { /*border*/ 0, /*outsideRegion*/ true)); } class TestProducerListener : public BnProducerListener { public: sp<IGraphicBufferProducer> mIgbp; TestProducerListener(const sp<IGraphicBufferProducer>& igbp) : mIgbp(igbp) {} void onBufferReleased() override { sp<GraphicBuffer> buffer; sp<Fence> fence; mIgbp->detachNextBuffer(&buffer, &fence); } }; TEST_F(BLASTBufferQueueTest, CustomProducerListener) { BLASTBufferQueueHelper adapter(mSurfaceControl, mDisplayWidth, mDisplayHeight); sp<IGraphicBufferProducer> igbProducer = adapter.getIGraphicBufferProducer(); ASSERT_NE(nullptr, igbProducer.get()); ASSERT_EQ(NO_ERROR, igbProducer->setMaxDequeuedBufferCount(2)); IGraphicBufferProducer::QueueBufferOutput qbOutput; ASSERT_EQ(NO_ERROR, igbProducer->connect(new TestProducerListener(igbProducer), NATIVE_WINDOW_API_CPU, false, &qbOutput)); ASSERT_NE(ui::Transform::ROT_INVALID, qbOutput.transformHint); for (int i = 0; i < 3; i++) { int slot; sp<Fence> fence; sp<GraphicBuffer> buf; auto ret = igbProducer->dequeueBuffer(&slot, &fence, mDisplayWidth, mDisplayHeight, PIXEL_FORMAT_RGBA_8888, GRALLOC_USAGE_SW_WRITE_OFTEN, nullptr, nullptr); ASSERT_EQ(IGraphicBufferProducer::BUFFER_NEEDS_REALLOCATION, ret); ASSERT_EQ(OK, igbProducer->requestBuffer(slot, &buf)); IGraphicBufferProducer::QueueBufferOutput qbOutput; IGraphicBufferProducer::QueueBufferInput input(systemTime(), false, HAL_DATASPACE_UNKNOWN, Rect(mDisplayWidth, mDisplayHeight), NATIVE_WINDOW_SCALING_MODE_FREEZE, 0, Fence::NO_FENCE); igbProducer->queueBuffer(slot, input, &qbOutput); } adapter.waitForCallbacks(); } class BLASTBufferQueueTransformTest : public BLASTBufferQueueTest { public: void test(uint32_t tr) { Loading