Anbox源码分析(四)——Anbox渲染原理(源码分析)

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 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;
}

这个线程里,显示初始化三个解码器,分别是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 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渲染相关的原理,所以,至此,本文就先告一段落了。感谢大家!

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值