Anbox source code analysis -- anbox rendering principle (source code analysis)

Anbox source code analysis (3)

In the previous article, we introduced the principle of Anbox video rendering. In this article, we started from the source code to have a deeper understanding of the mechanism and principle of Anbox and rendering

session manager entry

The entry function of session manager is in anbox/src/anbox/cmds/session_manager.cpp , when we read the source code of anbox, we can just go here and start. Enter the constructor.
First, it deals with a series of startup parameters. For rendering, the main parameter is software rendering. Look at its Description{"Use software rendering instead of hardware accelerated GL rendering"}, that is to say, software rendering or hardware rendering.
Then go straight to line 186

    const auto should_force_software_rendering = utils::get_env_value("ANBOX_FORCE_SOFTWARE_RENDERING", "false");
    auto gl_driver = graphics::GLRendererServer::Config::Driver::Host;
    if (should_force_software_rendering == "true" || use_software_rendering_)
     gl_driver = graphics::GLRendererServer::Config::Driver::Software;

    graphics::GLRendererServer::Config renderer_config {
      gl_driver,
      single_window_
    };
    auto gl_server = std::make_shared<graphics::GLRendererServer>(renderer_config, window_manager);

    platform->set_window_manager(window_manager);
    platform->set_renderer(gl_server->renderer());
    window_manager->setup();

According to the startup parameters, it is decided to use software rendering or hardware rendering, and then a shared pointer of GLRendererServer is generated. OK, let's go to anbox/src/anbox/graphics/gl_renderer_server.cpp . Look directly at the constructor:

GLRendererServer::GLRendererServer(const Config &config, const std::shared_ptr<wm::Manager> &wm)
    : renderer_(std::make_shared<::Renderer>()) {

  std::shared_ptr<LayerComposer::Strategy> composer_strategy;
  if (config.single_window)
    composer_strategy = std::make_shared<SingleWindowComposerStrategy>(wm);
  else
    composer_strategy = std::make_shared<MultiWindowComposerStrategy>(wm);

  composer_ = std::make_shared<LayerComposer>(renderer_, composer_strategy);

  auto gl_libs = emugl::default_gl_libraries();
  if (config.driver == Config::Driver::Software) {
    auto swiftshader_path = fs::path(utils::get_env_value("SWIFTSHADER_PATH"));
    const auto snap_path = utils::get_env_value("SNAP");
    if (!snap_path.empty())
      swiftshader_path = fs::path(snap_path) / "lib" / "anbox" / "swiftshader";
    if (!fs::exists(swiftshader_path))
      throw std::runtime_error("Software rendering is enabled, but SwiftShader library directory is not found.");

    gl_libs = std::vector<emugl::GLLibrary>{
      {emugl::GLLibrary::Type::EGL, (swiftshader_path / "libEGL.so").string()},
      {emugl::GLLibrary::Type::GLESv1, (swiftshader_path / "libGLES_CM.so").string()},
      {emugl::GLLibrary::Type::GLESv2, (swiftshader_path / "libGLESv2.so").string()},
    };
  }
  emugl_logger_struct log_funcs;
  log_funcs.coarse = logger_write;
  log_funcs.fine = logger_write;

  if (!emugl::initialize(gl_libs, &log_funcs, nullptr))
    BOOST_THROW_EXCEPTION(std::runtime_error("Failed to initialize OpenGL renderer"));

  renderer_->initialize(0);

  registerRenderer(renderer_);
  registerLayerComposer(composer_);
}

First, the corresponding layer composition strategy intelligent pointer is generated according to the window strategy used. In the layer composer module, the layers of different app s are synthesized on the same canvas, that is, the virtual Android facade linker mentioned in the first article.
Then read the corresponding library functions according to software rendering or hardware rendering. For example, for software rendering, anbox first reads the environment variables of swiftshader, if any, set the path of swiftshader library to the path read by the environment variables; then anbox reads the environment variables of snap, that is to say, if your anbox is started by using snap package, go to the corresponding path directly Read the swift shader's Library down the path. If the path of swift shader library is read, it will be read under the corresponding path libEGL.so ,libGLES_CM.so And libGLESv2.so. Namely, three library files corresponding to OpenGL ES.
Then initialize the three library files, and enter anbox/src/anbox/graphics/emugl/RenderApi.cpp :

