[Android] Android SurfaceFlinger 의 BufferQueue

Android 에서 BufferQueue 는 Surface 가 로 컬 창 을 구현 하 는 관건 이 며 Surface Flinger 프로 세 스에 머 물 러 서 비 스 를 진행 합 니 다. 다음은 BufferQueue 의 구조 부터 분석 하 겠 습 니 다.
1. BufferQueue 모드
BufferQueue 와 관련 된 소스 코드 는 frameworks / native / libs / gui / 에 있 으 며 다음 과 같은 몇 가지 중요 한 데이터 구조 와 관련된다.
class BufferQueue;
class BufferQueueCore;
class BufferQueueProducer;
class BufferQueueConsumer;

BufferQueue 는 전형 적 인 생산자 - 소비자 모델 에 속 하고 buffer 상 태 를 나타 내 는 BufferState 도 있 는데 모두 다섯 가지 상태 로 각각 FREE, DEQUEUED, QUEUED, ACQUIRED 와 SHARED 이다.FREE 는 buffer 가 Producer 에 의 해 dequeue 를 진행 할 수 있 음 을 나타 낸다. 이때 buffer 는 BufferQueue 에 속 하고 dequeue buffer 일 때 상태 가 DEQUEUED 로 변 한다.DEQUEUED 는 buffer 가 Producer dequeued 에 의 해 변경 되 었 으 나 아직 queued 와 canceled 에 의 해 변경 되 지 않 았 음 을 표시 합 니 다. 관련 release fence 알림 을 받 았 을 때 Producer 는 buffer 의 데 이 터 를 수정 할 수 있 습 니 다. 이때 buffer 는 Producer 에 속 하고 quue 나 attach buffer 에 속 할 때 상 태 는 QUEUED 로 변 하고 cancel 또는 detach buffer 에 속 할 때 상 태 는 FREE 로 변 합 니 다.QUEUED 는 Producer 가 buffer 를 채 우 고 Consumer queued 에 의 해 buffer 데이터 가 수 정 될 수 있 음 을 나타 내 며 fence 신호 와 관련 이 있 습 니 다. 이때 buffer 는 BufferQueue 에 속 합 니 다. acquire buffer 일 때 상 태 는 ACQUIRED 로 변 하고 다른 비동기 buffer 가 QUEUED 로 변 할 때 이 buffer 는 FREE 로 변 합 니 다.ACQUIRED 는 buffer 가 Consumer 에 의 해 acquired 되 고 buffer 데이터 가 수 정 될 수 있 음 을 나타 내 며 fence 신호 와 관련 이 있 습 니 다. 이때 buffer 는 Consumer 에 속 하고 release 또는 detach buffer 일 때 상태 가 FREE 로 변 합 니 다. buffer datach 후에 attach 를 진행 하면 그 상 태 는 ACQUIRED 로 변 합 니 다.SHARED 는 buffer 를 공유 모드 로 표시 하 는 동시에 FREE 를 제외 한 모든 상태 일 수 있 으 며, 여러 번 dequeud, queued, acquired 를 받 을 수 있 습 니 다.
buffer 의 몇 가지 상태 에서 Producer 와 Consumer 와 관련 되 고 그들의 상호작용 은 반드시 BufferQueue 라 는 관리자 나 중 개 를 통 해 이 루어 져 야 한다.Producer 는 일반적으로 응용 프로그램 으로 buffer 에 데 이 터 를 계속 채 워 줍 니 다. 그러나 먼저 BufferQueue 에 dequeue 를 진행 하여 buffer 를 신청 해 야 합 니 다. 이때 buffer 는 모든 것 이 고 데 이 터 를 채 운 후에 BufferQueue 에 queue 를 하고 제어 권 을 BufferQueue 에 돌려 주어 야 합 니 다. 이런 작업 은 모두 Producer 가 주동 적 으로 시작 한 것 입 니 다.한편, Consumer 는 수 동적 입 니 다. buffer 데이터 가 준 비 된 후에 야 소 비 를 하 라 는 통 지 를 받 았 습 니 다. 이 통 지 는 BufferQueue 에 의 해 시 작 된 것 입 니 다. BufferQueue 는 서비스 기구 입 니 다. 그 내부 에 Proxy Consumer Listener 가 있 고 buffer 데이터 가 준비 되면 Consumer 에 게 소 비 를 하 라 고 알 립 니 다.
2. BufferQueue 버퍼 할당 과정
전체 적 으로 BufferQueue 인 스 턴 스 가 없습니다. private BufferQueue 구조 함수 만 밝 혔 을 뿐 실현 되 지 않 았 습 니 다. 사실 사용 할 수 없습니다. static 함수 createBufferQueue 를 통 해 각각 BufferQueue Core, BufferQueue Producer 와 BufferQueue Consumer 를 만 들 었 습 니 다. 그 중에서 Buffer Queue Core 는 매개 변수 로 BufferQueue Producer 와 BufferQueue Consumer 에 전 달 했 습 니 다. 다음 과 같 습 니 다.
void BufferQueue::createBufferQueue(sp<IGraphicBufferProducer>* outProducer,
        sp<IGraphicBufferConsumer>* outConsumer,
        const sp<IGraphicBufferAlloc>& allocator) {
    LOG_ALWAYS_FATAL_IF(outProducer == NULL,
            "BufferQueue: outProducer must not be NULL");
    LOG_ALWAYS_FATAL_IF(outConsumer == NULL,
            "BufferQueue: outConsumer must not be NULL");

    sp<BufferQueueCore> core(new BufferQueueCore(allocator));
    LOG_ALWAYS_FATAL_IF(core == NULL,
            "BufferQueue: failed to create BufferQueueCore");

    sp<IGraphicBufferProducer> producer(new BufferQueueProducer(core));
    LOG_ALWAYS_FATAL_IF(producer == NULL,
            "BufferQueue: failed to create BufferQueueProducer");

    sp<IGraphicBufferConsumer> consumer(new BufferQueueConsumer(core));
    LOG_ALWAYS_FATAL_IF(consumer == NULL,
            "BufferQueue: failed to create BufferQueueConsumer");

    *outProducer = producer;
    *outConsumer = consumer;
}

