00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029 #include "NvEglRenderer.h"
00030 #include "NvLogging.h"
00031 #include "nvbuf_utils.h"
00032
00033 #include <cstring>
00034 #include <sys/time.h>
00035
00036 #define CAT_NAME "EglRenderer"
00037
00038 #define ERROR_GOTO_FAIL(val, string) \
00039 do { \
00040 if (val) {\
00041 CAT_DEBUG_MSG(string); \
00042 goto fail; \
00043 } \
00044 } while (0)
00045
00046 PFNEGLCREATEIMAGEKHRPROC NvEglRenderer::eglCreateImageKHR;
00047 PFNEGLDESTROYIMAGEKHRPROC NvEglRenderer::eglDestroyImageKHR;
00048 PFNEGLCREATESYNCKHRPROC NvEglRenderer::eglCreateSyncKHR;
00049 PFNEGLDESTROYSYNCKHRPROC NvEglRenderer::eglDestroySyncKHR;
00050 PFNEGLCLIENTWAITSYNCKHRPROC NvEglRenderer::eglClientWaitSyncKHR;
00051 PFNEGLGETSYNCATTRIBKHRPROC NvEglRenderer::eglGetSyncAttribKHR;
00052 PFNGLEGLIMAGETARGETTEXTURE2DOESPROC NvEglRenderer::glEGLImageTargetTexture2DOES;
00053
00054 using namespace std;
00055
00056 NvEglRenderer::NvEglRenderer(const char *name, uint32_t width, uint32_t height,
00057 uint32_t x_offset, uint32_t y_offset)
00058 :NvElement(name, valid_fields)
00059 {
00060 int depth;
00061 int screen_num;
00062 XSetWindowAttributes window_attributes;
00063 x_window = 0;
00064 x_display = NULL;
00065
00066 texture_id = 0;
00067 gc = NULL;
00068 fontinfo = NULL;
00069
00070 egl_surface = EGL_NO_SURFACE;
00071 egl_context = EGL_NO_CONTEXT;
00072 egl_display = EGL_NO_DISPLAY;
00073 egl_config = NULL;
00074
00075 memset(&last_render_time, 0, sizeof(last_render_time));
00076 stop_thread = false;
00077 render_thread = 0;
00078
00079 memset(overlay_str, 0, sizeof(overlay_str));
00080 overlay_str_x_offset = 0;
00081 overlay_str_y_offset = 0;
00082
00083 pthread_mutex_init(&render_lock, NULL);
00084 pthread_cond_init(&render_cond, NULL);
00085
00086 setFPS(30);
00087
00088 if (initEgl() < 0)
00089 {
00090 COMP_ERROR_MSG("Error getting EGL function addresses");
00091 goto error;
00092 }
00093
00094 x_display = XOpenDisplay(NULL);
00095 if (NULL == x_display)
00096 {
00097 COMP_ERROR_MSG("Error in opening display");
00098 goto error;
00099 }
00100
00101 screen_num = DefaultScreen(x_display);
00102 if (!width || !height)
00103 {
00104 width = DisplayWidth(x_display, screen_num);
00105 height = DisplayHeight(x_display, screen_num);
00106 x_offset = 0;
00107 y_offset = 0;
00108 }
00109 COMP_ERROR_MSG("Setting Screen width " << width << " height " << height);
00110
00111 COMP_DEBUG_MSG("Display opened successfully " << (size_t) x_display);
00112
00113 depth = DefaultDepth(x_display, DefaultScreen(x_display));
00114
00115 window_attributes.background_pixel =
00116 BlackPixel(x_display, DefaultScreen(x_display));
00117
00118 window_attributes.override_redirect = 1;
00119
00120 x_window = XCreateWindow(x_display,
00121 DefaultRootWindow(x_display), x_offset,
00122 y_offset, width, height,
00123 0,
00124 depth, CopyFromParent,
00125 CopyFromParent,
00126 (CWBackPixel | CWOverrideRedirect),
00127 &window_attributes);
00128
00129 XSelectInput(x_display, (int32_t) x_window, ExposureMask);
00130 XMapWindow(x_display, (int32_t) x_window);
00131 gc = XCreateGC(x_display, x_window, 0, NULL);
00132
00133 XSetForeground(x_display, gc,
00134 WhitePixel(x_display, DefaultScreen(x_display)) );
00135 fontinfo = XLoadQueryFont(x_display, "9x15bold");
00136
00137 pthread_mutex_lock(&render_lock);
00138 pthread_create(&render_thread, NULL, renderThread, this);
00139 pthread_cond_wait(&render_cond, &render_lock);
00140 pthread_mutex_unlock(&render_lock);
00141
00142 if(isInError())
00143 {
00144 pthread_join(render_thread, NULL);
00145 goto error;
00146 }
00147
00148 COMP_DEBUG_MSG("Renderer started successfully")
00149 return;
00150
00151 error:
00152 COMP_ERROR_MSG("Got ERROR closing display");
00153 is_in_error = 1;
00154 }
00155
00156 int
00157 NvEglRenderer::getDisplayResolution(uint32_t &width, uint32_t &height)
00158 {
00159 int screen_num;
00160 Display * x_display = XOpenDisplay(NULL);
00161 if (NULL == x_display)
00162 {
00163 return -1;
00164 }
00165
00166 screen_num = DefaultScreen(x_display);
00167 width = DisplayWidth(x_display, screen_num);
00168 height = DisplayHeight(x_display, screen_num);
00169
00170 XCloseDisplay(x_display);
00171 x_display = NULL;
00172
00173 return 0;
00174 }
00175
00176 void *
00177 NvEglRenderer::renderThread(void *arg)
00178 {
00179 EGLBoolean egl_status;
00180 NvEglRenderer *renderer = (NvEglRenderer *) arg;
00181 const char *comp_name = renderer->comp_name;
00182
00183 static EGLint rgba8888[] = {
00184 EGL_RED_SIZE, 8,
00185 EGL_GREEN_SIZE, 8,
00186 EGL_BLUE_SIZE, 8,
00187 EGL_ALPHA_SIZE, 8,
00188 EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
00189 EGL_NONE,
00190 };
00191 int num_configs = 0;
00192 EGLint context_attribs[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE };
00193 renderer->egl_display = eglGetDisplay(renderer->x_display);
00194 if (EGL_NO_DISPLAY == renderer->egl_display)
00195 {
00196 COMP_ERROR_MSG("Unable to get egl display");
00197 goto error;
00198 }
00199 COMP_DEBUG_MSG("Egl Got display " << (size_t) renderer->egl_display);
00200
00201 egl_status = eglInitialize(renderer->egl_display, 0, 0);
00202 if (!egl_status)
00203 {
00204 COMP_ERROR_MSG("Unable to initialize egl library");
00205 goto error;
00206 }
00207
00208 egl_status =
00209 eglChooseConfig(renderer->egl_display, rgba8888,
00210 &renderer->egl_config, 1, &num_configs);
00211 if (!egl_status)
00212 {
00213 COMP_ERROR_MSG("Error at eglChooseConfig");
00214 goto error;
00215 }
00216 COMP_DEBUG_MSG("Got numconfigs as " << num_configs);
00217
00218 renderer->egl_context =
00219 eglCreateContext(renderer->egl_display, renderer->egl_config,
00220 EGL_NO_CONTEXT, context_attribs);
00221 if (eglGetError() != EGL_SUCCESS)
00222 {
00223 COMP_ERROR_MSG("Got Error in eglCreateContext " << eglGetError());
00224 goto error;
00225 }
00226 renderer->egl_surface =
00227 eglCreateWindowSurface(renderer->egl_display, renderer->egl_config,
00228 (EGLNativeWindowType) renderer->x_window, NULL);
00229 if (renderer->egl_surface == EGL_NO_SURFACE)
00230 {
00231 COMP_ERROR_MSG("Error in creating egl surface " << eglGetError());
00232 goto error;
00233 }
00234
00235 eglMakeCurrent(renderer->egl_display, renderer->egl_surface,
00236 renderer->egl_surface, renderer->egl_context);
00237 if (eglGetError() != EGL_SUCCESS)
00238 {
00239 COMP_ERROR_MSG("Error in eglMakeCurrent " << eglGetError());
00240 goto error;
00241 }
00242
00243 if (renderer->InitializeShaders() < 0)
00244 {
00245 COMP_ERROR_MSG("Error while initializing shaders");
00246 goto error;
00247 }
00248
00249 renderer->create_texture();
00250
00251 pthread_mutex_lock(&renderer->render_lock);
00252 pthread_cond_broadcast(&renderer->render_cond);
00253 COMP_DEBUG_MSG("Starting render thread");
00254
00255 while (!renderer->isInError() && !renderer->stop_thread)
00256 {
00257 pthread_cond_wait(&renderer->render_cond, &renderer->render_lock);
00258 pthread_mutex_unlock(&renderer->render_lock);
00259
00260 if(renderer->stop_thread)
00261 {
00262 pthread_mutex_lock(&renderer->render_lock);
00263 break;
00264 }
00265
00266 renderer->renderInternal();
00267 COMP_DEBUG_MSG("Rendered fd=" << renderer->render_fd);
00268
00269 pthread_mutex_lock(&renderer->render_lock);
00270 pthread_cond_broadcast(&renderer->render_cond);
00271 }
00272 pthread_mutex_unlock(&renderer->render_lock);
00273 COMP_DEBUG_MSG("Stopped render thread");
00274
00275 finish:
00276 if (renderer->texture_id)
00277 {
00278 glDeleteTextures(1, &renderer->texture_id);
00279 }
00280
00281 if (renderer->egl_display != EGL_NO_DISPLAY)
00282 {
00283 eglMakeCurrent(renderer->egl_display, EGL_NO_SURFACE,
00284 EGL_NO_SURFACE, EGL_NO_CONTEXT);
00285 }
00286
00287 if (renderer->egl_surface != EGL_NO_SURFACE)
00288 {
00289 egl_status = eglDestroySurface(renderer->egl_display,
00290 renderer->egl_surface);
00291 if (egl_status == EGL_FALSE)
00292 {
00293 COMP_ERROR_MSG("EGL surface destruction failed");
00294 }
00295 }
00296
00297 if (renderer->egl_context != EGL_NO_CONTEXT)
00298 {
00299 egl_status = eglDestroyContext(renderer->egl_display,
00300 renderer->egl_context);
00301 if (egl_status == EGL_FALSE)
00302 {
00303 COMP_ERROR_MSG("EGL context destruction failed");
00304 }
00305 }
00306
00307 if (renderer->egl_display != EGL_NO_DISPLAY)
00308 {
00309 eglReleaseThread();
00310 eglTerminate(renderer->egl_display);
00311 }
00312
00313 pthread_mutex_lock(&renderer->render_lock);
00314 pthread_cond_broadcast(&renderer->render_cond);
00315 pthread_mutex_unlock(&renderer->render_lock);
00316 return NULL;
00317
00318 error:
00319 renderer->is_in_error = 1;
00320 goto finish;
00321 }
00322
00323 NvEglRenderer::~NvEglRenderer()
00324 {
00325 stop_thread = true;
00326
00327 pthread_mutex_lock(&render_lock);
00328 pthread_cond_broadcast(&render_cond);
00329 pthread_mutex_unlock(&render_lock);
00330
00331 pthread_join(render_thread, NULL);
00332
00333 pthread_mutex_destroy(&render_lock);
00334 pthread_cond_destroy(&render_cond);
00335
00336 if (fontinfo)
00337 {
00338 XFreeFont(x_display, fontinfo);
00339 }
00340
00341 if (gc)
00342 {
00343 XFreeGC(x_display, gc);
00344 }
00345
00346 if (x_window)
00347 {
00348 XUnmapWindow(x_display, (int32_t) x_window);
00349 XFlush(x_display);
00350 XDestroyWindow(x_display, (int32_t) x_window);
00351 }
00352 if (x_display)
00353 {
00354 XCloseDisplay(x_display);
00355 }
00356 }
00357
00358 int
00359 NvEglRenderer::render(int fd)
00360 {
00361 this->render_fd = fd;
00362 pthread_mutex_lock(&render_lock);
00363 pthread_cond_broadcast(&render_cond);
00364 COMP_DEBUG_MSG("Rendering fd=" << fd);
00365 pthread_cond_wait(&render_cond, &render_lock);
00366 pthread_mutex_unlock(&render_lock);
00367 return 0;
00368 }
00369
00370 int
00371 NvEglRenderer::renderInternal()
00372 {
00373 EGLImageKHR hEglImage;
00374 bool frame_is_late = false;
00375
00376 EGLSyncKHR egl_sync;
00377 int iErr;
00378 hEglImage = NvEGLImageFromFd(egl_display, render_fd);
00379 if (!hEglImage)
00380 {
00381 COMP_ERROR_MSG("Could not get EglImage from fd. Not rendering");
00382 return -1;
00383 }
00384
00385 glActiveTexture(GL_TEXTURE0);
00386 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
00387 glEGLImageTargetTexture2DOES(GL_TEXTURE_EXTERNAL_OES, hEglImage);
00388 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
00389
00390 iErr = glGetError();
00391 if (iErr != GL_NO_ERROR)
00392 {
00393 COMP_ERROR_MSG("glDrawArrays arrays failed:" << iErr);
00394 return -1;
00395 }
00396 egl_sync = eglCreateSyncKHR(egl_display, EGL_SYNC_FENCE_KHR, NULL);
00397 if (egl_sync == EGL_NO_SYNC_KHR)
00398 {
00399 COMP_ERROR_MSG("eglCreateSyncKHR() failed");
00400 return -1;
00401 }
00402 if (last_render_time.tv_sec != 0)
00403 {
00404 pthread_mutex_lock(&render_lock);
00405 last_render_time.tv_sec += render_time_sec;
00406 last_render_time.tv_nsec += render_time_nsec;
00407 last_render_time.tv_sec += last_render_time.tv_nsec / 1000000000UL;
00408 last_render_time.tv_nsec %= 1000000000UL;
00409
00410 if (isProfilingEnabled())
00411 {
00412 struct timeval cur_time;
00413 gettimeofday(&cur_time, NULL);
00414 if ((cur_time.tv_sec * 1000000.0 + cur_time.tv_usec) >
00415 (last_render_time.tv_sec * 1000000.0 +
00416 last_render_time.tv_nsec / 1000.0))
00417 {
00418 frame_is_late = true;
00419 }
00420 }
00421
00422 pthread_cond_timedwait(&render_cond, &render_lock,
00423 &last_render_time);
00424
00425 pthread_mutex_unlock(&render_lock);
00426 }
00427 else
00428 {
00429 struct timeval now;
00430
00431 gettimeofday(&now, NULL);
00432 last_render_time.tv_sec = now.tv_sec;
00433 last_render_time.tv_nsec = now.tv_usec * 1000L;
00434 }
00435 eglSwapBuffers(egl_display, egl_surface);
00436 if (eglGetError() != EGL_SUCCESS)
00437 {
00438 COMP_ERROR_MSG("Got Error in eglSwapBuffers " << eglGetError());
00439 return -1;
00440 }
00441 if (eglClientWaitSyncKHR (egl_display, egl_sync,
00442 EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, EGL_FOREVER_KHR) == EGL_FALSE)
00443 {
00444 COMP_ERROR_MSG("eglClientWaitSyncKHR failed!");
00445 }
00446
00447 if (eglDestroySyncKHR(egl_display, egl_sync) != EGL_TRUE)
00448 {
00449 COMP_ERROR_MSG("eglDestroySyncKHR failed!");
00450 }
00451 NvDestroyEGLImage(egl_display, hEglImage);
00452
00453 if (strlen(overlay_str) != 0)
00454 {
00455 XSetForeground(x_display, gc,
00456 BlackPixel(x_display, DefaultScreen(x_display)));
00457 XSetFont(x_display, gc, fontinfo->fid);
00458 XDrawString(x_display, x_window, gc, overlay_str_x_offset,
00459 overlay_str_y_offset, overlay_str, strlen(overlay_str));
00460 }
00461
00462 profiler.finishProcessing(0, frame_is_late);
00463
00464 return 0;
00465 }
00466
00467 int
00468 NvEglRenderer::setOverlayText(char *str, uint32_t x, uint32_t y)
00469 {
00470 strncpy(overlay_str, str, sizeof(overlay_str));
00471 overlay_str[sizeof(overlay_str) - 1] = '\0';
00472
00473 overlay_str_x_offset = x;
00474 overlay_str_y_offset = y;
00475
00476 return 0;
00477 }
00478
00479 int
00480 NvEglRenderer::setFPS(float fps)
00481 {
00482 uint64_t render_time_usec;
00483
00484 if (fps == 0)
00485 {
00486 COMP_WARN_MSG("Fps 0 is not allowed. Not changing fps");
00487 return -1;
00488 }
00489 pthread_mutex_lock(&render_lock);
00490 this->fps = fps;
00491
00492 render_time_usec = 1000000L / fps;
00493 render_time_sec = render_time_usec / 1000000;
00494 render_time_nsec = (render_time_usec % 1000000) * 1000L;
00495 pthread_mutex_unlock(&render_lock);
00496 return 0;
00497 }
00498
00499 NvEglRenderer *
00500 NvEglRenderer::createEglRenderer(const char *name, uint32_t width,
00501 uint32_t height, uint32_t x_offset,
00502 uint32_t y_offset)
00503 {
00504 NvEglRenderer* renderer = new NvEglRenderer(name, width, height,
00505 x_offset, y_offset);
00506 if (renderer->isInError())
00507 {
00508 delete renderer;
00509 return NULL;
00510 }
00511 return renderer;
00512 }
00513
00514 int
00515 NvEglRenderer::initEgl()
00516 {
00517 eglCreateImageKHR =
00518 (PFNEGLCREATEIMAGEKHRPROC) eglGetProcAddress("eglCreateImageKHR");
00519 ERROR_GOTO_FAIL(!eglCreateImageKHR,
00520 "ERROR getting proc addr of eglCreateImageKHR\n");
00521
00522 eglDestroyImageKHR =
00523 (PFNEGLDESTROYIMAGEKHRPROC) eglGetProcAddress("eglDestroyImageKHR");
00524 ERROR_GOTO_FAIL(!eglDestroyImageKHR,
00525 "ERROR getting proc addr of eglDestroyImageKHR\n");
00526
00527 eglCreateSyncKHR =
00528 (PFNEGLCREATESYNCKHRPROC) eglGetProcAddress("eglCreateSyncKHR");
00529 ERROR_GOTO_FAIL(!eglCreateSyncKHR,
00530 "ERROR getting proc addr of eglCreateSyncKHR\n");
00531
00532 eglDestroySyncKHR =
00533 (PFNEGLDESTROYSYNCKHRPROC) eglGetProcAddress("eglDestroySyncKHR");
00534 ERROR_GOTO_FAIL(!eglDestroySyncKHR,
00535 "ERROR getting proc addr of eglDestroySyncKHR\n");
00536
00537 eglClientWaitSyncKHR =
00538 (PFNEGLCLIENTWAITSYNCKHRPROC) eglGetProcAddress("eglClientWaitSyncKHR");
00539 ERROR_GOTO_FAIL(!eglClientWaitSyncKHR,
00540 "ERROR getting proc addr of eglClientWaitSyncKHR\n");
00541
00542 eglGetSyncAttribKHR =
00543 (PFNEGLGETSYNCATTRIBKHRPROC) eglGetProcAddress("eglGetSyncAttribKHR");
00544 ERROR_GOTO_FAIL(!eglGetSyncAttribKHR,
00545 "ERROR getting proc addr of eglGetSyncAttribKHR\n");
00546
00547 glEGLImageTargetTexture2DOES =
00548 (PFNGLEGLIMAGETARGETTEXTURE2DOESPROC)
00549 eglGetProcAddress("glEGLImageTargetTexture2DOES");
00550 ERROR_GOTO_FAIL(!glEGLImageTargetTexture2DOES,
00551 "ERROR getting proc addr of glEGLImageTargetTexture2DOES\n");
00552
00553 return 0;
00554
00555 fail:
00556 return -1;
00557 }
00558
00559 void
00560 NvEglRenderer::CreateShader(GLuint program, GLenum type, const char *source,
00561 int size)
00562 {
00563
00564 char log[4096];
00565 int result = GL_FALSE;
00566
00567 GLuint shader = glCreateShader(type);
00568
00569 glShaderSource(shader, 1, &source, &size);
00570 glCompileShader(shader);
00571 glGetShaderiv(shader, GL_COMPILE_STATUS, &result);
00572 if (!result)
00573 {
00574 glGetShaderInfoLog(shader, sizeof(log), NULL, log);
00575 COMP_DEBUG_MSG("Got Fatal Log as " << log);
00576 }
00577 glAttachShader(program, shader);
00578
00579 if (glGetError() != GL_NO_ERROR)
00580 {
00581 COMP_ERROR_MSG("Got gl error as " << glGetError());
00582 }
00583 }
00584
00585 int
00586 NvEglRenderer::InitializeShaders(void)
00587 {
00588 GLuint program;
00589 int result = GL_FALSE;
00590 char log[4096];
00591 uint32_t pos_location = 0;
00592 uint32_t tc_location = 0;
00593
00594 static const float kVertices[] = {
00595 -1.f, -1.f,
00596 1.f, -1.f,
00597 -1.f, 1.f,
00598 1.f, 1.f,
00599 };
00600 static const float kTextureCoords[] = {
00601 0.0f, 1.0f,
00602 1.0f, 1.0f,
00603 0.0f, 0.0f,
00604 1.0f, 0.0f,
00605 };
00606
00607 static const char kVertexShader[] = "varying vec2 interp_tc;\n"
00608 "attribute vec4 in_pos;\n"
00609 "attribute vec2 in_tc; \n"
00610 "void main() { \n"
00611 "interp_tc = in_tc; \n" "gl_Position = in_pos; \n" "}\n";
00612
00613 static const char kFragmentShader[] =
00614 "#extension GL_OES_EGL_image_external : require\n"
00615 "precision mediump float;\n" "varying vec2 interp_tc; \n"
00616 "uniform samplerExternalOES tex; \n" "void main() {\n"
00617 "gl_FragColor = texture2D(tex, interp_tc);\n" "}\n";
00618
00619 glEnable(GL_SCISSOR_TEST);
00620 program = glCreateProgram();
00621
00622 CreateShader(program, GL_VERTEX_SHADER, kVertexShader,
00623 sizeof(kVertexShader));
00624 CreateShader(program, GL_FRAGMENT_SHADER, kFragmentShader,
00625 sizeof(kFragmentShader));
00626
00627 glLinkProgram(program);
00628 if (glGetError() != GL_NO_ERROR)
00629 {
00630 COMP_ERROR_MSG("Got gl error as " << glGetError());
00631 return -1;
00632 }
00633
00634 glGetProgramiv(program, GL_LINK_STATUS, &result);
00635 if (!result)
00636 {
00637 glGetShaderInfoLog(program, sizeof(log), NULL, log);
00638 COMP_ERROR_MSG("Error while Linking " << log);
00639 return -1;
00640 }
00641
00642 glUseProgram(program);
00643 if (glGetError() != GL_NO_ERROR)
00644 {
00645 COMP_ERROR_MSG("Got gl error as " << glGetError());
00646 return -1;
00647 }
00648
00649 pos_location = glGetAttribLocation(program, "in_pos");
00650
00651 glEnableVertexAttribArray(pos_location);
00652 glVertexAttribPointer(pos_location, 2, GL_FLOAT, GL_FALSE, 0, kVertices);
00653
00654 tc_location = glGetAttribLocation(program, "in_tc");
00655
00656 glEnableVertexAttribArray(tc_location);
00657 glVertexAttribPointer(tc_location, 2, GL_FLOAT, GL_FALSE, 0,
00658 kTextureCoords);
00659
00660 glActiveTexture(GL_TEXTURE0);
00661 glUniform1i(glGetUniformLocation(program, "texSampler"), 0);
00662 if (glGetError() != GL_NO_ERROR)
00663 {
00664 COMP_ERROR_MSG("Got gl error as " << glGetError());
00665 return -1;
00666 }
00667 COMP_DEBUG_MSG("Shaders intialized");
00668 return 0;
00669 }
00670
00671 int
00672 NvEglRenderer::create_texture()
00673 {
00674 int viewport[4];
00675
00676 glGetIntegerv(GL_VIEWPORT, viewport);
00677 glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
00678 glScissor(viewport[0], viewport[1], viewport[2], viewport[3]);
00679
00680 glGenTextures(1, &texture_id);
00681
00682 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture_id);
00683 return 0;
00684 }