AndroidQ 图形系统(4)queueBuffer函数分析

上一篇文章分析了dequeueBuffer函数的过程,本篇接着分析queueBuffer函数,当我们需要绘制图像时,调用dequeueBuffer函数获取到可用的GraphicBuffer之后就可以开始绘制了,最常见的绘制操作就是Android上层View的draw方法了,其他还有OpenGL ES、 mediaserver 视频解码器都可以作为图形数据的来源。

当对GraphicBuffer的绘制操作完成之后就需要调用queueBuffer函数将这块buffer放入BufferQueue队列中并通过回调通知消费者使用这块buffer,图形数据最常见的消耗方就是SurfaceFlinger,该系统服务会消耗当前可见的 Surface(核心是GraphicBuffer),合成到显示部分。
具体实的合成流程是SurfaceFlinger使用 OpenGL和Hardware Composer来合成一组 Surface。
其他 OpenGL ES 应用也可以消耗图像流,例如相机应用会消耗相机预览图像流。非 GL 应用也可以是使用方,例如 ImageReader 类。(引自Android官网)。

我们从Surface.cppqueueBuffer函数开始看,这个函数比较长我们分为三部分来看。

Surface::queueBuffer

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
	......
    int i = getSlotFromBufferLocked(buffer);
    if (i < 0) {
        if (fenceFd >= 0) {
            close(fenceFd);
        }
        return i;
    }
    if (mSharedBufferSlot == i && mSharedBufferHasBeenQueued) {
        if (fenceFd >= 0) {
            close(fenceFd);
        }
        return OK;
    }


    // Make sure the crop rectangle is entirely inside the buffer.
    Rect crop(Rect::EMPTY_RECT);
    mCrop.intersect(Rect(buffer->width, buffer->height), &crop);

    sp<Fence> fence(fenceFd >= 0 ? new Fence(fenceFd) : Fence::NO_FENCE);
    IGraphicBufferProducer::QueueBufferOutput output;
    IGraphicBufferProducer::QueueBufferInput input(timestamp, isAutoTimestamp,
            static_cast<android_dataspace>(mDataSpace), crop, mScalingMode,
            mTransform ^ mStickyTransform, fence, mStickyTransform,
            mEnableFrameTimestamps);

    // we should send HDR metadata as needed if this becomes a bottleneck
    input.setHdrMetadata(mHdrMetadata);
    ALOGE("dongjiao...mConnectedToCpu = :%d,INVALID_RECT = :%d",mConnectedToCpu,(mDirtyRegion.bounds() == Rect::INVALID_RECT));
    if (mConnectedToCpu || mDirtyRegion.bounds() == Rect::INVALID_RECT) {
        input.setSurfaceDamage(Region::INVALID_REGION);
    } else {
        // Here we do two things:
        // 1) The surface damage was specified using the OpenGL ES convention of
        //    the origin being in the bottom-left corner. Here we flip to the
        //    convention that the rest of the system uses (top-left corner) by
        //    subtracting all top/bottom coordinates from the buffer height.
        // 2) If the buffer is coming in rotated (for example, because the EGL
        //    implementation is reacting to the transform hint coming back from
        //    SurfaceFlinger), the surface damage needs to be rotated the
        //    opposite direction, since it was generated assuming an unrotated
        //    buffer (the app doesn't know that the EGL implementation is
        //    reacting to the transform hint behind its back). The
        //    transformations in the switch statement below apply those
        //    complementary rotations (e.g., if 90 degrees, rotate 270 degrees).

        int width = buffer->width;
        int height = buffer->height;
        bool rotated90 = (mTransform ^ mStickyTransform) &
                NATIVE_WINDOW_TRANSFORM_ROT_90;
        if (rotated90) {
            std::swap(width, height);
        }

        Region flippedRegion;
        for (auto rect : mDirtyRegion) {
            int left = rect.left;
            int right = rect.right;
            int top = height - rect.bottom; // Flip from OpenGL convention
            int bottom = height - rect.top; // Flip from OpenGL convention
            switch (mTransform ^ mStickyTransform) {
                case NATIVE_WINDOW_TRANSFORM_ROT_90: {
                    // Rotate 270 degrees
                    Rect flippedRect{top, width - right, bottom, width - left};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
                case NATIVE_WINDOW_TRANSFORM_ROT_180: {
                    // Rotate 180 degrees
                    Rect flippedRect{width - right, height - bottom,
                            width - left, height - top};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
                case NATIVE_WINDOW_TRANSFORM_ROT_270: {
                    // Rotate 90 degrees
                    Rect flippedRect{height - bottom, left,
                            height - top, right};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
                default: {
                    Rect flippedRect{left, top, right, bottom};
                    flippedRegion.orSelf(flippedRect);
                    break;
                }
            }
        }

        input.setSurfaceDamage(flippedRegion);
    }
    .....
  }

首先第一部分,queueBuffer的第一个参数buffer就是当前绘制完成的GraphicBuffer,第二个参数fenceFd是一种用作同步的工具。

  1. 首先来看函数getSlotFromBufferLocked
int Surface::getSlotFromBufferLocked(
        android_native_buffer_t* buffer) const {
    for (int i = 0; i < NUM_BUFFER_SLOTS; i++) {
        if (mSlots[i].buffer != nullptr &&
                mSlots[i].buffer->handle == buffer->handle) {
            return i;
        }
    }
    ALOGE("getSlotFromBufferLocked: unknown buffer: %p", buffer->handle);
    return BAD_VALUE;
}

这个函数很简单,就是找到buffer在mSlots中的下标,mSlots为所有BufferSlot集合,得到下标之后进行一些合法性判断,如果是mSharedBufferSlot并且mSharedBufferHasBeenQueued代码当前使用的是共享buffer模式则直接返回OK,不太了解共享buffer模式,就跳过了。

  1. 接着定义一个空的Rect crop矩形,mCrop也是一个Rect对象,用于buffer的裁剪,我们看它的intersect函数:
bool Rect::intersect(const Rect& with, Rect* result) const {
    result->left = max(left, with.left);
    result->top = max(top, with.top);
    result->right = min(right, with.right);
    result->bottom = min(bottom, with.bottom);
    return !(result->isEmpty());
}

这个函数用于获取两个矩形相交的部分,并将相交部分赋值给result,再看看调用intersect传递的参数:
mCrop.intersect(Rect(buffer->width, buffer->height), &crop);
其实就是将mCropRect(buffer->width, buffer->height)相交部分赋值给crop

  1. 接着创建了两个QueueBufferOutput对象,input作为输入参数传递到BufferQueueProducerinput里面封装的这些信息很多都是外部应用程序设置的,如mDataSpacemScalingModemStickyTransform这些都会应用到最终的显示,而output会作为BufferQueueProducer的返回值。

  2. 接着有一个判断,mConnectedToCpu的含义是:CPU生产者(在CPU上运行并生成图形数据的代码,如上层的2Dcanvas绘图)连接到BufferQueue,可以通过函数native_window_api_connect传入参数NATIVE_WINDOW_API_CPU设置CPU生产者,但现在所用的图形数据更多来自OpenGL ES、 mediaserver 视频解码器和camera API,对应连接的API为NATIVE_WINDOW_API_EGLNATIVE_WINDOW_API_MEDIA
    NATIVE_WINDOW_API_CAMERA,所以mConnectedToCpu大多数为false。

mDirtyRegion:脏区域,需要更新图形数据的区域,如果没有需要更新的脏区域则设置无效脏区域。
否则就根据buffer宽高以及旋转角度按一定规则创建flippedRegion,并设置给input传递到BufferQueueProducer

setSurfaceDamage设置脏区域,对于非脏区域,会直接从之前的buffer中复制,仅仅更新指定的脏区域部分,提高效率。

我们可以看到所有的图形数据都被封装在了input中。

Surface::queueBuffer第一部分分析完了,接着看第二部分:

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
	......
		status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
	......
	 return err; 
	}

这部分就是本篇的核心,调用生产者queueBuffer函数,传递的依然是BufferSlot下标,到了BufferQueueProducer中通过下标就能找到绑定的GraphicBuffer

BufferQueueProducer::queueBuffer

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
    ATRACE_CALL();
    ATRACE_BUFFER_INDEX(slot);

    int64_t requestedPresentTimestamp;
    bool isAutoTimestamp;
    android_dataspace dataSpace;
    Rect crop(Rect::EMPTY_RECT);
    int scalingMode;
    uint32_t transform;
    uint32_t stickyTransform;
    sp<Fence> acquireFence;
    bool getFrameTimestamps = false;
    input.deflate(&requestedPresentTimestamp, &isAutoTimestamp, &dataSpace,
            &crop, &scalingMode, &transform, &acquireFence, &stickyTransform,
            &getFrameTimestamps);
    const Region& surfaceDamage = input.getSurfaceDamage();
    const HdrMetadata& hdrMetadata = input.getHdrMetadata();

    if (acquireFence == nullptr) {
        BQ_LOGE("queueBuffer: fence is NULL");
        return BAD_VALUE;
    }

    auto acquireFenceTime = std::make_shared<FenceTime>(acquireFence);

    switch (scalingMode) {
        case NATIVE_WINDOW_SCALING_MODE_FREEZE:
        case NATIVE_WINDOW_SCALING_MODE_SCALE_TO_WINDOW:
        case NATIVE_WINDOW_SCALING_MODE_SCALE_CROP:
        case NATIVE_WINDOW_SCALING_MODE_NO_SCALE_CROP:
            break;
        default:
            BQ_LOGE("queueBuffer: unknown scaling mode %d", scalingMode);
            return BAD_VALUE;
    }

    sp<IConsumerListener> frameAvailableListener;
    sp<IConsumerListener> frameReplacedListener;
    int callbackTicket = 0;
    uint64_t currentFrameNumber = 0;
    BufferItem item;
    { // Autolock scope
        std::lock_guard<std::mutex> lock(mCore->mMutex);

        if (mCore->mIsAbandoned) {
            BQ_LOGE("queueBuffer: BufferQueue has been abandoned");
            return NO_INIT;
        }

        if (mCore->mConnectedApi == BufferQueueCore::NO_CONNECTED_API) {
            BQ_LOGE("queueBuffer: BufferQueue has no connected producer");
            return NO_INIT;
        }

        if (slot < 0 || slot >= BufferQueueDefs::NUM_BUFFER_SLOTS) {
            BQ_LOGE("queueBuffer: slot index %d out of range [0, %d)",
                    slot, BufferQueueDefs::NUM_BUFFER_SLOTS);
            return BAD_VALUE;
        } else if (!mSlots[slot].mBufferState.isDequeued()) {
            BQ_LOGE("queueBuffer: slot %d is not owned by the producer "
                    "(state = %s)", slot, mSlots[slot].mBufferState.string());
            return BAD_VALUE;
        } else if (!mSlots[slot].mRequestBufferCalled) {
            BQ_LOGE("queueBuffer: slot %d was queued without requesting "
                    "a buffer", slot);
            return BAD_VALUE;
        }

        // If shared buffer mode has just been enabled, cache the slot of the
        // first buffer that is queued and mark it as the shared buffer.
        if (mCore->mSharedBufferMode && mCore->mSharedBufferSlot ==
                BufferQueueCore::INVALID_BUFFER_SLOT) {
            mCore->mSharedBufferSlot = slot;
            mSlots[slot].mBufferState.mShared = true;
        }

        BQ_LOGV("queueBuffer: slot=%d/%" PRIu64 " time=%" PRIu64 " dataSpace=%d"
                " validHdrMetadataTypes=0x%x crop=[%d,%d,%d,%d] transform=%#x scale=%s",
                slot, mCore->mFrameCounter + 1, requestedPresentTimestamp, dataSpace,
                hdrMetadata.validTypes, crop.left, crop.top, crop.right, crop.bottom,
                transform,
                BufferItem::scalingModeName(static_cast<uint32_t>(scalingMode)));

        const sp<GraphicBuffer>& graphicBuffer(mSlots[slot].mGraphicBuffer);
        Rect bufferRect(graphicBuffer->getWidth(), graphicBuffer->getHeight());
        Rect croppedRect(Rect::EMPTY_RECT);
        crop.intersect(bufferRect, &croppedRect);
        if (croppedRect != crop) {
            BQ_LOGE("queueBuffer: crop rect is not contained within the "
                    "buffer in slot %d", slot);
            return BAD_VALUE;
        }

        // Override UNKNOWN dataspace with consumer default
        if (dataSpace == HAL_DATASPACE_UNKNOWN) {
            dataSpace = mCore->mDefaultBufferDataSpace;
        }

        mSlots[slot].mFence = acquireFence;
        mSlots[slot].mBufferState.queue();

        // Increment the frame counter and store a local version of it
        // for use outside the lock on mCore->mMutex.
        ++mCore->mFrameCounter;
        currentFrameNumber = mCore->mFrameCounter;
        mSlots[slot].mFrameNumber = currentFrameNumber;

        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;

        mStickyTransform = stickyTransform;

        // Cache the shared buffer data so that the BufferItem can be recreated.
        if (mCore->mSharedBufferMode) {
            mCore->mSharedBufferCache.crop = crop;
            mCore->mSharedBufferCache.transform = transform;
            mCore->mSharedBufferCache.scalingMode = static_cast<uint32_t>(
                    scalingMode);
            mCore->mSharedBufferCache.dataspace = dataSpace;
        }
        output->bufferReplaced = false;
        if (mCore->mQueue.empty()) {
            // When the queue is empty, we can ignore mDequeueBufferCannotBlock
            // and simply queue this buffer
            mCore->mQueue.push_back(item);
            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);
            if (last.mIsDroppable) {

                if (!last.mIsStale) {
                    mSlots[last.mSlot].mBufferState.freeQueued();

                    // After leaving shared buffer mode, the shared buffer will
                    // still be around. Mark it as no longer shared if this
                    // operation causes it to be free.
                    if (!mCore->mSharedBufferMode &&
                            mSlots[last.mSlot].mBufferState.isFree()) {
                        mSlots[last.mSlot].mBufferState.mShared = false;
                    }
                    // Don't put the shared buffer on the free list.
                    if (!mSlots[last.mSlot].mBufferState.isShared()) {
                        mCore->mActiveBuffers.erase(last.mSlot);
                        mCore->mFreeBuffers.push_back(last.mSlot);
                        output->bufferReplaced = true;
                    }
                }

                // Make sure to merge the damage rect from the frame we're about
                // to drop into the new frame's damage rect.
                if (last.mSurfaceDamage.bounds() == Rect::INVALID_RECT ||
                    item.mSurfaceDamage.bounds() == Rect::INVALID_RECT) {
                    item.mSurfaceDamage = Region::INVALID_REGION;
                } else {
                    item.mSurfaceDamage |= last.mSurfaceDamage;
                }

                // Overwrite the droppable buffer with the incoming one
                mCore->mQueue.editItemAt(mCore->mQueue.size() - 1) = item;
                frameReplacedListener = mCore->mConsumerListener;
            } else {
                mCore->mQueue.push_back(item);
                frameAvailableListener = mCore->mConsumerListener;
            }
        }
        ......
        }