Buffer Queue Core 는 Buffer Queue 를 관리 하 는 핵심 클래스 로 다음 과 같은 두 가지 중요 한 구성원 변수 가 있 습 니 다. mAllocator 는 Surface Flinger 와 연결 되 어 Graphic Buffer 를 만 들 고 mSlots 는 buffer 데 이 터 를 저장 하 는 데 사용 되 며 64 개의 buffer 를 지원 합 니 다. 그 중에서 Buffer Slot 는 Graphic Buffer 와 BufferState 를 저장 합 니 다.
sp mAllocator;
BufferQueueDefs::SlotsType mSlots;
namespace BufferQueueDefs {
        enum { NUM_BUFFER_SLOTS = 64 };
        typedef BufferSlot SlotsType[NUM_BUFFER_SLOTS];
} 
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) {
    }

    sp mGraphicBuffer;
    EGLDisplay mEglDisplay;
    BufferState mBufferState;
    bool mRequestBufferCalled;
    uint64_t mFrameNumber;
    EGLSyncKHR mEglFence;
    sp mFence;
    bool mAcquireCalled;
    bool mNeedsReallocation;
};

위 에서 언급 한 buffer 분 배 는 생산자 에 의 해 시 작 됩 니 다. 즉, Buffer Queue Producer 의 deququeBuffer 작업 입 니 다. 함수 원형 은 다음 과 같 습 니 다.
status_t BufferQueueProducer::dequeueBuffer(int *outSlot,
        sp<:fence> *outFence, uint32_t width, uint32_t height,
        PixelFormat format, uint32_t usage) {
    ATRACE_CALL();
    { // Autolock scope
        Mutex::Autolock lock(mCore->mMutex);
        mConsumerName = mCore->mConsumerName;

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

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

    BQ_LOGV("dequeueBuffer: w=%u h=%u format=%#x, usage=%#x", width, height,
            format, usage);

    if ((width && !height) || (!width && height)) {
        BQ_LOGE("dequeueBuffer: invalid size: w=%u h=%u", width, height);
        return BAD_VALUE;
    }

    status_t returnFlags = NO_ERROR;
    EGLDisplay eglDisplay = EGL_NO_DISPLAY;
    EGLSyncKHR eglFence = EGL_NO_SYNC_KHR;
    bool attachedByConsumer = false;

    { // Autolock scope
        Mutex::Autolock lock(mCore->mMutex);
        mCore->waitWhileAllocatingLocked();

        if (format == 0) {
            format = mCore->mDefaultBufferFormat;
        }

        // Enable the usage bits the consumer requested
        usage |= mCore->mConsumerUsageBits;

        const bool useDefaultSize = !width && !height;
        if (useDefaultSize) {
            width = mCore->mDefaultWidth;
            height = mCore->mDefaultHeight;
        }

        int found = BufferItem::INVALID_BUFFER_SLOT;
        while (found == BufferItem::INVALID_BUFFER_SLOT) {
            status_t status = waitForFreeSlotThenRelock(FreeSlotCaller::Dequeue,
                    &found);
            if (status != NO_ERROR) {
                return status;
            }

            // This should not happen
            if (found == BufferQueueCore::INVALID_BUFFER_SLOT) {
                BQ_LOGE("dequeueBuffer: no available buffer slots");
                return -EBUSY;
            }

            const sp& buffer(mSlots[found].mGraphicBuffer);

            // If we are not allowed to allocate new buffers,
            // waitForFreeSlotThenRelock must have returned a slot containing a
            // buffer. If this buffer would require reallocation to meet the
            // requested attributes, we free it and attempt to get another one.
            if (!mCore->mAllowAllocation) {
                if (buffer->needsReallocation(width, height, format, usage)) {
                    if (mCore->mSharedBufferSlot == found) {
                        BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                                "buffer");
                        return BAD_VALUE;
                    }
                    mCore->mFreeSlots.insert(found);
                    mCore->clearBufferSlotLocked(found);
                    found = BufferItem::INVALID_BUFFER_SLOT;
                    continue;
                }
            }
        }

        const sp& buffer(mSlots[found].mGraphicBuffer);
        if (mCore->mSharedBufferSlot == found &&
                buffer->needsReallocation(width,  height, format, usage)) {
            BQ_LOGE("dequeueBuffer: cannot re-allocate a shared"
                    "buffer");

            return BAD_VALUE;
        }

        if (mCore->mSharedBufferSlot != found) {
            mCore->mActiveBuffers.insert(found);
        }
        *outSlot = found;
        ATRACE_BUFFER_INDEX(found);

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

        mSlots[found].mBufferState.dequeue();

        if ((buffer == NULL) ||
                buffer->needsReallocation(width, height, format, usage))
        {
            mSlots[found].mAcquireCalled = false;
            mSlots[found].mGraphicBuffer = NULL;
            mSlots[found].mRequestBufferCalled = false;
            mSlots[found].mEglDisplay = EGL_NO_DISPLAY;
            mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
            mSlots[found].mFence = Fence::NO_FENCE;
            mCore->mBufferAge = 0;
            mCore->mIsAllocating = true;

            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;
        }

        BQ_LOGV("dequeueBuffer: setting buffer age to %" PRIu64,
                mCore->mBufferAge);

        if (CC_UNLIKELY(mSlots[found].mFence == NULL)) {
            BQ_LOGE("dequeueBuffer: about to return a NULL fence - "
                    "slot=%d w=%d h=%d format=%u",
                    found, buffer->width, buffer->height, buffer->format);
        }

        eglDisplay = mSlots[found].mEglDisplay;
        eglFence = mSlots[found].mEglFence;
        // Don't return a fence in shared buffer mode, except for the first
        // frame.
        *outFence = (mCore->mSharedBufferMode &&
                mCore->mSharedBufferSlot == found) ?
                Fence::NO_FENCE : mSlots[found].mFence;
        mSlots[found].mEglFence = EGL_NO_SYNC_KHR;
        mSlots[found].mFence = Fence::NO_FENCE;

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

    if (returnFlags & BUFFER_NEEDS_REALLOCATION) {
        status_t error;
        BQ_LOGV("dequeueBuffer: allocating a new buffer for slot %d", *outSlot);
        sp graphicBuffer(mCore->mAllocator->createGraphicBuffer(
                width, height, format, usage,
                {mConsumerName.string(), mConsumerName.size()}, &error));
        { // Autolock scope
            Mutex::Autolock lock(mCore->mMutex);

            if (graphicBuffer != NULL && !mCore->mIsAbandoned) {
                graphicBuffer->setGenerationNumber(mCore->mGenerationNumber);
                mSlots[*outSlot].mGraphicBuffer = graphicBuffer;
            }

            mCore->mIsAllocating = false;
            mCore->mIsAllocatingCondition.broadcast();

            if (graphicBuffer == NULL) {
                mCore->mFreeSlots.insert(*outSlot);
                mCore->clearBufferSlotLocked(*outSlot);
                BQ_LOGE("dequeueBuffer: createGraphicBuffer failed");
                return error;
            }

            if (mCore->mIsAbandoned) {
                mCore->mFreeSlots.insert(*outSlot);
                mCore->clearBufferSlotLocked(*outSlot);
                BQ_LOGE("dequeueBuffer: BufferQueue has been abandoned");
                return NO_INIT;
            }

            VALIDATE_CONSISTENCY();
        } // Autolock scope
    }

    if (attachedByConsumer) {
        returnFlags |= BUFFER_NEEDS_REALLOCATION;
    }

    if (eglFence != EGL_NO_SYNC_KHR) {
        EGLint result = eglClientWaitSyncKHR(eglDisplay, eglFence, 0,
                1000000000);
        // If something goes wrong, log the error, but return the buffer without
        // synchronizing access to it. It's too late at this point to abort the
        // dequeue operation.
        if (result == EGL_FALSE) {
            BQ_LOGE("dequeueBuffer: error %#x waiting for fence",
                    eglGetError());
        } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
            BQ_LOGE("dequeueBuffer: timeout waiting for fence");
        }
        eglDestroySyncKHR(eglDisplay, eglFence);
    }

    BQ_LOGV("dequeueBuffer: returning slot=%d/%" PRIu64 " buf=%p flags=%#x",
            *outSlot,
            mSlots[*outSlot].mFrameNumber,
            mSlots[*outSlot].mGraphicBuffer->handle, returnFlags);

    return returnFlags;
}

