Anbox源码分析(四)

上篇文章我们从源码分析了一下Anbox是怎样一步步的准备了OpenGL ES的渲染环境的,这篇文章,我们继续分析Android的渲染指令是如何到达宿主机进行渲染的。

宿主机端

先从入口开始,上一篇我们已经提过,anbox的入口函数就是在session manage 。代码位置anbox/src/anbox/cmds/session_manager.cpp。直接来到215行

auto qemu_pipe_connector =std::make_shared<network::PublishedSocketConnector>(utils::string_format("%s/qemu_pipe", socket_path), rt,std::make_shared<qemu::PipeConnectionCreator>(gl_server->renderer(), rt));

Anbox建立了一个名字为qemu_pipeunix socket服务端,并等待Android端连接。我们跟进PublishedSocketConnector,路径在anbox/src/anbox/network/published_socket_connector.cpp,构造函数直接调用start_accept()函数

void PublishedSocketConnector::start_accept() {auto socket = std::make_shared<boost::asio::local::stream_protocol::socket>(runtime_->service());acceptor_.async_accept(*socket,[this, socket](boost::system::error_code const& err) {on_new_connection(socket, err);});
}

当有客户端连接时调用on_new_connection函数。

void PublishedSocketConnector::on_new_connection(std::shared_ptr<boost::asio::local::stream_protocol::socket> const& socket,boost::system::error_code const& err) {if (!err)connection_creator_->create_connection_for(socket);if (err.value() == boost::asio::error::operation_aborted)return;start_accept();
}

这个函数的先是调用connection_creator_create_connection_for函数,然后继续监听客户端连接。
这个connection_creator_就是session manage 中传进来的qemu::PipeConnectionCreator
我们继续追到anbox/src/anbox/qemu/pipe_connection_creator.cppcreate_connection_for函数

void PipeConnectionCreator::create_connection_for(std::shared_ptr<boost::asio::local::stream_protocol::socket> const&socket) {auto const messenger = std::make_shared<network::LocalSocketMessenger>(socket);const auto type = identify_client(messenger);auto const processor = create_processor(type, messenger);if (!processor)BOOST_THROW_EXCEPTION(std::runtime_error("Unhandled client type"));auto const &connection = std::make_shared<network::SocketConnection>(messenger, messenger, next_id(), connections_, processor);connection->set_name(client_type_to_string(type));connections_->add(connection);connection->read_next_message();
}

先是创建了一个LocalSocketMessenger,用来与客户端通信;
然后通过函数identify_client判断客户端的类型,对于渲染来说,类型就是pipe:opengles
接着根据客户端类型创建对应的processor,最后再创建对应的connection
创建完成后读取客户端消息。
在创建processor时,会创建处理OpenGL ES指令的线程,我们可以看anbox/src/anbox/graphics/opengles_message_processor.cpp

OpenGlesMessageProcessor::OpenGlesMessageProcessor(const std::shared_ptr<Renderer> &renderer,const std::shared_ptr<network::SocketMessenger> &messenger): messenger_(messenger),stream_(std::make_shared<BufferedIOStream>(messenger_)) {// We have to read the client flags first before we can continue// processing the actual commandsunsigned int client_flags = 0;auto err = messenger_->receive_msg(boost::asio::buffer(&client_flags, sizeof(unsigned int)));if (err) ERROR("%s", err.message());render_thread_.reset(RenderThread::create(renderer, stream_.get(), std::ref(global_lock)));if (!render_thread_->start())BOOST_THROW_EXCEPTION(std::runtime_error("Failed to start renderer thread"));
}

这里会创建一个线程render_thread_专门来处理OpenGL ES的指令。

intptr_t RenderThread::main() {RenderThreadInfo threadInfo;ChecksumCalculatorThreadInfo threadChecksumInfo;threadInfo.m_glDec.initGL(gles1_dispatch_get_proc_func, NULL);threadInfo.m_gl2Dec.initGL(gles2_dispatch_get_proc_func, NULL);initRenderControlContext(&threadInfo.m_rcDec);ReadBuffer readBuf(STREAM_BUFFER_SIZE);while (true) {int stat = readBuf.getData(m_stream);if (stat <= 0)break;bool progress;do {progress = false;std::unique_lock<std::mutex> l(m_lock);size_t last =threadInfo.m_glDec.decode(readBuf.buf(), readBuf.validData(), m_stream);if (last > 0) {progress = true;readBuf.consume(last);}last =threadInfo.m_gl2Dec.decode(readBuf.buf(), readBuf.validData(), m_stream);if (last > 0) {progress = true;readBuf.consume(last);}last = threadInfo.m_rcDec.decode(readBuf.buf(), readBuf.validData(), m_stream);if (last > 0) {readBuf.consume(last);progress = true;}} while (progress);}threadInfo.m_gl2Dec.freeShader();threadInfo.m_gl2Dec.freeProgram();// Release references to the current thread's context/surfaces if anyrenderer_->bindContext(0, 0, 0);if (threadInfo.currContext || threadInfo.currDrawSurf || threadInfo.currReadSurf)ERROR("RenderThread exiting with current context/surfaces");renderer_->drainWindowSurface();renderer_->drainRenderContext();return 0;
}

