2014/02/08
Linux 他 X11 で OpenGL 4 を使う
X11 (X window) では Platform Interface に GLX を用います。
Display が本当に意味を持つのは X11 ならではで、egl の API 仕様も
X window のプログラムを見ると納得です。
(1) pixel format の選択
サーバーが対応しているピクセルフォーマットから必要なものを選択しています。
フォーマットの組み合わせは GPU やドライバによって異なります。
X native の API にも似たような仕組みとして XVisualInfo があります。
GLXFBConfig は GLX による拡張で、XVisualInfo* の代わりに用いることが可能です。
Windows でも Win32 API に PIXELFORMATDESCRIPTOR (ChoosePixelFormat())
がありました。WGL Extension の WGL_ARB_pixel_format
(wglChoosePixelFormatARB()) はこれを置き換えるもので、
XVisualInfo と GLXFBConfig との関係に似ています。
もし XCreateColormap などで Visual (XVisualInfo) が必要になったら
下記のように取得すれば OK です。
上の visual_info は config_array 同様 XFree() が必要です。
egl のような wrapper を作る場合、メモリ管理を伴うのは少々都合が悪い
場合があります。
GLXFBConfig をそのまま保持せずに、ID に紐付けておくと管理が楽になります。
この fb_config_id があれば GLXConfig が一意に定まるわけです。
fb_config_id から GLXFBConfig を取り出す方法は下記の通り。
XVisualInfo を得る方法はすでに述べた通りですが、GLXFBConfig から
直接 VisualID を求めることもできます。
(2) Context の作成
任意の Version の OpenGL Context を作成するには GLX_ARB_create_context
( glXCreateContextAttribsARB() ) が必要です。
試した下記の環境ではデフォルトで OpenGL 4.3 が選択されていたので、
場合によっては使わなくても問題ないかもしれません。
・Ubuntu 13.10 desktop x86_64
・GeForce GTX650
・NVIDIA binary Xorg driver 319
Core Profile のみ、OpenGL ES 2.0 互換 Profile の指定など、細かい設定を
行うならやはり glXCreateContextAttribsARB() が必要です。
他にも glX extension を利用する場合が考えられるので、
API の取得はまとめて管理した方が良いでしょう。
glX extension だけでなく、GL 4.x の各命令でも API の取得が必要になります。
4番目の True は Direct Rendering の指定です。
本来の X protocol はネットワーク透過なのでアプリケーションからドライバに
直接アクセスできませんが、ローカルサーバー利用時は条件によって可能になります。
現在の context が Direct Rendering 可能な状態 (DRI) かどうかは
glXIsDirect() で確認することができます。
・GL_ARB_create_context
(3) Window の作成
GLXFBConfig が決まった時点で Window を作れるので (2) との順番は任意。
(4) MakeCurrent
Window は GLXDrawable です。
これでレンダリングできますが、OpenGL 2 以降の API を用いるには
個別に glXGetProcAddress() して関数を用意する必要があります。
(5) OpenGL API の取得
ゼロから始めるとここが一番手間かもしれません。
ただしこの流れは Windows で OpenGL を用いる場合と完全に同一です。
API 名 Table の作成、API の取り出しコードは Windows と共有することができます。
・OpenGL のはじめ方 (Windows)
下記は API Table の例です。
OpenGL 4.x の glcorearb.h からスクリプトで自動変換して生成しています。
この方法だと、新しい API や extension 対応ドライバがリリースされたら
即座に対応することができます。
Platform Interface のまとめ
Windows/Linux X11 は GetProcAddress() が必要ですが対応ドライバがあれば
新しい API をすぐ使えます。
OSX はそのまま OpenGL を使えて手間いらずですが 10.9 は OpenGL 4.1 まで。
OSX + XQuartz (HD4000) は残念ながら 2.1 でした。
Nexus 7 (2012) + Ubuntu desktop のように OpenGL ES 2.0 対応 GPU の
Linux では GLX ではなく X11 上で EGL + OpenGL ES 2.0 API が使えます。
Raspberry Pi や 旧 NetWalker のように、X が動いても OpenGL 未対応の場合
フレームバッファに直接レンダリングすることになります。
この場合も EGL なので、Window の有無にかかわらず利用できる EGL は互換性が
取りやすくなっています。
関連エントリ
・Mac OS X 10.9 Mavericks の OpenGL 4.1 対応と Windows 8.1 Intel HD 4000 Driver
・Mac OS X で OpenGL の描画 (Xcode5/retina)
・Nexus 7 (2012) Ubuntu desktop 上での開発とバッテリー設定
・NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (3)
・OpenGL のはじめ方
Display が本当に意味を持つのは X11 ならではで、egl の API 仕様も
X window のプログラムを見ると納得です。
Display* display= NULL; int screen= 0; display= XOpenDisplay( NULL ); screen= DefaultScreen( display );
(1) pixel format の選択
static int attrib_list[]= { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 24, GLX_STENCIL_SIZE, 8, None }; int n_items= 0; GLXFBConfig* config_array= glXChooseFBConfig( display, screen, attrib_list, &n_items ); 〜 if( n_items ){ GLXFBConfig fb_config= config_array[0]; 〜 } XFree( config_array );
サーバーが対応しているピクセルフォーマットから必要なものを選択しています。
フォーマットの組み合わせは GPU やドライバによって異なります。
X native の API にも似たような仕組みとして XVisualInfo があります。
GLXFBConfig は GLX による拡張で、XVisualInfo* の代わりに用いることが可能です。
Windows でも Win32 API に PIXELFORMATDESCRIPTOR (ChoosePixelFormat())
がありました。WGL Extension の WGL_ARB_pixel_format
(wglChoosePixelFormatARB()) はこれを置き換えるもので、
XVisualInfo と GLXFBConfig との関係に似ています。
もし XCreateColormap などで Visual (XVisualInfo) が必要になったら
下記のように取得すれば OK です。
// GLXFBConfig -> XVisualInfo XVisualInfo* visual_info= glXGetVisualFromFBConfig( display, fb_config ); 〜 XFree( visual_info );
上の visual_info は config_array 同様 XFree() が必要です。
egl のような wrapper を作る場合、メモリ管理を伴うのは少々都合が悪い
場合があります。
GLXFBConfig をそのまま保持せずに、ID に紐付けておくと管理が楽になります。
// GLXFBConfig -> fb_config_id int fb_config_id= 0; glXGetFBConfigAttrib( display, fb_config, GLX_FBCONFIG_ID, &fb_config_id );
この fb_config_id があれば GLXConfig が一意に定まるわけです。
fb_config_id から GLXFBConfig を取り出す方法は下記の通り。
// fb_config_id -> GLXFBConfig int attr_list[]= { GLX_FBCONFIG_ID, fb_config_id, None }; int n_items= 0; GLXFBConfig* fb_config_ptr= glXChooseFBConfig( display, screen, attr_list, &n_items ); assert( n_items == 1 ); GLXFBConfig fb_config= fb_config_ptr[0]; 〜 XFree( fb_config_ptr );
XVisualInfo を得る方法はすでに述べた通りですが、GLXFBConfig から
直接 VisualID を求めることもできます。
// GLXFBConfig -> VisualID int visual_id= 0; glXGetFBConfigAttrib( display, config_array[0], GLX_VISUAL_ID, &visual_id );
(2) Context の作成
任意の Version の OpenGL Context を作成するには GLX_ARB_create_context
( glXCreateContextAttribsARB() ) が必要です。
試した下記の環境ではデフォルトで OpenGL 4.3 が選択されていたので、
場合によっては使わなくても問題ないかもしれません。
・Ubuntu 13.10 desktop x86_64
・GeForce GTX650
・NVIDIA binary Xorg driver 319
Core Profile のみ、OpenGL ES 2.0 互換 Profile の指定など、細かい設定を
行うならやはり glXCreateContextAttribsARB() が必要です。
// Extension API glXCreateContextAttribsARB() の取り出し PFNGLXCREATECONTEXTATTRIBSARBPROC f_glXCreateContextAttribsARB= (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress( (const GLubyte*)"glXCreateContextAttribsARB" ); // OpenGL 4.3 context 作成 static int att_command[]= { GLX_CONTEXT_MAJOR_VERSION_ARB, 4, GLX_CONTEXT_MINOR_VERSION_ARB, 3, GLX_CONTEXT_FLAGS_ARB, 0, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, None }; GLXContext context= f_glXCreateContextAttribsARB( display, fb_config, 0, True, att_command );
他にも glX extension を利用する場合が考えられるので、
API の取得はまとめて管理した方が良いでしょう。
glX extension だけでなく、GL 4.x の各命令でも API の取得が必要になります。
4番目の True は Direct Rendering の指定です。
本来の X protocol はネットワーク透過なのでアプリケーションからドライバに
直接アクセスできませんが、ローカルサーバー利用時は条件によって可能になります。
現在の context が Direct Rendering 可能な状態 (DRI) かどうかは
glXIsDirect() で確認することができます。
・GL_ARB_create_context
(3) Window の作成
GLXFBConfig が決まった時点で Window を作れるので (2) との順番は任意。
XVisualInfo* visual_info= glXGetVisualFromFBConfig( display, fb_config ); Window root= RootWindow( NativeDisplay, screen ); XSetWindowAttributes attr; attr.background_pixel= 0; attr.border_pixel= 0; attr.colormap= XCreateColormap( display, root, visual_info->visual, AllocNone ); attr.event_mask= 0 |StructureNotifyMask |ExposureMask |KeyPressMask |KeyReleaseMask |ButtonPressMask |ButtonReleaseMask ; Window win= XCreateWindow( display, root, x, y, width, height, 0, // border visual_info->depth, InputOutput, visual_info->visual, CWBorderPixel|CWColormap|CWEventMask, &attr ); XFree( visual_info ); visual_info= NULL; XMapWindow( display, win ); XFlush( display );
(4) MakeCurrent
glXMakeCurrent( display, win, context );
Window は GLXDrawable です。
これでレンダリングできますが、OpenGL 2 以降の API を用いるには
個別に glXGetProcAddress() して関数を用意する必要があります。
(5) OpenGL API の取得
ゼロから始めるとここが一番手間かもしれません。
ただしこの流れは Windows で OpenGL を用いる場合と完全に同一です。
API 名 Table の作成、API の取り出しコードは Windows と共有することができます。
・OpenGL のはじめ方 (Windows)
下記は API Table の例です。
OpenGL 4.x の glcorearb.h からスクリプトで自動変換して生成しています。
この方法だと、新しい API や extension 対応ドライバがリリースされたら
即座に対応することができます。
// flatlib3 GL_HeaderParser.py // linux/GLFunction_GL4.inc void (APIENTRY *p_glBlendColor)( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha ); void (APIENTRY *p_glBlendEquation)( GLenum mode ); void (APIENTRY *p_glDrawRangeElements)( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices ); 〜 void (APIENTRY *p_glTexPageCommitmentARB)( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth, GLboolean resident ); // total=525 ImportListType ImportList_GL4[]= { { "glBlendColor", (void*)&p_glBlendColor }, { "glBlendEquation", (void*)&p_glBlendEquation }, { "glDrawRangeElements", (void*)&p_glDrawRangeElements }, { "glTexImage3D", (void*)&p_glTexImage3D }, { "glTexSubImage3D", (void*)&p_glTexSubImage3D }, 〜 { "glGetNamedStringARB", (void*)&p_glGetNamedStringARB }, { "glGetNamedStringivARB", (void*)&p_glGetNamedStringivARB }, { "glTexPageCommitmentARB", (void*)&p_glTexPageCommitmentARB }, { NULL, NULL } }; // total=525
// flatlib3 GL_HeaderParser.py // linux/GLFunction_GL4.h extern void (APIENTRY *p_glBlendColor)( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha ); inline void glBlendColor( GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha ) { FL_GLAPI_ASSERT( p_glBlendColor ); return p_glBlendColor( red, green, blue, alpha ); } extern void (APIENTRY *p_glBlendEquation)( GLenum mode ); inline void glBlendEquation( GLenum mode ) { FL_GLAPI_ASSERT( p_glBlendEquation ); return p_glBlendEquation( mode ); } extern void (APIENTRY *p_glDrawRangeElements)( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices ); inline void glDrawRangeElements( GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, const GLvoid * indices ) { FL_GLAPI_ASSERT( p_glDrawRangeElements ); return p_glDrawRangeElements( mode, start, end, count, type, indices ); } 〜
Platform Interface のまとめ
Windows WGL GL4.x wglGetProcAddress() Linux X11 GLX GL4.x glXGetProcAddress() OSX NSOpenGL GL4.1 OSX + X11 GLX GL2.1 (XQuartz + HD4000) Linux X11 + ES EGL ES2/3 Android EGL ES2/3 iOS GLK/EAGL ES2/3
Windows/Linux X11 は GetProcAddress() が必要ですが対応ドライバがあれば
新しい API をすぐ使えます。
OSX はそのまま OpenGL を使えて手間いらずですが 10.9 は OpenGL 4.1 まで。
OSX + XQuartz (HD4000) は残念ながら 2.1 でした。
Nexus 7 (2012) + Ubuntu desktop のように OpenGL ES 2.0 対応 GPU の
Linux では GLX ではなく X11 上で EGL + OpenGL ES 2.0 API が使えます。
Raspberry Pi や 旧 NetWalker のように、X が動いても OpenGL 未対応の場合
フレームバッファに直接レンダリングすることになります。
この場合も EGL なので、Window の有無にかかわらず利用できる EGL は互換性が
取りやすくなっています。
関連エントリ
・Mac OS X 10.9 Mavericks の OpenGL 4.1 対応と Windows 8.1 Intel HD 4000 Driver
・Mac OS X で OpenGL の描画 (Xcode5/retina)
・Nexus 7 (2012) Ubuntu desktop 上での開発とバッテリー設定
・NetWalker PC-Z1 i.MX515 OpenGL ES 2.0 (3)
・OpenGL のはじめ方