一直很好奇鼠标光标是如何实现的,它反映很快、延迟很小,没有受到 Android 显示系统的影响。正好最近做相关的工作,跟着源码好好研究一下。从 Input 说起
本文参考 Android 9.0 源码。
我们并不是要讲 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.cppvoid SpriteController::doUpdateSprites() {
// 从invalidatedSprites 中收集需要更新的 Sprite
Vector updates;
size_t numSprites;
{ // acquire lock
AutoMutex _l(mLock);
numSprites = mLocked.invalidatedSprites.size();
for (size_t i = 0;
i < numSprites;
i++) {
const sp& 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 = 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.cppsp SpriteController::obtainSurface(int32_t width, int32_t height) {
ensureSurfaceComposerClient();
sp 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.cppstatus_t SurfaceFlinger::createLayer(const String8& name, const sp& client, uint32_t w,
uint32_t h, PixelFormat format, uint32_t flags,
int32_t windowType, int32_t ownerUid, sp* handle,
sp* gbp,
const sp& parentHandle,
const sp& 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.cppstatus_t SurfaceFlinger::createBufferLayer(const sp& client,
const String8& name, uint32_t w, uint32_t h, uint32_t flags, PixelFormat& format,
sp* handle, sp* gbp, sp* outLayer)
{
......
// 创建一个BufferLayer
sp 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.cppstatus_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.cppvoid SurfaceFlinger::handleMessageRefresh() {
......
preComposition(refreshStartTime);
//合成预处理
rebuildLayerStacks();
//重新构建LayerStacks
setUpHWComposer();
//更新HWComposer的图层和属性
doDebugFlashRegions();
//图形绘制的debug模式
doTracing("handleRefresh");
logLayerStats();
doComposition();
//合成所有图层
postComposition(refreshStartTime);
//合成后处理
......
}
我们还是只关心 Cursor 的操作,它位于 HWComposer 控制的图层中。
frameworks/native/services/surfaceflinger/SurfaceFlinger.cppvoid SurfaceFlinger::setUpHWComposer() {
......
// 遍历所有的DisplayDevice,为绘制做准备
for (size_t dpy=0 ;
dpybeginFrame(mustRecompose);
if (mustRecompose) {
mDisplays[dpy]->lastCompositionHadVisibleLayers = !empty;
}
}// 设置HWC Layer
if (CC_UNLIKELY(mGeometryInvalid)) {
mGeometryInvalid = false;
for (size_t dpy=0 ;
dpyhasHwcLayer(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.cppvoid BufferLayer::setPerFrameData(const sp& 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(error));
}// 获取HDR数据并设置到HWC中
const HdrMetadata& metadata = https://www.it610.com/article/mConsumer->getCurrentHdrMetadata();
error = hwcLayer->setPerFrameMetadata(displayDevice->getSupportedPerFrameMetadata(), metadata);
......
// 获取渲染的数据buffer和Fence,设置到HWC中
sp 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 方式合成,但可能无法正确显示。
setCursorPosition()
,这个方法用来设置 Cursor 的位置,具体的实现依然在 HWComposer 中。当用户进程更新 Surface 图形时,SurfaceFlinger 会发送INVALIDATE
消息给相应的 Layer。消息处理函数调用handleTransaction()
和handlePageFlip()
来更新Layer对象。handleTransaction()
用来处理 Layer 和显示设备的变化,它继续调用
handleTransactionLocked()
。frameworks/native/services/surfaceflinger/SurfaceFlinger.cppvoid 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.cppvoid 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.cppvoid Layer::updateCursorPosition(const sp& 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 来叠加。
由于跟平台相关的实现具有私密性,这里不再继续分析。
参考文档: Android GUI系统之SurfaceFlinger)
Android SurfaceFlinger学习-HWComposer Composition工作流程
【Android 鼠标光标的图形合成】移动端显示技术杂谈
推荐阅读
- android-8~23 View.java - dispatchTouchEvent源码
- android-23 View.java - dispatchTouchEvent源码
- Android Studio 插件
- Android 6.0 闪光灯的使用
- Android下载更新代码
- android 常用URI
- Android MVP + 娉涘瀷锛屽疄鐜颁簡鍙嬪ソVP浜や簰鍙夾ctivity娼滃湪鐨勫唴瀛樻硠闇茬殑浼樺寲
- Android问题-新电脑新系统WIN764位上安装简版本的XE8提示“Unit not found: 'System'”
- react native android 开发,基础配置笔记。