android 鼠标光标的图形合成原理实例探究-kb88凯时官网登录

来自:网络
时间:2024-06-09
阅读:
免费资源网,https://freexyz.cn/

引言

一直很好奇鼠标光标是如何实现的,它反映很快、延迟很小,没有受到 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 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.cpp
sp 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 设置了ehiddenecursorwindow,它们表明创建的 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, 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.cpp
status_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.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 最核心的属性mpotentialcursorcreatesurface()只是设置了这个属性,真正的使用在 surfaceflinger 渲染过程中。接着我发现,想把这个东西看明白,先需要把 android 图形合成弄清楚,这可是的庞大的工程。借张图,有兴趣的自己研究。

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 ; 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.cpp
void 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 = 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 方式合成,但可能无法正确显示。

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& 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鼠标光标图形合成的资料请关注其它相关文章!

免费资源网,https://freexyz.cn/
返回顶部
顶部
网站地图