BLASTBufferQueue源码深入理解

Android14图形系统升级:BLASTBufferQueue如何提升SurfaceFlinger性能? 本文深入解析了Android 14图形系统的关键升级——BLASTBufferQueue如何通过重构SurfaceFlinger的渲染管线来提升性能。它通过减少进程间通信(IPC)开销、优化缓冲区生命周期管理以及引入事务化提交机制,显著提升了高帧率场景下的渲染效率和系统响应速度。 阅读详情
AI摘要

本文围绕BLASTBufferQueue展开,介绍了BufferQueue部分的核心方法,如生产者的dequeueBuffer、requestBuffer、queueBuffer等方法,消费者的acquireBuffer、releaseBuffer等方法,还分析了BufferSlot的状态转移。此外,探讨了sf触发releaseBuffer的流程,解释了其在下一帧才调用到app层面的原因。

BLASTBufferQueue创建部分

BLASTBufferQueue::BLASTBufferQueue(const std::string& name, bool updateDestinationFrame)
      : mSurfaceControl(nullptr),
        mSize(1, 1),
        mRequestedSize(mSize),
        mFormat(PIXEL_FORMAT_RGBA_8888),
        mTransactionReadyCallback(nullptr),
        mSyncTransaction(nullptr),
        mUpdateDestinationFrame(updateDestinationFrame) {
    //创建BufferQueue部分,并且会给mProducer,mConsumer进行赋值
    createBufferQueue(&mProducer, &mConsumer);
   
   //创建BLASTBufferItemConsumer部分
    mBufferItemConsumer = new BLASTBufferItemConsumer(mConsumer,
                                                      GraphicBuffer::USAGE_HW_COMPOSER |
                                                              GraphicBuffer::USAGE_HW_TEXTURE,
                                                      1, false, this);
    static int32_t id = 0;
    mName = name + "#" + std::to_string(id);
    auto consumerName = mName + "(BLAST Consumer)" + std::to_string(id);
    mQueuedBufferTrace = "QueuedBuffer - " + mName + "BLAST#" + std::to_string(id);
    id++;
    mBufferItemConsumer->setName(String8(consumerName.c_str()));
    mBufferItemConsumer->setFrameAvailableListener(this);
    mBufferItemConsumer->setBufferFreedListener(this);

    ComposerService::getComposerService()->getMaxAcquiredBufferCount(&mMaxAcquiredBuffers);
    mBufferItemConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBuffers);
    mCurrentMaxAcquiredBufferCount = mMaxAcquiredBuffers;
    mNumAcquired = 0;
    mNumFrameAvailable = 0;

}

BufferQueue部分

下面来看看核心的方法createBufferQueue

void BLASTBufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
                                         sp<IGraphicBufferConsumer>* outConsumer) {
  //创建核心的BufferQueueCore
    sp<BufferQueueCore> core(new BufferQueueCore());

  //基于BufferQueueCore创建BBQBufferQueueProducer
    sp<IGraphicBufferProducer> producer(new BBQBufferQueueProducer(core));
     //基于BufferQueueCore创建BufferQueueConsumer
    sp<BufferQueueConsumer> consumer(new BufferQueueConsumer(core));
    consumer->setAllowExtraAcquire(true);
  
    *outProducer = producer;
    *outConsumer = consumer;
}

下面重点看看BufferQueueCore

BufferQueueCore::BufferQueueCore()
      : mMutex(),//若干个成员变量初始化
        mIsAbandoned(false),
        mConsumerControlledByApp(false),
        mConsumerName(getUniqueName()),
        mConsumerListener(),
        mConsumerUsageBits(0),
        mConsumerIsProtected(false),
        mConnectedApi(NO_CONNECTED_API),
        mLinkedToDeath(),
        mConnectedProducerListener(),
        mBufferReleasedCbEnabled(false),
        mSlots(),
        mQueue(),
        mFreeSlots(),
        mFreeBuffers(),
        mUnusedSlots(),
        mActiveBuffers(),
        mDequeueCondition(),
        mDequeueBufferCannotBlock(false),
        mQueueBufferCanDrop(false),
        mLegacyBufferDrop(true),
        mDefaultBufferFormat(PIXEL_FORMAT_RGBA_8888),
        mDefaultWidth(1),
        mDefaultHeight(1),
        mDefaultBufferDataSpace(HAL_DATASPACE_UNKNOWN),
        mMaxBufferCount(BufferQueueDefs::NUM_BUFFER_SLOTS),
        mMaxAcquiredBufferCount(1),
        mMaxDequeuedBufferCount(1),
        mBufferHasBeenQueued(false),
        mFrameCounter(0),
        mTransformHint(0),
        mIsAllocating(false),
        mIsAllocatingCondition(),
        mAllowAllocation(true),
        mBufferAge(0),
        mGenerationNumber(0),
        mAsyncMode(false),
        mSharedBufferMode(false),
        mAutoRefresh(false),
        mSharedBufferSlot(INVALID_BUFFER_SLOT),
        mSharedBufferCache(Rect::INVALID_RECT, 0, NATIVE_WINDOW_SCALING_MODE_FREEZE,
                           HAL_DATASPACE_UNKNOWN),
        mLastQueuedSlot(INVALID_BUFFER_SLOT),
        mUniqueId(getUniqueId()),
        mAutoPrerotation(false),
        mTransformHintInUse(0) {
    int numStartingBuffers = getMaxBufferCountLocked();
    for (int s = 0; s < numStartingBuffers; s++) {
        mFreeSlots.insert(s);//初始化时候针对mFreeSlots填入了MaxBufferCount个
    }
    for (int s = numStartingBuffers; s < BufferQueueDefs::NUM_BUFFER_SLOTS;
            s++) {
        mUnusedSlots.push_front(s);//除了mFreeSlots部分,其他都是填入mUnusedSlots
    }
}

上面的构造中有以下几个成员变量非常关键需要重点介绍

