1 module gfx.window.xcb.context; 2 3 version(linux): 4 5 import gfx.bindings.core : SharedLib; 6 import gfx.gl3.context : GlAttribs, GlContext, glVersions; 7 import X11.Xlib : XDisplay = Display, XErrorEvent; 8 9 /// GlX backed OpenGL context 10 class XcbGlContext : GlContext 11 { 12 import gfx.bindings.core : SharedSym; 13 import gfx.bindings.opengl.gl : Gl; 14 import gfx.bindings.opengl.glx : Glx, GLXContext, GLXFBConfig; 15 import gfx.core.rc : atomicRcCode, Disposable; 16 17 mixin(atomicRcCode); 18 19 private XDisplay* _dpy; 20 private int _mainScreenNum; 21 private GlAttribs _attribs; 22 private Glx _glx; 23 private Gl _gl; 24 private string[] _glxAvailExts; 25 private string[] _glAvailExts; 26 private DummyWindow[size_t] dummies; 27 private size_t hiddenDummy; 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 /// It internally creates a dummy window 36 this (XDisplay* dpy, in int mainScreenNum, in GlAttribs attribs) 37 { 38 import gfx.bindings.core : openSharedLib, loadSharedSym, SharedLib; 39 import gfx.bindings.opengl : splitExtString; 40 import gfx.bindings.opengl.gl : GL_EXTENSIONS; 41 import gfx.bindings.opengl.glx : PFN_glXGetProcAddressARB; 42 import std.algorithm : canFind; 43 import std.exception : enforce; 44 import std.experimental.logger : trace, tracef; 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 hiddenDummy = XcbGlContext.createDummy(); 70 GlAttribs attrs = attribs; 71 72 auto oldHandler = XSetErrorHandler(&createCtxErrorHandler); 73 74 foreach (const glVer; glVersions) { 75 attrs.majorVersion = glVer / 10; 76 attrs.minorVersion = glVer % 10; 77 if (attrs.decimalVersion < attribs.decimalVersion) break; 78 79 const ctxAttribs = getCtxAttribs(attrs); 80 tracef("attempting to create OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 81 82 createContextErrorFlag = false; 83 _ctx = _glx.CreateContextAttribsARB(_dpy, fbc, null, 1, &ctxAttribs[0]); 84 85 if (_ctx && !createContextErrorFlag) break; 86 } 87 88 XSetErrorHandler(oldHandler); 89 90 enforce(_ctx); 91 XSync(_dpy, 0); 92 _attribs = attrs; 93 94 tracef("created OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 95 96 XcbGlContext.makeCurrent(hiddenDummy); 97 _gl = new Gl(&loadSymbol); 98 99 trace("done loading GL/GLX"); 100 } 101 102 override void dispose() { 103 import gfx.bindings.core : closeSharedLib; 104 import gfx.core.rc : disposeArray; 105 import std.experimental.logger : trace; 106 107 disposeArray(dummies); 108 109 _glx.DestroyContext(_dpy, _ctx); 110 trace("destroyed GL/GLX context"); 111 } 112 113 114 override @property Gl gl() { 115 return _gl; 116 } 117 118 override @property GlAttribs attribs() { 119 return _attribs; 120 } 121 122 override bool makeCurrent(size_t nativeHandle) 123 { 124 import gfx.bindings.opengl.glx : GLXDrawable; 125 return _glx.MakeCurrent(_dpy, cast(GLXDrawable)nativeHandle, _ctx) != 0; 126 } 127 128 override void doneCurrent() 129 { 130 _glx.MakeCurrent(_dpy, 0, null); 131 } 132 133 override @property bool current() const 134 { 135 return _glx.GetCurrentContext() is _ctx; 136 } 137 138 override @property int swapInterval() 139 { 140 import gfx.bindings.opengl.glx : GLXDrawable; 141 if (MESA_swap_control) { 142 return _glx.GetSwapIntervalMESA(); 143 } 144 else if (EXT_swap_control) { 145 GLXDrawable drawable = _glx.GetCurrentDrawable(); 146 uint swap; 147 148 if (drawable) { 149 import gfx.bindings.opengl.glx : GLX_SWAP_INTERVAL_EXT; 150 _glx.QueryDrawable(_dpy, drawable, GLX_SWAP_INTERVAL_EXT, &swap); 151 return swap; 152 } 153 else { 154 import std.experimental.logger : warningf; 155 warningf("could not get glx drawable to get swap interval"); 156 return -1; 157 } 158 159 } 160 return -1; 161 } 162 163 override @property void swapInterval(int interval) 164 { 165 import gfx.bindings.opengl.glx : GLXDrawable; 166 167 if (MESA_swap_control) { 168 _glx.SwapIntervalMESA(interval); 169 } 170 else if (EXT_swap_control) { 171 GLXDrawable drawable = _glx.GetCurrentDrawable(); 172 173 if (drawable) { 174 _glx.SwapIntervalEXT(_dpy, drawable, interval); 175 } 176 else { 177 import std.experimental.logger : warningf; 178 warningf("could not get glx drawable to set swap interval"); 179 } 180 } 181 } 182 183 override void swapBuffers(size_t nativeHandle) 184 { 185 import gfx.bindings.opengl.glx : GLXDrawable; 186 _glx.SwapBuffers(_dpy, cast(GLXDrawable)nativeHandle); 187 } 188 189 private GLXFBConfig getGlxFBConfig(in GlAttribs attribs) 190 { 191 import X11.Xlib : XFree; 192 193 const glxAttribs = getGlxAttribs(attribs); 194 195 int numConfigs; 196 auto fbConfigs = _glx.ChooseFBConfig(_dpy, _mainScreenNum, &glxAttribs[0], &numConfigs); 197 198 if (!fbConfigs || !numConfigs) 199 { 200 import std.experimental.logger : critical; 201 critical("GFX-GLX: could not get fb config"); 202 return null; 203 } 204 scope (exit) XFree(fbConfigs); 205 206 return fbConfigs[0]; 207 } 208 209 override size_t createDummy() { 210 if (hiddenDummy) { 211 const d = hiddenDummy; 212 hiddenDummy = 0; 213 return d; 214 } 215 auto dummy = new DummyWindow(_dpy, _glx, getGlxFBConfig(_attribs)); 216 size_t hdl = dummy.win; 217 dummies[hdl] = dummy; 218 return hdl; 219 } 220 221 override void releaseDummy(size_t dummy) { 222 auto d = dummy in dummies; 223 if (d) { 224 auto win = *d; 225 win.dispose(); 226 dummies.remove(dummy); 227 } 228 } 229 230 private static class DummyWindow : Disposable 231 { 232 import X11.Xlib : XWindow = Window, XColormap = Colormap; 233 import X11.Xlib; 234 235 XWindow win; 236 XColormap cmap; 237 XDisplay* dpy; 238 239 this (XDisplay* dpy, Glx glx, GLXFBConfig fbc) 240 { 241 this.dpy = dpy; 242 auto vi = glx.GetVisualFromFBConfig( dpy, fbc ); 243 scope(exit) { 244 XFree(vi); 245 } 246 247 cmap = XCreateColormap(dpy, XRootWindow(dpy, XDefaultScreen(dpy)), 248 vi.visual, AllocNone ); 249 250 XSetWindowAttributes swa; 251 swa.colormap = cmap; 252 swa.background_pixmap = None ; 253 swa.border_pixel = 0; 254 swa.event_mask = StructureNotifyMask; 255 256 win = XCreateWindow(dpy, XRootWindow(dpy, vi.screen), 257 0, 0, 100, 100, 0, vi.depth, InputOutput, vi.visual, 258 CWBorderPixel|CWColormap|CWEventMask, &swa); 259 } 260 261 override void dispose() 262 { 263 XDestroyWindow(dpy, win); 264 XFreeColormap(dpy, cmap); 265 } 266 } 267 } 268 269 270 private SharedLib loadGlLib() 271 { 272 import gfx.bindings.core : openSharedLib; 273 274 immutable glLibNames = ["libGL.so.1", "libGL.so"]; 275 276 foreach (ln; glLibNames) { 277 auto lib = openSharedLib(ln); 278 if (lib) { 279 import std.experimental.logger : tracef; 280 tracef("opening shared library %s", ln); 281 return lib; 282 } 283 } 284 285 import std.conv : to; 286 throw new Exception("could not load any of these libraries: " ~ glLibNames.to!string); 287 } 288 289 private bool createContextErrorFlag; 290 291 extern(C) private int createCtxErrorHandler(XDisplay *dpy, XErrorEvent *error) 292 { 293 createContextErrorFlag = true; 294 return 0; 295 } 296 297 private int[] getGlxAttribs(in GlAttribs attribs) pure 298 { 299 import gfx.bindings.opengl.glx; 300 import gfx.graal.format : formatDesc, redBits, greenBits, blueBits, 301 alphaBits, depthBits, stencilBits; 302 303 int[] glxAttribs = [ 304 GLX_X_RENDERABLE, 1, 305 GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, 306 GLX_RENDER_TYPE, GLX_RGBA_BIT, 307 GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR 308 ]; 309 310 const colorDesc = formatDesc(attribs.colorFormat); 311 const depthStencilDesc = formatDesc(attribs.depthStencilFormat); 312 313 const r = redBits(colorDesc.surfaceType); 314 const g = greenBits(colorDesc.surfaceType); 315 const b = blueBits(colorDesc.surfaceType); 316 const a = alphaBits(colorDesc.surfaceType); 317 const d = depthBits(depthStencilDesc.surfaceType); 318 const s = stencilBits(depthStencilDesc.surfaceType); 319 320 if (r) glxAttribs ~= [GLX_RED_SIZE, r]; 321 if (g) glxAttribs ~= [GLX_GREEN_SIZE, g]; 322 if (b) glxAttribs ~= [GLX_BLUE_SIZE, b]; 323 if (a) glxAttribs ~= [GLX_ALPHA_SIZE, a]; 324 if (d) glxAttribs ~= [GLX_DEPTH_SIZE, d]; 325 if (s) glxAttribs ~= [GLX_STENCIL_SIZE, s]; 326 327 if (attribs.doublebuffer) glxAttribs ~= [GLX_DOUBLEBUFFER, 1]; 328 329 if (attribs.samples > 1) 330 glxAttribs ~= [GLX_SAMPLE_BUFFERS, 1, GLX_SAMPLES, attribs.samples]; 331 332 return glxAttribs ~ 0; 333 } 334 335 private int[] getCtxAttribs(in GlAttribs attribs) pure 336 { 337 import gfx.bindings.opengl.glx; 338 import gfx.gl3.context : GlProfile; 339 340 int[] ctxAttribs = [ 341 GLX_CONTEXT_MAJOR_VERSION_ARB, attribs.majorVersion, 342 GLX_CONTEXT_MINOR_VERSION_ARB, attribs.minorVersion 343 ]; 344 if (attribs.decimalVersion >= 32) { 345 ctxAttribs ~= GLX_CONTEXT_PROFILE_MASK_ARB; 346 if (attribs.profile == GlProfile.core) { 347 ctxAttribs ~= GLX_CONTEXT_CORE_PROFILE_BIT_ARB; 348 } 349 else { 350 ctxAttribs ~= GLX_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 351 } 352 } 353 354 return ctxAttribs ~ 0; 355 }