Anbox源码分析(三)
上一篇,我们介绍了Anbox视频渲染的原理,这一篇,我们从源码入手,更深入的理解Anbox与渲染的机制和原理
session manager入口
session manager
的入口函数在anbox/src/anbox/cmds/session_manager.cpp
,我们阅读anbox源码时可以直接到这里开始就可以。进入构造函数。
先是处理一系列的启动参数,对渲染来说,主要参数是software-rendering
,看它的Description{"Use software rendering instead of hardware accelerated GL rendering"}
,就是说使用软件渲染还是硬件渲染。
接下来直接到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();
根据启动参数,决定使用软件渲染或硬件渲染,然后生成了一个GLRendererServer
的共享指针。OK,那我们追进去,到anbox/src/anbox/graphics/gl_renderer_server.cpp
。直接看构造函数:
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_);
}
先是根据使用的窗口策略生成对应的图层合成策略智能指针,LayerComposer
这个模块里面就是把不同app的图层合成到同一个画布上,也就是第一篇所说的 虚拟一个Android的sufaceflinger。
然后根据软件渲染还是硬件渲染读取对应的库函数,以软件渲染为例,anbox
先去读取swiftshader
的环境变量,如果存在,则将swiftshader
库的路径设置为环境变量读取到的路径;然后anbox
读取SNAP
的环境变量,也就说说如果你的anbox
是使用snap
打包启动的,则直接去对应的路径下读取swiftshader
的库。如果读取到了swiftshader
库的路径,则在对应路径下读取libEGL.so
、libGLES_CM.so
和libGLESv2.so
。即OpenGL ES
对应的三个库文件。
然后去初始化这三个库文件,进入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;
}
}
对三个库文件分别进入dispatch
初始化,以init_egl_dispatch
为例,其他两个库是一样的操作,进入anbox/external/android-emugl/host/libOpenGLESDispatch/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;
}
打开对应路径,将egl需要用到的函数全部通过findSymbol
或eglGetProcAddress
的方式得到函数地址,方便后续直接调用。
这样就得到了OpenGL ES
所有的函数地址了。
好,我们回到anbox/src/anbox/graphics/gl_renderer_server.cpp
,初始化OpenGL函数后,会去初始化渲染器,来到了anbox/src/anbox/graphics/emugl/Renderer.cpp
的initialize
,其实就是在display :0
上初始化OpenGL ES
相关的环境,跟我们平时使用OpenGL ES
时候的操作基本上一致,不再多说了。
做完这些事情以后,算是gl_renderer_server
的初始化就完成了,然后我们回到anbox/src/anbox/cmds/session_manager.cpp
,下一步window_manager->setup();
就要创建本地窗口了,其实就是我们看到的Android的窗口,所有Android 的画面都会显示在这个窗口上。
还是以单窗口为例,我们追到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");
}
}
先是创建了一个Window
的对象,然后调用这个对象的attach()
函数,来到了anbox/src/anbox/wm/window.cpp
bool Window::attach() {
if (!renderer_)
return false;
attached_ = renderer_->createNativeWindow(native_handle());
return attached_;
}
可以看到,这里其实还是调用的renderer
的创建本地窗口的方法。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;
}
可以看到,还是跟通常使用OpenGL ES
一样,将本地窗口转化为OpenGL ES
的窗口,供渲染器使用。
至此,渲染环境准备完毕,下一节我们从代码中看看Android中的OpenGL ES指令是如何传输到Anbox上使用的。