2. 1.노말 서피스 출력(서비스 클라이언트)
그래픽 버퍼의 가상 주소를 획득한 후 그래픽 버퍼의 메모리에 랜더링 하여 서피스 플링거로 출력 요청하게 된다.
노말 서피스의 출력 요청 과정
SurfaceTextureClient의 queueBuffer함수는 서피스 플링거에게 출력할 버퍼를 알려준다.
int SurfaceTextureClient::queueBuffer(android_native_buffer_t* buffer) {
timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
해당 버퍼의 마지막 출력 요청 시간.
int i = getSlotFromBufferLocked(buffer);
출력 요청할 버퍼의 인덱스를 검색한다.
ISurfaceTexture::QueueBufferOutput output;
ISurfaceTexture::QueueBufferInput input(timestamp, crop, mScalingMode, mTransform);
status_t err = mSurfaceTexture->queueBuffer(i, input, &output);
BufferQueue의 queueBuffer를 호출한다.
( i : 출력할 버퍼 인덱스, input(timestamp:해당 버퍼의 출력 요청 시간등),
output:서버에 저장된 그래픽 버퍼의 가로,세로 값을 저장함. 이 값은 애플리케이션에서 다음 그래픽 버퍼 획득 시
기본 값으로 사용 됨.)
}
2
3. 2.노말 서피스 출력(서비스 서버)
출력 방식은 동기, 비동기 2가지의 경우가 있다.
1.동기식:애플리케이션에서 출력 요청이 한번 발생하면 서피스 플링거 서비스에서도 한번의 출력이 발생 됨.
2.비동기식:출력 요청이 발생한 횟수에 관계없이 서피스 플링거 서비스의 합성에 맞추어 화면 출력이 됨.
status_t BufferQueue::queueBuffer(int buf, const QueueBufferInput& input, QueueBufferOutput* output) {
if (mSynchronousMode) { 동기식일 경우
mQueue.push_back(buf); 출력 대기 큐의 제일 마지막에 넣는다.(FIFO 방식)
listener = mConsumerListener;
mConsumerListener 는 SurfaceTexture 생성자에서 BufferQueue::ProxyConsumerListener 로 설정되어 있음.
이 lister는 출력 가능한 버퍼가 대기 하고 있다는 것을 서피스 플링거에게 알려줌.
}
else 비동기식
{
if (mQueue.empty()) 출력 대기 큐에 대기하는 버퍼가 없다면
{
mQueue.push_back(buf); 현재 요청된 버퍼를 대기 큐에 저장함.
listener = mConsumerListener;
}
else { 출력 대기 하고 있는 버퍼가 있다면
Fifo::iterator front(mQueue.begin()); 출력 대기 큐의 첫번째 인덱스를 획득해서,
mSlots[*front].mBufferState = BufferSlot::FREE; 슬롯의 상태를 FREE로 만든다.
*front = buf; 출력 대기 큐의 첫번째 인덱스에 새롭게 요청한 인덱스를 넣는다.
}
mSlots[buf].mBufferState = BufferSlot::QUEUED; 버퍼의 상태를 QUEUED로 바꿈.
즉, 클라이언트는 출력 요청 하였고,서피스 플링거 서비스는 해당 버퍼를 아직 합성하지 않았다는 의미 임.
mDequeueCondition.broadcast(); FREE상태를 기다리는 dequeueBuffer 함수를 깨운다.
if (listener != 0) {
listener->onFrameAvailable(); 서피스 플링거에게 합성 신호를 보내게 요청 함.
}
3
4. 2.노말 서피스 출력(서비스 서버)
void BufferQueue::ProxyConsumerListener::onFrameAvailable() {
sp<BufferQueue::ConsumerListener> listener(mConsumerListener.promote());
if (listener != NULL) {
listener->onFrameAvailable();
}
} SurfaceTexture는 BufferQueue::ConsumerListener 를 상속 받기 때문에
SurfaceTexture::onFrameAvailable가 호출 된다.
SurfaceTexture.cpp
void SurfaceTexture::onFrameAvailable() {
if (listener != NULL) {
ST_LOGV("actually calling onFrameAvailable");
listener->onFrameAvailable();
}
}
Layer.cpp
struct FrameQueuedListener : public SurfaceTexture::FrameAvailableListener {
FrameQueuedListener(Layer* layer) : mLayer(layer) { }
virtual void onFrameAvailable() {
if (that != 0) {
that->onFrameQueued();
}
}};
void Layer::onFrameQueued() {
android_atomic_inc(&mQueuedFrames); mQueuedFrames는 출력 요청이 발생한 횟수를 저장함.
mFlinger->signalLayerUpdate(); 서피스 플링거에게 합성 시그널을 보냄.
}
4
5. 3.디스플레이 이벤트 동기화
젤리빈부터 하드웨어에서 발생하는 Vsync 이벤트에 맞춰 렌더링을 시작하도록 변경 됨.
서피스 플링거 역시 Vsync 에 맞춰 합성을 시작 함.
Vsync Event
1.연속적 통지: Vsync 이벤트 발생 시 마다 이벤트 통지
2. 일회성 통지: Vsync 이벤트 요청 시 마다 이벤트 통지
서피스 플링거는 일회성 통지를 사용, 즉 출력 버퍼 큐에 새로운 버퍼가 추가되면 Vsync 에 맞춰 합성 시그널이 발생됨.
SurfaceFlinger.cpp
void SurfaceFlinger::signalLayerUpdate() {
mEventQueue.invalidate();
}
Messagequeue.cpp
void MessageQueue::invalidate() {
mEvents->requestNextVsync();
mEvents 는 EventThread 의 Connection class를 가르킨다.
}
EventThread.cpp
void EventThread::Connection::requestNextVsync() {
mEventThread->requestNextVsync(this); mEventThread 는 EventThread를 가르킨다.
}
void EventThread::requestNextVsync( const sp<EventThread::Connection>& connection) {
if (connection->count < 0) { count의 초기 값이 -1이다.
connection->count = 0; count 값이 0 이면 일회성 통지를 의미함.
mCondition.broadcast(); EventThread::threadLoop() 에서 기다리는 thread를 깨움.
}
}
5
6. 3.디스플레이 이벤트 동기화
EventThread::threadLoop() 에서 Vsync가 발생 할때 까지 대기 하다 mCondition.broadcast(); 가 불리면 깨어 난다.
bool EventThread::threadLoop() {
do {
do {
timestamp = mVSyncTimestamp;
mVSyncTimestamp는 HWComposer로 부터 Vsync가 전달되면 갱신된다.
즉, HWComposer 로 부터 전달된 Vsync로 깨어난 경우가 아니라면, mVSyncTimestamp 은 0 이다.
현재 경우는, requestNextVsync 에 의해 깨어 났기 때문에 mVSyncTimestamp 가 0 이다.
mVSyncTimestamp = 0;
bool waitForNextVsync = false;
size_t count = mDisplayEventConnections.size(); VSync 이벤트를 기다리는 수신자들(서피스 플링거)을 획득한다.
for (size_t i=0 ; i<count ; i++) {
sp<Connection> connection =mDisplayEventConnections.itemAt(i).promote();
connectionList.add(connection);
if (connection!=0 && connection->count >= 0) {
EventThread::requestNextVsync 에서 count 값을 0으로 설정했다. 이 의미는 Vsync 를 받아 출력해야 할
Event가 있다는 의미이다.
waitForNextVsync = true;
Vsync가 필요하다는 의미로 waitForNextVsync 를 설정 한다.
break;
}
if (timestamp) { timestamp 가 0 보다 크다는 것은 HWComposer 로 부터 Vsync가 발생 되었다는 의미이다.
현재 경우는 Vsync가 전달되지 않았기 때문에 0 이다.
if (!waitForNextVsync) {
} else {
이 경우는 HWComposer 로 부터 Vsync가 발생된 경우임으로 첫번째 do while문을 빠져 나간다.
break;
}
}
6
7. 3.디스플레이 이벤트 동기화
else { HWComposer 로 부터 Vsync 가 발생 되지 않을 경우
if (waitForNextVsync) { Vsync가 필요 함으로
enableVSyncLocked(); HWCompser에게 Vsync 발생 요청
}
mCondition.wait(mLock); HWCompser로 부터 Vsync 가 발생되기를 기다림.
} while(true); // 첫번째 do while문
const size_t count = mDisplayEventConnections.size(); 이벤트 쓰레드에 등록된 Vsync 이벤트 수신자를 획득한다.
for (size_t i=0 ; i<count ; i++) {
const int32_t count = connection->count;
if (count >= 1) {
reportVsync = true; 수신자의 모드가 연속(count =1) 일 경우 reportVsync 를 수신 할 수 있다.
} else if (count >= -1) {
If (count == 0) {
reportVsync = true; 수신자의 모드가 일회성(count =0) 일 경우 reportVsync 를 수신 할 수 있다.
}
if (reportVsync) {
displayEventConnections.add(connection);
Vsync를 수신 받을 수신자를 displayEventConnections 에 추가한다.
}
}
while (!displayEventConnections.size()); Vsync를 수신 받을 수신자가 있기 때문에 두번째 do while 문을 벗어난다.
vsync.header.type = DisplayEventReceiver::DISPLAY_EVENT_VSYNC; 수신자에게 보낼 이벤트 타입 설정
vsync.header.timestamp = timestamp; Vsync 발생 시간
const size_t count = displayEventConnections.size(); Vsync를 수신 받을 수신자의 개수를 가져온다.
for (size_t i=0 ; i<count ; i++) {
status_t err = conn->postEvent(vsync); 각 수신자에게 Vsync를 보낸다.
}
7
8. 3.디스플레이 이벤트 동기화
Vsync 수신시의 처리
애플리케이션 측에서는 choreographer class를 이용하면 Vsync 이벤트를 수신 할 수 있다.
서피스 플링거는 네이티브 루퍼에서 Vsync event를 수신한다.
네이티브 루퍼는 Vsync가 수신되면 MessageQueue::setEventThread 의 MessageQueue::cb_eventReceiver 가 불려진다.
MessageQueue.cpp
void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
{
mEventThread = eventThread;
mEvents = eventThread->createEventConnection();
mEventTube = mEvents->getDataChannel();
mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT, MessageQueue::cb_eventReceiver, this);
}
int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
return queue->eventReceiver(fd, events);
}
int MessageQueue::eventReceiver(int fd, int events) {
while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
mHandler->signalRefresh(); MessageQueue::Handler::signalRefresh 를 호출한다.
break;
}
}
}
8
9. 3.디스플레이 이벤트 동기화
void MessageQueue::Handler::signalRefresh() {
if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
네이티브 루퍼에 MessageQueue::REFRESH 를 보낸다.
}
}
void MessageQueue::Handler::handleMessage(const Message& message) {
case REFRESH:
android_atomic_and(~eventMaskRefresh, &mEventMask);
mQueue.mFlinger->onMessageReceived(message.what);
Surfaceflinger의 onMessageReceived 를 호출한다.
break;
}
}
9
10. 4.레이어 합성과 화면 출력
Surfaceflinger의 onMessageReceived 함수는 화면 출력 준비가 된 레이어들을 모아서 합성한 후 프레임 버퍼에 출력한다.
void SurfaceFlinger::onMessageReceived(int32_t what)
{
switch (what) {
case MessageQueue::INVALIDATE:
case MessageQueue::REFRESH: {
….
handlePageFlip(); 레이어에 할당된 그래픽 버퍼를 EGL Image로 만들어 텍스쳐로 사용할 수 있도록 만든다.---(1)
handleRepaint();합성
hw.compositionComplete(); 합성이 완료 되었다는 것을 하드웨어에게 알려줌.
postFramebuffer(); 완료된 합성 이미지를 프레임 버퍼에 출력.------------(2)
}
(1) handlePageFlip
void SurfaceFlinger::handlePageFlip()
{
const bool visibleRegions = lockPageFlip(currentLayers);
}
bool SurfaceFlinger::lockPageFlip(const LayerVector& currentLayers)
{
layer->lockPageFlip(recomputeVisibleRegions);
}
Layer.cpp
void Layer::lockPageFlip(bool& recomputeVisibleRegions)
{
if (mSurfaceTexture->updateTexImage(&r) < NO_ERROR) {
return;
}
10
11. 3.레이어 합성과 화면 출력
status_t SurfaceTexture::updateTexImage(BufferRejecter* rejecter) {
err = mBufferQueue->acquireBuffer(&item); 출력 대기 큐의 첫번째 요소에 저장된 버퍼를 획득한다.
BufferQueue.cpp
status_t BufferQueue::acquireBuffer(BufferItem *buffer)
{
if (!mQueue.empty()) {
Fifo::iterator front(mQueue.begin());
int buf = *front; 출력 대기 큐의 첫번째 인덱스를 가져온다.
buffer->mGraphicBuffer = mSlots[buf].mGraphicBuffer; 그래픽 버퍼를 변수에 저장.
buffer->mTimestamp = mSlots[buf].mTimestamp; 버퍼의 출력 요청 시간 저장.
mSlots[buf].mBufferState = BufferSlot::ACQUIRED;
버퍼 상태를 ACQUIRED 로 바꾸어서, 다시 queuebuffer 할 수 없도록 함.
mQueue.erase(front); 다음 버퍼를 위해 출력 대기 큐에서 삭제.
}
if (item.mGraphicBuffer != NULL) { 획득한 버퍼 인덱스의 GraphicBuffer 가 있으면
mEGLSlots[buf].mGraphicBuffer = 0; 기존 mEGLSlots 의 내용을 지운다.
if (mEGLSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) {
eglDestroyImageKHR(dpy, mEGLSlots[buf].mEglImage);
mEGLSlots[buf].mEglImage = EGL_NO_IMAGE_KHR;
}
mEGLSlots[buf].mGraphicBuffer = item.mGraphicBuffer; GraphicBuffer 다시 지정.
}
image = createImage(dpy, mEGLSlots[buf].mGraphicBuffer); EGL Image를 다시 만든다.
mEGLSlots[buf].mEglImage = image; 만든 EGL Image를 mEGLSlots에 저장한다.
11
12. 3.레이어 합성과 화면 출력
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
바인딩된 텍스쳐에 대한 소스를 생성한 EGL Image로 지정 한다.
(즉, 어떤 EGL Image를 사용할지 결정 함.)
if (mCurrentTexture != BufferQueue::INVALID_BUFFER_SLOT) {
status_t status = mBufferQueue->releaseBuffer(mCurrentTexture, dpy, 이전 버퍼를 release한다.
mEGLSlots[mCurrentTexture].mFence);
}
BufferQueue.cpp
status_t BufferQueue::releaseBuffer(int buf, EGLDisplay display,EGLSyncKHR fence) {
if (mSlots[buf].mBufferState == BufferSlot::ACQUIRED) {
mSlots[buf].mBufferState = BufferSlot::FREE; status를 FREE 로 만들고
}
mDequeueCondition.broadcast(); FREE 인 버퍼가 있음을 broadcast한다.
}
mCurrentTexture = buf; 현재 index를 mCurrentTexture 에 저장 한다.
mCurrentTextureBuf = mEGLSlots[buf].mGraphicBuffer;
}
12
13. 3.화면 출력 시 Buffer status 변경
BufferQueue::releaseBuffe에 의해
7 mslot의 4번 status가 FREE로 변경됨.
mSlot
출력 대기 큐에서 [0]
4번 인덱스를 mQueue의 첫번 째 요소의
삭제 mQueue 1 mSlot의 status를 FREE로 만든다.
4 [1]
[0] 2->4 X [2] F D->F
EGL Image
[1] [3] 5
2 출력할 버퍼의 인덱스를
mQueue의 처음에 넣는다. A 그래픽 버퍼 출력할 그래픽 버퍼를
[2] [4]
EGL Image로 만든다.
D->A
acquireBuffer에 의해
3 Status 를
ACQUIRED로 변경함
mEGLSlots
[0] 6
mEGLSlots이 생성한
[1] EGL Image를 가리키게 한다.
[2]
[3]
[4]
13