这个函数也比较多,同样分部分来看,首先看第一部分:

  1. 定义了一堆变量,用来保存Surface传递过来的input里面封装的buffer信息。
  2. 接着需要检测scalingMode,缩放模式,当buffer内容和屏幕不成比例时采用什么方式处理,scalingMode可以通过函数native_window_set_scaling_mode进行设置,如果没有设置直接返回BAD_VALUE
  3. 又进行一堆检测: BufferQueue是否被弃用,buffer是否连接到BufferQueueBufferSlot下标是否合法,当前这个BufferSlot状态是否是DEQUEUEmRequestBufferCalled是否为true(即此BufferSlot是否调用了requestBuffer函数)。
  4. 接着又创建了三个对象,graphicBuffer(此次queue的具体buffer),bufferRect(根据当前graphicBuffer创建的矩形区域),croppedRect(裁剪区域,值为crop和bufferRect相交部分,其实这里的crop.intersect(bufferRect, &croppedRect)和前面Surface里面mCrop.intersect(Rect(buffer->width, buffer->height), &crop)会得到同样的裁剪区域,只要裁剪的部分包含在当前buffer之内)。
  5. 接着将当前BufferSlot状态修改为QUEUE,并修改一些状态值。
  6. BufferSlot以及GraphicBuffer的信息进一步封装到BufferItem,而BufferItem又存储在BufferQueueCoremQueue中,所以一般我们说的生产者-消费者模型中的buffer队列,更具体的话就是mQueue。如果mQueue为空就直接将BufferItem添加进去,并将BufferQueueCoremConsumerListener给到frameAvailableListener,从AndroidQ 图形系统(2)生产者-消费者模型知道,mConsumerListener是在ConsumerBase的构造函数中传递的BufferQueue::ProxyConsumerListener
    对于mQueue非空的情况,首先取到mQueue尾部的BufferItem,根据mIsDroppable值查看是否需要丢弃,什么情况下需要丢弃?有很多种情况,常见就是同步模式即mAsyncMode为true,至于需要丢弃的情况怎么处理的我这里不去看了,对于不需要丢弃的情况总是将BufferItem放入mQueue的尾部。
    此部分已经分析完了,主要做的事情就是上面这六点。