// mSlots is an array of buffer slots that must be mirrored on the producer
    // side. This allows buffer ownership to be transferred between the producer
    // and consumer without sending a GraphicBuffer over Binder. The entire
    // array is initialized to NULL at construction time, and buffers are
    // allocated for a slot when requestBuffer is called with that slot's index.
    BufferQueueDefs::SlotsType mSlots;

    // mQueue is a FIFO of queued buffers used in synchronous mode.
    Fifo mQueue;

    // mFreeSlots contains all of the slots which are FREE and do not currently
    // have a buffer attached.
    std::set<int> mFreeSlots;

    // mFreeBuffers contains all of the slots which are FREE and currently have
    // a buffer attached.
    std::list<int> mFreeBuffers;

    // mUnusedSlots contains all slots that are currently unused. They should be
    // free and not have a buffer attached.
    std::list<int> mUnusedSlots;

    // mActiveBuffers contains all slots which have a non-FREE buffer attached.
    std::set<int> mActiveBuffers;

mQueue —> 存放BufferItem的FIFO队列
mSlots —> BufferSlot结构体数组,数组长度固定为64
mFreeSlots —> BufferSlot状态为FREE,且没有GraphicBuffer与之相绑定的slot集合
mFreeBuffers —> BufferSlot状态为FREE,且有GraphicBuffer与之相绑定的slot集合
mActiveBuffers —> BufferSlot状态不为FREE(即DEQUEUED、QUEUED、ACQUIRED、SHARED)的slot集合。既然状态不是FREE,那么该BufferSlot必然有一个GraphicBuffer与之相绑定
mUnusedSlots —> 未参与使用的slot集合,由 mMaxBufferCount 决定

注意mFreeSlots,mFreeBuffers,mActiveBuffers,mUnusedSlots都其实是一个int集合,内容就是mSlots数组的一个个index

详细剖析一下mSlots这个变量:

namespace BufferQueueDefs {
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
    } // namespace BufferQueueDefs
    
struct BufferSlot {

    BufferSlot()
    : mGraphicBuffer(nullptr),
      mEglDisplay(EGL_NO_DISPLAY),
      mBufferState(),
      mRequestBufferCalled(false),
      mFrameNumber(0),
      mEglFence(EGL_NO_SYNC_KHR),
      mFence(Fence::NO_FENCE),
      mAcquireCalled(false),
      mNeedsReallocation(false) {
    }
      }

可以看到mSlots其实BufferSlot的一个数组,BufferSlot重要包含了mGraphicBuffer和mBufferState两个核心变量,下面来看看核心变量mBufferState,类型是BufferState

// BufferState tracks the states in which a buffer slot can be.
struct BufferState {

    // All slots are initially FREE (not dequeued, queued, acquired, or shared).
    BufferState()
    : mDequeueCount(0),
      mQueueCount(0),
      mAcquireCount(0),
      mShared(false) {
    }

    uint32_t mDequeueCount;
    uint32_t mQueueCount;
    uint32_t mAcquireCount;
    bool mShared;

    // A buffer can be in one of five states, represented as below:
    //
    //         | mShared | mDequeueCount | mQueueCount | mAcquireCount |
    // --------|---------|---------------|-------------|---------------|
    // FREE    |  false  |       0       |      0      |       0       |
    // DEQUEUED|  false  |       1       |      0      |       0       |
    // QUEUED  |  false  |       0       |      1      |       0       |
    // ACQUIRED|  false  |       0       |      0      |       1       |
    // SHARED  |  true   |      any      |     any     |      any      |
    //
    // FREE indicates that the buffer is available to be dequeued by the
    // producer. The slot is "owned" by BufferQueue. It transitions to DEQUEUED
    // when dequeueBuffer is called.
    //
    // DEQUEUED indicates that the buffer has been dequeued by the producer, but
    // has not yet been queued or canceled. The producer may modify the
    // buffer's contents as soon as the associated release fence is signaled.
    // The slot is "owned" by the producer. It can transition to QUEUED (via
    // queueBuffer or attachBuffer) or back to FREE (via cancelBuffer or
    // detachBuffer).
    //
    // QUEUED indicates that the buffer has been filled by the producer and
    // queued for use by the consumer. The buffer contents may continue to be
    // modified for a finite time, so the contents must not be accessed until
    // the associated fence is signaled. The slot is "owned" by BufferQueue. It
    // can transition to ACQUIRED (via acquireBuffer) or to FREE (if another
    // buffer is queued in asynchronous mode).
    //
    // ACQUIRED indicates that the buffer has been acquired by the consumer. As
    // with QUEUED, the contents must not be accessed by the consumer until the
    // acquire fence is signaled. The slot is "owned" by the consumer. It
    // transitions to FREE when releaseBuffer (or detachBuffer) is called. A
    // detached buffer can also enter the ACQUIRED state via attachBuffer.
    //
    // SHARED indicates that this buffer is being used in shared buffer
    // mode. It can be in any combination of the other states at the same time,
    // except for FREE (since that excludes being in any other state). It can
    // also be dequeued, queued, or acquired multiple times.

可以看到这里的BufferState就是我们常说的一下几个状态
// FREE
// DEQUEUED
// QUEUED
// ACQUIRED
// SHARED
但是大家可以看到,每一个状态并不是用类似enum这种变量的,而是用对对应整形计数变量表示,比如:
inline void dequeue() {
mDequeueCount++;
}
inline bool isDequeued() const {
return mDequeueCount > 0;
}

isDequeued()方法就是代表状态,dequeue就是操作变量,简单就是改变状态,都是使用各自计数来的

所有的状态方法如下:

//是否为Free状态就是判断一下其他三个状态计数是否为0
    inline bool isFree() const {
        return !isAcquired() && !isDequeued() && !isQueued();
    }

    inline bool isDequeued() const {
        return mDequeueCount > 0;
    }

    inline bool isQueued() const {
        return mQueueCount > 0;
    }

    inline bool isAcquired() const {
        return mAcquireCount > 0;
    }

    inline bool isShared() const {
        return mShared;
    }

    inline void reset() {
        *this = BufferState();
    }

    const char* string() const;

    inline void dequeue() {
        //dequeue操作就是简单的把mDequeueCount进行加操作
        mDequeueCount++;
    }

