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_2_0 250 // GLES 2.x pnames 251 switch (pname) { 252 case GL_ALIASED_LINE_WIDTH_RANGE: 253 case GL_ALIASED_POINT_SIZE_RANGE: 254 needed = 2; 255 break; 256 257 case GL_BLEND_COLOR: 258 case GL_COLOR_CLEAR_VALUE: 259 case GL_COLOR_WRITEMASK: 260 case GL_SCISSOR_BOX: 261 case GL_VIEWPORT: 262 needed = 4; 263 break; 264 265 case GL_COMPRESSED_TEXTURE_FORMATS: 266 glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed); 267 break; 268 269 case GL_SHADER_BINARY_FORMATS: 270 glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &needed); 271 break; 272 } 273#endif 274 275#ifdef GL_VERSION_ES_CM_1_1 276 // GLES 1.x pnames 277 switch (pname) { 278 case GL_ALIASED_LINE_WIDTH_RANGE: 279 case GL_ALIASED_POINT_SIZE_RANGE: 280 case GL_DEPTH_RANGE: 281 case GL_SMOOTH_LINE_WIDTH_RANGE: 282 case GL_SMOOTH_POINT_SIZE_RANGE: 283 needed = 2; 284 break; 285 286 case GL_CURRENT_NORMAL: 287 case GL_POINT_DISTANCE_ATTENUATION: 288 needed = 3; 289 break; 290 291 case GL_COLOR_CLEAR_VALUE: 292 case GL_COLOR_WRITEMASK: 293 case GL_CURRENT_COLOR: 294 case GL_CURRENT_TEXTURE_COORDS: 295 case GL_FOG_COLOR: 296 case GL_LIGHT_MODEL_AMBIENT: 297 case GL_SCISSOR_BOX: 298 case GL_VIEWPORT: 299 needed = 4; 300 break; 301 302 case GL_MODELVIEW_MATRIX: 303 case GL_PROJECTION_MATRIX: 304 case GL_TEXTURE_MATRIX: 305 needed = 16; 306 break; 307 308 case GL_COMPRESSED_TEXTURE_FORMATS: 309 glGetIntegerv(GL_NUM_COMPRESSED_TEXTURE_FORMATS, &needed); 310 break; 311 } 312#endif 313 return needed; 314} 315 316template <typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY, 317 typename ARRAYRELEASER, typename CTYPE, void GET(GLenum, CTYPE*)> 318static void 319get 320 (JNIEnv *_env, jobject _this, jint pname, JTYPEARRAY params_ref, jint offset) { 321 jint _exception = 0; 322 const char * _exceptionType; 323 const char * _exceptionMessage; 324 CTYPE *params_base = (CTYPE *) 0; 325 jint _remaining; 326 CTYPE *params = (CTYPE *) 0; 327 int _needed = 0; 328 329 if (!params_ref) { 330 _exception = 1; 331 _exceptionType = "java/lang/IllegalArgumentException"; 332 _exceptionMessage = "params == null"; 333 goto exit; 334 } 335 if (offset < 0) { 336 _exception = 1; 337 _exceptionType = "java/lang/IllegalArgumentException"; 338 _exceptionMessage = "offset < 0"; 339 goto exit; 340 } 341 _remaining = _env->GetArrayLength(params_ref) - offset; 342 _needed = getNeededCount(pname); 343 // if we didn't find this pname, we just assume the user passed 344 // an array of the right size -- this might happen with extensions 345 // or if we forget an enum here. 346 if (_remaining < _needed) { 347 _exception = 1; 348 _exceptionType = "java/lang/IllegalArgumentException"; 349 _exceptionMessage = "length - offset < needed"; 350 goto exit; 351 } 352 params_base = (CTYPE *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>( 353 _env, params_ref, (jboolean *)0); 354 params = params_base + offset; 355 356 GET( 357 (GLenum)pname, 358 (CTYPE *)params 359 ); 360 361exit: 362 if (params_base) { 363 releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>( 364 _env, params_ref, params_base, !_exception); 365 } 366 if (_exception) { 367 jniThrowException(_env, _exceptionType, _exceptionMessage); 368 } 369} 370 371 372template <typename CTYPE, typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY, 373 typename ARRAYRELEASER, void GET(GLenum, CTYPE*)> 374static void 375getarray 376 (JNIEnv *_env, jobject _this, jint pname, jobject params_buf) { 377 jint _exception = 0; 378 const char * _exceptionType; 379 const char * _exceptionMessage; 380 JTYPEARRAY _array = (JTYPEARRAY) 0; 381 jint _bufferOffset = (jint) 0; 382 jint _remaining; 383 CTYPE *params = (CTYPE *) 0; 384 int _needed = 0; 385 386 params = (CTYPE *)getPointer(_env, params_buf, (jarray*)&_array, &_remaining, &_bufferOffset); 387 _remaining /= sizeof(CTYPE); // convert from bytes to item count 388 _needed = getNeededCount(pname); 389 // if we didn't find this pname, we just assume the user passed 390 // an array of the right size -- this might happen with extensions 391 // or if we forget an enum here. 392 if (_needed>0 && _remaining < _needed) { 393 _exception = 1; 394 _exceptionType = "java/lang/IllegalArgumentException"; 395 _exceptionMessage = "remaining() < needed"; 396 goto exit; 397 } 398 if (params == NULL) { 399 char * _paramsBase = (char *) getArrayPointer<JTYPEARRAY, ARRAYGETTER>( 400 _env, _array, (jboolean *) 0); 401 params = (CTYPE *) (_paramsBase + _bufferOffset); 402 } 403 GET( 404 (GLenum)pname, 405 (CTYPE *)params 406 ); 407 408exit: 409 if (_array) { 410 releaseArrayPointer<JTYPEARRAY, NTYPEARRAY, ARRAYRELEASER>( 411 _env, _array, (NTYPEARRAY)params, _exception ? JNI_FALSE : JNI_TRUE); 412 } 413 if (_exception) { 414 jniThrowException(_env, _exceptionType, _exceptionMessage); 415 } 416} 417 418// -------------------------------------------------------------------------- 419