diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /gfx/gl/GLContextProviderWGL.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'gfx/gl/GLContextProviderWGL.cpp')
-rw-r--r-- | gfx/gl/GLContextProviderWGL.cpp | 651 |
1 files changed, 651 insertions, 0 deletions
diff --git a/gfx/gl/GLContextProviderWGL.cpp b/gfx/gl/GLContextProviderWGL.cpp new file mode 100644 index 000000000..a1373f78f --- /dev/null +++ b/gfx/gl/GLContextProviderWGL.cpp @@ -0,0 +1,651 @@ +/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GLContextProvider.h" +#include "GLContextWGL.h" +#include "GLLibraryLoader.h" +#include "nsDebug.h" +#include "nsIWidget.h" +#include "gfxPlatform.h" +#include "gfxWindowsSurface.h" + +#include "gfxCrashReporterUtils.h" + +#include "prenv.h" + +#include "mozilla/Preferences.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/widget/CompositorWidget.h" + +namespace mozilla { +namespace gl { + +using namespace mozilla::gfx; +using namespace mozilla::widget; + +WGLLibrary sWGLLib; + +HWND +WGLLibrary::CreateDummyWindow(HDC* aWindowDC) +{ + WNDCLASSW wc; + if (!GetClassInfoW(GetModuleHandle(nullptr), L"GLContextWGLClass", &wc)) { + ZeroMemory(&wc, sizeof(WNDCLASSW)); + wc.style = CS_OWNDC; + wc.hInstance = GetModuleHandle(nullptr); + wc.lpfnWndProc = DefWindowProc; + wc.lpszClassName = L"GLContextWGLClass"; + if (!RegisterClassW(&wc)) { + NS_WARNING("Failed to register GLContextWGLClass?!"); + // er. failed to register our class? + return nullptr; + } + } + + HWND win = CreateWindowW(L"GLContextWGLClass", L"GLContextWGL", 0, + 0, 0, 16, 16, + nullptr, nullptr, GetModuleHandle(nullptr), + nullptr); + NS_ENSURE_TRUE(win, nullptr); + + HDC dc = GetDC(win); + NS_ENSURE_TRUE(dc, nullptr); + + if (mWindowPixelFormat == 0) { + PIXELFORMATDESCRIPTOR pfd; + ZeroMemory(&pfd, sizeof(PIXELFORMATDESCRIPTOR)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; + pfd.iPixelType = PFD_TYPE_RGBA; + pfd.cColorBits = 24; + pfd.cRedBits = 8; + pfd.cGreenBits = 8; + pfd.cBlueBits = 8; + pfd.cAlphaBits = 8; + pfd.cDepthBits = 0; + pfd.iLayerType = PFD_MAIN_PLANE; + + mWindowPixelFormat = ChoosePixelFormat(dc, &pfd); + } + + if (!mWindowPixelFormat || + !SetPixelFormat(dc, mWindowPixelFormat, nullptr)) + { + NS_WARNING("SetPixelFormat failed!"); + DestroyWindow(win); + return nullptr; + } + + if (aWindowDC) { + *aWindowDC = dc; + } + + return win; +} + +static inline bool +HasExtension(const char* aExtensions, const char* aRequiredExtension) +{ + return GLContext::ListHasExtension( + reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension); +} + +bool +WGLLibrary::EnsureInitialized() +{ + if (mInitialized) + return true; + + mozilla::ScopedGfxFeatureReporter reporter("WGL"); + + std::string libGLFilename = "Opengl32.dll"; + // SU_SPIES_DIRECTORY is for AMD CodeXL/gDEBugger + if (PR_GetEnv("SU_SPIES_DIRECTORY")) { + libGLFilename = std::string(PR_GetEnv("SU_SPIES_DIRECTORY")) + "\\opengl32.dll"; + } + + if (!mOGLLibrary) { + mOGLLibrary = PR_LoadLibrary(libGLFilename.c_str()); + if (!mOGLLibrary) { + NS_WARNING("Couldn't load OpenGL library."); + return false; + } + } + + const GLLibraryLoader::SymLoadStruct earlySymbols[] = { + { (PRFuncPtr*) &fCreateContext, { "wglCreateContext", nullptr } }, + { (PRFuncPtr*) &fMakeCurrent, { "wglMakeCurrent", nullptr } }, + { (PRFuncPtr*) &fGetProcAddress, { "wglGetProcAddress", nullptr } }, + { (PRFuncPtr*) &fDeleteContext, { "wglDeleteContext", nullptr } }, + { (PRFuncPtr*) &fGetCurrentContext, { "wglGetCurrentContext", nullptr } }, + { (PRFuncPtr*) &fGetCurrentDC, { "wglGetCurrentDC", nullptr } }, + { nullptr, { nullptr } } + }; + + if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &earlySymbols[0])) { + NS_WARNING("Couldn't find required entry points in OpenGL DLL (early init)"); + return false; + } + + // This is ridiculous -- we have to actually create a context to + // get the OpenGL ICD to load. + mWindow = CreateDummyWindow(&mWindowDC); + NS_ENSURE_TRUE(mWindow, false); + + // create rendering context + mWindowGLContext = fCreateContext(mWindowDC); + NS_ENSURE_TRUE(mWindowGLContext, false); + + if (!fMakeCurrent(mWindowDC, mWindowGLContext)) { + NS_WARNING("wglMakeCurrent failed"); + return false; + } + + const auto curCtx = fGetCurrentContext(); + const auto curDC = fGetCurrentDC(); + + const auto lookupFunc = (GLLibraryLoader::PlatformLookupFunction)fGetProcAddress; + + // Now we can grab all the other symbols that we couldn't without having + // a context current. + const GLLibraryLoader::SymLoadStruct pbufferSymbols[] = { + { (PRFuncPtr*) &fCreatePbuffer, { "wglCreatePbufferARB", "wglCreatePbufferEXT", nullptr } }, + { (PRFuncPtr*) &fDestroyPbuffer, { "wglDestroyPbufferARB", "wglDestroyPbufferEXT", nullptr } }, + { (PRFuncPtr*) &fGetPbufferDC, { "wglGetPbufferDCARB", "wglGetPbufferDCEXT", nullptr } }, + { (PRFuncPtr*) &fBindTexImage, { "wglBindTexImageARB", "wglBindTexImageEXT", nullptr } }, + { (PRFuncPtr*) &fReleaseTexImage, { "wglReleaseTexImageARB", "wglReleaseTexImageEXT", nullptr } }, + { nullptr, { nullptr } } + }; + + const GLLibraryLoader::SymLoadStruct pixFmtSymbols[] = { + { (PRFuncPtr*) &fChoosePixelFormat, { "wglChoosePixelFormatARB", "wglChoosePixelFormatEXT", nullptr } }, + { (PRFuncPtr*) &fGetPixelFormatAttribiv, { "wglGetPixelFormatAttribivARB", "wglGetPixelFormatAttribivEXT", nullptr } }, + { nullptr, { nullptr } } + }; + + if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pbufferSymbols[0], lookupFunc)) { + // this isn't an error, just means that pbuffers aren't supported + fCreatePbuffer = nullptr; + } + + if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &pixFmtSymbols[0], lookupFunc)) { + // this isn't an error, just means that we don't have the pixel format extension + fChoosePixelFormat = nullptr; + } + + const GLLibraryLoader::SymLoadStruct extensionsSymbols[] = { + { (PRFuncPtr*) &fGetExtensionsString, { "wglGetExtensionsStringARB", nullptr} }, + { nullptr, { nullptr } } + }; + + const GLLibraryLoader::SymLoadStruct robustnessSymbols[] = { + { (PRFuncPtr*) &fCreateContextAttribs, { "wglCreateContextAttribsARB", nullptr} }, + { nullptr, { nullptr } } + }; + + const GLLibraryLoader::SymLoadStruct dxInteropSymbols[] = { + { (PRFuncPtr*)&fDXSetResourceShareHandle,{ "wglDXSetResourceShareHandleNV", nullptr } }, + { (PRFuncPtr*)&fDXOpenDevice, { "wglDXOpenDeviceNV", nullptr } }, + { (PRFuncPtr*)&fDXCloseDevice, { "wglDXCloseDeviceNV", nullptr } }, + { (PRFuncPtr*)&fDXRegisterObject, { "wglDXRegisterObjectNV", nullptr } }, + { (PRFuncPtr*)&fDXUnregisterObject, { "wglDXUnregisterObjectNV", nullptr } }, + { (PRFuncPtr*)&fDXObjectAccess, { "wglDXObjectAccessNV", nullptr } }, + { (PRFuncPtr*)&fDXLockObjects, { "wglDXLockObjectsNV", nullptr } }, + { (PRFuncPtr*)&fDXUnlockObjects, { "wglDXUnlockObjectsNV", nullptr } }, + { nullptr, { nullptr } } + }; + + if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &extensionsSymbols[0], lookupFunc)) { + const char* extString = fGetExtensionsString(mWindowDC); + MOZ_ASSERT(extString); + MOZ_ASSERT(HasExtension(extString, "WGL_ARB_extensions_string")); + + if (HasExtension(extString, "WGL_ARB_create_context")) { + if (GLLibraryLoader::LoadSymbols(mOGLLibrary, &robustnessSymbols[0], lookupFunc)) { + if (HasExtension(extString, "WGL_ARB_create_context_robustness")) { + mHasRobustness = true; + } + } else { + NS_ERROR("WGL supports ARB_create_context without supplying its functions."); + fCreateContextAttribs = nullptr; + } + } + + //// + + mHasDXInterop = HasExtension(extString, "WGL_NV_DX_interop"); + mHasDXInterop2 = HasExtension(extString, "WGL_NV_DX_interop2"); + + nsCString blocklistId; + if (gfxUtils::IsFeatureBlacklisted(nullptr, nsIGfxInfo::FEATURE_DX_INTEROP2, + &blocklistId) && + !gfxPrefs::IgnoreDXInterop2Blacklist()) + { + mHasDXInterop2 = false; + } + + if (mHasDXInterop || mHasDXInterop2) { + if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &dxInteropSymbols[0], + lookupFunc)) + { + NS_ERROR("WGL supports NV_DX_interop(2) without supplying its functions."); + fDXSetResourceShareHandle = nullptr; + fDXOpenDevice = nullptr; + fDXCloseDevice = nullptr; + fDXRegisterObject = nullptr; + fDXUnregisterObject = nullptr; + fDXObjectAccess = nullptr; + fDXLockObjects = nullptr; + fDXUnlockObjects = nullptr; + + mHasDXInterop = false; + mHasDXInterop2 = false; + } + } + } + + // reset back to the previous context, just in case + fMakeCurrent(curDC, curCtx); + + if (mHasRobustness) { + fDeleteContext(mWindowGLContext); + + const int attribs[] = { + LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB, + LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB, + 0 + }; + + mWindowGLContext = fCreateContextAttribs(mWindowDC, nullptr, attribs); + if (!mWindowGLContext) { + mHasRobustness = false; + mWindowGLContext = fCreateContext(mWindowDC); + } + } + + mInitialized = true; + + reporter.SetSuccessful(); + return true; +} + +GLContextWGL::GLContextWGL(CreateContextFlags flags, const SurfaceCaps& caps, + bool isOffscreen, HDC aDC, HGLRC aContext, HWND aWindow) + : GLContext(flags, caps, nullptr, isOffscreen), + mDC(aDC), + mContext(aContext), + mWnd(aWindow), + mPBuffer(nullptr), + mPixelFormat(0), + mIsDoubleBuffered(false) +{ + // See 899855 + SetProfileVersion(ContextProfile::OpenGLCompatibility, 200); +} + +GLContextWGL::GLContextWGL(CreateContextFlags flags, const SurfaceCaps& caps, + bool isOffscreen, HANDLE aPbuffer, HDC aDC, HGLRC aContext, + int aPixelFormat) + : GLContext(flags, caps, nullptr, isOffscreen), + mDC(aDC), + mContext(aContext), + mWnd(nullptr), + mPBuffer(aPbuffer), + mPixelFormat(aPixelFormat), + mIsDoubleBuffered(false) +{ + // See 899855 + SetProfileVersion(ContextProfile::OpenGLCompatibility, 200); +} + +GLContextWGL::~GLContextWGL() +{ + MarkDestroyed(); + + sWGLLib.fDeleteContext(mContext); + + if (mPBuffer) + sWGLLib.fDestroyPbuffer(mPBuffer); + if (mWnd) + DestroyWindow(mWnd); +} + +bool +GLContextWGL::Init() +{ + if (!mDC || !mContext) + return false; + + // see bug 929506 comment 29. wglGetProcAddress requires a current context. + if (!sWGLLib.fMakeCurrent(mDC, mContext)) + return false; + + SetupLookupFunction(); + if (!InitWithPrefix("gl", true)) + return false; + + return true; +} + +bool +GLContextWGL::MakeCurrentImpl(bool aForce) +{ + BOOL succeeded = true; + + // wglGetCurrentContext seems to just pull the HGLRC out + // of its TLS slot, so no need to do our own tls slot. + // You would think that wglMakeCurrent would avoid doing + // work if mContext was already current, but not so much.. + if (aForce || sWGLLib.fGetCurrentContext() != mContext) { + succeeded = sWGLLib.fMakeCurrent(mDC, mContext); + NS_ASSERTION(succeeded, "Failed to make GL context current!"); + } + + return succeeded; +} + +bool +GLContextWGL::IsCurrent() +{ + return sWGLLib.fGetCurrentContext() == mContext; +} + +void +GLContextWGL::SetIsDoubleBuffered(bool aIsDB) +{ + mIsDoubleBuffered = aIsDB; +} + +bool +GLContextWGL::IsDoubleBuffered() const +{ + return mIsDoubleBuffered; +} + +bool +GLContextWGL::SupportsRobustness() const +{ + return sWGLLib.HasRobustness(); +} + +bool +GLContextWGL::SwapBuffers() { + if (!mIsDoubleBuffered) + return false; + return ::SwapBuffers(mDC); +} + +bool +GLContextWGL::SetupLookupFunction() +{ + // Make sure that we have a ref to the OGL library; + // when run under CodeXL, wglGetProcAddress won't return + // the right thing for some core functions. + MOZ_ASSERT(mLibrary == nullptr); + + mLibrary = sWGLLib.GetOGLLibrary(); + mLookupFunc = (PlatformLookupFunction)sWGLLib.fGetProcAddress; + return true; +} + +static bool +GetMaxSize(HDC hDC, int format, IntSize& size) +{ + int query[] = {LOCAL_WGL_MAX_PBUFFER_WIDTH_ARB, LOCAL_WGL_MAX_PBUFFER_HEIGHT_ARB}; + int result[2]; + + // (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int* piAttributes, int* piValues) + if (!sWGLLib.fGetPixelFormatAttribiv(hDC, format, 0, 2, query, result)) + return false; + + size.width = result[0]; + size.height = result[1]; + return true; +} + +static bool +IsValidSizeForFormat(HDC hDC, int format, + const IntSize& requested) +{ + IntSize max; + if (!GetMaxSize(hDC, format, max)) + return true; + + if (requested.width > max.width) + return false; + if (requested.height > max.height) + return false; + + return true; +} + +static GLContextWGL* +GetGlobalContextWGL() +{ + return static_cast<GLContextWGL*>(GLContextProviderWGL::GetGlobalContext()); +} + +already_AddRefed<GLContext> +GLContextProviderWGL::CreateWrappingExisting(void*, void*) +{ + return nullptr; +} + +already_AddRefed<GLContext> +GLContextProviderWGL::CreateForCompositorWidget(CompositorWidget* aCompositorWidget, bool aForceAccelerated) +{ + return CreateForWindow(aCompositorWidget->RealWidget(), aForceAccelerated); +} + +already_AddRefed<GLContext> +GLContextProviderWGL::CreateForWindow(nsIWidget* aWidget, bool aForceAccelerated) +{ + if (!sWGLLib.EnsureInitialized()) { + return nullptr; + } + + /** + * We need to make sure we call SetPixelFormat -after- calling + * EnsureInitialized, otherwise it can load/unload the dll and + * wglCreateContext will fail. + */ + + HDC dc = (HDC)aWidget->GetNativeData(NS_NATIVE_GRAPHIC); + + SetPixelFormat(dc, sWGLLib.GetWindowPixelFormat(), nullptr); + HGLRC context; + + if (sWGLLib.HasRobustness()) { + int attribs[] = { + LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB, + LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB, + 0 + }; + + context = sWGLLib.fCreateContextAttribs(dc, nullptr, attribs); + } else { + context = sWGLLib.fCreateContext(dc); + } + + if (!context) { + return nullptr; + } + + SurfaceCaps caps = SurfaceCaps::ForRGBA(); + RefPtr<GLContextWGL> glContext = new GLContextWGL(CreateContextFlags::NONE, caps, + false, dc, context); + if (!glContext->Init()) { + return nullptr; + } + + glContext->SetIsDoubleBuffered(true); + + return glContext.forget(); +} + +static already_AddRefed<GLContextWGL> +CreatePBufferOffscreenContext(CreateContextFlags flags, const IntSize& aSize) +{ + WGLLibrary& wgl = sWGLLib; + + const int pfAttribs[] = { + LOCAL_WGL_SUPPORT_OPENGL_ARB, LOCAL_GL_TRUE, + LOCAL_WGL_ACCELERATION_ARB, LOCAL_WGL_FULL_ACCELERATION_ARB, + + LOCAL_WGL_DRAW_TO_PBUFFER_ARB, LOCAL_GL_TRUE, + LOCAL_WGL_DOUBLE_BUFFER_ARB, LOCAL_GL_FALSE, + LOCAL_WGL_STEREO_ARB, LOCAL_GL_FALSE, + + 0 + }; + + // We only need one! + static const uint32_t kMaxFormats = 1024; + int formats[kMaxFormats]; + uint32_t foundFormats; + HDC windowDC = wgl.GetWindowDC(); + if (!wgl.fChoosePixelFormat(windowDC, pfAttribs, nullptr, kMaxFormats, formats, + &foundFormats) + || foundFormats == 0) + { + return nullptr; + } + + // We don't care; just pick the first one. + int chosenFormat = formats[0]; + if (!IsValidSizeForFormat(windowDC, chosenFormat, aSize)) + return nullptr; + + const int pbAttribs[] = { 0 }; + HANDLE pbuffer = wgl.fCreatePbuffer(windowDC, chosenFormat, aSize.width, aSize.height, + pbAttribs); + if (!pbuffer) { + return nullptr; + } + + HDC pbdc = wgl.fGetPbufferDC(pbuffer); + NS_ASSERTION(pbdc, "expected a dc"); + + HGLRC context; + if (wgl.HasRobustness()) { + const int attribs[] = { + LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB, + LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB, + 0 + }; + context = wgl.fCreateContextAttribs(pbdc, nullptr, attribs); + } else { + context = wgl.fCreateContext(pbdc); + } + + if (!context) { + wgl.fDestroyPbuffer(pbuffer); + return nullptr; + } + + SurfaceCaps dummyCaps = SurfaceCaps::Any(); + RefPtr<GLContextWGL> glContext = new GLContextWGL(flags, dummyCaps, true, pbuffer, + pbdc, context, chosenFormat); + return glContext.forget(); +} + +static already_AddRefed<GLContextWGL> +CreateWindowOffscreenContext() +{ + HDC dc; + HWND win = sWGLLib.CreateDummyWindow(&dc); + if (!win) { + return nullptr; + } + + HGLRC context = sWGLLib.fCreateContext(dc); + if (sWGLLib.HasRobustness()) { + int attribs[] = { + LOCAL_WGL_CONTEXT_FLAGS_ARB, LOCAL_WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB, + LOCAL_WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, LOCAL_WGL_LOSE_CONTEXT_ON_RESET_ARB, + 0 + }; + + context = sWGLLib.fCreateContextAttribs(dc, nullptr, attribs); + } else { + context = sWGLLib.fCreateContext(dc); + } + + if (!context) { + return nullptr; + } + + SurfaceCaps caps = SurfaceCaps::ForRGBA(); + RefPtr<GLContextWGL> glContext = new GLContextWGL(CreateContextFlags::NONE, caps, + true, dc, context, win); + return glContext.forget(); +} + +/*static*/ already_AddRefed<GLContext> +GLContextProviderWGL::CreateHeadless(CreateContextFlags flags, + nsACString* const out_failureId) +{ + if (!sWGLLib.EnsureInitialized()) { + return nullptr; + } + + RefPtr<GLContextWGL> glContext; + + // Always try to create a pbuffer context first, because we + // want the context isolation. + if (sWGLLib.fCreatePbuffer && + sWGLLib.fChoosePixelFormat) + { + IntSize dummySize = IntSize(16, 16); + glContext = CreatePBufferOffscreenContext(flags, dummySize); + } + + // If it failed, then create a window context and use a FBO. + if (!glContext) { + glContext = CreateWindowOffscreenContext(); + } + + if (!glContext || + !glContext->Init()) + { + return nullptr; + } + + RefPtr<GLContext> retGL = glContext.get(); + return retGL.forget(); +} + +/*static*/ already_AddRefed<GLContext> +GLContextProviderWGL::CreateOffscreen(const IntSize& size, + const SurfaceCaps& minCaps, + CreateContextFlags flags, + nsACString* const out_failureId) +{ + RefPtr<GLContext> gl = CreateHeadless(flags, out_failureId); + if (!gl) + return nullptr; + + if (!gl->InitOffscreen(size, minCaps)) { + *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_WGL_INIT"); + return nullptr; + } + + return gl.forget(); +} + +/*static*/ GLContext* +GLContextProviderWGL::GetGlobalContext() +{ + return nullptr; +} + +/*static*/ void +GLContextProviderWGL::Shutdown() +{ +} + +} /* namespace gl */ +} /* namespace mozilla */ |