1 module gfx.window.win32.context; 2 3 version(Windows): 4 5 import core.sys.windows.wingdi; 6 import gfx.bindings.core; 7 import gfx.bindings.opengl.gl : Gl, GL_TRUE; 8 import gfx.bindings.opengl.wgl; 9 import gfx.gl3.context : GlAttribs, GlContext, GlProfile, glVersions; 10 11 /// Helper to get an attrib list to pass to wglChoosePixelFormatARB 12 public int[] getAttribList(in GlAttribs attribs) { 13 import gfx.graal.format : formatDesc, colorBits, depthBits, stencilBits; 14 15 const cd = formatDesc(attribs.colorFormat); 16 const dsd = formatDesc(attribs.depthStencilFormat); 17 18 int[] attribList = [ 19 WGL_DRAW_TO_WINDOW_ARB, GL_TRUE, 20 WGL_SUPPORT_OPENGL_ARB, GL_TRUE, 21 WGL_DOUBLE_BUFFER_ARB, GL_TRUE, 22 WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB, 23 WGL_TRANSPARENT_ARB, GL_TRUE, 24 WGL_COLOR_BITS_ARB, colorBits(cd.surfaceType), 25 WGL_DEPTH_BITS_ARB, depthBits(dsd.surfaceType), 26 WGL_STENCIL_BITS_ARB, stencilBits(dsd.surfaceType), 27 ]; 28 if (attribs.samples > 1) { 29 attribList ~= [ 30 WGL_SAMPLE_BUFFERS_ARB, GL_TRUE, 31 WGL_SAMPLES_ARB, attribs.samples, 32 ]; 33 } 34 return attribList ~ 0; 35 } 36 37 /// Helper to fill a struct for ChoosePixelFormat 38 public void setupPFD(in GlAttribs attribs, PIXELFORMATDESCRIPTOR* pfd) 39 { 40 pfd.nSize = PIXELFORMATDESCRIPTOR.sizeof; 41 pfd.nVersion = 1; 42 43 pfd.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW; 44 if (attribs.doublebuffer) pfd.dwFlags |= PFD_DOUBLEBUFFER; 45 46 pfd.iPixelType = PFD_TYPE_RGBA; 47 48 import gfx.graal.format : formatDesc, alphaBits, redBits, greenBits, 49 blueBits, colorBits, alphaShift, redShift, 50 greenShift, blueShift, depthBits, stencilBits; 51 52 const cd = formatDesc(attribs.colorFormat); 53 const dsd = formatDesc(attribs.depthStencilFormat); 54 55 pfd.cColorBits = cast(ubyte)colorBits(cd.surfaceType); 56 pfd.cRedBits = cast(ubyte)redBits(cd.surfaceType); 57 pfd.cRedShift = cast(ubyte)redShift(cd.surfaceType); 58 pfd.cGreenBits = cast(ubyte)greenBits(cd.surfaceType); 59 pfd.cGreenShift = cast(ubyte)greenShift(cd.surfaceType); 60 pfd.cBlueBits = cast(ubyte)blueBits(cd.surfaceType); 61 pfd.cBlueShift = cast(ubyte)blueShift(cd.surfaceType); 62 pfd.cAlphaBits = cast(ubyte)alphaBits(cd.surfaceType); 63 pfd.cAlphaShift = cast(ubyte)alphaShift(cd.surfaceType); 64 pfd.cDepthBits = cast(ubyte)depthBits(dsd.surfaceType); 65 pfd.cStencilBits = cast(ubyte)stencilBits(dsd.surfaceType); 66 67 pfd.iLayerType = PFD_MAIN_PLANE; 68 } 69 70 /// Wgl implementation of GlContext 71 public class Win32GlContext : GlContext 72 { 73 import core.sys.windows.windows; 74 import gfx.core.rc : atomicRcCode, Disposable; 75 import gfx.window.win32 : registerWindowClass, wndClassName; 76 import std.exception : enforce; 77 import std.experimental.logger : tracef; 78 79 mixin(atomicRcCode); 80 81 private Wgl _wgl; 82 private Gl _gl; 83 private GlAttribs _attribs; 84 private HGLRC _ctx; 85 private int _pixelFormat; 86 private DummyWindow[size_t] dummies; 87 private size_t hiddenDummy; 88 89 this(in GlAttribs attribs) { 90 _attribs = attribs; 91 92 registerWindowClass(); 93 94 hiddenDummy = Win32GlContext.createDummy(); 95 96 auto dc = GetDC(cast(HWND)hiddenDummy); 97 scope(exit) ReleaseDC(cast(HWND)hiddenDummy, dc); 98 99 PIXELFORMATDESCRIPTOR pfd; 100 setupPFD(_attribs, &pfd); 101 const chosen = ChoosePixelFormat(dc, &pfd); 102 SetPixelFormat(dc, chosen, &pfd); 103 104 import gfx.bindings.core : loadSharedSym; 105 auto lib = loadGlLib(); 106 PFN_wglGetProcAddress getProcAddress = cast(PFN_wglGetProcAddress)enforce( 107 loadSharedSym(lib, "wglGetProcAddress"), "could not load wglGetProcAddress" 108 ); 109 SharedSym loader (in string symbol) { 110 import std.string : toStringz; 111 auto proc = cast(SharedSym)getProcAddress(toStringz(symbol)); 112 if (!proc) { 113 proc = cast(SharedSym)loadSharedSym(lib, symbol); 114 } 115 import std.stdio; 116 //writefln("%s = %x", symbol, proc); 117 return proc; 118 } 119 120 { 121 PFN_wglCreateContext _createContext = cast(PFN_wglCreateContext)enforce( 122 loadSharedSym(lib, "wglCreateContext"), "could not load wglCreateContext" 123 ); 124 PFN_wglMakeCurrent _makeCurrent = cast(PFN_wglMakeCurrent)enforce( 125 loadSharedSym(lib, "wglMakeCurrent"), "could not load wglMakeCurrent" 126 ); 127 PFN_wglDeleteContext _deleteContext = cast(PFN_wglDeleteContext)enforce( 128 loadSharedSym(lib, "wglDeleteContext"), "could not load wglDeleteContext" 129 ); 130 // creating and binding a dummy temporary context 131 auto ctx = enforce(_createContext(dc), "could not create legacy wgl context"); 132 scope(exit) _deleteContext(ctx); 133 enforce(_makeCurrent(dc, ctx), "could not make legacy context current"); 134 _wgl = new Wgl(&loader); 135 } 136 137 auto attribList = getAttribList(attribs); 138 uint nf; 139 _wgl.ChoosePixelFormatARB(dc, attribList.ptr, null, 1, &_pixelFormat, &nf); 140 enforce(nf > 0, "wglChoosePixelFormatARB failed"); 141 142 GlAttribs attrs = attribs; 143 foreach (const glVer; glVersions) { 144 attrs.majorVersion = glVer / 10; 145 attrs.minorVersion = glVer % 10; 146 if (attrs.decimalVersion < attribs.decimalVersion) break; 147 148 const ctxAttribs = getCtxAttribs(attrs); 149 tracef("attempting to create OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 150 151 _ctx = _wgl.CreateContextAttribsARB(dc, null, &ctxAttribs[0]); 152 153 if (_ctx) break; 154 } 155 156 enforce(_ctx, "Failed creating Wgl context"); 157 tracef("created OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 158 _attribs = attrs; 159 Win32GlContext.makeCurrent(hiddenDummy); 160 _gl = new Gl(&loader); 161 } 162 163 override void dispose() { 164 import gfx.core.rc : disposeArray; 165 166 disposeArray(dummies); 167 168 _wgl.DeleteContext(_ctx); 169 tracef("destroyed GL/WGL context"); 170 } 171 172 override @property Gl gl() { 173 return _gl; 174 } 175 176 override @property GlAttribs attribs() { 177 return _attribs; 178 } 179 180 void setPixelFormat(HWND hwnd) { 181 PIXELFORMATDESCRIPTOR pfd; 182 setupPFD(attribs, &pfd); 183 auto dc = GetDC(hwnd); 184 SetPixelFormat(dc, _pixelFormat, &pfd); 185 ReleaseDC(hwnd, dc); 186 } 187 188 override bool makeCurrent(size_t nativeHandle) 189 { 190 auto wnd = cast(HWND)nativeHandle; 191 auto dc = GetDC(wnd); 192 scope(exit) ReleaseDC(wnd, dc); 193 if (!_wgl.MakeCurrent(dc, _ctx)) { 194 printLastError(); 195 return false; 196 } 197 return true; 198 } 199 200 override void doneCurrent() 201 { 202 _wgl.MakeCurrent(null, null); 203 } 204 205 override @property bool current() const 206 { 207 return _wgl.GetCurrentContext() == _ctx; 208 } 209 210 override @property int swapInterval() 211 { 212 return _wgl.GetSwapIntervalEXT(); 213 } 214 215 override @property void swapInterval(int interval) 216 { 217 _wgl.SwapIntervalEXT(interval); 218 } 219 220 override void swapBuffers(size_t nativeHandle) 221 { 222 auto wnd = cast(HWND)nativeHandle; 223 auto dc = GetDC(wnd); 224 scope(exit) ReleaseDC(wnd, dc); 225 if (!SwapBuffers(dc)) { 226 printLastError(); 227 } 228 } 229 230 override size_t createDummy() { 231 if (hiddenDummy) { 232 const d = hiddenDummy; 233 hiddenDummy = 0; 234 return d; 235 } 236 auto dummy = new DummyWindow(); 237 const hdl = cast(size_t)dummy.hWnd; 238 dummies[hdl] = dummy; 239 return hdl; 240 } 241 242 override void releaseDummy(size_t dummy) { 243 auto d = dummy in dummies; 244 if (d) { 245 auto win = *d; 246 win.dispose(); 247 dummies.remove(dummy); 248 } 249 } 250 251 private void printLastError() { 252 import std.experimental.logger : errorf; 253 const err = GetLastError(); 254 LPSTR messageBuffer = null; 255 size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 256 null, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), cast(LPSTR)&messageBuffer, 0, null); 257 auto buf = new char[size]; 258 buf[] = messageBuffer[0 .. size]; 259 errorf(buf.idup); 260 LocalFree(messageBuffer); 261 } 262 263 private static class DummyWindow : Disposable 264 { 265 HWND hWnd; 266 267 this () { 268 import std.utf : toUTF16z; 269 270 hWnd = enforce( 271 CreateWindowEx( 272 WS_EX_CLIENTEDGE, 273 wndClassName.toUTF16z, 274 null, 275 WS_OVERLAPPEDWINDOW, 276 CW_USEDEFAULT, 277 CW_USEDEFAULT, 278 CW_USEDEFAULT, 279 CW_USEDEFAULT, 280 null, null, GetModuleHandle(null), null 281 ), 282 "could not create win32 dummy window" 283 ); 284 } 285 286 override void dispose() { 287 DestroyWindow(hWnd); 288 } 289 290 } 291 292 } 293 294 int[] getCtxAttribs(in GlAttribs attribs) { 295 int[] ctxAttribs = [ 296 WGL_CONTEXT_MAJOR_VERSION_ARB, attribs.majorVersion, 297 WGL_CONTEXT_MINOR_VERSION_ARB, attribs.minorVersion 298 ]; 299 if (attribs.decimalVersion >= 32) { 300 ctxAttribs ~= WGL_CONTEXT_PROFILE_MASK_ARB; 301 if (attribs.profile == GlProfile.core) { 302 ctxAttribs ~= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; 303 } 304 else { 305 ctxAttribs ~= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 306 } 307 } 308 return ctxAttribs ~ 0; 309 } 310 311 private SharedLib loadGlLib() 312 { 313 import gfx.bindings.core : openSharedLib; 314 315 immutable glLibNames = [ "opengl32.dll" ]; 316 317 foreach (ln; glLibNames) { 318 auto lib = openSharedLib(ln); 319 if (lib) { 320 import std.experimental.logger : tracef; 321 tracef("opening shared library %s", ln); 322 return lib; 323 } 324 } 325 326 import std.conv : to; 327 throw new Exception("could not load any of these libraries: " ~ glLibNames.to!string); 328 }