这个线程里,显示初始化三个解码器,分别是GLESv1DecoderGLESv2DecoderrenderControl_decoder_context_t,然后根据收到的socket客户端的信息,来分别解析这三种指令。
解码的相关的代码在anbox/external/android-emugl/host/libs,此部分内容是Anbox从Android Emulator里挪过来的,这里就不再做分析了。

Android端

这样,如果Android里面采集OpenGL ES相关的指令,并通过三个对应的编码器将指令传输出来,Anbox就可以实现将Android里的所有OpenGL ES指令在宿主机上执行,从而进行相应的渲染。

那我们来看一下Android是如何采集OpenGL ES指令并通过编码器传输出来的。
先看一下anbox/android/opengl/system/egl/Android.mk。里面告诉我们,这个文件夹下的源文件会编译为库文件libEGL_emulation.so.

$(call emugl-begin-shared-library,libEGL_emulation)

在Anbox源码分析(二)——Anbox渲染原理里已经介绍过了Android加载OpenGL ES的流程,所以只要将libEGL_emulation.so放在/system/lib64/egl下,Android就会自动去加载该库,并将其作为OpenGL ES的默认库文件,这样Android里所有的OpenGL ES调用都会经过该库。也就是说我们可以收集到Android里所有的OpenGL ES相关的指令了。

现在我们再看一下是怎么讲指令传输出来的。众所周知,通过egl来调用OpenGL时首先要调用eglInitialize函数,可以看anbox/android/opengl/system/egl/egl.cppeglInitialize函数

EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
{VALIDATE_DISPLAY(dpy,EGL_FALSE);if (!s_display.initialize(&s_eglIface)) {return EGL_FALSE;}if (major!=NULL)*major = s_display.getVersionMajor();if (minor!=NULL)*minor = s_display.getVersionMinor();return EGL_TRUE;
}

这个函数调用了s_display.initialize(&s_eglIface),我们来到anbox/android/opengl/system/egl/eglDisplay.cppinitialize函数。这个函数首先加载了libGLESv1_CM_emulation.solibGLESv2_emulation.so。也就是OpenGL ES 1.0OpenGL ES2.0的库。然后

HostConnection *hcon = HostConnection::get();

跟进去anbox/android/opengl/system/OpenglSystemCommon/HostConnection.cpp

HostConnection *HostConnection::get()
{/* TODO: Make this configurable with a system property */const int useQemuPipe = USE_QEMU_PIPE;// Get thread infoEGLThreadInfo *tinfo = getEGLThreadInfo();if (!tinfo) {return NULL;}if (tinfo->hostConn == NULL) {HostConnection *con = new HostConnection();if (NULL == con) {return NULL;}if (useQemuPipe) {QemuPipeStream *stream = new QemuPipeStream(STREAM_BUFFER_SIZE);if (!stream) {ALOGE("Failed to create QemuPipeStream for host connection!!!\n");delete con;return NULL;}if (stream->connect() < 0) {ALOGE("Failed to connect to host (QemuPipeStream)!!!\n");delete stream;delete con;return NULL;}con->m_stream = stream;}else /* !useQemuPipe */{TcpStream *stream = new TcpStream(STREAM_BUFFER_SIZE);if (!stream) {ALOGE("Failed to create TcpStream for host connection!!!\n");delete con;return NULL;}if (stream->connect("10.0.2.2", STREAM_PORT_NUM) < 0) {ALOGE("Failed to connect to host (TcpStream)!!!\n");delete stream;delete con;return NULL;}con->m_stream = stream;}// send zero 'clientFlags' to the host.unsigned int *pClientFlags =(unsigned int *)con->m_stream->allocBuffer(sizeof(unsigned int));*pClientFlags = 0;con->m_stream->commitBuffer(sizeof(unsigned int));ALOGD("HostConnection::get() New Host Connection established %p, tid %d\n", con, gettid());tinfo->hostConn = con;}return tinfo->hostConn;
}

这里Anbox用的是qemuPipe,所以先创建QemuPipeStream,再进行stream->connect()
我们可以看一下anbox/android/opengl/system/OpenglSystemCommon/QemuPipeStream.cpp

int QemuPipeStream::connect(void)
{m_sock = qemu_pipe_open("opengles");if (!valid()) return -1;return 0;
}

connect的时候会以"opengles"为标志连接qemu_pipe,也就与前面我们讲到anbox创建的unix socket中的pipe:opengles对应上了。这样Android端与宿主机端就连接上了,然后只需要通过这个通道将Android里的OpenGL ES指令传输到宿主机端,就可以实现Android内所有APP的渲染了。
由于本人暂时就研究了Anbox渲染相关的原理,所以,至此,本文就先告一段落了。感谢大家!