    inline void detachProducer() {
        if (mDequeueCount > 0) {
            mDequeueCount--;
        }
    }

    inline void attachProducer() {
        mDequeueCount++;
    }

    inline void queue() {
        //会对mDequeueCount先进行减
        if (mDequeueCount > 0) {
            mDequeueCount--;
        }
        //再对mQueueCount进行加操作
        mQueueCount++;
    }

    inline void cancel() {
        if (mDequeueCount > 0) {
            mDequeueCount--;
        }
    }

    inline void freeQueued() {
        if (mQueueCount > 0) {
            mQueueCount--;
        }
    }

    inline void acquire() {
        //首先对mQueueCount会进行减操作
        if (mQueueCount > 0) {
            mQueueCount--;
        }
        //然后再是对mAcquireCount进行加操作
        mAcquireCount++;
    }

    inline void acquireNotInQueue() {
        mAcquireCount++;
    }

    inline void release() {//release只对mAcquireCount进行了减操作
        if (mAcquireCount > 0) {
            mAcquireCount--;
        }
    }

    inline void detachConsumer() {
        if (mAcquireCount > 0) {
            mAcquireCount--;
        }
    }

    inline void attachConsumer() {
        mAcquireCount++;
    }
};

总结图:

在这里插入图片描述在这里插入图片描述

补充一下几个状态转移图:
在这里插入图片描述
–>1、一开始如果什么都没发生那肯定BufferSlot对应BufferState是FREE状态
---->2、生产者app一般先进行dequue操作,这个时候状态就变成了 DEQUEUE状态
------->3、生产者app dequeue获取了graphicbuffer后,绘制完成进行queue操作,变成了QUEUED状态
------------>4、queuebuffer后通知消费者,消费者接受通知后,会进行acquire操作,这个时候状态变成了ACQUIRED
--------------->5、消费者对buffer消费完成后就可以对bufferslot进行relaese操作,这个时候就变成FREE状态

dequeueBuffer() --生产者方法

status_t BufferQueueProducer::dequeueBuffer(int* outSlot, sp<android::Fence>* outFence,
                                            uint32_t width, uint32_t height, PixelFormat format,
                                            uint64_t usage, uint64_t* outBufferAge,
                                            FrameEventHistoryDelta* outTimestamps) {
  //省略
        int found = BufferItem::INVALID_BUFFER_SLOT;//作为入参found
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
        		//核心方法从mSlots数组找到一个可以用的BufferSlot,把相关的index赋值到found这个变量
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue, lock, &found);
        //省略
        }
        ATRACE_FORMAT("dequeueBuffer found = %d",found);
        const sp<GraphicBuffer>& buffer(mSlots[found].mGraphicBuffer);
        
        *outSlot = found;
        ATRACE_BUFFER_INDEX(found);

        attachedByConsumer = mSlots[found].mNeedsReallocation;
        mSlots[found].mNeedsReallocation = false;

        mSlots[found].mBufferState.dequeue();//把状态变成Dequeue

        if ((buffer == nullptr) ||
                buffer->needsReallocation(width, height, format, BQ_LAYER_COUNT, usage))
        {
         //如果需要进行buffer申请
            returnFlags |= BUFFER_NEEDS_REALLOCATION;
        } else {
            // We add 1 because that will be the frame number when this buffer
            // is queued
            mCore->mBufferAge = mCore->mFrameCounter + 1 - mSlots[found].mFrameNumber;
        }
//省略部分

        if (!(returnFlags & BUFFER_NEEDS_REALLOCATION)) {//不需要allocation
            if (mCore->mConsumerListener != nullptr) {//通知回调一下consumeronFrameDequeued
                mCore->mConsumerListener->onFrameDequeued(mSlots[*outSlot].mGraphicBuffer->getId());
            }
        }
    } // Autolock scope

    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
     //开始申请GraphicBuffer
        sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
                width, height, format, BQ_LAYER_COUNT, usage,
                {mConsumerName.string(), mConsumerName.size()});

        status_t error = graphicBuffer->initCheck();
        //申请GraphicBuffer成功
            if (error == NO_ERROR && !mCore->mIsAbandoned) {
                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                //把申请成功的buffer赋值到了mSlots的mGraphicBuffer中
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
                if (mCore->mConsumerListener != nullptr) {
                    mCore->mConsumerListener->onFrameDequeued(
                            mSlots[*outSlot].mGraphicBuffer->getId());
                }
            }
        } // Autolock scope
    }
 //省略
    return returnFlags;
}

接下来看看核心方法waitForFreeSlotThenRelock

status_t BufferQueueProducer::waitForFreeSlotThenRelock(FreeSlotCaller caller,
        std::unique_lock<std::mutex>& lock, int* found) const {
  
    while (tryAgain) {
        int dequeuedCount = 0;
        int acquiredCount = 0;
        for (int s : mCore->mActiveBuffers) {
            if (mSlots[s].mBufferState.isDequeued()) {
                ++dequeuedCount;
            }
            if (mSlots[s].mBufferState.isAcquired()) {
                ++acquiredCount;
            }
        }
        *found = BufferQueueCore::INVALID_BUFFER_SLOT;
//省略部分
        if (tooManyBuffers) {
        
        } else {
            // If in shared buffer mode and a shared buffer exists, always
            // return it.
            if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot !=
                    BufferQueueCore::INVALID_BUFFER_SLOT) {
                *found = mCore->mSharedBufferSlot;
            } else {
                if (caller == FreeSlotCaller::Dequeue) {//如果dequeue方法
                    // If we're calling this from dequeue, prefer free buffers
                    int slot = getFreeBufferLocked();//先从FreeBuffer的数组中获取
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else if (mCore->mAllowAllocation) {//如果没有找到则到FreeSlot数组中找
                        *found = getFreeSlotLocked();
                    }
                } else {
                    // If we're calling this from attach, prefer free slots
                    int slot = getFreeSlotLocked();//先从FreeSlot数组中找
                    if (slot != BufferQueueCore::INVALID_BUFFER_SLOT) {
                        *found = slot;
                    } else {//没找到再去FreeBuffer中找
                        *found = getFreeBufferLocked();
                    }
                }
            }
        }
//省略

    return NO_ERROR;
}
int BufferQueueProducer::getFreeSlotLocked() const {
    if (mCore->mFreeSlots.empty()) {
        return BufferQueueCore::INVALID_BUFFER_SLOT;
    }
    int slot = *(mCore->mFreeSlots.begin());
    mCore->mFreeSlots.erase(slot);
    return slot;
}

