Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Android asynchronous rendering #9576

Merged
merged 21 commits into from
Sep 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
9168f8a
[core] remove throttling from geometry tile
ivovandongen Aug 9, 2017
ee8f7f1
[android] remove texture view
ivovandongen Jul 18, 2017
373c959
[android] extract RendererBackend from NativeMapView
ivovandongen Jul 20, 2017
d4bc138
[android] async rendering
ivovandongen Jul 21, 2017
cbb310f
[android] schedule work on the gl thread using GLSurfaceView#queueEvent
ivovandongen Sep 11, 2017
2b92dbb
[android] self-contained map renderer
ivovandongen Sep 14, 2017
37cf403
[android] fix ZoomButtonController initialisation order
ivovandongen Sep 15, 2017
caa16f4
[core] allow context to be abandoned on destruction
ivovandongen Sep 13, 2017
92ec3cf
[android] Re-initialise renderer, backend and context when the androi…
ivovandongen Sep 13, 2017
3c9da2f
[core] send signal to RenderCustomLayer when gl context has been lost
ivovandongen Sep 15, 2017
247aaee
[android] signal renderer when gl surface is re-initialized that gl c…
ivovandongen Sep 15, 2017
7462355
[android] map snapshots
ivovandongen Sep 18, 2017
db7950d
[core] Actor/ActorRef - ask calls to void methods
ivovandongen Sep 18, 2017
f6c6d45
[android] reset renderer on the GL thread when map is destroyed
ivovandongen Sep 18, 2017
c9c2e96
[android] speed up ui tests
ivovandongen Sep 18, 2017
9bdaba9
[android] re-implemented the fps listener api
ivovandongen Sep 18, 2017
2cfe348
[core] custom layer - add contextLost callback
ivovandongen Sep 19, 2017
a95630d
[android] CustomLayer - optionally accept a context lost callback fun…
ivovandongen Sep 19, 2017
1d65f74
[android] example custom layer - add context lost callback
ivovandongen Sep 19, 2017
94c44ab
[android] fix double map activity
ivovandongen Sep 19, 2017
77f3a53
[android] fix map in dialog activity
ivovandongen Sep 19, 2017
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions include/mbgl/actor/message.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,32 @@ class AskMessageImpl : public Message {
std::promise<ResultType> promise;
};

template <class Object, class MemberFn, class ArgsTuple>
class AskMessageImpl<void, Object, MemberFn, ArgsTuple> : public Message {
public:
AskMessageImpl(std::promise<void> promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_)
: object(object_),
memberFn(memberFn_),
argsTuple(std::move(argsTuple_)),
promise(std::move(promise_)) {
}

void operator()() override {
ask(std::make_index_sequence<std::tuple_size<ArgsTuple>::value>());
promise.set_value();
}

template <std::size_t... I>
void ask(std::index_sequence<I...>) {
(object.*memberFn)(std::move(std::get<I>(argsTuple))...);
}

Object& object;
MemberFn memberFn;
ArgsTuple argsTuple;
std::promise<void> promise;
};