Anbox源码分析(四)——Anbox渲染原理(源码分析)相关推荐

  1. c语言 bcd码 16进制字符串 原理,ASCII码、HEX、字符、BCD 等等 基础知识思考

    每每遇到这些问题就要想个半天,想不明白还不舒服,今天特别把所想整理下避免以后再次进入思想漩涡!!! 计算机存储和传输都是以字节为单位 1 bit     = 1  二进制数据 1 byte  = 8  ...

  2. 四年级计算机期末质量分析,四年级数学期末试卷质量分析

    知之为知之,不知为不知,是知也.学习是永无止境的,只有通过不断学习才能提升自己修养.下面小编带来的是人教版四年级数学期末试卷质量分析,希望对你有帮助. 一.试卷分析 本试卷以<数学课程标准> ...

  3. 四年级计算机期末质量分析,四年级语文科期末检测质量分析报告格式

    四年级语文科期末检测质量分析报告格式 作者:王赛娜时间:2017-07-08 一.试题分析  1.试卷的结构和内容分布    本套试题共四个部分9个大题,第一部分为书写乐园(4分),分成2小题,第1题 ...

  4. Anbox源码分析(三)——Anbox渲染原理(源码分析)

    Anbox源码分析(三) 上一篇,我们介绍了Anbox视频渲染的原理,这一篇,我们从源码入手,更深入的理解Anbox与渲染的机制和原理 session manager入口 session manage ...

  5. Lucene 原理与代码分析完整版

    原文地址为: Lucene 原理与代码分析完整版 Lucene 原理与代码分析系列文章已经基本告一段落,可能问题篇还会有新的更新. 完整版pdf可由以下链接下载. Lucene 原理与代码分析完整版 ...

  6. 一文搞定校验码(奇偶校验,海明,CRC 码)

    文章目录 效验码 计算码距方法 奇偶校验码 校验原理 奇偶校验 异或法制 总结 海明校验码 海明校验码的分布规律 海明码纠错以及定位 实现原理 海明码完善 总结 循环冗余校验码(CRC) 模2除算法 ...

  7. 全球计算机行业发展现状分析,2021年船舶计算机发展趋势预测分析

    <全球与中国船舶计算机行业现状分析与发展前景研究报告(2021年版)>主要研究分析了船舶计算机行业市场运行态势并对船舶计算机行业发展趋势作出预测.报告首先介绍了船舶计算机行业的相关知识及国 ...

  8. Spring 源码分析(四) ——MVC(二)概述

    随时随地技术实战干货,获取项目源码.学习资料,请关注源代码社区公众号(ydmsq666) from:Spring 源码分析(四) --MVC(二)概述 - 水门-kay的个人页面 - OSCHINA ...

  9. 手机自动化测试:Appium源码分析之跟踪代码分析四 1

    手机自动化测试:Appium源码分析之跟踪代码分析四 控制器模块 // Appium webserver controller methods // https://github.com/hugs/a ...

最新文章

  1. java内存规范_Java内存模型-jsr133规范介绍
  2. modalDialog注意点
  3. python爬取时怎么获取头部header
  4. io.js 1.0.x发布
  5. java调用存储过程同时获取[返回参数]和[结果集]
  6. Java web(2012/2/22)
  7. 音响系统测试软件苹果,再谈汽车音响调试专用相位软件JL AUDIO Tools,苹果ios坛友的福音!|汽车数码...
  8. Aria2保姆级教程
  9. macbook 虚拟机安装win7
  10. 从历史上的错误数据中吸取教训
  11. SEP8266 由零开始(一),最小系统,下载,与WiFi建立
  12. Notes Fifteenth Day-渗透攻击-红队-内部信息搜集
  13. kafka connector使用(Docker一键启动版)
  14. vue里使用echarts画世界地图
  15. 微信支付/退费(服务商)模式
  16. OpenStack-Pike版Ironic安装指导分析-(上)
  17. 基于Ubuntu的esp32编程学习(https://www.bilibili.com/video/BV1wV4y1G7Vk?p=22vd_source=c89885f80e65caacb539e)
  18. char *s=“\ta\017bc“所占字节数
  19. 最全unicode编码
  20. 视频转图片如何快速完成

热门文章

  1. Debian下安装Mosquitto
  2. 把计算机知识列表合为一列,怎么把相同表格的数据合并
  3. flink 1.14编译:flink-fs-hadoop-shaded找不到
  4. python中search用法_Python中的python re.search方法详解
  5. jsp mysql企业网站_JSP基于MySQL构建中小企业电子商务网站.pdf
  6. HTML谷歌怎么加背景音乐,谷歌Chrome浏览器怎么提取网页的背景音乐?
  7. Android版本9华为,华为应用市场旧版本下载-华为应用市场老版v9.0.0.303 安卓版 - 极光下载站...
  8. 浮躁和傲慢,这样的人比比皆是
  9. (MIUI)小米手机录音丢失找回
  10. 《现代控制系统》第五章——反馈控制系统性能分析 5.3 二阶系统的性能