前回はEGLで組んでみましたが、折角普通のLinux,OpenGLが使えるボードということで、X11とGLXで前回同様のアプリケーションを作成してみようと思います。
NVIDIAでLinux OpenGL といえば、関数エントリが共有ライブラリに準備されていることで有名です。この組み込み用ではどうであるか確認してみます。
$ nm -Do /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1 ## 一部抜粋 ### /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:00067478 T glActiveStencilFaceEXT /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0005ec80 T glActiveTexture /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:00064688 T glActiveTextureARB /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0006b58c T glActiveVaryingNV /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0005d82c T glAlphaFunc /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0006ed38 T glAlphaFuncx /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:000656d4 T glAreProgramsResidentNV /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0005e4d8 T glAreTexturesResident /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:00064568 T glAreTexturesResidentEXT /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0005e19c T glArrayElement /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0006440c T glArrayElementEXT /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0006aa24 T glAttachObjectARB /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:000603f8 T glAttachShader /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0005b800 T glBegin /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:00061498 T glBeginConditionalRender /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0006b154 T glBeginConditionalRenderNV /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:0006b10c T glBeginConditionalRenderNVX /usr/lib/arm-linux-gnueabihf/tegra/libGL.so.1:00066d08 T glBeginOcclusionQueryNV
今回のボードでも前例通り各関数の実体が含まれるようです。
そこで、GLヘッダを読み込む前に、 GL_GLEXT_PROTOTYPES (1) を定義して extern宣言させた状態にしておきます。
早速プログラムを示します。
#include#include #include #include #include #include #include #define GL_GLEXT_PROTOTYPES (1) #define GLX_GLXEXT_PROTOTYPES (1) #include #include #include #include static Display* xdisp; static Window window; static GLuint shaderProgram; GLXContext glxContext; XVisualInfo* visInfo; void initializeOpenGL( int screen ); void terminateOpenGL(); void mainloop(); GLuint createShaderProgram(); int main() { xdisp = XOpenDisplay( NULL ); if( xdisp == NULL ) { fprintf( stderr, "Cannot open display.\n" ); return -1; } int screen = DefaultScreen( xdisp ); initializeOpenGL( screen ); XSelectInput( xdisp, window, KeyPressMask|ButtonPressMask|UnmapNotify ); Atom atom, atom2; atom = XInternAtom( xdisp, "WM_DELETE_WINDOW", False ); XSetWMProtocols( xdisp, window, &atom, 1 ); atom2 = XInternAtom( xdisp, "WM_PROTOCOLS", False ); XMapWindow( xdisp, window ); XFlush( xdisp ); shaderProgram = createShaderProgram(); XEvent evt; bool isFinish = false; int frameCount = 0; timeval t0, t1; gettimeofday( &t0, NULL ); const int countSecond = 5; do { while( XPending( xdisp ) ) { XNextEvent( xdisp, &evt ); switch( evt.type ) { case ClientMessage: if( evt.xclient.message_type == atom2 && evt.xclient.data.l[0] == atom ) { XFlush( xdisp ); isFinish = true; } break; case KeyPress: isFinish = true; break; } } mainloop(); frameCount++; gettimeofday( &t1, NULL ); if( t1.tv_sec * 1000000 + t1.tv_usec - t0.tv_sec * 1000000 - t0.tv_usec > countSecond * 1000000 ) { printf( "FPS: %.2f\n", frameCount / (float)countSecond ); gettimeofday( &t0, NULL ); frameCount = 0; } } while( !isFinish ); glDeleteProgram( shaderProgram ); terminateOpenGL(); XDestroyWindow( xdisp, window ); XCloseDisplay( xdisp ); return 0; } void initializeOpenGL( int screen ) { int attrib[] = { GLX_RGBA, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DOUBLEBUFFER, GLX_DEPTH_SIZE, 24, None, }; visInfo = glXChooseVisual( xdisp, 0, attrib ); if( !visInfo ) { fprintf( stderr, "Error: invalid visInfo\n" ); return; } XSetWindowAttributes wattr; wattr.background_pixel = 0; wattr.border_pixel = 0; wattr.colormap = XCreateColormap( xdisp, RootWindow( xdisp, screen ), visInfo->visual, AllocNone ); wattr.override_redirect = false; wattr.event_mask = StructureNotifyMask | ExposureMask | KeyPressMask ; unsigned long mask = CWBackPixel | CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect; window = XCreateWindow( xdisp, RootWindow( xdisp, screen ), 0, 0, 640, 480, 0, visInfo->depth, InputOutput, visInfo->visual, mask, &wattr ); glxContext = glXCreateContext( xdisp, visInfo, NULL, True ); glXMakeCurrent( xdisp, window, glxContext ); printf( "GL_RENDERER: %s\n", (char*)glGetString( GL_RENDERER ) ); printf( "GL_VENDOR: %s\n", (char*)glGetString( GL_VENDOR ) ); printf( "GL_VERSION: %s\n", (char*)glGetString( GL_VERSION ) ); } void terminateOpenGL() { glXDestroyContext( xdisp, glxContext ); } static GLchar srcVertexShader[] = "attribute vec4 position0;\n" "attribute vec4 color0;\n" "varying vec4 vsout_color0;\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_color0 = color0;\n" "}"; static GLchar srcFragmentShader[] = "precision mediump float;\n" "varying vec4 vsout_color0;\n" "void main() {\n" " gl_FragColor = vsout_color0;\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() { static unsigned int count = 0; static float angle = 0; glClearColor( 0.25f, 0.25f, 0.25f, 0.0f ); glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); glUseProgram( shaderProgram ); GLint locPos = glGetAttribLocation( shaderProgram, "position0" ); GLint locColor = glGetAttribLocation( shaderProgram, "color0" ); float vertices[] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, }; GLint stride = sizeof(float)*7; glVertexAttribPointer( locPos, 3, GL_FLOAT, false, stride, &vertices[0] ); glVertexAttribPointer( locColor, 4, GL_FLOAT, false, stride, &vertices[3] ); glEnableVertexAttribArray( locPos ); glEnableVertexAttribArray( locColor ); glUniform1f( glGetUniformLocation( shaderProgram, "theta" ), count*0.001f ); glDrawArrays( GL_TRIANGLES, 0, 3 ); glXSwapBuffers( xdisp, window ); count++; }
GL_RENDERER: GK20A/AXI GL_VENDOR: NVIDIA Corporation GL_VERSION: 4.4.0 NVIDIA 19.3 FPS: 1294.80 FPS: 1151.00 FPS: 1046.20 FPS: 1073.00 FPS: 1049.40 FPS: 1051.40 FPS: 987.60 FPS: 995.60 FPS: 1008.20 FPS: 1582.60
平均するとおよそ1000ちょいのFPSといったところでしょうか。前回のEGLに比べると多少パフォーマンスがよい傾向にありそうです。
VSyncをオフにしてみようと思ったのですが、 glXSwapIntervalEXT がこの環境では使用できず試してみることは出来ませんでした。コンテキストは予想通り最新の 4.4 が取得されました。
プログラムコードがglVertexAttribPointerでクライアント配列を渡している部分があったり、毎描画フレームで無駄な処理をしているところが多いので、もしかするとCPU側でバラツキの要因を持っているのかもしれません。