namespace actor {

template <class Object, class MemberFn, class... Args>
Expand Down
2 changes: 2 additions & 0 deletions include/mbgl/renderer/renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ class Renderer {
const optional<std::string> programCacheDir = {});
~Renderer();

void markContextLost();

void setObserver(RendererObserver*);

void render(const UpdateParameters&);
Expand Down
16 changes: 16 additions & 0 deletions include/mbgl/style/layers/custom_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ struct CustomLayerRenderParameters {
*/
using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&);

/**
* Called when the system has destroyed the underlying GL context. The
* `CustomLayerDeinitializeFunction` will not be called in this case, however
* `CustomLayerInitializeFunction` will be called instead to prepare for a new render.
*
*/
using CustomLayerContextLostFunction = void (*)(void* context);

/**
* Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
* method is called once, from the main thread, at a point when the GL context is active.
Expand All @@ -51,8 +59,16 @@ class CustomLayer : public Layer {
CustomLayer(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
CustomLayerContextLostFunction,
CustomLayerDeinitializeFunction,
void* context);

CustomLayer(const std::string& id,
CustomLayerInitializeFunction,
CustomLayerRenderFunction,
CustomLayerDeinitializeFunction,
void* context);

~CustomLayer() final;

// Visibility
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
package com.mapbox.mapboxsdk.egl;

import android.opengl.GLSurfaceView;
import android.support.annotation.NonNull;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLDisplay;

import timber.log.Timber;

import static com.mapbox.mapboxsdk.utils.Compare.compare;
import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_MASK_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_ALPHA_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_BLUE_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_BUFFER_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_COLOR_BUFFER_TYPE;
import static javax.microedition.khronos.egl.EGL10.EGL_CONFIG_CAVEAT;
import static javax.microedition.khronos.egl.EGL10.EGL_DEPTH_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_GREEN_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_NONE;
import static javax.microedition.khronos.egl.EGL10.EGL_RED_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_RENDERABLE_TYPE;
import static javax.microedition.khronos.egl.EGL10.EGL_RGB_BUFFER;
import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLES;
import static javax.microedition.khronos.egl.EGL10.EGL_SAMPLE_BUFFERS;
import static javax.microedition.khronos.egl.EGL10.EGL_STENCIL_SIZE;
import static javax.microedition.khronos.egl.EGL10.EGL_SURFACE_TYPE;
import static javax.microedition.khronos.egl.EGL10.EGL_WINDOW_BIT;

/**
* Selects the right EGLConfig needed for `mapbox-gl-native`
*/
public class EGLConfigChooser implements GLSurfaceView.EGLConfigChooser {

/**
* Requires API level 17
*
* @see android.opengl.EGL14.EGL_CONFORMANT;
*/
@SuppressWarnings("JavadocReference")
private static final int EGL_CONFORMANT = 0x3042;

/**
* Requires API level 17
*
* @see android.opengl.EGL14.EGL_OPENGL_ES2_BIT;
*/
@SuppressWarnings("JavadocReference")
private static final int EGL_OPENGL_ES2_BIT = 0x0004;

@Override
public EGLConfig chooseConfig(EGL10 egl, EGLDisplay display) {
int[] configAttribs = getConfigAttributes();

// Determine number of possible configurations
int[] numConfigs = getNumberOfConfigurations(egl, display, configAttribs);
if (numConfigs[0] < 1) {
Timber.e("eglChooseConfig() returned no configs.");
throw new EGLConfigException("eglChooseConfig() failed");
}

// Get all possible configurations
EGLConfig[] possibleConfigurations = getPossibleConfigurations(egl, display, configAttribs, numConfigs);

// Choose best match
EGLConfig config = chooseBestMatchConfig(egl, display, possibleConfigurations);
if (config == null) {
Timber.e("No config chosen");
throw new EGLConfigException("No config chosen");
}

return config;
}

private int[] getNumberOfConfigurations(EGL10 egl, EGLDisplay display, int[] configAttributes) {
int[] numConfigs = new int[1];
if (!egl.eglChooseConfig(display, configAttributes, null, 0, numConfigs)) {
Timber.e("eglChooseConfig(NULL) returned error %d", egl.eglGetError());
throw new EGLConfigException("eglChooseConfig() failed");
}
return numConfigs;
}

private EGLConfig[] getPossibleConfigurations(EGL10 egl, EGLDisplay display,
int[] configAttributes, int[] numConfigs) {
EGLConfig[] configs = new EGLConfig[numConfigs[0]];
if (!egl.eglChooseConfig(display, configAttributes, configs, numConfigs[0], numConfigs)) {
Timber.e("eglChooseConfig() returned error %d", egl.eglGetError());
throw new EGLConfigException("eglChooseConfig() failed");
}
return configs;
}

// Quality
enum BufferFormat {
Format16Bit(3),
Format32BitNoAlpha(1),
Format32BitAlpha(2),
Format24Bit(0),
Unknown(4);

int value;

BufferFormat(int value) {
this.value = value;
}
}

enum DepthStencilFormat {
Format16Depth8Stencil(1),
Format24Depth8Stencil(0);

int value;

DepthStencilFormat(int value) {
this.value = value;
}
}

private EGLConfig chooseBestMatchConfig(EGL10 egl, EGLDisplay display, EGLConfig[] configs) {
class Config implements Comparable<Config> {
private final BufferFormat bufferFormat;
private final DepthStencilFormat depthStencilFormat;
private final boolean isNotConformant;
private final boolean isCaveat;
private final int index;
private final EGLConfig config;

public Config(BufferFormat bufferFormat, DepthStencilFormat depthStencilFormat,
boolean isNotConformant, boolean isCaveat, int index, EGLConfig config) {
this.bufferFormat = bufferFormat;
this.depthStencilFormat = depthStencilFormat;
this.isNotConformant = isNotConformant;
this.isCaveat = isCaveat;
this.index = index;
this.config = config;
}


@Override
public int compareTo(@NonNull Config other) {
int i = compare(bufferFormat.value, other.bufferFormat.value);
if (i != 0) {
return i;
}

i = compare(depthStencilFormat.value, other.depthStencilFormat.value);
if (i != 0) {
return i;
}

i = compare(isNotConformant, other.isNotConformant);
if (i != 0) {
return i;
}

i = compare(isCaveat, other.isCaveat);
if (i != 0) {
return i;
}

i = compare(index, other.index);
if (i != 0) {
return i;
}

return 0;
}
}

List<Config> matches = new ArrayList<>();

int i = 0;
for (EGLConfig config : configs) {
i++;

int caveat = getConfigAttr(egl, display, config, EGL_CONFIG_CAVEAT);
int conformant = getConfigAttr(egl, display, config, EGL_CONFORMANT);
int bits = getConfigAttr(egl, display, config, EGL_BUFFER_SIZE);
int red = getConfigAttr(egl, display, config, EGL_RED_SIZE);
int green = getConfigAttr(egl, display, config, EGL_GREEN_SIZE);
int blue = getConfigAttr(egl, display, config, EGL_BLUE_SIZE);
int alpha = getConfigAttr(egl, display, config, EGL_ALPHA_SIZE);
int alphaMask = getConfigAttr(egl, display, config, EGL_ALPHA_MASK_SIZE);
int depth = getConfigAttr(egl, display, config, EGL_DEPTH_SIZE);
int stencil = getConfigAttr(egl, display, config, EGL_STENCIL_SIZE);
int sampleBuffers = getConfigAttr(egl, display, config, EGL_SAMPLE_BUFFERS);
int samples = getConfigAttr(egl, display, config, EGL_SAMPLES);

boolean 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 = BufferFormat.Format16Bit;
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
bufferFormat = BufferFormat.Format32BitNoAlpha;
} else if ((bits == 32) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 8)) {
bufferFormat = BufferFormat.Format32BitAlpha;
} else if ((bits == 24) && (red == 8) && (green == 8) && (blue == 8) && (alpha == 0)) {
bufferFormat = BufferFormat.Format24Bit;
} else {
bufferFormat = BufferFormat.Unknown;
}

// Work out the config's depth stencil format
DepthStencilFormat depthStencilFormat;
if ((depth == 16) && (stencil == 8)) {
depthStencilFormat = DepthStencilFormat.Format16Depth8Stencil;
} else {
depthStencilFormat = DepthStencilFormat.Format24Depth8Stencil;
}

boolean isNotConformant = (conformant & EGL_OPENGL_ES2_BIT) != EGL_OPENGL_ES2_BIT;
boolean isCaveat = caveat != EGL_NONE;

// Ignore formats we don't recognise
if (bufferFormat != BufferFormat.Unknown) {
matches.add(new Config(bufferFormat, depthStencilFormat, isNotConformant, isCaveat, i, config));
}
}

}