上面waitForFreeSlotThenRelock就是其实就是去FreeBuffer或者FreeSlot中寻找一个BufferSlot,返回这个slot的在数组的index,赋值给found

dequeueBuffer主要干的事情如下:
1、通过waitForFreeSlotThenRelock寻找到Free状态的BufferSlot
2、判断这个BufferSlot是否需要重新申请buffer,如果需要则进行构造新的GraphicBuffer

requestBuffer()–生产者方法

主要就是实现了根据传递进来的slot这个index,然后吧对应mSlots的mGraphicBuffer赋值给参数buf指针

status_t BufferQueueProducer::requestBuffer(int slot, sp<GraphicBuffer>* buf) {
    mSlots[slot].mRequestBufferCalled = true;
    *buf = mSlots[slot].mGraphicBuffer;
    return NO_ERROR;
}

queueBuffer()–生产者方法

//注意传递参数里面有一个关键的slot即mSlots中的index
status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
  
    BufferItem item;
    {

        mSlots[slot].mFence = acquireFence;
        mSlots[slot].mBufferState.queue();//这个地方就会调用mBufferState的queue,状态就改变了
        
				//开始把mSlots[slot]挨个赋值给BufferItem
        item.mAcquireCalled = mSlots[slot].mAcquireCalled;
        item.mGraphicBuffer = mSlots[slot].mGraphicBuffer;
        item.mCrop = crop;
        item.mTransform = transform &
                ~static_cast<uint32_t>(NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY);
        item.mTransformToDisplayInverse =
                (transform & NATIVE_WINDOW_TRANSFORM_INVERSE_DISPLAY) != 0;
        item.mScalingMode = static_cast<uint32_t>(scalingMode);
        item.mTimestamp = requestedPresentTimestamp;
        item.mIsAutoTimestamp = isAutoTimestamp;
        item.mDataSpace = dataSpace;
        item.mHdrMetadata = hdrMetadata;
        item.mFrameNumber = currentFrameNumber;
        item.mSlot = slot;
        item.mFence = acquireFence;
        item.mFenceTime = acquireFenceTime;
        item.mIsDroppable = mCore->mAsyncMode ||
                (mConsumerIsSurfaceFlinger && mCore->mQueueBufferCanDrop) ||
                (mCore->mLegacyBufferDrop && mCore->mQueueBufferCanDrop) ||
                (mCore->mSharedBufferMode && mCore->mSharedBufferSlot == slot);
        item.mSurfaceDamage = surfaceDamage;
        item.mQueuedBuffer = true;
        item.mAutoRefresh = mCore->mSharedBufferMode && mCore->mAutoRefresh;
        item.mApi = mCore->mConnectedApi;

        if (mCore->mQueue.empty()) {//如果mQueue为空
       
            mCore->mQueue.push_back(item);//直接push到mQueue的
            frameAvailableListener = mCore->mConsumerListener;
        } else {
            // When the queue is not empty, we need to look at the last buffer
            // in the queue to see if we need to replace it
            const BufferItem& last = mCore->mQueue.itemAt(
                    mCore->mQueue.size() - 1);//获取末尾的Item
             //判断末尾Item是不是可以被丢弃的
            if (last.mIsDroppable) {
			//省略
            } else {
                mCore->mQueue.push_back(item);//直接push
                frameAvailableListener = mCore->mConsumerListener;
            }
        }

        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);//这里核心部分,会调用到消费者的onFrameAvailable
        } else if (frameReplacedListener != nullptr) {
            frameReplacedListener->onFrameReplaced(item);
        }

    return NO_ERROR;
}

上面queueBuffer主要干了以下几件事:
1、根据入参slot,把mSlots中的BufferState变成Queued状态
2、根据BufferSlot相关变量构建出一个新的BufferItem对象,且塞入mQueue集合
3、最后通过onFrameAvailable通知消费者

onFrameAvailable


void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
    std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
    SurfaceComposerClient::Transaction* prevTransaction = nullptr;
    bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
        ATRACE_CALL();
    {
        BBQ_TRACE();
        std::unique_lock _lock{mMutex};
        const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
        BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));

        if (syncTransactionSet) {
            bool mayNeedToWaitForBuffer = true;
            // If we are going to re-use the same mSyncTransaction, release the buffer that may
            // already be set in the Transaction. This is to allow us a free slot early to continue
            // processing a new buffer.
            if (!mAcquireSingleBuffer) {
                auto bufferData = mSyncTransaction->getAndClearBuffer(mSurfaceControl);
                if (bufferData) {
                    BQA_LOGD("Releasing previous buffer when syncing: framenumber=%" PRIu64,
                             bufferData->frameNumber);
                    releaseBuffer(bufferData->generateReleaseCallbackId(),
                                  bufferData->acquireFence);
                    // Because we just released a buffer, we know there's no need to wait for a free
                    // buffer.
                    mayNeedToWaitForBuffer = false;
                }
            }

            if (mayNeedToWaitForBuffer) {
                flushAndWaitForFreeBuffer(_lock);
            }
        }

        // add to shadow queue
        mNumFrameAvailable++;
        if (waitForTransactionCallback && mNumFrameAvailable >= 2) {
            acquireAndReleaseBuffer();
        }
        ATRACE_INT(mQueuedBufferTrace.c_str(),
                   mNumFrameAvailable + mNumAcquired - mPendingRelease.size());

        BQA_LOGV("onFrameAvailable framenumber=%" PRIu64 " syncTransactionSet=%s",
                 item.mFrameNumber, boolToString(syncTransactionSet));
        {
            ATRACE_FORMAT("onFrameAvailable syncTransactionSet = %d",syncTransactionSet);
        }
        if (syncTransactionSet) {
            acquireNextBufferLocked(mSyncTransaction);

            // Only need a commit callback when syncing to ensure the buffer that's synced has been
            // sent to SF
            incStrong((void*)transactionCommittedCallbackThunk);
            mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
                                                              static_cast<void*>(this));
            mSyncedFrameNumbers.emplace(item.mFrameNumber);
            if (mAcquireSingleBuffer) {
                prevCallback = mTransactionReadyCallback;
                prevTransaction = mSyncTransaction;
                mTransactionReadyCallback = nullptr;
                mSyncTransaction = nullptr;
            }
        } else if (!waitForTransactionCallback) {
            acquireNextBufferLocked(std::nullopt);
        }
    }
    if (prevCallback) {
        prevCallback(prevTransaction);
    }
}

