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_pipe
的unix 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.cpp
的create_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 commands
unsigned 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 any
renderer_->bindContext(0, 0, 0);
if (threadInfo.currContext || threadInfo.currDrawSurf || threadInfo.currReadSurf)
ERROR("RenderThread exiting with current context/surfaces");
renderer_->drainWindowSurface();
renderer_->drainRenderContext();
return 0;
}
这个线程里,显示初始化三个解码器,分别是GLESv1Decoder
、GLESv2Decoder
和renderControl_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.cpp
的eglInitialize
函数
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.cpp
的initialize
函数。这个函数首先加载了libGLESv1_CM_emulation.so
和libGLESv2_emulation.so
。也就是OpenGL ES 1.0
和OpenGL 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 info
EGLThreadInfo *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渲染相关的原理,所以,至此,本文就先告一段落了。感谢大家!