Skip to content

Commit

Permalink
Use multi-threaded uploads by default. Always use fences to synchroni…
Browse files Browse the repository at this point in the history
…ze GL contexts but only use fences with GLES 3.
  • Loading branch information
alasram committed Sep 21, 2024
1 parent d5857a1 commit 20ba5d1
Show file tree
Hide file tree
Showing 8 changed files with 65 additions and 73 deletions.
3 changes: 1 addition & 2 deletions include/mbgl/gl/renderer_backend.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<UploadThreadContext> createUploadThreadContext() { return nullptr; }
gl::ResourceUploadThreadPool& getResourceUploadThreadPool();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@
#include <mbgl/util/logging.hpp>
#include <mbgl/util/string.hpp>

#include <sys/system_properties.h>

#include <cassert>
#include <mutex>
#include <stdexcept>
Expand All @@ -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 {
Expand All @@ -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<AndroidGLRenderableResource>(*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<AndroidGLRenderableResource>(*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();
}

Expand Down Expand Up @@ -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<gl::UploadThreadContext> AndroidGLRendererBackend::createUploadThreadContext() {
Expand All @@ -140,62 +113,71 @@ 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);
}

int config_count = 0;
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_,
Expand All @@ -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() {
Expand All @@ -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();
Expand All @@ -263,35 +248,37 @@ void AndroidUploadThreadContext::destroyContext() {

const std::lock_guard<std::mutex> 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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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<gl::UploadThreadContext> createUploadThreadContext() override;
void initFreeThreadedUpload() override;

// mbgl::gfx::RendererBackend implementation
public:
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
6 changes: 0 additions & 6 deletions src/mbgl/gl/buffer_resource.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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;
Expand Down
1 change: 0 additions & 1 deletion src/mbgl/gl/renderer_backend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<gl::ResourceUploadThreadPool>(*this);
}
return *resourceUploadThreadPool;
Expand Down
2 changes: 1 addition & 1 deletion test/gl/async_resources.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<gl::UploadThreadContext> createUploadThreadContext() override;
};
Expand Down

0 comments on commit 20ba5d1

Please sign in to comment.