// Sort
Collections.sort(matches);

if (matches.size() == 0) {
throw new EGLConfigException("No matching configurations after filtering");
}

Config bestMatch = matches.get(0);

if (bestMatch.isCaveat) {
Timber.w("Chosen config has a caveat.");
}

if (bestMatch.isNotConformant) {
Timber.w("Chosen config is not conformant.");
}

return bestMatch.config;
}

private int getConfigAttr(EGL10 egl, EGLDisplay display, EGLConfig config, int attributeName) {
int[] attributevalue = new int[1];
if (!egl.eglGetConfigAttrib(display, config, attributeName, attributevalue)) {
Timber.e("eglGetConfigAttrib(%d) returned error %d", attributeName, egl.eglGetError());
throw new EGLConfigException("eglGetConfigAttrib() failed");
}
return attributevalue[0];
}


private int[] getConfigAttributes() {
boolean emulator = inEmulator();
Timber.i("In emulator: %s", emulator);

// Get all configs at least RGB 565 with 16 depth and 8 stencil
return new int[] {
EGL_CONFIG_CAVEAT, EGL_NONE,
EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
EGL_BUFFER_SIZE, 16,
EGL_RED_SIZE, 5,
EGL_GREEN_SIZE, 6,
EGL_BLUE_SIZE, 5,
EGL_ALPHA_SIZE, 0,
EGL_DEPTH_SIZE, 16,
EGL_STENCIL_SIZE, 8,
(emulator ? EGL_NONE : EGL_CONFORMANT), EGL_OPENGL_ES2_BIT,
(emulator ? EGL_NONE : EGL_COLOR_BUFFER_TYPE), EGL_RGB_BUFFER,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
EGL_NONE
};
}

/**
* Detect if we are in emulator.
*/
private boolean inEmulator() {
return System.getProperty("ro.kernel.qemu") != null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.mapbox.mapboxsdk.egl;

/**
* Used for EGL configuration exceptions
*/
public class EGLConfigException extends RuntimeException {
public EGLConfigException() {
}

public EGLConfigException(String message) {
super(message);
}

public EGLConfigException(String message, Throwable cause) {
super(message, cause);
}

public EGLConfigException(Throwable cause) {
super(cause);
}
}
Loading