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