Android模拟器图形绘原理(二十二)(1)

对于EGL,GLES1.1和GLES2.0的模拟这里会通过QEMU Pipe的方式传输到模拟器。在Android层中的实现,通过将上层的指令转化为一个通用的协议流,然后通过一个叫做QEMU PIPE的高速通道来进行传输,这个管道是通过内核驱动来实现,提供了高速的带宽,可以非常高效的进行读写。当数据通过流写入到设备文件中,然后驱动从中拿到数据之后。绘制指令协议流被模拟器读取之后。

  • 模拟器到RenderThread

模拟器接收到指令协议流之后并没有做改变,直接将指令导到Render相关类。

  • Android模拟器的指令转化

模拟器指令转化

Android模拟器实现多个转化的库,实现了上层的EGL,GLES。将相应的函数调用转化为正确的宿主机的桌面API调用。

GLX(Linux),AGL(OS X),WGL(Windows)。OpenGL 2.0来模拟GLES1.1,GLES2.0.

Android系统到模拟器

在Goldfish-openGL下提供了对于EGL,GLES1.1,GLES2.0的相应的编码类,对于其中实现的每一个方法获取到当前gl_encoder_context持有的IOStream,来将数据写入到流之中来进行通信。对于Android系统和模拟器之间的连接是通过HostConnection来实现的。其中的通信实现采用的是QemuPipe。

Android模拟器实现了一种特殊的虚拟设备类来提供宿主系统和模拟器之间非常快速的通信渠道。该种通道的打开连接方式。

  • 首先打开/dev/qemu_pipe设备来进行读和写操作,从Linux3.10开始,设备被重新命名为/dev/goldfish_pipe,但是和之前的操作还是一样的。

  • 提供一个零结尾的字符创描述我们所要连接的服务。

  • 然后通过简单的读写操作便可以和其进行通信。

fd = open(“/dev/qemu_pipe”, O_RDWR);

const char* pipeName = “”;

ret = write(fd, pipeName, strlen(pipeName)+1);

if (ret < 0) {

//error

}

… ready to go

这里的pipeName是要使用的服务名程,这里支持的服务有

  • tcp:

提供一个非内部模拟器的NAT router,我们只能使用这个socket进行读写,接受,不能够进行连接非本地socket。

  • unix:

打开一个Unix域socket在主机上

  • opengles

连接到OpenGL ES模拟进程,现在这个实现等于连接tcp:22468,但是未来可能会改变。

  • qemud

连接到qemud服务在模拟器内,这个取代了老版本中通过/dev/ttys1的连接方式.

在内核中代码,向外提供了一个对于qemu_pipe,其中包含了我们如何与其进行交互。

由于QEMU Pipe发送数据的时候使用的是裸包,其速度要比TCP的方式快很多。

通信协议的实现

对于指令的传输,要对指令进行编解码。emugen,通过这个工具可以进行编码解码类的生成。在GLES1.1,GLES2.0,EGL之中定义了一些代码生成时,需要用到的文件。用来定义生成代码的文是.types,.in,.attrib。对于EGL的声明则是在renderControl。对于EGL的文件都是以‘renderControl’开头的,这个主要是历史原因,他们调用了gralloc系统的模块来管理图形缓冲区在比EGL更低的级别。

EGL/GLES函数调用被通过一些规范文件进行描述,这些文件描述了类型,函数签名和它们的一些属性。系统的encoder静态库就是通过这些生成的文件来构建的,它们包含了可以将EGL/GLES命令转化为简单的byte信息的通过IOStream进行发送。

模拟器的绘制

模拟器接收渲染指令的位置,

在android/opengles.cpp控制了动态的装载渲染库,和正确的初始化,构建它。host 渲染的库在host/libs/libOpenglRender下,在模拟器opengles下的代码掌管动态装载一些渲染的库。

  • RendererImpl

模拟器接收指令渲染的实现在RendererImpl中,对于每一个新来的渲染client,都会通过createRenderChannel来创建一个RenderChannel,然后创建一个RenderThread。

RenderChannelPtr RendererImpl::createRenderChannel() {

const auto channel = std::make_shared();

std::unique_ptr rt(RenderThread::create(

shared_from_this(), channel));

if (!rt->start()) {

fprintf(stderr, “Failed to start RenderThread\n”);

return nullptr;

}

return channel;

}

RenderThread相关创建代码

std::unique_ptr RenderThread::create(

std::weak_ptr renderer,

std::shared_ptr channel) {

return std::unique_ptr(

new RenderThread(renderer, channel));

}

