Skip to content
This repository was archived by the owner on Jun 21, 2023. It is now read-only.

Commit 735dff5

Browse files
authored
[sdk] Add CustomLayer interface and example (#508)
* [sdk] Add CustomLayer interface and example
1 parent 345b4a7 commit 735dff5

File tree

5 files changed

+425
-0
lines changed

5 files changed

+425
-0
lines changed
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
#pragma once
2+
3+
#include <array>
4+
5+
// CustomLayerRenderParameters and CustomLayerHost represent public C++ API for the CustomLayer
6+
// See more details https://github.com/mapbox/mapbox-gl-native/blob/master/include/mbgl/gl/custom_layer.hpp
7+
8+
namespace mbgl {
9+
namespace style {
10+
11+
/**
12+
* Parameters that define the current camera position for a `CustomLayerHost::render()` function.
13+
*/
14+
struct CustomLayerRenderParameters {
15+
double width;
16+
double height;
17+
double latitude;
18+
double longitude;
19+
double zoom;
20+
double bearing;
21+
double pitch;
22+
double fieldOfView;
23+
std::array<double, 16> projectionMatrix;
24+
};
25+
26+
class CustomLayerHost {
27+
public:
28+
virtual ~CustomLayerHost() = default;
29+
30+
/**
31+
* Initialize any GL state needed by the custom layer. This method is called once, from the
32+
* main thread, at a point when the GL context is active but before rendering for the first
33+
* time.
34+
*
35+
* Resources that are acquired in this method must be released in the `deinitialize` function.
36+
*/
37+
virtual void initialize() = 0;
38+
39+
/**
40+
* Render the layer. This method is called once per frame. The implementation should not make
41+
* any assumptions about the GL state (other than that the correct context is active). It may
42+
* make changes to the state, and is not required to reset values such as the depth mask, stencil
43+
* mask, and corresponding test flags to their original values.
44+
* Make sure that you are drawing your fragments with a z value of 1 to take advantage of the
45+
* opaque fragment culling in case there are opaque layers above your custom layer.
46+
*/
47+
virtual void render(const CustomLayerRenderParameters &) = 0;
48+
49+
/**
50+
* Called when the system has destroyed the underlying GL context. The
51+
* `deinitialize` function will not be called in this case, however
52+
* `initialize` will be called instead to prepare for a new render.
53+
*
54+
*/
55+
virtual void contextLost() = 0;
56+
57+
/**
58+
* Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This
59+
* method is called once, from the main thread, at a point when the GL context is active.
60+
*
61+
* Note that it may be called even when the `initialize` function has not been called.
62+
*/
63+
virtual void deinitialize() = 0;
64+
};
65+
66+
}
67+
}

MapboxGLAndroidSDKTestApp/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
apply plugin: 'com.android.application'
2+
apply from: "${rootDir}/gradle/native-build.gradle"
23

34
android {
45
compileSdkVersion androidVersions.compileSdkVersion
@@ -17,6 +18,8 @@ android {
1718
targetCompatibility JavaVersion.VERSION_1_8
1819
}
1920

21+
nativeBuild(["example-custom-layer"])
22+
2023
packagingOptions {
2124
exclude 'META-INF/LICENSE.txt'
2225
exclude 'META-INF/NOTICE.txt'
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
cmake_minimum_required(VERSION 3.10)
2+
3+
if (NOT ANDROID_NDK_TOOLCHAIN_INCLUDED)
4+
message(FATAL_ERROR "-- Toolchain file not included, see https://developer.android.com/ndk/guides/cmake")
5+
endif ()
6+
7+
add_library(
8+
example-custom-layer MODULE
9+
${PROJECT_SOURCE_DIR}/example_custom_layer.cpp
10+
)
11+
12+
target_include_directories(
13+
example-custom-layer
14+
PRIVATE
15+
../../../../MapboxGLAndroidSDK/src/main/cpp
16+
)
17+
18+
# Use the same compile_options as in mapbox-gl
19+
target_compile_options(
20+
example-custom-layer
21+
PRIVATE
22+
$<$<CONFIG:Release>:-Oz>
23+
$<$<CONFIG:Release>:-Qunused-arguments>
24+
$<$<CONFIG:Release>:-flto>
25+
$<$<CONFIG:Release>:-fvisibility=hidden>
26+
$<$<CONFIG:Release>:-fvisibility-inlines-hidden>)
27+
28+
# Use the same linker options as in mapbox-gl
29+
target_link_libraries(
30+
example-custom-layer
31+
PRIVATE
32+
GLESv2
33+
log
34+
$<$<CONFIG:Release>:-Wl,--gc-sections>
35+
$<$<CONFIG:Release>:-Wl,--icf=all>
36+
$<$<CONFIG:Release>:-flto>
37+
$<$<CONFIG:Release>:-fuse-ld=lld>
38+
$<$<CONFIG:Release>:-Wl,--lto-new-pass-manager>
39+
$<$<CONFIG:Release>:-Wl,--pack-dyn-relocs=android>
40+
$<$<CONFIG:Release>:-Wl,--lto-O3>
41+
)
42+
Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
#include <GLES2/gl2.h>
2+
#include <android/log.h>
3+
#include <jni.h>
4+
#include <sstream>
5+
#include "custom_layer.hpp"
6+
7+
8+
// DEBUGGING
9+
10+
const char* LOG_TAG = "Custom Layer Example";
11+
12+
const char* stringFromError(GLenum err) {
13+
switch (err) {
14+
case GL_INVALID_ENUM:
15+
return "GL_INVALID_ENUM";
16+
17+
case GL_INVALID_VALUE:
18+
return "GL_INVALID_VALUE";
19+
20+
case GL_INVALID_OPERATION:
21+
return "GL_INVALID_OPERATION";
22+
23+
case GL_INVALID_FRAMEBUFFER_OPERATION:
24+
return "GL_INVALID_FRAMEBUFFER_OPERATION";
25+
26+
case GL_OUT_OF_MEMORY:
27+
return "GL_OUT_OF_MEMORY";
28+
29+
#ifdef GL_TABLE_TOO_LARGE
30+
case GL_TABLE_TOO_LARGE:
31+
return "GL_TABLE_TOO_LARGE";
32+
#endif
33+
34+
#ifdef GL_STACK_OVERFLOW
35+
case GL_STACK_OVERFLOW:
36+
return "GL_STACK_OVERFLOW";
37+
#endif
38+
39+
#ifdef GL_STACK_UNDERFLOW
40+
case GL_STACK_UNDERFLOW:
41+
return "GL_STACK_UNDERFLOW";
42+
#endif
43+
44+
#ifdef GL_CONTEXT_LOST
45+
case GL_CONTEXT_LOST:
46+
return "GL_CONTEXT_LOST";
47+
#endif
48+
49+
default:
50+
return "GL_UNKNOWN";
51+
}
52+
}
53+
54+
struct Error : std::runtime_error {
55+
using std::runtime_error::runtime_error;
56+
};
57+
58+
void checkError(const char *cmd, const char *file, int line) {
59+
60+
GLenum err = GL_NO_ERROR;
61+
if ((err = glGetError()) != GL_NO_ERROR) {
62+
std::ostringstream message;
63+
message << cmd << ": Error " << stringFromError(err);
64+
65+
// Check for further errors
66+
while ((err = glGetError()) != GL_NO_ERROR) {
67+
message << ", " << stringFromError(err);
68+
}
69+
70+
message << " at " << file << ":" << line;
71+
__android_log_write(ANDROID_LOG_ERROR, LOG_TAG, message.str().c_str());
72+
throw Error(message.str());
73+
}
74+
}
75+
76+
#ifndef NDEBUG
77+
#define GL_CHECK_ERROR(cmd) ([&]() { struct __MBGL_C_E { ~__MBGL_C_E() noexcept(false) { checkError(#cmd, __FILE__, __LINE__); } } __MBGL_C_E; return cmd; }())
78+
#else
79+
#define GL_CHECK_ERROR(cmd) (cmd)
80+
#endif
81+
82+
void checkLinkStatus(GLuint program) {
83+
GLint isLinked = 0;
84+
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
85+
if (isLinked == GL_FALSE) {
86+
GLint maxLength = 0;
87+
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &maxLength);
88+
GLchar infoLog[maxLength];
89+
glGetProgramInfoLog(program, maxLength, &maxLength, &infoLog[0]);
90+
__android_log_write(ANDROID_LOG_ERROR, LOG_TAG, &infoLog[0]);
91+
throw Error(infoLog);
92+
}
93+
94+
}
95+
96+
void checkCompileStatus(GLuint shader) {
97+
GLint isCompiled = 0;
98+
glGetShaderiv(shader, GL_COMPILE_STATUS, &isCompiled);
99+
if (isCompiled == GL_FALSE) {
100+
GLint maxLength = 0;
101+
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &maxLength);
102+
103+
// The maxLength includes the NULL character
104+
GLchar errorLog[maxLength];
105+
glGetShaderInfoLog(shader, maxLength, &maxLength, &errorLog[0]);
106+
__android_log_write(ANDROID_LOG_ERROR, LOG_TAG, &errorLog[0]);
107+
throw Error(errorLog);
108+
}
109+
}
110+
111+
// /DEBUGGING
112+
113+
static const GLchar * vertexShaderSource = "attribute vec2 a_pos; void main() { gl_Position = vec4(a_pos, 0, 1); }";
114+
static const GLchar * fragmentShaderSource = "uniform highp vec4 fill_color; void main() { gl_FragColor = fill_color; }";
115+
116+
class ExampleCustomLayer: mbgl::style::CustomLayerHost {
117+
public:
118+
~ExampleCustomLayer() {
119+
}
120+
121+
void initialize() {
122+
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Initialize");
123+
124+
// Debug info
125+
int maxAttrib;
126+
GL_CHECK_ERROR(glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &maxAttrib));
127+
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Max vertex attributes: %i", maxAttrib);
128+
129+
program = GL_CHECK_ERROR(glCreateProgram());
130+
vertexShader = GL_CHECK_ERROR(glCreateShader(GL_VERTEX_SHADER));
131+
fragmentShader = GL_CHECK_ERROR(glCreateShader(GL_FRAGMENT_SHADER));
132+
133+
GL_CHECK_ERROR(glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr));
134+
GL_CHECK_ERROR(glCompileShader(vertexShader));
135+
checkCompileStatus(vertexShader);
136+
GL_CHECK_ERROR(glAttachShader(program, vertexShader));
137+
GL_CHECK_ERROR(glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr));
138+
GL_CHECK_ERROR(glCompileShader(fragmentShader));
139+
checkCompileStatus(fragmentShader);
140+
GL_CHECK_ERROR(glAttachShader(program, fragmentShader));
141+
GL_CHECK_ERROR(glLinkProgram(program));
142+
checkLinkStatus(program);
143+
144+
a_pos = GL_CHECK_ERROR(glGetAttribLocation(program, "a_pos"));
145+
fill_color = GL_CHECK_ERROR(glGetUniformLocation(program, "fill_color"));
146+
147+
GLfloat background[] = { -1, -1, 1, -1, -1, 1, 1, 1 };
148+
GL_CHECK_ERROR(glGenBuffers(1, &buffer));
149+
GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
150+
GL_CHECK_ERROR(glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), background, GL_STATIC_DRAW));
151+
}
152+
153+
void render(const mbgl::style::CustomLayerRenderParameters&) {
154+
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "Render");
155+
glUseProgram(program);
156+
glBindBuffer(GL_ARRAY_BUFFER, buffer);
157+
glEnableVertexAttribArray(a_pos);
158+
glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL);
159+
glDisable(GL_STENCIL_TEST);
160+
glDisable(GL_DEPTH_TEST);
161+
glUniform4fv(fill_color, 1, color);
162+
163+
GL_CHECK_ERROR(glUseProgram(program));
164+
GL_CHECK_ERROR(glBindBuffer(GL_ARRAY_BUFFER, buffer));
165+
GL_CHECK_ERROR(glEnableVertexAttribArray(a_pos));
166+
GL_CHECK_ERROR(glVertexAttribPointer(a_pos, 2, GL_FLOAT, GL_FALSE, 0, NULL));
167+
GL_CHECK_ERROR(glDisable(GL_STENCIL_TEST));
168+
GL_CHECK_ERROR(glDisable(GL_DEPTH_TEST));
169+
GL_CHECK_ERROR(glUniform4fv(fill_color, 1, color));
170+
GL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
171+
172+
}
173+
174+
void contextLost() {
175+
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "ContextLost");
176+
program = 0;
177+
}
178+
179+
void deinitialize() {
180+
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "DeInitialize");
181+
if (program) {
182+
glDeleteBuffers(1, &buffer);
183+
glDetachShader(program, vertexShader);
184+
glDetachShader(program, fragmentShader);
185+
glDeleteShader(vertexShader);
186+
glDeleteShader(fragmentShader);
187+
glDeleteProgram(program);
188+
}
189+
}
190+
191+
GLuint program = 0;
192+
GLuint vertexShader = 0;
193+
GLuint fragmentShader = 0;
194+
GLuint buffer = 0;
195+
GLuint a_pos = 0;
196+
GLuint fill_color = 0;
197+
198+
static GLfloat color[];
199+
};
200+
201+
GLfloat ExampleCustomLayer::color[] = { 0.0f, 1.0f, 0.0f, 1.0f };
202+
203+
jlong JNICALL nativeCreateContext(JNIEnv*, jobject) {
204+
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "nativeCreateContext");
205+
auto exampleCustomLayer = std::make_unique<ExampleCustomLayer>();
206+
return reinterpret_cast<jlong>(exampleCustomLayer.release());
207+
}
208+
209+
void JNICALL nativeSetColor(JNIEnv*, jobject, jfloat red, jfloat green, jfloat blue, jfloat alpha) {
210+
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "nativeSetColor: %.2f, %.2f, %.2f, %.2f", red, green, blue, alpha);
211+
ExampleCustomLayer::color[0] = red;
212+
ExampleCustomLayer::color[1] = green;
213+
ExampleCustomLayer::color[2] = blue;
214+
ExampleCustomLayer::color[3] = alpha;
215+
}
216+
217+
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
218+
__android_log_write(ANDROID_LOG_INFO, LOG_TAG, "OnLoad");
219+
220+
JNIEnv *env = nullptr;
221+
vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6);
222+
223+
jclass customLayerClass = env->FindClass("com/mapbox/mapboxsdk/testapp/model/customlayer/ExampleCustomLayer");
224+
225+
JNINativeMethod methods[] = {
226+
{"createContext", "()J", reinterpret_cast<void *>(&nativeCreateContext)},
227+
{"setColor", "(FFFF)V", reinterpret_cast<void *>(&nativeSetColor)}
228+
};
229+
230+
if (env->RegisterNatives(customLayerClass, methods, 2) < 0) {
231+
env->ExceptionDescribe();
232+
return JNI_ERR;
233+
}
234+
235+
return JNI_VERSION_1_6;
236+
}
237+
238+
extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *, void *) {
239+
}

0 commit comments

Comments
 (0)