#include "texture_navi_render_egl.h" #include #include #include #include #include #include #include "backend/gl_process_resolver.h" #include "backend/wayland_egl.h" #include "logging.h" #include "view/flutter_view.h" constexpr uint32_t kMapProviderTextureId = 98765; constexpr char kNaviRenderSoName[] = "libnav_render.so"; std::mutex g_gl_mutex; TextureNaviRender::TextureNaviRender(const FlutterView* view) : Texture(kMapProviderTextureId, GL_TEXTURE_2D, GL_RGBA8, Create, Dispose), m_egl_backend(reinterpret_cast(view->GetBackend())), m_h_module(nullptr) {} TextureNaviRender::~TextureNaviRender() { if (m_h_module) { for (auto const& [key, context] : m_render_api.ctx) { m_render_api.de_initialize(context); } dlclose(m_h_module); } } flutter::EncodableValue TextureNaviRender::Create( void* userdata, const std::map* args) { auto* obj = static_cast(userdata); std::string access_token; auto it = args->find(flutter::EncodableValue("access_token")); if (it != args->end() && !it->second.IsNull()) { access_token = std::get(it->second); } std::string module; it = args->find(flutter::EncodableValue("module")); if (it != args->end() && !it->second.IsNull()) { module = std::get(it->second); } if (module.empty()) { module = kNaviRenderSoName; spdlog::info("\"module\" not set, using {}", module); } bool map_flutter_assets = false; it = args->find(flutter::EncodableValue("map_flutter_assets")); if (it != args->end() && !it->second.IsNull()) { map_flutter_assets = std::get(it->second); } std::string asset_path; if (map_flutter_assets) { asset_path = obj->m_flutter_engine->GetAssetDirectory(); } else { it = args->find(flutter::EncodableValue("asset_path")); if (it != args->end() && !it->second.IsNull()) { asset_path = std::get(it->second); } } if (asset_path.empty()) { spdlog::error("\"asset_path\" not set!!"); } std::string cache_folder; it = args->find(flutter::EncodableValue("cache_folder")); if (it != args->end() && !it->second.IsNull()) { cache_folder = std::get(it->second); } if (cache_folder.empty()) { spdlog::error("\"cache_folder\" not set!!"); } mkdir(cache_folder.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); std::string misc_folder; it = args->find(flutter::EncodableValue("misc_folder")); if (it != args->end() && !it->second.IsNull()) { misc_folder = std::get(it->second); } if (cache_folder.empty()) { spdlog::error("\"misc_folder\" not set!!"); } obj->m_interface_version = 0; it = args->find(flutter::EncodableValue("intf_ver")); if (it != args->end() && !it->second.IsNull()) { obj->m_interface_version = std::get(it->second); } if (!obj->m_h_module) { SPDLOG_DEBUG("Attempting to open [{}]", module); obj->m_h_module = dlopen(module.c_str(), RTLD_NOW | RTLD_GLOBAL); if (obj->m_h_module) { obj->InitRenderApi(); spdlog::info("navigation render interface version: 0x{:x}", obj->m_render_api.version()); } else { return flutter::EncodableValue(flutter::EncodableMap{ {flutter::EncodableValue("result"), flutter::EncodableValue(-1)}, {flutter::EncodableValue("error"), flutter::EncodableValue("Failed to load module")}}); } } SPDLOG_DEBUG("Initializing Navigation Texture ({} x {})", obj->m_width, obj->m_height); std::lock_guard guard(g_gl_mutex); obj->m_egl_backend->MakeTextureCurrent(); glViewport(0, 0, obj->m_width, obj->m_height); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); /// Framebuffer glGenFramebuffers(1, &obj->m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, obj->m_fbo); /// Output Texture glGenTextures(1, &obj->m_texture_id); glBindTexture(GL_TEXTURE_2D, obj->m_texture_id); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, obj->m_width, obj->m_height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, obj->m_texture_id, 0); glGenRenderbuffers(1, &obj->m_rbo); glBindRenderbuffer(GL_RENDERBUFFER, obj->m_rbo); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, obj->m_width, obj->m_height); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, obj->m_rbo); if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { return flutter::EncodableValue(flutter::EncodableMap{ {flutter::EncodableValue("result"), flutter::EncodableValue(-1)}, {flutter::EncodableValue("error"), flutter::EncodableValue("Bad framebuffer status")}}); } NAV_RENDER_API_CONTEXT_T* context = nullptr; if (obj->m_interface_version < 2) { context = obj->m_render_api.initialize( access_token.c_str(), obj->m_width, obj->m_height, asset_path.c_str(), cache_folder.c_str(), misc_folder.c_str()); } else if (obj->m_interface_version == 2) { NavRenderConfig nav_render_init = { .dpy = obj->m_egl_backend->GetDisplay(), .context = obj->m_egl_backend->GetTextureContext(), .framebufferId = obj->m_fbo, .access_token = access_token.c_str(), .width = obj->m_width, .height = obj->m_height, .asset_path = asset_path.c_str(), .cache_folder = cache_folder.c_str(), .misc_folder = misc_folder.c_str(), .pfnLogger = [](int level, const char* /* context */, const char* message) { switch (level) { case SPDLOG_LEVEL_TRACE: spdlog::trace(message); break; case SPDLOG_LEVEL_DEBUG: spdlog::debug(message); break; case SPDLOG_LEVEL_INFO: spdlog::info(message); break; case SPDLOG_LEVEL_WARN: spdlog::warn(message); break; case SPDLOG_LEVEL_ERROR: spdlog::error(message); break; case SPDLOG_LEVEL_CRITICAL: spdlog::critical(message); break; default: case SPDLOG_LEVEL_OFF: break; } }, .pfnGlLoader = [](void* /* userdata */, const char* procname) -> const void* { return GlProcessResolver::GetInstance().process_resolver(procname); }}; nav_render_init.dpy = obj->m_egl_backend->GetDisplay(); nav_render_init.context = obj->m_egl_backend->GetTextureContext(); nav_render_init.framebufferId = obj->m_fbo; context = obj->m_render_api.initialize2(&nav_render_init); } if (!context) { return flutter::EncodableValue(flutter::EncodableMap{ {flutter::EncodableValue("result"), flutter::EncodableValue(-1)}, {flutter::EncodableValue("error"), flutter::EncodableValue("Navigation Failed to Initialize")}}); } obj->m_render_api.ctx[obj->m_texture_id] = context; glBindFramebuffer(GL_FRAMEBUFFER, 0); glFinish(); obj->m_egl_backend->ClearCurrent(); obj->Enable(obj->m_texture_id); obj->m_run_enable = true; return flutter::EncodableValue(flutter::EncodableMap{ {flutter::EncodableValue("result"), flutter::EncodableValue(0)}, {flutter::EncodableValue("textureId"), flutter::EncodableValue(static_cast(obj->m_texture_id))}, {flutter::EncodableValue("width"), flutter::EncodableValue(obj->m_width)}, {flutter::EncodableValue("height"), flutter::EncodableValue(obj->m_height)}, {flutter::EncodableValue("render_ctx"), flutter::EncodableValue(reinterpret_cast(context))}}); } void TextureNaviRender::Dispose(void* userdata, GLuint name) { auto* obj = static_cast(userdata); const auto context = obj->m_render_api.ctx[obj->m_texture_id]; obj->m_run_enable = false; obj->m_render_api.de_initialize(context); obj->Disable(name); std::lock_guard guard(g_gl_mutex); glDeleteTextures(1, &obj->m_texture_id); glDeleteRenderbuffers(1, &obj->m_rbo); glDeleteFramebuffers(1, &obj->m_fbo); } void TextureNaviRender::RunTask(void* userdata) { auto* obj = static_cast(userdata); for (auto [key, context] : obj->m_render_api.ctx) { if (obj->m_run_enable) { obj->m_render_api.run_task(context); } } } void TextureNaviRender::Draw(void* userdata) { auto* obj = static_cast(userdata); if (!obj->m_draw_next || !obj->m_run_enable) return; for (auto const& [key, context] : obj->m_render_api.ctx) { std::lock_guard guard(g_gl_mutex); if (obj->m_interface_version == 2) { obj->m_render_api.render2(context); } else { obj->m_egl_backend->MakeTextureCurrent(); glClear(GL_COLOR_BUFFER_BIT); glBindFramebuffer(GL_FRAMEBUFFER, obj->m_fbo); glViewport(0, 0, obj->m_width, obj->m_height); obj->m_render_api.render(context, obj->m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, 0); obj->m_egl_backend->ClearCurrent(); } obj->m_draw_next = false; obj->FrameReady(); } } void TextureNaviRender::InitRenderApi() { m_render_api.version = reinterpret_cast( dlsym(m_h_module, "nav_render_version")); if (m_render_api.version && m_render_api.version() != EXPECTED_RENDER_API_VERSION) { assert(false); } m_render_api.initialize = reinterpret_cast( dlsym(m_h_module, "nav_render_initialize")); m_render_api.initialize2 = reinterpret_cast( dlsym(m_h_module, "nav_render_initialize2")); assert(m_render_api.initialize || m_render_api.initialize2); m_render_api.de_initialize = reinterpret_cast( dlsym(m_h_module, "nav_render_de_initialize")); assert(m_render_api.de_initialize); m_render_api.run_task = reinterpret_cast( dlsym(m_h_module, "nav_render_run_task")); assert(m_render_api.run_task); if (m_interface_version == 2) { m_render_api.render2 = reinterpret_cast( dlsym(m_h_module, "nav_render_render2")); assert(m_render_api.render2); } else { m_render_api.render = reinterpret_cast( dlsym(m_h_module, "nav_render_render")); assert(m_render_api.render); } m_render_api.resize = reinterpret_cast( dlsym(m_h_module, "nav_render_resize")); assert(m_render_api.resize); }