acquireNextBufferLocked


void BLASTBufferQueue::acquireNextBufferLocked(
        const std::optional<SurfaceComposerClient::Transaction*> transaction) {

    BufferItem bufferItem;

    status_t status =
            mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);//消费者中获取bufferItem

    auto buffer = bufferItem.mGraphicBuffer;
 
    auto releaseBufferCallback =
            std::bind(releaseBufferCallbackThunk, wp<BLASTBufferQueue>(this) /* callbackContext */,
                      std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
    sp<Fence> fence = bufferItem.mFence ? new Fence(bufferItem.mFence->dup()) : Fence::NO_FENCE;
    //最为关键吧buffer设置到了事务中
    t->setBuffer(mSurfaceControl, buffer, fence, bufferItem.mFrameNumber, releaseBufferCallback);
    t->setDataspace(mSurfaceControl, static_cast<ui::Dataspace>(bufferItem.mDataSpace));
    t->setHdrMetadata(mSurfaceControl, bufferItem.mHdrMetadata);
    t->setSurfaceDamageRegion(mSurfaceControl, bufferItem.mSurfaceDamage);
    t->addTransactionCompletedCallback(transactionCallbackThunk, static_cast<void*>(this));
//省略
}

acquireBuffer()–消费者方法

//注意这有个入参outBuffer
status_t BufferQueueConsumer::acquireBuffer(BufferItem* outBuffer,
        nsecs_t expectedPresent, uint64_t maxFrameNumber) {

        BufferQueueCore::Fifo::iterator front(mCore->mQueue.begin());//注意这个front就是mQueue的第一个
//省略
        if (sharedBufferAvailable && mCore->mQueue.empty()) {
 
        } else if (acquireNonDroppableBuffer && front->mIsDroppable) {
            return NO_BUFFER_AVAILABLE;
        } else {
            //把front的slot及指向对象赋值给outBuffer这个入参
            slot = front->mSlot;
            *outBuffer = *front;
        }

        if (!outBuffer->mIsStale) {
            mSlots[slot].mAcquireCalled = true;
            if (mCore->mQueue.empty()) {
                mSlots[slot].mBufferState.acquireNotInQueue();
            } else {
            //下面就是BufferState进行相关的改变,调用acquire方法
                mSlots[slot].mBufferState.acquire();
            }
            mSlots[slot].mFence = Fence::NO_FENCE;
        }

			//mQueue中移除掉这个front
        mCore->mQueue.erase(front);

    return NO_ERROR;
}

可以看到acquireBuffer主要干的事情如:
1、mQueue中取出最顶部的BufferItem

2、把顶部的BufferItem的slot获取,且让入参outBuffer指向该BufferItem

3、把mSlots中对应的BufferState变成acquired,且mQueue移除掉front

releaseBuffer()–消费者方法