deququeBuffer 의 목적 은 바로 Producer 에 사용 가능 한 buffer 를 신청 하 는 것 입 니 다. 최종 적 으로 buffer slots 배열 색인 을 얻 었 습 니 다. outSlot 매개 변 수 를 통 해 되 돌아 갑 니 다. outFence 는 동기 화 형식 입 니 다. Fence 를 사용 할 때 Fence 신 호 를 기 다 려 야 buffer 를 수정 할 수 있 습 니 다. width 와 height 참 수 는 일정한 제한 이 있 습 니 다. 최대 치 는 GL 을 초과 해 서 는 안 됩 니 다.MAX_VIEWPORT_DIMS 와 GLMAX_TEXTURE_SIZE 의 최소 값 입 니 다. 이 값 은 glGetIntegerv 를 통 해 얻 을 수 있 습 니 다. updateTexImage 와 관련 하여 width 와 height 가 0 일 때 기본 값 1 을 사용 합 니 다. 그 중 하나 가 0 이면 오류 가 발생 합 니 다. format 와 usage 는 buffer 의 형식 과 용 도 를 지정 합 니 다. format 가 0 일 때 기본 mDefault BufferFormat, 즉 PIXEL 을 사용 합 니 다.FORMAT_RGBA_8888。
deququeBuffer 함수 에 서 는 먼저 BufferQueue 가 사용 가능 한 지, BufferQueue 가 Producer, width, height 인자 에 연결 되 어 있 는 지, 실패 할 때 오류 가 발생 했 는 지 확인 합 니 다.그리고 while 순환 에 들 어가 사용 가능 한 buffer slot 를 찾 습 니 다. 그러나 그 전에 width, height, format, usage 변 수 를 처리 해 야 합 니 다. 다른 buffer 할당 작업 이 끝 날 때 까지 기 다 려 야 합 니 다. pthread 조건 변 수 를 사용 합 니 다.while 순환 에서 사용 가능 한 buffer slot 를 찾 을 때 먼저 wait ForFreeSlot Then Relock 에 들 어 갑 니 다. 이름 에서 알 수 있 듯 이 FREE 상 태 를 가 져 오 는 buffer slot 입 니 다. 또한 buffer 의 DEQUEUEED 와 ACQUIRED 상태의 개 수 를 통계 할 수 있 습 니 다. 제 한 된 최대 치 를 초과 할 수 없고 자주 disconect 를 한 후에 바로 connect 할 수 없습니다. 이 는 메모리 가 소 진 될 수 있 습 니 다.buffer 를 기다 리 는 동안 차단, 지연 효 과 는 buffer 조회 결과 에 영향 을 줍 니 다.buffer slot 를 가 져 온 후에 실제 buffer slot 의 배열 색인 입 니 다. 이 때 는 메모리 가 진정 으로 분배 되 지 않 았 습 니 다. 새로운 buffer 를 할당 할 수 있 는 권한 이 없 을 때 이 단 계 는 보통 사용 가능 한 buffer 로 돌아 갑 니 다. 그러나 주의해 야 할 것 은 이 buffer 가 가끔 가장 좋 은 것 이 아니 라 재 분 배 를 해 야 합 니 다. 이 비우 면이 buffer 는 free 로 떨 어 지고 뒤의 재 분 배 를 기다 릴 것 입 니 다.while 순환 후 buffer 가 재배 치 되 어야 할 경우 createGraphic Buffer 를 통 해 완성 되 며, 이로써 dequeueBuffer 가 완 료 됩 니 다.
dequeue Buffer 에서 가 져 온 것 은 buffer slot 색인 입 니 다. 그러면 서로 다른 프로 세 스 에 속 하 는 서버 와 클 라 이언 트 가 같은 메모리 에 접근 하 는 것 을 어떻게 보장 합 니까? 이것 은 Android 의 Binder 체 제 를 사 용 했 습 니 다. 다음 과 같은 몇 가지 관련 된 종 류 를 포함 합 니 다.
class BufferQueueProducer : public BnGraphicBufferProducer, private IBinder::DeathRecipient
class BnGraphicBufferProducer : public BnInterface
class BpGraphicBufferProducer : public BpInterface

