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

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

在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);

在服务器这一端,我们用的到的只有两个函数,这两个函数也是在ChannelStream中做了封装的,分别为

  • commitBuffer(size_t size)

mChannel->writeToGuest(std::move(mWriteBuffer));

  • readRaw(void* buf, size_t* inout_len)

mChannel->readFromGuest(&mReadBuffer, blocking);

通过write和read函数可以看出是对端在使用的,用来接收从我们的队列之中读数据。

由于Android模拟器端接受绘制渲染指令是通过Qemu Pipe来接收的,所以最开始接收到数据的位置则是管道服务,其实现在EmuglPipe中,在OpenglEsPipe文件中。

auto renderer = android_getOpenglesRenderer();

if (!renderer) {

D(“Trying to open the OpenGLES pipe without GPU emulation!”);

return nullptr;

}

EmuglPipe* pipe = new EmuglPipe(mHwPipe, this, renderer);

if (!pipe->mIsWorking) {

delete pipe;

pipe = nullptr;

}

return pipe;

获取一个Renderer也就是我们上面提到的用来进行指令转化在本地平台进行绘制的。然后创建一个EmuglPipe实例。

实例创建的构造函数

EmuglPipe(void* hwPipe, Service* service,

const emugl::RendererPtr& renderer)
AndroidPipe(hwPipe, service) {

mChannel = renderer->createRenderChannel();

if (!mChannel) {

D(“Failed to create an OpenGLES pipe channel!”);

return;

}

mIsWorking = true;

mChannel->setEventCallback([this](RenderChannel::State events) {this->onChannelHostEvent(events);});

}

到此回到了上面最初介绍的Render的createRenderChannel函数。

EmuglPipe提供了几个函数onGuestClose,onGuestPoll,onGuestRecv,onGuestSend等对于Guest读写的回调,当有数据到来或者要写回的时候调用,这个时候就会调用renderChannel来进行指令流的读写。

代码文档位置

设计文档

  • qemu/android/docs

  • android-emugl/DESIGN

相关代码

尾声

如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

架构篇

《Jetpack全家桶打造全新Google标准架构模式》

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
重要,希望读者们能谨记这一点。

这里,笔者分享一份从架构哲学的层面来剖析的视频及资料分享给大家梳理了多年的架构经验,筹备近6个月最新录制的,相信这份视频能给你带来不一样的启发、收获。[外链图片转存中…(img-MLUzOVKb-1714805282318)]

PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-zrLqE4ro-1714805282318)]

架构篇

《Jetpack全家桶打造全新Google标准架构模式》
[外链图片转存中…(img-6d826xE7-1714805282318)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值