Ubuntuで EGL使えるか?

先日NVIDIAのドライバをインストールした Ubuntu ですが、なんとEGLやGLES2についてのライブラリをもっているようでした。(331.20ではダメだった・・・)

$ find /usr/ 2>/dev/null | grep GLES
/usr/lib32/libGLESv1_CM.so.1
/usr/lib32/libGLESv1_CM.so.331.49
/usr/lib32/libGLESv2.so.2
/usr/lib32/libGLESv2.so
/usr/lib32/libGLESv2.so.331.49
/usr/lib32/libGLESv1_CM.so

これは X 上で EGL & GLES2が使えるんじゃないかと思い試してみることにしました。

まずXのヘッダやライブラリをインストールします.
$ sudo apt-get install xorg-dev

/usr/include/features.h:324:26: 致命的エラー: bits/predefs.h: そのようなファイルやディレクトリはありません

32bitビルドする際には上記のようなエラーメッセージが出てしまうので、libc6-dev-i386 パッケージをインストールして対応します。またC++の標準ライブラリについても同様にエラーが生じてしまうのでこちらは g++-multilib をインストールして対応します。

sudo apt-get install libc6-dev-i386 g++-multilib

作成したプログラムは以下のようなものです。サンプルなのですごく適当です。
まずはGLXというかウィンドウ生成部分についてです。

#include 
#include 
#include 
#include 

#include 
#include 
#include 

#include 
#include 

static Display* xdisp;
static Window window;

EGLDisplay eglDisplay;
EGLContext eglContext;
EGLSurface eglSurface;

int main() {
  xdisp = XOpenDisplay( NULL );
  if( xdisp == NULL ) {
    return -1;
 u }

  Window rootWin = DefaultRootWindow( xdisp );

  int screen = DefaultScreen( xdisp );
  unsigned long white = WhitePixel( xdisp, screen );
  unsigned long black = BlackPixel( xdisp, screen );

  window = XCreateSimpleWindow( xdisp, rootWin,
                                100, 100,
                                640, 480,
                                2,
                                black, white );


  XSelectInput( xdisp, window, KeyPressMask );

  XMapWindow( xdisp, window );

  if( initializeEGL() < 0 ) {
    exit( EXIT_FAILURE );
  }

  GLuint shaderProgram = createShaderProgram();
  mainLoop( shaderProgram );
  glDeleteProgram( shaderProgram );

  eglDestroyContext( eglDisplay, eglContext );
  eglDestroySurface( eglDisplay, eglSurface );
  eglTerminate( eglDisplay );

  XDestroyWindow( xdisp, window );
  XCloseDisplay( xdisp );

  return 0;
}

EGLの初期化部分はこんな感じです。

int initializeEGL() {
  eglDisplay = eglGetDisplay( (EGLNativeDisplayType) xdisp );
  if( eglDisplay == EGL_NO_DISPLAY ) {
    fprintf( stderr, "NO EGL DISPLAY\n" );
    return -1;
  }
  if( !eglInitialize( eglDisplay, NULL, NULL ) ) {
    fprintf( stderr, "Unable to initialize EGL\n" );
    return -1;
  }

  EGLint attr[] = {
    EGL_BUFFER_SIZE, 16,
    EGL_RENDERABLE_TYPE,
    EGL_OPENGL_ES2_BIT,
    EGL_NONE,
  };
  EGLConfig cfg;
  EGLint numConfigs;
  if( !eglChooseConfig( eglDisplay, attr, &cfg, 1, &numConfigs ) ) {
    fprintf( stderr, "Failed to choose config.\n" );
    return -1;
  }
  if( numConfigs != 1 ) {
    fprintf( stderr, "exactly one config. (%d)\n", numConfigs );
    return -1;
  }

  eglSurface = eglCreateWindowSurface( eglDisplay, cfg, window, NULL );
  if( eglSurface == EGL_NO_SURFACE ) {
    fprintf( stderr, "Unable to create surface (eglError=%d)\n", eglGetError() );
    return -1;
  }

  EGLint ctxattr[] = {
    EGL_CONTEXT_CLIENT_VERSION, 2,
    EGL_NONE,
  };
  eglContext = eglCreateContext( eglDisplay, cfg, EGL_NO_CONTEXT, ctxattr );
  if( eglContext == EGL_NO_CONTEXT ) {
    fprintf( stderr, "Unable to create EGL context. (%d)\n", eglGetError() );
    return -1;
  }
  eglMakeCurrent( eglDisplay, eglSurface, eglSurface, eglContext );

  char* p = (char*)glGetString( GL_RENDERER );
  if( p ) {
    printf( "RENDERER: %s\n", p );
  }
  return 0;
}

メインループとなっているGLの描画部分とリソース生成部分はこんな感じ。

