diff --git a/include/mbgl/gl/renderer_backend.hpp b/include/mbgl/gl/renderer_backend.hpp index c471790b41a..473c7263262 100644 --- a/include/mbgl/gl/renderer_backend.hpp +++ b/include/mbgl/gl/renderer_backend.hpp @@ -39,8 +39,7 @@ class RendererBackend : public gfx::RendererBackend { void initShaders(gfx::ShaderRegistry&, const ProgramParameters& programParameters) override; #endif - virtual bool supportFreeThreadedUpload() const { return false; } - virtual void initFreeThreadedUpload() {} + virtual bool supportFreeThreadedUpload() { return false; } virtual std::shared_ptr createUploadThreadContext() { return nullptr; } gl::ResourceUploadThreadPool& getResourceUploadThreadPool(); diff --git a/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.cpp b/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.cpp index 1e97e5d7542..64feca0fa93 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.cpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.cpp @@ -7,8 +7,6 @@ #include #include -#include - #include #include #include @@ -22,24 +20,6 @@ std::mutex& getEglMutex() { return eglMutex; } -std::string androidSysProp(const char* key) { - assert(strlen(key) < PROP_NAME_MAX); - if (__system_property_find(key) == nullptr) { - return ""; - } - char prop[PROP_VALUE_MAX + 1]; - __system_property_get(key, prop); - return prop; -} - -bool inEmulator() { - return androidSysProp("ro.kernel.qemu") == "1" || androidSysProp("ro.boot.qemu") == "1" || - androidSysProp("ro.hardware.egl") == "emulation" || - util::contains(androidSysProp("ro.build.fingerprint"), "emu") || - util::contains(androidSysProp("ro.build.product"), "emu") || - util::contains(androidSysProp("ro.product.device"), "emu"); -} - } // namespace class AndroidGLRenderableResource final : public mbgl::gl::RenderableResource { @@ -66,22 +46,10 @@ class AndroidGLRenderableResource final : public mbgl::gl::RenderableResource { AndroidGLRendererBackend::AndroidGLRendererBackend(bool multiThreadedGpuResourceUpload_) : gl::RendererBackend(gfx::ContextMode::Unique), - mbgl::gfx::Renderable({64, 64}, std::make_unique(*this)) { - if (multiThreadedGpuResourceUpload_) { - if (inEmulator()) { - mbgl::Log::Error(mbgl::Event::OpenGL, - "Multi-threaded GPU resource upload is not supported in the emulator"); - } else { - multiThreadedGpuResourceUpload = true; - } - } -} + mbgl::gfx::Renderable({64, 64}, std::make_unique(*this)), + multiThreadedGpuResourceUpload(multiThreadedGpuResourceUpload_) {} AndroidGLRendererBackend::~AndroidGLRendererBackend() { - if (eglWaitClient() == EGL_FALSE) { - auto err = "eglWaitClient failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); - } destroyResourceUploadThreadPool(); } @@ -115,8 +83,13 @@ void AndroidGLRendererBackend::markContextLost() { } } -bool AndroidGLRendererBackend::supportFreeThreadedUpload() const { - return multiThreadedGpuResourceUpload; +bool AndroidGLRendererBackend::supportFreeThreadedUpload() { + if (multiThreadedGpuResourceUpload) { + initFreeThreadedUpload(); + return eglClientVersion >= 3; + } else { + return false; + } } std::shared_ptr AndroidGLRendererBackend::createUploadThreadContext() { @@ -140,35 +113,35 @@ void AndroidGLRendererBackend::initFreeThreadedUpload() { eglMainCtx = eglGetCurrentContext(); if (eglMainCtx == EGL_NO_CONTEXT) { constexpr const char* err = "eglGetCurrentContext returned EGL_NO_CONTEXT"; - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } eglDsply = eglGetCurrentDisplay(); if (eglDsply == EGL_NO_DISPLAY) { constexpr const char* err = "eglGetCurrentDisplay returned EGL_NO_DISPLAY"; - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } eglSurf = eglGetCurrentSurface(EGL_READ); if (eglSurf == EGL_NO_SURFACE) { constexpr const char* err = "eglGetCurrentSurface returned EGL_NO_SURFACE"; - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } EGLSurface writeSurf = eglGetCurrentSurface(EGL_DRAW); if (eglSurf != writeSurf) { constexpr const char* err = "EGL_READ and EGL_DRAW surfaces are different"; - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } int config_id = 0; if (eglQueryContext(eglDsply, eglMainCtx, EGL_CONFIG_ID, &config_id) == EGL_FALSE) { auto err = "eglQueryContext for EGL_CONFIG_ID failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } @@ -176,26 +149,35 @@ void AndroidGLRendererBackend::initFreeThreadedUpload() { const EGLint attribs[] = {EGL_CONFIG_ID, config_id, EGL_NONE}; if (eglChooseConfig(eglDsply, attribs, nullptr, 0, &config_count) == EGL_FALSE) { auto err = "eglChooseConfig failed to query config_count. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } if (config_count != 1) { auto err = "eglChooseConfig returned multiple configs: " + std::to_string(config_count); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } if (eglChooseConfig(eglDsply, attribs, &eglConfig, 1, &config_count) == EGL_FALSE) { auto err = "eglChooseConfig failed to query config. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } if (eglQueryContext(eglDsply, eglMainCtx, EGL_CONTEXT_CLIENT_VERSION, &eglClientVersion) == EGL_FALSE) { auto err = "eglQueryContext for client version failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } + Log::Debug(Event::OpenGL, "Running MapLibre Native with OpenGL ES version " + std::to_string(eglClientVersion)); + if (eglClientVersion < 3) { + // Fence sync objects are not supported with OpenGL ES 2.0. They may be supported as + // extensions but we simply require core OpenGL ES 3.0 to be available to enable shared contexts + Log::Error( + Event::OpenGL, + "Multithreaded resource uploads is not supported with OpenGL ES " + std::to_string(eglClientVersion)); + multiThreadedGpuResourceUpload = false; + } } AndroidUploadThreadContext::AndroidUploadThreadContext(AndroidRendererBackend& backend_, @@ -218,11 +200,10 @@ AndroidUploadThreadContext::~AndroidUploadThreadContext() { } if (ctx == sharedContext) { - mbgl::Log::Error(mbgl::Event::OpenGL, "AndroidUploadThreadContext::destroyContext() must be explicitly called"); + Log::Error(Event::OpenGL, "AndroidUploadThreadContext::destroyContext() must be explicitly called"); } else { - mbgl::Log::Error(mbgl::Event::OpenGL, "Unexpected context bound to an Upload thread"); + Log::Error(Event::OpenGL, "Unexpected context bound to an Upload thread"); } - assert(ctx == EGL_NO_CONTEXT); } void AndroidUploadThreadContext::createContext() { @@ -235,24 +216,28 @@ void AndroidUploadThreadContext::createContext() { assert(sharedContext == EGL_NO_CONTEXT); assert(surface == EGL_NO_SURFACE); +#ifdef MLN_EGL_DEBUG + int attribs[] = {EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL_CONTEXT_OPENGL_DEBUG, EGL_TRUE, EGL_NONE}; +#else int attribs[] = {EGL_CONTEXT_CLIENT_VERSION, clientVersion, EGL_NONE}; +#endif sharedContext = eglCreateContext(display, config, mainContext, attribs); if (sharedContext == EGL_NO_CONTEXT) { auto err = "eglCreateContext returned EGL_NO_CONTEXT. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } surface = eglCreatePbufferSurface(display, config, nullptr); if (surface == EGL_NO_SURFACE) { auto err = "eglCreatePbufferSurface failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } if (eglMakeCurrent(display, surface, surface, sharedContext) == EGL_FALSE) { auto err = "eglMakeCurrent for shared context failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Error(Event::OpenGL, err); throw std::runtime_error(err); } MLN_TRACE_GL_CONTEXT(); @@ -263,35 +248,37 @@ void AndroidUploadThreadContext::destroyContext() { const std::lock_guard lock(getEglMutex()); + // On Mali drivers EGL shared contexts functions fail when the main context is destroyed before the + // shared contexts. i.e. FinalizerDaemon is run after the main context is destroyed. + if (eglWaitClient() == EGL_FALSE) { auto err = "eglWaitClient failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Warning(Event::OpenGL, err); } auto ctx = eglGetCurrentContext(); if (ctx == EGL_NO_CONTEXT) { constexpr const char* err = "AndroidUploadThreadContext::destroyContext() expects a persistently bound EGL shared context"; - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Warning(Event::OpenGL, err); } else if (ctx != sharedContext) { constexpr const char* err = "AndroidUploadThreadContext::destroyContext(): expects a single EGL context to be used in each Upload " "thread"; - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Warning(Event::OpenGL, err); } - assert(ctx == sharedContext); if (eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) == EGL_FALSE) { auto err = "eglMakeCurrent with EGL_NO_CONTEXT failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Warning(Event::OpenGL, err); } if (eglDestroyContext(display, sharedContext) == EGL_FALSE) { auto err = "eglDestroyContext failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Warning(Event::OpenGL, err); } if (eglDestroySurface(display, surface) == EGL_FALSE) { auto err = "eglDestroySurface failed. Error code " + std::to_string(eglGetError()); - mbgl::Log::Error(mbgl::Event::OpenGL, err); + Log::Warning(Event::OpenGL, err); } display = EGL_NO_DISPLAY; diff --git a/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.hpp b/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.hpp index 1052a530a31..a98306e39ad 100644 --- a/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.hpp +++ b/platform/android/MapLibreAndroid/src/cpp/android_gl_renderer_backend.hpp @@ -26,9 +26,8 @@ class AndroidGLRendererBackend : public AndroidRendererBackend, void resizeFramebuffer(int width, int height) override; PremultipliedImage readFramebuffer() override; - bool supportFreeThreadedUpload() const override; + bool supportFreeThreadedUpload() override; std::shared_ptr createUploadThreadContext() override; - void initFreeThreadedUpload() override; // mbgl::gfx::RendererBackend implementation public: @@ -47,6 +46,9 @@ class AndroidGLRendererBackend : public AndroidRendererBackend, mbgl::gl::ProcAddress getExtensionFunctionPointer(const char*) override; void updateAssumedState() override; +private: + void initFreeThreadedUpload(); + private: int eglClientVersion = 0; EGLContext eglMainCtx = EGL_NO_CONTEXT; diff --git a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java index 4a0710ef1cf..fdc17491fb5 100644 --- a/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java +++ b/platform/android/MapLibreAndroid/src/main/java/org/maplibre/android/maps/MapLibreMapOptions.java @@ -94,7 +94,7 @@ public class MapLibreMapOptions implements Parcelable { private boolean crossSourceCollisions = true; - private boolean multiThreadedGpuResourceUploadEnabled = false; + private boolean multiThreadedGpuResourceUploadEnabled = true; /** * Creates a new MapLibreMapOptions object. diff --git a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java index bf5c4f815e2..6385bad09f1 100644 --- a/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java +++ b/platform/android/MapLibreAndroid/src/opengl/java/org/maplibre/android/maps/renderer/egl/EGLContextFactory.java @@ -11,13 +11,24 @@ import javax.microedition.khronos.egl.EGLDisplay; public class EGLContextFactory implements GLSurfaceView.EGLContextFactory { + private static final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; public EGLContext createContext(EGL10 egl, @Nullable EGLDisplay display, @Nullable EGLConfig config) { if (display == null || config == null) { return EGL10.EGL_NO_CONTEXT; } - int[] attrib_list = {0x3098, 2, EGL10.EGL_NONE}; - return egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list); + // Try and get a GLES 3 context + int[] attrib_list = {EGL_CONTEXT_CLIENT_VERSION, 3, EGL10.EGL_NONE}; + EGLContext context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list); + if (context == EGL10.EGL_NO_CONTEXT) { + Log.e("DefaultContextFactory", "Failed to create an OpenGL ES 3 context. Retrying with OpenGL ES 2..."); + attrib_list[1] = 2; + context = egl.eglCreateContext(display, config, EGL10.EGL_NO_CONTEXT, attrib_list); + } + if (context == EGL10.EGL_NO_CONTEXT) { + Log.e("DefaultContextFactory", "Failed to create an OpenGL ES 3 or OpenGL ES 2 context."); + } + return context; } public void destroyContext(EGL10 egl, EGLDisplay display, diff --git a/src/mbgl/gl/buffer_resource.cpp b/src/mbgl/gl/buffer_resource.cpp index 38198d58c50..61a1574d9a5 100644 --- a/src/mbgl/gl/buffer_resource.cpp +++ b/src/mbgl/gl/buffer_resource.cpp @@ -110,10 +110,8 @@ void BufferResource::wait() { } assert(asyncUploadCommands.data.empty()); assert(asyncUploadCommands.type == BufferAsyncUploadCommandType::None); -#ifdef MLN_RENDER_BACKEND_USE_UPLOAD_GL_FENCE gpuFence.gpuWait(); gpuFence.reset(); -#endif asyncUploadIssued = false; asyncUploadRequested = false; } @@ -138,11 +136,7 @@ void BufferResource::issueAsyncUpload() { assert(false); break; } -#ifdef MLN_RENDER_BACKEND_USE_UPLOAD_GL_FENCE gpuFence.insert(); -#else - MBGL_CHECK_ERROR(glFlush()); -#endif cmd.type = BufferAsyncUploadCommandType::None; cmd.dataSize = 0; diff --git a/src/mbgl/gl/renderer_backend.cpp b/src/mbgl/gl/renderer_backend.cpp index 7e0036770a9..b13536d7d4d 100644 --- a/src/mbgl/gl/renderer_backend.cpp +++ b/src/mbgl/gl/renderer_backend.cpp @@ -155,7 +155,6 @@ gl::ResourceUploadThreadPool& RendererBackend::getResourceUploadThreadPool() { throw std::runtime_error("Parallel resource upload is not supported on this backend"); } if (!resourceUploadThreadPool) { - initFreeThreadedUpload(); resourceUploadThreadPool = std::make_unique(*this); } return *resourceUploadThreadPool; diff --git a/test/gl/async_resources.test.cpp b/test/gl/async_resources.test.cpp index 8feb772dbbf..4db37af2748 100644 --- a/test/gl/async_resources.test.cpp +++ b/test/gl/async_resources.test.cpp @@ -33,7 +33,7 @@ class BackendWithMockedSharedContexts : public gl::HeadlessBackend { BackendWithMockedSharedContexts() : gl::HeadlessBackend({32, 32}) {} - bool supportFreeThreadedUpload() const override { return true; } + bool supportFreeThreadedUpload() override { return true; } std::shared_ptr createUploadThreadContext() override; };