精华内容
下载资源
问答
  • php中ob(Output Buffer 输出缓冲)函数使用方法
  • buffer函数

    千次阅读 2013-07-21 20:02:51
    #include #include // for placement new const int BUF =512; ...char buffer[BUF]; // chunk of memory int main () {using namespace std; double *pdl ,*pd2; int i; cout pdl =new
    #include <iostream>
    
    #include <new>   // for placement new
    const int BUF =512;
    const int N =5;
    char buffer[BUF]; // chunk of memory
    int main ()
    {using namespace std;
    double *pdl ,*pd2;
    int i;
    cout << "Calling new and placement new:\n";
    pdl =new double[N]; //use heap
    pd2 =new (buffer) double[N]; // use buffer array
    for (i=0; i < N; i++)
    pd2[i]=pdl[i]=1000 + 20.0 * i;
    cout << "Buffer addresses:\n "<< " heap: " << pdl
    <<" static: " << (void *) buffer <<endl;
    cout << "Buffer contents:\n";
    for (i=0; i < N; i++)
    {cout << pdl[i] << " at " << &pdl[i] << "; ";
    cout << pd2[i] << " at " << &pd2[i] << endl;
    }
    cout << "\nCal1ing new and placement new a second time:\n";
    double *pd3, *pd4;
    pd3= new double[N];
    pd4 =new (buffer) double[N];
    for (i=0; i < N; i++)
    pd4[i] = pd3[i] =1000 + 20.0*i;
    cout << "Buffer contents:\n" ;
    for (i=0; i < N; i++)
    {cout << pd3[i] << " at " << &pd3[i] << "; ";
    cout << pd4[i] << " at " <<&pd4[i] << endl;
    }
    cout << "\nCalling new and placement new a third time:\n" ;
    delete [] pdl;
    pdl= new double[N];
    pd2 = new (buffer + N*sizeof(double)) double[N];
    for (i= 0; i< N; i++)
    pd2[i] = pdl[i]=1000 + 20.0*i;
    cout << "Buffer contents:\n";
    for (i =0; i < N; i++)
    {cout << pdl[i] << "at "<< &pdl[i] << "; ";
    cout << pd2[i] << " at " << &pd2[i] << endl;
    }
    delete [] pdl;
    delete [] pd3;
    return 0;
    }
    展开全文
  • 本篇接着分析queueBuffer函数,当我们需要绘制图像时,调用dequeueBuffer函数获取到可用的GraphicBuffer之后就可以开始绘制了,最常见的绘制操作就是Android上层View的draw方法了,当对GraphicBuffer的绘制操作完成...

    上一篇文章分析了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。

    展开全文
  • geotools中的buffer函数中所需缓冲区半径参数单位为度,而实际应用我们希望用米,故需要有米到度的转换转换公式如下: degree = meter / (2 * Math.PI * 6371004) * 360 ...

    geotools中的buffer函数中所需缓冲区半径参数单位为度,而实际应用我们希望用米,故需要有米到度的转换转换公式如下:

    degree = meter / (2 * Math.PI * 6371004) * 360

    展开全文
  • postgresql中ST_Buffer、ST_DWithin函数用法

    万次阅读 2017-05-07 11:15:37
    ST_Buffer:用于矢量对象生成缓冲区geometry对象,可用于缓冲区对象的显示,使用举例(其中bufferColumn字段是geometry类型): update tableName set bufferColumn=ST_Buffer( ST_GeomFromText( 'LINESTRING(50 ...

    ST_Buffer:用于矢量对象生成缓冲区geometry对象,可用于缓冲区对象的显示,使用举例(其中bufferColumn字段是geometry类型):

    update tableName set bufferColumn=ST_Buffer( ST_GeomFromText( 'LINESTRING(50 50,150 150,150 50)'), 10, 'endcap=round join=round');

    这是其中一种类型的缓冲区用法,其他用法参考:http://postgis.net/docs/ST_Buffer.html

    ST_DWithin:检测一个对象是否在另一个对象的缓冲区范围内,不生成缓冲区对象,效率更高,速度更快。使用举例:

    查询tableName表中存在于objectid=100的对象10米缓冲区内的所有对象,其中geom为geometry字段:

    select * from tableName where ST_DWithin(geom, (select geom from tableNamewhere objectid=100), 10)=true

    其他用法参考:http://postgis.net/docs/ST_DWithin.html


    展开全文
  • Buffer的构造函数

    2020-03-01 16:21:20
    缓冲区是在内容中操作数据的容器,Node.js 中的Buffer缓冲区模块,支持开发者在缓冲区结构中创建、读取、 写入和操作二进制数据,该模块是全局性的,所以在使用时不需要使用require()函数来加载。 在Node.js手册中...
  • sort函数用法

    万次阅读 多人点赞 2018-04-10 09:30:54
    sort函数是一个非常强大的排序函数用法如下://使用sort函数进行升序排序#include &lt;stdio.h&gt;#include &lt;algorithm&gt;//sort函数所在的函数库using namespace std;//sort所在的标准命名空间...
  • memset函数使用方法

    千次阅读 2019-01-25 23:03:22
    memset()函数原型是extern void *memset(void *buffer, int c, int count) buffer:为指针或是数组, c:是赋给buffer的值, count:是buffer的长度. 这个函数在socket中多用于清空数组.如:原型是memset(buffer, 0, ...
  • 本文实例讲述了C++中memset函数用法。分享给大家供大家参考,具体如下: 功 能: 将s所指向的某一块内存中的每个字节的内容全部设置为ch指定的ASCII值,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化...
  • sprintf函数用法详解

    万次阅读 多人点赞 2017-07-14 09:06:41
    sprintf() 格式化输出函数(图形) 功能: 函数sprintf()用来作格式化的...说 明: 函数sprintf()的用法和printf()函数一样,只是sprintf()函数给出第一个参数string(一般为字符数组),然后再调用 outtextxy()函数
  • 方法说明: 获取字符串的字节长度。 这个函数与 String.prototype.length 不同点在于,后者返回的是字符串的字符数。 语法: 代码如下: Buffer.byteLength(string, [encoding]) 接收参数: string 字符创 ...
  • gets函数用法

    千次阅读 2019-01-07 08:44:39
    gets是从标准输入设备读字符串函数函数原型:char * gets ( char * str ); 功能为:从stdin流中读取字符串,直至接受到换行符或EOF时停止,并将读取的结果存放在buffer指针所指向的字符数组中。换行符不作为读取...
  • Buffer() is deprecated due to security and usability issues. Please use the Buffer.alloc(), Buffer.allocUnsafe(), or Buffer.from() methods instead. NodeJS运行下列代码时报错 let streamHeader = new ...
  • assert()函数用法总结

    万次阅读 多人点赞 2017-04-21 11:05:35
    在调试结束后,可以通过在包含#include 的语句之前插入 #define NDEBUG 来禁用assert调用,示例代码如下: #include #define NDEBUG #include 用法总结与注意事项: 1)在函数开始处检验传入参数的合法性如: int ...
  • string函数用法

    2010-04-28 21:31:00
    string函数用法@函数名称: strdup 函数原型: char *strdup(const char *s) 函数功能: 字符串拷贝,目的空间由该函数分配 函数返回: 指向拷贝后的字符串指针 参数说明: src-待拷贝的源字符串 所属文件: #...
  • memset 函数用法

    千次阅读 2010-02-01 00:31:00
    用法:#include 功能:把buffer所指内存区域的前count个字节设置成字符c。说明:返回指向buffer的指针。用来对一段内存空间全部设置为某个字符。举例:char a[100];memset(a, /0, sizeof(a));memset可以方便的清
  • 主要介绍了node.js中的buffer.copy方法使用说明,本文介绍了buffer.copy的方法说明、语法、接收参数、使用实例和实现源码,需要的朋友可以参考下
  • 关于OpenCV中常见函数用法总结

    千次阅读 2017-03-14 18:30:22
    关于OpenCV中常见函数用法总结 一 一般Mat的赋值操作 二 求Mat中的最大值以及最小值 三 randn()函数给图像添加高斯噪声 四 mean()函数的用法 五 系统计时器 六 矩阵之间的四则运算:gemm()函数 七 利用OpenCV如何实现...
  • Protocol Buffer Repeated 对应类型为 ::google::protobuf::RepeatedPtrField 如果想要遍历该类型,可以使用 iterator typedef ::google::protobuf::RepeatedPtrField<Person> ptrf; // classmates is ...
  • fgets函数用法

    千次阅读 2018-04-09 12:41:00
    1、头文件 #include &lt;stdio.h&gt;2、原型:char *fgets(char *s, int ... 描述:1)fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to...
  • glutSwapBuffers函数用法

    万次阅读 2015-04-23 16:20:27
    文章来源:...glutSwapBuffers函数是OpenGL中GLUT工具包中用于实现双缓冲技术的一个重要函数。该函数的功能是交换两
  • getcwd()函数用法

    千次阅读 2020-03-09 21:16:55
    Linux 中C语言getcwd()函数用法 先来看该函数的声明: #include<unistd.h> char *getcwd(char *buf,size_t size); 参数说明:getcwd()会将当前工作目录的绝对路径复制到参数buffer所指的内存空间中,参数...
  • 简直就是弱爆了,原来所谓的buffer是抽象的呀 buffer就是系统分配给用户的一块数据存储单元,和变量其实是一样的呀,omyga!... 如果nMinBufLength 比当前buffer大,那么就调用ReleaseBuffer函数
  • fscanf函数使用方法

    千次阅读 2011-08-16 17:44:31
    fscan()函数用于从文件中格式化输入, 把获取到的每一行用空白符分隔(任何空白符,如\t, \n, \r, \f, \v等)成数组,并且返回,如: 文件t1.txt的内容为:renren kaixin qq sina,sohu 代码: $fp = fope
  • C++ memset函数用法

    2018-03-20 09:37:30
    #include&lt;stdio.h&gt;#include&lt;string.h&gt;int main(){ char buffer[] = "I love you!...,buffer); memset(buffer,'*',strlen(buffer)); printf("%s\n",buffer);}
  • sprintf_s函数用法

    万次阅读 2017-10-26 16:08:57
    sprintf_s是一个函数,其函数功能是将数据格式化输出到字符串。sprintf_s对于格式化string中的格式化的字符的有效性进行了检查,sprintf_s也携带着接收格式化字符串的缓冲区的大小。 sprintf_s将格式化字符串存到...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 250,800
精华内容 100,320
关键字:

buffer函数的用法