接着看剩下的部分:

status_t BufferQueueProducer::queueBuffer(int slot,
        const QueueBufferInput &input, QueueBufferOutput *output) {
	......
	    mCore->mBufferHasBeenQueued = true;
        mCore->mDequeueCondition.notify_all();
        mCore->mLastQueuedSlot = slot;

        output->width = mCore->mDefaultWidth;
        output->height = mCore->mDefaultHeight;
        output->transformHint = mCore->mTransformHint;
        output->numPendingBuffers = static_cast<uint32_t>(mCore->mQueue.size());
        output->nextFrameNumber = mCore->mFrameCounter + 1;

        ATRACE_INT(mCore->mConsumerName.string(),
                static_cast<int32_t>(mCore->mQueue.size()));
        mCore->mOccupancyTracker.registerOccupancyChange(mCore->mQueue.size());

        // Take a ticket for the callback functions
        callbackTicket = mNextCallbackTicket++;

        VALIDATE_CONSISTENCY();
    } // Autolock scope

    // It is okay not to clear the GraphicBuffer when the consumer is SurfaceFlinger because
    // it is guaranteed that the BufferQueue is inside SurfaceFlinger's process and
    // there will be no Binder call
    if (!mConsumerIsSurfaceFlinger) {
        item.mGraphicBuffer.clear();
    }

    // Call back without the main BufferQueue lock held, but with the callback
    // lock held so we can ensure that callbacks occur in order

    int connectedApi;
    sp<Fence> lastQueuedFence;

    { // scope for the lock
        std::unique_lock<std::mutex> lock(mCallbackMutex);
        while (callbackTicket != mCurrentCallbackTicket) {
            mCallbackCondition.wait(lock);
        }

        if (frameAvailableListener != nullptr) {
            frameAvailableListener->onFrameAvailable(item);
        } else if (frameReplacedListener != nullptr) {
            frameReplacedListener->onFrameReplaced(item);
        }

        connectedApi = mCore->mConnectedApi;
        lastQueuedFence = std::move(mLastQueueBufferFence);

        mLastQueueBufferFence = std::move(acquireFence);
        mLastQueuedCrop = item.mCrop;
        mLastQueuedTransform = item.mTransform;

        ++mCurrentCallbackTicket;
        mCallbackCondition.notify_all();
    }

    // Update and get FrameEventHistory.
    nsecs_t postedTime = systemTime(SYSTEM_TIME_MONOTONIC);
    NewFrameEventsEntry newFrameEventsEntry = {
        currentFrameNumber,
        postedTime,
        requestedPresentTimestamp,
        std::move(acquireFenceTime)
    };
    addAndGetFrameTimestamps(&newFrameEventsEntry,
            getFrameTimestamps ? &output->frameTimestamps : nullptr);

    // Wait without lock held
    if (connectedApi == NATIVE_WINDOW_API_EGL) {
        // Waiting here allows for two full buffers to be queued but not a
        // third. In the event that frames take varying time, this makes a
        // small trade-off in favor of latency rather than throughput.
        lastQueuedFence->waitForever("Throttling EGL Production");
    }
    return NO_ERROR;
}