다음은 BpGraphic Buffer Producer 의 dequeue Buffer 구현 입 니 다. 관건 은 reote 를 통 해 transact 를 호출 하 는 것 입 니 다. 이때 native BnGraphic Buffer Producer 의 onTransact 로 갑 니 다.
virtual status_t dequeueBuffer(int *buf, sp<Fence>* fence, uint32_t width,
            uint32_t height, PixelFormat format, uint32_t usage) {
        Parcel data, reply;
        data.writeInterfaceToken(IGraphicBufferProducer::getInterfaceDescriptor());
        data.writeUint32(width);
        data.writeUint32(height);
        data.writeInt32(static_cast<int32_t>(format));
        data.writeUint32(usage);
        status_t result = remote()->transact(DEQUEUE_BUFFER, data, &reply);
        if (result != NO_ERROR) {
            return result;
        }
        *buf = reply.readInt32();
        bool nonNull = reply.readInt32();
        if (nonNull) {
            *fence = new Fence();
            result = reply.read(**fence);
            if (result != NO_ERROR) {
                fence->clear();
                return result;
            }
        }
        result = reply.readInt32();
        return result;
    }

다음은 BnGraphic Buffer Producer 의 onTransact 부분 코드 입 니 다. DEQUEUEBUFFER 시 data 에서 파 라 메 터 를 가 져 온 후 buffer 생산자 의 dequeue Buffer 를 호출 한 후 결 과 를 reply 파라미터 로 되 돌려 줍 니 다.
status_t BnGraphicBufferProducer::onTransact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    switch(code) {
        case DEQUEUE_BUFFER: {
            CHECK_INTERFACE(IGraphicBufferProducer, data, reply);
            uint32_t width = data.readUint32();
            uint32_t height = data.readUint32();
            PixelFormat format = static_cast<PixelFormat>(data.readInt32());
            uint32_t usage = data.readUint32();
            int buf = 0;
            sp<Fence> fence;
            int result = dequeueBuffer(&buf, &fence, width, height, format,
                    usage);
            reply->writeInt32(buf);
            reply->writeInt32(fence != NULL);
            if (fence != NULL) {
                reply->write(*fence);
            }
            reply->writeInt32(result);
            return NO_ERROR;
        }
    }
    return BBinder::onTransact(code, data, reply, flags);
}

