1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "GLContextProvider.h"
7 #include "GLContextWGL.h"
8 #include "GLLibraryLoader.h"
9 #include "nsDebug.h"
10 #include "nsIWidget.h"
11 #include "gfxPlatform.h"
12 #include "gfxWindowsSurface.h"
13 
14 #include "gfxCrashReporterUtils.h"
15 
16 #include "prenv.h"
17 
18 #include "mozilla/gfx/gfxVars.h"
19 #include "mozilla/Preferences.h"
20 #include "mozilla/ScopeExit.h"
21 #include "mozilla/StaticPtr.h"
22 #include "mozilla/layers/CompositorOptions.h"
23 #include "mozilla/widget/CompositorWidget.h"
24 #include "mozilla/widget/WinCompositorWidget.h"
25 
26 namespace mozilla {
27 namespace gl {
28 
29 using namespace mozilla::gfx;
30 using namespace mozilla::widget;
31 
32 WGLLibrary sWGLLib;
33 
34 /*
35 ScopedWindow::~ScopedWindow()
36 {
37     if (mDC) {
38         MOZ_ALWAYS_TRUE( ReleaseDC(mDC) );
39     }
40     if (mWindow) {
41         MOZ_ALWAYS_TRUE( DestroyWindow(mWindow) );
42     }
43 }
44 */
CreateDummyWindow()45 static HWND CreateDummyWindow() {
46   WNDCLASSW wc{};
47   if (!GetClassInfoW(GetModuleHandle(nullptr), L"GLContextWGLClass", &wc)) {
48     wc = {};
49     wc.style = CS_OWNDC;
50     wc.hInstance = GetModuleHandle(nullptr);
51     wc.lpfnWndProc = DefWindowProc;
52     wc.lpszClassName = L"GLContextWGLClass";
53     if (!RegisterClassW(&wc)) {
54       NS_WARNING("Failed to register GLContextWGLClass?!");
55       // er. failed to register our class?
56       return nullptr;
57     }
58   }
59 
60   return CreateWindowW(L"GLContextWGLClass", L"GLContextWGL", 0, 0, 0, 1, 1,
61                        nullptr, nullptr, GetModuleHandle(nullptr), nullptr);
62 }
63 
HasExtension(const char * aExtensions,const char * aRequiredExtension)64 static inline bool HasExtension(const char* aExtensions,
65                                 const char* aRequiredExtension) {
66   return GLContext::ListHasExtension(
67       reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
68 }
69 
GetSymbolLoader() const70 SymbolLoader WGLLibrary::GetSymbolLoader() const {
71   auto ret = SymbolLoader(*mOGLLibrary);
72   ret.mPfn = SymbolLoader::GetProcAddressT(mSymbols.fGetProcAddress);
73   return ret;
74 }
75 
EnsureInitialized()76 bool WGLLibrary::EnsureInitialized() {
77   if (mInitialized) return true;
78 
79   mozilla::ScopedGfxFeatureReporter reporter("WGL");
80 
81   std::wstring libGLFilename = L"Opengl32.dll";
82   // SU_SPIES_DIRECTORY is for AMD CodeXL/gDEBugger
83   if (_wgetenv(L"SU_SPIES_DIRECTORY")) {
84     libGLFilename =
85         std::wstring(_wgetenv(L"SU_SPIES_DIRECTORY")) + L"\\opengl32.dll";
86   }
87 
88   if (!mOGLLibrary) {
89     mOGLLibrary = LoadLibraryWithFlags(libGLFilename.c_str());
90     if (!mOGLLibrary) {
91       NS_WARNING("Couldn't load OpenGL library.");
92       return false;
93     }
94   }
95 
96 #define SYMBOL(X)                 \
97   {                               \
98     (PRFuncPtr*)&mSymbols.f##X, { \
99       { "wgl" #X }                \
100     }                             \
101   }
102 #define END_OF_SYMBOLS {nullptr, {}}
103 
104   {
105     const auto loader = SymbolLoader(*mOGLLibrary);
106     const SymLoadStruct earlySymbols[] = {SYMBOL(CreateContext),
107                                           SYMBOL(MakeCurrent),
108                                           SYMBOL(GetProcAddress),
109                                           SYMBOL(DeleteContext),
110                                           SYMBOL(GetCurrentContext),
111                                           SYMBOL(GetCurrentDC),
112                                           END_OF_SYMBOLS};
113 
114     if (!loader.LoadSymbols(earlySymbols)) {
115       NS_WARNING(
116           "Couldn't find required entry points in OpenGL DLL (early init)");
117       return false;
118     }
119   }
120 
121   mDummyWindow = CreateDummyWindow();
122   MOZ_ASSERT(mDummyWindow);
123   if (!mDummyWindow) return false;
124   auto cleanup = MakeScopeExit([&]() { Reset(); });
125 
126   mRootDc = GetDC(mDummyWindow);
127   MOZ_ASSERT(mRootDc);
128   if (!mRootDc) return false;
129 
130   // --
131 
132   {
133     PIXELFORMATDESCRIPTOR pfd{};
134     pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
135     pfd.nVersion = 1;
136     pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL;
137     // pfd.iPixelType = PFD_TYPE_RGBA;
138     // pfd.cColorBits = 24;
139     // pfd.cRedBits = 8;
140     // pfd.cGreenBits = 8;
141     // pfd.cBlueBits = 8;
142     // pfd.cAlphaBits = 8;
143     pfd.iLayerType = PFD_MAIN_PLANE;
144 
145     const auto pixelFormat = ChoosePixelFormat(mRootDc, &pfd);
146     MOZ_ASSERT(pixelFormat);
147     if (!pixelFormat) return false;
148     const bool setPixelFormatOk = SetPixelFormat(mRootDc, pixelFormat, nullptr);
149     MOZ_ASSERT(setPixelFormatOk);
150     if (!setPixelFormatOk) return false;
151   }
152 
153   // --
154 
155   // create rendering context
156   mDummyGlrc = mSymbols.fCreateContext(mRootDc);
157   if (!mDummyGlrc) return false;
158 
159   const auto curCtx = mSymbols.fGetCurrentContext();
160   const auto curDC = mSymbols.fGetCurrentDC();
161 
162   if (!mSymbols.fMakeCurrent(mRootDc, mDummyGlrc)) {
163     NS_WARNING("wglMakeCurrent failed");
164     return false;
165   }
166   const auto resetContext =
167       MakeScopeExit([&]() { mSymbols.fMakeCurrent(curDC, curCtx); });
168 
169   const auto loader = GetSymbolLoader();
170 
171   // Now we can grab all the other symbols that we couldn't without having
172   // a context current.
173   // clang-format off
174     const SymLoadStruct reqExtSymbols[] = {
175         { (PRFuncPtr*)&mSymbols.fCreatePbuffer, {{ "wglCreatePbufferARB", "wglCreatePbufferEXT" }} },
176         { (PRFuncPtr*)&mSymbols.fDestroyPbuffer, {{ "wglDestroyPbufferARB", "wglDestroyPbufferEXT" }} },
177         { (PRFuncPtr*)&mSymbols.fGetPbufferDC, {{ "wglGetPbufferDCARB", "wglGetPbufferDCEXT" }} },
178         { (PRFuncPtr*)&mSymbols.fReleasePbufferDC, {{ "wglReleasePbufferDCARB", "wglReleasePbufferDCEXT" }} },
179     //    { (PRFuncPtr*)&mSymbols.fBindTexImage, {{ "wglBindTexImageARB", "wglBindTexImageEXT" }} },
180     //    { (PRFuncPtr*)&mSymbols.fReleaseTexImage, {{ "wglReleaseTexImageARB", "wglReleaseTexImageEXT" }} },
181         { (PRFuncPtr*)&mSymbols.fChoosePixelFormat, {{ "wglChoosePixelFormatARB", "wglChoosePixelFormatEXT" }} },
182     //    { (PRFuncPtr*)&mSymbols.fGetPixelFormatAttribiv, {{ "wglGetPixelFormatAttribivARB", "wglGetPixelFormatAttribivEXT" }} },
183         SYMBOL(GetExtensionsStringARB),
184         END_OF_SYMBOLS
185     };
186   // clang-format on
187   if (!loader.LoadSymbols(reqExtSymbols)) {
188     NS_WARNING("reqExtSymbols missing");
189     return false;
190   }
191 
192   // --
193 
194   const auto extString = mSymbols.fGetExtensionsStringARB(mRootDc);
195   MOZ_ASSERT(extString);
196   MOZ_ASSERT(HasExtension(extString, "WGL_ARB_extensions_string"));
197 
198   // --
199 
200   if (HasExtension(extString, "WGL_ARB_create_context")) {
201     const SymLoadStruct createContextSymbols[] = {
202         SYMBOL(CreateContextAttribsARB), END_OF_SYMBOLS};
203     if (loader.LoadSymbols(createContextSymbols)) {
204       if (HasExtension(extString, "WGL_ARB_create_context_robustness")) {
205         mHasRobustness = true;
206       }
207     } else {
208       NS_ERROR(
209           "WGL_ARB_create_context announced without supplying its functions.");
210       ClearSymbols(createContextSymbols);
211     }
212   }
213 
214   // --
215 
216   bool hasDXInterop2 = HasExtension(extString, "WGL_NV_DX_interop2");
217   if (gfxVars::DXInterop2Blocked() &&
218       !StaticPrefs::gl_ignore_dx_interop2_blacklist()) {
219     hasDXInterop2 = false;
220   }
221 
222   if (hasDXInterop2) {
223     const SymLoadStruct dxInteropSymbols[] = {
224         SYMBOL(DXSetResourceShareHandleNV),
225         SYMBOL(DXOpenDeviceNV),
226         SYMBOL(DXCloseDeviceNV),
227         SYMBOL(DXRegisterObjectNV),
228         SYMBOL(DXUnregisterObjectNV),
229         SYMBOL(DXObjectAccessNV),
230         SYMBOL(DXLockObjectsNV),
231         SYMBOL(DXUnlockObjectsNV),
232         END_OF_SYMBOLS};
233     if (!loader.LoadSymbols(dxInteropSymbols)) {
234       NS_ERROR(
235           "WGL_NV_DX_interop2 announceed without supplying its functions.");
236       ClearSymbols(dxInteropSymbols);
237     }
238   }
239 
240   // --
241 
242   cleanup.release();
243 
244   mInitialized = true;
245 
246   reporter.SetSuccessful();
247   return true;
248 }
249 
250 #undef SYMBOL
251 #undef END_OF_SYMBOLS
252 
Reset()253 void WGLLibrary::Reset() {
254   if (mDummyGlrc) {
255     (void)mSymbols.fDeleteContext(mDummyGlrc);
256     mDummyGlrc = nullptr;
257   }
258   if (mRootDc) {
259     (void)ReleaseDC(mDummyWindow, mRootDc);
260     mRootDc = nullptr;
261   }
262   if (mDummyWindow) {
263     (void)DestroyWindow(mDummyWindow);
264     mDummyWindow = nullptr;
265   }
266 }
267 
GLContextWGL(const GLContextDesc & desc,HDC aDC,HGLRC aContext,HWND aWindow)268 GLContextWGL::GLContextWGL(const GLContextDesc& desc, HDC aDC, HGLRC aContext,
269                            HWND aWindow)
270     : GLContext(desc, nullptr, false),
271       mDC(aDC),
272       mContext(aContext),
273       mWnd(aWindow),
274       mPBuffer(nullptr),
275       mPixelFormat(0) {}
276 
GLContextWGL(const GLContextDesc & desc,HANDLE aPbuffer,HDC aDC,HGLRC aContext,int aPixelFormat)277 GLContextWGL::GLContextWGL(const GLContextDesc& desc, HANDLE aPbuffer, HDC aDC,
278                            HGLRC aContext, int aPixelFormat)
279     : GLContext(desc, nullptr, false),
280       mDC(aDC),
281       mContext(aContext),
282       mWnd(nullptr),
283       mPBuffer(aPbuffer),
284       mPixelFormat(aPixelFormat) {}
285 
~GLContextWGL()286 GLContextWGL::~GLContextWGL() {
287   MarkDestroyed();
288 
289   (void)sWGLLib.mSymbols.fDeleteContext(mContext);
290 
291   if (mPBuffer) {
292     (void)sWGLLib.mSymbols.fReleasePbufferDC(mPBuffer, mDC);
293     (void)sWGLLib.mSymbols.fDestroyPbuffer(mPBuffer);
294   }
295   if (mWnd) {
296     (void)ReleaseDC(mWnd, mDC);
297     DestroyWindow(mWnd);
298   }
299 }
300 
MakeCurrentImpl() const301 bool GLContextWGL::MakeCurrentImpl() const {
302   const bool succeeded = sWGLLib.mSymbols.fMakeCurrent(mDC, mContext);
303   NS_ASSERTION(succeeded, "Failed to make GL context current!");
304   return succeeded;
305 }
306 
IsCurrentImpl() const307 bool GLContextWGL::IsCurrentImpl() const {
308   return sWGLLib.mSymbols.fGetCurrentContext() == mContext;
309 }
310 
SwapBuffers()311 bool GLContextWGL::SwapBuffers() {
312   if (!mIsDoubleBuffered) return false;
313   return ::SwapBuffers(mDC);
314 }
315 
GetWSIInfo(nsCString * const out) const316 void GLContextWGL::GetWSIInfo(nsCString* const out) const {
317   out->AppendLiteral("wglGetExtensionsString: ");
318   out->Append(sWGLLib.mSymbols.fGetExtensionsStringARB(mDC));
319 }
320 
321 HGLRC
CreateContextWithFallback(const HDC dc,const bool tryRobustBuffers) const322 WGLLibrary::CreateContextWithFallback(const HDC dc,
323                                       const bool tryRobustBuffers) const {
324   if (mHasRobustness) {
325     if (tryRobustBuffers) {
326       const int attribs[] = {LOCAL_WGL_CONTEXT_FLAGS_ARB,
327                              LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB,
328                              LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
329                              LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB, 0};
330       const auto context =
331           mSymbols.fCreateContextAttribsARB(dc, nullptr, attribs);
332       if (context) return context;
333     }
334 
335     const int attribs[] = {LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB,
336                            LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB, 0};
337     const auto context =
338         mSymbols.fCreateContextAttribsARB(dc, nullptr, attribs);
339     if (context) return context;
340   }
341   if (mSymbols.fCreateContextAttribsARB) {
342     const auto context =
343         mSymbols.fCreateContextAttribsARB(dc, nullptr, nullptr);
344     if (context) return context;
345   }
346   return mSymbols.fCreateContext(dc);
347 }
348 
CreateForWidget(const HWND window,const bool isWebRender,const bool requireAccelerated)349 static RefPtr<GLContext> CreateForWidget(const HWND window,
350                                          const bool isWebRender,
351                                          const bool requireAccelerated) {
352   auto& wgl = sWGLLib;
353   if (!wgl.EnsureInitialized()) return nullptr;
354 
355   const auto dc = GetDC(window);
356   if (!dc) return nullptr;
357   auto cleanupDc = MakeScopeExit([&]() { (void)ReleaseDC(window, dc); });
358 
359   int chosenFormat;
360   UINT foundFormats = 0;
361 
362   if (!foundFormats) {
363     const int kAttribs[] = {LOCAL_WGL_DRAW_TO_WINDOW_ARB,
364                             true,
365                             LOCAL_WGL_SUPPORT_OPENGL_ARB,
366                             true,
367                             LOCAL_WGL_DOUBLE_BUFFER_ARB,
368                             true,
369                             LOCAL_WGL_ACCELERATION_ARB,
370                             LOCAL_WGL_FULL_ACCELERATION_ARB,
371                             0};
372     const int kAttribsForWebRender[] = {LOCAL_WGL_DRAW_TO_WINDOW_ARB,
373                                         true,
374                                         LOCAL_WGL_SUPPORT_OPENGL_ARB,
375                                         true,
376                                         LOCAL_WGL_DOUBLE_BUFFER_ARB,
377                                         true,
378                                         LOCAL_WGL_DEPTH_BITS_ARB,
379                                         24,
380                                         LOCAL_WGL_ACCELERATION_ARB,
381                                         LOCAL_WGL_FULL_ACCELERATION_ARB,
382                                         0};
383     const int* attribs;
384     if (isWebRender) {
385       attribs = kAttribsForWebRender;
386     } else {
387       attribs = kAttribs;
388     }
389 
390     if (!wgl.mSymbols.fChoosePixelFormat(wgl.RootDc(), attribs, nullptr, 1,
391                                          &chosenFormat, &foundFormats)) {
392       foundFormats = 0;
393     }
394   }
395   if (!foundFormats) {
396     if (requireAccelerated) return nullptr;
397 
398     const int kAttribs[] = {LOCAL_WGL_DRAW_TO_WINDOW_ARB,
399                             true,
400                             LOCAL_WGL_SUPPORT_OPENGL_ARB,
401                             true,
402                             LOCAL_WGL_DOUBLE_BUFFER_ARB,
403                             true,
404                             0};
405     const int kAttribsForWebRender[] = {LOCAL_WGL_DRAW_TO_WINDOW_ARB,
406                                         true,
407                                         LOCAL_WGL_SUPPORT_OPENGL_ARB,
408                                         true,
409                                         LOCAL_WGL_DOUBLE_BUFFER_ARB,
410                                         true,
411                                         LOCAL_WGL_DEPTH_BITS_ARB,
412                                         24,
413                                         0};
414 
415     const int* attribs;
416     if (isWebRender) {
417       attribs = kAttribsForWebRender;
418     } else {
419       attribs = kAttribs;
420     }
421 
422     if (!wgl.mSymbols.fChoosePixelFormat(wgl.RootDc(), attribs, nullptr, 1,
423                                          &chosenFormat, &foundFormats)) {
424       foundFormats = 0;
425     }
426   }
427   if (!foundFormats) return nullptr;
428 
429   // We need to make sure we call SetPixelFormat -after- calling
430   // EnsureInitialized, otherwise it can load/unload the dll and
431   // wglCreateContext will fail.
432 
433   SetPixelFormat(dc, chosenFormat, nullptr);
434   const auto context = sWGLLib.CreateContextWithFallback(dc, false);
435   if (!context) return nullptr;
436 
437   const RefPtr<GLContextWGL> gl = new GLContextWGL({}, dc, context);
438   cleanupDc.release();
439   gl->mIsDoubleBuffered = true;
440   if (!gl->Init()) return nullptr;
441 
442   return gl;
443 }
444 
CreateForCompositorWidget(CompositorWidget * aCompositorWidget,bool aHardwareWebRender,bool aForceAccelerated)445 already_AddRefed<GLContext> GLContextProviderWGL::CreateForCompositorWidget(
446     CompositorWidget* aCompositorWidget, bool aHardwareWebRender,
447     bool aForceAccelerated) {
448   if (!aCompositorWidget) {
449     MOZ_ASSERT(false);
450     return nullptr;
451   }
452   return CreateForWidget(aCompositorWidget->AsWindows()->GetHwnd(),
453                          aHardwareWebRender, aForceAccelerated)
454       .forget();
455 }
456 
457 /*static*/
CreateHeadless(const GLContextCreateDesc & desc,nsACString * const out_failureId)458 already_AddRefed<GLContext> GLContextProviderWGL::CreateHeadless(
459     const GLContextCreateDesc& desc, nsACString* const out_failureId) {
460   auto& wgl = sWGLLib;
461   if (!wgl.EnsureInitialized()) return nullptr;
462 
463   int chosenFormat;
464   UINT foundFormats = 0;
465 
466   if (!foundFormats) {
467     const int kAttribs[] = {LOCAL_WGL_DRAW_TO_PBUFFER_ARB,
468                             true,
469                             LOCAL_WGL_SUPPORT_OPENGL_ARB,
470                             true,
471                             LOCAL_WGL_ACCELERATION_ARB,
472                             LOCAL_WGL_FULL_ACCELERATION_ARB,
473                             0};
474     if (!wgl.mSymbols.fChoosePixelFormat(wgl.RootDc(), kAttribs, nullptr, 1,
475                                          &chosenFormat, &foundFormats)) {
476       foundFormats = 0;
477     }
478   }
479   if (!foundFormats) {
480     const int kAttribs[] = {LOCAL_WGL_DRAW_TO_PBUFFER_ARB, true,
481                             LOCAL_WGL_SUPPORT_OPENGL_ARB, true, 0};
482     if (!wgl.mSymbols.fChoosePixelFormat(wgl.RootDc(), kAttribs, nullptr, 1,
483                                          &chosenFormat, &foundFormats)) {
484       foundFormats = 0;
485     }
486   }
487   if (!foundFormats) return nullptr;
488   const int kPbufferAttribs[] = {0};
489   const auto pbuffer = wgl.mSymbols.fCreatePbuffer(wgl.RootDc(), chosenFormat,
490                                                    1, 1, kPbufferAttribs);
491   if (!pbuffer) return nullptr;
492   auto cleanupPbuffer =
493       MakeScopeExit([&]() { (void)wgl.mSymbols.fDestroyPbuffer(pbuffer); });
494 
495   const auto dc = wgl.mSymbols.fGetPbufferDC(pbuffer);
496   if (!dc) return nullptr;
497   auto cleanupDc = MakeScopeExit(
498       [&]() { (void)wgl.mSymbols.fReleasePbufferDC(pbuffer, dc); });
499 
500   const auto context = wgl.CreateContextWithFallback(dc, true);
501   if (!context) return nullptr;
502 
503   const auto fullDesc = GLContextDesc{desc, true};
504   const RefPtr<GLContextWGL> gl =
505       new GLContextWGL(fullDesc, pbuffer, dc, context, chosenFormat);
506   cleanupPbuffer.release();
507   cleanupDc.release();
508   if (!gl->Init()) return nullptr;
509 
510   return RefPtr<GLContext>(gl.get()).forget();
511 }
512 
513 /*static*/
GetGlobalContext()514 GLContext* GLContextProviderWGL::GetGlobalContext() { return nullptr; }
515 
516 /*static*/
Shutdown()517 void GLContextProviderWGL::Shutdown() {}
518 
519 } /* namespace gl */
520 } /* namespace mozilla */
521