1 module gfx.window.xcb.context; 2 3 version(linux): 4 5 import gfx.bindings.opengl.loader : SharedLib; 6 import gfx.gl3.context : GlAttribs, GlContext, glVersions; 7 import X11.Xlib : XDisplay = Display, XErrorEvent; 8 9 import gfx.window.xcb : gfxXcbLog; 10 11 /// GlX backed OpenGL context 12 class XcbGlContext : GlContext 13 { 14 import gfx.bindings.opengl.loader : SharedSym; 15 import gfx.bindings.opengl.gl : Gl; 16 import gfx.bindings.opengl.glx : Glx, GLXContext, GLXFBConfig; 17 import gfx.core.rc : atomicRcCode, Disposable; 18 19 mixin(atomicRcCode); 20 21 private XDisplay* _dpy; 22 private int _mainScreenNum; 23 private GlAttribs _attribs; 24 private Glx _glx; 25 private Gl _gl; 26 private string[] _glxAvailExts; 27 private string[] _glAvailExts; 28 private GLXContext _ctx; 29 private bool ARB_create_context; 30 private bool MESA_query_renderer; 31 private bool MESA_swap_control; 32 private bool EXT_swap_control; 33 34 /// Contruct an OpenGL context for the given display and screen. 35 /// The window is necessary to make the context current on and loading GL symbols. 36 /// It can be a dummy window destroyed right initialization. 37 this (XDisplay* dpy, in int mainScreenNum, in GlAttribs attribs, in size_t window) 38 { 39 import gfx.bindings.opengl.loader : openSharedLib, loadSharedSym, SharedLib; 40 import gfx.bindings.opengl.gl : GL_EXTENSIONS; 41 import gfx.bindings.opengl.glx : PFN_glXGetProcAddressARB; 42 import gfx.bindings.opengl.util : splitExtString; 43 import std.algorithm : canFind; 44 import std.exception : enforce; 45 import X11.Xlib : XSetErrorHandler, XSync; 46 47 _dpy = dpy; 48 _mainScreenNum = mainScreenNum; 49 _attribs = attribs; 50 51 auto lib = loadGlLib(); 52 auto getProcAddress = cast(PFN_glXGetProcAddressARB)enforce(loadSharedSym(lib, "glXGetProcAddressARB")); 53 SharedSym loadSymbol(in string symbol) { 54 import std..string : toStringz; 55 return cast(SharedSym)getProcAddress(cast(const(ubyte)*)toStringz(symbol)); 56 } 57 58 _glx = new Glx(&loadSymbol); 59 60 const glxExts = splitExtString(_glx.QueryExtensionsString(_dpy, _mainScreenNum)); 61 ARB_create_context = glxExts.canFind("GLX_ARB_create_context"); 62 MESA_query_renderer = glxExts.canFind("GLX_MESA_query_renderer"); 63 MESA_swap_control = glxExts.canFind("GLX_MESA_swap_control"); 64 EXT_swap_control = glxExts.canFind("GLX_EXT_swap_control"); 65 66 enforce( ARB_create_context && ( MESA_swap_control || EXT_swap_control )); 67 68 auto fbc = getGlxFBConfig(attribs); 69 GlAttribs attrs = attribs; 70 71 auto oldHandler = XSetErrorHandler(&createCtxErrorHandler); 72 73 foreach (const glVer; glVersions) { 74 attrs.majorVersion = glVer / 10; 75 attrs.minorVersion = glVer % 10; 76 if (attrs.decimalVersion < attribs.decimalVersion) break; 77 78 const ctxAttribs = getCtxAttribs(attrs); 79 gfxXcbLog.tracef("attempting to create OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 80 81 createContextErrorFlag = false; 82 _ctx = _glx.CreateContextAttribsARB(_dpy, fbc, null, 1, &ctxAttribs[0]); 83 84 if (_ctx && !createContextErrorFlag) break; 85 } 86 87 XSetErrorHandler(oldHandler); 88 89 enforce(_ctx); 90 XSync(_dpy, 0); 91 _attribs = attrs; 92 93 gfxXcbLog.tracef("created OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 94 95 XcbGlContext.makeCurrent(window); 96 _gl = new Gl(&loadSymbol); 97 98 gfxXcbLog.trace("done loading GL/GLX"); 99 } 100 101 override void dispose() { 102 import gfx.bindings.opengl.loader : closeSharedLib; 103 104 _glx.DestroyContext(_dpy, _ctx); 105 gfxXcbLog.trace("destroyed GL/GLX context"); 106 } 107 108 109 override @property Gl gl() { 110 return _gl; 111 } 112 113 override @property GlAttribs attribs() { 114 return _attribs; 115 } 116 117 override bool makeCurrent(size_t nativeHandle) 118 { 119 import gfx.bindings.opengl.glx : GLXDrawable; 120 return _glx.MakeCurrent(_dpy, cast(GLXDrawable)nativeHandle, _ctx) != 0; 121 } 122 123 override void doneCurrent() 124 { 125 _glx.MakeCurrent(_dpy, 0, null); 126 } 127 128 override @property bool current() const 129 { 130 return _glx.GetCurrentContext() is _ctx; 131 } 132 133 override @property int swapInterval() 134 { 135 import gfx.bindings.opengl.glx : GLXDrawable; 136 if (MESA_swap_control) { 137 return _glx.GetSwapIntervalMESA(); 138 } 139 else if (EXT_swap_control) { 140 GLXDrawable drawable = _glx.GetCurrentDrawable(); 141 uint swap; 142 143 if (drawable) { 144 import gfx.bindings.opengl.glx : GLX_SWAP_INTERVAL_EXT; 145 _glx.QueryDrawable(_dpy, drawable, GLX_SWAP_INTERVAL_EXT, &swap); 146 return swap; 147 } 148 else { 149 gfxXcbLog.warning("could not get glx drawable to get swap interval"); 150 return -1; 151 } 152 153 } 154 return -1; 155 } 156 157 override @property void swapInterval(int interval) 158 { 159 import gfx.bindings.opengl.glx : GLXDrawable; 160 161 if (MESA_swap_control) { 162 _glx.SwapIntervalMESA(interval); 163 } 164 else if (EXT_swap_control) { 165 GLXDrawable drawable = _glx.GetCurrentDrawable(); 166 167 if (drawable) { 168 _glx.SwapIntervalEXT(_dpy, drawable, interval); 169 } 170 else { 171 gfxXcbLog.warning("could not get glx drawable to set swap interval"); 172 } 173 } 174 } 175 176 override void swapBuffers(size_t nativeHandle) 177 { 178 import gfx.bindings.opengl.glx : GLXDrawable; 179 _glx.SwapBuffers(_dpy, cast(GLXDrawable)nativeHandle); 180 } 181 182 private GLXFBConfig getGlxFBConfig(in GlAttribs attribs) 183 { 184 import X11.Xlib : XFree; 185 186 const glxAttribs = getGlxAttribs(attribs); 187 188 int numConfigs; 189 auto fbConfigs = _glx.ChooseFBConfig(_dpy, _mainScreenNum, &glxAttribs[0], &numConfigs); 190 191 if (!fbConfigs || !numConfigs) 192 { 193 gfxXcbLog.error("GFX-GLX: could not get fb config"); 194 return null; 195 } 196 scope (exit) XFree(fbConfigs); 197 198 return fbConfigs[0]; 199 } 200 201 } 202 203 204 private SharedLib loadGlLib() 205 { 206 import gfx.bindings.opengl.loader : openSharedLib; 207 208 immutable glLibNames = ["libGL.so.1", "libGL.so"]; 209 210 foreach (ln; glLibNames) { 211 auto lib = openSharedLib(ln); 212 if (lib) { 213 gfxXcbLog.tracef("opening shared library %s", ln); 214 return lib; 215 } 216 } 217 218 import std.conv : to; 219 throw new Exception("could not load any of these libraries: " ~ glLibNames.to!string); 220 } 221 222 private bool createContextErrorFlag; 223 224 extern(C) private int createCtxErrorHandler(XDisplay *dpy, XErrorEvent *error) 225 { 226 createContextErrorFlag = true; 227 return 0; 228 } 229 230 private int[] getGlxAttribs(in GlAttribs attribs) pure 231 { 232 import gfx.bindings.opengl.glx; 233 import gfx.graal.format : formatDesc, redBits, greenBits, blueBits, 234 alphaBits, depthBits, stencilBits; 235 236 int[] glxAttribs = [ 237 GLX_X_RENDERABLE, 1, 238 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 239 GLX_RENDER_TYPE, GLX_RGBA_BIT, 240 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR 241 ]; 242 243 const colorDesc = formatDesc(attribs.colorFormat); 244 const depthStencilDesc = formatDesc(attribs.depthStencilFormat); 245 246 const r = redBits(colorDesc.surfaceType); 247 const g = greenBits(colorDesc.surfaceType); 248 const b = blueBits(colorDesc.surfaceType); 249 const a = alphaBits(colorDesc.surfaceType); 250 const d = depthBits(depthStencilDesc.surfaceType); 251 const s = stencilBits(depthStencilDesc.surfaceType); 252 253 if (r) glxAttribs ~= [GLX_RED_SIZE, r]; 254 if (g) glxAttribs ~= [GLX_GREEN_SIZE, g]; 255 if (b) glxAttribs ~= [GLX_BLUE_SIZE, b]; 256 if (a) glxAttribs ~= [GLX_ALPHA_SIZE, a]; 257 if (d) glxAttribs ~= [GLX_DEPTH_SIZE, d]; 258 if (s) glxAttribs ~= [GLX_STENCIL_SIZE, s]; 259 260 if (attribs.doublebuffer) glxAttribs ~= [GLX_DOUBLEBUFFER, 1]; 261 262 if (attribs.samples > 1) 263 glxAttribs ~= [GLX_SAMPLE_BUFFERS, 1, GLX_SAMPLES, attribs.samples]; 264 265 return glxAttribs ~ 0; 266 } 267 268 private int[] getCtxAttribs(in GlAttribs attribs) pure 269 { 270 import gfx.bindings.opengl.glx; 271 import gfx.gl3.context : GlProfile; 272 273 int[] ctxAttribs = [ 274 GLX_CONTEXT_MAJOR_VERSION_ARB, attribs.majorVersion, 275 GLX_CONTEXT_MINOR_VERSION_ARB, attribs.minorVersion 276 ]; 277 if (attribs.decimalVersion >= 32) { 278 ctxAttribs ~= GLX_CONTEXT_PROFILE_MASK_ARB; 279 if (attribs.profile == GlProfile.core) { 280 ctxAttribs ~= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; 281 } 282 else { 283 ctxAttribs ~= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 284 } 285 } 286 287 return ctxAttribs ~ 0; 288 }