IGraphicBuffer Producer 에는 두 개의 struct 가 있 습 니 다. QueueBufferInput 과 QueueBuffer Output 은 다음 과 같 습 니 다. 그 중에서 QueueBuffer Input 은 Flattenable 프로 토 콜 을 사용 하여 한 대상 인 buffer 입 형 디지털화 에 사 용 됩 니 다.
struct QueueBufferInput : public Flattenable
struct QueueBufferOutput

3. 응용 프로그램 과 BufferQueue
앞에서 BufferQueue 의 기본 원 리 를 소 개 했 는데 어떻게 응용 프로그램 과 연결 되 었 습 니까?frameworks / base / cmds / bootanimation / 이 시스템 의 부팅 애니메이션 을 예 로 들 면 bootanimation 과 Surface Flinger 는 OpenGL ES 를 사용 하여 UI 디 스 플레이 를 완성 합 니 다. Android 장치 에 전기 가 들 어가 면 BootLoader 화면, Kernel 그림, Android 화면 등 몇 가지 다른 부팅 화면 이 표 시 될 수 있 습 니 다. bootanim. rc 시작 스 크 립 트 의 내용 은 다음 과 같 습 니 다.
service bootanim /system/bin/bootanimation
    class core
    user graphics
    group graphics audio
    disabled
    oneshot
    writepid /dev/stune/top-app/tasks

