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