/* * Copyright (C) 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef ANDROID_GUI_BLAST_BUFFER_QUEUE_H #define ANDROID_GUI_BLAST_BUFFER_QUEUE_H #include #include #include #include #include #include #include #include #include #include #include namespace android { class BLASTBufferQueue; class BufferItemConsumer; class BLASTBufferItemConsumer : public BufferItemConsumer { public: BLASTBufferItemConsumer(const sp& consumer, uint64_t consumerUsage, int bufferCount, bool controlledByApp, wp bbq) : BufferItemConsumer(consumer, consumerUsage, bufferCount, controlledByApp), mBLASTBufferQueue(std::move(bbq)), mCurrentlyConnected(false), mPreviouslyConnected(false) {} void onDisconnect() override EXCLUDES(mMutex); void addAndGetFrameTimestamps(const NewFrameEventsEntry* newTimestamps, FrameEventHistoryDelta* outDelta) override EXCLUDES(mMutex); void updateFrameTimestamps(uint64_t frameNumber, uint64_t previousFrameNumber, nsecs_t refreshStartTime, const sp& gpuCompositionDoneFence, const sp& presentFence, const sp& prevReleaseFence, CompositorTiming compositorTiming, nsecs_t latchTime, nsecs_t dequeueReadyTime) EXCLUDES(mMutex); void getConnectionEvents(uint64_t frameNumber, bool* needsDisconnect) EXCLUDES(mMutex); void resizeFrameEventHistory(size_t newSize); protected: void onSidebandStreamChanged() override EXCLUDES(mMutex); #if COM_ANDROID_GRAPHICS_LIBGUI_FLAGS(BQ_SETFRAMERATE) void onSetFrameRate(float frameRate, int8_t compatibility, int8_t changeFrameRateStrategy) override; #endif private: const wp mBLASTBufferQueue; uint64_t mCurrentFrameNumber GUARDED_BY(mMutex) = 0; Mutex mMutex; ConsumerFrameEventHistory mFrameEventHistory GUARDED_BY(mMutex); std::queue mDisconnectEvents GUARDED_BY(mMutex); bool mCurrentlyConnected GUARDED_BY(mMutex); bool mPreviouslyConnected GUARDED_BY(mMutex); }; class BLASTBufferQueue : public ConsumerBase::FrameAvailableListener { public: BLASTBufferQueue(const std::string& name, bool updateDestinationFrame = true); BLASTBufferQueue(const std::string& name, const sp& surface, int width, int height, int32_t format); sp getIGraphicBufferProducer() const { return mProducer; } sp getSurface(bool includeSurfaceControlHandle); bool isSameSurfaceControl(const sp& surfaceControl) const; void onFrameReplaced(const BufferItem& item) override; void onFrameAvailable(const BufferItem& item) override; void onFrameDequeued(const uint64_t) override; void onFrameCancelled(const uint64_t) override; void transactionCommittedCallback(nsecs_t latchTime, const sp& presentFence, const std::vector& stats); virtual void transactionCallback(nsecs_t latchTime, const sp& presentFence, const std::vector& stats); void releaseBufferCallback(const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount); void releaseBufferCallbackLocked(const ReleaseCallbackId& id, const sp& releaseFence, std::optional currentMaxAcquiredBufferCount, bool fakeRelease) REQUIRES(mMutex); bool syncNextTransaction(std::function callback, bool acquireSingleBuffer = true); void stopContinuousSyncTransaction(); void clearSyncTransaction(); void mergeWithNextTransaction(SurfaceComposerClient::Transaction* t, uint64_t frameNumber); void applyPendingTransactions(uint64_t frameNumber); SurfaceComposerClient::Transaction* gatherPendingTransactions(uint64_t frameNumber); void update(const sp& surface, uint32_t width, uint32_t height, int32_t format); status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless); status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info); void setSidebandStream(const sp& stream); uint32_t getLastTransformHint() const; uint64_t getLastAcquiredFrameNum(); /** * Set a callback to be invoked when we are hung. The string parameter * indicates the reason for the hang. */ void setTransactionHangCallback(std::function callback); virtual ~BLASTBufferQueue(); private: friend class BLASTBufferQueueHelper; friend class BBQBufferQueueProducer; // can't be copied BLASTBufferQueue& operator = (const BLASTBufferQueue& rhs); BLASTBufferQueue(const BLASTBufferQueue& rhs); void createBufferQueue(sp* outProducer, sp* outConsumer); void resizeFrameEventHistory(size_t newSize); status_t acquireNextBufferLocked( const std::optional transaction) REQUIRES(mMutex); Rect computeCrop(const BufferItem& item) REQUIRES(mMutex); // Return true if we need to reject the buffer based on the scaling mode and the buffer size. bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex); static PixelFormat convertBufferFormat(PixelFormat& format); void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber) REQUIRES(mMutex); void flushShadowQueue() REQUIRES(mMutex); void acquireAndReleaseBuffer() REQUIRES(mMutex); void releaseBuffer(const ReleaseCallbackId& callbackId, const sp& releaseFence) REQUIRES(mMutex); std::string mName; // Represents the queued buffer count from buffer queue, // pre-BLAST. This is mNumFrameAvailable (buffers that queued to blast) + // mNumAcquired (buffers that queued to SF) mPendingRelease.size() (buffers that are held by // blast). This counter is read by android studio profiler. std::string mQueuedBufferTrace; sp mSurfaceControl GUARDED_BY(mMutex); mutable std::mutex mMutex; std::condition_variable mCallbackCV; // BufferQueue internally allows 1 more than // the max to be acquired int32_t mMaxAcquiredBuffers = 1; int32_t mNumFrameAvailable GUARDED_BY(mMutex) = 0; int32_t mNumAcquired GUARDED_BY(mMutex) = 0; // A value used to identify if a producer has been changed for the same SurfaceControl. // This is needed to know when the frame number has been reset to make sure we don't // latch stale buffers and that we don't wait on barriers from an old producer. uint32_t mProducerId = 0; // Keep a reference to the submitted buffers so we can release when surfaceflinger drops the // buffer or the buffer has been presented and a new buffer is ready to be presented. std::unordered_map mSubmitted GUARDED_BY(mMutex); // Keep a queue of the released buffers instead of immediately releasing // the buffers back to the buffer queue. This would be controlled by SF // setting the max acquired buffer count. struct ReleasedBuffer { ReleaseCallbackId callbackId; sp releaseFence; bool operator==(const ReleasedBuffer& rhs) const { // Only compare Id so if we somehow got two callbacks // with different fences we don't decrement mNumAcquired // too far. return rhs.callbackId == callbackId; } }; std::deque mPendingRelease GUARDED_BY(mMutex); ui::Size mSize GUARDED_BY(mMutex); ui::Size mRequestedSize GUARDED_BY(mMutex); int32_t mFormat GUARDED_BY(mMutex); struct BufferInfo { bool hasBuffer = false; uint32_t width; uint32_t height; uint32_t transform; // This is used to check if we should update the blast layer size immediately or wait until // we get the next buffer. This will support scenarios where the layer can change sizes // and the buffer will scale to fit the new size. uint32_t scalingMode = NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW; Rect crop; void update(bool hasBuffer, uint32_t width, uint32_t height, uint32_t transform, uint32_t scalingMode, const Rect& crop) { this->hasBuffer = hasBuffer; this->width = width; this->height = height; this->transform = transform; this->scalingMode = scalingMode; if (!crop.isEmpty()) { this->crop = crop; } else { this->crop = Rect(width, height); } } }; // Last acquired buffer's info. This is used to calculate the correct scale when size change is // requested. We need to use the old buffer's info to determine what scale we need to apply to // ensure the correct size. BufferInfo mLastBufferInfo GUARDED_BY(mMutex); void setMatrix(SurfaceComposerClient::Transaction* t, const BufferInfo& bufferInfo) REQUIRES(mMutex); uint32_t mTransformHint GUARDED_BY(mMutex); sp mConsumer; sp mProducer; sp mBufferItemConsumer; std::function mTransactionReadyCallback GUARDED_BY(mMutex); SurfaceComposerClient::Transaction* mSyncTransaction GUARDED_BY(mMutex); std::vector> mPendingTransactions GUARDED_BY(mMutex); std::queue> mPendingFrameTimelines GUARDED_BY(mMutex); // Tracks the last acquired frame number uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0; // Queues up transactions using this token in SurfaceFlinger. This prevents queued up // transactions from other parts of the client from blocking this transaction. const sp mApplyToken GUARDED_BY(mMutex) = sp::make(); // Guards access to mDequeueTimestamps since we cannot hold to mMutex in onFrameDequeued or // we will deadlock. std::mutex mTimestampMutex; // Tracks buffer dequeue times by the client. This info is sent to SurfaceFlinger which uses // it for debugging purposes. std::unordered_map mDequeueTimestamps GUARDED_BY(mTimestampMutex); // Keep track of SurfaceControls that have submitted a transaction and BBQ is waiting on a // callback for them. std::queue> mSurfaceControlsWithPendingCallback GUARDED_BY(mMutex); uint32_t mCurrentMaxAcquiredBufferCount GUARDED_BY(mMutex); // Flag to determine if syncTransaction should only acquire a single buffer and then clear or // continue to acquire buffers until explicitly cleared bool mAcquireSingleBuffer GUARDED_BY(mMutex) = true; // True if BBQ will update the destination frame used to scale the buffer to the requested size. // If false, the caller is responsible for updating the destination frame on the BBQ // surfacecontol. This is useful if the caller wants to synchronize the buffer scale with // additional scales in the hierarchy. bool mUpdateDestinationFrame GUARDED_BY(mMutex) = true; // We send all transactions on our apply token over one-way binder calls to avoid blocking // client threads. All of our transactions remain in order, since they are one-way binder calls // from a single process, to a single interface. However once we give up a Transaction for sync // we can start to have ordering issues. When we return from sync to normal frame production, // we wait on the commit callback of sync frames ensuring ordering, however we don't want to // wait on the commit callback for every normal frame (since even emitting them has a // performance cost) this means we need a method to ensure frames are in order when switching // from one-way application on our apply token, to application on some other apply token. We // make use of setBufferHasBarrier to declare this ordering. This boolean simply tracks when we // need to set this flag, notably only in the case where we are transitioning from a previous // transaction applied by us (one way, may not yet have reached server) and an upcoming // transaction that will be applied by some sync consumer. bool mAppliedLastTransaction GUARDED_BY(mMutex) = false; uint64_t mLastAppliedFrameNumber GUARDED_BY(mMutex) = 0; std::function mTransactionHangCallback; std::unordered_set mSyncedFrameNumbers GUARDED_BY(mMutex); }; } // namespace android #endif // ANDROID_GUI_SURFACE_H