bool initialize(const std::vector<GLLibrary> &libs, emugl_logger_struct *log_funcs, logger_t crash_func) {
  set_emugl_crash_reporter(crash_func);
  if (log_funcs) {
    set_emugl_logger(log_funcs->coarse);
    set_emugl_cxt_logger(log_funcs->fine);
  }

  for (const auto &lib : libs) {
    const auto path = lib.path.c_str();
    switch (lib.type) {
    case GLLibrary::Type::EGL:
      if (!init_egl_dispatch(path))
        return false;
      break;
    case GLLibrary::Type::GLESv1:
      if (!gles1_dispatch_init(path, &s_gles1))
        return false;
      break;
    case GLLibrary::Type::GLESv2:
      if (!gles2_dispatch_init(path, &s_gles2))
        return false;
      break;
    default:
      break;
    }
  }

The three library files are initialized with init_ egl_ For example, the other two libraries operate the same way. Enter anbox / external / Android debug / host / libopengles dispatch/ EGLDispatch.cpp

bool init_egl_dispatch(const char *path) {
    const char *libName = getenv(egl_lib_env_var);
    if (!libName)
      libName = path;
    if (!libName)
      return false;

    char error[256];
    emugl::SharedLibrary *lib = emugl::SharedLibrary::open(libName, error, sizeof(error));
    if (!lib) {
        printf("Failed to open %s: [%s]\n", libName, error);
        return false;
    }

    LIST_RENDER_EGL_FUNCTIONS(RENDER_EGL_LOAD_FIELD)
    LIST_RENDER_EGL_EXTENSIONS_FUNCTIONS(RENDER_EGL_LOAD_OPTIONAL_FIELD)

    s_egl.initialized = true;

    return true;
}

Open the corresponding path, and use findSymbol or eglGetProcAddress to get the function address of all the functions needed by egl, which is convenient for subsequent direct calls.
This gives all the function addresses of OpenGL ES.
OK, let's go back to anbox/src/anbox/graphics/gl_renderer_server.cpp , after initializing the OpenGL function, the renderer will be initialize d, and you will find anbox/src/anbox/graphics/emugl/Renderer.cpp In fact, initializing the OpenGL ES related environment on display: 0 is basically the same as our usual operations when using OpenGL ES, so we won't talk about it any more.
After all this, it's GL_ renderer_ The initialization of the server is finished, and then we go back to anbox/src/anbox/cmds/session_manager.cpp , next window_ Manager - > setup(); it's about to create a local window. In fact, it's the Android window we see. All Android screens will be displayed in this window.
Take single window as an example. Let's go back to anbox/src/anbox/wm/single_window_manager.cpp

void SingleWindowManager::setup() {
  if (auto p = platform_.lock()) {
    window_ = p->create_window(0, window_size_, "Anbox - Android in a Box");
    if (!window_->attach())
      WARNING("Failed to attach window to renderer");
  } else {
    throw std::runtime_error("Can't create window as we don't have a platform abstraction");
  }
}

First, an object of Window is created, then the attach() function of the object is called to anbox/src/anbox/wm/. window.cpp

bool Window::attach() {
  if (!renderer_)
    return false;
  attached_ = renderer_->createNativeWindow(native_handle());
  return attached_;
}

As you can see, this is actually the method of creating local window by calling render. anbox/src/anbox/graphics/emugl/Renderer.cpp

RendererWindow *Renderer::createNativeWindow(
    EGLNativeWindowType native_window) {
  m_lock.lock();

  auto window = new RendererWindow;
  window->native_window = native_window;
  window->surface = s_egl.eglCreateWindowSurface(
      m_eglDisplay, m_eglConfig, window->native_window, nullptr);
  if (window->surface == EGL_NO_SURFACE) {
    delete window;
    m_lock.unlock();
    return nullptr;
  }

  if (!bindWindow_locked(window)) {
    s_egl.eglDestroySurface(m_eglDisplay, window->surface);
    delete window;
    m_lock.unlock();
    return nullptr;
  }

  s_gles2.glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |
                  GL_STENCIL_BUFFER_BIT);
  s_egl.eglSwapBuffers(m_eglDisplay, window->surface);

  unbind_locked();

  m_nativeWindows.insert({native_window, window});

  m_lock.unlock();

  return window;
}

As you can see, it is the same as the usual use of OpenGL ES to convert the local window to the window of OpenGL ES for the renderer to use.
At this point, the rendering environment is ready. In the next section, we will see how the OpenGL ES instruction in Android is transferred to the Anbox for use.

Tags: Android Session Swift Linker

Posted on Tue, 09 Jun 2020 03:27:28 -0400 by ScratchyAnt