X11 (X window) では Platform Interface に GLX を用います。
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() で確認することができます。
(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 と共有することができます。
下記は 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 のはじめ方