前書き
この記事では、Android GUIシステムのためのSurfaceFlinger合成レイヤーの具体的なプロセスを分析します。合成プロセスはSFの最もコアなタスクであり、このプロセスはSFのビジネスロジック全体を貫きます。したがって、SFの合成プロセスを理解することは、Android GUIシステムをより深く理解するために不可欠です。
VSYNC合成の制御
SFのVSYNC信号による同期制御の紹介では、描画・合成処理においてVSYNC信号が果たす役割を分析しました。 SFのinitメソッドでは、合成遅延ソースをEventThreadで生成・管理すると同時に、SF MessageQueueを通じて合成遅延ソースをリッスンするためのSF用コネクションを作成し、EventThreadのリスナーコレクションに登録します。SF用のコネクションを作成し、EventThreadのリスナーコレクションに登録することで、合成遅延元がVSYNC信号を受信すると、SF MessageQueueに通知し、MessageQueueからSFにイベントが通知され、合成処理が開始できるようになります。一方、MessageQueueはSFの "秘書 "として、SFからのメッセージイベントを受信します。
void SurfaceFlinger::init() {
...
sp<VSyncSource> sfVsyncSrc = new DispSyncSource(&mPrimaryDispSync,
sfVsyncPhaseOffsetNs, false);//コンポジット遅延Vsyncソースの作成
mSFEventThread = new EventThread(sfVsyncSrc);
mEventQueue.setEventThread(mSFEventThread);//合成遅延vysncソースをSFのイベント管理に関連付ける。
...
}
//frameworks/native/services/surfaceflinger/MessageQueue.cpp
//合成遅延ソースのEventThreadは、SFのMessageQueueに関連付けられる。
void MessageQueue::setEventThread(const sp<EventThread>& eventThread)
{
mEventThread = eventThread;
//遅延ソースのSF用Connectionリスナーを作成する
mEvents = eventThread->createEventConnection();
mEventTube = mEvents->getDataChannel();//ビットチューブを取得する
//遅延ソースのEventHandlerがVSYNC信号cbを受信したときにトリガーされるLooperに、その記述子fdを追加する。_eventReceiver
mLooper->addFd(mEventTube->getFd(), 0, ALOOPER_EVENT_INPUT,
MessageQueue::cb_eventReceiver, this);
}
//このコールバック・メソッドは、LooperがConnectionに対応するBitTubeのファイル・ディスクリプタに到着するイベントをリッスンするときにトリガーされる。
int MessageQueue::cb_eventReceiver(int fd, int events, void* data) {
MessageQueue* queue = reinterpret_cast<MessageQueue *>(data);
return queue->eventReceiver(fd, events);
}
//MessageQueueがVSYNCシグナルを要求した場合、合成遅延ソースはVSYNCシグナルを受信し、このコールバックをトリガーする。
int MessageQueue::eventReceiver(int fd, int events) {
ssize_t n;
DisplayEventReceiver::Event buffer[8];
while ((n = DisplayEventReceiver::getEvents(mEventTube, buffer, 8)) > 0) {
for (int i=0 ; i<n ; i++) {
if (buffer[i].header.type == DisplayEventReceiver::DISPLAY_EVENT_VSYNC) {
#if INVALIDATE_ON_VSYNC
mHandler->dispatchInvalidate();
#else
mHandler->dispatchRefresh();
#endif
break;
}
}
}
return 1;
}
void MessageQueue::Handler::dispatchRefresh() {
if ((android_atomic_or(eventMaskRefresh, &mEventMask) & eventMaskRefresh) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::REFRESH));
}
}
void MessageQueue::Handler::dispatchInvalidate() {
if ((android_atomic_or(eventMaskInvalidate, &mEventMask) & eventMaskInvalidate) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::INVALIDATE));
}
}
void MessageQueue::Handler::dispatchTransaction() {
if ((android_atomic_or(eventMaskTransaction, &mEventMask) & eventMaskTransaction) == 0) {
mQueue.mLooper->sendMessage(this, Message(MessageQueue::TRANSACTION));
}
}
void MessageQueue::Handler::handleMessage(const Message& message) {
switch (message.what) {
case INVALIDATE:
android_atomic_and(~eventMaskInvalidate, &mEventMask);
mQueue.mFlinger->onMessageReceived(message.what);
break;
case REFRESH:
android_atomic_and(~eventMaskRefresh, &mEventMask);
mQueue.mFlinger->onMessageReceived(message.what);
break;
case TRANSACTION:
android_atomic_and(~eventMaskTransaction, &mEventMask);
mQueue.mFlinger->onMessageReceived(message.what);
break;
}
}
void SurfaceFlinger::onMessageReceived(int32_t what) {
ATRACE_CALL();
switch (what) {
case MessageQueue::TRANSACTION:
//レイヤーの可視領域ダーティ・エリアの計算に影響するレイヤーやディスプレイのプロパティの変更を処理する責任がある。
handleMessageTransaction();
break;
case MessageQueue::INVALIDATE:
handleMessageTransaction();
//メインコールはhandlePageFlipで、各レイヤーのBufferQueueから最新のバッファデータを取得し、その内容に従ってダーティ領域を更新する。
handleMessageInvalidate();
signalRefresh();//これがhandleMessageRefreshのトリガーとなる。
break;
case MessageQueue::REFRESH:
handleMessageRefresh();//合成とレンダリング出力
break;
}
}
SFのonMessageReceivedメソッドでは、TRANSACTIONイベント、INVALIDATEイベント、REFRESHイベントをそれぞれ処理します。 TRANSACTIONイベントでは、レイヤの可視領域やダーティ領域の計算に影響を与えやすい、レイヤとディスプレイの属性変更を主に処理します。INVALIDATEイベントでは、TRANSACTIONイベントの処理内容に加え、合成レイヤの最新フレームデータを取得する必要があり、同時に、REFRESHイベントの内容に従ってダーティエリアを更新する必要があるため、主に合成とレンダリング出力処理を行います。実際、TRANSACTIONイベントとREFRESHイベントの内容は、マージとレンダリング出力処理を一通り処理するINVALIDATEイベントに含まれていることがわかります。ほとんどの場合、SFのタスクはINVALIDATEイベントも処理することです。そのため、次の分析の焦点はINVALIDATEイベントから始まります。
トリガー合成のタイミング
SFの合成機構は、VSYNC信号に依存しますが、ディスプレイデバイスは、常に合成動作ではありませんが、明らかに、ディスプレイデバイスのレイヤは、任意の変更を持っていないとき、それはSFの合成をさせる必要はありませんし、すべきではない、そのようなデータの新しいフレームを描画するための最も一般的なアプリケーションとして、レイヤの内部が変更された場合にのみ、この時間は、レイヤを介して行われます。BufferQueueのコンシューマインタフェースのonFrameAvaliable通知SFは、データの新しいフレームを持って、その後、レイヤーの合成は、上記のMessageQueueであるリスナーにVSYNC信号通知の遅延ソースの合成をトリガする要求に応じて要求VSYNC信号、EventThreadに行く必要がレンダリングする必要があります。MessageQueueはSFに通知し、合成処理を逆行させます。
//frameworks/native/services/surfaceflinger/Layer.cpp
void Layer::onFrameAvailable() {
android_atomic_inc(&mQueuedFrames);//mQueuedFrames
mFlinger->signalLayerUpdate();//合成処理をスケジューリングする
}
新しいフレームのデータが準備できたので、SF経由でMessageQueueに通知して、合成処理をスケジュールします。
//
void SurfaceFlinger::signalLayerUpdate() {
mEventQueue.invalidate();
}
MessageQueueのinvalidateメソッドでVSYNCシグナルを一度リクエストします。
////frameworks/native/services/surfaceflinger/MessageQueue.cpp
void MessageQueue::invalidate() {
#if INVALIDATE_ON_VSYNC
mEvents->requestNextVsync();//VSYNCを要求する
#else
mHandler->dispatchInvalidate();
#endif
}
invalidateはrequestNextVsyncでVSYNC信号を一度要求し、これは合成遅延ソースのEventThreadを通して行われます。MessageQueueは最終的にSFに合成処理を実行するように通知します。
レイヤーと表示プロパティの変更処理
handleMessageTransactionのタスクはレイヤーとディスプレイのプロパティの変更を処理することです。レイヤーの内部にはmCurrentStateとmDrawingStateという2つのStateオブジェクトがあり、レイヤーの状態情報を保持しています。 ユーザーがレイヤーのメソッドを呼び出してサイズや透明度などの属性を変更すると、その設定値はmCurrentStateオブジェクトに保存され、mDrawingStateは現在使われている状態です。mDrawingStateは現在使われている状態であり、ユーザーの変更によってレイヤーの描画処理に影響を与えないことを目的としています。そのため、handleMessageTransactionメソッドは関連するレイヤープロパティの変更を計算し、これらの変更はその後の可視領域の計算に影響を与える可能性があります。
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::handleMessageTransaction() {
uint32_t transactionFlags = peekTransactionFlags(eTransactionMask);
if (transactionFlags) {
handleTransaction(transactionFlags);
}
}
void SurfaceFlinger::handleTransaction(uint32_t transactionFlags)
{
Mutex::Autolock _l(mStateLock);
...
transactionFlags = getTransactionFlags(eTransactionMask);
//さらなる処理
handleTransactionLocked(transactionFlags);
...
}
//frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
//現在の状態のレイヤーセットを取得する
const LayerVector& currentLayers(mCurrentState.layersSortedByZ);
const size_t count = currentLayers.size();
if (transactionFlags & eTraversalNeeded) {
for (size_t i=0 ; i<count ; i++) {//すべてのレイヤーを繰り返し処理する
const sp<Layer>& layer(currentLayers[i]);
//ハードウェアは、これらのレイヤーの最終的な合成レンダリングに責任を負う。 レイヤーのアクションフラグが取得され、アクションが必要かどうかを判断するために使用される。
uint32_t trFlags = layer->getTransactionFlags(eTransactionNeeded);
if (!trFlags) continue;
//レイヤーのdoTransactionの呼び出しは、可視領域の計算に影響する可能性のある変更を処理する。
//mVisibleRegionsDirtyをtrueに設定するだけでよい!
const uint32_t flags = layer->doTransaction(0);
if (flags & Layer::eVisibleRegion)
mVisibleRegionsDirty = true;
}
}
//ディスプレイ・デバイスの変更を処理する
if (transactionFlags & eDisplayTransactionNeeded) {
//現在の表示デバイス
const KeyedVector< wp<IBinder>, DisplayDeviceState>& curr(mCurrentState.displays);
//レイヤーの表示デバイスは
const KeyedVector< wp<IBinder>, DisplayDeviceState>& draw(mDrawingState.displays);
....
}
//トランスフォームヒントの処理
if (transactionFlags & (eTraversalNeeded|eDisplayTransactionNeeded)) {
...
}
const LayerVector& layers(mDrawingState.layersSortedByZ);
//これはレイヤーが追加されたことを意味するので、mVisibleRegionsDirtyをtrueに設定する。
if (currentLayers.size() > layers.size()) {
// layers have been added
mVisibleRegionsDirty = true;
}
// some layers might have been removed, so
// we need to update the regions they're exposing.
//layer削除された
if (mLayersRemoved) {
mLayersRemoved = false;
mVisibleRegionsDirty = true;
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(layers[i]);
if (currentLayers.indexOf(layer) < 0) {//このレイヤーが存在しない場合
// this layer is not visible anymore
// TODO: we could traverse the tree from front to back and
// compute the actual visible region
// TODO: we could cache the transformed region
const Layer::State& s(layer->getDrawingState());
Region visibleReg = s.transform.transform(
Region(Rect(s.active.w, s.active.h)));
//削除されたレイヤーの可視領域を以降の更新で無効にする
invalidateLayerStack(s.layerStack, visibleReg);
}
}
}
//変更をコミットする
commitTransaction();
}
mDrawingStateはSF合成で使用する描画状態で、mCurrentStateはSFの最新の描画状態です。
struct State {
LayerVector layersSortedByZ;
DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
};
このコレクション内のレイヤーはSFが合成に使用するレイヤーです。このコレクション内のレイヤーはSFが合成に使用しようとしているものです。別のメンバはディスプレイデバイスの状態情報を記述しており、これは実際にはMapで、キー値はデバイスのTokenで、これもIBinderで、値はディスプレイデバイスの状態を表すDisplayDeviceStateです。
handleTransactionLockedメソッドでは、まず現在の状態からLayerコレクションを取り出し、各レイヤーの属性変更を処理するDoTransaction処理を行い、後でその処理結果を解析します。mVisibleRegionsDirtyがtrueとマークされ、表示デバイスの変更が処理されますが、なぜここで処理されるかというと、表示デバイスのホットプラグに対応するためではないかと思います。前後のデバイスの状態を比較することで、デバイスが削除されたのか、追加されたのか、デバイスの他の情報が変更されたのか、などを知ることができます。< wp, sp >デバイスの追加や削除はSFのmDisplaysから行います。mDisplaysはSFが使用するディスプレイデバイスを保持するDefaultKeyedVectorです。
次のステップは、レイヤーのシステムのパフォーマンスを向上させるために使用される、トランスフォームヒントの変更に対処することです。
最後の部分はレイヤーの変更に対処することで、2つの描画合成処理の前後に、新しいレイヤーが追加されることがあります。はinvalidateLayerStackで処理されます。その後、mCurrentStateをmDrawingStateに代入するcommitTransactionによってトランザクションがサブミットされます。
void SurfaceFlinger::commitTransaction()
{
...
mDrawingState = mCurrentState;
mTransactionPending = false;
mAnimTransactionPending = false;
mTransactionCV.broadcast();
}
レイヤーのプロパティの変更
//レイヤーの変更を処理する
uint32_t Layer::doTransaction(uint32_t flags) {
const Layer::State& s(getDrawingState());
const Layer::State& c(getCurrentState());
//サイズが変更された
const bool sizeChanged = (c.requested.w != s.requested.w) ||
(c.requested.h != s.requested.h);
if (sizeChanged) {
// record the new size, form this point on, when the client request
// a buffer, it'll get the new size.
//layerのサイズに変更がある。
mSurfaceFlingerConsumer->setDefaultBufferSize(
c.requested.w, c.requested.h);
}
if (!isFixedSize()) {
const bool resizePending = (c.requested.w != c.active.w) ||
(c.requested.h != c.active.h);
if (resizePending) {
// don't let Layer::doTransaction update the drawing state
// if we have a pending resize, unless we are in fixed-size mode.
// the drawing state will be updated only once we receive a buffer
// with the correct size.
//
// in particular, we want to make sure the clip (which is part
// of the geometry state) is latched together with the size but is
// latched immediately when no resizing is involved.
flags |= eDontUpdateGeometryState;
}
}
// always set active to requested, unless we're asked not to
// this is used by Layer, which special cases resizes.
if (flags & eDontUpdateGeometryState) {
} else {
Layer::State& editCurrentState(getCurrentState());
editCurrentState.active = c.requested;
}
//アクティビティサイズも変更された
if (s.active != c.active) {
// invalidate and recompute the visible regions if needed
flags |= Layer::eVisibleRegion;
}
//レイヤーの位置、zorder、alpha,matrix,transparent region,flags,crops.変更が発生すると、シーケンスは自動的にインクリメントされる。以下は、属性が変更されたことを示す非等価表示である。
if (c.sequence != s.sequence) {
// invalidate and recompute the visible regions if needed
flags |= eVisibleRegion;
this->contentDirty = true;
// we may use linear filtering, if the matrix scales us
const uint8_t type = c.transform.getType();
mNeedsFiltering = (!c.transform.preserveRects() ||
(type >= Transform::SCALE));
}
// Commit the transaction
commitTransaction();//レイヤーの描画状態を更新する
return flags;
}
レイヤーのフレームデータを取得し、ディスプレイデバイスのダーティエリアを計算します。
void SurfaceFlinger::handleMessageInvalidate() {
ATRACE_CALL();
handlePageFlip();
}
void SurfaceFlinger::handlePageFlip()
{
Region dirtyRegion;
bool visibleRegions = false;
//レイヤーのリストを取得する
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(layers[i]);
//レイヤーのイメージを更新し、最新の表示データをmActiveBufferに取得するには、latchBufferを使用する。
const Region dirty(layer->latchBuffer(visibleRegions));
const Layer::State& s(layer->getDrawingState());
invalidateLayerStack(s.layerStack, dirty);//レイヤーに関連するデバイスの更新領域を設定する
}
mVisibleRegionsDirty |= visibleRegions;
}
翻訳を更新した後、次のステップはレイヤーの表示データを取得することです。これはレイヤーのlatchBufferメソッドを通じて取得され、レイヤーの可視領域はlatchBufferを通じて知ることができます。ダーティ・エリアはinvalidateLayerStackによって計算されます。
合成プロセス分析
合成 主要プロセス
void SurfaceFlinger::handleMessageRefresh() {
...
preComposition();
rebuildLayerStacks();
setUpHWComposer();
...
doComposition();
postComposition();
}
preComposition
コンポジットの処理は主に、別途説明するhandleMessageRefreshの中で行われます。
//合成前の前処理
void SurfaceFlinger::preComposition()
{
bool needExtraInvalidate = false;
const LayerVector& layers(mDrawingState.layersSortedByZ);
const size_t count = layers.size();
//ハードウェアはこれらのレイヤーのレンダリング合成を担当する。 現在描画されているレイヤーがフェッチされ、それぞれのレイヤーに対してonPreCompositionが呼び出され、未処理のフレームがあるかどうかが判断され、あればハードウェアが合成する。
//そして、needExtraInvalidateがtrueに設定され、追加の合成とレンダリング操作が必要であることを示す。
for (size_t i=0 ; i<count ; i++) {
if (layers[i]->onPreComposition()) {
needExtraInvalidate = true;
}
}
//未処理のフレームを持つレイヤーがある場合、別のコンポジットとレンダリング操作が必要となる。
if (needExtraInvalidate) {
signalLayerUpdate();
}
}
//合成前処理コールバックは、レイヤーにまだ未処理のキュー・フレームがあるかどうかを決定する。
bool Layer::onPreComposition() {
mRefreshPending = false;
return mQueuedFrames > 0;
}
preCompositionはコンポジットの前処理で、コンポジットの前に新しいフレームデータmQueuedFrames > 0を持つレイヤーがあるかどうかを判断します。
レイヤースタックの再構築
//デバイスの可視レイヤーセットを再構築し、各レイヤーの可視領域とダーティ領域を計算する。
void SurfaceFlinger::rebuildLayerStacks() {
// rebuild the visible layer list per screen
if (CC_UNLIKELY(mVisibleRegionsDirty)) {
ATRACE_CALL();
mVisibleRegionsDirty = false;
invalidateHwcGeometry();
const LayerVector& layers(mDrawingState.layersSortedByZ);
//各ディスプレイ・デバイスに対して、可視レイヤーを再構築する必要がある。
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
Region opaqueRegion;//不透明領域
Region dirtyRegion;//透明領域
Vector< sp<Layer> > layersSortedByZ;//可視レイヤーのセット
const sp<DisplayDevice>& hw(mDisplays[dpy]);
const Transform& tr(hw->getTransform());//デバイスの変換行列
const Rect bounds(hw->getBounds());//デバイスの表示領域
if (hw->canDraw()) {
//デバイスレイヤーの可視領域を計算する
SurfaceFlinger::computeVisibleRegions(layers,
hw->getLayerStack(), dirtyRegion, opaqueRegion);
//可視レイヤーを再構築する
const size_t count = layers.size();
for (size_t i=0 ; i<count ; i++) {
const sp<Layer>& layer(layers[i]);
const Layer::State& s(layer->getDrawingState());
//レイヤーだけが、ディスプレイ・デバイスのlayerStackにマッチし、そのデバイスに表示される。
if (s.layerStack == hw->getLayerStack()) {
//描画される領域は、レイヤーの非透過領域である。
Region drawRegion(tr.transform(
layer->visibleNonTransparentRegion));
drawRegion.andSelf(bounds);//レイヤーの可視領域が現在のデバイスのウィンドウ領域と交差する場合、ハードウェアはこれらのレイヤーを合成する責任を負う。
//可視領域と現在のデバイス領域の間に交点がある場合、そのレイヤーを表示する必要があり、可視レイヤーのセットに追加する。
if (!drawRegion.isEmpty()) {
layersSortedByZ.add(layer);
}
}
}
}
//例えば、ホーム画面には、スタートバー、アプリ、ナビゲーションバーに対応するレイヤーがあるかもしれない。
//これらのレイヤーは、layersSortedByZコレクションに格納される。
hw->setVisibleLayersSortedByZ(layersSortedByZ);
hw->undefinedRegion.set(bounds);//初期の未定義領域は、デバイスの表示領域である。
hw->undefinedRegion.subtractSelf(tr.transform(opaqueRegion));//未定義の領域=現在未定義の領域-不透明領域
hw->dirtyRegion.orSelf(dirtyRegion);//ダーティ領域を設定する
}
}
}
rebuildLayerStacks は、デバイスの可視レイヤーのセットを再構築し、各レイヤーの可視領域とダーティ領域を計算します。rebuildLayerStacksは各表示デバイスを処理し、computeVisibleRegionsによって各表示デバイスのレイヤーの可視領域とダーティ領域を計算し、レイヤーの可視領域とデバイスのウィンドウ領域の交点がそのレイヤーがデバイスで表示可能であることを示す場合、そのレイヤーをデバイスのウィンドウ領域に追加します。レイヤーの可視領域がデバイスのウィンドウ領域と交差すると、そのレイヤーはデバイスで表示できることを意味し、その可視レイヤーコレクション、layersSortedByZ に追加され、最後に setVisibleLayersSortedByZ によってコレクションがディスプレイデバイスに設定され、ディスプレイデバイスのダーティ領域が更新されます。
setUpHWComposer
void SurfaceFlinger::setUpHWComposer() {
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
mDisplays[dpy]->beginFrame();
}
HWComposer& hwc(getHwComposer());
if (hwc.initCheck() == NO_ERROR) {
// build the h/w work list
if (CC_UNLIKELY(mHwWorkListDirty)) {
mHwWorkListDirty = false;
//デバイスごとにworkListを作成する
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
sp<const DisplayDevice> hw(mDisplays[dpy]);
const int32_t id = hw->getHwcDisplayId();
if (id >= 0) {
const Vector< sp<Layer> >& currentLayers(
hw->getVisibleLayersSortedByZ());//各デバイスの可視レイヤーセットを取得する
const size_t count = currentLayers.size();
//デバイスのworkdListを作成する
if (hwc.createWorkList(id, count) == NO_ERROR) {
HWComposer::LayerListIterator cur = hwc.begin(id);
const HWComposer::LayerListIterator end = hwc.end(id);
//LayerListIterator作成されたワークリストを走査するために、特にDisplayDataを走査する。.list->hwLayers
for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
const sp<Layer>& layer(currentLayers[i]);
layer->setGeometry(hw, *cur);
if (mDebugDisableHWC || mDebugRegion || mDaltonize) {
cur->setSkip(true);
}
}
}
}
}
}
// set the per-frame data
//現在表示されているデバイスを反復処理する
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
sp<const DisplayDevice> hw(mDisplays[dpy]);
const int32_t id = hw->getHwcDisplayId();
if (id >= 0) {
//rebuildLayerStacksメソッドで設定された、デバイスの可視レイヤーコレクションを取得する。
const Vector< sp<Layer> >& currentLayers(
hw->getVisibleLayersSortedByZ());
const size_t count = currentLayers.size();
HWComposer::LayerListIterator cur = hwc.begin(id);
const HWComposer::LayerListIterator end = hwc.end(id);
//可視レイヤーの現在のフレームのデータを設定する
for (size_t i=0 ; cur!=end && i<count ; ++i, ++cur) {
/*
* update the per-frame h/w composer data for each layer
* and build the transparent region of the FB
*/
//ここで、LayerListIteratorの++この操作は、DisplayDataを繰り返し処理する。.list->hwLayers
// *curHWCLayerVersion1は抽象クラスHWCLayerを実装している。
const sp<Layer>& layer(currentLayers[i]);
layer->setPerFrameData(hw, *cur);
}
}
}
//HWCのprepareによる合成の決定
status_t err = hwc.prepare();
ALOGE_IF(err, "HWComposer::prepare failed (%s)", strerror(-err));
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
sp<const DisplayDevice> hw(mDisplays[dpy]);
hw->prepareFrame(hwc);
}
}
}
setUpHWComposerは、表示デバイスのworkListを作成し、各デバイスで表示するために出力するレイヤのフレームデータを設定し、最後にHWCの準備によって表示デバイスの可視レイヤの合成方法を決定する役割を果たします。
ワークリストの作成
//frameworks/native/services/surfaceflinger/DisplayHardware/HWComposer.cpp
//デバイスはワークリストを作成し、idはディスプレイ・デバイスのid、numLayersはディスプレイ・デバイスの可視レイヤーの数である。
status_t HWComposer::createWorkList(int32_t id, size_t numLayers) {
if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
return BAD_INDEX;
}
if (mHwc) {
//各デバイスは、ディスプレイ・デバイスを記述するDisplayDataを持つ。
DisplayData& disp(mDisplayData[id]);
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// we need space for the HWC_FRAMEBUFFER_TARGET
//バージョンがHWCの場合_DEVICE_API_VERSION_1_1代わりに、追加のhwc_layer_1_t合成されたテクスチャを保持するために使用される
numLayers++;
}
//ワークリストを初期化するのは、主にDispalyDataのhwcを初期化するためである。_display_contents_1ハードウェアは、これらのレイヤーの最終的な合成レンダリングを担当し、そのためのメモリ空間を開放する。
if (disp.capacity < numLayers || disp.list == NULL) {
size_t size = sizeof(hwc_display_contents_1_t)
+ numLayers * sizeof(hwc_layer_1_t);
free(disp.list);
//hwcの割り当て_display_contents_1_tハードウェアは、これらのレイヤーの最終的な合成レンダリングに責任を負う。
disp.list = (hwc_display_contents_1_t*)malloc(size);
disp.capacity = numLayers;
}
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
//hwc_display_contents_1内部hwLayersの最後はFrameBufferTargetで、ここに合成されたテクスチャが格納される。
//ここでは、レイヤーを取り出してframebufferTargetに割り当てている。
disp.framebufferTarget = &disp.list->hwLayers[numLayers - 1];
memset(disp.framebufferTarget, 0, sizeof(hwc_layer_1_t));
const hwc_rect_t r = { 0, 0, (int) disp.width, (int) disp.height };
//ここでは、合成されたhwcを保持するために初期化されている。_layer_1_t
//型はHWC_FRAMEBUFFER_TARGETつまり、GPUによって合成される。
disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;
disp.framebufferTarget->hints = 0;
disp.framebufferTarget->flags = 0;
disp.framebufferTarget->handle = disp.fbTargetHandle;
disp.framebufferTarget->transform = 0;
disp.framebufferTarget->blending = HWC_BLENDING_PREMULT;
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
disp.framebufferTarget->sourceCropf.left = 0;
disp.framebufferTarget->sourceCropf.top = 0;
disp.framebufferTarget->sourceCropf.right = disp.width;
disp.framebufferTarget->sourceCropf.bottom = disp.height;
} else {
disp.framebufferTarget->sourceCrop = r;
}
disp.framebufferTarget->displayFrame = r;
disp.framebufferTarget->visibleRegionScreen.numRects = 1;
disp.framebufferTarget->visibleRegionScreen.rects =
&disp.framebufferTarget->displayFrame;
disp.framebufferTarget->acquireFenceFd = -1;
disp.framebufferTarget->releaseFenceFd = -1;
disp.framebufferTarget->planeAlpha = 0xFF;
}
disp.list->retireFenceFd = -1;
disp.list->flags = HWC_GEOMETRY_CHANGED;
disp.list->numHwLayers = numLayers;
}
return NO_ERROR;
}
DisplayDataは以下のように定義されています:
struct DisplayData {
DisplayData();
~DisplayData();
uint32_t width;
uint32_t height;
uint32_t format; // pixel format from FB hal, for pre-hwc-1.1
float xdpi;
float ydpi;
nsecs_t refresh;
bool connected;
bool hasFbComp;//GLESコンポジティングをマークする
bool hasOvComp;//ハードウェア合成をマークする
size_t capacity;
//listこの表示デバイス上のすべてのレイヤーデータを含めて、レイヤーデータはhwLayers、hwc_display_contents_1この構造体は
//この構造体は、ディスプレイデバイスへの出力を記述する。.hの定義は
//リスト->hwLayers最後のレイヤーは
hwc_display_contents_1* list;
hwc_layer_1* framebufferTarget;//gup合成されたレイヤーはframebufferTargetに置かれる。
buffer_handle_t fbTargetHandle;
sp<Fence> lastRetireFence; // signals when the last set op retires
sp<Fence> lastDisplayFence; // signals when the last set op takes
// effect on screen
buffer_handle_t outbufHandle;
sp<Fence> outbufAcquireFence;
// protected by mEventControlLock
int32_t events;
};
DisplayDataのhwc_display_contents_1は、デバイスに表示されるレイヤーを格納するために使用され、この構造体は以下のように定義されます。
//この構造体は、ディスプレイ・デバイスへの出力を記述する。
typedef struct hwc_display_contents_1 {
...
uint32_t flags;
size_t numHwLayers;//レイヤーの数は
hwc_layer_1_t hwLayers[0];//デバイスに表示するために合成されるレイヤーは、hwc_layer_1_t
} hwc_display_contents_1_t;
hwc_display_contents_1は内部的に表示されるレイヤーの数を指定し、それはhwc_layer_1_t配列によって記述されます。createWorkListは、hwc_display_contents_1とその内部hwc_layer_1_t配列のメモリを確保し、表示するレイヤーの情報を保持します。createWorkListで、デバイスのバージョンがHWC_DEVICE_API_VERSION_1_1である場合、frameBufferTargetがサポートされていることを意味し、GLES合成レイヤーの情報を格納するために使用される追加のhwc_layer_1_tを作成する必要があります。このhwc_layer_1_tは、SLESコンポジットレイヤーの情報を格納するために使用され、そのコンポジットタイプはHWC_FRAMEBUFFER_TARGETとして指定され、DisplayDataにframebufferTargetとして記述されます。つまり、hwLayersはコンポジットされるレイヤーとコンポジットされるレイヤーの情報を含んでいます。
レイヤーのフレームデータ設定
workListが表示デバイスのために作られた後、それはレイヤーを保持する構造を持つだけですが、表示デバイスがどのようにレイヤーがこのデータを取得し、表示のために合成するかを知るために、各レイヤーのフレームデータも伝えなければなりません。レイヤーの最新のフレームデータはレイヤーのlatchBufferを通して取得され、mActiveBufferに格納されます。
//レイヤーの現在のフレームデータを設定する
void Layer::setPerFrameData(const sp<const DisplayDevice>& hw,
HWComposer::HWCLayerInterface& layer) {
// we have to set the visible region on every frame because
// we currently free it during onLayerDisplayed(), which is called
// after HWComposer::commit() -- every frame.
// Apply this display's projection's viewport to the visible region
// before giving it to the HWC HAL.
const Transform& tr = hw->getTransform();
Region visible = tr.transform(visibleRegion.intersect(hw->getViewport()));
layer.setVisibleRegionScreen(visible);
// NOTE: buffer can be NULL if the client never drew into this
// layer yet, or if we ran out of memory
//現在のレイヤーのバッファはHWCLayerInterfaceを通して保存される。
layer.setBuffer(mActiveBuffer);
}
//HWCLayerVersion1
//GraphicBufferを対応するhwcに保存する。_layer_1_t
virtual void setBuffer(const sp<GraphicBuffer>& buffer) {
if (buffer == 0 || buffer->handle == 0) {
getLayer()->compositionType = HWC_FRAMEBUFFER;
getLayer()->flags |= HWC_SKIP_LAYER;
getLayer()->handle = 0;
} else {
getLayer()->handle = buffer->handle;//バッファハンドル
}
}
レイヤーの合成方法の決定
status_t HWComposer::prepare() {
for (size_t i=0 ; i<mNumDisplays ; i++) {
DisplayData& disp(mDisplayData[i]);//デバイスのDisplayDataを取得する
if (disp.framebufferTarget) {//これは保留中の合成hwcを持っている_layer_1_t
// make sure to reset the type to HWC_FRAMEBUFFER_TARGET
// DO NOT reset the handle field to NULL, because it's possible
// that we have nothing to redraw (eg: eglSwapBuffers() not called)
// in which case, we should continue to use the same buffer.
LOG_FATAL_IF(disp.list == NULL);
//framebufferTargetのコンポジットタイプがHWCであることを確認する。_FRAMEBUFFER_TARGET
disp.framebufferTarget->compositionType = HWC_FRAMEBUFFER_TARGET;
}
if (!disp.connected && disp.list != NULL) {
ALOGW("WARNING: disp %d: connected, non-null list, layers=%d",
i, disp.list->numHwLayers);
}
mLists[i] = disp.list;//DispalayDataをフェッチするリストは、mListsの1つのコピーに格納される。_display_contents_1
if (mLists[i]) {
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
mLists[i]->outbuf = disp.outbufHandle;
mLists[i]->outbufAcquireFenceFd = -1;
} else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// garbage data to catch improper use
mLists[i]->dpy = (hwc_display_t)0xDEADBEEF;
mLists[i]->sur = (hwc_surface_t)0xDEADBEEF;
} else {
mLists[i]->dpy = EGL_NO_DISPLAY;
mLists[i]->sur = EGL_NO_SURFACE;
}
}
}
//バッファはディスプレイ・デバイス用に準備され、ハードウェア合成モジュールは、どのレイヤーがハードウェア合成の準備ができているかを決定し、HWCでそれらをヒットする。_OVERLAY
//デフォルトの合成タイプはHWCである。_FRAMEBUFFER
int err = mHwc->prepare(mHwc, mNumDisplays, mLists);
ALOGE_IF(err, "HWComposer: prepare failed (%s)", strerror(-err));
if (err == NO_ERROR) {
// here we're just making sure that "skip" layers are set
// to HWC_FRAMEBUFFER and we're also counting how many layers
// we have of each type.
//
// If there are no window layers, we treat the display has having FB
// composition, because SurfaceFlinger will use GLES to draw the
// wormhole region.
//各ディスプレイ・デバイスに対して
for (size_t i=0 ; i<mNumDisplays ; i++) {
//デバイスのDisplayDataを取得する
DisplayData& disp(mDisplayData[i]);
disp.hasFbComp = false;
disp.hasOvComp = false;
//hwcをフェッチする_display_contents_1
if (disp.list) {
//ディスプレイ・デバイスの各hwcに対して_layer_1_tどちらもコンポジットタイプを決定する
for (size_t i=0 ; i<disp.list->numHwLayers ; i++) {
hwc_layer_1_t& l = disp.list->hwLayers[i];
//ALOGD("prepare: %d, type=%d, handle=%p",
// i, l.compositionType, l.handle);
if (l.flags & HWC_SKIP_LAYER) {//スキップする必要があるレイヤーは、OPENGLを使用して合成される。
l.compositionType = HWC_FRAMEBUFFER;
}
if (l.compositionType == HWC_FRAMEBUFFER) {//コンポジットタイプはHWC_FRAMEBUFFEROPENGL合成の場合
disp.hasFbComp = true;
}
if (l.compositionType == HWC_OVERLAY) {//コンポジットタイプがHWCの場合_OVERLAYそして、ハードウェア合成
disp.hasOvComp = true;
}
}
if (disp.list->numHwLayers == (disp.framebufferTarget ? 1 : 0)) {
disp.hasFbComp = true;
}
} else {
disp.hasFbComp = true;//OPENGLコンポジティングを使用してハードウェア・コンポジティングがない場合、レイヤーの最終的なコンポジット・レンダリングはハードウェアが担当する。
}
}
}
return (status_t)err;
}
prepareは、mLists配列内のすべてのディスプレイデバイスのhwc_display_contents_1を防ぎ、次にhwcハードウェアprepareメソッドを通じて、各ディスプレイデバイスのレイヤーがハードウェア合成をサポートしているかどうかを決定し、サポートしている場合はそのcompositionTypeをHWC_OVERLAYとしてマークし、デフォルトではデフォルトでは、コンポジションタイプはHWC_FRAMEBUFFERで、これはGLESによるコンポジットを意味します。最後に、この結果は、DisplayDataのhasFbCompとhasOvCompを更新するために使用されます。hasFbCompとhasOvCompは、それぞれ、GLESコンポジティングレイヤーとハードウェアコンポジティングレイヤーがあるかどうかを示します。
ドコンポジション
void SurfaceFlinger::doComposition() {
ATRACE_CALL();
const bool repaintEverything = android_atomic_and(0, &mRepaintEverything);
//同じことが各ディスプレイ・デバイスに対しても行われる
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
const sp<DisplayDevice>& hw(mDisplays[dpy]);
if (hw->canDraw()) {
// transform the dirty region into this screen's coordinate space
const Region dirtyRegion(hw->getDirtyRegion(repaintEverything));
// repaint the framebuffer (if needed)
//ソフトウェア合成を必要とするレイヤーが存在しない可能性もある。
doDisplayComposition(hw, dirtyRegion);
hw->dirtyRegion.clear();
hw->flip(hw->swapRegion);
hw->swapRegion.clear();
}
// inform the h/w that we're done compositing
hw->compositionComplete();
}
//ハードウェアはこれらのレイヤーの最終的な合成を担当し、通常のレイヤーからのデータとEGLによって合成されたデータの両方を、合成のためにハードウェア合成モジュールに送る。
postFramebuffer();
}
doCompositiontは、SLES合成が必要なレイヤーの処理を担当し、最終的にpostFramebufferを介して表示合成のためにハードウェア合成モジュールに送信されます。 GLES合成されたレイヤーはdoDisplayCompositionで処理されます。まず、その実装を見てみましょう:
void SurfaceFlinger::doDisplayComposition(const sp<const DisplayDevice>& hw,
const Region& inDirtyRegion)
{
Region dirtyRegion(inDirtyRegion);
// compute the invalid region
hw->swapRegion.orSelf(dirtyRegion);
uint32_t flags = hw->getFlags();
if (flags & DisplayDevice::SWAP_RECTANGLE) {
// we can redraw only what's dirty, but since SWAP_RECTANGLE only
// takes a rectangle, we must make sure to update that whole
// rectangle in that case
dirtyRegion.set(hw->swapRegion.bounds());
} else {
if (flags & DisplayDevice::PARTIAL_UPDATES) {
// We need to redraw the rectangle that will be updated
// (pushed to the framebuffer).
// This is needed because PARTIAL_UPDATES only takes one
// rectangle instead of a region (see DisplayDevice::flip())
dirtyRegion.set(hw->swapRegion.bounds());
} else {
// we need to redraw everything (the whole screen)
dirtyRegion.set(hw->bounds());
hw->swapRegion = dirtyRegion;
}
}
if (CC_LIKELY(!mDaltonize)) {
//キーポイント1 レイヤーの合成
doComposeSurfaces(hw, dirtyRegion);
} else {
RenderEngine& engine(getRenderEngine());
engine.beginGroup(mDaltonizer());
doComposeSurfaces(hw, dirtyRegion);
engine.endGroup();
}
// update the swap region and clear the dirty region
hw->swapRegion.orSelf(dirtyRegion);
// swap buffers (presentation)
//キーポイント2 合成されたテクスチャをEGLローカルウィンドウでレンダリングし、ローカルウィンドウの対応するBufferQueueをトリガーして、それを消費するようにFrameBufferSurfaceに通知する。
hw->swapBuffers(getHwComposer());
}
void SurfaceFlinger::doComposeSurfaces(const sp<const DisplayDevice>& hw, const Region& dirty)
{
RenderEngine& engine(getRenderEngine());
const int32_t id = hw->getHwcDisplayId();
HWComposer& hwc(getHwComposer());
HWComposer::LayerListIterator cur = hwc.begin(id);
const HWComposer::LayerListIterator end = hwc.end(id);
//デバイスのレイヤーセットにGLES合成を必要とするレイヤーがある場合、trueを返す。
bool hasGlesComposition = hwc.hasGlesComposition(id);
if (hasGlesComposition) {//GLESによる合成
//EGLの表示とコンテキストを設定し、合成されたテクスチャがレンダリングされるEGLのローカル・ウィンドウ・オブジェクトを設定する。
if (!hw->makeCurrent(mEGLDisplay, mEGLContext)) {
ALOGW("DisplayDevice::makeCurrent failed. Aborting surface composition for display %s",
hw->getDisplayName().string());
return;
}
...
}
/*
* and then, render the layers targeted at the framebuffer
*/
//表示デバイスの可視レイヤーセットを取得する
const Vector< sp<Layer> >& layers(hw->getVisibleLayersSortedByZ());
const size_t count = layers.size();
const Transform& tr = hw->getTransform();
if (cur != end) {
// we're using h/w composer
//これらのレイヤーを繰り返し処理する
for (size_t i=0 ; i<count && cur!=end ; ++i, ++cur) {
const sp<Layer>& layer(layers[i]);
//layerのトリミング領域のセットアップは、以下のようになる。
const Region clip(dirty.intersect(tr.transform(layer->visibleRegion)));
if (!clip.isEmpty()) {
switch (cur->getCompositionType()) {
case HWC_OVERLAY: {//現在のレイヤーがハードウェアで合成される場合、処理は必要なく、合成はハードウェアに任される。
...
break;
}
//現在のレイヤーはソフトウェアで合成する必要があり、EGLを介してテクスチャとして合成するためにdrawメソッドを呼び出す。
//GLESによってコンポジットされる必要があるレイヤーの場合、それらはすべて同じテクスチャー上に描画され、このテクスチャーのバッファは、後でswapBufferを介してデバイスのframeBufferTargetにサブミットされることに注意すべきである。
case HWC_FRAMEBUFFER: {
layer->draw(hw, clip);
break;
}
...
}
}
layer->setAcquireFence(hw, *cur);
}
} else { ... }
}
doComposeSurfacesメソッドでは、まずDisplayDeviceのmakeCurrentメソッドを通じてEGLディスプレイとコンテキストが設定され、コンポジットされるオブジェクトがDisplayDeviceのローカルウィンドウにレンダリングされます。その後、デバイスの可視レイヤーが取得され、GLESによって部分的に合成されたレイヤーのみを気にするループで走査され、drawメソッドを呼び出すことで処理されます。
//Layerソフトウェア合成を実行する
void Layer::draw(const sp<const DisplayDevice>& hw, const Region& clip) const {
onDraw(hw, clip);
}
void Layer::draw(const sp<const DisplayDevice>& hw) {
onDraw( hw, Region(hw->bounds()) );
}
void Layer::onDraw(const sp<const DisplayDevice>& hw, const Region& clip) const
{
ATRACE_CALL();
// Bind the current buffer to the GL texture, and wait for it to be
// ready for us to draw into.
//現在のBufferをGLテクスチャにバインドし、レンダリングを待つ。
status_t err = mSurfaceFlingerConsumer->bindTextureImage();
if (err != NO_ERROR) {
ALOGW("onDraw: bindTextureImage failed (err=%d)", err);
// Go ahead and draw the buffer anyway; no matter what we do the screen
// is probably going to have something visibly wrong.
}
bool blackOutLayer = isProtected() || (isSecure() && !hw->isSecure());
RenderEngine& engine(mFlinger->getRenderEngine());
if (!blackOutLayer) {
// TODO: we could be more subtle with isFixedSize()
const bool useFiltering = getFiltering() || needsFiltering(hw) || isFixedSize();
// Query the texture matrix given our current filtering mode.
float textureMatrix[16];
mSurfaceFlingerConsumer->setFilteringEnabled(useFiltering);
mSurfaceFlingerConsumer->getTransformMatrix(textureMatrix);
// Set things up for texturing.
mTexture.setDimensions(mActiveBuffer->getWidth(), mActiveBuffer->getHeight());
mTexture.setFiltering(useFiltering);
mTexture.setMatrix(textureMatrix);
//レンダリングエンジン用にレイヤーテクスチャを設定する
engine.setupLayerTexturing(mTexture);
} else {
engine.setupLayerBlackedOut();
}
//テクスチャはOPENGLを介してレンダリングされ、OPENGLはレイヤーの描画領域、テクスチャ座標などを計算する。
drawWithOpenGL(hw, clip);
engine.disableTexturing();
}
status_t GLConsumer::bindTextureImageLocked() {
...
glBindTexture(mTexTarget, mTexName);//レンダリングされたテクスチャをバインドする
if (mCurrentTexture == BufferQueue::INVALID_BUFFER_SLOT) {
...
} else {
EGLImageKHR image = mEglSlots[mCurrentTexture].mEglImage;
glEGLImageTargetTexture2DOES(mTexTarget, (GLeglImageOES)image);
while ((error = glGetError()) != GL_NO_ERROR) {
ST_LOGE("bindTextureImage: error binding external texture image %p"
": %#04x", image, error);
return UNKNOWN_ERROR;
}
}
// Wait for the new buffer to be ready.
return doGLFenceWaitLocked();
}
bindTextureImaageLocked glBindTextureではレンダリングテクスチャをバインドします。mTexNameはテクスチャIDで、このテクスチャIDはLayerの構築時に生成され、レンダリングテクスチャmTextureもLayerの構築時に初期化されます。テクスチャID mTextureNameはRenderEngineによって生成され、このテクスチャIDはLayerのコンシューマGLConsumerに渡されます。
Layer(...){
...
mFlinger->getRenderEngine().genTextures(1, &mTextureName);
mTexture.init(Texture::TEXTURE_EXTERNAL, mTextureName);
...
}
void Layer::onFirstRef() {
// Creates a custom BufferQueue for SurfaceFlingerConsumer to use
mBufferQueue = new SurfaceTextureLayer(mFlinger);//BufferQueueを作成する
//BufferQueueレイヤーの消費者は
mSurfaceFlingerConsumer = new SurfaceFlingerConsumer(mBufferQueue, mTextureName);
...
}
class SurfaceFlingerConsumer : public GLConsumer {
public:
SurfaceFlingerConsumer(const sp<BufferQueue>& bq, uint32_t tex)
: GLConsumer(bq, tex, GLConsumer::TEXTURE_EXTERNAL, false)
{}
...
}
Layerはテクスチャをレンダリングする前にmakeCurrentによってDisplayDeviceを通してEGLを設定します。
DisplayDevice::DisplayDevice(
const sp<SurfaceFlinger>& flinger,
DisplayType type,
int32_t hwcId,
bool isSecure,
const wp<IBinder>& displayToken,
const sp<DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer,
EGLConfig config)
: mFlinger(flinger),
mType(type), mHwcDisplayId(hwcId),
mDisplayToken(displayToken),
mDisplaySurface(displaySurface),//これはFrameBufferSurfaceである。
mDisplay(EGL_NO_DISPLAY),
mSurface(EGL_NO_SURFACE),
mDisplayWidth(), mDisplayHeight(), mFormat(),
mFlags(),
mPageFlipCount(),
mIsSecure(isSecure),
mSecureLayerVisible(false),
mScreenAcquired(false),
mLayerStack(NO_LAYER_STACK),
mOrientation()
{
/**
* Surfaceローカル・ウィンドウは、プロデューサーとして機能するBufferQueueを介して作成され、FrameBufferSurfaceはコンシューマーとして機能する。
* これらは同じBufferQueueを共有し、SurfaceはEGLがWindowSurfaceを作成するためのローカルウィンドウとして使用される。
* eglSwapBuffersこのメソッドは、描画されたテクスチャをANativeWindowのQueueBufferメソッドでキューにバッファリングし
* FrameBufferSurfaceHWCのOnFrameAvaliableコールバックが消費側に通知され、消費側はそのGraphicBufferを取り出し、HWCのfbPostに渡す。
* それらをディスプレイ・デバイスのDisplayDataのFramebufferTargetに設定し、その後HWCのコミットによってディスプレイ・デバイスにサブミットする。
*/
mNativeWindow = new Surface(producer, false);
ANativeWindow* const window = mNativeWindow.get();
int format;
window->query(window, NATIVE_WINDOW_FORMAT, &format);
// Make sure that composition can never be stalled by a virtual display
// consumer that isn't processing buffers fast enough. We have to do this
// in two places:
// * Here, in case the display is composed entirely by HWC.
// * In makeCurrent(), using eglSwapInterval. Some EGL drivers set the
// window's swap interval in eglMakeCurrent, so they'll override the
// interval we set here.
if (mType >= DisplayDevice::DISPLAY_VIRTUAL)
window->setSwapInterval(window, 0);
/*
* Create our display's surface
*/
EGLSurface surface;
EGLint w, h;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
//EGLSurfaceオブジェクトはSurfaceのANativeWindowを介して作成され、EGL合成からのテクスチャデータはSurfaceのBufferQueueに格納される。
surface = eglCreateWindowSurface(display, config, window, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &mDisplayWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &mDisplayHeight);
mDisplay = display;//作成されたEGLDisplayを保存する
mSurface = surface;//作成したEGLSurfaceを保存する
mFormat = format;
mPageFlipCount = 0;
...
}
//frameworks/native/services/surfaceflinger/DisplayHardware/FramebufferSurface.cpp
//EGL合成されたBufferは、最終的にこのコールバックを介してFramebufferSurfaceに通知される。
void FramebufferSurface::onFrameAvailable() {
sp<GraphicBuffer> buf;
sp<Fence> acquireFence;
status_t err = nextBuffer(buf, acquireFence);//合成されたテクスチャバッファをフェッチする
if (err != NO_ERROR) {
ALOGE("error latching nnext FramebufferSurface buffer: %s (%d)",
strerror(-err), err);
return;
}
//テクスチャバッファをHWC経由でディスプレイデバイスのバッファに設定する。正確にはDisplayDataのFramebufferTargetに設定する。
err = mHwc.fbPost(mDisplayType, acquireFence, buf);
if (err != NO_ERROR) {
ALOGE("error posting framebuffer: %d", err);
}
}
status_t FramebufferSurface::nextBuffer(sp<GraphicBuffer>& outBuffer, sp<Fence>& outFence) {
Mutex::Autolock lock(mMutex);
BufferQueue::BufferItem item;
status_t err = acquireBufferLocked(&item, 0);//BufferItemの取得
...
//対応するGrapicBufferは、フェッチされたBufferItemに基づいてフェッチされる。
mCurrentBufferSlot = item.mBuf;
//対応するスロットのバッファに移動する
mCurrentBuffer = mSlots[mCurrentBufferSlot].mGraphicBuffer;
outFence = item.mFence;
outBuffer = mCurrentBuffer;
return NO_ERROR;
}
//合成されたバッファは、DisplayDataのframeBufferTargetメンバに保存される。
int HWComposer::fbPost(int32_t id,
const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buffer) {
//ハードウェア合成モジュールのAPIはバージョン1である。.1そのときだけ、framebufferTargetをサポートする。
if (mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
return setFramebufferTarget(id, acquireFence, buffer);
} else {
acquireFence->waitForever("HWComposer::fbPost");
return mFbDev->post(mFbDev, buffer->handle);
}
}
//EGL合成されたテクスチャ・バッファを、ディスプレイ・デバイスDispDataのframebufferTargetに設定する。
status_t HWComposer::setFramebufferTarget(int32_t id,
const sp<Fence>& acquireFence, const sp<GraphicBuffer>& buf) {
if (uint32_t(id)>31 || !mAllocatedDisplayIDs.hasBit(id)) {
return BAD_INDEX;
}
//設定されるディスプレイ・デバイスのDisplayData。
DisplayData& disp(mDisplayData[id]);
if (!disp.framebufferTarget) {
// this should never happen, but apparently eglCreateWindowSurface()
// triggers a Surface::queueBuffer() on some
// devices (!?) -- log and ignore.
ALOGE("HWComposer: framebufferTarget is null");
return NO_ERROR;
}
int acquireFenceFd = -1;
if (acquireFence->isValid()) {
acquireFenceFd = acquireFence->dup();
}
// ALOGD("fbPost: handle=%p, fence=%d", buf->handle, acquireFenceFd);
//tagetハンドルをバッファのハンドルに設定する
disp.fbTargetHandle = buf->handle;
disp.framebufferTarget->handle = disp.fbTargetHandle;
//フェンスのセットアップ
disp.framebufferTarget->acquireFenceFd = acquireFenceFd;
return NO_ERROR;
}
ポストフレームバッファ
doCompositionの最後のステップは、デバイスのコンポジットされたレイヤーと、ハードウェアコンポジットが必要なレイヤーを、最終的な表示のためにハードウェアコンポジットモジュールに提出することです。これはpostFramebufferを介して行われます。
//ハードウェア合成モジュールに合成を通知する
void SurfaceFlinger::postFramebuffer()
{
...
HWComposer& hwc(getHwComposer());
if (hwc.initCheck() == NO_ERROR) {
if (!hwc.supportsFramebufferTarget()) {
// EGL spec says:
// "surface must be bound to the calling thread's current context,
// for the current rendering API."
getDefaultDisplayDevice()->makeCurrent(mEGLDisplay, mEGLContext);
}
//ハードウェア合成にディスプレイを提出する
hwc.commit();
}
}
//レイヤーデータをハードウェア合成モジュールに送信する
status_t HWComposer::commit() {
int err = NO_ERROR;
if (mHwc) {
//mListsをハードウェア合成に提出する。_display_contents_1表示デバイスが最終的に表示するレイヤーデータを保持するポインタの配列。
err = mHwc->set(mHwc, mNumDisplays, mLists);
...
}
return (status_t)err;
}
最終的に、デバイス番号はhwc_display_contents_1ポインタデータとともに、HWComposerのコミットメソッドを通じてハードウェア合成モジュールに渡されます。hwc_display_contents_1には、SLESを通じて合成されたレイヤーが含まれ、その情報はhwLayersの最後のhwc_layer_tに格納されます。_layer_l_tに格納されます。hwLayersは、ハードウェアによって合成される必要があるレイヤーを持つこともあります。いずれの場合でも、それらはハードウェア合成モジュールに一緒に送信され、ハードウェアはこれらのレイヤーの最終的な合成レンダリングを担当します。