bootanimation 이 시 작 된 후에 먼저 다음 과 같은 main 함수 에 들 어가 속성 debug. sf. nobootanimation 을 통 해 애니메이션 을 시작 하 는 지 여 부 를 판단 하고 애니메이션 이 있 을 때 스 레 드 풀 을 시작 한 다음 에 BootAnimation 대상 을 만 들 고 스 레 드 풀 에 가입 합 니 다.
int main()
{
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.nobootanimation", value, "0");
    int noBootAnimation = atoi(value);
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {

        sp proc(ProcessState::self());
        ProcessState::self()->startThreadPool();

        // create the boot animation object
        sp boot = new BootAnimation();

        IPCThreadState::self()->joinThreadPool();

    }
    return 0;
}

BootAnimation 은 애니메이션 을 시작 하 는 구현 클래스 로 Thread 와 Binder 를 계승 하여 구조 함수 에 Surface Composer Client 대상, 즉 mSession 을 생 성하 여 Surface Flinger 와 의 연결 채널 을 만 들 었 고 앞에서 언급 한 IGraphicBuffer Producer 는 응용 프로그램 과 BufferQueue 의 전송 채널 이 며 BufferQueue 는 모든 응용 프로그램의 회화 과정 을 책임 집 니 다.Surface Flinger 는 모든 프로그램의 최종 그림 결 과 를 혼합 하여 물리 화면 에 통일 적 으로 표시 합 니 다.
class BootAnimation : public Thread, public IBinder::DeathRecipient

BootAnimation::BootAnimation() : Thread(false), mClockEnabled(true), mTimeIsAccurate(false),
        mTimeCheckThread(NULL) {
    mSession = new SurfaceComposerClient();

    // If the system has already booted, the animation is not being used for a boot.
    mSystemBoot = !property_get_bool(BOOT_COMPLETED_PROP_NAME, 0);
}

BootAnimation 상속 자 RefBase 는 포인터 가 처음 인 용 될 때 onFirst Ref 함 수 를 터치 합 니 다. 원 격 Binder 서비스 와 연결 한 후 Binder 서비스 가 마 운 트 된 빈 공간 을 처리 하고 binder Died 함 수 를 다시 설치 한 다음 run 을 호출 하여 BootAnimation 스 레 드 를 시작 합 니 다.
void BootAnimation::onFirstRef() {
    status_t err = mSession->linkToComposerDeath(this);
    ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err));
    if (err == NO_ERROR) {
        run("BootAnimation", PRIORITY_DISPLAY);
    }
}

