HWComposer, 又聽到了 Composer 這個名字.
前面 SurfaceComposerClient 並沒有真的去 compose, 這個傢伙會不會有所不同呢?
我們先看一下它的建構子, 這邊帶入的 flinger 跟 handler 其實都是 SurfaceFlinger
HWComposer::HWComposer(
const sp<SurfaceFlinger>& flinger,
EventHandler& handler)
: mFlinger(flinger),
mFbDev(0), mHwc(0), mNumDisplays(1),
mCBContext(new cb_context),
mEventHandler(handler),
mDebugForceFakeVSync(false)
{
for (size_t i =0 ; i<MAX_HWC_DISPLAYS ; i++) {
mLists[i] = 0;
}
for (size_t i=0 ; i<HWC_NUM_PHYSICAL_DISPLAY_TYPES ; i++) {
mLastHwVSync[i] = 0;
mVSyncCounts[i] = 0;
}
bool needVSyncThread = true;
// Note: some devices may insist that the FB HAL be opened before HWC.
int fberr = loadFbHalModule();
loadHwcModule();
if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// close FB HAL if we don't needed it.
// FIXME: this is temporary until we're not forced to open FB HAL
// before HWC.
framebuffer_close(mFbDev);
mFbDev = NULL;
}
// these display IDs are always reserved
for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
mAllocatedDisplayIDs.markBit(i);
}
if (mHwc) {
ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
(hwcApiVersion(mHwc) >> 24) & 0xff,
(hwcApiVersion(mHwc) >> 16) & 0xff);
if (mHwc->registerProcs) {
mCBContext->hwc = this;
mCBContext->procs.invalidate = &hook_invalidate;
mCBContext->procs.vsync = &hook_vsync;
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
mCBContext->procs.hotplug = &hook_hotplug;
else
mCBContext->procs.hotplug = NULL;
memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
mHwc->registerProcs(mHwc, &mCBContext->procs);
}
// don't need a vsync thread if we have a hardware composer
needVSyncThread = false;
// always turn vsync off when we start
eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
// the number of displays we actually have depends on the
// hw composer version
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
// 1.3 adds support for virtual displays
mNumDisplays = MAX_HWC_DISPLAYS;
} else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// 1.1 adds support for multiple displays
mNumDisplays = NUM_BUILTIN_DISPLAYS;
} else {
mNumDisplays = 1;
}
}
if (mFbDev) {
ALOG_ASSERT(!(mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)),
"should only have fbdev if no hwc or hwc is 1.0");
DisplayData& disp(mDisplayData[HWC_DISPLAY_PRIMARY]);
disp.connected = true;
disp.format = mFbDev->format;
DisplayConfig config = DisplayConfig();
config.width = mFbDev->width;
config.height = mFbDev->height;
config.xdpi = mFbDev->xdpi;
config.ydpi = mFbDev->ydpi;
config.refresh = nsecs_t(1e9 / mFbDev->fps);
disp.configs.push_back(config);
disp.currentConfig = 0;
} else if (mHwc) {
// here we're guaranteed to have at least HWC 1.1
ALOGD()
for (size_t i =0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
queryDisplayProperties(i);
}
}
if (needVSyncThread) {
// we don't have VSYNC support, we need to fake it
ALOGE("needVSyncThread, create VSyncThread");
mVSyncThread = new VSyncThread(*this);
}
}
◢▆▅▄▃崩╰(〒皿〒)╯潰▃▄▅▇◣
就不能簡單一點么?
好的我們還是一行一行乖乖地分析:
前面的兩個初始化現在不知道功能是啥, 暫且跳過
needVSyncThread 表示需要開啟 VSync, 跟上一篇的 DispSync 有關!
接下來使用 loadFbHalModule() 開啟 FB HAL.
這個 FB (frameBuffer) 也和 GraphicBuffer 息息相關,
理論上所有繪圖的動作都是交給 GPU 去做的.
每個 APP 都會拿到自己的 Layer 去繪圖,
並且要把每一張圖都疊加 (compose) 在一起輸出到螢幕, 這塊顯示用的 buffer 就是 frameBuffer 了
因為這個特性, 在 HWComposer 去啟動 FrameBuffer HAL 也是相當合情合理的.
展開看一下:
// Load and prepare the FB HAL, which uses the gralloc module. Sets mFbDev.
int HWComposer::loadFbHalModule()
{
hw_module_t const* module;
int err = hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module);
if (err != 0) {
ALOGE("%s module not found", GRALLOC_HARDWARE_MODULE_ID);
return err;
}
return framebuffer_open(module, &mFbDev);
}
static inline int framebuffer_open(const struct hw_module_t* module,
struct framebuffer_device_t** device) {
return module->methods->open(module,
GRALLOC_HARDWARE_FB0, (struct hw_device_t**)device);
}
這邊每個 vendor 的作法都不盡相同, 就不列上 HAL 的 code 了
loadFbHalModule 可以取得 HAL 的接口 mFbDev
再來是讀取 HWComposer 的 HAL:
// Load and prepare the hardware composer module. Sets mHwc.
void HWComposer::loadHwcModule()
{
hw_module_t const* module;
if (hw_get_module(HWC_HARDWARE_MODULE_ID, &module) != 0) {
ALOGE("%s module not found", HWC_HARDWARE_MODULE_ID);
return;
}
int err = hwc_open_1(module, &mHwc);
if (err) {
ALOGE("%s device failed to initialize (%s)",
HWC_HARDWARE_COMPOSER, strerror(-err));
return;
}
if (!hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_0) ||
hwcHeaderVersion(mHwc) < MIN_HWC_HEADER_VERSION ||
hwcHeaderVersion(mHwc) > HWC_HEADER_VERSION) {
ALOGE("%s device version %#x unsupported, will not be used",
HWC_HARDWARE_COMPOSER, mHwc->common.version);
hwc_close_1(mHwc);
mHwc = NULL;
return;
}
}
static inline int hwc_open_1(const struct hw_module_t* module,
hwc_composer_device_1_t** device) {
return module->methods->open(module,
HWC_HARDWARE_COMPOSER, (struct hw_device_t**)device);
}
IMX 的 HAL 行為:
1.) 再去開啟 FSL HW module, 把工作轉交給該 Module
2.) 啟動 HAL 的 VSyncThread, 這個 VSyncThread 會依據 primary display 是否連接上, 來決定是否要啟動 FakeVSync
如果沒有連上 Display, 則會使用 FakeVSync (睡一段時間起來)
反之則會呼叫 ioctl (MXCFB_WAIT_FOR_VSYNC)
無論哪兩者都會再去呼叫 mCtx->m_callback->vsync
這個 mCtx->m_callback 是透過 hwc_registerProcs 這個 API 註冊進來的
HWComposer 會呼叫 registerProcs 將 vsync call back 設置為 hook_vsync
hook_vsync 又會去呼叫到 mEventHandler.onVSyncReceived
最後 SurfaceFlinger 會依照螢幕是否開啟來決定是否要啟動 VSyncThread
從 log 上面可以看到在 vsync 有 enable 的時候每次來 vsync 的時間大概是 16 ms 左右, 也就是 FPS 大約是 60 HZ
回到 HWComposer 的建構子:
if (mFbDev && mHwc && hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// close FB HAL if we don't needed it.
// FIXME: this is temporary until we're not forced to open FB HAL
// before HWC.
framebuffer_close(mFbDev);
mFbDev = NULL;
}
// these display IDs are always reserved
for (size_t i=0 ; i<NUM_BUILTIN_DISPLAYS ; i++) {
mAllocatedDisplayIDs.markBit(i);
}
讀取完 FrameBuffer 跟 HWComposer 的 HAL module 之後,
針對 HWC API > 1.1 的版本會將 FrameBuffer HAL 關閉,
這應該是因為將它整合到 HWComposer 的相關操作了, 後面再找看看在哪裡!
這邊將 mAllocatedDisplayIDs 的 0, 1 bit 先標記起來, 定義為 primary & external 的 display
/* Display types and associated mask bits. */
enum {
HWC_DISPLAY_PRIMARY = 0,
HWC_DISPLAY_EXTERNAL = 1, // HDMI, DP, etc.
HWC_DISPLAY_VIRTUAL = 2,
HWC_NUM_PHYSICAL_DISPLAY_TYPES = 2,
HWC_NUM_DISPLAY_TYPES = 3,
};
再往下進行 HWComposer HAL module 的 callback function 註冊:
if (mHwc) {
ALOGI("Using %s version %u.%u", HWC_HARDWARE_COMPOSER,
(hwcApiVersion(mHwc) >> 24) & 0xff,
(hwcApiVersion(mHwc) >> 16) & 0xff);
if (mHwc->registerProcs) {
mCBContext->hwc = this;
mCBContext->procs.invalidate = &hook_invalidate;
mCBContext->procs.vsync = &hook_vsync;
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1))
mCBContext->procs.hotplug = &hook_hotplug;
else
mCBContext->procs.hotplug = NULL;
memset(mCBContext->procs.zero, 0, sizeof(mCBContext->procs.zero));
mHwc->registerProcs(mHwc, &mCBContext->procs);
}
// don't need a vsync thread if we have a hardware composer
needVSyncThread = false;
// always turn vsync off when we start
eventControl(HWC_DISPLAY_PRIMARY, HWC_EVENT_VSYNC, 0);
// the number of displays we actually have depends on the
// hw composer version
if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_3)) {
// 1.3 adds support for virtual displays
mNumDisplays = MAX_HWC_DISPLAYS;
} else if (hwcHasApiVersion(mHwc, HWC_DEVICE_API_VERSION_1_1)) {
// 1.1 adds support for multiple displays
mNumDisplays = NUM_BUILTIN_DISPLAYS;
} else {
mNumDisplays = 1;
}
}
一開始透過 eventControl 關閉自身的 VSync thread 與 HAL 的 VSync thread.
因為這裡把 needVSyncThread 設置為 false, 所以實際上是不會生成自身的 VSync thread 的.
順帶一提, 如果沒有 HWComposer HAL的支援時, 我們需要 Fake 一個 VSync 出來, 其實跟 HWComposer HAL 做的 Fake 是一樣的, 我們看一下:
如果 mEnabled 為 true, 則每隔 mRefreshPeriod 的時間後會發出一次 onVSyncReceived 給 SurfaceFlinger
threadLoop 內容如下:
bool HWComposer::VSyncThread::threadLoop() {
{ // scope for lock
Mutex::Autolock _l(mLock);
while (!mEnabled) {
mCondition.wait(mLock);
}
}
const nsecs_t period = mRefreshPeriod;
const nsecs_t now = systemTime(CLOCK_MONOTONIC);
nsecs_t next_vsync = mNextFakeVSync;
nsecs_t sleep = next_vsync - now;
if (sleep < 0) {
// we missed, find where the next vsync should be
sleep = (period - ((now - next_vsync) % period));
next_vsync = now + sleep;
}
mNextFakeVSync = next_vsync + period;
struct timespec spec;
spec.tv_sec = next_vsync / 1000000000;
spec.tv_nsec = next_vsync % 1000000000;
int err;
do {
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL);
} while (err<0 && errno == EINTR);
if (err == 0) {
mHwc.mEventHandler.onVSyncReceived(0, next_vsync);
}
return true;
}
中間的計算式意義如下:
1.) 當 next_vsync >= now, 則 sleep 到 next_vsync 時間, 更新下一次 VSync 到來時間 (+peroid)
2.) 當 next_vsync < now, 則先更新 next_vsync 到相鄰最近的一次 peroid time.
(假設原先的 mNextFakeVSync = 5, now = 17, period = 10,
我們知道最近一次的 Vsync 是 25, 計算方式如下:
Step 1.) 先算出最靠近 now 的 next_vsync 間隔所需要的sleep time
sleep = 10 - (17-5)%10 = 10 - 2 = 8.
Step 2.) next_vsync即為 now + sleep
next_vsync = 17 + 8 = 25.
接下來依據 HWComposer HAL 的可支援性設置 mNumDisplays,
這會在後面呼叫 HAL 時持續的使用到.
最後呼叫 queryDisplayProperties() 帶入 primary & external,
這個 API 在 hotplug 的時候也會呼叫到, 用來獲取 display 的參數,
並把 mDisplayData.connected 設置為 true
HWComposer 的初始化做完了, 但還沒開始做事呢.
再回到 SurfaceFlinger::init()
生成一個 RendorEngine, 這裡會依據你的 version 來生成 GLESXXRenderEngine()
跟 EGL 相關的操作暫且跳過, 往下進入 Display 的初始化:
...
// initialize our non-virtual displays
for (size_t i=0 ; i<DisplayDevice::NUM_BUILTIN_DISPLAY_TYPES ; i++) {
DisplayDevice::DisplayType type((DisplayDevice::DisplayType)i);
// set-up the displays that are already connected
if (mHwc->isConnected(i) || type==DisplayDevice::DISPLAY_PRIMARY) {
// All non-virtual displays are currently considered secure.
bool isSecure = true;
createBuiltinDisplayLocked(type);
wp<IBinder> token = mBuiltinDisplays[i];
sp<IGraphicBufferProducer> producer;
sp<IGraphicBufferConsumer> consumer;
BufferQueue::createBufferQueue(&producer, &consumer,
new GraphicBufferAlloc());
sp<FramebufferSurface> fbs = new FramebufferSurface(*mHwc, i,
consumer);
int32_t hwcId = allocateHwcDisplayId(type);
sp<DisplayDevice> hw = new DisplayDevice(this,
type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
fbs, producer,
mRenderEngine->getEGLConfig());
if (i > DisplayDevice::DISPLAY_PRIMARY) {
// FIXME: currently we don't get blank/unblank requests
// for displays other than the main display, so we always
// assume a connected display is unblanked.
ALOGD("marking display %zu as acquired/unblanked", i);
hw->setPowerMode(HWC_POWER_MODE_NORMAL);
}
mDisplays.add(token, hw);
}
}
...
HWComposer 前面在 queryDisplayProperties 時已經將 primary & external 都設置為 connected,
下面依序展開:
/*
生成 BBinder 並賦值給 mBuiltinDisplays
在 Binder 的設計架構下, BBinder 一方是 Service, 負責接收資訊,
後面一定會看到有人生成 BpBinder 來傳送資訊
*/
void SurfaceFlinger::createBuiltinDisplayLocked(DisplayDevice::DisplayType type) {
mBuiltinDisplays[type] = new BBinder();
DisplayDeviceState info(type);
// All non-virtual displays are currently considered secure.
info.isSecure = true;
mCurrentState.displays.add(mBuiltinDisplays[type], info);
}
這邊的 mCurrentState 是一個 State 的結構, 叫做 State 的人非常多, 這邊的定義如下:
struct State {
LayerVector layersSortedByZ;
DefaultKeyedVector< wp<IBinder>, DisplayDeviceState> displays;
};
可以看到它由一個 LayerVector 跟 DefaultKeyedVector 所組成
在 createBuiltinDisplayLocked 中, 還生成了一個 DisplayDeviceState 的 info 變數, 拿來記錄 primary & external 的資訊:
struct DisplayDeviceState {
DisplayDeviceState();
DisplayDeviceState(DisplayDevice::DisplayType type);
bool isValid() const { return type >= 0; }
bool isMainDisplay() const { return type == DisplayDevice::DISPLAY_PRIMARY; }
bool isVirtualDisplay() const { return type >= DisplayDevice::DISPLAY_VIRTUAL; }
DisplayDevice::DisplayType type;
sp<IGraphicBufferProducer> surface;
uint32_t layerStack;
Rect viewport;
Rect frame;
uint8_t orientation;
uint32_t width, height;
String8 displayName;
bool isSecure;
};
SurfaceFlinger::DisplayDeviceState::DisplayDeviceState(DisplayDevice::DisplayType type)
: type(type), layerStack(DisplayDevice::NO_LAYER_STACK), orientation(0), width(0), height(0) {
viewport.makeInvalid();
frame.makeInvalid();
}
接下來又看到了熟悉的 BufferQueue !
上一次分析它是在前面使用者透過 SurfaceComposerClient 來建立 Layer 的時候
這裡 createBufferQueue 的時候還自帶了一個 allocator
(不過實際上應該可以也帶NULL才對...
我的印象中 Binder 如果發現 IPC 的對象是自己的話不會去多占一個通訊位子 XD
也許只是為了簡化流程吧)
它把 consumer 轉交給了 FramebufferSurface, 從前面的經驗我們可以知道 FramebufferSurface 應該會繼承 ConsumerBase 並且去實作 onFrameAvailable
它的建構子如下:
FramebufferSurface::FramebufferSurface(HWComposer& hwc, int disp,
const sp<IGraphicBufferConsumer>& consumer) :
ConsumerBase(consumer),
mDisplayType(disp),
mCurrentBufferSlot(-1),
mCurrentBuffer(0),
mHwc(hwc)
{
mName = "FramebufferSurface";
mConsumer->setConsumerName(mName);
int usage = GRALLOC_USAGE_HW_FB | GRALLOC_USAGE_HW_RENDER |
GRALLOC_USAGE_HW_COMPOSER;
if(disp != 0) {
usage |= GRALLOC_USAGE_HW_FBX;
}
mConsumer->setConsumerUsageBits(usage);
mConsumer->setDefaultBufferFormat(mHwc.getFormat(disp));
mConsumer->setDefaultBufferSize(mHwc.getWidth(disp), mHwc.getHeight(disp));
mConsumer->setDefaultMaxBufferCount(NUM_FRAMEBUFFER_SURFACE_BUFFERS);
}
帶入了 HWComposer, display_id, 以及 consumer
consumer 再透過 ConsumerBase 跟 BufferQueueCore 串在一起, 這邊不再贅述
繼續往下看,
int32_t hwcId = allocateHwcDisplayId(type); 對於 BUILT_IN_DISPLAY 來說就是再度回傳 type
這裡將剛剛還沒用上的 producer 轉交給了 DisplayDevice
...
sp<DisplayDevice> hw = new DisplayDevice(this,
type, hwcId, mHwc->getFormat(hwcId), isSecure, token,
fbs, producer,
mRenderEngine->getEGLConfig());
...
// 建構子如下:
DisplayDevice::DisplayDevice(
const sp<SurfaceFlinger>& flinger,
DisplayType type,
int32_t hwcId,
int format,
bool isSecure,
const wp<IBinder>& displayToken,
const sp<DisplaySurface>& displaySurface,
const sp<IGraphicBufferProducer>& producer,
EGLConfig config)
: lastCompositionHadVisibleLayers(false),
mFlinger(flinger),
mType(type), mHwcDisplayId(hwcId),
mDisplayToken(displayToken),
mDisplaySurface(displaySurface),
mDisplay(EGL_NO_DISPLAY),
mSurface(EGL_NO_SURFACE),
mDisplayWidth(), mDisplayHeight(), mFormat(),
mFlags(),
mPageFlipCount(),
mIsSecure(isSecure),
mSecureLayerVisible(false),
mLayerStack(NO_LAYER_STACK),
mOrientation(),
mPowerMode(HWC_POWER_MODE_OFF),
mActiveConfig(0)
{
mNativeWindow = new Surface(producer, false);
ANativeWindow* const window = mNativeWindow.get();
/*
* Create our display's surface
*/
EGLSurface surface;
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (config == EGL_NO_CONFIG) {
config = RenderEngine::chooseEglConfig(display, format);
}
surface = eglCreateWindowSurface(display, config, window, NULL);
eglQuerySurface(display, surface, EGL_WIDTH, &mDisplayWidth);
eglQuerySurface(display, surface, EGL_HEIGHT, &mDisplayHeight);
if (mType >= DisplayDevice::DISPLAY_VIRTUAL) {
// reset virtual display buffer format.
// because it is changed by eglCreateWindowSurface().
native_window_set_buffers_format(window, format);
window->setSwapInterval(window, 0);
}
mConfig = config;
mDisplay = display;
mSurface = surface;
mFormat = format;
mPageFlipCount = 0;
mViewport.makeInvalid();
mFrame.makeInvalid();
// virtual displays are always considered enabled
mPowerMode = (mType >= DisplayDevice::DISPLAY_VIRTUAL) ?
HWC_POWER_MODE_NORMAL : HWC_POWER_MODE_OFF;
// Name the display. The name will be replaced shortly if the display
// was created with createDisplay().
switch (mType) {
case DISPLAY_PRIMARY:
mDisplayName = "Built-in Screen";
break;
case DISPLAY_EXTERNAL:
mDisplayName = "HDMI Screen";
break;
default:
mDisplayName = "Virtual Screen"; // e.g. Overlay #n
break;
}
// initialize the display orientation transform.
setProjection(DisplayState::eOrientationDefault, mViewport, mFrame);
#ifdef EGL_ANDROID_swap_rectangle
// only enable swap rectange in PRIMARY and EXTERNAL display, hwcomposer
// has problem to support swap rectange in Virtual display.
if (eglSetSwapRectangleANDROID(display, surface,
0, 0, mDisplayWidth, mDisplayHeight) == EGL_TRUE && mType <= DISPLAY_EXTERNAL) {
// This could fail if this extension is not supported by this
// specific surface (of config)
mFlags |= SWAP_RECTANGLE;
}
// when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE
// choose PARTIAL_UPDATES, which should be more efficient
if (mFlags & PARTIAL_UPDATES)
mFlags &= ~SWAP_RECTANGLE;
#endif
}
還記得前面 BootAnimation 透過 SurfaceControl 建立了一個 Surface 嗎?
DisplayDevice 自身的 mNativeWindow 也同樣是一個 Surface 類型
幾個重要的成員變數如下:
mDisplayToken => 前面生成的 Binder 對象
mDisplaySurface => FramebufferSurface 對象 (consumer)
mNativeWindow => Surface 對象 (producer)
mSurface => EGLSurface, 會由它自己的 Surface 透過 producer 取得 GraphicBuffer
對於 BootAnimation 來說, producer 是它自己, consumer 是 SurfaceFlinger.
它繪製開機動畫提供給 SurfaceFlinger 使用.
對於 SurfaceFlinger 來說, producer 是 HWComposer, 它負責把屬於這個 Display 的圖形繪製成一張, 並呈現到對應的 FrameBuffer (螢幕)
生成後的 DisplayDevice 被加到了 mDisplay 這個 KeyedVector 中, 方便後續管理使用.
這個 EventControlThread 只有專心做一件事情, 就是來控制 VSync 是否要啟動.