一直很好奇滑鼠游標是如何實現的,它反映很快、延遲很小,沒有受到 Android 顯示系統的影響。正好最近做相關的工作,跟著原始碼好好研究一下。
本文參考 Android 9.0 原始碼。
從 Input 說起
我們並不是要講 Input,只想看看滑鼠游標的繪製過程。但是,Android 將滑鼠游標的實現放到了 Input 中,這看起來也是合理的。在 Input 中,游標由類Sprite
實現。原始碼中對 Sprite
的解釋為:顯示在其他圖層之上的圖形物件。看來 Sprite
並非專為游標設計,但在原始碼中的位置表明,它在 Android 中也只為滑鼠或觸控之類的輸入裝置的游標服務。Sprite
的定義中也只提供了簡單的圖形操作。
frameworks/base/libs/input/SpriteController.h
/*
* A sprite is a simple graphical object that is displayed on-screen above other layers.
* The basic sprite class is an interface.
* The implementation is provided by the sprite controller.
*/
class Sprite : public RefBase {
protected:
Sprite() { }
virtual ~Sprite() { }
public:
enum {
// The base layer for pointer sprites.
BASE_LAYER_POINTER = 0, // reserve space for 1 pointer
// The base layer for spot sprites.
BASE_LAYER_SPOT = 1, // reserve space for MAX_POINTER_ID spots
};
/* Sets the bitmap that is drawn by the sprite.
* The sprite retains a copy of the bitmap for subsequent rendering. */
virtual void setIcon(const SpriteIcon& icon) = 0;
inline void clearIcon() {
setIcon(SpriteIcon());
}
/* Sets whether the sprite is visible. */
virtual void setVisible(bool visible) = 0;
/* Sets the sprite position on screen, relative to the sprite's hot spot. */
virtual void setPosition(float x, float y) = 0;
/* Sets the layer of the sprite, relative to the system sprite overlay layer.
* Layer 0 is the overlay layer, > 0 appear above this layer. */
virtual void setLayer(int32_t layer) = 0;
/* Sets the sprite alpha blend ratio between 0.0 and 1.0. */
virtual void setAlpha(float alpha) = 0;
/* Sets the sprite transformation matrix. */
virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0;
};
控制游標的類叫做 SpriteController
,PointerController 會使用這個類來顯示游標。這裡我們只關心游標圖形的合成,真正顯示和更新游標的方法是 SpriteController::doUpdateSprites()
。
frameworks/base/libs/input/SpriteController.cpp
void SpriteController::doUpdateSprites() {
// 從invalidatedSprites 中收集需要更新的 Sprite
Vector<SpriteUpdate> updates;
size_t numSprites;
{ // acquire lock
AutoMutex _l(mLock);
numSprites = mLocked.invalidatedSprites.size();
for (size_t i = 0; i < numSprites; i++) {
const sp<SpriteImpl>& sprite = mLocked.invalidatedSprites.itemAt(i);
updates.push(SpriteUpdate(sprite, sprite->getStateLocked()));
sprite->resetDirtyLocked();
}
mLocked.invalidatedSprites.clear();
} // release lock
// surfaces 未建立或丟失時,重新建立 surface
bool surfaceChanged = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) {
update.state.surfaceWidth = update.state.icon.bitmap.width();
update.state.surfaceHeight = update.state.icon.bitmap.height();
update.state.surfaceDrawn = false;
update.state.surfaceVisible = false;
// 建立 Surface,我們這次的關注點
update.state.surfaceControl = obtainSurface(
update.state.surfaceWidth, update.state.surfaceHeight);
if (update.state.surfaceControl != NULL) {
update.surfaceChanged = surfaceChanged = true;
}
}
}
// 如果需要,重新調整 sprites 大小
SurfaceComposerClient::Transaction t;
bool needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
......
if (update.state.surfaceWidth < desiredWidth
|| update.state.surfaceHeight < desiredHeight) {
needApplyTransaction = true;
t.setSize(update.state.surfaceControl,
desiredWidth, desiredHeight);
......
}
}
}
if (needApplyTransaction) {
t.apply();
}
// 如果需要,重畫 sprites
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) {
update.state.surfaceDrawn = false;
update.surfaceChanged = surfaceChanged = true;
}
if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn
&& update.state.wantSurfaceVisible()) {
sp<Surface> surface = update.state.surfaceControl->getSurface();
ANativeWindow_Buffer outBuffer;
......
// 使用 SKIA 畫圖
SkBitmap surfaceBitmap;
ssize_t bpr = outBuffer.stride * bytesPerPixel(outBuffer.format);
surfaceBitmap.installPixels(SkImageInfo::MakeN32Premul(outBuffer.width, outBuffer.height),
outBuffer.bits, bpr);
SkCanvas surfaceCanvas(surfaceBitmap);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
surfaceCanvas.drawBitmap(update.state.icon.bitmap, 0, 0, &paint);
if (outBuffer.width > update.state.icon.bitmap.width()) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRect(SkRect::MakeLTRB(update.state.icon.bitmap.width(), 0,
outBuffer.width, update.state.icon.bitmap.height()), paint);
}
if (outBuffer.height > update.state.icon.bitmap.height()) {
paint.setColor(0); // transparent fill color
surfaceCanvas.drawRect(SkRect::MakeLTRB(0, update.state.icon.bitmap.height(),
outBuffer.width, outBuffer.height), paint);
}
......
}
// 根據 dirty 值來設定 Surface
needApplyTransaction = false;
for (size_t i = 0; i < numSprites; i++) {
SpriteUpdate& update = updates.editItemAt(i);
bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible()
&& update.state.surfaceDrawn;
bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible;
bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible;
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
| DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) {
......
}
if (needApplyTransaction) {
status_t status = t.apply();
if (status) {
ALOGE("Error applying Surface transaction");
}
}
......
}
一次的游標的更新就會涉及到如此多的程式碼邏輯,可見UI真是不容易。其他的邏輯線不管,這次我們只關心游標的圖層。上述程式碼通過 obtainSurface()
來建立 Surface。
frameworks/base/libs/input/SpriteController.cpp
sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) {
ensureSurfaceComposerClient();
sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface(
String8("Sprite"), width, height, PIXEL_FORMAT_RGBA_8888,
ISurfaceComposerClient::eHidden |
ISurfaceComposerClient::eCursorWindow);
if (surfaceControl == NULL || !surfaceControl->isValid()) {
ALOGE("Error creating sprite surface.");
return NULL;
}
return surfaceControl;
}
這裡我們需要重點關注的是 createSurface()
方法中的引數 flags
。Sprite 中這個 flags
設定了eHidden
和eCursorWindow
,它們表明建立的 Surface 是隱藏的,並標識為 Cursor 使用。
來到 Surface
Input 中為游標建立了一個 Surface,並且標識這是一個 Cursor 使用的 Surface。之後,Surface 中會根據情形對游標圖層做特殊處理,這裡的關鍵字就是 Cursor
。
我們還是以游標圖層為主線進行跟蹤,先繼續看下createSurface()
。經過一系列的 Binder 呼叫和 Message傳遞,最終通過 SurfaceFlinger 的createLayer()
完成圖層建立。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createLayer(const String8& name, const sp<Client>& client, uint32_t w,
uint32_t h, PixelFormat format, uint32_t flags,
int32_t windowType, int32_t ownerUid, sp<IBinder>* handle,
sp<IGraphicBufferProducer>* gbp,
const sp<IBinder>& parentHandle,
const sp<Layer>& parentLayer) {
......
switch (flags & ISurfaceComposerClient::eFXSurfaceMask) {
// 普通圖層
case ISurfaceComposerClient::eFXSurfaceNormal:
result = createBufferLayer(client,
uniqueName, w, h, flags, format,
handle, gbp, &layer);
break;
// 純色圖層
case ISurfaceComposerClient::eFXSurfaceColor:
result = createColorLayer(client,
uniqueName, w, h, flags,
handle, &layer);
break;
default:
result = BAD_VALUE;
break;
}
......
// Client中通過Layer管理Surface,將建立的Layer加入到LayerStack中
result = addClientLayer(client, *handle, *gbp, layer, parentHandle, parentLayer);
if (result != NO_ERROR) {
return result;
}
mInterceptor->saveSurfaceCreation(layer);
setTransactionFlags(eTransactionNeeded);
return result;
}
createLayer()
中,游標算是普通圖層,所以僅需呼叫createBufferLayer()
來建立。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
status_t SurfaceFlinger::createBufferLayer(const sp<Client>& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
sp<IBinder>* handle, sp<IGraphicBufferProducer>* gbp, sp<Layer>* outLayer)
{
......
// 建立一個BufferLayer
sp<BufferLayer> layer = new BufferLayer(this, client, name, w, h, flags);
// 設定Buffer屬性
status_t err = layer->setBuffers(w, h, format, flags);
if (err == NO_ERROR) {
*handle = layer->getHandle(); // 獲取Layer的控制程式碼
*gbp = layer->getProducer(); // 獲取GraphicBufferProducer物件
*outLayer = layer;
}
ALOGE_IF(err, "createBufferLayer() failed (%s)", strerror(-err));
return err;
}
其中layer->setBuffers()
設定了該BufferLayer的屬性。可以看到,當申請的是一個 Cursor 圖層時,mPotentialCursor
被設定為true
,表明該 BufferLayer 作為 Cursor 使用。
frameworks/native/services/surfaceflinger/BufferLayer.cpp
status_t BufferLayer::setBuffers(uint32_t w, uint32_t h, PixelFormat format, uint32_t flags) {
......
mFormat = format;
mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false;
mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false;
mCurrentOpacity = getOpacityForFormat(format);
mConsumer->setDefaultBufferSize(w, h);
mConsumer->setDefaultBufferFormat(format);
mConsumer->setConsumerUsageBits(getEffectiveUsage(0));
return NO_ERROR;
}
SurfaceFlinger 中的 Cursor 操作
上面講到 Cursor Layer 最核心的屬性mPotentialCursor
,createSurface()
只是設定了這個屬性,真正的使用在 SurfaceFlinger 渲染過程中。接著我發現,想把這個東西看明白,先需要把 Android 圖形合成弄清楚,這可是的龐大的工程。借張圖,有興趣的自己研究。
但是,時間有限,怎麼辦?我的解決辦法就是搜尋關鍵字。搜尋關鍵字Cursor
後,可以得到一些相關的操作。SurfaceFlinger 接收到 Vsync 訊號後,會呼叫handleMessageRefresh()
來重新整理顯示。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::handleMessageRefresh() {
......
preComposition(refreshStartTime); //合成預處理
rebuildLayerStacks(); //重新構建LayerStacks
setUpHWComposer(); //更新HWComposer的圖層和屬性
doDebugFlashRegions(); //圖形繪製的debug模式
doTracing("handleRefresh");
logLayerStats();
doComposition(); //合成所有圖層
postComposition(refreshStartTime); //合成後處理
......
}
我們還是隻關心 Cursor 的操作,它位於 HWComposer 控制的圖層中。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::setUpHWComposer() {
......
// 遍歷所有的DisplayDevice,為繪製做準備
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
......
mDisplays[dpy]->beginFrame(mustRecompose);
if (mustRecompose) {
mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty;
}
}
// 設定HWC Layer
if (CC_UNLIKELY(mGeometryInvalid)) {
mGeometryInvalid = false;
for (size_t dpy=0 ; dpy<mDisplays.size() ; dpy++) {
......
for (size_t i = 0; i < currentLayers.size(); i++) {
const auto& layer = currentLayers[i];
// 嘗試建立HWC Layer,如果失敗則強制OpenGL渲染
if (!layer->hasHwcLayer(hwcId)) {
if (!layer->createHwcLayer(getBE().mHwc.get(), hwcId)) {
layer->forceClientComposition(hwcId);
continue;
}
}
// 設定HWC Layer的顯示區域、合成模式、Alpha、Order等
layer->setGeometry(displayDevice, i);
// HWC被禁止或繪製debug模式時,強制OpenGL渲染
if (mDebugDisableHWC || mDebugRegion) {
layer->forceClientComposition(hwcId);
}
......
}
// 準備HWC需要渲染的資料
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
auto& displayDevice = mDisplays[displayId];
const auto hwcId = displayDevice->getHwcDisplayId();
......
//呼叫 setPerFrameData方法
layer->setPerFrameData(displayDevice);
......
}
......
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
......
// 嘗試進行顯示
status_t result = displayDevice->prepareFrame(*getBE().mHwc);
......
}
}
其中setPerFrameData()
完成 HWComposer 的相關設定,為顯示做準備。
frameworks/native/services/surfaceflinger/BufferLayer.cpp
void BufferLayer::setPerFrameData(const sp<const DisplayDevice>& displayDevice) {
......
// 設定可見區域
auto error = hwcLayer->setVisibleRegion(visible);
......
// 設定重新整理區域
error = hwcLayer->setSurfaceDamage(surfaceDamageRegion);
......
// Sideband layers設定
if (getBE().compositionInfo.hwc.sidebandStream.get()) {
setCompositionType(hwcId, HWC2::Composition::Sideband);
error = hwcLayer->setSidebandStream(getBE().compositionInfo.hwc.sidebandStream->handle());
......
return;
}
if (mPotentialCursor) {
// Cursor layers設定
setCompositionType(hwcId, HWC2::Composition::Cursor);
} else {
// Device layers設定
setCompositionType(hwcId, HWC2::Composition::Device);
}
// 設定色彩空間
error = hwcLayer->setDataspace(mCurrentDataSpace);
if (error != HWC2::Error::None) {
ALOGE("[%s] Failed to set dataspace %d: %s (%d)", mName.string(), mCurrentDataSpace,
to_string(error).c_str(), static_cast<int32_t>(error));
}
// 獲取HDR資料並設定到HWC中
const HdrMetadata& metadata = mConsumer->getCurrentHdrMetadata();
error = hwcLayer->setPerFrameMetadata(displayDevice->getSupportedPerFrameMetadata(), metadata);
......
// 獲取渲染的資料buffer和Fence,設定到HWC中
sp<GraphicBuffer> hwcBuffer;
hwcInfo.bufferCache.getHwcBuffer(getBE().compositionInfo.mBufferSlot,
getBE().compositionInfo.mBuffer, &hwcSlot, &hwcBuffer);
auto acquireFence = mConsumer->getCurrentFence();
error = hwcLayer->setBuffer(hwcSlot, hwcBuffer, acquireFence);
......
}
我們終於找到了希望看到的mPotentialCursor
,通過這個標識告訴 HWC2 這是一個 CursorLayer。除此之外,對於 CursorLayer 的操作與 DeviceLayer 並沒有區別。所以,SurfaceFlinger 更多的是希望 HWComposer 根據 Layer 的型別進行不同處理。目前 HWC2 支援的 Layer 型別有,
- HWC2_COMPOSITION_CLIENT:不通過 HWC 硬體來合成圖層。GPU 將這類圖層合成到一個影像 Buffer 中,然後傳遞給 HWC。
- HWC2_COMPOSITION_DEVICE:使用 HWC 硬體來合成圖層。
- HWC2_COMPOSITION_SOLID_COLOR:用來處理 ColorLayer 資料,如果 HWC 不支援,則改為使用 CLIENT 方式合成。
- HWC2_COMPOSITION_CURSOR:用來處理 CursorLayer 資料,位置通過
setCursorPosition
非同步設定。如果 HWC 不支援,則改為使用 CLIENT 或 DEVICE 方式合成。 - HWC2_COMPOSITION_SIDEBAND:對於這種 Layer,需要由外部機制提供內容更新,例如電視訊號的視訊資料。如果 HWC 不支援,則改為使用 CLIENT 或 DEVICE 方式合成,但可能無法正確顯示。
Cursor Layer還有一個重要的操作,setCursorPosition()
,這個方法用來設定 Cursor 的位置,具體的實現依然在 HWComposer 中。當使用者程式更新 Surface 圖形時,SurfaceFlinger 會傳送INVALIDATE
訊息給相應的 Layer。訊息處理函式呼叫handleTransaction()
和handlePageFlip()
來更新Layer物件。handleTransaction()
用來處理 Layer 和顯示裝置的變化,它繼續呼叫handleTransactionLocked()
。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::handleTransactionLocked(uint32_t transactionFlags)
{
......
// 處理Layer的變化
if (transactionFlags & eTraversalNeeded) {
......
}
// 處理顯示裝置的變化
if (transactionFlags & eDisplayTransactionNeeded) {
processDisplayChangesLocked();
processDisplayHotplugEventsLocked();
}
// 設定transform hint
if (transactionFlags & (eDisplayLayerStackChanged|eDisplayTransactionNeeded)) {
......
}
//處理Layer的增減
if (mLayersAdded) {
......
}
if (mLayersRemoved) {
......
}
commitTransaction();
// 更新游標位置
updateCursorAsync();
}
我們找到了 Cursor 更新的地方,SurfaceFlinger 更新圖形時會同步更新游標位置。之後,在 Vsync 到來時,完成影像的更新顯示。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cpp
void SurfaceFlinger::updateCursorAsync()
{
for (size_t displayId = 0; displayId < mDisplays.size(); ++displayId) {
......
// 呼叫Layer的對應方法
for (auto& layer : displayDevice->getVisibleLayersSortedByZ()) {
layer->updateCursorPosition(displayDevice);
}
}
}
frameworks/native/services/surfaceflinger/Layer.cpp
void Layer::updateCursorPosition(const sp<const DisplayDevice>& displayDevice) {
// HWC Layer不存在或者不是Cursor Layer,不做處理
auto hwcId = displayDevice->getHwcDisplayId();
if (getBE().mHwcLayers.count(hwcId) == 0 ||
getCompositionType(hwcId) != HWC2::Composition::Cursor) {
return;
}
......
// 獲取圖層的位置
Rect bounds = reduce(win, s.activeTransparentRegion);
Rect frame(getTransform().transform(bounds));
frame.intersect(displayDevice->getViewport(), &frame);
if (!s.finalCrop.isEmpty()) {
frame.intersect(s.finalCrop, &frame);
}
auto& displayTransform(displayDevice->getTransform());
auto position = displayTransform.transform(frame);
// 呼叫HWC的方法來設定圖層位置
auto error = getBE().mHwcLayers[hwcId].layer->setCursorPosition(position.left, position.top);
}
到達 HWComposer
上面分析了許多程式碼,但真正與 Cursor 相關的並不多。CursorLayer 的真正實現還是在 HWComposer 中。但是 HWComposer 的實現是與平臺相關的,不同的平臺對 CursorLayer 的實現可能不同。效率的方式是使用一個獨立的硬體 OSD 來顯示 CursorLayer,然後通過硬體合成的方式將 CursorLayer 疊加到 UI 顯示層。使用這種方式,游標的移動效率也很高,只要改變硬體 OSD 顯示的位置即可。如果沒有獨立的硬體 OSD 來使用,就只能在標準顯示層上進行軟體疊加,或者使用 GPU 來疊加。
由於跟平臺相關的實現具有私密性,這裡不再繼續分析。