void BootAnimation::binderDied(const wp<IBinder>&)
{
    // woah, surfaceflinger died!
    ALOGD("SurfaceFlinger died, exiting...");

    // calling requestExit() is not enough here because the Surface code
    // might be blocked on a condition variable that will never be updated.
    kill( getpid(), SIGKILL );
    requestExit();
    audioplay::destroy();
}

다음은 ready ToRun 입 니 다. 관건 적 인 두 단 계 는 Surface ComposerClient 를 통 해 buffer 와 EGL 설정 을 만 드 는 것 입 니 다. 다음 코드 는 다음 과 같 습 니 다.
status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

    sp dtoken(SurfaceComposerClient::getBuiltInDisplay(
            ISurfaceComposer::eDisplayIdMain));
    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
    if (status)
        return -1;

    // create the native surface
    sp control = session()->createSurface(String8("BootAnimation"),
            dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565);

    SurfaceComposerClient::openGlobalTransaction();
    control->setLayer(0x40000000);
    SurfaceComposerClient::closeGlobalTransaction();

    sp s = control->getSurface();

    // initialize opengl and egl
    const EGLint attribs[] = {
            EGL_RED_SIZE,   8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE,  8,
            EGL_DEPTH_SIZE, 0,
            EGL_NONE
    };
    EGLint w, h;
    EGLint numConfigs;
    EGLConfig config;
    EGLSurface surface;
    EGLContext context;

    EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);

    eglInitialize(display, 0, 0);
    eglChooseConfig(display, attribs, &config, 1, &numConfigs);
    surface = eglCreateWindowSurface(display, config, s.get(), NULL);
    context = eglCreateContext(display, config, NULL, NULL);
    eglQuerySurface(display, surface, EGL_WIDTH, &w);
    eglQuerySurface(display, surface, EGL_HEIGHT, &h);

    if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE)
        return NO_INIT;

    mDisplay = display;
    mContext = context;
    mSurface = surface;
    mWidth = w;
    mHeight = h;
    mFlingerSurfaceControl = control;
    mFlingerSurface = s;

    // If the device has encryption turned on or is in process
    // of being encrypted we show the encrypted boot animation.
    char decrypt[PROPERTY_VALUE_MAX];
    property_get("vold.decrypt", decrypt, "");

    bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt);

    if (encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0)) {
        mZipFileName = SYSTEM_ENCRYPTED_BOOTANIMATION_FILE;
    }
    else if (access(OEM_BOOTANIMATION_FILE, R_OK) == 0) {
        mZipFileName = OEM_BOOTANIMATION_FILE;
    }
    else if (access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) {
        mZipFileName = SYSTEM_BOOTANIMATION_FILE;
    }
    return NO_ERROR;
}

Surface ComposerClient 의 createSurface, Surface Flinger 에서 의 실현 은 바로 Client 의 createSurface 입 니 다. 다음 과 같 습 니 다.
status_t Client::createSurface(
        const String8& name,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp* handle,
        sp* gbp)
{
    /*
     * createSurface must be called from the GL thread so that it can
     * have access to the GL context.
     */

    class MessageCreateLayer : public MessageBase {
        SurfaceFlinger* flinger;
        Client* client;
        sp* handle;
        sp* gbp;
        status_t result;
        const String8& name;
        uint32_t w, h;
        PixelFormat format;
        uint32_t flags;
    public:
        MessageCreateLayer(SurfaceFlinger* flinger,
                const String8& name, Client* client,
                uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
                sp* handle,
                sp* gbp)
            : flinger(flinger), client(client),
              handle(handle), gbp(gbp), result(NO_ERROR),
              name(name), w(w), h(h), format(format), flags(flags) {
        }
        status_t getResult() const { return result; }
        virtual bool handler() {
            result = flinger->createLayer(name, client, w, h, format, flags,
                    handle, gbp);
            return true;
        }
    };

    sp msg = new MessageCreateLayer(mFlinger.get(),
            name, this, w, h, format, flags, handle, gbp);
    mFlinger->postMessageSync(msg);
    return static_cast( msg.get() )->getResult();
}

