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