这部分主要做了如下事情:

  1. mBufferHasBeenQueued一个状态值,代表buffer已经queuedmDequeueCondition是C++条件变量用作等待/唤醒,这里调用notify_all会唤醒调用了wait的线程,即在dequeueBuffer函数中因为tooManyBuffers而陷入等待状态的线程,当一块buffer被queued之后就可以继续dequeue了,output最终会返回给Surface进程,mConsumerIsSurfaceFlinger表示消费者是否SurfaceFlinger进程,默认值为true。
  2. frameAvailableListener不为空则调用onFrameAvailable回调函数通知有buffer可以进行消费,这个函数会经过一系列调用,最终的实现类是BufferQueueLayer(一种最常用的Layer),最后如果连接BufferQueue的方式为NATIVE_WINDOW_API_EGL,会通过Fence陷入等待,这里会一直等,知道上一帧绘制完成,图形系统的生产消费非常依赖Fence同步机制,Android引入三重缓冲之后,GPU,CPU,显示硬件可以各持有一个buffer工作,例如当CPU持有的buffer数据已经处理完了,但此时GUP,显示硬件还在使用buffer,CPU并不能立马申请下一个buffer,需要等待,我这里对Fence不了解,所以文中出现的和Fence同步相关的代码就略过了。

到此queueBuffer函数已经分析完毕,代码看着多,逻辑比较简单,就是将Surface进程数据传过来,然后封装成BufferItem,放入BufferQueueCoremQueue中,再通过frameAvailableListener通知消费者去消费。

