Apparently some versions of Android, notable 4.4.4 running on my Nexus 5, creates a GLContext on the Android UIThread which is the one we are now using for rendering after #2909. If the context is not restored, nothing gets rendered (or sometimes partially, or artifacts) because Android will try to do GL stuff on the context used by Mapbox GL and mess things up.
703 lines
25 KiB
C++
Executable File
703 lines
25 KiB
C++
Executable File
#include "native_map_view.hpp"
|
|
#include "jni.hpp"
|
|
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <cassert>
|
|
#include <memory>
|
|
#include <list>
|
|
#include <tuple>
|
|
|
|
#include <sys/system_properties.h>
|
|
|
|
#include <GLES2/gl2.h>
|
|
|
|
#include <mbgl/platform/platform.hpp>
|
|
#include <mbgl/platform/event.hpp>
|
|
#include <mbgl/platform/log.hpp>
|
|
#include <mbgl/gl/gl.hpp>
|
|
#include <mbgl/util/constants.hpp>
|
|
|
|
namespace mbgl {
|
|
namespace android {
|
|
|
|
void log_egl_string(EGLDisplay display, EGLint name, const char *label) {
|
|
const char *str = eglQueryString(display, name);
|
|
if (str == nullptr) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglQueryString(%d) returned error %d", name,
|
|
eglGetError());
|
|
throw std::runtime_error("eglQueryString() failed");
|
|
} else {
|
|
char buf[513];
|
|
for (int len = std::strlen(str), pos = 0; len > 0; len -= 512, pos += 512) {
|
|
strncpy(buf, str + pos, 512);
|
|
buf[512] = 0;
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "EGL %s: %s", label, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
void log_gl_string(GLenum name, const char *label) {
|
|
const GLubyte *str = glGetString(name);
|
|
if (str == nullptr) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "glGetString(%d) returned error %d", name,
|
|
glGetError());
|
|
throw std::runtime_error("glGetString() failed");
|
|
} else {
|
|
char buf[513];
|
|
for (int len = std::strlen(reinterpret_cast<const char *>(str)), pos = 0; len > 0;
|
|
len -= 512, pos += 512) {
|
|
strncpy(buf, reinterpret_cast<const char *>(str) + pos, 512);
|
|
buf[512] = 0;
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "GL %s: %s", label, buf);
|
|
}
|
|
}
|
|
}
|
|
|
|
NativeMapView::NativeMapView(JNIEnv *env_, jobject obj_, float pixelRatio_, int availableProcessors_, size_t totalMemory_)
|
|
: mbgl::View(*this),
|
|
env(env_),
|
|
pixelRatio(pixelRatio_),
|
|
availableProcessors(availableProcessors_),
|
|
totalMemory(totalMemory_) {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::NativeMapView");
|
|
|
|
assert(env_ != nullptr);
|
|
assert(obj_ != nullptr);
|
|
|
|
if (env->GetJavaVM(&vm) < 0) {
|
|
env->ExceptionDescribe();
|
|
return;
|
|
}
|
|
|
|
obj = env->NewWeakGlobalRef(obj_);
|
|
if (obj == nullptr) {
|
|
env->ExceptionDescribe();
|
|
return;
|
|
}
|
|
|
|
fileSource = std::make_unique<mbgl::DefaultFileSource>(
|
|
mbgl::android::cachePath + "/mbgl-offline.db",
|
|
mbgl::android::apkPath);
|
|
|
|
map = std::make_unique<mbgl::Map>(*this, *fileSource, MapMode::Continuous);
|
|
|
|
float zoomFactor = map->getMaxZoom() - map->getMinZoom() + 1;
|
|
float cpuFactor = availableProcessors;
|
|
float memoryFactor = static_cast<float>(totalMemory) / 1000.0f / 1000.0f / 1000.0f;
|
|
float sizeFactor = (static_cast<float>(map->getWidth()) / mbgl::util::tileSize) *
|
|
(static_cast<float>(map->getHeight()) / mbgl::util::tileSize);
|
|
|
|
size_t cacheSize = zoomFactor * cpuFactor * memoryFactor * sizeFactor * 0.5f;
|
|
|
|
map->setSourceTileCacheSize(cacheSize);
|
|
}
|
|
|
|
NativeMapView::~NativeMapView() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::~NativeMapView");
|
|
terminateContext();
|
|
destroySurface();
|
|
terminateDisplay();
|
|
|
|
assert(vm != nullptr);
|
|
assert(obj != nullptr);
|
|
|
|
map.reset();
|
|
fileSource.reset();
|
|
|
|
env->DeleteWeakGlobalRef(obj);
|
|
|
|
obj = nullptr;
|
|
env = nullptr;
|
|
vm = nullptr;
|
|
}
|
|
|
|
float NativeMapView::getPixelRatio() const {
|
|
return pixelRatio;
|
|
}
|
|
|
|
std::array<uint16_t, 2> NativeMapView::getSize() const {
|
|
return {{ static_cast<uint16_t>(width), static_cast<uint16_t>(height) }};
|
|
}
|
|
|
|
std::array<uint16_t, 2> NativeMapView::getFramebufferSize() const {
|
|
return {{ static_cast<uint16_t>(fbWidth), static_cast<uint16_t>(fbHeight) }};
|
|
}
|
|
|
|
void NativeMapView::activate() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::activate");
|
|
|
|
oldDisplay = eglGetCurrentDisplay();
|
|
oldReadSurface = eglGetCurrentSurface(EGL_READ);
|
|
oldDrawSurface = eglGetCurrentSurface(EGL_DRAW);
|
|
oldContext = eglGetCurrentContext();
|
|
|
|
assert(vm != nullptr);
|
|
|
|
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE) && (context != EGL_NO_CONTEXT)) {
|
|
if (!eglMakeCurrent(display, surface, surface, context)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglMakeCurrent() failed");
|
|
}
|
|
|
|
if (!eglSwapInterval(display, 0)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapInterval() returned error %d", eglGetError());
|
|
throw std::runtime_error("eglSwapInterval() failed");
|
|
}
|
|
} else {
|
|
mbgl::Log::Info(mbgl::Event::Android, "Not activating as we are not ready");
|
|
}
|
|
}
|
|
|
|
void NativeMapView::deactivate() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::deactivate");
|
|
|
|
assert(vm != nullptr);
|
|
|
|
if (oldContext != context && oldContext != EGL_NO_CONTEXT) {
|
|
if (!eglMakeCurrent(oldDisplay, oldDrawSurface, oldReadSurface, oldContext)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglMakeCurrent() failed");
|
|
}
|
|
} else if (display != EGL_NO_DISPLAY) {
|
|
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent(EGL_NO_CONTEXT) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglMakeCurrent() failed");
|
|
}
|
|
} else {
|
|
mbgl::Log::Info(mbgl::Event::Android, "Not deactivating as we are not ready");
|
|
}
|
|
}
|
|
|
|
void NativeMapView::invalidate() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::invalidate()");
|
|
|
|
assert(vm != nullptr);
|
|
assert(obj != nullptr);
|
|
|
|
env->CallVoidMethod(obj, onInvalidateId);
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
}
|
|
}
|
|
|
|
void NativeMapView::render() {
|
|
activate();
|
|
|
|
if(sizeChanged){
|
|
sizeChanged = false;
|
|
glViewport(0, 0, fbWidth, fbHeight);
|
|
}
|
|
|
|
map->render();
|
|
|
|
if ((display != EGL_NO_DISPLAY) && (surface != EGL_NO_SURFACE)) {
|
|
if (!eglSwapBuffers(display, surface)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglSwapBuffers() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglSwapBuffers() failed");
|
|
}
|
|
|
|
updateFps();
|
|
} else {
|
|
mbgl::Log::Info(mbgl::Event::Android, "Not swapping as we are not ready");
|
|
}
|
|
|
|
deactivate();
|
|
}
|
|
|
|
mbgl::Map &NativeMapView::getMap() { return *map; }
|
|
|
|
mbgl::DefaultFileSource &NativeMapView::getFileSource() { return *fileSource; }
|
|
|
|
bool NativeMapView::inEmulator() {
|
|
// Detect if we are in emulator
|
|
char prop[PROP_VALUE_MAX];
|
|
__system_property_get("ro.kernel.qemu", prop);
|
|
return strtol(prop, nullptr, 0) == 1;
|
|
}
|
|
|
|
void NativeMapView::initializeDisplay() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeDisplay");
|
|
|
|
assert(display == EGL_NO_DISPLAY);
|
|
assert(config == nullptr);
|
|
assert(format < 0);
|
|
|
|
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
|
|
if (display == EGL_NO_DISPLAY) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetDisplay() returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetDisplay() failed");
|
|
}
|
|
|
|
EGLint major, minor;
|
|
if (!eglInitialize(display, &major, &minor)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglInitialize() returned error %d", eglGetError());
|
|
throw std::runtime_error("eglInitialize() failed");
|
|
}
|
|
if ((major <= 1) && (minor < 3)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "EGL version is too low, need 1.3, got %d.%d", major,
|
|
minor);
|
|
throw std::runtime_error("EGL version is too low");
|
|
}
|
|
|
|
log_egl_string(display, EGL_VENDOR, "Vendor");
|
|
log_egl_string(display, EGL_VERSION, "Version");
|
|
log_egl_string(display, EGL_CLIENT_APIS, "Client APIs");
|
|
log_egl_string(display, EGL_EXTENSIONS, "Client Extensions");
|
|
|
|
// Detect if we are in emulator
|
|
if (inEmulator()) {
|
|
mbgl::Log::Warning(mbgl::Event::Android, "In emulator! Enabling hacks :-(");
|
|
}
|
|
|
|
// Get all configs at least RGB 565 with 16 depth and 8 stencil
|
|
EGLint configAttribs[] = {
|
|
EGL_CONFIG_CAVEAT, EGL_NONE, EGL_RENDERABLE_TYPE,
|
|
EGL_OPENGL_ES2_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
|
|
EGL_BUFFER_SIZE, 16, EGL_RED_SIZE,
|
|
5, EGL_GREEN_SIZE, 6,
|
|
EGL_BLUE_SIZE, 5, EGL_DEPTH_SIZE,
|
|
16, EGL_STENCIL_SIZE, 8,
|
|
(inEmulator() ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT, // Ugly hack
|
|
(inEmulator() ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER, // Ugly hack
|
|
EGL_NONE};
|
|
EGLint numConfigs;
|
|
if (!eglChooseConfig(display, configAttribs, nullptr, 0, &numConfigs)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig(NULL) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglChooseConfig() failed");
|
|
}
|
|
if (numConfigs < 1) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned no configs.");
|
|
throw std::runtime_error("eglChooseConfig() failed");
|
|
}
|
|
|
|
const auto configs = std::make_unique<EGLConfig[]>(numConfigs);
|
|
if (!eglChooseConfig(display, configAttribs, configs.get(), numConfigs, &numConfigs)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglChooseConfig() returned error %d", eglGetError());
|
|
throw std::runtime_error("eglChooseConfig() failed");
|
|
}
|
|
|
|
config = chooseConfig(configs.get(), numConfigs);
|
|
if (config == nullptr) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "No config chosen");
|
|
throw std::runtime_error("No config chosen");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglGetConfigAttrib() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen window format is %d", format);
|
|
}
|
|
|
|
void NativeMapView::terminateDisplay() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateDisplay");
|
|
|
|
if (display != EGL_NO_DISPLAY) {
|
|
// Destroy the surface first, if it still exists. This call needs a valid surface.
|
|
if (surface != EGL_NO_SURFACE) {
|
|
if (!eglDestroySurface(display, surface)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglDestroySurface() failed");
|
|
}
|
|
surface = EGL_NO_SURFACE;
|
|
}
|
|
|
|
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglMakeCurrent() failed");
|
|
}
|
|
|
|
if (!eglTerminate(display)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglTerminate() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglTerminate() failed");
|
|
}
|
|
}
|
|
|
|
display = EGL_NO_DISPLAY;
|
|
config = nullptr;
|
|
format = -1;
|
|
}
|
|
|
|
void NativeMapView::initializeContext() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::initializeContext");
|
|
|
|
assert(display != EGL_NO_DISPLAY);
|
|
assert(context == EGL_NO_CONTEXT);
|
|
assert(config != nullptr);
|
|
|
|
const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE};
|
|
context = eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
|
|
if (context == EGL_NO_CONTEXT) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateContext() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglCreateContext() failed");
|
|
}
|
|
}
|
|
|
|
void NativeMapView::terminateContext() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::terminateContext");
|
|
if (display != EGL_NO_DISPLAY) {
|
|
|
|
if (!eglMakeCurrent(display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglMakeCurrent(EGL_NO_CONTEXT) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglMakeCurrent() failed");
|
|
}
|
|
|
|
if (context != EGL_NO_CONTEXT) {
|
|
if (!eglDestroyContext(display, context)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroyContext() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglDestroyContext() failed");
|
|
}
|
|
}
|
|
}
|
|
|
|
context = EGL_NO_CONTEXT;
|
|
}
|
|
|
|
void NativeMapView::createSurface(ANativeWindow *window_) {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::createSurface");
|
|
|
|
assert(window == nullptr);
|
|
assert(window_ != nullptr);
|
|
window = window_;
|
|
|
|
assert(display != EGL_NO_DISPLAY);
|
|
assert(surface == EGL_NO_SURFACE);
|
|
assert(config != nullptr);
|
|
assert(format >= 0);
|
|
|
|
ANativeWindow_setBuffersGeometry(window, 0, 0, format);
|
|
|
|
const EGLint surfaceAttribs[] = {EGL_NONE};
|
|
surface = eglCreateWindowSurface(display, config, window, surfaceAttribs);
|
|
if (surface == EGL_NO_SURFACE) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglCreateWindowSurface() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglCreateWindowSurface() failed");
|
|
}
|
|
|
|
if (!firstTime) {
|
|
firstTime = true;
|
|
|
|
if (!eglMakeCurrent(display, surface, surface, context)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglMakeCurrent() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglMakeCurrent() failed");
|
|
}
|
|
|
|
log_gl_string(GL_VENDOR, "Vendor");
|
|
log_gl_string(GL_RENDERER, "Renderer");
|
|
log_gl_string(GL_VERSION, "Version");
|
|
if (!inEmulator()) {
|
|
log_gl_string(GL_SHADING_LANGUAGE_VERSION,
|
|
"SL Version"); // In the emulator this returns NULL with error code 0?
|
|
// https://code.google.com/p/android/issues/detail?id=78977
|
|
}
|
|
|
|
log_gl_string(GL_EXTENSIONS, "Extensions");
|
|
mbgl::gl::InitializeExtensions([] (const char * name) {
|
|
return reinterpret_cast<mbgl::gl::glProc>(eglGetProcAddress(name));
|
|
});
|
|
}
|
|
}
|
|
|
|
void NativeMapView::destroySurface() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::destroySurface");
|
|
|
|
if (surface != EGL_NO_SURFACE) {
|
|
if (!eglDestroySurface(display, surface)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "eglDestroySurface() returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglDestroySurface() failed");
|
|
}
|
|
}
|
|
|
|
surface = EGL_NO_SURFACE;
|
|
|
|
if (window != nullptr) {
|
|
ANativeWindow_release(window);
|
|
window = nullptr;
|
|
}
|
|
}
|
|
|
|
// Speed
|
|
/*
|
|
typedef enum {
|
|
Format16Bit = 0,
|
|
Format32BitNoAlpha = 1,
|
|
Format32BitAlpha = 2,
|
|
Format24Bit = 3,
|
|
Unknown = 4
|
|
} BufferFormat;
|
|
|
|
typedef enum {
|
|
Format16Depth8Stencil = 0,
|
|
Format24Depth8Stencil = 1,
|
|
} DepthStencilFormat;
|
|
*/
|
|
|
|
// Quality
|
|
typedef enum {
|
|
Format16Bit = 3,
|
|
Format32BitNoAlpha = 1,
|
|
Format32BitAlpha = 2,
|
|
Format24Bit = 0,
|
|
Unknown = 4
|
|
} BufferFormat;
|
|
|
|
typedef enum {
|
|
Format16Depth8Stencil = 1,
|
|
Format24Depth8Stencil = 0,
|
|
} DepthStencilFormat;
|
|
|
|
// Tuple is <buffer_format, depth_stencil_format, is_not_conformant, is_caveat, config_num,
|
|
// config_id>
|
|
typedef std::tuple<BufferFormat, DepthStencilFormat, bool, bool, int, EGLConfig> ConfigProperties;
|
|
|
|
EGLConfig NativeMapView::chooseConfig(const EGLConfig configs[], EGLint numConfigs) {
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "Found %d configs", numConfigs);
|
|
|
|
// Create a list of configs that pass our filters
|
|
std::list<ConfigProperties> configList;
|
|
for (int i = 0; i < numConfigs; i++) {
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "Config %d:", i);
|
|
|
|
EGLint caveat, conformant, bits, red, green, blue, alpha, alphaMask, depth, stencil,
|
|
sampleBuffers, samples;
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFIG_CAVEAT, &caveat)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_CONFIG_CAVEAT) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_CONFORMANT, &conformant)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_CONFORMANT) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_BUFFER_SIZE, &bits)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_BUFFER_SIZE) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_RED_SIZE, &red)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_RED_SIZE) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_GREEN_SIZE, &green)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_GREEN_SIZE) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_BLUE_SIZE, &blue)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_BLUE_SIZE) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_SIZE, &alpha)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_ALPHA_SIZE) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_ALPHA_MASK_SIZE, &alphaMask)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_ALPHA_MASK_SIZE) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_DEPTH_SIZE, &depth)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_DEPTH_SIZE) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_STENCIL_SIZE, &stencil)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_STENCIL_SIZE) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLE_BUFFERS, &sampleBuffers)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_SAMPLE_BUFFERS) returned error %d",
|
|
eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
if (!eglGetConfigAttrib(display, configs[i], EGL_SAMPLES, &samples)) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL,
|
|
"eglGetConfigAttrib(EGL_SAMPLES) returned error %d", eglGetError());
|
|
throw std::runtime_error("eglGetConfigAttrib() failed");
|
|
}
|
|
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Caveat: %d", caveat);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Conformant: %d", conformant);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Color: %d", bits);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Red: %d", red);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Green: %d", green);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Blue: %d", blue);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Alpha: %d", alpha);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Alpha mask: %d", alphaMask);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Depth: %d", depth);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Stencil: %d", stencil);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Sample buffers: %d", sampleBuffers);
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "...Samples: %d", samples);
|
|
|
|
bool configOk = true;
|
|
configOk &= (depth == 24) || (depth == 16);
|
|
configOk &= stencil == 8;
|
|
configOk &= sampleBuffers == 0;
|
|
configOk &= samples == 0;
|
|
|
|
// Filter our configs first for depth, stencil and anti-aliasing
|
|
if (configOk) {
|
|
// Work out the config's buffer format
|
|
BufferFormat bufferFormat;
|
|
if ((bits == 16) && (red == 5) && (green == 6) && (blue == 5) && (alpha == 0)) {
|
|
bufferFormat = Format16Bit;
|
|
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
|
|
bufferFormat = Format32BitNoAlpha;
|
|
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
|
|
bufferFormat = Format32BitAlpha;
|
|
} else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
|
|
bufferFormat = Format24Bit;
|
|
} else {
|
|
bufferFormat = Unknown;
|
|
}
|
|
|
|
// Work out the config's depth stencil format
|
|
DepthStencilFormat depthStencilFormat;
|
|
if ((depth == 16) && (stencil == 8)) {
|
|
depthStencilFormat = Format16Depth8Stencil;
|
|
} else {
|
|
depthStencilFormat = Format24Depth8Stencil;
|
|
}
|
|
|
|
bool isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
|
|
bool isCaveat = caveat != EGL_NONE;
|
|
EGLConfig configId = configs[i];
|
|
|
|
// Ignore formats we don't recognise
|
|
if (bufferFormat != Unknown) {
|
|
configList.push_back(std::make_tuple(bufferFormat, depthStencilFormat,
|
|
isNotConformant, isCaveat, i, configId));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (configList.empty()) {
|
|
mbgl::Log::Error(mbgl::Event::OpenGL, "Config list was empty.");
|
|
}
|
|
|
|
// Sort the configs to find the best one
|
|
configList.sort();
|
|
bool isConformant = !std::get<2>(configList.front());
|
|
bool isCaveat = std::get<3>(configList.front());
|
|
int configNum = std::get<4>(configList.front());
|
|
EGLConfig configId = std::get<5>(configList.front());
|
|
|
|
mbgl::Log::Info(mbgl::Event::OpenGL, "Chosen config is %d", configNum);
|
|
|
|
if (isCaveat) {
|
|
mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config has a caveat.");
|
|
}
|
|
if (!isConformant) {
|
|
mbgl::Log::Warning(mbgl::Event::OpenGL, "Chosen config is not conformant.");
|
|
}
|
|
|
|
return configId;
|
|
}
|
|
|
|
void NativeMapView::notifyMapChange(mbgl::MapChange change) {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::notifyMapChange()");
|
|
|
|
assert(vm != nullptr);
|
|
assert(obj != nullptr);
|
|
|
|
env->CallVoidMethod(obj, onMapChangedId, change);
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
}
|
|
}
|
|
|
|
void NativeMapView::enableFps(bool enable) {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::enableFps()");
|
|
|
|
fpsEnabled = enable;
|
|
}
|
|
|
|
void NativeMapView::updateFps() {
|
|
mbgl::Log::Debug(mbgl::Event::Android, "NativeMapView::updateFps()");
|
|
|
|
if (!fpsEnabled) {
|
|
return;
|
|
}
|
|
|
|
static int frames = 0;
|
|
static int64_t timeElapsed = 0LL;
|
|
|
|
frames++;
|
|
struct timespec now;
|
|
clock_gettime(CLOCK_MONOTONIC, &now);
|
|
int64_t currentTime = now.tv_sec * 1000000000LL + now.tv_nsec;
|
|
|
|
if (currentTime - timeElapsed >= 1) {
|
|
fps = frames / ((currentTime - timeElapsed) / 1E9);
|
|
mbgl::Log::Debug(mbgl::Event::Render, "FPS: %4.2f", fps);
|
|
timeElapsed = currentTime;
|
|
frames = 0;
|
|
}
|
|
|
|
assert(vm != nullptr);
|
|
assert(obj != nullptr);
|
|
|
|
env->CallVoidMethod(obj, onFpsChangedId, fps);
|
|
if (env->ExceptionCheck()) {
|
|
env->ExceptionDescribe();
|
|
}
|
|
}
|
|
|
|
void NativeMapView::resizeView(int w, int h) {
|
|
width = w;
|
|
height = h;
|
|
sizeChanged = true;
|
|
map->update(mbgl::Update::Dimensions);
|
|
}
|
|
|
|
void NativeMapView::resizeFramebuffer(int w, int h) {
|
|
fbWidth = w;
|
|
fbHeight = h;
|
|
map->update(mbgl::Update::Repaint);
|
|
}
|
|
|
|
void NativeMapView::setInsets(mbgl::EdgeInsets insets_) {
|
|
insets = insets_;
|
|
}
|
|
|
|
}
|
|
}
|