static GLchar srcVertexShader[] =
  "attribute vec4 position0;\n"
  "attribute vec2 texcoord0;\n"
  "varying vec2 vsout_texcoord0;\n"
  "uniform float  theta;\n"
  "void main() {\n"
  "  gl_Position = position0;\n"
  "  gl_Position.x = position0.x * cos(theta) - position0.y * sin(theta);\n"
  "  gl_Position.y = position0.x * sin(theta) + position0.y * cos(theta);\n"
  "  vsout_texcoord0 = texcoord0;\n"
  "}";

static GLchar srcFragmentShader[] =
  "precision mediump float;\n"
  "varying vec2 vsout_texcoord0;\n"
  "void main() {\n"
  "  gl_FragColor = vec4( vsout_texcoord0.xy, 0, 1 );\n"
  "}";

void checkCompiled( GLuint shader ) {
  GLint status;
  glGetShaderiv( shader, GL_COMPILE_STATUS, &status );
  if( status != GL_TRUE ) {
    GLint length;
    glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &length );
    if( length ) {
      char* buf = (char*)malloc( length );
      glGetShaderInfoLog( shader, length, NULL, buf );
      fprintf( stderr, "CompiledLog: %s\n", buf );
      free( buf );
    }
    exit( EXIT_FAILURE );
  }

}

GLuint createShaderProgram() {
  GLuint shaderVS = glCreateShader( GL_VERTEX_SHADER );
  GLuint shaderFS = glCreateShader( GL_FRAGMENT_SHADER );

  GLsizei length;
  GLchar* vsSources[] = {
    srcVertexShader,
  };
  GLchar* fsSources[] = {
    srcFragmentShader,
  };

  glShaderSource( shaderVS, 1, (GLchar**) &vsSources, NULL );
  glCompileShader( shaderVS );
  checkCompiled( shaderVS );

  glShaderSource( shaderFS, 1, (GLchar**) &fsSources, NULL );
  glCompileShader( shaderFS );
  checkCompiled( shaderFS );

  GLuint program;
  program = glCreateProgram();
  glAttachShader( program, shaderVS );
  glAttachShader( program, shaderFS );

  glLinkProgram( program );

  return program;
}


void mainLoop( GLuint  shaderProgram ) {
  XEvent evt;
  bool isEnd = false;
  while( !isEnd ) {
    while( XPending( xdisp ) ) {
      XNextEvent( xdisp, &evt );
      switch( evt.type ) {
      case KeyPress:
        isEnd = true;
        break;
      }
    }

    glClearColor( 0.25f, 0.25f, 0.125f, 0.0f );
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

    glUseProgram( shaderProgram );

    GLint locPos = glGetAttribLocation( shaderProgram, "position0" );
    GLint locUV0 = glGetAttribLocation( shaderProgram, "texcoord0" );

    float vertices[] = {
      0.0f, 0.0f, 0.0f,   0.0f, 0.0f,
      1.0f, 0.0f, 0.0f,   1.0f, 0.0f,
      1.0f, 1.0f, 0.0f,   1.0f, 1.0f,
    };
    glVertexAttribPointer( locPos, 3, GL_FLOAT, false, sizeof(float)*5, &vertices[0] );
    glVertexAttribPointer( locUV0, 2, GL_FLOAT, false, sizeof(float)*5, &vertices[3] );
    glEnableVertexAttribArray( locPos );
    glEnableVertexAttribArray( locUV0 );

    static int count = 0;
    glUniform1f( glGetUniformLocation( shaderProgram, "theta" ), count*0.001f );
    glDrawArrays( GL_TRIANGLES, 0, 3 );

    // usleep( 1000 );
    eglSwapBuffers( eglDisplay, eglSurface );
    count++;
  }
}

これらを動かしてみてスクリーンショットをとったのが以下の図です。
無事にEGL+GLES2で三角形が描画されました。ちゃんとGLES2用として動いているようで、シェーダーで precision 設定を忘れたらコンパイルが通りませんでした。

ubuntu-egl-gles2

おまけ

このソースコードのビルドには以下のオプションを設定しています。
32bitコンパイルすることを忘れないようにしましょう。

g++ -m32 -g -I/usr/include -I./ -lX11 -lEGL -lGLESv2 -L/usr/lib/i386-linux-gnu

また、Xの転送をしている場合、eglの初期化で失敗するようです。
自分の環境ではウィンドウをWindows7上のCygwin Xサーバーへ転送させるようにして処理していたのですが、これでは初期化で失敗しました。少なくともEGL使用時にはネットワークをまたぐことはできないようです(たぶん)。

まとめ

LinuxでもNVIDIAの最新ドライバをインストールすると EGL+GLES2を使用可能なことがわかりました。
ただ検索結果を見ても気付くように 32bit のものしか対応できない感じですね。今後ARM系も 64bit 化の波が来そうですし、その頃にはあわせて提供もされてくるのかな、と思いました。

OpenGLプログラミング
すらりんをフォローする
すらりん日記

コメント

タイトルとURLをコピーしました