接着回到Surface看剩下的代码,

int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
		......
		
		if (err != OK)  {
        	ALOGE("queueBuffer: error queuing buffer to SurfaceTexture, %d", err);
   		 }

    if (mEnableFrameTimestamps) {
        mFrameEventHistory->applyDelta(output.frameTimestamps);
        // Update timestamps with the local acquire fence.
        // The consumer doesn't send it back to prevent us from having two
        // file descriptors of the same fence.
        mFrameEventHistory->updateAcquireFence(mNextFrameNumber,
                std::make_shared<FenceTime>(fence));

        // Cache timestamps of signaled fences so we can close their file
        // descriptors.
        mFrameEventHistory->updateSignalTimes();
    }

    mLastFrameNumber = mNextFrameNumber;

    mDefaultWidth = output.width;
    mDefaultHeight = output.height;
    mNextFrameNumber = output.nextFrameNumber;

    // Ignore transform hint if sticky transform is set or transform to display inverse flag is
    // set.
    if (mStickyTransform == 0 && !transformToDisplayInverse()) {
        mTransformHint = output.transformHint;
    }

    mConsumerRunningBehind = (output.numPendingBuffers >= 2);

    if (!mConnectedToCpu) {
        // Clear surface damage back to full-buffer
        mDirtyRegion = Region::INVALID_REGION;
    }

    if (mSharedBufferMode && mAutoRefresh && mSharedBufferSlot == i) {
        mSharedBufferHasBeenQueued = true;
    }

    mQueueBufferCondition.broadcast();

    if (CC_UNLIKELY(atrace_is_tag_enabled(ATRACE_TAG_GRAPHICS))) {
        static FenceMonitor gpuCompletionThread("GPU completion");
        gpuCompletionThread.queueFence(fence);
    }

    return err;

这后面部分其实没什么好说的了,会更新一些变量,清空脏区域,通过条件变量mQueueBufferCondition唤醒等待下一帧的线程,处理一下Fence。

  • 3
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值