最近工作中的问题笔记
调试GLES, 发现某平台有各种问题,
1.用三星手机测试和iphone测试, 在drawcall以后不调用glDisableVertexAttribArray, 没有问题. 但在某平台下,后续的draw call会花屏.
实际问题主要是因为shader会优化掉某些不用的attrib, 这种情况下, 要根据shader内的信息, disable掉不用的attrib.
这个问题还可能好说,虽然还没找到GLES spec上怎么说, 但是配对使用是最保险的, 不配对可能是undefined behavior, 已知GL下确实有可能会有问题.
2.最奇葩的问题是该平台的shader, GL/ES的spec说生成program以后, 可以detach/delete shader, 但是在某平台上, 如果删除shader竟然也会渲染出错.
由于引擎的代码过于复杂, 为了排除其他因素, 专门用NativeActivity简化才最终确定这个问题.下面代码画了三个cube, 如果使用detach sader/delete shader, 只显示第一个cube (line130).
1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 //BEGIN_INCLUDE(all) 19 #include <jni.h> 20 #include <errno.h> 21 22 #include <EGL/egl.h> 23 #include <GLES/gl.h> 24 #include <GLES2/gl2.h> 25 26 #include <android/sensor.h> 27 #include <android/log.h> 28 #include <android_native_app_glue.h> 29 30 #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "native-activity", __VA_ARGS__)) 31 #define LOGW(...) ((void)__android_log_print(ANDROID_LOG_WARN, "native-activity", __VA_ARGS__)) 32 33 /** 34 * Our saved state data. 35 */ 36 struct saved_state { 37 float angle; 38 int32_t x; 39 int32_t y; 40 }; 41 42 /** 43 * Shared state for our app. 44 */ 45 struct engine { 46 struct android_app* app; 47 48 ASensorManager* sensorManager; 49 const ASensor* accelerometerSensor; 50 ASensorEventQueue* sensorEventQueue; 51 52 int animating; 53 EGLDisplay display; 54 EGLSurface surface; 55 EGLContext context; 56 int32_t width; 57 int32_t height; 58 GLuint program[3]; 59 struct saved_state state; 60 }; 61 62 GLuint Position = 1; 63 GLuint UV = 2; 64 65 ////////////////////////////////////////////////////////////////////////// 66 GLuint LoadShader(GLenum shaderType, const char* pSource) { 67 68 GLuint shader = glCreateShader(shaderType); 69 70 if (shader != 0) { 71 glShaderSource(shader, 1, &pSource, NULL); 72 glCompileShader(shader); 73 GLint compiled = 0; 74 glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); 75 if (compiled == 0) { 76 GLint infoLen = 0; 77 glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); 78 if (infoLen != 0) { 79 char* buf = (char*)alloca(infoLen); 80 if (buf != NULL) { 81 glGetShaderInfoLog(shader, infoLen, NULL, buf); 82 LOGW( "Could not compile shader %d:\n%s\n", shaderType, buf ); 83 } 84 glDeleteShader(shader); 85 shader = 0; 86 } 87 } 88 } 89 return shader; 90 } 91 92 ////////////////////////////////////////////////////////////////////////// 93 GLuint CreateProgram(const char* pVertexSource, const char* pFragmentSource) { 94 95 GLuint vertexShader = LoadShader(GL_VERTEX_SHADER, pVertexSource); 96 if (vertexShader == 0) { 97 return 0; 98 } 99 100 GLuint pixelShader = LoadShader(GL_FRAGMENT_SHADER, pFragmentSource); 101 if (pixelShader == 0) { 102 return 0; 103 } 104 105 GLuint program = glCreateProgram(); 106 if (program != 0) { 107 glAttachShader(program, vertexShader); 108 glAttachShader(program, pixelShader); 109 110 glBindAttribLocation(program, Position, "vsin_POSITION0"); 111 glBindAttribLocation(program, UV, "vsin_TEXCOORD0"); 112 113 glLinkProgram(program); 114 GLint linkStatus = GL_FALSE; 115 glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); 116 if (linkStatus != GL_TRUE) { 117 GLint bufLength = 0; 118 glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); 119 if (bufLength != 0) { 120 char* buf = (char*)alloca(bufLength); 121 if (buf != NULL) { 122 glGetProgramInfoLog(program, bufLength, NULL, buf); 123 LOGW( "Could not link program:\n%s\n", buf ); 124 } 125 } 126 glDeleteProgram(program); 127 program = 0; 128 } 129 130 //uncomment this will get weird problems 131 glDetachShader(program, vertexShader); 132 glDetachShader(program, pixelShader); 133 glDeleteShader(vertexShader); 134 glDeleteShader(pixelShader); 135 } 136 return program; 137 } 138 139 140 /** 141 * Initialize an EGL context for the current display. 142 */ 143 static int engine_init_display(struct engine* engine) { 144 // initialize OpenGL ES and EGL 145 146 /* 147 * Here specify the attributes of the desired configuration. 148 * Below, we select an EGLConfig with at least 8 bits per color 149 * component compatible with on-screen windows 150 */ 151 const EGLint attribs[] = { 152 EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 153 EGL_BLUE_SIZE, 8, 154 EGL_GREEN_SIZE, 8, 155 EGL_RED_SIZE, 8, 156 EGL_DEPTH_SIZE, 24, 157 EGL_STENCIL_SIZE, 8, 158 EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, //GLES 2.0 159 EGL_CONFORMANT, EGL_OPENGL_ES2_BIT, 160 EGL_NONE 161 }; 162 EGLint w, h, dummy, format; 163 EGLint numConfigs; 164 EGLConfig config; 165 EGLSurface surface; 166 EGLContext context; 167 168 EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); 169 170 eglInitialize(display, 0, 0); 171 172 /* Here, the application chooses the configuration it desires. In this 173 * sample, we have a very simplified selection process, where we pick 174 * the first EGLConfig that matches our criteria */ 175 eglChooseConfig(display, attribs, &config, 1, &numConfigs); 176 177 /* EGL_NATIVE_VISUAL_ID is an attribute of the EGLConfig that is 178 * guaranteed to be accepted by ANativeWindow_setBuffersGeometry(). 179 * As soon as we picked a EGLConfig, we can safely reconfigure the 180 * ANativeWindow buffers to match, using EGL_NATIVE_VISUAL_ID. */ 181 eglGetConfigAttrib(display, config, EGL_NATIVE_VISUAL_ID, &format); 182 183 ANativeWindow_setBuffersGeometry(engine->app->window, 0, 0, format); 184 185 surface = eglCreateWindowSurface(display, config, engine->app->window, NULL); 186 187 const EGLint ContextAttribs[] = { 188 EGL_CONTEXT_CLIENT_VERSION, 2, 189 EGL_NONE, 190 }; 191 context = eglCreateContext(display, config, EGL_NO_CONTEXT, ContextAttribs); 192 193 if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) { 194 LOGW("Unable to eglMakeCurrent"); 195 return -1; 196 } 197 198 eglQuerySurface(display, surface, EGL_WIDTH, &w); 199 eglQuerySurface(display, surface, EGL_HEIGHT, &h); 200 201 engine->display = display; 202 engine->context = context; 203 engine->surface = surface; 204 engine->width = w; 205 engine->height = h; 206 engine->state.angle = 0; 207 208 // Initialize GL state. 209 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_FASTEST); 210 glDisable(GL_CULL_FACE); 211 //glShadeModel(GL_SMOOTH); 212 //glDisable(GL_DEPTH_TEST); 213 214 //create shader 215 const char* vertexShaders[3] = { 216 "#version 100 \n\ 217 //#extension GL_OES_standard_derivatives : enable \n\ 218 //uniform vec4 WORLD_POS; \n\ 219 uniform vec4 _WORLD2PROJECTED_ROW3; \n\ 220 uniform vec4 _WORLD2PROJECTED_ROW2; \n\ 221 uniform vec4 _WORLD2PROJECTED_ROW1; \n\ 222 uniform vec4 _WORLD2PROJECTED_ROW0; \n\ 223 attribute vec3 vsin_POSITION0; \n\ 224 attribute vec4 vsin_TEXCOORD0; \n\ 225 varying mediump vec4 vsout_TEXCOORD0; \n\ 226 varying mediump vec4 vsout_COLOR0; \n\ 227 void main () \n\ 228 { \n\ 229 vec4 r_6 = vec4(vsin_POSITION0,1);// + WORLD_POS; \n\ 230 mediump vec4 r_8; \n\ 231 r_8.x = dot (r_6, _WORLD2PROJECTED_ROW0); \n\ 232 r_8.y = dot (r_6, _WORLD2PROJECTED_ROW1); \n\ 233 r_8.z = dot (r_6, _WORLD2PROJECTED_ROW2); \n\ 234 r_8.w = dot (r_6, _WORLD2PROJECTED_ROW3); \n\ 235 gl_Position = r_8; \n\ 236 vsout_TEXCOORD0 = vsin_TEXCOORD0; \n\ 237 vsout_COLOR0 = vec4( normalize(vsin_POSITION0-vec3(5,5,5) ),1); \n\ 238 }", 239 240 "#version 100 \n\ 241 //#extension GL_OES_standard_derivatives : enable \n\ 242 //uniform vec4 WORLD_POS; \n\ 243 uniform vec4 _WORLD2PROJECTED_ROW3; \n\ 244 uniform vec4 _WORLD2PROJECTED_ROW2; \n\ 245 uniform vec4 _WORLD2PROJECTED_ROW1; \n\ 246 uniform vec4 _WORLD2PROJECTED_ROW0; \n\ 247 attribute vec3 vsin_POSITION0; \n\ 248 attribute vec4 vsin_TEXCOORD0; \n\ 249 varying mediump vec4 vsout_TEXCOORD0; \n\ 250 varying mediump vec4 vsout_COLOR0; \n\ 251 void main () \n\ 252 { \n\ 253 vec4 r_6 = vec4(vsin_POSITION0,1) + vec4(20,0,0,0);// + WORLD_POS; \n\ 254 mediump vec4 r_8; \n\ 255 r_8.x = dot (r_6, _WORLD2PROJECTED_ROW0); \n\ 256 r_8.y = dot (r_6, _WORLD2PROJECTED_ROW1); \n\ 257 r_8.z = dot (r_6, _WORLD2PROJECTED_ROW2); \n\ 258 r_8.w = dot (r_6, _WORLD2PROJECTED_ROW3); \n\ 259 gl_Position = r_8; \n\ 260 vsout_TEXCOORD0 = vsin_TEXCOORD0; \n\ 261 vsout_COLOR0 = vec4( vec3(1,1,1) - normalize(vsin_POSITION0-vec3(5,5,5) ),1); \n\ 262 }", 263 264 "#version 100 \n\ 265 //#extension GL_OES_standard_derivatives : enable \n\ 266 //uniform vec4 WORLD_POS; \n\ 267 uniform vec4 _WORLD2PROJECTED_ROW3; \n\ 268 uniform vec4 _WORLD2PROJECTED_ROW2; \n\ 269 uniform vec4 _WORLD2PROJECTED_ROW1; \n\ 270 uniform vec4 _WORLD2PROJECTED_ROW0; \n\ 271 attribute vec3 vsin_POSITION0; \n\ 272 attribute vec4 vsin_TEXCOORD0; \n\ 273 varying mediump vec4 vsout_TEXCOORD0; \n\ 274 varying mediump vec4 vsout_COLOR0; \n\ 275 void main () \n\ 276 { \n\ 277 vec4 r_6 = vec4(vsin_POSITION0,1) + vec4(-20,0,0,0);// + WORLD_POS; \n\ 278 mediump vec4 r_8; \n\ 279 r_8.x = dot (r_6, _WORLD2PROJECTED_ROW0); \n\ 280 r_8.y = dot (r_6, _WORLD2PROJECTED_ROW1); \n\ 281 r_8.z = dot (r_6, _WORLD2PROJECTED_ROW2); \n\ 282 r_8.w = dot (r_6, _WORLD2PROJECTED_ROW3); \n\ 283 gl_Position = r_8; \n\ 284 vsout_TEXCOORD0 = vsin_TEXCOORD0; \n\ 285 vsout_COLOR0 = vec4(1,1,1,1); \n\ 286 }", }; 287 288 const char* fragmentShader[3] = { 289 290 "#version 100 \n\ 291 precision mediump float; \n\ 292 #extension GL_OES_standard_derivatives : enable \n\ 293 //uniform mediump sampler2D AlbedoSampler; \n\ 294 varying mediump vec4 vsout_TEXCOORD0; \n\ 295 varying mediump vec4 vsout_COLOR0; \n\ 296 void main () \n\ 297 { \n\ 298 //gl_FragColor = texture2D (AlbedoSampler, vsout_TEXCOORD0.xy) + vsout_COLOR0; \n\ 299 //gl_FragColor = vsout_COLOR0; \n\ 300 gl_FragColor = vsout_COLOR0; \n\ 301 }", 302 303 304 "#version 100 \n\ 305 precision mediump float; \n\ 306 #extension GL_OES_standard_derivatives : enable \n\ 307 //uniform mediump sampler2D AlbedoSampler; \n\ 308 varying mediump vec4 vsout_TEXCOORD0; \n\ 309 varying mediump vec4 vsout_COLOR0; \n\ 310 void main () \n\ 311 { \n\ 312 //gl_FragColor = texture2D (AlbedoSampler, vsout_TEXCOORD0.xy); \n\ 313 //gl_FragColor = texture2D (AlbedoSampler, vsout_TEXCOORD0.xy) + vsout_COLOR0; \n\ 314 gl_FragColor = vsout_COLOR0; \n\ 315 }", 316 317 318 "#version 100 \n\ 319 precision mediump float; \n\ 320 #extension GL_OES_standard_derivatives : enable \n\ 321 //uniform mediump sampler2D AlbedoSampler; \n\ 322 varying mediump vec4 vsout_TEXCOORD0; \n\ 323 varying mediump vec4 vsout_COLOR0; \n\ 324 void main () \n\ 325 { \n\ 326 //gl_FragColor = texture2D (AlbedoSampler, vsout_TEXCOORD0.xy); \n\ 327 //gl_FragColor = texture2D (AlbedoSampler, vsout_TEXCOORD0.xy) + vsout_COLOR0; \n\ 328 gl_FragColor = vsout_COLOR0; \n\ 329 }", 330 331 }; 332 333 engine->program[0] = CreateProgram(vertexShaders[0], fragmentShader[0]); 334 engine->program[1] = CreateProgram(vertexShaders[1], fragmentShader[1]); 335 engine->program[2] = CreateProgram(vertexShaders[2], fragmentShader[2]); 336 337 return 0; 338 } 339 340 /** 341 * Just the current frame in the display. 342 */ 343 static void engine_draw_frame(struct engine* engine) { 344 if (engine->display == NULL) { 345 // No display. 346 return; 347 } 348 349 // Just fill the screen with a color. 350 //glClearColor(((float)engine->state.x)/engine->width, engine->state.angle, 351 // ((float)engine->state.y)/engine->height, 1); 352 353 glClearColor( 0.5, 0.5, 0.5, 1); 354 glClearDepthf(1.0f); 355 glClear(GL_COLOR_BUFFER_BIT); 356 357 358 359 #define Scale 10.0f 360 361 static const GLfloat CubeVertices[36][3] = { 362 Scale, Scale, Scale, 363 Scale, Scale, 0, 364 Scale, 0, 0, 365 366 Scale, Scale, Scale, 367 Scale, 0, 0, 368 Scale, 0, Scale, 369 370 0, 0, 0, 371 Scale, 0, 0, 372 Scale, Scale, 0, 373 374 0, 0, 0, 375 Scale, Scale, 0, 376 0, Scale, 0, 377 378 0, Scale, 0, 379 Scale, Scale, 0, 380 Scale, Scale, Scale, 381 382 0, Scale, 0, 383 Scale, Scale, Scale, 384 0, Scale, Scale, 385 386 0, Scale, 0, 387 0, 0, Scale, 388 0, 0, 0, 389 390 0, Scale, 0, 391 0, Scale, Scale, 392 0, 0, Scale, 393 394 0, 0, Scale, 395 0, 0, 0, 396 Scale, 0, 0, 397 398 0, 0, Scale, 399 Scale, 0, 0, 400 Scale, 0, Scale, 401 402 0, Scale, Scale, 403 Scale, 0, Scale, 404 0, 0, Scale, 405 406 0, Scale, Scale, 407 Scale, Scale, Scale, 408 Scale, 0, Scale, 409 }; 410 411 static const GLfloat CubeUV[36][2] = { 412 0, 0, 413 0, 1, 414 1, 1, 415 416 0, 0, 417 1, 1, 418 1, 0, 419 420 1, 0, 421 1, 1, 422 0, 1, 423 424 1, 0, 425 0, 1, 426 0, 0, 427 428 0, 0, 429 0, 1, 430 1, 1, 431 432 0, 0, 433 1, 1, 434 1, 0, 435 436 0, 0, 437 1, 1, 438 1, 0, 439 }; 440 441 //for simplification, this view-projection matrix is copied from engine using matrix utility 442 GLfloat viewporj[4][4] = 443 { 444 1.73217857,0,-3.09281524e-011,0, 445 -3.49910968e-011, 1.95973194, -1.95973027, 0, 446 -1.26294461e-011, -0.707330883, -0.707331479, 52.8758965, 447 -1.26254051e-011, -0.707104564, -0.70710516, 84.8589783 448 }; 449 450 int i = 0; 451 for(i = 0; i < 3; ++i) 452 { 453 glUseProgram(engine->program[i]); 454 glUniform4fv( glGetUniformLocation(engine->program[i], "_WORLD2PROJECTED_ROW0"), 1, viewporj[0]); 455 glUniform4fv( glGetUniformLocation(engine->program[i], "_WORLD2PROJECTED_ROW1"), 1, viewporj[1]); 456 glUniform4fv( glGetUniformLocation(engine->program[i], "_WORLD2PROJECTED_ROW2"), 1, viewporj[2]); 457 glUniform4fv( glGetUniformLocation(engine->program[i], "_WORLD2PROJECTED_ROW3"), 1, viewporj[3]); 458 glEnableVertexAttribArray( Position ); 459 glVertexAttribPointer(Position, 3, GL_FLOAT, GL_FALSE, 0, CubeVertices); 460 //setup vertex UV buffer 461 glEnableVertexAttribArray(UV); 462 glVertexAttribPointer(UV, 2, GL_FLOAT, GL_FALSE, 0, CubeUV); 463 //draw call & swap 464 glDrawArrays(GL_TRIANGLES, 0, 36); 465 466 glDisableVertexAttribArray(Position); 467 glDisableVertexAttribArray(UV); 468 } 469 470 471 eglSwapBuffers(engine->display, engine->surface); 472 } 473 474 /** 475 * Tear down the EGL context currently associated with the display. 476 */ 477 static void engine_term_display(struct engine* engine) { 478 if (engine->display != EGL_NO_DISPLAY) { 479 eglMakeCurrent(engine->display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); 480 if (engine->context != EGL_NO_CONTEXT) { 481 eglDestroyContext(engine->display, engine->context); 482 } 483 if (engine->surface != EGL_NO_SURFACE) { 484 eglDestroySurface(engine->display, engine->surface); 485 } 486 eglTerminate(engine->display); 487 } 488 engine->animating = 0; 489 engine->display = EGL_NO_DISPLAY; 490 engine->context = EGL_NO_CONTEXT; 491 engine->surface = EGL_NO_SURFACE; 492 } 493 494 /** 495 * Process the next input event. 496 */ 497 static int32_t engine_handle_input(struct android_app* app, AInputEvent* event) { 498 struct engine* engine = (struct engine*)app->userData; 499 if (AInputEvent_getType(event) == AINPUT_EVENT_TYPE_MOTION) { 500 engine->animating = 1; 501 engine->state.x = AMotionEvent_getX(event, 0); 502 engine->state.y = AMotionEvent_getY(event, 0); 503 return 1; 504 } 505 return 0; 506 } 507 508 /** 509 * Process the next main command. 510 */ 511 static void engine_handle_cmd(struct android_app* app, int32_t cmd) { 512 struct engine* engine = (struct engine*)app->userData; 513 switch (cmd) { 514 case APP_CMD_SAVE_STATE: 515 // The system has asked us to save our current state. Do so. 516 engine->app->savedState = malloc(sizeof(struct saved_state)); 517 *((struct saved_state*)engine->app->savedState) = engine->state; 518 engine->app->savedStateSize = sizeof(struct saved_state); 519 break; 520 case APP_CMD_INIT_WINDOW: 521 // The window is being shown, get it ready. 522 if (engine->app->window != NULL) { 523 engine_init_display(engine); 524 engine_draw_frame(engine); 525 } 526 break; 527 case APP_CMD_TERM_WINDOW: 528 // The window is being hidden or closed, clean it up. 529 engine_term_display(engine); 530 break; 531 case APP_CMD_GAINED_FOCUS: 532 // When our app gains focus, we start monitoring the accelerometer. 533 if (engine->accelerometerSensor != NULL) { 534 ASensorEventQueue_enableSensor(engine->sensorEventQueue, 535 engine->accelerometerSensor); 536 // We'd like to get 60 events per second (in us). 537 ASensorEventQueue_setEventRate(engine->sensorEventQueue, 538 engine->accelerometerSensor, (1000L/60)*1000); 539 } 540 break; 541 case APP_CMD_LOST_FOCUS: 542 // When our app loses focus, we stop monitoring the accelerometer. 543 // This is to avoid consuming battery while not being used. 544 if (engine->accelerometerSensor != NULL) { 545 ASensorEventQueue_disableSensor(engine->sensorEventQueue, 546 engine->accelerometerSensor); 547 } 548 // Also stop animating. 549 engine->animating = 0; 550 engine_draw_frame(engine); 551 break; 552 } 553 } 554 555 /** 556 * This is the main entry point of a native application that is using 557 * android_native_app_glue. It runs in its own thread, with its own 558 * event loop for receiving input events and doing other things. 559 */ 560 void android_main(struct android_app* state) { 561 struct engine engine; 562 563 // Make sure glue isn't stripped. 564 app_dummy(); 565 566 memset(&engine, 0, sizeof(engine)); 567 state->userData = &engine; 568 state->onAppCmd = engine_handle_cmd; 569 state->onInputEvent = engine_handle_input; 570 engine.app = state; 571 572 // Prepare to monitor accelerometer 573 engine.sensorManager = ASensorManager_getInstance(); 574 engine.accelerometerSensor = ASensorManager_getDefaultSensor(engine.sensorManager, 575 ASENSOR_TYPE_ACCELEROMETER); 576 engine.sensorEventQueue = ASensorManager_createEventQueue(engine.sensorManager, 577 state->looper, LOOPER_ID_USER, NULL, NULL); 578 579 if (state->savedState != NULL) { 580 // We are starting with a previous saved state; restore from it. 581 engine.state = *(struct saved_state*)state->savedState; 582 } 583 584 // loop waiting for stuff to do. 585 586 while (1) { 587 // Read all pending events. 588 int ident; 589 int events; 590 struct android_poll_source* source; 591 592 // If not animating, we will block forever waiting for events. 593 // If animating, we loop until all events are read, then continue 594 // to draw the next frame of animation. 595 while ((ident=ALooper_pollAll(engine.animating ? 0 : -1, NULL, &events, 596 (void**)&source)) >= 0) { 597 598 // Process this event. 599 if (source != NULL) { 600 source->process(state, source); 601 } 602 603 // If a sensor has data, process it now. 604 if (ident == LOOPER_ID_USER) { 605 if (engine.accelerometerSensor != NULL) { 606 ASensorEvent event; 607 while (ASensorEventQueue_getEvents(engine.sensorEventQueue, 608 &event, 1) > 0) { 609 //LOGI("accelerometer: x=%f y=%f z=%f", 610 // event.acceleration.x, event.acceleration.y, 611 // event.acceleration.z); 612 } 613 } 614 } 615 616 // Check if we are exiting. 617 if (state->destroyRequested != 0) { 618 engine_term_display(&engine); 619 return; 620 } 621 } 622 623 if (engine.animating) { 624 // Done with events; draw next animation frame. 625 engine.state.angle += .01f; 626 if (engine.state.angle > 1) { 627 engine.state.angle = 0; 628 } 629 630 // Drawing is throttled to the screen update rate, so there 631 // is no need to do timing here. 632 engine_draw_frame(&engine); 633 } 634 } 635 } 636 //END_INCLUDE(all)
主要是对GL/ES不太熟悉, 而且bug比较诡异, 根本没想到会出这种问题. 代码贴出来, 或许是别的地方有问题, 希望有大大看到帮忙瞧一眼.
顺便, 该平台的GLES是2.0, build version是1.8, 可以正常显式的三星手机也是GLES2.0, build version是1.9rc.
另外, 之前OBB的问题, 使用40M+的OBB, 运行时基本不会报错了, 但是现在OBB单个文件夹内的文件数太少了, 打包时会抛出异常.
http://stackoverflow.com/questions/13562617/using-jobb-tool-in-android
SO上说,内置的OBB格式是FAT16,单个文件夹内只能有512个项...想起来自己引擎新写的pacakge,应该算没有白写.
公司的项目, 考虑使用多文件夹来重新组织数据, 但是太繁琐,太不可控, 一不小心就会超了, 或者考虑也得写自定义的package.
工作很忙, 暂时写这么多吧.
对于OBB, 目前最快的方案是使用子文件夹, 这样根目录的限制就没有了, mount以后把子文件夹的路径加上, 作为data的根路径, 应该最简单了.