1 #include <jni.h>
2 #include <JNIHelp.h>
3 #include <android_runtime/AndroidRuntime.h>
4 #include <utils/misc.h>
5 #include <assert.h>
6
7 static int initialized = 0;
8
9 static jclass nioAccessClass;
10 static jclass bufferClass;
11 static jmethodID getBasePointerID;
12 static jmethodID getBaseArrayID;
13 static jmethodID getBaseArrayOffsetID;
14 static jfieldID positionID;
15 static jfieldID limitID;
16 static jfieldID elementSizeShiftID;
17
18
19 /* special calls implemented in Android's GLES wrapper used to more
20 * efficiently bound-check passed arrays */
21 extern "C" {
22 #ifdef GL_VERSION_ES_CM_1_1
23 GL_API void GL_APIENTRY glColorPointerBounds(GLint size, GLenum type, GLsizei stride,
24 const GLvoid *ptr, GLsizei count);
25 GL_API void GL_APIENTRY glNormalPointerBounds(GLenum type, GLsizei stride,
26 const GLvoid *pointer, GLsizei count);
27 GL_API void GL_APIENTRY glTexCoordPointerBounds(GLint size, GLenum type,
28 GLsizei stride, const GLvoid *pointer, GLsizei count);
29 GL_API void GL_APIENTRY glVertexPointerBounds(GLint size, GLenum type,
30 GLsizei stride, const GLvoid *pointer, GLsizei count);
31 GL_API void GL_APIENTRY glPointSizePointerOESBounds(GLenum type,
32 GLsizei stride, const GLvoid *pointer, GLsizei count);
33 GL_API void GL_APIENTRY glMatrixIndexPointerOESBounds(GLint size, GLenum type,
34 GLsizei stride, const GLvoid *pointer, GLsizei count);
35 GL_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
glVertexAttribPointerBounds(GLuint indx,GLint size,GLenum type,GLboolean normalized,GLsizei stride,const GLvoid * pointer,GLsizei count)39 static 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
glVertexAttribIPointerBounds(GLuint indx,GLint size,GLenum type,GLsizei stride,const GLvoid * pointer,GLsizei count)45 static 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
54 static void
nativeClassInit(JNIEnv * _env,jclass glImplClass)55 nativeClassInit(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
76 static void *
getPointer(JNIEnv * _env,jobject buffer,jarray * array,jint * remaining,jint * offset)77 getPointer(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
103 class ByteArrayGetter {
104 public:
Get(JNIEnv * _env,jbyteArray array,jboolean * is_copy)105 static void* Get(JNIEnv* _env, jbyteArray array, jboolean* is_copy) {
106 return _env->GetByteArrayElements(array, is_copy);
107 }
108 };
109 class BooleanArrayGetter {
110 public:
Get(JNIEnv * _env,jbooleanArray array,jboolean * is_copy)111 static void* Get(JNIEnv* _env, jbooleanArray array, jboolean* is_copy) {
112 return _env->GetBooleanArrayElements(array, is_copy);
113 }
114 };
115 class CharArrayGetter {
116 public:
Get(JNIEnv * _env,jcharArray array,jboolean * is_copy)117 static void* Get(JNIEnv* _env, jcharArray array, jboolean* is_copy) {
118 return _env->GetCharArrayElements(array, is_copy);
119 }
120 };
121 class ShortArrayGetter {
122 public:
Get(JNIEnv * _env,jshortArray array,jboolean * is_copy)123 static void* Get(JNIEnv* _env, jshortArray array, jboolean* is_copy) {
124 return _env->GetShortArrayElements(array, is_copy);
125 }
126 };
127 class IntArrayGetter {
128 public:
Get(JNIEnv * _env,jintArray array,jboolean * is_copy)129 static void* Get(JNIEnv* _env, jintArray array, jboolean* is_copy) {
130 return _env->GetIntArrayElements(array, is_copy);
131 }
132 };
133 class LongArrayGetter {
134 public:
Get(JNIEnv * _env,jlongArray array,jboolean * is_copy)135 static void* Get(JNIEnv* _env, jlongArray array, jboolean* is_copy) {
136 return _env->GetLongArrayElements(array, is_copy);
137 }
138 };
139 class FloatArrayGetter {
140 public:
Get(JNIEnv * _env,jfloatArray array,jboolean * is_copy)141 static void* Get(JNIEnv* _env, jfloatArray array, jboolean* is_copy) {
142 return _env->GetFloatArrayElements(array, is_copy);
143 }
144 };
145 class DoubleArrayGetter {
146 public:
Get(JNIEnv * _env,jdoubleArray array,jboolean * is_copy)147 static void* Get(JNIEnv* _env, jdoubleArray array, jboolean* is_copy) {
148 return _env->GetDoubleArrayElements(array, is_copy);
149 }
150 };
151
152 template<typename JTYPEARRAY, typename ARRAYGETTER>
153 static void*
getArrayPointer(JNIEnv * _env,JTYPEARRAY array,jboolean * is_copy)154 getArrayPointer(JNIEnv *_env, JTYPEARRAY array, jboolean* is_copy) {
155 return ARRAYGETTER::Get(_env, array, is_copy);
156 }
157
158 class ByteArrayReleaser {
159 public:
Release(JNIEnv * _env,jbyteArray array,jbyte * data,jboolean commit)160 static void Release(JNIEnv* _env, jbyteArray array, jbyte* data, jboolean commit) {
161 _env->ReleaseByteArrayElements(array, data, commit ? 0 : JNI_ABORT);
162 }
163 };
164 class BooleanArrayReleaser {
165 public:
Release(JNIEnv * _env,jbooleanArray array,jboolean * data,jboolean commit)166 static void Release(JNIEnv* _env, jbooleanArray array, jboolean* data, jboolean commit) {
167 _env->ReleaseBooleanArrayElements(array, data, commit ? 0 : JNI_ABORT);
168 }
169 };
170 class CharArrayReleaser {
171 public:
Release(JNIEnv * _env,jcharArray array,jchar * data,jboolean commit)172 static void Release(JNIEnv* _env, jcharArray array, jchar* data, jboolean commit) {
173 _env->ReleaseCharArrayElements(array, data, commit ? 0 : JNI_ABORT);
174 }
175 };
176 class ShortArrayReleaser {
177 public:
Release(JNIEnv * _env,jshortArray array,jshort * data,jboolean commit)178 static void Release(JNIEnv* _env, jshortArray array, jshort* data, jboolean commit) {
179 _env->ReleaseShortArrayElements(array, data, commit ? 0 : JNI_ABORT);
180 }
181 };
182 class IntArrayReleaser {
183 public:
Release(JNIEnv * _env,jintArray array,jint * data,jboolean commit)184 static void Release(JNIEnv* _env, jintArray array, jint* data, jboolean commit) {
185 _env->ReleaseIntArrayElements(array, data, commit ? 0 : JNI_ABORT);
186 }
187 };
188 class LongArrayReleaser {
189 public:
Release(JNIEnv * _env,jlongArray array,jlong * data,jboolean commit)190 static void Release(JNIEnv* _env, jlongArray array, jlong* data, jboolean commit) {
191 _env->ReleaseLongArrayElements(array, data, commit ? 0 : JNI_ABORT);
192 }
193 };
194 class FloatArrayReleaser {
195 public:
Release(JNIEnv * _env,jfloatArray array,jfloat * data,jboolean commit)196 static void Release(JNIEnv* _env, jfloatArray array, jfloat* data, jboolean commit) {
197 _env->ReleaseFloatArrayElements(array, data, commit ? 0 : JNI_ABORT);
198 }
199 };
200 class DoubleArrayReleaser {
201 public:
Release(JNIEnv * _env,jdoubleArray array,jdouble * data,jboolean commit)202 static void Release(JNIEnv* _env, jdoubleArray array, jdouble* data, jboolean commit) {
203 _env->ReleaseDoubleArrayElements(array, data, commit ? 0 : JNI_ABORT);
204 }
205 };
206
207 template<typename JTYPEARRAY, typename NTYPEARRAY, typename ARRAYRELEASER>
208 static void
releaseArrayPointer(JNIEnv * _env,JTYPEARRAY array,NTYPEARRAY data,jboolean commit)209 releaseArrayPointer(JNIEnv *_env, JTYPEARRAY array, NTYPEARRAY data, jboolean commit) {
210 ARRAYRELEASER::Release(_env, array, data, commit);
211 }
212
213 static void
releasePointer(JNIEnv * _env,jarray array,void * data,jboolean commit)214 releasePointer(JNIEnv *_env, jarray array, void *data, jboolean commit)
215 {
216 _env->ReleasePrimitiveArrayCritical(array, data,
217 commit ? 0 : JNI_ABORT);
218 }
219
220 static void *
getDirectBufferPointer(JNIEnv * _env,jobject buffer)221 getDirectBufferPointer(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 */
getNeededCount(GLint pname)247 static 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
316 template <typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY,
317 typename ARRAYRELEASER, typename CTYPE, void GET(GLenum, CTYPE*)>
318 static void
get(JNIEnv * _env,jobject _this,jint pname,JTYPEARRAY params_ref,jint offset)319 get
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
361 exit:
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
372 template <typename CTYPE, typename JTYPEARRAY, typename ARRAYGETTER, typename NTYPEARRAY,
373 typename ARRAYRELEASER, void GET(GLenum, CTYPE*)>
374 static void
getarray(JNIEnv * _env,jobject _this,jint pname,jobject params_buf)375 getarray
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
408 exit:
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