Client:: createSurface 에서 메시지 post 를 Surface Flinger 에 동기 화 하 는 동안 Layer 를 만 들 었 습 니 다. 다음 createNormal Layer:
status_t SurfaceFlinger::createLayer(
        const String8& name,
        const sp& client,
        uint32_t w, uint32_t h, PixelFormat format, uint32_t flags,
        sp* handle, sp* gbp)
{
    //ALOGD("createLayer for (%d x %d), name=%s", w, h, name.string());
    if (int32_t(w|h) < 0) {
        ALOGE("createLayer() failed, w or h is negative (w=%d, h=%d)",
                int(w), int(h));
        return BAD_VALUE;
    }

    status_t result = NO_ERROR;

    sp layer;

    switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
        case ISurfaceComposerClient::eFXSurfaceNormal:
            result = createNormalLayer(client,
                    name, w, h, flags, format,
                    handle, gbp, &layer);
            break;
        case ISurfaceComposerClient::eFXSurfaceDim:
            result = createDimLayer(client,
                    name, w, h, flags,
                    handle, gbp, &layer);
            break;
        default:
            result = BAD_VALUE;
            break;
    }

    if (result != NO_ERROR) {
        return result;
    }

    result = addClientLayer(client, *handle, *gbp, layer);
    if (result != NO_ERROR) {
        return result;
    }

    setTransactionFlags(eTransactionNeeded);
    return result;
}

Surface Flinger:: create Normal Layer 를 통 해 알 수 있 듯 이 여기 서 진정 으로 Layer 대상 을 만 들 었 고 Buffer Queue 와 Surface Flinger Consumer, Monitor Producer 를 만 들 었 습 니 다. 이렇게 Buffer 중개, 생산자, 소비자 생 성 이 완료 되 었 습 니 다. Layer 는 마치 많은 그림 의 한 층 과 같 습 니 다. 마지막 으로 Surface Flinger 를 통 해 처리 하여 화면 에 표 시 됩 니 다.
status_t SurfaceFlinger::createNormalLayer(const sp& client,
        const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
        sp* handle, sp* gbp, sp* outLayer)
{
    // initialize the surfaces
    switch (format) {
    case PIXEL_FORMAT_TRANSPARENT:
    case PIXEL_FORMAT_TRANSLUCENT:
        format = PIXEL_FORMAT_RGBA_8888;
        break;
    case PIXEL_FORMAT_OPAQUE:
        format = PIXEL_FORMAT_RGBX_8888;
        break;
    }

    *outLayer = new Layer(this, client, name, w, h, flags);
    status_t err = (*outLayer)->setBuffers(w, h, format, flags);
    if (err == NO_ERROR) {
        *handle = (*outLayer)->getHandle();
        *gbp = (*outLayer)->getProducer();
    }

    ALOGE_IF(err, "createNormalLayer() failed (%s)", strerror(-err));
    return err;
}

이로써 buffer 생 성 이 완료 되 었 습 니 다. EGL 설정 이 완료 되 었 고 threadLoop 에 들 어 갔 습 니 다. 이 어 안 드 로 이 드 나 movie 가 될 수 있 습 니 다. 안 에는 모두 OpenGL 작업 으로 애니메이션 을 표시 하고 마지막 으로 EGL 을 통 해 자원 을 방출 하여 스 레 드 풀 을 종료 할 수 있 습 니 다.
bool BootAnimation::threadLoop()
{
    bool r;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZipFileName.isEmpty()) {
        r = android();
    } else {
        r = movie();
    }

    eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
    eglDestroyContext(mDisplay, mContext);
    eglDestroySurface(mDisplay, mSurface);
    mFlingerSurface.clear();
    mFlingerSurfaceControl.clear();
    eglTerminate(mDisplay);
    eglReleaseThread();
    IPCThreadState::self()->stopProcess();
    return r;
}

좋은 웹페이지 즐겨찾기