status_t BufferQueueConsumer::releaseBuffer(int slot, uint64_t frameNumber,
        const sp<Fence>& releaseFence, EGLDisplay eglDisplay,
        EGLSyncKHR eglFence) {

    sp<IProducerListener> listener;
    { // Autolock scope
        std::lock_guard<std::mutex> lock(mCore->mMutex);
 //省略部分
        mSlots[slot].mEglDisplay = eglDisplay;
        mSlots[slot].mEglFence = eglFence;
        mSlots[slot].mFence = releaseFence;
        
     //最重要调用了mBufferState的release方法
        mSlots[slot].mBufferState.release();

     //省略部分
     //下面就把slot从 mCore->mActiveBuffers移除,放入到  mCore->mFreeBuffers
        if (!mSlots[slot].mBufferState.isShared()) {
            mCore->mActiveBuffers.erase(slot);
            mCore->mFreeBuffers.push_back(slot);
        }
 //省略部分

    return NO_ERROR;
}

整个release方法就干了2件事
1、调用了mBufferState的release方法,让状态就变成了FREE

2、从 mCore->mActiveBuffers移除,放入到 mCore->mFreeBuffers

相关release触发流程,这里以perffetto来结合代码看
每次的sf在合成时候都会触发上一帧画面的releaseBuffer操作
在这里插入图片描述

但是刚启动第一帧明显没有看到有releaseBuffer,第二帧才看见
第一帧,只见到了onTransactionCompleted:
在这里插入图片描述
第二帧,见到了onTransactionCompleted同时,也见到了releaseBuffer相关,因为这次release是上一帧的buffer:
在这里插入图片描述
看看前后两次差异到底在哪里?为啥第一次就没有releaseBuffer相关调用,第二次就有了呢?
看一下onTransactionCompleted方法的差异化执行:


void TransactionCompletedListener::onTransactionCompleted(ListenerStats listenerStats) {
    std::unordered_map<CallbackId, CallbackTranslation, CallbackIdHash> callbacksMap;
    std::multimap<int32_t, sp<JankDataListener>> jankListenersMap;
        ATRACE_CALL();
    
        // handle on complete callbacks
        for (auto callbackId : transactionStats.callbackIds) {
            if (callbackId.type != CallbackId::Type::ON_COMPLETE) {
                continue;
            }
            auto& [callbackFunction, callbackSurfaceControls] = callbacksMap[callbackId];
            if (!callbackFunction) {
                ALOGE("cannot call null callback function, skipping");
                continue;
            }
            std::vector<SurfaceControlStats> surfaceControlStats;
            for (const auto& surfaceStats : transactionStats.surfaceStats) {
                surfaceControlStats
                        .emplace_back(callbacksMap[callbackId]
                                              .surfaceControls[surfaceStats.surfaceControl],
                                      transactionStats.latchTime, surfaceStats.acquireTimeOrFence,
                                      transactionStats.presentFence,
                                      surfaceStats.previousReleaseFence, surfaceStats.transformHint,
                                      surfaceStats.eventStats,
                                      surfaceStats.currentMaxAcquiredBufferCount);
                if (callbacksMap[callbackId].surfaceControls[surfaceStats.surfaceControl]) {
                    callbacksMap[callbackId]
                            .surfaceControls[surfaceStats.surfaceControl]
                            ->setTransformHint(surfaceStats.transformHint);
                }
                //这里就是是否会执行releaseBuffer的关键,靠传递过来的previousReleaseCallbackId是不是有效
                if (surfaceStats.previousReleaseCallbackId != ReleaseCallbackId::INVALID_ID) {
                
                    ReleaseBufferCallback callback;
                    {
                        std::scoped_lock<std::mutex> lock(mMutex);
                        callback = popReleaseBufferCallbackLocked(
                                surfaceStats.previousReleaseCallbackId);
                    }
                    if (callback) {
                        callback(surfaceStats.previousReleaseCallbackId,
                                 surfaceStats.previousReleaseFence
                                         ? surfaceStats.previousReleaseFence
                                         : Fence::NO_FENCE,
                                 surfaceStats.currentMaxAcquiredBufferCount);
                    }
                }
            }

            callbackFunction(transactionStats.latchTime, transactionStats.presentFence,
                             surfaceControlStats);
        }
//省略
}

这里的previousReleaseCallbackId设置其实服务端sf进行的设置,下面会进行详细介绍。

sf如何触发的release呢?

触发流程如下:
sf端是在线程中发起的触发跨进程onTransactionCompleted调用
在这里插入图片描述这里sf子线程跨进程调用是由主线程sendCallbacks方法触发的

在这里插入图片描述

sendCallbacks方法

void TransactionCallbackInvoker::sendCallbacks(bool onCommitOnly) {
    ATRACE_CALL();
    // For each listener
    //获取全局的mCompletedTransactions变量再进行遍历
    auto completedTransactionsItr = mCompletedTransactions.begin();
    BackgroundExecutor::Callbacks callbacks;
    while (completedTransactionsItr != mCompletedTransactions.end()) {
    //省略部分
        // If the listener has completed transactions
        if (!listenerStats.transactionStats.empty()) {
            // If the listener is still alive
            if (listener->isBinderAlive()) {
            //塞入callbacks集合
                callbacks.emplace_back([stats = std::move(listenerStats)]() {
                        ATRACE_NAME("ITransactionCompletedListener onTransactionCompleted");
                    interface_cast<ITransactionCompletedListener>(stats.listener)
                            ->onTransactionCompleted(stats);
                });
            }
        }
        completedTransactionsItr++;
    }
    BackgroundExecutor::getInstance().sendCallbacks(std::move(callbacks));
}

重点看看mCompletedTransactions这个集合的哪来的,通过查询主要是findOrCreateTransactionStats方法进行加入相关的元素,追踪后发现实际上这里主要是看addCallbackHandles方法会填入对应,最终发现是在releasePendingBuffer方法中塞入:


void BufferStateLayer::releasePendingBuffer(nsecs_t dequeueReadyTime) {
  for (const auto& handle : mDrawingState.callbackHandles) {
        handle->transformHint = mTransformHint;
        handle->dequeueReadyTime = dequeueReadyTime;
        handle->currentMaxAcquiredBufferCount =
                mFlinger->getMaxAcquiredBufferCountForCurrentRefreshRate(mOwnerUid);
    }
//注意这里是把mPreviousReleaseCallbackId变量赋值给  handle->previousReleaseCallbackId 
    for (auto& handle : mDrawingState.callbackHandles) {
        if (handle->releasePreviousBuffer &&
            mDrawingState.releaseBufferEndpoint == handle->listener) {
            handle->previousReleaseCallbackId = mPreviousReleaseCallbackId;
            break;
        }
    }
 //调用addCallbackHandles
    mFlinger->getTransactionCallbackInvoker().addCallbackHandles(
            mDrawingState.callbackHandles, jankData);
}

这里的mPreviousReleaseCallbackId是哪里来的呢?那么就需要看updateActiveBuffer

updateActiveBuffer

updateActiveBuffer方法会对mPreviousReleaseCallbackId这个变量进行赋值,大家注意这里的为啥叫做前一帧的CallbackId,因为下面这个updateActiveBuffer就是赋值是先进行的mPreviousReleaseCallbackId赋值,然后才进行的新buffer的赋值,所以这个mPreviousReleaseCallbackId其实上一个的buffer的Id,不是当前这次的
在这里插入图片描述

综上既可以解释releaseBuffer是在下一帧上帧后才由sf调用到app层面让其releaseBuffer。

更多framework干货请关注“千里马学框架”

Android14 SurfaceFlinger-BLASTBufferQueue的创建 分为两步。 阅读详情

相关推荐

Android 12(S) 图形显示系统 - BufferQueue的工作流程(八)

Python微信订餐小程序课程视频 https://edu.youkuaiyun.com/course/detail/36074 Python实战量化交易理财系统 https://edu.youkuaiyun.com/course/detail/35475 题外话 最近总有一个感觉:在不断学习中,越发的感觉自己的无知,自己是不是要从“愚昧之巅”掉到“绝望之谷”了,哈哈哈🐶 邓宁-克鲁格效应 一、前言 前面的文章中已经讲解了如何去创建一个Surface,也讲了一些操作Surface的知识,接下来就是如何利用这个Surface

「 虚幻私塾」 4503

Android14 BLASTBufferQueue实战:从创建到Surface绘制的完整流程解析

本文深入解析了Android 14中BLASTBufferQueue的完整工作流程,从创建、初始化到Surface绘制与提交。作为默认的图形缓冲区管理架构,BLASTBufferQueue通过解耦应用与SurfaceFlinger,实现了更高效的异步事务通信,显著提升了UI动画和多窗口性能。文章结合实战代码,剖析了关键对象与调试技巧。

weixin_29306011的博客 222

Android14图形栈升级指南:BLASTBufferQueue特性详解与性能优化

本文深入解析了Android 14图形栈的核心升级——BLASTBufferQueue。它通过解耦渲染与合成时机、优化缓冲区管理,旨在显著降低合成延迟,提升应用渲染性能与帧率稳定性。文章详细剖析了其设计原理、创建流程及性能优化实战,为系统工程师释放设备图形潜力提供关键指导。

blue42的博客 445

BufferQueue研究

了解BufferQueue,有助于分析应用卡顿问题。

lonely_fireworks的专栏 3675

Android SurfaceFlinger——创建缓冲队列(二十二)

BLASTBufferQueue 是 Android 系统中用于图形缓冲区管理的一个重要组件,它是在 Android 12(代号为S)中引入的。在 Android 的图形显示系统中,BufferQueue 被用来协调生产者和消费者之间的缓冲区交换。然而,传统的 BufferQueue 实现涉及到了复杂的跨进程通信(IPC),这可能会成为性能瓶颈,尤其是在高帧率显示和复杂 UI 动画场景下。BLASTBufferQueue 目的是为了减少 IPC 的开销并提高效率。

小旭的专栏 1573

CMOS sensor 基础

NA

m0_72053187的博客 1658

Android Perfetto 系列 5:Android App 基于 Choreographer 的渲染流程

每个 Vsync 周期到来时,Vsync 信号唤醒 Choreographer 执行应用的绘制操作,如果每个 Vsync 周期应用都能渲染完成,那么应用的 fps 就是120,给用户的感觉就是非常流畅,这就是引入 Choreographer 的主要作用。这就涉及到了 Vsync 的请求逻辑,在连续的操作,比如动画、滑动、Fling 这些情况下,每一帧的 doFrame 的时候,都会根据情况触发下一个 Vsync 的申请,这样我们就可以获得连续的 Vsync 信号。

Gracker的专栏 637

基于SHIS凝视型短波红外高光谱相机(900-1700nm)区分不同宝石的差异性和可分性(宝石分选)

SHIS系列凝视型高光谱相机基于液晶可调谐滤波器(LCTF),通过电控LCTF进行连续细分窄波段分光,对每个波段进行高分辨率的二维面阵成像,进行图像处理后,获得完整的高光谱高清图像序列。具有低功耗、小型化、使用方便和易于扩展的优势。其工作波段覆盖400nm至1000nm,能够捕捉宝石在不同波长下的光谱特征,为宝石鉴定提供了新的工具。

wayho的博客 709

Android 性能优化【篇六:滑动卡帧分析流程】

本篇主要围绕Android性能优化,滑动卡帧这类问题为主题,先结网上已有的一些资料阐述滑动出帧流程,然后结合MTK的分析思路,其他厂商的思路,进行说明。首先需要注意一下,我对这块了解的其实不多,本篇属于单纯的摘抄、归纳、总结。PS:本人对这块不是很熟,所以本篇内容基本上都是摘抄各位大佬的资料在学习滑动卡帧类问题分析思路之前,需要先对Android Display显示模块的功能原理进行解读,我对这块不是很熟,这里主要引用整理网上的一些大神的资料。

qq_27672101的博客 331

NVIDIA DGX Spark实战:如何在桌面上轻松运行200B参数大模型?

本文详细介绍了如何利用NVIDIA DGX Spark在桌面端轻松运行200B参数大模型。通过实战演示,从系统初始化、模型获取部署到内存管理与性能调优,重点解析了其统一内存架构如何解决本地推理的显存瓶颈,并探讨了进行轻量化微调(如LoRA)的可能性,为AI开发者和研究团队提供了高效的本地化大模型操作方案。

weixin_29054195的博客 885

国腾|GM8775C替换TC358775/MIPI转双通道LVDS

1 产品概述 GM8775C 型DSI 转双通道 LVDS 发送器产品主要实现将MIPI DSI 转单/双通道 LVDS 功能,MIPI 支持 1/2/3/4 通道可选,最大支持 4Gbps 速率。LVDS时钟频率最高154MHz,最大支持视频格式为 FULL HD(1920 x 1200)。 该芯片主要应用于手持设备、双屏显示,大屏幕显示等应用需求。 2 产品特征 a) I/0电源电压:1.8V /3.3V; b) core 电源电压...

