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 のはじめ方