RenderThread::RenderThread(std::weak_ptr renderer,

std::shared_ptr channel)
emugl::Thread(android::base::ThreadFlags::MaskSignals, 2 * 1024 * 1024),

mChannel(channel), mRenderer(renderer) {}

在RenderThread创建成功之后,调用了其start方法。进入死循环,从ChannelStream之中读取指令流,然后对指令流进行decode操作。

ChannelStream stream(mChannel, RenderChannel::Buffer::KSmallSize);

while(1) {

initialize decoders

//初始化解码部分

tInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL); tInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL); initRenderControlContext(&tInfo.m_rcDec);

ReadBuffer readBuf(kStreamBufferSize);

const int stat = readBuf.getData(&stream, packetSize);

//尝试通过GLES1解码器来解码指令流

size_t last = tInfo.m_glDec.decode( readBuf.buf(), readBuf.validData(), &stream, &checksumCalc);

if (last > 0) {

progress = true;

readBuf.consume(last);

}

//尝试通过GLESV2的解码器来进行指令流

last = tInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(),

&stream, &checksumCalc);

FrameBuffer::getFB()->unlockContextStructureRead();

if (last > 0) {

progress = true;

readBuf.consume(last);

}

//尝试通过renderControl解码器来进行指令流的解码

last = tInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(),

&stream, &checksumCalc);

if (last > 0) {

readBuf.consume(last);

progress = true;

}

}

解码过程,省略部分代码。保留了核心处理代码。

指令流的来源

上面的指令流处理的数据从ChannelStream中来获取,这里从ChannelStream着手进行分析。

  • ChannelStream

我们先来看一下我们的协议流数据从何处而来,从数据读取翻译过程可以看出是来自我们的 ChannelStream,而ChannelStream又是对于Channle的包装。接下来看一下ChannelStream的实现。

可以看到其是对于RenderChannel的一个包装,同时有两个Buffer。

  • ChannelStream 实现自IOStream

class ChannelStream final : public IOStream

ChannelStream(std::shared_ptr channel, size_t bufSize);

声明了以下变量

std::shared_ptr mChannel;

RenderChannel::Buffer mWriteBuffer;

RenderChannel::Buffer mReadBuffer;

ChannelStream是对于RenderChannel进行了一次包装,对于具体的操作还是交到RenderChannel进行执行,RenderChannel负责在Guest和Host之间的协议数据通信,然后ChannleStream提供了一些buffer在对其封装的基础上,更方便的获取其中的数据,同时由于继承自IOStream,也定义了其中的一些接口,更方便调用。对于数据的读写最终调用了RenderChannelreadFromGuestwriteToGuest,其提供了一个Buffer来方便进行数据的读写。

  • RenderChannel

RenderChannel中的数据从哪里而来呢?跟进其几个读写方法,我们便会发现,其具体的执行是交给了mFromGuestmToGuest,其类型分别为

BufferQueue mFromGuest;

BufferQueue mToGuest;

通过调用其push,pop方法,从中获取数据,到此,我们可以再继续跟进一下BufferQueue的创建和实现。

  • BufferQueue

mFromGuest(kGuestToHostQueueCapacity, mLock),

mToGuest(kHostToGuestQueueCapacity, mLock)

BufferQueue模型是Renderchannel的一个先进先出的队列,Buffer实例可以被用在不同的线程之间,其同步原理是在创建的时候,传递了一个锁进去。其内部的buffer利用就是RenderChannel的buffer。对于队列的一些基本操作进行了相应的锁处理。

BufferQueue(size_t capacity, android::base::Lock& lock)
mCapacity(capacity), mBuffers(new Buffer[capacity]), mLock(lock) {}

这里只是简单地传递数据,确定buffer的大小,同时为其加锁。

对于Buffer的读写,这里提供了四个关键函数。

  • tryWrite(Buffer&& buffer)

mFromGuest.tryPushLocked(std::move(buffer));

  • tryRead(Buffer* buffer)

mToGuest.tryPopLocked(buffer);

  • writeToGuest(Buffer&& buffer)

mToGuest.pushLocked(std::move(buffer));

  • readFromGuest(Buffer* buffer, bool blocking)

mFromGuest.popLocked(buffer);

最后

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助

因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

[外链图片转存中…(img-vkdlM5Dh-1715426483111)]

[外链图片转存中…(img-XQZa6pd8-1715426483113)]

[外链图片转存中…(img-mROebPsM-1715426483114)]

[外链图片转存中…(img-bGcKUVUX-1715426483115)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门

如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

  • 30
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值