Vasse的博客 1073

【转】自上而下解读Android显示流程

假如没有这样一个模块,所有合成任务都将有GPU完成,则会带来性能与功耗的负担,GPU主要用于绘制,参与合成则会造成资源的浪费,特别是在10年前,GPU参与合成,性能会急剧下降——对于如今来说,并没有那么大影响了,基本顺手捎带着就完成了,如今的考虑主要在于功耗,GPU合成的效率太低了。这里的原则也是减小带宽。这是Android发展到2.0之后才出现的概念,算是SF的附属子模块,之前是运行在SF主线程的,如今改成单独的Service,这是Android的设计理念,效率其实是下降了,两个进程的交互是相当频繁的。

fightingup 5300

Unity卡通渲染实战:原神PMX模型Blender转FBX全流程

卡通渲染是二次元游戏开发的核心技术之一,其本质在于对光照模型、顶点色控制与边缘光生成的精准建模。本文从基础概念切入,解析MMD PMX格式的数据结构原理,阐明顶点色(Vertex Color)与Toon Ramp贴图在风格化渲染中的技术价值;进而覆盖Blender到Unity的跨引擎资产流转关键路径,重点解决法线校准、骨骼命名适配、FBX导出参数配置等工业级断点问题;最终落地于Unity内置管线与URP双环境下的卡通Shader定制与Render Feature实现。内容聚焦原神风格角色资产重铸实践,涵盖P

