模拟器接收渲染指令的位置,
在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,也定义了其中的一些接口,更方便调用。对于数据的读写最终调用了RenderChannel
的readFromGuest
和writeToGuest
,其提供了一个Buffer来方便进行数据的读写。
- RenderChannel
RenderChannel中的数据从哪里而来呢?跟进其几个读写方法,我们便会发现,其具体的执行是交给了mFromGuest
和mToGuest
,其类型分别为
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
相关代码
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**
[外链图片转存中…(img-M7LuGi6k-1715426553558)]
[外链图片转存中…(img-h7p5rEia-1715426553560)]
[外链图片转存中…(img-vH41GcNH-1715426553562)]
[外链图片转存中…(img-PDAhQRpH-1715426553565)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!