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