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 }