1#include <jni.h> 2#include <JNIHelp.h> 3#include <android_runtime/AndroidRuntime.h> 4#include <utils/misc.h> 5#include <assert.h> 6 7static int initialized = 0; 8 9static jclass nioAccessClass; 10static jclass bufferClass; 11static jmethodID getBasePointerID; 12static jmethodID getBaseArrayID; 13static jmethodID getBaseArrayOffsetID; 14static jfieldID positionID; 15static jfieldID limitID; 16static jfieldID elementSizeShiftID; 17 18 19/* special calls implemented in Android's GLES wrapper used to more 20 * efficiently bound-check passed arrays */ 21extern "C" { 22#ifdef GL_VERSION_ES_CM_1_1 23GL_API void GL_APIENTRY glColorPointerBounds(GLint size, GLenum type, GLsizei stride, 24 const GLvoid *ptr, GLsizei count); 25GL_API void GL_APIENTRY glNormalPointerBounds(GLenum type, GLsizei stride, 26 const GLvoid *pointer, GLsizei count); 27GL_API void GL_APIENTRY glTexCoordPointerBounds(GLint size, GLenum type, 28 GLsizei stride, const GLvoid *pointer, GLsizei count); 29GL_API void GL_APIENTRY glVertexPointerBounds(GLint size, GLenum type, 30 GLsizei stride, const GLvoid *pointer, GLsizei count); 31GL_API void GL_APIENTRY glPointSizePointerOESBounds(GLenum type, 32 GLsizei stride, const GLvoid *pointer, GLsizei count); 33GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type, 34 GLsizei stride, const GLvoid *pointer, GLsizei count); 35GL_API void GL_APIENTRY glWeightPointerOESBounds(GLint size, GLenum type, 36 GLsizei stride, const GLvoid *pointer, GLsizei count); 37#endif 38#ifdef GL_ES_VERSION_2_0 39static void glVertexAttribPointerBounds(GLuint indx, GLint size, GLenum type, 40 GLboolean normalized, GLsizei stride, const GLvoid *pointer, GLsizei count) { 41 glVertexAttribPointer(indx, size, type, normalized, stride, pointer); 42} 43#endif 44#ifdef GL_ES_VERSION_3_0 45static void glVertexAttribIPointerBounds(GLuint indx, GLint size, GLenum type, 46 GLsizei stride, const GLvoid *pointer, GLsizei count) { 47 glVertexAttribIPointer(indx, size, type, stride, pointer); 48} 49#endif 50} 51 52/* Cache method IDs each time the class is loaded. */ 53 54static void 55nativeClassInit(JNIEnv *_env, jclass glImplClass) 56{ 57 jclass nioAccessClassLocal = _env->FindClass("java/nio/NIOAccess"); 58 nioAccessClass = (jclass) _env->NewGlobalRef(nioAccessClassLocal); 59 60 jclass bufferClassLocal = _env->FindClass("java/nio/Buffer"); 61 bufferClass = (jclass) _env->NewGlobalRef(bufferClassLocal); 62 63 getBasePointerID = _env->GetStaticMethodID(nioAccessClass, 64 "getBasePointer", "(Ljava/nio/Buffer;)J"); 65 getBaseArrayID = _env->GetStaticMethodID(nioAccessClass, 66 "getBaseArray", "(Ljava/nio/Buffer;)Ljava/lang/Object;"); 67 getBaseArrayOffsetID = _env->GetStaticMethodID(nioAccessClass, 68 "getBaseArrayOffset", "(Ljava/nio/Buffer;)I"); 69 70 positionID = _env->GetFieldID(bufferClass, "position", "I"); 71 limitID = _env->GetFieldID(bufferClass, "limit", "I"); 72 elementSizeShiftID = 73 _env->GetFieldID(bufferClass, "_elementSizeShift", "I"); 74} 75 76static void * 77getPointer(JNIEnv *_env, jobject buffer, jarray *array, jint *remaining, jint *offset) 78{ 79 jint position; 80 jint limit; 81 jint elementSizeShift; 82 jlong pointer; 83 84 position = _env->GetIntField(buffer, positionID); 85 limit = _env->GetIntField(buffer, limitID); 86 elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); 87 *remaining = (limit - position) << elementSizeShift; 88 pointer = _env->CallStaticLongMethod(nioAccessClass, 89 getBasePointerID, buffer); 90 if (pointer != 0L) { 91 *array = NULL; 92 return reinterpret_cast<void*>(pointer); 93 } 94 95 *array = (jarray) _env->CallStaticObjectMethod(nioAccessClass, 96 getBaseArrayID, buffer); 97 *offset = _env->CallStaticIntMethod(nioAccessClass, 98 getBaseArrayOffsetID, buffer); 99 100 return NULL; 101} 102 103class ByteArrayGetter { 104public: 105 static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) { 106 return _env->GetByteArrayElements(array, is_copy); 107 } 108}; 109class BooleanArrayGetter { 110public: 111 static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) { 112 return _env->GetBooleanArrayElements(array, is_copy); 113 } 114}; 115class CharArrayGetter { 116public: 117 static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) { 118 return _env->GetCharArrayElements(array, is_copy); 119 } 120}; 121class ShortArrayGetter { 122public: 123 static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) { 124 return _env->GetShortArrayElements(array, is_copy); 125 } 126}; 127class IntArrayGetter { 128public: 129 static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) { 130 return _env->GetIntArrayElements(array, is_copy); 131 } 132}; 133class LongArrayGetter { 134public: 135 static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) { 136 return _env->GetLongArrayElements(array, is_copy); 137 } 138}; 139class FloatArrayGetter { 140public: 141 static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) { 142 return _env->GetFloatArrayElements(array, is_copy); 143 } 144}; 145class DoubleArrayGetter { 146public: 147 static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) { 148 return _env->GetDoubleArrayElements(array, is_copy); 149 } 150}; 151 152template<typename JTYPEARRAY, typename ARRAYGETTER> 153static void* 154getArrayPointer(JNIEnv *_env, JTYPEARRAY array, jboolean* is_copy) { 155 return ARRAYGETTER::Get(_env, array, is_copy); 156} 157 158class ByteArrayReleaser { 159public: 160 static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jboolean commit) { 161 _env->ReleaseByteArrayElements(array, data, commit ? 0 : JNI_ABORT); 162 } 163}; 164class BooleanArrayReleaser { 165public: 166 static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jboolean commit) { 167 _env->ReleaseBooleanArrayElements(array, data, commit ? 0 : JNI_ABORT); 168 } 169}; 170class CharArrayReleaser { 171public: 172 static void Release(JNIEnv* _env, jcharArray array, jchar* data, jboolean commit) { 173 _env->ReleaseCharArrayElements(array, data, commit ? 0 : JNI_ABORT); 174 } 175}; 176class ShortArrayReleaser { 177public: 178 static void Release(JNIEnv* _env, jshortArray array, jshort* data, jboolean commit) { 179 _env->ReleaseShortArrayElements(array, data, commit ? 0 : JNI_ABORT); 180 } 181}; 182class IntArrayReleaser { 183public: 184 static void Release(JNIEnv* _env, jintArray array, jint* data, jboolean commit) { 185 _env->ReleaseIntArrayElements(array, data, commit ? 0 : JNI_ABORT); 186 } 187}; 188class LongArrayReleaser { 189public: 190 static void Release(JNIEnv* _env, jlongArray array, jlong* data, jboolean commit) { 191 _env->ReleaseLongArrayElements(array, data, commit ? 0 : JNI_ABORT); 192 } 193}; 194class FloatArrayReleaser { 195public: 196 static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jboolean commit) { 197 _env->ReleaseFloatArrayElements(array, data, commit ? 0 : JNI_ABORT); 198 } 199}; 200class DoubleArrayReleaser { 201public: 202 static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jboolean commit) { 203 _env->ReleaseDoubleArrayElements(array, data, commit ? 0 : JNI_ABORT); 204 } 205}; 206 207template<typename JTYPEARRAY, typename NTYPEARRAY, typename ARRAYRELEASER> 208static void 209releaseArrayPointer(JNIEnv *_env, JTYPEARRAY array, NTYPEARRAY data, jboolean commit) { 210 ARRAYRELEASER::Release(_env, array, data, commit); 211} 212 213static void 214releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit) 215{ 216 _env->ReleasePrimitiveArrayCritical(array, data, 217 commit ? 0 : JNI_ABORT); 218} 219 220static void * 221getDirectBufferPointer(JNIEnv *_env, jobject buffer) { 222 char* buf = (char*) _env->GetDirectBufferAddress(buffer); 223 if (buf) { 224 jint position = _env->GetIntField(buffer, positionID); 225 jint elementSizeShift = _env->GetIntField(buffer, elementSizeShiftID); 226 buf += position << elementSizeShift; 227 } else { 228 jniThrowException(_env, "java/lang/IllegalArgumentException", 229 "Must use a native order direct Buffer"); 230 } 231 return (void*) buf; 232} 233 234// -------------------------------------------------------------------------- 235 236/* 237 * returns the number of values glGet returns for a given pname. 238 * 239 * The code below is written such that pnames requiring only one values 240 * are the default (and are not explicitely tested for). This makes the 241 * checking code much shorter/readable/efficient. 242 * 243 * This means that unknown pnames (e.g.: extensions) will default to 1. If 244 * that unknown pname needs more than 1 value, then the validation check 245 * is incomplete and the app may crash if it passed the wrong number params. 246 */ 247static int getNeededCount(GLint pname) { 248 int needed = 1; 249#ifdef GL_ES_VERSION_3_0 250 // GLES 3.x pnames 251 switch (pname) { 252 case GL_MAX_VIEWPORT_DIMS: 253 needed = 2; 254 break; 255 256 case GL_PROGRAM_BINARY_FORMATS: 257 glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, &needed); 258 break; 259 } 260#endif 261 262#ifdef GL_ES_VERSION_2_0 263 // GLES 2.x pnames 264 switch (pname) { 265 case GL_ALIASED_LINE_WIDTH_RANGE: 266 case GL_ALIASED_POINT_SIZE_RANGE: 267 needed = 2; 268 break; 269 270 case GL_BLEND_COLOR: 271 case GL_COLOR_CLEAR_VALUE: 272 case GL_COLOR_WRITEMASK: 273 case GL_SCISSOR_BOX: 274 case GL_VIEWPORT: 275 needed = 4; 276 break; 277 278 case GL_COMPRESSED_TEXTURE_FORMATS: 279 glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed); 280 break; 281 282 case GL_SHADER_BINARY_FORMATS: 283 glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &needed); 284 break; 285 } 286#endif 287 288#ifdef GL_VERSION_ES_CM_1_1 289 // GLES 1.x pnames 290 switch (pname) { 291 case GL_ALIASED_LINE_WIDTH_RANGE: 292 case GL_ALIASED_POINT_SIZE_RANGE: 293 case GL_DEPTH_RANGE: 294 case GL_SMOOTH_LINE_WIDTH_RANGE: 295 case GL_SMOOTH_POINT_SIZE_RANGE: 296 needed = 2; 297 break; 298 299 case GL_CURRENT_NORMAL: 300 case GL_POINT_DISTANCE_ATTENUATION: 301 needed = 3; 302 break; 303 304 case GL_COLOR_CLEAR_VALUE: 305 case GL_COLOR_WRITEMASK: 306 case GL_CURRENT_COLOR: 307 case GL_CURRENT_TEXTURE_COORDS: 308 case GL_FOG_COLOR: 309 case GL_LIGHT_MODEL_AMBIENT: 310 case GL_SCISSOR_BOX: 311 case GL_VIEWPORT: 312 needed = 4; 313 break; 314 315 case GL_MODELVIEW_MATRIX: 316 case GL_PROJECTION_MATRIX: 317 case GL_TEXTURE_MATRIX: 318 needed = 16; 319 break; 320 321 case GL_COMPRESSED_TEXTURE_FORMATS: 322 glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed); 323 break; 324 } 325#endif 326 return needed; 327} 328 329template <typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY, 330 typename ARRAYRELEASER, typename CTYPE, void GET(GLenum, CTYPE*)> 331static void 332get 333 (JNIEnv *_env, jobject _this, jint pname, JTYPEARRAY params_ref, jint offset) { 334 jint _exception = 0; 335 const char * _exceptionType; 336 const char * _exceptionMessage; 337 CTYPE *params_base = (CTYPE *) 0; 338 jint _remaining; 339 CTYPE *params = (CTYPE *) 0; 340 int _needed = 0; 341 342 if (!params_ref) { 343 _exception = 1; 344 _exceptionType = "java/lang/IllegalArgumentException"; 345 _exceptionMessage = "params == null"; 346 goto exit; 347 } 348 if (offset < 0) { 349 _exception = 1; 350 _exceptionType = "java/lang/IllegalArgumentException"; 351 _exceptionMessage = "offset < 0"; 352 goto exit; 353 } 354 _remaining = _env->GetArrayLength(params_ref) - offset; 355 _needed = getNeededCount(pname); 356 // if we didn't find this pname, we just assume the user passed 357 // an array of the right size -- this might happen with extensions 358 // or if we forget an enum here. 359 if (_remaining < _needed) { 360 _exception = 1; 361 _exceptionType = "java/lang/IllegalArgumentException"; 362 _exceptionMessage = "length - offset < needed"; 363 goto exit; 364 } 365 params_base = (CTYPE *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>( 366 _env, params_ref, (jboolean *)0); 367 params = params_base + offset; 368 369 GET( 370 (GLenum)pname, 371 (CTYPE *)params 372 ); 373 374exit: 375 if (params_base) { 376 releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>( 377 _env, params_ref, params_base, !_exception); 378 } 379 if (_exception) { 380 jniThrowException(_env, _exceptionType, _exceptionMessage); 381 } 382} 383 384 385template <typename CTYPE, typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY, 386 typename ARRAYRELEASER, void GET(GLenum, CTYPE*)> 387static void 388getarray 389 (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { 390 jint _exception = 0; 391 const char * _exceptionType; 392 const char * _exceptionMessage; 393 JTYPEARRAY _array = (JTYPEARRAY) 0; 394 jint _bufferOffset = (jint) 0; 395 jint _remaining; 396 CTYPE *params = (CTYPE *) 0; 397 int _needed = 0; 398 399 params = (CTYPE *)getPointer(_env, params_buf, (jarray*)&_array, &_remaining, &_bufferOffset); 400 _remaining /= sizeof(CTYPE); // convert from bytes to item count 401 _needed = getNeededCount(pname); 402 // if we didn't find this pname, we just assume the user passed 403 // an array of the right size -- this might happen with extensions 404 // or if we forget an enum here. 405 if (_needed>0 && _remaining < _needed) { 406 _exception = 1; 407 _exceptionType = "java/lang/IllegalArgumentException"; 408 _exceptionMessage = "remaining() < needed"; 409 goto exit; 410 } 411 if (params == NULL) { 412 char * _paramsBase = (char *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>( 413 _env, _array, (jboolean *) 0); 414 params = (CTYPE *) (_paramsBase + _bufferOffset); 415 } 416 GET( 417 (GLenum)pname, 418 (CTYPE *)params 419 ); 420 421exit: 422 if (_array) { 423 releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>( 424 _env, _array, (NTYPEARRAY)params, _exception ? JNI_FALSE : JNI_TRUE); 425 } 426 if (_exception) { 427 jniThrowException(_env, _exceptionType, _exceptionMessage); 428 } 429} 430 431// -------------------------------------------------------------------------- 432