weixin_38166557的博客 437

Android 14 - 绘制体系 - 概览

从Android 12开始,Android的绘制系统有结构性变化, 在绘制的生产消费者模式中,新增BLASTBufferQueue,客户端进程自行进行queue的生产和消费,随后通过Transation提交到SurfaceFlinger,如此可以使得各进程将缓存提交到SufrfaceFlinger后合并到同一事务后同步提交,在同一帧生效。实际上,从Android12到Android14整个绘制系统在各个环节也都有了或大或小的。

temp7695的博客 2340

深入掌握英飞凌TriCore中断向量表

摘要:本文探讨了英飞凌Tc277芯片中断机制的原理与实现,重点解析了中断向量表基地址的链接文件定义方式、BIV寄存器的初始化过程以及每个中断条目的32字节空间分配规则。通过分析中断向量号(1-255)的计算方法,阐明了中断条目跳转地址(如STM0中断向量号37对应0x800F04A0)的生成逻辑。文章还揭示了.map文件中14字节跳转指令的组成原理,以及最终通过a14寄存器跳转到实际中断函数(如stm0Sr0ISR)的执行流程,为嵌入式开发人员理解中断处理机制提供了清晰的技术路径。

IT技术猿猴的博客 1713

OpenVPN 新增 windows 客户端

虚拟私人网络(Virtual Private Network,简称VPN)是一种网络技术,它允许用户通过公共网络(如互联网)建立一个安全的连接通道。本文介绍如何添加 windows 客户端。

利昂的技术存档 3769

Android图像生产消费模型

之后生产者开始绘制。在生产消费模型中,生产者与消费者在接收到VSync信号时开始生产和消费,但由于生产速率与消费速率不同,因此当下一次VSync信号到来时,生产与消费过程不一定都能完成,这时会通过Fence机制来保障生产消费过程的同步。在Android 12之后,生产消费模型由BLASTBufferQueue进行管理,并且将生产消费模型从SurfaceFlinger服务端移动了客户端。1)减轻了SurfaceFlinger的压力,SurfaceFlinger不再需要管理每个Layer中的生产消费模型。

LeeDuoZuiShuai的博客 1731

基于Python实现学习进度跟踪系统

{{ task }}: {{ "%.1f"|format(hours) }}小时

寒寒豆豆软件公司的博客 195

SurfaceFlinger BlastBufferQueue生产者消费者模型(4)

BlastBufferQueue是一个消费者生产者的模型,他们消费生产的对象是GraphicBuffer,GraphicBuffer其实就是一块内存,只不过是一块比较特殊的内存,使用的是dma buffer,相比于一般的内存,它是一块更高效的跨进程共享内存,后面会专门有一篇来介绍它。在Android 12开始,无论是这个模型的消费者还是生产者都在app侧,应用在渲染前,去队列里拿一块buffer,然后进行渲染,后放回队列,并且通知消费者可以取buffer消费,就是让SurfaceFlinger用于合成。

起司锅仔的博客 2080

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

SurfaceFlinger BufferQueue(二) BufferQueue初始化

上一篇文章讲到App在ViewRootImpl的draw流程中,而上面提到的surface的mGraphicBufferProducer是ViewRootImpl的relayoutWindow中,创建了BLASTBufferQueue,然后创建surface时传进去的。那么接下来我们开始分析BLASTBufferQueue。

Rainman博的专栏 869

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

BLASTBufferQueue端的GraphicBuffer怎么和SurfaceFlinger端的BufferStateLayer关联上

在跟进hwc_layer是如何产生的,并如何被填充上GraphicBuffer的,怎么也找不到被填充的GraphicBuffer的来源,最终找到了,它的来源是客户端的BLASTBufferQueue传递过来的,我们这里跟进下这个流程。一. BLASTBufferQueue是如何传递GraphicBuffer到SurfaceFlinger端的BufferStateLayer的。我们接着继续往下看,layer->setBuffer会调用子类的setBuffer,子类是BufferStateLayer。

IT先森 2248

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

Android 12 BLASTBufferQueue 深度分析

Android 12推出的BLASTBufferQueue重构了传统图形渲染架构,将BufferQueue管理权从SurfaceFlinger(SF)下移到应用进程。这一改进解决了多窗口和高刷新率场景下的三大痛点:减少90%以上的IPC通信开销,降低SF负载,提升应用对缓冲区的自主管理能力。核心设计是应用进程直接管理BufferQueue,SF仅作为轻量级消费者处理合成,同时保持Gralloc模块的实际内存分配机制。该架构使120Hz场景下渲染延迟降低1-3ms,SF负载减少30%,并支持应用根据需求灵活配

weixin_42065195的博客 958

图像显示框架十三——BufferQueue的工作流程(基于Android 15源码分析)

指出了是一个“状态更新+条件触发”的过程,其核心是更新中的状态机。​​真正意义上的释放​​ 是指缓冲区​​从ACQUIRED状态成功迁移到FREE状态​​,并被加入到空闲缓冲区集合中。随后通过回调通知生产者,从而完成一个“释放-申请-使用”的完整循环。

龚礼鹏的博客 1197

Android T systemTrace 说明

BLASTBufferQueue

wangcheng74520的专栏 2132

Android14 SurfaceFlinger Surface的创建

ViewRootImpl通过WMS创建surfaceControl然后通过surfaceControl创建BLASTBufferQueue让后通过BLASTBufferQueue.createSurface生成一个BBQSurface, 继承与Surface, 也就是创建了Surface, 最后返回给mSurface.BLASTBufferQueue是连接生产者和消费者的纽带。

u012627628的博客 1858
上一篇: Hal深入实战/perfetto-systrace实战/SurfaceFlinger合集-安卓framework开发实战开发
下一篇: 千里马2023年终总结-android framework实战
千里马学框架
博客等级 码龄6年 1万+粉丝 650原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

千里马学框架

帮助你了,就请我喝杯咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值