|
| 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