1 module gfx.window.win32.context; 2 3 version(Windows): 4 5 import core.sys.windows.wingdi; 6 import gfx.bindings.opengl.gl : Gl, GL_TRUE; 7 import gfx.bindings.opengl.loader; 8 import gfx.bindings.opengl.wgl; 9 import gfx.gl3.context : GlAttribs, GlContext, GlProfile, glVersions; 10 import gfx.window.win32 : gfxW32Log; 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 { 89 _attribs = attribs; 90 91 registerWindowClass(); 92 93 auto dc = GetDC(window); 94 scope(exit) ReleaseDC(window, dc); 95 96 PIXELFORMATDESCRIPTOR pfd; 97 setupPFD(_attribs, &pfd); 98 const chosen = ChoosePixelFormat(dc, &pfd); 99 SetPixelFormat(dc, chosen, &pfd); 100 101 import gfx.bindings.opengl.loader : loadSharedSym; 102 auto lib = loadGlLib(); 103 PFN_wglGetProcAddress getProcAddress = cast(PFN_wglGetProcAddress)enforce( 104 loadSharedSym(lib, "wglGetProcAddress"), "could not load wglGetProcAddress" 105 ); 106 SharedSym loader (in string symbol) { 107 import std..string : toStringz; 108 auto proc = cast(SharedSym)getProcAddress(toStringz(symbol)); 109 if (!proc) { 110 proc = cast(SharedSym)loadSharedSym(lib, symbol); 111 } 112 import std.stdio; 113 //writefln("%s = %x", symbol, proc); 114 return proc; 115 } 116 117 { 118 PFN_wglCreateContext _createContext = cast(PFN_wglCreateContext)enforce( 119 loadSharedSym(lib, "wglCreateContext"), "could not load wglCreateContext" 120 ); 121 PFN_wglMakeCurrent _makeCurrent = cast(PFN_wglMakeCurrent)enforce( 122 loadSharedSym(lib, "wglMakeCurrent"), "could not load wglMakeCurrent" 123 ); 124 PFN_wglDeleteContext _deleteContext = cast(PFN_wglDeleteContext)enforce( 125 loadSharedSym(lib, "wglDeleteContext"), "could not load wglDeleteContext" 126 ); 127 // creating and binding a dummy temporary context 128 auto ctx = enforce(_createContext(dc), "could not create legacy wgl context"); 129 scope(exit) _deleteContext(ctx); 130 enforce(_makeCurrent(dc, ctx), "could not make legacy context current"); 131 _wgl = new Wgl(&loader); 132 } 133 134 auto attribList = getAttribList(attribs); 135 uint nf; 136 _wgl.ChoosePixelFormatARB(dc, attribList.ptr, null, 1, &_pixelFormat, &nf); 137 enforce(nf > 0, "wglChoosePixelFormatARB failed"); 138 139 GlAttribs attrs = attribs; 140 foreach (const glVer; glVersions) { 141 attrs.majorVersion = glVer / 10; 142 attrs.minorVersion = glVer % 10; 143 if (attrs.decimalVersion < attribs.decimalVersion) break; 144 145 const ctxAttribs = getCtxAttribs(attrs); 146 gfxW32Log.tracef("attempting to create OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 147 148 _ctx = _wgl.CreateContextAttribsARB(dc, null, &ctxAttribs[0]); 149 150 if (_ctx) break; 151 } 152 153 enforce(_ctx, "Failed creating Wgl context"); 154 gfxW32Log.tracef("created OpenGL %s.%s context", attrs.majorVersion, attrs.minorVersion); 155 _attribs = attrs; 156 Win32GlContext.makeCurrent(cast(size_t)window); 157 _gl = new Gl(&loader); 158 } 159 160 override void dispose() { 161 _wgl.DeleteContext(_ctx); 162 gfxW32Log.tracef("destroyed GL/WGL context"); 163 } 164 165 override @property Gl gl() { 166 return _gl; 167 } 168 169 override @property GlAttribs attribs() { 170 return _attribs; 171 } 172 173 void setPixelFormat(HWND hwnd) { 174 PIXELFORMATDESCRIPTOR pfd; 175 setupPFD(attribs, &pfd); 176 auto dc = GetDC(hwnd); 177 SetPixelFormat(dc, _pixelFormat, &pfd); 178 ReleaseDC(hwnd, dc); 179 } 180 181 override bool makeCurrent(size_t nativeHandle) 182 { 183 auto wnd = cast(HWND)nativeHandle; 184 auto dc = GetDC(wnd); 185 scope(exit) ReleaseDC(wnd, dc); 186 if (!_wgl.MakeCurrent(dc, _ctx)) { 187 printLastError(); 188 return false; 189 } 190 return true; 191 } 192 193 override void doneCurrent() 194 { 195 _wgl.MakeCurrent(null, null); 196 } 197 198 override @property bool current() const 199 { 200 return _wgl.GetCurrentContext() == _ctx; 201 } 202 203 override @property int swapInterval() 204 { 205 return _wgl.GetSwapIntervalEXT(); 206 } 207 208 override @property void swapInterval(int interval) 209 { 210 _wgl.SwapIntervalEXT(interval); 211 } 212 213 override void swapBuffers(size_t nativeHandle) 214 { 215 auto wnd = cast(HWND)nativeHandle; 216 auto dc = GetDC(wnd); 217 scope(exit) ReleaseDC(wnd, dc); 218 if (!SwapBuffers(dc)) { 219 printLastError(); 220 } 221 } 222 223 private void printLastError() { 224 const err = GetLastError(); 225 LPSTR messageBuffer = null; 226 size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, 227 null, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), cast(LPSTR)&messageBuffer, 0, null); 228 auto buf = new char[size]; 229 buf[] = messageBuffer[0 .. size]; 230 gfxW32Log.errorf(buf.idup); 231 LocalFree(messageBuffer); 232 } 233 234 } 235 236 int[] getCtxAttribs(in GlAttribs attribs) { 237 int[] ctxAttribs = [ 238 WGL_CONTEXT_MAJOR_VERSION_ARB, attribs.majorVersion, 239 WGL_CONTEXT_MINOR_VERSION_ARB, attribs.minorVersion 240 ]; 241 if (attribs.decimalVersion >= 32) { 242 ctxAttribs ~= WGL_CONTEXT_PROFILE_MASK_ARB; 243 if (attribs.profile == GlProfile.core) { 244 ctxAttribs ~= WGL_CONTEXT_CORE_PROFILE_BIT_ARB; 245 } 246 else { 247 ctxAttribs ~= WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB; 248 } 249 } 250 return ctxAttribs ~ 0; 251 } 252 253 private SharedLib loadGlLib() 254 { 255 import gfx.bindings.opengl.loader : openSharedLib; 256 257 immutable glLibNames = [ "opengl32.dll" ]; 258 259 foreach (ln; glLibNames) { 260 auto lib = openSharedLib(ln); 261 if (lib) { 262 gfxW32Log.tracef("opening shared library %s", ln); 263 return lib; 264 } 265 } 266 267 import std.conv : to; 268 throw new Exception("could not load any of these libraries: " ~ glLibNames.to!string); 269 }