Bug 1528396 - More precise GL symbol loading. r=lsalzman
☠☠ backed out by ddbb01dd4359 ☠ ☠
authorJeff Gilbert <jgilbert@mozilla.com>
Wed, 20 Feb 2019 15:46:03 +0000
changeset 460158 e74b9560a9d4a993f93b0144485e4f05a266c66b
parent 460157 5fafa838de111e11253c041ecf98e56f782beac4
child 460159 dd7e5eb2880d2fc9746c9390303f39c87ebee624
push id112059
push useropoprus@mozilla.com
push dateThu, 21 Feb 2019 09:45:58 +0000
treeherdermozilla-inbound@9d2f9b494284 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerslsalzman
bugs1528396
milestone67.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1528396 - More precise GL symbol loading. r=lsalzman In particular, don't fallback to loading symbols from any loaded library. Differential Revision: https://phabricator.services.mozilla.com/D20455
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextCGL.h
gfx/gl/GLContextEAGL.h
gfx/gl/GLContextEGL.h
gfx/gl/GLContextGLX.h
gfx/gl/GLContextProviderCGL.mm
gfx/gl/GLContextProviderEAGL.mm
gfx/gl/GLContextProviderEGL.cpp
gfx/gl/GLContextProviderGLX.cpp
gfx/gl/GLContextProviderWGL.cpp
gfx/gl/GLContextWGL.h
gfx/gl/GLLibraryEGL.cpp
gfx/gl/GLLibraryEGL.h
gfx/gl/GLLibraryLoader.cpp
gfx/gl/GLLibraryLoader.h
gfx/gl/GLXLibrary.h
gfx/gl/WGLLibrary.h
gfx/layers/wr/WebRenderBridgeParent.cpp
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -59,21 +59,21 @@ namespace gl {
 
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
 MOZ_THREAD_LOCAL(uintptr_t) GLContext::sCurrentContext;
 
 // If adding defines, don't forget to undefine symbols. See #undef block below.
 // clang-format off
-#define CORE_SYMBOL(x) { (PRFuncPtr*) &mSymbols.f##x, { #x, nullptr } }
-#define CORE_EXT_SYMBOL2(x,y,z) { (PRFuncPtr*) &mSymbols.f##x, { #x, #x #y, #x #z, nullptr } }
-#define EXT_SYMBOL2(x,y,z) { (PRFuncPtr*) &mSymbols.f##x, { #x #y, #x #z, nullptr } }
-#define EXT_SYMBOL3(x,y,z,w) { (PRFuncPtr*) &mSymbols.f##x, { #x #y, #x #z, #x #w, nullptr } }
-#define END_SYMBOLS { nullptr, { nullptr } }
+#define CORE_SYMBOL(x) { (PRFuncPtr*) &mSymbols.f##x, {{ "gl" #x }} }
+#define CORE_EXT_SYMBOL2(x,y,z) { (PRFuncPtr*) &mSymbols.f##x, {{ "gl" #x, "gl" #x #y, "gl" #x #z }} }
+#define EXT_SYMBOL2(x,y,z) { (PRFuncPtr*) &mSymbols.f##x, {{ "gl" #x #y, "gl" #x #z }} }
+#define EXT_SYMBOL3(x,y,z,w) { (PRFuncPtr*) &mSymbols.f##x, {{ "gl" #x #y, "gl" #x #z, "gl" #x #w }} }
+#define END_SYMBOLS { nullptr, {} }
 // clang-format on
 
 // should match the order of GLExtensions, and be null-terminated.
 static const char* const sExtensionNames[] = {
     "NO_EXTENSION",
     "GL_AMD_compressed_ATC_texture",
     "GL_ANGLE_depth_texture",
     "GL_ANGLE_framebuffer_blit",
@@ -297,220 +297,220 @@ GLContext::~GLContext() {
                                                GLuint id, GLenum severity,
                                                GLsizei length,
                                                const GLchar* message,
                                                const GLvoid* userParam) {
   GLContext* gl = (GLContext*)userParam;
   gl->DebugCallback(source, type, id, severity, length, message);
 }
 
-static void ClearSymbols(const GLLibraryLoader::SymLoadStruct* symbols) {
-  while (symbols->symPointer) {
-    *symbols->symPointer = nullptr;
-    symbols++;
-  }
-}
-
-bool GLContext::InitWithPrefix(const char* prefix, bool trygl) {
+bool GLContext::Init() {
   MOZ_RELEASE_ASSERT(!mSymbols.fBindFramebuffer,
-                     "GFX: InitWithPrefix should only be called once.");
+                     "GFX: GLContext::Init should only be called once.");
 
   ScopedGfxFeatureReporter reporter("GL Context");
 
-  if (!InitWithPrefixImpl(prefix, trygl)) {
+  if (!InitImpl()) {
     // If initialization fails, zero the symbols to avoid hard-to-understand
     // bugs.
     mSymbols = {};
     NS_WARNING("GLContext::InitWithPrefix failed!");
     return false;
   }
 
   reporter.SetSuccessful();
   return true;
 }
 
-static bool LoadGLSymbols(GLContext* gl, const char* prefix, bool trygl,
-                          const GLLibraryLoader::SymLoadStruct* list,
-                          const char* desc) {
+static bool LoadSymbolsWithDesc(const SymbolLoader& loader,
+                                const SymLoadStruct* list, const char* desc) {
   const auto warnOnFailure = bool(desc);
-  if (gl->LoadSymbols(list, trygl, prefix, warnOnFailure)) return true;
+  if (loader.LoadSymbols(list, warnOnFailure)) return true;
 
   ClearSymbols(list);
 
   if (desc) {
     const nsPrintfCString err("Failed to load symbols for %s.", desc);
     NS_ERROR(err.BeginReading());
   }
   return false;
 }
 
-bool GLContext::LoadExtSymbols(const char* prefix, bool trygl,
+bool GLContext::LoadExtSymbols(const SymbolLoader& loader,
                                const SymLoadStruct* list, GLExtensions ext) {
   const char* extName = sExtensionNames[size_t(ext)];
-  if (!LoadGLSymbols(this, prefix, trygl, list, extName)) {
+  if (!LoadSymbolsWithDesc(loader, list, extName)) {
     MarkExtensionUnsupported(ext);
     return false;
   }
   return true;
 };
 
-bool GLContext::LoadFeatureSymbols(const char* prefix, bool trygl,
+bool GLContext::LoadFeatureSymbols(const SymbolLoader& loader,
                                    const SymLoadStruct* list,
                                    GLFeature feature) {
   const char* featureName = GetFeatureName(feature);
-  if (!LoadGLSymbols(this, prefix, trygl, list, featureName)) {
+  if (!LoadSymbolsWithDesc(loader, list, featureName)) {
     MarkUnsupported(feature);
     return false;
   }
   return true;
 };
 
-bool GLContext::InitWithPrefixImpl(const char* prefix, bool trygl) {
+bool GLContext::InitImpl() {
   if (!MakeCurrent(true)) return false;
 
+  const auto loader = GetSymbolLoader();
+  if (!loader) return false;
+
+  const auto fnLoadSymbols = [&](const SymLoadStruct* const list,
+                                 const char* const desc) {
+    return LoadSymbolsWithDesc(*loader, list, desc);
+  };
+
   // clang-format off
     const SymLoadStruct coreSymbols[] = {
-        { (PRFuncPtr*) &mSymbols.fActiveTexture, { "ActiveTexture", "ActiveTextureARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fAttachShader, { "AttachShader", "AttachShaderARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBindAttribLocation, { "BindAttribLocation", "BindAttribLocationARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBindBuffer, { "BindBuffer", "BindBufferARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBindTexture, { "BindTexture", "BindTextureARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBlendColor, { "BlendColor", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBlendEquation, { "BlendEquation", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBlendEquationSeparate, { "BlendEquationSeparate", "BlendEquationSeparateEXT", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBlendFunc, { "BlendFunc", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBlendFuncSeparate, { "BlendFuncSeparate", "BlendFuncSeparateEXT", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBufferData, { "BufferData", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fBufferSubData, { "BufferSubData", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fClear, { "Clear", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fClearColor, { "ClearColor", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fClearStencil, { "ClearStencil", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fColorMask, { "ColorMask", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fCompressedTexImage2D, {"CompressedTexImage2D", nullptr} },
-        { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage2D, {"CompressedTexSubImage2D", nullptr} },
-        { (PRFuncPtr*) &mSymbols.fCullFace, { "CullFace", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDetachShader, { "DetachShader", "DetachShaderARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDepthFunc, { "DepthFunc", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDepthMask, { "DepthMask", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDisable, { "Disable", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDisableVertexAttribArray, { "DisableVertexAttribArray", "DisableVertexAttribArrayARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDrawArrays, { "DrawArrays", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDrawElements, { "DrawElements", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fEnable, { "Enable", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fEnableVertexAttribArray, { "EnableVertexAttribArray", "EnableVertexAttribArrayARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fFinish, { "Finish", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fFlush, { "Flush", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fFrontFace, { "FrontFace", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetActiveAttrib, { "GetActiveAttrib", "GetActiveAttribARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetActiveUniform, { "GetActiveUniform", "GetActiveUniformARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetAttachedShaders, { "GetAttachedShaders", "GetAttachedShadersARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetAttribLocation, { "GetAttribLocation", "GetAttribLocationARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetIntegerv, { "GetIntegerv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetFloatv, { "GetFloatv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetBooleanv, { "GetBooleanv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetBufferParameteriv, { "GetBufferParameteriv", "GetBufferParameterivARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetError, { "GetError", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetProgramiv, { "GetProgramiv", "GetProgramivARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetProgramInfoLog, { "GetProgramInfoLog", "GetProgramInfoLogARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fTexParameteri, { "TexParameteri", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fTexParameteriv, { "TexParameteriv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fTexParameterf, { "TexParameterf", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetString, { "GetString", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetTexParameterfv, { "GetTexParameterfv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetTexParameteriv, { "GetTexParameteriv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetUniformfv, { "GetUniformfv", "GetUniformfvARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetUniformiv, { "GetUniformiv", "GetUniformivARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetUniformLocation, { "GetUniformLocation", "GetUniformLocationARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetVertexAttribfv, { "GetVertexAttribfv", "GetVertexAttribfvARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetVertexAttribiv, { "GetVertexAttribiv", "GetVertexAttribivARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetVertexAttribPointerv, { "GetVertexAttribPointerv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fHint, { "Hint", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fIsBuffer, { "IsBuffer", "IsBufferARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fIsEnabled, { "IsEnabled", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fIsProgram, { "IsProgram", "IsProgramARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fIsShader, { "IsShader", "IsShaderARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fIsTexture, { "IsTexture", "IsTextureARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fLineWidth, { "LineWidth", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fLinkProgram, { "LinkProgram", "LinkProgramARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fPixelStorei, { "PixelStorei", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fPolygonOffset, { "PolygonOffset", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fReadPixels, { "ReadPixels", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fSampleCoverage, { "SampleCoverage", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fScissor, { "Scissor", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fStencilFunc, { "StencilFunc", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fStencilFuncSeparate, { "StencilFuncSeparate", "StencilFuncSeparateEXT", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fStencilMask, { "StencilMask", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fStencilMaskSeparate, { "StencilMaskSeparate", "StencilMaskSeparateEXT", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fStencilOp, { "StencilOp", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fStencilOpSeparate, { "StencilOpSeparate", "StencilOpSeparateEXT", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fTexImage2D, { "TexImage2D", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fTexSubImage2D, { "TexSubImage2D", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform1f, { "Uniform1f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform1fv, { "Uniform1fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform1i, { "Uniform1i", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform1iv, { "Uniform1iv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform2f, { "Uniform2f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform2fv, { "Uniform2fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform2i, { "Uniform2i", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform2iv, { "Uniform2iv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform3f, { "Uniform3f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform3fv, { "Uniform3fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform3i, { "Uniform3i", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform3iv, { "Uniform3iv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform4f, { "Uniform4f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform4fv, { "Uniform4fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform4i, { "Uniform4i", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniform4iv, { "Uniform4iv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniformMatrix2fv, { "UniformMatrix2fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniformMatrix3fv, { "UniformMatrix3fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUniformMatrix4fv, { "UniformMatrix4fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fUseProgram, { "UseProgram", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fValidateProgram, { "ValidateProgram", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttribPointer, { "VertexAttribPointer", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib1f, { "VertexAttrib1f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib2f, { "VertexAttrib2f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib3f, { "VertexAttrib3f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib4f, { "VertexAttrib4f", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib1fv, { "VertexAttrib1fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib2fv, { "VertexAttrib2fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib3fv, { "VertexAttrib3fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttrib4fv, { "VertexAttrib4fv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fViewport, { "Viewport", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fCompileShader, { "CompileShader", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fCopyTexImage2D, { "CopyTexImage2D", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fCopyTexSubImage2D, { "CopyTexSubImage2D", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetShaderiv, { "GetShaderiv", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetShaderInfoLog, { "GetShaderInfoLog", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGetShaderSource, { "GetShaderSource", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fShaderSource, { "ShaderSource", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fVertexAttribPointer, { "VertexAttribPointer", nullptr } },
-
-        { (PRFuncPtr*) &mSymbols.fGenBuffers, { "GenBuffers", "GenBuffersARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fGenTextures, { "GenTextures", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fCreateProgram, { "CreateProgram", "CreateProgramARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fCreateShader, { "CreateShader", "CreateShaderARB", nullptr } },
-
-        { (PRFuncPtr*) &mSymbols.fDeleteBuffers, { "DeleteBuffers", "DeleteBuffersARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDeleteTextures, { "DeleteTextures", "DeleteTexturesARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDeleteProgram, { "DeleteProgram", "DeleteProgramARB", nullptr } },
-        { (PRFuncPtr*) &mSymbols.fDeleteShader, { "DeleteShader", "DeleteShaderARB", nullptr } },
+        { (PRFuncPtr*) &mSymbols.fActiveTexture, {{ "glActiveTexture", "glActiveTextureARB" }} },
+        { (PRFuncPtr*) &mSymbols.fAttachShader, {{ "glAttachShader", "glAttachShaderARB" }} },
+        { (PRFuncPtr*) &mSymbols.fBindAttribLocation, {{ "glBindAttribLocation", "glBindAttribLocationARB" }} },
+        { (PRFuncPtr*) &mSymbols.fBindBuffer, {{ "glBindBuffer", "glBindBufferARB" }} },
+        { (PRFuncPtr*) &mSymbols.fBindTexture, {{ "glBindTexture", "glBindTextureARB" }} },
+        { (PRFuncPtr*) &mSymbols.fBlendColor, {{ "glBlendColor" }} },
+        { (PRFuncPtr*) &mSymbols.fBlendEquation, {{ "glBlendEquation" }} },
+        { (PRFuncPtr*) &mSymbols.fBlendEquationSeparate, {{ "glBlendEquationSeparate", "glBlendEquationSeparateEXT" }} },
+        { (PRFuncPtr*) &mSymbols.fBlendFunc, {{ "glBlendFunc" }} },
+        { (PRFuncPtr*) &mSymbols.fBlendFuncSeparate, {{ "glBlendFuncSeparate", "glBlendFuncSeparateEXT" }} },
+        { (PRFuncPtr*) &mSymbols.fBufferData, {{ "glBufferData" }} },
+        { (PRFuncPtr*) &mSymbols.fBufferSubData, {{ "glBufferSubData" }} },
+        { (PRFuncPtr*) &mSymbols.fClear, {{ "glClear" }} },
+        { (PRFuncPtr*) &mSymbols.fClearColor, {{ "glClearColor" }} },
+        { (PRFuncPtr*) &mSymbols.fClearStencil, {{ "glClearStencil" }} },
+        { (PRFuncPtr*) &mSymbols.fColorMask, {{ "glColorMask" }} },
+        { (PRFuncPtr*) &mSymbols.fCompressedTexImage2D, {{ "glCompressedTexImage2D" }} },
+        { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage2D, {{ "glCompressedTexSubImage2D" }} },
+        { (PRFuncPtr*) &mSymbols.fCullFace, {{ "glCullFace" }} },
+        { (PRFuncPtr*) &mSymbols.fDetachShader, {{ "glDetachShader", "glDetachShaderARB" }} },
+        { (PRFuncPtr*) &mSymbols.fDepthFunc, {{ "glDepthFunc" }} },
+        { (PRFuncPtr*) &mSymbols.fDepthMask, {{ "glDepthMask" }} },
+        { (PRFuncPtr*) &mSymbols.fDisable, {{ "glDisable" }} },
+        { (PRFuncPtr*) &mSymbols.fDisableVertexAttribArray, {{ "glDisableVertexAttribArray", "glDisableVertexAttribArrayARB" }} },
+        { (PRFuncPtr*) &mSymbols.fDrawArrays, {{ "glDrawArrays" }} },
+        { (PRFuncPtr*) &mSymbols.fDrawElements, {{ "glDrawElements" }} },
+        { (PRFuncPtr*) &mSymbols.fEnable, {{ "glEnable" }} },
+        { (PRFuncPtr*) &mSymbols.fEnableVertexAttribArray, {{ "glEnableVertexAttribArray", "glEnableVertexAttribArrayARB" }} },
+        { (PRFuncPtr*) &mSymbols.fFinish, {{ "glFinish" }} },
+        { (PRFuncPtr*) &mSymbols.fFlush, {{ "glFlush" }} },
+        { (PRFuncPtr*) &mSymbols.fFrontFace, {{ "glFrontFace" }} },
+        { (PRFuncPtr*) &mSymbols.fGetActiveAttrib, {{ "glGetActiveAttrib", "glGetActiveAttribARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetActiveUniform, {{ "glGetActiveUniform", "glGetActiveUniformARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetAttachedShaders, {{ "glGetAttachedShaders", "glGetAttachedShadersARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetAttribLocation, {{ "glGetAttribLocation", "glGetAttribLocationARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetIntegerv, {{ "glGetIntegerv" }} },
+        { (PRFuncPtr*) &mSymbols.fGetFloatv, {{ "glGetFloatv" }} },
+        { (PRFuncPtr*) &mSymbols.fGetBooleanv, {{ "glGetBooleanv" }} },
+        { (PRFuncPtr*) &mSymbols.fGetBufferParameteriv, {{ "glGetBufferParameteriv", "glGetBufferParameterivARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetError, {{ "glGetError" }} },
+        { (PRFuncPtr*) &mSymbols.fGetProgramiv, {{ "glGetProgramiv", "glGetProgramivARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetProgramInfoLog, {{ "glGetProgramInfoLog", "glGetProgramInfoLogARB" }} },
+        { (PRFuncPtr*) &mSymbols.fTexParameteri, {{ "glTexParameteri" }} },
+        { (PRFuncPtr*) &mSymbols.fTexParameteriv, {{ "glTexParameteriv" }} },
+        { (PRFuncPtr*) &mSymbols.fTexParameterf, {{ "glTexParameterf" }} },
+        { (PRFuncPtr*) &mSymbols.fGetString, {{ "glGetString" }} },
+        { (PRFuncPtr*) &mSymbols.fGetTexParameterfv, {{ "glGetTexParameterfv" }} },
+        { (PRFuncPtr*) &mSymbols.fGetTexParameteriv, {{ "glGetTexParameteriv" }} },
+        { (PRFuncPtr*) &mSymbols.fGetUniformfv, {{ "glGetUniformfv", "glGetUniformfvARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetUniformiv, {{ "glGetUniformiv", "glGetUniformivARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetUniformLocation, {{ "glGetUniformLocation", "glGetUniformLocationARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetVertexAttribfv, {{ "glGetVertexAttribfv", "glGetVertexAttribfvARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetVertexAttribiv, {{ "glGetVertexAttribiv", "glGetVertexAttribivARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGetVertexAttribPointerv, {{ "glGetVertexAttribPointerv" }} },
+        { (PRFuncPtr*) &mSymbols.fHint, {{ "glHint" }} },
+        { (PRFuncPtr*) &mSymbols.fIsBuffer, {{ "glIsBuffer", "glIsBufferARB" }} },
+        { (PRFuncPtr*) &mSymbols.fIsEnabled, {{ "glIsEnabled" }} },
+        { (PRFuncPtr*) &mSymbols.fIsProgram, {{ "glIsProgram", "glIsProgramARB" }} },
+        { (PRFuncPtr*) &mSymbols.fIsShader, {{ "glIsShader", "glIsShaderARB" }} },
+        { (PRFuncPtr*) &mSymbols.fIsTexture, {{ "glIsTexture", "glIsTextureARB" }} },
+        { (PRFuncPtr*) &mSymbols.fLineWidth, {{ "glLineWidth" }} },
+        { (PRFuncPtr*) &mSymbols.fLinkProgram, {{ "glLinkProgram", "glLinkProgramARB" }} },
+        { (PRFuncPtr*) &mSymbols.fPixelStorei, {{ "glPixelStorei" }} },
+        { (PRFuncPtr*) &mSymbols.fPolygonOffset, {{ "glPolygonOffset" }} },
+        { (PRFuncPtr*) &mSymbols.fReadPixels, {{ "glReadPixels" }} },
+        { (PRFuncPtr*) &mSymbols.fSampleCoverage, {{ "glSampleCoverage" }} },
+        { (PRFuncPtr*) &mSymbols.fScissor, {{ "glScissor" }} },
+        { (PRFuncPtr*) &mSymbols.fStencilFunc, {{ "glStencilFunc" }} },
+        { (PRFuncPtr*) &mSymbols.fStencilFuncSeparate, {{ "glStencilFuncSeparate", "glStencilFuncSeparateEXT" }} },
+        { (PRFuncPtr*) &mSymbols.fStencilMask, {{ "glStencilMask" }} },
+        { (PRFuncPtr*) &mSymbols.fStencilMaskSeparate, {{ "glStencilMaskSeparate", "glStencilMaskSeparateEXT" }} },
+        { (PRFuncPtr*) &mSymbols.fStencilOp, {{ "glStencilOp" }} },
+        { (PRFuncPtr*) &mSymbols.fStencilOpSeparate, {{ "glStencilOpSeparate", "glStencilOpSeparateEXT" }} },
+        { (PRFuncPtr*) &mSymbols.fTexImage2D, {{ "glTexImage2D" }} },
+        { (PRFuncPtr*) &mSymbols.fTexSubImage2D, {{ "glTexSubImage2D" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform1f, {{ "glUniform1f" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform1fv, {{ "glUniform1fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform1i, {{ "glUniform1i" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform1iv, {{ "glUniform1iv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform2f, {{ "glUniform2f" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform2fv, {{ "glUniform2fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform2i, {{ "glUniform2i" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform2iv, {{ "glUniform2iv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform3f, {{ "glUniform3f" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform3fv, {{ "glUniform3fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform3i, {{ "glUniform3i" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform3iv, {{ "glUniform3iv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform4f, {{ "glUniform4f" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform4fv, {{ "glUniform4fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform4i, {{ "glUniform4i" }} },
+        { (PRFuncPtr*) &mSymbols.fUniform4iv, {{ "glUniform4iv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniformMatrix2fv, {{ "glUniformMatrix2fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniformMatrix3fv, {{ "glUniformMatrix3fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUniformMatrix4fv, {{ "glUniformMatrix4fv" }} },
+        { (PRFuncPtr*) &mSymbols.fUseProgram, {{ "glUseProgram" }} },
+        { (PRFuncPtr*) &mSymbols.fValidateProgram, {{ "glValidateProgram" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttribPointer, {{ "glVertexAttribPointer" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib1f, {{ "glVertexAttrib1f" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib2f, {{ "glVertexAttrib2f" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib3f, {{ "glVertexAttrib3f" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib4f, {{ "glVertexAttrib4f" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib1fv, {{ "glVertexAttrib1fv" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib2fv, {{ "glVertexAttrib2fv" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib3fv, {{ "glVertexAttrib3fv" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttrib4fv, {{ "glVertexAttrib4fv" }} },
+        { (PRFuncPtr*) &mSymbols.fViewport, {{ "glViewport" }} },
+        { (PRFuncPtr*) &mSymbols.fCompileShader, {{ "glCompileShader" }} },
+        { (PRFuncPtr*) &mSymbols.fCopyTexImage2D, {{ "glCopyTexImage2D" }} },
+        { (PRFuncPtr*) &mSymbols.fCopyTexSubImage2D, {{ "glCopyTexSubImage2D" }} },
+        { (PRFuncPtr*) &mSymbols.fGetShaderiv, {{ "glGetShaderiv" }} },
+        { (PRFuncPtr*) &mSymbols.fGetShaderInfoLog, {{ "glGetShaderInfoLog" }} },
+        { (PRFuncPtr*) &mSymbols.fGetShaderSource, {{ "glGetShaderSource" }} },
+        { (PRFuncPtr*) &mSymbols.fShaderSource, {{ "glShaderSource" }} },
+        { (PRFuncPtr*) &mSymbols.fVertexAttribPointer, {{ "glVertexAttribPointer" }} },
+
+        { (PRFuncPtr*) &mSymbols.fGenBuffers, {{ "glGenBuffers", "glGenBuffersARB" }} },
+        { (PRFuncPtr*) &mSymbols.fGenTextures, {{ "glGenTextures" }} },
+        { (PRFuncPtr*) &mSymbols.fCreateProgram, {{ "glCreateProgram", "glCreateProgramARB" }} },
+        { (PRFuncPtr*) &mSymbols.fCreateShader, {{ "glCreateShader", "glCreateShaderARB" }} },
+
+        { (PRFuncPtr*) &mSymbols.fDeleteBuffers, {{ "glDeleteBuffers", "glDeleteBuffersARB" }} },
+        { (PRFuncPtr*) &mSymbols.fDeleteTextures, {{ "glDeleteTextures", "glDeleteTexturesARB" }} },
+        { (PRFuncPtr*) &mSymbols.fDeleteProgram, {{ "glDeleteProgram", "glDeleteProgramARB" }} },
+        { (PRFuncPtr*) &mSymbols.fDeleteShader, {{ "glDeleteShader", "glDeleteShaderARB" }} },
 
         END_SYMBOLS
     };
   // clang-format on
 
-  if (!LoadGLSymbols(this, prefix, trygl, coreSymbols, "GL")) return false;
+  if (!fnLoadSymbols(coreSymbols, "GL")) return false;
 
   {
     const SymLoadStruct symbols[] = {
         {(PRFuncPtr*)&mSymbols.fGetGraphicsResetStatus,
-         {"GetGraphicsResetStatus", "GetGraphicsResetStatusARB",
-          "GetGraphicsResetStatusKHR", "GetGraphicsResetStatusEXT", nullptr}},
+         {{"glGetGraphicsResetStatus", "glGetGraphicsResetStatusARB",
+           "glGetGraphicsResetStatusKHR", "glGetGraphicsResetStatusEXT"}}},
         END_SYMBOLS};
-    (void)LoadGLSymbols(this, prefix, trygl, symbols, nullptr);
+    (void)fnLoadSymbols(symbols, nullptr);
 
     auto err = fGetError();
     if (err == LOCAL_GL_CONTEXT_LOST) {
       MOZ_ASSERT(mSymbols.fGetGraphicsResetStatus);
       const auto status = fGetGraphicsResetStatus();
       if (status) {
         printf_stderr("Unflushed glGetGraphicsResetStatus: 0x%04x\n", status);
       }
@@ -566,34 +566,33 @@ bool GLContext::InitWithPrefixImpl(const
   ////////////////
 
   // Load OpenGL ES 2.0 symbols, or desktop if we aren't using ES 2.
   if (mProfile == ContextProfile::OpenGLES) {
     const SymLoadStruct symbols[] = {CORE_SYMBOL(GetShaderPrecisionFormat),
                                      CORE_SYMBOL(ClearDepthf),
                                      CORE_SYMBOL(DepthRangef), END_SYMBOLS};
 
-    if (!LoadGLSymbols(this, prefix, trygl, symbols, "OpenGL ES")) return false;
+    if (!fnLoadSymbols(symbols, "OpenGL ES")) return false;
   } else {
     const SymLoadStruct symbols[] = {
         CORE_SYMBOL(ClearDepth), CORE_SYMBOL(DepthRange),
         CORE_SYMBOL(ReadBuffer), CORE_SYMBOL(MapBuffer),
         CORE_SYMBOL(UnmapBuffer), CORE_SYMBOL(PointParameterf),
         CORE_SYMBOL(DrawBuffer),
         // The following functions are only used by Skia/GL in desktop mode.
         // Other parts of Gecko should avoid using these
         CORE_SYMBOL(DrawBuffers), CORE_SYMBOL(ClientActiveTexture),
         CORE_SYMBOL(DisableClientState), CORE_SYMBOL(EnableClientState),
         CORE_SYMBOL(LoadIdentity), CORE_SYMBOL(LoadMatrixf),
         CORE_SYMBOL(MatrixMode), CORE_SYMBOL(PolygonMode), CORE_SYMBOL(TexGeni),
         CORE_SYMBOL(TexGenf), CORE_SYMBOL(TexGenfv), CORE_SYMBOL(VertexPointer),
         END_SYMBOLS};
 
-    if (!LoadGLSymbols(this, prefix, trygl, symbols, "Desktop OpenGL"))
-      return false;
+    if (!fnLoadSymbols(symbols, "Desktop OpenGL")) return false;
   }
 
   ////////////////
 
   const char* glVendorString = (const char*)fGetString(LOCAL_GL_VENDOR);
   const char* glRendererString = (const char*)fGetString(LOCAL_GL_RENDERER);
   if (!glVendorString || !glRendererString) return false;
 
@@ -648,20 +647,19 @@ bool GLContext::InitWithPrefixImpl(const
     printf_stderr("GL_RENDERER: %s\n", glRendererString);
     printf_stderr("mRenderer: %s\n", rendererMatchStrings[size_t(mRenderer)]);
   }
 
   ////////////////
 
   if (mVersion >= 300) {  // Both GL3 and ES3.
     const SymLoadStruct symbols[] = {
-        {(PRFuncPtr*)&mSymbols.fGetStringi, {"GetStringi", nullptr}},
-        END_SYMBOLS};
-
-    if (!LoadGLSymbols(this, prefix, trygl, symbols, "GetStringi")) {
+        {(PRFuncPtr*)&mSymbols.fGetStringi, {{"glGetStringi"}}}, END_SYMBOLS};
+
+    if (!fnLoadSymbols(symbols, "GetStringi")) {
       MOZ_RELEASE_ASSERT(false, "GFX: GetStringi is required!");
       return false;
     }
   }
 
   InitExtensions();
   if (mProfile != ContextProfile::OpenGLES) {
     if (mVersion >= 310 && !IsExtensionSupported(ARB_compatibility)) {
@@ -726,19 +724,19 @@ bool GLContext::InitWithPrefixImpl(const
     MOZ_ASSERT(
         (mSymbols.fMapBuffer && mSymbols.fUnmapBuffer),
         "ARB_pixel_buffer_object supported without glMapBuffer/UnmapBuffer"
         " being available!");
   }
 
   ////////////////////////////////////////////////////////////////////////////
 
-  const auto fnLoadForFeature = [this, prefix, trygl](const SymLoadStruct* list,
-                                                      GLFeature feature) {
-    return this->LoadFeatureSymbols(prefix, trygl, list, feature);
+  const auto fnLoadForFeature = [&](const SymLoadStruct* list,
+                                    GLFeature feature) {
+    return this->LoadFeatureSymbols(*loader, list, feature);
   };
 
   // Check for ARB_framebuffer_objects
   if (IsSupported(GLFeature::framebuffer_object)) {
     // https://www.opengl.org/registry/specs/ARB/framebuffer_object.txt
     const SymLoadStruct symbols[] = {
         CORE_SYMBOL(IsRenderbuffer),
         CORE_SYMBOL(BindRenderbuffer),
@@ -797,17 +795,17 @@ bool GLContext::InitWithPrefixImpl(const
           END_SYMBOLS};
       fnLoadForFeature(symbols, GLFeature::framebuffer_multisample);
     }
 
     if (IsExtensionSupported(GLContext::ARB_geometry_shader4) ||
         IsExtensionSupported(GLContext::NV_geometry_program4)) {
       const SymLoadStruct symbols[] = {
           EXT_SYMBOL2(FramebufferTextureLayer, ARB, EXT), END_SYMBOLS};
-      if (!LoadGLSymbols(this, prefix, trygl, symbols,
+      if (!fnLoadSymbols(symbols,
                          "ARB_geometry_shader4/NV_geometry_program4")) {
         MarkExtensionUnsupported(GLContext::ARB_geometry_shader4);
         MarkExtensionUnsupported(GLContext::NV_geometry_program4);
       }
     }
   }
 
   if (!IsSupported(GLFeature::framebuffer_object) &&
@@ -819,17 +817,17 @@ bool GLContext::InitWithPrefixImpl(const
                      "GFX: mSymbols.fBindFramebuffer zero or not set.");
 
   ////////////////
 
   const auto err = fGetError();
   MOZ_RELEASE_ASSERT(!IsBadCallError(err));
   if (err) return false;
 
-  LoadMoreSymbols(prefix, trygl);
+  LoadMoreSymbols(*loader);
 
   ////////////////////////////////////////////////////////////////////////////
 
   raw_fGetIntegerv(LOCAL_GL_VIEWPORT, mViewportRect);
   raw_fGetIntegerv(LOCAL_GL_SCISSOR_BOX, mScissorRect);
   raw_fGetIntegerv(LOCAL_GL_MAX_TEXTURE_SIZE, &mMaxTextureSize);
   raw_fGetIntegerv(LOCAL_GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mMaxCubeMapTextureSize);
   raw_fGetIntegerv(LOCAL_GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize);
@@ -930,29 +928,27 @@ bool GLContext::InitWithPrefixImpl(const
     fDebugMessageCallback(&StaticDebugCallback, (void*)this);
     fDebugMessageControl(LOCAL_GL_DONT_CARE, LOCAL_GL_DONT_CARE,
                          LOCAL_GL_DONT_CARE, 0, nullptr, true);
   }
 
   return true;
 }
 
-void GLContext::LoadMoreSymbols(const char* prefix, bool trygl) {
-  const auto fnLoadForExt = [this, prefix, trygl](const SymLoadStruct* list,
-                                                  GLExtensions ext) {
-    return this->LoadExtSymbols(prefix, trygl, list, ext);
+void GLContext::LoadMoreSymbols(const SymbolLoader& loader) {
+  const auto fnLoadForExt = [&](const SymLoadStruct* list, GLExtensions ext) {
+    return this->LoadExtSymbols(loader, list, ext);
   };
 
-  const auto fnLoadForFeature = [this, prefix, trygl](const SymLoadStruct* list,
-                                                      GLFeature feature) {
-    return this->LoadFeatureSymbols(prefix, trygl, list, feature);
+  const auto fnLoadForFeature = [&](const SymLoadStruct* list,
+                                    GLFeature feature) {
+    return this->LoadFeatureSymbols(loader, list, feature);
   };
 
-  const auto fnLoadFeatureByCore = [this, fnLoadForFeature](
-                                       const SymLoadStruct* coreList,
+  const auto fnLoadFeatureByCore = [&](const SymLoadStruct* coreList,
                                        const SymLoadStruct* extList,
                                        GLFeature feature) {
     const bool useCore = this->IsFeatureProvidedByCoreSymbols(feature);
     const auto list = useCore ? coreList : extList;
     return fnLoadForFeature(list, feature);
   };
 
   if (IsSupported(GLFeature::robustness)) {
@@ -979,19 +975,19 @@ void GLContext::LoadMoreSymbols(const ch
         CORE_SYMBOL(WaitSync),   CORE_SYMBOL(GetInteger64v),
         CORE_SYMBOL(GetSynciv),  END_SYMBOLS};
     fnLoadForFeature(symbols, GLFeature::sync);
   }
 
   if (IsExtensionSupported(OES_EGL_image)) {
     const SymLoadStruct symbols[] = {
         {(PRFuncPtr*)&mSymbols.fEGLImageTargetTexture2D,
-         {"EGLImageTargetTexture2DOES", nullptr}},
+         {{"glEGLImageTargetTexture2DOES"}}},
         {(PRFuncPtr*)&mSymbols.fEGLImageTargetRenderbufferStorage,
-         {"EGLImageTargetRenderbufferStorageOES", nullptr}},
+         {{"glEGLImageTargetRenderbufferStorageOES"}}},
         END_SYMBOLS};
     fnLoadForExt(symbols, OES_EGL_image);
   }
 
   if (IsExtensionSupported(APPLE_texture_range)) {
     const SymLoadStruct symbols[] = {CORE_SYMBOL(TextureRangeAPPLE),
                                      END_SYMBOLS};
     fnLoadForExt(symbols, APPLE_texture_range);
@@ -1002,441 +998,441 @@ void GLContext::LoadMoreSymbols(const ch
                                      CORE_SYMBOL(TestObjectAPPLE), END_SYMBOLS};
     fnLoadForExt(symbols, APPLE_fence);
   }
 
   // clang-format off
 
     if (IsSupported(GLFeature::vertex_array_object)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fIsVertexArray, { "IsVertexArray", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGenVertexArrays, { "GenVertexArrays", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindVertexArray, { "BindVertexArray", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, { "DeleteVertexArrays", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fIsVertexArray, {{ "glIsVertexArray" }} },
+            { (PRFuncPtr*) &mSymbols.fGenVertexArrays, {{ "glGenVertexArrays" }} },
+            { (PRFuncPtr*) &mSymbols.fBindVertexArray, {{ "glBindVertexArray" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, {{ "glDeleteVertexArrays" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fIsVertexArray, { "IsVertexArrayARB", "IsVertexArrayOES", "IsVertexArrayAPPLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGenVertexArrays, { "GenVertexArraysARB", "GenVertexArraysOES", "GenVertexArraysAPPLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindVertexArray, { "BindVertexArrayARB", "BindVertexArrayOES", "BindVertexArrayAPPLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, { "DeleteVertexArraysARB", "DeleteVertexArraysOES", "DeleteVertexArraysAPPLE", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fIsVertexArray, {{ "glIsVertexArrayARB", "glIsVertexArrayOES", "glIsVertexArrayAPPLE" }} },
+            { (PRFuncPtr*) &mSymbols.fGenVertexArrays, {{ "glGenVertexArraysARB", "glGenVertexArraysOES", "glGenVertexArraysAPPLE" }} },
+            { (PRFuncPtr*) &mSymbols.fBindVertexArray, {{ "glBindVertexArrayARB", "glBindVertexArrayOES", "glBindVertexArrayAPPLE" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, {{ "glDeleteVertexArraysARB", "glDeleteVertexArraysOES", "glDeleteVertexArraysAPPLE" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::vertex_array_object);
     }
 
     if (IsSupported(GLFeature::draw_instanced)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDrawArraysInstanced, { "DrawArraysInstanced", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, { "DrawElementsInstanced", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fDrawArraysInstanced, {{ "glDrawArraysInstanced" }} },
+            { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, {{ "glDrawElementsInstanced" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDrawArraysInstanced, { "DrawArraysInstancedARB", "DrawArraysInstancedEXT", "DrawArraysInstancedNV", "DrawArraysInstancedANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, { "DrawElementsInstancedARB", "DrawElementsInstancedEXT", "DrawElementsInstancedNV", "DrawElementsInstancedANGLE", nullptr }
+            { (PRFuncPtr*) &mSymbols.fDrawArraysInstanced, {{ "glDrawArraysInstancedARB", "glDrawArraysInstancedEXT", "glDrawArraysInstancedNV", "glDrawArraysInstancedANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, {{ "glDrawElementsInstancedARB", "glDrawElementsInstancedEXT", "glDrawElementsInstancedNV", "glDrawElementsInstancedANGLE" }}
             },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_instanced);
     }
 
     if (IsSupported(GLFeature::instanced_arrays)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fVertexAttribDivisor, { "VertexAttribDivisor", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribDivisor, {{ "glVertexAttribDivisor" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fVertexAttribDivisor, { "VertexAttribDivisorARB", "VertexAttribDivisorNV", "VertexAttribDivisorANGLE", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribDivisor, {{ "glVertexAttribDivisorARB", "glVertexAttribDivisorNV", "glVertexAttribDivisorANGLE" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::instanced_arrays);
     }
 
     if (IsSupported(GLFeature::texture_storage)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2D", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3D", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fTexStorage2D, {{ "glTexStorage2D" }} },
+            { (PRFuncPtr*) &mSymbols.fTexStorage3D, {{ "glTexStorage3D" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fTexStorage2D, { "TexStorage2DEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fTexStorage3D, { "TexStorage3DEXT", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fTexStorage2D, {{ "glTexStorage2DEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fTexStorage3D, {{ "glTexStorage3DEXT" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_storage);
     }
 
     if (IsSupported(GLFeature::sampler_objects)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGenSamplers, { "GenSamplers", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteSamplers, { "DeleteSamplers", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fIsSampler, { "IsSampler", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindSampler, { "BindSampler", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fSamplerParameteri, { "SamplerParameteri", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fSamplerParameteriv, { "SamplerParameteriv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fSamplerParameterf, { "SamplerParameterf", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fSamplerParameterfv, { "SamplerParameterfv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetSamplerParameteriv, { "GetSamplerParameteriv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetSamplerParameterfv, { "GetSamplerParameterfv", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGenSamplers, {{ "glGenSamplers" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteSamplers, {{ "glDeleteSamplers" }} },
+            { (PRFuncPtr*) &mSymbols.fIsSampler, {{ "glIsSampler" }} },
+            { (PRFuncPtr*) &mSymbols.fBindSampler, {{ "glBindSampler" }} },
+            { (PRFuncPtr*) &mSymbols.fSamplerParameteri, {{ "glSamplerParameteri" }} },
+            { (PRFuncPtr*) &mSymbols.fSamplerParameteriv, {{ "glSamplerParameteriv" }} },
+            { (PRFuncPtr*) &mSymbols.fSamplerParameterf, {{ "glSamplerParameterf" }} },
+            { (PRFuncPtr*) &mSymbols.fSamplerParameterfv, {{ "glSamplerParameterfv" }} },
+            { (PRFuncPtr*) &mSymbols.fGetSamplerParameteriv, {{ "glGetSamplerParameteriv" }} },
+            { (PRFuncPtr*) &mSymbols.fGetSamplerParameterfv, {{ "glGetSamplerParameterfv" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::sampler_objects);
     }
 
     // ARB_transform_feedback2/NV_transform_feedback2 is a
     // superset of EXT_transform_feedback/NV_transform_feedback
     // and adds glPauseTransformFeedback &
     // glResumeTransformFeedback, which are required for WebGL2.
     if (IsSupported(GLFeature::transform_feedback2)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fBindBufferBase, { "BindBufferBase", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindBufferRange, { "BindBufferRange", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGenTransformFeedbacks, { "GenTransformFeedbacks", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindTransformFeedback, { "BindTransformFeedback", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteTransformFeedbacks, { "DeleteTransformFeedbacks", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fIsTransformFeedback, { "IsTransformFeedback", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBeginTransformFeedback, { "BeginTransformFeedback", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fEndTransformFeedback, { "EndTransformFeedback", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fTransformFeedbackVaryings, { "TransformFeedbackVaryings", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, { "GetTransformFeedbackVarying", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, { "PauseTransformFeedback", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, { "ResumeTransformFeedback", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fBindBufferBase, {{ "glBindBufferBase" }} },
+            { (PRFuncPtr*) &mSymbols.fBindBufferRange, {{ "glBindBufferRange" }} },
+            { (PRFuncPtr*) &mSymbols.fGenTransformFeedbacks, {{ "glGenTransformFeedbacks" }} },
+            { (PRFuncPtr*) &mSymbols.fBindTransformFeedback, {{ "glBindTransformFeedback" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteTransformFeedbacks, {{ "glDeleteTransformFeedbacks" }} },
+            { (PRFuncPtr*) &mSymbols.fIsTransformFeedback, {{ "glIsTransformFeedback" }} },
+            { (PRFuncPtr*) &mSymbols.fBeginTransformFeedback, {{ "glBeginTransformFeedback" }} },
+            { (PRFuncPtr*) &mSymbols.fEndTransformFeedback, {{ "glEndTransformFeedback" }} },
+            { (PRFuncPtr*) &mSymbols.fTransformFeedbackVaryings, {{ "glTransformFeedbackVaryings" }} },
+            { (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, {{ "glGetTransformFeedbackVarying" }} },
+            { (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, {{ "glPauseTransformFeedback" }} },
+            { (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, {{ "glResumeTransformFeedback" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fBindBufferBase, { "BindBufferBaseEXT", "BindBufferBaseNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindBufferRange, { "BindBufferRangeEXT", "BindBufferRangeNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGenTransformFeedbacks, { "GenTransformFeedbacksNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBindTransformFeedback, { "BindTransformFeedbackNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteTransformFeedbacks, { "DeleteTransformFeedbacksNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fIsTransformFeedback, { "IsTransformFeedbackNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fBeginTransformFeedback, { "BeginTransformFeedbackEXT", "BeginTransformFeedbackNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fEndTransformFeedback, { "EndTransformFeedbackEXT", "EndTransformFeedbackNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fTransformFeedbackVaryings, { "TransformFeedbackVaryingsEXT", "TransformFeedbackVaryingsNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, { "GetTransformFeedbackVaryingEXT", "GetTransformFeedbackVaryingNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, { "PauseTransformFeedbackNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, { "ResumeTransformFeedbackNV", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fBindBufferBase, {{ "glBindBufferBaseEXT", "glBindBufferBaseNV" }} },
+            { (PRFuncPtr*) &mSymbols.fBindBufferRange, {{ "glBindBufferRangeEXT", "glBindBufferRangeNV" }} },
+            { (PRFuncPtr*) &mSymbols.fGenTransformFeedbacks, {{ "glGenTransformFeedbacksNV" }} },
+            { (PRFuncPtr*) &mSymbols.fBindTransformFeedback, {{ "glBindTransformFeedbackNV" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteTransformFeedbacks, {{ "glDeleteTransformFeedbacksNV" }} },
+            { (PRFuncPtr*) &mSymbols.fIsTransformFeedback, {{ "glIsTransformFeedbackNV" }} },
+            { (PRFuncPtr*) &mSymbols.fBeginTransformFeedback, {{ "glBeginTransformFeedbackEXT", "glBeginTransformFeedbackNV" }} },
+            { (PRFuncPtr*) &mSymbols.fEndTransformFeedback, {{ "glEndTransformFeedbackEXT", "glEndTransformFeedbackNV" }} },
+            { (PRFuncPtr*) &mSymbols.fTransformFeedbackVaryings, {{ "glTransformFeedbackVaryingsEXT", "glTransformFeedbackVaryingsNV" }} },
+            { (PRFuncPtr*) &mSymbols.fGetTransformFeedbackVarying, {{ "glGetTransformFeedbackVaryingEXT", "glGetTransformFeedbackVaryingNV" }} },
+            { (PRFuncPtr*) &mSymbols.fPauseTransformFeedback, {{ "glPauseTransformFeedbackNV" }} },
+            { (PRFuncPtr*) &mSymbols.fResumeTransformFeedback, {{ "glResumeTransformFeedbackNV" }} },
             END_SYMBOLS
         };
         if (!fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_storage)) {
             // Also mark bind_buffer_offset as unsupported.
             MarkUnsupported(GLFeature::bind_buffer_offset);
         }
     }
 
     if (IsSupported(GLFeature::bind_buffer_offset)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fBindBufferOffset, { "BindBufferOffset", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fBindBufferOffset, {{ "glBindBufferOffset" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
             { (PRFuncPtr*) &mSymbols.fBindBufferOffset,
-              { "BindBufferOffsetEXT", "BindBufferOffsetNV", nullptr }
+              {{ "glBindBufferOffsetEXT", "glBindBufferOffsetNV" }}
             },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::bind_buffer_offset);
     }
 
     if (IsSupported(GLFeature::query_counter)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fQueryCounter, { "QueryCounter", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fQueryCounter, {{ "glQueryCounter" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fQueryCounter, { "QueryCounterEXT", "QueryCounterANGLE", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fQueryCounter, {{ "glQueryCounterEXT", "glQueryCounterANGLE" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::query_counter);
     }
 
     if (IsSupported(GLFeature::query_objects)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQuery", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueries", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueries", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQuery", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryiv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuiv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQuery", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fBeginQuery, {{ "glBeginQuery" }} },
+            { (PRFuncPtr*) &mSymbols.fGenQueries, {{ "glGenQueries" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteQueries, {{ "glDeleteQueries" }} },
+            { (PRFuncPtr*) &mSymbols.fEndQuery, {{ "glEndQuery" }} },
+            { (PRFuncPtr*) &mSymbols.fGetQueryiv, {{ "glGetQueryiv" }} },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, {{ "glGetQueryObjectuiv" }} },
+            { (PRFuncPtr*) &mSymbols.fIsQuery, {{ "glIsQuery" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQueryEXT", "BeginQueryANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueriesEXT", "GenQueriesANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueriesEXT", "DeleteQueriesANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQueryEXT", "EndQueryANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryivEXT", "GetQueryivANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuivEXT", "GetQueryObjectuivANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQueryEXT", "IsQueryANGLE", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fBeginQuery, {{ "glBeginQueryEXT", "glBeginQueryANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fGenQueries, {{ "glGenQueriesEXT", "glGenQueriesANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteQueries, {{ "glDeleteQueriesEXT", "glDeleteQueriesANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fEndQuery, {{ "glEndQueryEXT", "glEndQueryANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fGetQueryiv, {{ "glGetQueryivEXT", "glGetQueryivANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, {{ "glGetQueryObjectuivEXT", "glGetQueryObjectuivANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fIsQuery, {{ "glIsQueryEXT", "glIsQueryANGLE" }} },
             END_SYMBOLS
         };
         if (!fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::query_objects)) {
             MarkUnsupported(GLFeature::get_query_object_i64v);
             MarkUnsupported(GLFeature::get_query_object_iv);
             MarkUnsupported(GLFeature::occlusion_query);
             MarkUnsupported(GLFeature::occlusion_query_boolean);
             MarkUnsupported(GLFeature::occlusion_query2);
         }
     }
 
     if (IsSupported(GLFeature::get_query_object_i64v)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, { "GetQueryObjecti64v", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, { "GetQueryObjectui64v", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, {{ "glGetQueryObjecti64v" }} },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, {{ "glGetQueryObjectui64v" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, { "GetQueryObjecti64vEXT", "GetQueryObjecti64vANGLE", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, { "GetQueryObjectui64vEXT", "GetQueryObjectui64vANGLE", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjecti64v, {{ "glGetQueryObjecti64vEXT", "glGetQueryObjecti64vANGLE" }} },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, {{ "glGetQueryObjectui64vEXT", "glGetQueryObjectui64vANGLE" }} },
             END_SYMBOLS
         };
         if (!fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::get_query_object_i64v)) {
             MarkUnsupported(GLFeature::query_counter);
         }
     }
 
     if (IsSupported(GLFeature::get_query_object_iv)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectiv", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, {{ "glGetQueryObjectiv" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectivEXT", "GetQueryObjectivANGLE", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, {{ "glGetQueryObjectivEXT", "glGetQueryObjectivANGLE" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::get_query_object_iv);
     }
 
     if (IsSupported(GLFeature::clear_buffers)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fClearBufferfi,  { "ClearBufferfi",  nullptr } },
-            { (PRFuncPtr*) &mSymbols.fClearBufferfv,  { "ClearBufferfv",  nullptr } },
-            { (PRFuncPtr*) &mSymbols.fClearBufferiv,  { "ClearBufferiv",  nullptr } },
-            { (PRFuncPtr*) &mSymbols.fClearBufferuiv, { "ClearBufferuiv", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fClearBufferfi,  {{ "glClearBufferfi",  }} },
+            { (PRFuncPtr*) &mSymbols.fClearBufferfv,  {{ "glClearBufferfv",  }} },
+            { (PRFuncPtr*) &mSymbols.fClearBufferiv,  {{ "glClearBufferiv",  }} },
+            { (PRFuncPtr*) &mSymbols.fClearBufferuiv, {{ "glClearBufferuiv" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::clear_buffers);
     }
 
     if (IsSupported(GLFeature::copy_buffer)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fCopyBufferSubData, { "CopyBufferSubData", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fCopyBufferSubData, {{ "glCopyBufferSubData" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::copy_buffer);
     }
 
     if (IsSupported(GLFeature::draw_buffers)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDrawBuffers, { "DrawBuffers", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fDrawBuffers, {{ "glDrawBuffers" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDrawBuffers, { "DrawBuffersARB", "DrawBuffersEXT", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fDrawBuffers, {{ "glDrawBuffersARB", "glDrawBuffersEXT" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_buffers);
     }
 
     if (IsSupported(GLFeature::draw_range_elements)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDrawRangeElements, { "DrawRangeElements", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fDrawRangeElements, {{ "glDrawRangeElements" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDrawRangeElements, { "DrawRangeElementsEXT", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fDrawRangeElements, {{ "glDrawRangeElementsEXT" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_range_elements);
     }
 
     if (IsSupported(GLFeature::get_integer_indexed)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegeri_v", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, {{ "glGetIntegeri_v" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] ={
-            { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, { "GetIntegerIndexedvEXT", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, {{ "glGetIntegerIndexedvEXT" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::get_integer_indexed);
     }
 
     if (IsSupported(GLFeature::get_integer64_indexed)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetInteger64i_v, { "GetInteger64i_v", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetInteger64i_v, {{ "glGetInteger64i_v" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::get_integer64_indexed);
     }
 
     if (IsSupported(GLFeature::gpu_shader4)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetVertexAttribIiv, { "GetVertexAttribIiv", "GetVertexAttribIivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetVertexAttribIuiv, { "GetVertexAttribIuiv", "GetVertexAttribIuivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fVertexAttribI4i, { "VertexAttribI4i", "VertexAttribI4iEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fVertexAttribI4iv, { "VertexAttribI4iv","VertexAttribI4ivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fVertexAttribI4ui, { "VertexAttribI4ui", "VertexAttribI4uiEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fVertexAttribI4uiv, { "VertexAttribI4uiv", "VertexAttribI4uivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fVertexAttribIPointer, { "VertexAttribIPointer", "VertexAttribIPointerEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform1ui,  { "Uniform1ui", "Uniform1uiEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform2ui,  { "Uniform2ui", "Uniform2uiEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform3ui,  { "Uniform3ui", "Uniform3uiEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform4ui,  { "Uniform4ui", "Uniform4uiEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform1uiv, { "Uniform1uiv", "Uniform1uivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform2uiv, { "Uniform2uiv", "Uniform2uivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform3uiv, { "Uniform3uiv", "Uniform3uivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniform4uiv, { "Uniform4uiv", "Uniform4uivEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetFragDataLocation, { "GetFragDataLocation", "GetFragDataLocationEXT", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetUniformuiv, { "GetUniformuiv", "GetUniformuivEXT", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetVertexAttribIiv, {{ "glGetVertexAttribIiv", "glGetVertexAttribIivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fGetVertexAttribIuiv, {{ "glGetVertexAttribIuiv", "glGetVertexAttribIuivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribI4i, {{ "glVertexAttribI4i", "glVertexAttribI4iEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribI4iv, {{ "glVertexAttribI4iv", "glVertexAttribI4ivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribI4ui, {{ "glVertexAttribI4ui", "glVertexAttribI4uiEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribI4uiv, {{ "glVertexAttribI4uiv", "glVertexAttribI4uivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fVertexAttribIPointer, {{ "glVertexAttribIPointer", "glVertexAttribIPointerEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform1ui,  {{ "glUniform1ui", "glUniform1uiEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform2ui,  {{ "glUniform2ui", "glUniform2uiEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform3ui,  {{ "glUniform3ui", "glUniform3uiEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform4ui,  {{ "glUniform4ui", "glUniform4uiEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform1uiv, {{ "glUniform1uiv", "glUniform1uivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform2uiv, {{ "glUniform2uiv", "glUniform2uivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform3uiv, {{ "glUniform3uiv", "glUniform3uivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fUniform4uiv, {{ "glUniform4uiv", "glUniform4uivEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fGetFragDataLocation, {{ "glGetFragDataLocation", "glGetFragDataLocationEXT" }} },
+            { (PRFuncPtr*) &mSymbols.fGetUniformuiv, {{ "glGetUniformuiv", "glGetUniformuivEXT" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::gpu_shader4);
     }
 
     if (IsSupported(GLFeature::map_buffer_range)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fMapBufferRange, { "MapBufferRange", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fFlushMappedBufferRange, { "FlushMappedBufferRange", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUnmapBuffer, { "UnmapBuffer", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fMapBufferRange, {{ "glMapBufferRange" }} },
+            { (PRFuncPtr*) &mSymbols.fFlushMappedBufferRange, {{ "glFlushMappedBufferRange" }} },
+            { (PRFuncPtr*) &mSymbols.fUnmapBuffer, {{ "glUnmapBuffer" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::map_buffer_range);
     }
 
     if (IsSupported(GLFeature::texture_3D)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fTexImage3D, { "TexImage3D", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3D", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fTexImage3D, {{ "glTexImage3D" }} },
+            { (PRFuncPtr*) &mSymbols.fTexSubImage3D, {{ "glTexSubImage3D" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fTexSubImage3D, { "TexSubImage3DEXT", "TexSubImage3DOES", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fTexSubImage3D, {{ "glTexSubImage3DEXT", "glTexSubImage3DOES" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D);
     }
 
     if (IsSupported(GLFeature::texture_3D_compressed)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, { "CompressedTexImage3D", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, { "CompressedTexSubImage3D", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, {{ "glCompressedTexImage3D" }} },
+            { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, {{ "glCompressedTexSubImage3D" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, { "CompressedTexImage3DARB", "CompressedTexImage3DOES", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, { "CompressedTexSubImage3DARB", "CompressedTexSubImage3DOES", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, {{ "glCompressedTexImage3DARB", "glCompressedTexImage3DOES" }} },
+            { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, {{ "glCompressedTexSubImage3DARB", "glCompressedTexSubImage3DOES" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D_compressed);
     }
 
     if (IsSupported(GLFeature::texture_3D_copy)) {
         const SymLoadStruct coreSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fCopyTexSubImage3D, { "CopyTexSubImage3D", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fCopyTexSubImage3D, {{ "glCopyTexSubImage3D" }} },
             END_SYMBOLS
         };
         const SymLoadStruct extSymbols[] = {
-            { (PRFuncPtr*) &mSymbols.fCopyTexSubImage3D, { "CopyTexSubImage3DEXT", "CopyTexSubImage3DOES", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fCopyTexSubImage3D, {{ "glCopyTexSubImage3DEXT", "glCopyTexSubImage3DOES" }} },
             END_SYMBOLS
         };
         fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D_copy);
     }
 
     if (IsSupported(GLFeature::uniform_buffer_object)) {
         // Note: Don't query for glGetActiveUniformName because it is not
         // supported by GL ES 3.
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGetUniformIndices, { "GetUniformIndices", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetActiveUniformsiv, { "GetActiveUniformsiv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetUniformBlockIndex, { "GetUniformBlockIndex", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetActiveUniformBlockiv, { "GetActiveUniformBlockiv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetActiveUniformBlockName, { "GetActiveUniformBlockName", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniformBlockBinding, { "UniformBlockBinding", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGetUniformIndices, {{ "glGetUniformIndices" }} },
+            { (PRFuncPtr*) &mSymbols.fGetActiveUniformsiv, {{ "glGetActiveUniformsiv" }} },
+            { (PRFuncPtr*) &mSymbols.fGetUniformBlockIndex, {{ "glGetUniformBlockIndex" }} },
+            { (PRFuncPtr*) &mSymbols.fGetActiveUniformBlockiv, {{ "glGetActiveUniformBlockiv" }} },
+            { (PRFuncPtr*) &mSymbols.fGetActiveUniformBlockName, {{ "glGetActiveUniformBlockName" }} },
+            { (PRFuncPtr*) &mSymbols.fUniformBlockBinding, {{ "glUniformBlockBinding" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::uniform_buffer_object);
     }
 
     if (IsSupported(GLFeature::uniform_matrix_nonsquare)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fUniformMatrix2x3fv, { "UniformMatrix2x3fv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniformMatrix2x4fv, { "UniformMatrix2x4fv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniformMatrix3x2fv, { "UniformMatrix3x2fv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniformMatrix3x4fv, { "UniformMatrix3x4fv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniformMatrix4x2fv, { "UniformMatrix4x2fv", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fUniformMatrix4x3fv, { "UniformMatrix4x3fv", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fUniformMatrix2x3fv, {{ "glUniformMatrix2x3fv" }} },
+            { (PRFuncPtr*) &mSymbols.fUniformMatrix2x4fv, {{ "glUniformMatrix2x4fv" }} },
+            { (PRFuncPtr*) &mSymbols.fUniformMatrix3x2fv, {{ "glUniformMatrix3x2fv" }} },
+            { (PRFuncPtr*) &mSymbols.fUniformMatrix3x4fv, {{ "glUniformMatrix3x4fv" }} },
+            { (PRFuncPtr*) &mSymbols.fUniformMatrix4x2fv, {{ "glUniformMatrix4x2fv" }} },
+            { (PRFuncPtr*) &mSymbols.fUniformMatrix4x3fv, {{ "glUniformMatrix4x3fv" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::uniform_matrix_nonsquare);
     }
 
     if (IsSupported(GLFeature::internalformat_query)) {
         const SymLoadStruct symbols[] = {
             CORE_SYMBOL(GetInternalformativ),
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::internalformat_query);
     }
 
     if (IsSupported(GLFeature::invalidate_framebuffer)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fInvalidateFramebuffer,    { "InvalidateFramebuffer", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fInvalidateSubFramebuffer, { "InvalidateSubFramebuffer", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fInvalidateFramebuffer,    {{ "glInvalidateFramebuffer" }} },
+            { (PRFuncPtr*) &mSymbols.fInvalidateSubFramebuffer, {{ "glInvalidateSubFramebuffer" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::invalidate_framebuffer);
     }
 
     if (IsSupported(GLFeature::prim_restart)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex,    { "PrimitiveRestartIndex", "PrimitiveRestartIndexNV", nullptr } },
+            { (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex,    {{ "glPrimitiveRestartIndex", "glPrimitiveRestartIndexNV" }} },
             END_SYMBOLS
         };
         fnLoadForFeature(symbols, GLFeature::prim_restart);
     }
 
     if (IsExtensionSupported(KHR_debug)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fDebugMessageControl,  { "DebugMessageControl",  "DebugMessageControlKHR",  nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDebugMessageInsert,   { "DebugMessageInsert",   "DebugMessageInsertKHR",   nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDebugMessageCallback, { "DebugMessageCallback", "DebugMessageCallbackKHR", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetDebugMessageLog,   { "GetDebugMessageLog",   "GetDebugMessageLogKHR",   nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetPointerv,          { "GetPointerv",          "GetPointervKHR",          nullptr } },
-            { (PRFuncPtr*) &mSymbols.fPushDebugGroup,       { "PushDebugGroup",       "PushDebugGroupKHR",       nullptr } },
-            { (PRFuncPtr*) &mSymbols.fPopDebugGroup,        { "PopDebugGroup",        "PopDebugGroupKHR",        nullptr } },
-            { (PRFuncPtr*) &mSymbols.fObjectLabel,          { "ObjectLabel",          "ObjectLabelKHR",          nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetObjectLabel,       { "GetObjectLabel",       "GetObjectLabelKHR",       nullptr } },
-            { (PRFuncPtr*) &mSymbols.fObjectPtrLabel,       { "ObjectPtrLabel",       "ObjectPtrLabelKHR",       nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetObjectPtrLabel,    { "GetObjectPtrLabel",    "GetObjectPtrLabelKHR",    nullptr } },
+            { (PRFuncPtr*) &mSymbols.fDebugMessageControl,  {{ "glDebugMessageControl",  "glDebugMessageControlKHR", }} },
+            { (PRFuncPtr*) &mSymbols.fDebugMessageInsert,   {{ "glDebugMessageInsert",   "glDebugMessageInsertKHR",  }} },
+            { (PRFuncPtr*) &mSymbols.fDebugMessageCallback, {{ "glDebugMessageCallback", "glDebugMessageCallbackKHR" }} },
+            { (PRFuncPtr*) &mSymbols.fGetDebugMessageLog,   {{ "glGetDebugMessageLog",   "glGetDebugMessageLogKHR",  }} },
+            { (PRFuncPtr*) &mSymbols.fGetPointerv,          {{ "glGetPointerv",          "glGetPointervKHR",         }} },
+            { (PRFuncPtr*) &mSymbols.fPushDebugGroup,       {{ "glPushDebugGroup",       "glPushDebugGroupKHR",      }} },
+            { (PRFuncPtr*) &mSymbols.fPopDebugGroup,        {{ "glPopDebugGroup",        "glPopDebugGroupKHR",       }} },
+            { (PRFuncPtr*) &mSymbols.fObjectLabel,          {{ "glObjectLabel",          "glObjectLabelKHR",         }} },
+            { (PRFuncPtr*) &mSymbols.fGetObjectLabel,       {{ "glGetObjectLabel",       "glGetObjectLabelKHR",      }} },
+            { (PRFuncPtr*) &mSymbols.fObjectPtrLabel,       {{ "glObjectPtrLabel",       "glObjectPtrLabelKHR",      }} },
+            { (PRFuncPtr*) &mSymbols.fGetObjectPtrLabel,    {{ "glGetObjectPtrLabel",    "glGetObjectPtrLabelKHR",   }} },
             END_SYMBOLS
         };
         fnLoadForExt(symbols, KHR_debug);
     }
 
     if (IsExtensionSupported(NV_fence)) {
         const SymLoadStruct symbols[] = {
-            { (PRFuncPtr*) &mSymbols.fGenFences,    { "GenFencesNV",    nullptr } },
-            { (PRFuncPtr*) &mSymbols.fDeleteFences, { "DeleteFencesNV", nullptr } },
-            { (PRFuncPtr*) &mSymbols.fSetFence,     { "SetFenceNV",     nullptr } },
-            { (PRFuncPtr*) &mSymbols.fTestFence,    { "TestFenceNV",    nullptr } },
-            { (PRFuncPtr*) &mSymbols.fFinishFence,  { "FinishFenceNV",  nullptr } },
-            { (PRFuncPtr*) &mSymbols.fIsFence,      { "IsFenceNV",      nullptr } },
-            { (PRFuncPtr*) &mSymbols.fGetFenceiv,   { "GetFenceivNV",   nullptr } },
+            { (PRFuncPtr*) &mSymbols.fGenFences,    {{ "glGenFencesNV"    }} },
+            { (PRFuncPtr*) &mSymbols.fDeleteFences, {{ "glDeleteFencesNV" }} },
+            { (PRFuncPtr*) &mSymbols.fSetFence,     {{ "glSetFenceNV"     }} },
+            { (PRFuncPtr*) &mSymbols.fTestFence,    {{ "glTestFenceNV"    }} },
+            { (PRFuncPtr*) &mSymbols.fFinishFence,  {{ "glFinishFenceNV"  }} },
+            { (PRFuncPtr*) &mSymbols.fIsFence,      {{ "glIsFenceNV"      }} },
+            { (PRFuncPtr*) &mSymbols.fGetFenceiv,   {{ "glGetFenceivNV"   }} },
             END_SYMBOLS
         };
         fnLoadForExt(symbols, NV_fence);
     }
 
   // clang-format on
 
   if (IsExtensionSupported(NV_texture_barrier)) {
     const SymLoadStruct symbols[] = {
-        {(PRFuncPtr*)&mSymbols.fTextureBarrier, {"TextureBarrierNV", nullptr}},
+        {(PRFuncPtr*)&mSymbols.fTextureBarrier, {{"glTextureBarrierNV"}}},
         END_SYMBOLS};
     fnLoadForExt(symbols, NV_texture_barrier);
   }
 
   if (IsSupported(GLFeature::read_buffer)) {
     const SymLoadStruct symbols[] = {CORE_SYMBOL(ReadBuffer), END_SYMBOLS};
     fnLoadForFeature(symbols, GLFeature::read_buffer);
   }
@@ -1447,17 +1443,17 @@ void GLContext::LoadMoreSymbols(const ch
     fnLoadForExt(symbols, APPLE_framebuffer_multisample);
   }
 
   // Load developer symbols, don't fail if we can't find them.
   const SymLoadStruct devSymbols[] = {CORE_SYMBOL(GetTexImage),
                                       CORE_SYMBOL(GetTexLevelParameteriv),
                                       END_SYMBOLS};
   const bool warnOnFailures = ShouldSpew();
-  LoadSymbols(devSymbols, trygl, prefix, warnOnFailures);
+  loader.LoadSymbols(devSymbols, warnOnFailures);
 }
 
 #undef CORE_SYMBOL
 #undef CORE_EXT_SYMBOL2
 #undef EXT_SYMBOL2
 #undef EXT_SYMBOL3
 #undef END_SYMBOLS
 
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -186,18 +186,17 @@ enum class GLRenderer {
   Tegra,
   AndroidEmulator,
   GalliumLlvmpipe,
   IntelHD3000,
   MicrosoftBasicRenderDriver,
   Other
 };
 
-class GLContext : public GLLibraryLoader,
-                  public GenericAtomicRefCounted,
+class GLContext : public GenericAtomicRefCounted,
                   public SupportsWeakPtr<GLContext> {
  public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(GLContext)
   static MOZ_THREAD_LOCAL(uintptr_t) sCurrentContext;
 
   bool mImplicitMakeCurrent = false;
   bool mUseTLSIsCurrent;
 
@@ -3306,18 +3305,18 @@ class GLContext : public GLLibraryLoader
 #undef AFTER_GL_CALL
 #undef ASSERT_SYMBOL_PRESENT
 // #undef TRACKING_CONTEXT // Needed in GLContext.cpp
 #undef ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL
 
   // -----------------------------------------------------------------------------
   // Constructor
  protected:
-  explicit GLContext(CreateContextFlags flags, const SurfaceCaps& caps,
-                     GLContext* sharedContext = nullptr,
+  explicit GLContext(CreateContextFlags flags,
+                     const SurfaceCaps& caps, GLContext* sharedContext = nullptr,
                      bool isOffscreen = false, bool canUseTLSIsCurrent = false);
 
   // -----------------------------------------------------------------------------
   // Destructor
  public:
   virtual ~GLContext();
 
   // Mark this context as destroyed.  This will nullptr out all
@@ -3328,20 +3327,16 @@ class GLContext : public GLLibraryLoader
   virtual void OnMarkDestroyed() {}
 
   // -----------------------------------------------------------------------------
   // Everything that isn't standard GL APIs
  protected:
   typedef gfx::SurfaceFormat SurfaceFormat;
 
  public:
-  virtual bool Init() = 0;
-
-  virtual bool SetupLookupFunction() = 0;
-
   virtual void ReleaseSurface() {}
 
   bool IsDestroyed() const {
     // MarkDestroyed will mark all these as null.
     return mContextLost && mSymbols.fUseProgram == nullptr;
   }
 
   GLContext* GetSharedContext() { return mSharedContext; }
@@ -3366,16 +3361,18 @@ class GLContext : public GLLibraryLoader
    * Defines a two-dimensional texture image for context target surface
    */
   virtual bool BindTexImage() { return false; }
   /*
    * Releases a color buffer that is being used as a texture
    */
   virtual bool ReleaseTexImage() { return false; }
 
+  virtual Maybe<SymbolLoader> GetSymbolLoader() const = 0;
+
   // Before reads from offscreen texture
   void GuaranteeResolve();
 
   /*
    * Resize the current offscreen buffer.  Returns true on success.
    * If it returns false, the context should be treated as unusable
    * and should be recreated.  After the resize, the viewport is not
    * changed; glViewport should be called as appropriate.
@@ -3559,26 +3556,24 @@ class GLContext : public GLLibraryLoader
   GLScreenBuffer* Screen() const { return mScreen.get(); }
 
   bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }
 
   bool IsDrawingToDefaultFramebuffer();
 
   bool IsOffscreenSizeAllowed(const gfx::IntSize& aSize) const;
 
- protected:
-  bool InitWithPrefix(const char* prefix, bool trygl);
+  virtual bool Init();
 
  private:
-  bool InitWithPrefixImpl(const char* prefix, bool trygl);
-  void LoadMoreSymbols(const char* prefix, bool trygl);
-  bool LoadExtSymbols(const char* prefix, bool trygl, const SymLoadStruct* list,
+  bool InitImpl();
+  void LoadMoreSymbols(const SymbolLoader& loader);
+  bool LoadExtSymbols(const SymbolLoader& loader, const SymLoadStruct* list,
                       GLExtensions ext);
-  bool LoadFeatureSymbols(const char* prefix, bool trygl,
-                          const SymLoadStruct* list, GLFeature feature);
+  bool LoadFeatureSymbols(const SymbolLoader& loader, const SymLoadStruct* list, GLFeature feature);
 
  protected:
   void InitExtensions();
 
   GLint mViewportRect[4] = {};
   GLint mScissorRect[4] = {};
 
   uint32_t mMaxTexOrRbSize = 0;
--- a/gfx/gl/GLContextCGL.h
+++ b/gfx/gl/GLContextCGL.h
@@ -36,32 +36,30 @@ class GLContextCGL : public GLContext {
     return GLContextType::CGL;
   }
 
   static GLContextCGL* Cast(GLContext* gl) {
     MOZ_ASSERT(gl->GetContextType() == GLContextType::CGL);
     return static_cast<GLContextCGL*>(gl);
   }
 
-  bool Init() override;
-
   NSOpenGLContext* GetNSOpenGLContext() const { return mContext; }
   CGLContextObj GetCGLContext() const;
 
   virtual bool MakeCurrentImpl() const override;
 
   virtual bool IsCurrentImpl() const override;
 
   virtual GLenum GetPreferredARGB32Format() const override;
 
-  virtual bool SetupLookupFunction() override;
-
   virtual bool IsDoubleBuffered() const override;
 
   virtual bool SwapBuffers() override;
 
   virtual void GetWSIInfo(nsCString* const out) const override;
+
+  Maybe<SymbolLoader> GetSymbolLoader() const override;
 };
 
 }  // namespace gl
 }  // namespace mozilla
 
 #endif  // GLCONTEXTCGL_H_
--- a/gfx/gl/GLContextEAGL.h
+++ b/gfx/gl/GLContextEAGL.h
@@ -32,27 +32,25 @@ class GLContextEAGL : public GLContext {
     return GLContextType::EAGL;
   }
 
   static GLContextEAGL* Cast(GLContext* gl) {
     MOZ_ASSERT(gl->GetContextType() == GLContextType::EAGL);
     return static_cast<GLContextEAGL*>(gl);
   }
 
-  bool Init() override;
-
   bool AttachToWindow(nsIWidget* aWidget);
 
   EAGLContext* GetEAGLContext() const { return mContext; }
 
   virtual bool MakeCurrentImpl() const override;
 
   virtual bool IsCurrentImpl() const override;
 
-  virtual bool SetupLookupFunction() override;
+  Maybe<SymbolLoader> GetSymbolLoader() const override;
 
   virtual bool IsDoubleBuffered() const override;
 
   virtual bool SwapBuffers() override;
 
   virtual void GetWSIInfo(nsCString* const out) const override;
 
   virtual GLuint GetDefaultFramebuffer() override { return mBackbufferFB; }
--- a/gfx/gl/GLContextEGL.h
+++ b/gfx/gl/GLContextEGL.h
@@ -62,17 +62,17 @@ class GLContextEGL : public GLContext {
   virtual bool MakeCurrentImpl() const override;
 
   virtual bool IsCurrentImpl() const override;
 
   virtual bool RenewSurface(widget::CompositorWidget* aWidget) override;
 
   virtual void ReleaseSurface() override;
 
-  virtual bool SetupLookupFunction() override;
+  Maybe<SymbolLoader> GetSymbolLoader() const override;
 
   virtual bool SwapBuffers() override;
 
   virtual void GetWSIInfo(nsCString* const out) const override;
 
   // hold a reference to the given surface
   // for the lifetime of this context.
   void HoldSurface(gfxASurface* aSurf);
--- a/gfx/gl/GLContextGLX.h
+++ b/gfx/gl/GLContextGLX.h
@@ -45,17 +45,17 @@ class GLContextGLX : public GLContext {
   }
 
   bool Init() override;
 
   virtual bool MakeCurrentImpl() const override;
 
   virtual bool IsCurrentImpl() const override;
 
-  virtual bool SetupLookupFunction() override;
+  Maybe<SymbolLoader> GetSymbolLoader() const override;
 
   virtual bool IsDoubleBuffered() const override;
 
   virtual bool SwapBuffers() override;
 
   virtual void GetWSIInfo(nsCString* const out) const override;
 
   // Overrides the current GLXDrawable backing the context and makes the
--- a/gfx/gl/GLContextProviderCGL.mm
+++ b/gfx/gl/GLContextProviderCGL.mm
@@ -51,16 +51,18 @@ class CGLLibrary {
     return true;
   }
 
   bool UseDoubleBufferedWindows() const {
     MOZ_ASSERT(mInitialized);
     return mUseDoubleBufferedWindows;
   }
 
+  const auto& Library() const { return mOGLLibrary; }
+
  private:
   bool mInitialized = false;
   bool mUseDoubleBufferedWindows = true;
   PRLibrary* mOGLLibrary = nullptr;
 };
 
 CGLLibrary sCGLLibrary;
 
@@ -77,22 +79,16 @@ GLContextCGL::~GLContextCGL() {
       // this, the next time we call [NSOpenGLContext currentContext],
       // "invalid context" will be printed to the console.
       [NSOpenGLContext clearCurrentContext];
     }
     [mContext release];
   }
 }
 
-bool GLContextCGL::Init() {
-  if (!InitWithPrefix("gl", true)) return false;
-
-  return true;
-}
-
 CGLContextObj GLContextCGL::GetCGLContext() const {
   return static_cast<CGLContextObj>([mContext CGLContextObj]);
 }
 
 bool GLContextCGL::MakeCurrentImpl() const {
   if (mContext) {
     [mContext makeCurrentContext];
     MOZ_ASSERT(IsCurrentImpl());
@@ -107,29 +103,33 @@ bool GLContextCGL::MakeCurrentImpl() con
   }
   return true;
 }
 
 bool GLContextCGL::IsCurrentImpl() const { return [NSOpenGLContext currentContext] == mContext; }
 
 GLenum GLContextCGL::GetPreferredARGB32Format() const { return LOCAL_GL_BGRA; }
 
-bool GLContextCGL::SetupLookupFunction() { return false; }
-
 bool GLContextCGL::IsDoubleBuffered() const { return sCGLLibrary.UseDoubleBufferedWindows(); }
 
 bool GLContextCGL::SwapBuffers() {
   AUTO_PROFILER_LABEL("GLContextCGL::SwapBuffers", GRAPHICS);
 
   [mContext flushBuffer];
   return true;
 }
 
 void GLContextCGL::GetWSIInfo(nsCString* const out) const { out->AppendLiteral("CGL"); }
 
+Maybe<SymbolLoader> GLContextCGL::GetSymbolLoader() const
+{
+    const auto& lib = sCGLLibrary.Library();
+    return Some(SymbolLoader(*lib));
+}
+
 already_AddRefed<GLContext> GLContextProviderCGL::CreateWrappingExisting(void*, void*) {
   return nullptr;
 }
 
 static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered[] = {
     NSOpenGLPFAAllowOfflineRenderers, 0};
 
 static const NSOpenGLPixelFormatAttribute kAttribs_singleBuffered_accel[] = {
--- a/gfx/gl/GLContextProviderEAGL.mm
+++ b/gfx/gl/GLContextProviderEAGL.mm
@@ -44,22 +44,16 @@ GLContextEAGL::~GLContextEAGL() {
   }
 
   if (mContext) {
     [EAGLContext setCurrentContext:nil];
     [mContext release];
   }
 }
 
-bool GLContextEAGL::Init() {
-  if (!InitWithPrefix("gl", true)) return false;
-
-  return true;
-}
-
 bool GLContextEAGL::AttachToWindow(nsIWidget* aWidget) {
   // This should only be called once
   MOZ_ASSERT(!mBackbufferFB && !mBackbufferRB);
 
   UIView* view = reinterpret_cast<UIView*>(aWidget->GetNativeData(NS_NATIVE_WIDGET));
 
   if (!view) {
     MOZ_CRASH("no view!");
@@ -101,17 +95,28 @@ bool GLContextEAGL::MakeCurrentImpl() co
       return false;
     }
   }
   return true;
 }
 
 bool GLContextEAGL::IsCurrentImpl() const { return [EAGLContext currentContext] == mContext; }
 
-bool GLContextEAGL::SetupLookupFunction() { return false; }
+static PRFuncPtr GLAPIENTRY GetLoadedProcAddress(const char* const name) {
+  PRLibrary* lib = nullptr;
+  const auto& ret = PR_FindFunctionSymbolAndLibrary(name, &leakedLibRef);
+  if (lib) {
+    PR_UnloadLibrary(lib);
+  }
+  return ret;
+}
+
+Maybe<SymbolLoader> GLContextEAGL::GetSymbolLoader() const {
+  return Some(SymbolLoader(&GetLoadedProcAddress));
+}
 
 bool GLContextEAGL::IsDoubleBuffered() const { return true; }
 
 bool GLContextEAGL::SwapBuffers() {
   AUTO_PROFILER_LABEL("GLContextEAGL::SwapBuffers", GRAPHICS);
 
   [mContext presentRenderbuffer:LOCAL_GL_RENDERBUFFER];
   return true;
--- a/gfx/gl/GLContextProviderEGL.cpp
+++ b/gfx/gl/GLContextProviderEGL.cpp
@@ -361,32 +361,17 @@ GLContextEGL::~GLContextEGL() {
 
   mEgl->fDestroyContext(EGL_DISPLAY(), mContext);
 
   mozilla::gl::DestroySurface(mSurface);
   mozilla::gl::DestroySurface(mFallbackSurface);
 }
 
 bool GLContextEGL::Init() {
-#if defined(ANDROID)
-  // We can't use LoadApitraceLibrary here because the GLContext
-  // expects its own handle to the GL library
-  if (!OpenLibrary(APITRACE_LIB))
-#endif
-    if (!OpenLibrary(GLES2_LIB)) {
-#if defined(XP_UNIX)
-      if (!OpenLibrary(GLES2_LIB2)) {
-        NS_WARNING("Couldn't load GLES2 LIB.");
-        return false;
-      }
-#endif
-    }
-
-  SetupLookupFunction();
-  if (!InitWithPrefix("gl", true)) return false;
+  if (!GLContext::Init()) return false;
 
   bool current = MakeCurrent();
   if (!current) {
     gfx::LogFailure(
         NS_LITERAL_CSTRING("Couldn't get device attachments for device."));
     return false;
   }
 
@@ -496,19 +481,18 @@ void GLContextEGL::ReleaseSurface() {
     mozilla::gl::DestroySurface(mSurface);
   }
   if (mSurface == mSurfaceOverride) {
     mSurfaceOverride = EGL_NO_SURFACE;
   }
   mSurface = EGL_NO_SURFACE;
 }
 
-bool GLContextEGL::SetupLookupFunction() {
-  mLookupFunc = mEgl->GetLookupFunction();
-  return true;
+Maybe<SymbolLoader> GLContextEGL::GetSymbolLoader() const {
+  return mEgl->GetSymbolLoader();
 }
 
 bool GLContextEGL::SwapBuffers() {
   EGLSurface surface =
       mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface;
   if (surface) {
     return mEgl->fSwapBuffers(EGL_DISPLAY(), surface);
   } else {
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -69,53 +69,52 @@ bool GLXLibrary::EnsureInitialized() {
     return false;
   }
   mTriedInitializing = true;
 
   // Force enabling s3 texture compression. (Bug 774134)
   PR_SetEnv("force_s3tc_enable=true");
 
   if (!mOGLLibrary) {
-    const char* libGLfilename = nullptr;
-    bool forceFeatureReport = false;
-
     // see e.g. bug 608526: it is intrinsically interesting to know whether we
     // have dynamically linked to libGL.so.1 because at least the NVIDIA
     // implementation requires an executable stack, which causes mprotect calls,
     // which trigger glibc bug
     // http://sourceware.org/bugzilla/show_bug.cgi?id=12225
+    const char* libGLfilename = "libGL.so.1";
 #ifdef __OpenBSD__
     libGLfilename = "libGL.so";
-#else
-    libGLfilename = "libGL.so.1";
 #endif
 
+    const bool forceFeatureReport = false;
     ScopedGfxFeatureReporter reporter(libGLfilename, forceFeatureReport);
     mOGLLibrary = PR_LoadLibrary(libGLfilename);
     if (!mOGLLibrary) {
       NS_WARNING("Couldn't load OpenGL shared library.");
       return false;
     }
     reporter.SetSuccessful();
   }
 
   if (gfxEnv::GlxDebug()) {
     mDebug = true;
   }
 
-#define SYMBOL(X)                                     \
-  {                                                   \
-    (PRFuncPtr*)&mSymbols.f##X, { "glX" #X, nullptr } \
+#define SYMBOL(X)                 \
+  {                               \
+    (PRFuncPtr*)&mSymbols.f##X, { \
+      { "glX" #X }                \
+    }                             \
   }
-#define END_OF_SYMBOLS   \
-  {                      \
-    nullptr, { nullptr } \
+#define END_OF_SYMBOLS \
+  {                    \
+    nullptr, {}        \
   }
 
-  const GLLibraryLoader::SymLoadStruct symbols[] = {
+  const SymLoadStruct symbols[] = {
       /* functions that were in GLX 1.0 */
       SYMBOL(DestroyContext),
       SYMBOL(MakeCurrent),
       SYMBOL(SwapBuffers),
       SYMBOL(QueryVersion),
       SYMBOL(GetConfig),
       SYMBOL(GetCurrentContext),
       SYMBOL(WaitGL),
@@ -132,57 +131,57 @@ bool GLXLibrary::EnsureInitialized() {
       SYMBOL(GetFBConfigAttrib),
       SYMBOL(GetFBConfigs),
       SYMBOL(CreatePixmap),
       SYMBOL(DestroyPixmap),
       SYMBOL(CreateNewContext),
 
       // Core in GLX 1.4, ARB extension before.
       {(PRFuncPtr*)&mSymbols.fGetProcAddress,
-       {"glXGetProcAddress", "glXGetProcAddressARB", nullptr}},
+       {{"glXGetProcAddress", "glXGetProcAddressARB"}}},
       END_OF_SYMBOLS};
-  if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols)) {
-    NS_WARNING("Couldn't load required GLX symbols.");
-    return false;
+
+  {
+    const SymbolLoader libLoader(*mOGLLibrary);
+    if (!libLoader.LoadSymbols(symbols)) {
+      NS_WARNING("Couldn't load required GLX symbols.");
+      return false;
+    }
   }
+  const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
 
   Display* display = DefaultXDisplay();
   int screen = DefaultScreen(display);
 
   {
     int major, minor;
     if (!fQueryVersion(display, &major, &minor) || major != 1 || minor < 3) {
       NS_ERROR("GLX version older than 1.3. (released in 1998)");
       return false;
     }
   }
 
-  const GLLibraryLoader::SymLoadStruct symbols_texturefrompixmap[] = {
+  const SymLoadStruct symbols_texturefrompixmap[] = {
       SYMBOL(BindTexImageEXT), SYMBOL(ReleaseTexImageEXT), END_OF_SYMBOLS};
 
-  const GLLibraryLoader::SymLoadStruct symbols_createcontext[] = {
+  const SymLoadStruct symbols_createcontext[] = {
       SYMBOL(CreateContextAttribsARB), END_OF_SYMBOLS};
 
-  const GLLibraryLoader::SymLoadStruct symbols_videosync[] = {
+  const SymLoadStruct symbols_videosync[] = {
       SYMBOL(GetVideoSyncSGI), SYMBOL(WaitVideoSyncSGI), END_OF_SYMBOLS};
 
-  const GLLibraryLoader::SymLoadStruct symbols_swapcontrol[] = {
-      SYMBOL(SwapIntervalEXT), END_OF_SYMBOLS};
-
-  const auto lookupFunction =
-      (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
+  const SymLoadStruct symbols_swapcontrol[] = {SYMBOL(SwapIntervalEXT),
+                                               END_OF_SYMBOLS};
 
-  const auto fnLoadSymbols =
-      [&](const GLLibraryLoader::SymLoadStruct* symbols) {
-        if (GLLibraryLoader::LoadSymbols(mOGLLibrary, symbols, lookupFunction))
-          return true;
+  const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
+    if (pfnLoader.LoadSymbols(symbols)) return true;
 
-        GLLibraryLoader::ClearSymbols(symbols);
-        return false;
-      };
+    ClearSymbols(symbols);
+    return false;
+  };
 
   const char* clientVendor = fGetClientString(display, LOCAL_GLX_VENDOR);
   const char* serverVendor =
       fQueryServerString(display, screen, LOCAL_GLX_VENDOR);
   const char* extensionsStr = fQueryExtensionsString(display, screen);
 
   if (HasExtension(extensionsStr, "GLX_EXT_texture_from_pixmap") &&
       fnLoadSymbols(symbols_texturefrompixmap)) {
@@ -566,18 +565,17 @@ GLContextGLX::~GLContextGLX() {
   mGLX->fDestroyContext(mDisplay, mContext);
 
   if (mDeleteDrawable) {
     mGLX->fDestroyPixmap(mDisplay, mDrawable);
   }
 }
 
 bool GLContextGLX::Init() {
-  SetupLookupFunction();
-  if (!InitWithPrefix("gl", true)) {
+  if (!GLContext::Init()) {
     return false;
   }
 
   // EXT_framebuffer_object is not supported on Core contexts
   // so we'll also check for ARB_framebuffer_object
   if (!IsExtensionSupported(EXT_framebuffer_object) &&
       !IsSupported(GLFeature::framebuffer_object))
     return false;
@@ -604,19 +602,19 @@ bool GLContextGLX::MakeCurrentImpl() con
   }
   return succeeded;
 }
 
 bool GLContextGLX::IsCurrentImpl() const {
   return mGLX->fGetCurrentContext() == mContext;
 }
 
-bool GLContextGLX::SetupLookupFunction() {
-  mLookupFunc = (PlatformLookupFunction)sGLXLibrary.GetGetProcAddress();
-  return true;
+Maybe<SymbolLoader> GLContextGLX::GetSymbolLoader() const {
+  const auto pfn = sGLXLibrary.GetGetProcAddress();
+  return Some(SymbolLoader(pfn));
 }
 
 bool GLContextGLX::IsDoubleBuffered() const { return mDoubleBuffered; }
 
 bool GLContextGLX::SwapBuffers() {
   if (!mDoubleBuffered) return false;
   mGLX->fSwapBuffers(mDisplay, mDrawable);
   return true;
--- a/gfx/gl/GLContextProviderWGL.cpp
+++ b/gfx/gl/GLContextProviderWGL.cpp
@@ -63,16 +63,22 @@ static HWND CreateDummyWindow() {
 }
 
 static inline bool HasExtension(const char* aExtensions,
                                 const char* aRequiredExtension) {
   return GLContext::ListHasExtension(
       reinterpret_cast<const GLubyte*>(aExtensions), aRequiredExtension);
 }
 
+SymbolLoader WGLLibrary::GetSymbolLoader() const {
+  auto ret = SymbolLoader(*mOGLLibrary);
+  ret.mPfn = SymbolLoader::GetProcAddressT(mSymbols.fGetProcAddress);
+  return ret;
+}
+
 bool WGLLibrary::EnsureInitialized() {
   if (mInitialized) return true;
 
   mozilla::ScopedGfxFeatureReporter reporter("WGL");
 
   std::wstring libGLFilename = L"Opengl32.dll";
   // SU_SPIES_DIRECTORY is for AMD CodeXL/gDEBugger
   if (_wgetenv(L"SU_SPIES_DIRECTORY")) {
@@ -83,34 +89,39 @@ bool WGLLibrary::EnsureInitialized() {
   if (!mOGLLibrary) {
     mOGLLibrary = LoadLibraryWithFlags(libGLFilename.c_str());
     if (!mOGLLibrary) {
       NS_WARNING("Couldn't load OpenGL library.");
       return false;
     }
   }
 
-#define SYMBOL(X)                                     \
-  {                                                   \
-    (PRFuncPtr*)&mSymbols.f##X, { "wgl" #X, nullptr } \
+#define SYMBOL(X)                 \
+  {                               \
+    (PRFuncPtr*)&mSymbols.f##X, { \
+      { "wgl" #X }                \
+    }                             \
   }
-#define END_OF_SYMBOLS   \
-  {                      \
-    nullptr, { nullptr } \
-  }
+#define END_OF_SYMBOLS {nullptr, {}}
 
-  const GLLibraryLoader::SymLoadStruct earlySymbols[] = {
-      SYMBOL(CreateContext), SYMBOL(MakeCurrent),       SYMBOL(GetProcAddress),
-      SYMBOL(DeleteContext), SYMBOL(GetCurrentContext), SYMBOL(GetCurrentDC),
-      END_OF_SYMBOLS};
+  {
+    const auto loader = SymbolLoader(*mOGLLibrary);
+    const SymLoadStruct earlySymbols[] = {SYMBOL(CreateContext),
+                                          SYMBOL(MakeCurrent),
+                                          SYMBOL(GetProcAddress),
+                                          SYMBOL(DeleteContext),
+                                          SYMBOL(GetCurrentContext),
+                                          SYMBOL(GetCurrentDC),
+                                          END_OF_SYMBOLS};
 
-  if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, &earlySymbols[0])) {
-    NS_WARNING(
-        "Couldn't find required entry points in OpenGL DLL (early init)");
-    return false;
+    if (!loader.LoadSymbols(earlySymbols)) {
+      NS_WARNING(
+          "Couldn't find required entry points in OpenGL DLL (early init)");
+      return false;
+    }
   }
 
   mDummyWindow = CreateDummyWindow();
   MOZ_ASSERT(mDummyWindow);
   if (!mDummyWindow) return false;
   auto cleanup = MakeScopeExit([&]() { Reset(); });
 
   mRootDc = GetDC(mDummyWindow);
@@ -151,53 +162,56 @@ bool WGLLibrary::EnsureInitialized() {
 
   if (!mSymbols.fMakeCurrent(mRootDc, mDummyGlrc)) {
     NS_WARNING("wglMakeCurrent failed");
     return false;
   }
   const auto resetContext =
       MakeScopeExit([&]() { mSymbols.fMakeCurrent(curDC, curCtx); });
 
-  const auto lookupFunc =
-      (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
+  const auto loader = [&]() {
+    const auto pfn = SymbolLoader::GetProcAddressT(mSymbols.fGetProcAddress);
+    auto ret = SymbolLoader(pfn);
+    ret.mLib = mOGLLibrary;
+    return ret;
+  }();
 
   // Now we can grab all the other symbols that we couldn't without having
   // a context current.
   // clang-format off
-    const GLLibraryLoader::SymLoadStruct reqExtSymbols[] = {
-        { (PRFuncPtr*)&mSymbols.fCreatePbuffer, { "wglCreatePbufferARB", "wglCreatePbufferEXT", nullptr } },
-        { (PRFuncPtr*)&mSymbols.fDestroyPbuffer, { "wglDestroyPbufferARB", "wglDestroyPbufferEXT", nullptr } },
-        { (PRFuncPtr*)&mSymbols.fGetPbufferDC, { "wglGetPbufferDCARB", "wglGetPbufferDCEXT", nullptr } },
-        { (PRFuncPtr*)&mSymbols.fReleasePbufferDC, { "wglReleasePbufferDCARB", "wglReleasePbufferDCEXT", nullptr } },
-    //    { (PRFuncPtr*)&mSymbols.fBindTexImage, { "wglBindTexImageARB", "wglBindTexImageEXT", nullptr } },
-    //    { (PRFuncPtr*)&mSymbols.fReleaseTexImage, { "wglReleaseTexImageARB", "wglReleaseTexImageEXT", nullptr } },
-        { (PRFuncPtr*)&mSymbols.fChoosePixelFormat, { "wglChoosePixelFormatARB", "wglChoosePixelFormatEXT", nullptr } },
-    //    { (PRFuncPtr*)&mSymbols.fGetPixelFormatAttribiv, { "wglGetPixelFormatAttribivARB", "wglGetPixelFormatAttribivEXT", nullptr } },
+    const SymLoadStruct reqExtSymbols[] = {
+        { (PRFuncPtr*)&mSymbols.fCreatePbuffer, {{ "wglCreatePbufferARB", "wglCreatePbufferEXT" }} },
+        { (PRFuncPtr*)&mSymbols.fDestroyPbuffer, {{ "wglDestroyPbufferARB", "wglDestroyPbufferEXT" }} },
+        { (PRFuncPtr*)&mSymbols.fGetPbufferDC, {{ "wglGetPbufferDCARB", "wglGetPbufferDCEXT" }} },
+        { (PRFuncPtr*)&mSymbols.fReleasePbufferDC, {{ "wglReleasePbufferDCARB", "wglReleasePbufferDCEXT" }} },
+    //    { (PRFuncPtr*)&mSymbols.fBindTexImage, {{ "wglBindTexImageARB", "wglBindTexImageEXT" }} },
+    //    { (PRFuncPtr*)&mSymbols.fReleaseTexImage, {{ "wglReleaseTexImageARB", "wglReleaseTexImageEXT" }} },
+        { (PRFuncPtr*)&mSymbols.fChoosePixelFormat, {{ "wglChoosePixelFormatARB", "wglChoosePixelFormatEXT" }} },
+    //    { (PRFuncPtr*)&mSymbols.fGetPixelFormatAttribiv, {{ "wglGetPixelFormatAttribivARB", "wglGetPixelFormatAttribivEXT" }} },
         SYMBOL(GetExtensionsStringARB),
         END_OF_SYMBOLS
     };
   // clang-format on
-  if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, reqExtSymbols, lookupFunc)) {
+  if (!loader.LoadSymbols(reqExtSymbols)) {
     NS_WARNING("reqExtSymbols missing");
     return false;
   }
 
   // --
 
   const auto extString = mSymbols.fGetExtensionsStringARB(mRootDc);
   MOZ_ASSERT(extString);
   MOZ_ASSERT(HasExtension(extString, "WGL_ARB_extensions_string"));
 
   // --
 
   if (HasExtension(extString, "WGL_ARB_create_context")) {
-    const GLLibraryLoader::SymLoadStruct createContextSymbols[] = {
+    const SymLoadStruct createContextSymbols[] = {
         SYMBOL(CreateContextAttribsARB), END_OF_SYMBOLS};
-    if (GLLibraryLoader::LoadSymbols(mOGLLibrary, createContextSymbols,
-                                     lookupFunc)) {
+    if (loader.LoadSymbols(createContextSymbols)) {
       if (HasExtension(extString, "WGL_ARB_create_context_robustness")) {
         mHasRobustness = true;
       }
     } else {
       NS_ERROR(
           "WGL_ARB_create_context announced without supplying its functions.");
       ClearSymbols(createContextSymbols);
     }
@@ -206,28 +220,27 @@ bool WGLLibrary::EnsureInitialized() {
   // --
 
   bool hasDXInterop2 = HasExtension(extString, "WGL_NV_DX_interop2");
   if (gfxVars::DXInterop2Blocked() && !gfxPrefs::IgnoreDXInterop2Blacklist()) {
     hasDXInterop2 = false;
   }
 
   if (hasDXInterop2) {
-    const GLLibraryLoader::SymLoadStruct dxInteropSymbols[] = {
+    const SymLoadStruct dxInteropSymbols[] = {
         SYMBOL(DXSetResourceShareHandleNV),
         SYMBOL(DXOpenDeviceNV),
         SYMBOL(DXCloseDeviceNV),
         SYMBOL(DXRegisterObjectNV),
         SYMBOL(DXUnregisterObjectNV),
         SYMBOL(DXObjectAccessNV),
         SYMBOL(DXLockObjectsNV),
         SYMBOL(DXUnlockObjectsNV),
         END_OF_SYMBOLS};
-    if (!GLLibraryLoader::LoadSymbols(mOGLLibrary, dxInteropSymbols,
-                                      lookupFunc)) {
+    if (!loader.LoadSymbols(dxInteropSymbols)) {
       NS_ERROR(
           "WGL_NV_DX_interop2 announceed without supplying its functions.");
       ClearSymbols(dxInteropSymbols);
     }
   }
 
   // --
 
@@ -287,28 +300,16 @@ GLContextWGL::~GLContextWGL() {
     (void)sWGLLib.mSymbols.fDestroyPbuffer(mPBuffer);
   }
   if (mWnd) {
     (void)ReleaseDC(mWnd, mDC);
     DestroyWindow(mWnd);
   }
 }
 
-bool GLContextWGL::Init() {
-  if (!mDC || !mContext) return false;
-
-  // see bug 929506 comment 29. wglGetProcAddress requires a current context.
-  if (!sWGLLib.mSymbols.fMakeCurrent(mDC, mContext)) return false;
-
-  SetupLookupFunction();
-  if (!InitWithPrefix("gl", true)) return false;
-
-  return true;
-}
-
 bool GLContextWGL::MakeCurrentImpl() const {
   const bool succeeded = sWGLLib.mSymbols.fMakeCurrent(mDC, mContext);
   NS_ASSERTION(succeeded, "Failed to make GL context current!");
   return succeeded;
 }
 
 bool GLContextWGL::IsCurrentImpl() const {
   return sWGLLib.mSymbols.fGetCurrentContext() == mContext;
@@ -319,27 +320,16 @@ bool GLContextWGL::SwapBuffers() {
   return ::SwapBuffers(mDC);
 }
 
 void GLContextWGL::GetWSIInfo(nsCString* const out) const {
   out->AppendLiteral("wglGetExtensionsString: ");
   out->Append(sWGLLib.mSymbols.fGetExtensionsStringARB(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.mSymbols.fGetProcAddress;
-  return true;
-}
-
 already_AddRefed<GLContext> GLContextProviderWGL::CreateWrappingExisting(
     void*, void*) {
   return nullptr;
 }
 
 HGLRC
 WGLLibrary::CreateContextWithFallback(const HDC dc,
                                       const bool tryRobustBuffers) const {
--- a/gfx/gl/GLContextWGL.h
+++ b/gfx/gl/GLContextWGL.h
@@ -27,23 +27,26 @@ class GLContextWGL final : public GLCont
                int aPixelFormat);
 
   ~GLContextWGL();
 
   virtual GLContextType GetContextType() const override {
     return GLContextType::WGL;
   }
 
-  bool Init() override;
   virtual bool MakeCurrentImpl() const override;
   virtual bool IsCurrentImpl() const override;
   virtual bool IsDoubleBuffered() const override { return mIsDoubleBuffered; }
   virtual bool SwapBuffers() override;
-  virtual bool SetupLookupFunction() override;
   virtual void GetWSIInfo(nsCString* const out) const override;
+
+  Maybe<SymbolLoader> GetSymbolLoader() const override {
+    return Some(sWGLLib.GetSymbolLoader());
+  }
+
   HGLRC Context() { return mContext; }
 
  protected:
   friend class GLContextProviderWGL;
 
   HDC mDC;
   HGLRC mContext;
   HWND mWnd;
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -345,24 +345,48 @@ bool GLLibraryEGL::ReadbackEGLImage(EGLI
       ShaderConfigFromTargetAndFormat(target, out_surface->GetFormat());
   int shaderConfig = config.mFeatures;
   mReadbackGL->ReadTexImageHelper()->ReadTexImage(
       out_surface, 0, target, out_surface->GetSize(), shaderConfig);
 
   return true;
 }
 
+// -
+
+#if defined(XP_UNIX)
+#  define GLES2_LIB "libGLESv2.so"
+#  define GLES2_LIB2 "libGLESv2.so.2"
+#elif defined(XP_WIN)
+#  define GLES2_LIB "libGLESv2.dll"
+#else
+#  error "Platform not recognized"
+#endif
+
+Maybe<SymbolLoader> GLLibraryEGL::GetSymbolLoader() const {
+  auto ret = SymbolLoader(mSymbols.fGetProcAddress);
+  ret.mLib = mGLLibrary;
+  return Some(ret);
+}
+
+// -
+
 /* static */ bool GLLibraryEGL::EnsureInitialized(
     bool forceAccel, nsACString* const out_failureId) {
   if (!sEGLLibrary) {
     sEGLLibrary = new GLLibraryEGL();
   }
   return sEGLLibrary->DoEnsureInitialized(forceAccel, out_failureId);
 }
 
+bool GLLibraryEGL::DoEnsureInitialized() {
+  nsCString failureId;
+  return DoEnsureInitialized(false, &failureId);
+}
+
 bool GLLibraryEGL::DoEnsureInitialized(bool forceAccel,
                                        nsACString* const out_failureId) {
   if (mInitialized && !mSymbols.fTerminate) {
     *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_DESTROYED");
     MOZ_ASSERT(false);
     return false;
   }
 
@@ -394,24 +418,19 @@ bool GLLibraryEGL::DoEnsureInitialized(b
       if (LoadLibraryForEGLOnWindows(
               NS_LITERAL_STRING(NS_STRINGIFY(MOZ_D3DCOMPILER_VISTA_DLL))))
         break;
 #  endif
 
       MOZ_ASSERT(false, "d3dcompiler DLL loading failed.");
     } while (false);
 
-    LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
+    mGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libGLESv2.dll"));
 
     mEGLLibrary = LoadLibraryForEGLOnWindows(NS_LITERAL_STRING("libEGL.dll"));
-
-    if (!mEGLLibrary) {
-      *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD");
-      return false;
-    }
   }
 
 #else  // !Windows
 
   // On non-Windows (Android) we use system copies of libEGL. We look for
   // the APITrace lib, libEGL.so, and libEGL.so.1 in that order.
 
 #  if defined(ANDROID)
@@ -423,111 +442,127 @@ bool GLLibraryEGL::DoEnsureInitialized(b
     mEGLLibrary = PR_LoadLibrary("libEGL.so");
   }
 #  if defined(XP_UNIX)
   if (!mEGLLibrary) {
     mEGLLibrary = PR_LoadLibrary("libEGL.so.1");
   }
 #  endif
 
-  if (!mEGLLibrary) {
-    NS_WARNING("Couldn't load EGL LIB.");
-    *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD_2");
-    return false;
+#  ifdef APITRACE_LIB
+  if (!mGLLibrary) {
+    mGLLibrary = PR_LoadLibrary(APITRACE_LIB);
   }
+#  endif
+
+  if (!mGLLibrary) {
+    mGLLibrary = PR_LoadLibrary(GLES2_LIB);
+  }
+
+#  ifdef GLES2_LIB2
+  if (!mGLLibrary) {
+    mGLLibrary = PR_LoadLibrary(GLES2_LIB2);
+  }
+#  endif
 
 #endif  // !Windows
 
-#define SYMBOL(X)                                     \
-  {                                                   \
-    (PRFuncPtr*)&mSymbols.f##X, { "egl" #X, nullptr } \
+  if (!mEGLLibrary || !mGLLibrary) {
+    NS_WARNING("Couldn't load EGL LIB.");
+    *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_LOAD_3");
+    return false;
   }
-#define END_OF_SYMBOLS   \
-  {                      \
-    nullptr, { nullptr } \
+
+#define SYMBOL(X)                 \
+  {                               \
+    (PRFuncPtr*)&mSymbols.f##X, { \
+      { "egl" #X }                \
+    }                             \
+  }
+#define END_OF_SYMBOLS \
+  {                    \
+    nullptr, {}        \
   }
 
-  GLLibraryLoader::SymLoadStruct earlySymbols[] = {
-      SYMBOL(GetDisplay),
-      SYMBOL(Terminate),
-      SYMBOL(GetCurrentSurface),
-      SYMBOL(GetCurrentContext),
-      SYMBOL(MakeCurrent),
-      SYMBOL(DestroyContext),
-      SYMBOL(CreateContext),
-      SYMBOL(DestroySurface),
-      SYMBOL(CreateWindowSurface),
-      SYMBOL(CreatePbufferSurface),
-      SYMBOL(CreatePbufferFromClientBuffer),
-      SYMBOL(CreatePixmapSurface),
-      SYMBOL(BindAPI),
-      SYMBOL(Initialize),
-      SYMBOL(ChooseConfig),
-      SYMBOL(GetError),
-      SYMBOL(GetConfigs),
-      SYMBOL(GetConfigAttrib),
-      SYMBOL(WaitNative),
-      SYMBOL(GetProcAddress),
-      SYMBOL(SwapBuffers),
-      SYMBOL(CopyBuffers),
-      SYMBOL(QueryString),
-      SYMBOL(QueryContext),
-      SYMBOL(BindTexImage),
-      SYMBOL(ReleaseTexImage),
-      SYMBOL(SwapInterval),
-      SYMBOL(QuerySurface),
-      END_OF_SYMBOLS};
+  SymLoadStruct earlySymbols[] = {SYMBOL(GetDisplay),
+                                  SYMBOL(Terminate),
+                                  SYMBOL(GetCurrentSurface),
+                                  SYMBOL(GetCurrentContext),
+                                  SYMBOL(MakeCurrent),
+                                  SYMBOL(DestroyContext),
+                                  SYMBOL(CreateContext),
+                                  SYMBOL(DestroySurface),
+                                  SYMBOL(CreateWindowSurface),
+                                  SYMBOL(CreatePbufferSurface),
+                                  SYMBOL(CreatePbufferFromClientBuffer),
+                                  SYMBOL(CreatePixmapSurface),
+                                  SYMBOL(BindAPI),
+                                  SYMBOL(Initialize),
+                                  SYMBOL(ChooseConfig),
+                                  SYMBOL(GetError),
+                                  SYMBOL(GetConfigs),
+                                  SYMBOL(GetConfigAttrib),
+                                  SYMBOL(WaitNative),
+                                  SYMBOL(GetProcAddress),
+                                  SYMBOL(SwapBuffers),
+                                  SYMBOL(CopyBuffers),
+                                  SYMBOL(QueryString),
+                                  SYMBOL(QueryContext),
+                                  SYMBOL(BindTexImage),
+                                  SYMBOL(ReleaseTexImage),
+                                  SYMBOL(SwapInterval),
+                                  SYMBOL(QuerySurface),
+                                  END_OF_SYMBOLS};
 
-  if (!GLLibraryLoader::LoadSymbols(mEGLLibrary, earlySymbols)) {
-    NS_WARNING(
-        "Couldn't find required entry points in EGL library (early init)");
-    *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM");
-    return false;
+  {
+    const SymbolLoader libLoader(*mEGLLibrary);
+    if (!libLoader.LoadSymbols(earlySymbols)) {
+      NS_WARNING(
+          "Couldn't find required entry points in EGL library (early init)");
+      *out_failureId = NS_LITERAL_CSTRING("FEATURE_FAILURE_EGL_SYM");
+      return false;
+    }
   }
 
   {
     const char internalFuncName[] =
         "_Z35eglQueryStringImplementationANDROIDPvi";
     const auto& internalFunc =
         PR_FindFunctionSymbol(mEGLLibrary, internalFuncName);
     if (internalFunc) {
       *(PRFuncPtr*)&mSymbols.fQueryString = internalFunc;
     }
   }
+  const SymbolLoader pfnLoader(mSymbols.fGetProcAddress);
 
   InitClientExtensions();
 
-  const auto lookupFunction =
-      (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
+  const auto fnLoadSymbols = [&](const SymLoadStruct* symbols) {
+    if (pfnLoader.LoadSymbols(symbols)) return true;
 
-  const auto fnLoadSymbols =
-      [&](const GLLibraryLoader::SymLoadStruct* symbols) {
-        if (GLLibraryLoader::LoadSymbols(mEGLLibrary, symbols, lookupFunction))
-          return true;
-
-        ClearSymbols(symbols);
-        return false;
-      };
+    ClearSymbols(symbols);
+    return false;
+  };
 
   // Check the ANGLE support the system has
   nsCOMPtr<nsIGfxInfo> gfxInfo = do_GetService("@mozilla.org/gfx/info;1");
   mIsANGLE = IsExtensionSupported(ANGLE_platform_angle);
 
   // Client exts are ready. (But not display exts!)
 
   if (mIsANGLE) {
     MOZ_ASSERT(IsExtensionSupported(ANGLE_platform_angle_d3d));
-    const GLLibraryLoader::SymLoadStruct angleSymbols[] = {
-        SYMBOL(GetPlatformDisplayEXT), END_OF_SYMBOLS};
+    const SymLoadStruct angleSymbols[] = {SYMBOL(GetPlatformDisplayEXT),
+                                          END_OF_SYMBOLS};
     if (!fnLoadSymbols(angleSymbols)) {
       gfxCriticalError() << "Failed to load ANGLE symbols!";
       return false;
     }
     MOZ_ASSERT(IsExtensionSupported(ANGLE_platform_angle_d3d));
-    const GLLibraryLoader::SymLoadStruct createDeviceSymbols[] = {
+    const SymLoadStruct createDeviceSymbols[] = {
         SYMBOL(CreateDeviceANGLE), SYMBOL(ReleaseDeviceANGLE), END_OF_SYMBOLS};
     if (!fnLoadSymbols(createDeviceSymbols)) {
       NS_ERROR(
           "EGL supports ANGLE_device_creation without exposing its functions!");
       MarkExtensionUnsupported(ANGLE_device_creation);
     }
   }
 
@@ -537,115 +572,115 @@ bool GLLibraryEGL::DoEnsureInitialized(b
   }
 
   InitDisplayExtensions();
 
   ////////////////////////////////////
   // Alright, load display exts.
 
   if (IsExtensionSupported(KHR_lock_surface)) {
-    const GLLibraryLoader::SymLoadStruct lockSymbols[] = {
+    const SymLoadStruct lockSymbols[] = {
         SYMBOL(LockSurfaceKHR), SYMBOL(UnlockSurfaceKHR), END_OF_SYMBOLS};
     if (!fnLoadSymbols(lockSymbols)) {
       NS_ERROR("EGL supports KHR_lock_surface without exposing its functions!");
       MarkExtensionUnsupported(KHR_lock_surface);
     }
   }
 
   if (IsExtensionSupported(ANGLE_surface_d3d_texture_2d_share_handle)) {
-    const GLLibraryLoader::SymLoadStruct d3dSymbols[] = {
-        SYMBOL(QuerySurfacePointerANGLE), END_OF_SYMBOLS};
+    const SymLoadStruct d3dSymbols[] = {SYMBOL(QuerySurfacePointerANGLE),
+                                        END_OF_SYMBOLS};
     if (!fnLoadSymbols(d3dSymbols)) {
       NS_ERROR(
           "EGL supports ANGLE_surface_d3d_texture_2d_share_handle without "
           "exposing its functions!");
       MarkExtensionUnsupported(ANGLE_surface_d3d_texture_2d_share_handle);
     }
   }
 
   if (IsExtensionSupported(KHR_fence_sync)) {
-    const GLLibraryLoader::SymLoadStruct syncSymbols[] = {
+    const SymLoadStruct syncSymbols[] = {
         SYMBOL(CreateSyncKHR), SYMBOL(DestroySyncKHR),
         SYMBOL(ClientWaitSyncKHR), SYMBOL(GetSyncAttribKHR), END_OF_SYMBOLS};
     if (!fnLoadSymbols(syncSymbols)) {
       NS_ERROR("EGL supports KHR_fence_sync without exposing its functions!");
       MarkExtensionUnsupported(KHR_fence_sync);
     }
   }
 
   if (IsExtensionSupported(KHR_image) || IsExtensionSupported(KHR_image_base)) {
-    const GLLibraryLoader::SymLoadStruct imageSymbols[] = {
+    const SymLoadStruct imageSymbols[] = {
         SYMBOL(CreateImageKHR), SYMBOL(DestroyImageKHR), END_OF_SYMBOLS};
     if (!fnLoadSymbols(imageSymbols)) {
       NS_ERROR("EGL supports KHR_image(_base) without exposing its functions!");
       MarkExtensionUnsupported(KHR_image);
       MarkExtensionUnsupported(KHR_image_base);
       MarkExtensionUnsupported(KHR_image_pixmap);
     }
   } else {
     MarkExtensionUnsupported(KHR_image_pixmap);
   }
 
   if (IsExtensionSupported(ANDROID_native_fence_sync)) {
-    const GLLibraryLoader::SymLoadStruct nativeFenceSymbols[] = {
-        SYMBOL(DupNativeFenceFDANDROID), END_OF_SYMBOLS};
+    const SymLoadStruct nativeFenceSymbols[] = {SYMBOL(DupNativeFenceFDANDROID),
+                                                END_OF_SYMBOLS};
     if (!fnLoadSymbols(nativeFenceSymbols)) {
       NS_ERROR(
           "EGL supports ANDROID_native_fence_sync without exposing its "
           "functions!");
       MarkExtensionUnsupported(ANDROID_native_fence_sync);
     }
   }
 
   if (IsExtensionSupported(KHR_stream)) {
-    const GLLibraryLoader::SymLoadStruct streamSymbols[] = {
+    const SymLoadStruct streamSymbols[] = {
         SYMBOL(CreateStreamKHR), SYMBOL(DestroyStreamKHR),
         SYMBOL(QueryStreamKHR), END_OF_SYMBOLS};
     if (!fnLoadSymbols(streamSymbols)) {
       NS_ERROR("EGL supports KHR_stream without exposing its functions!");
       MarkExtensionUnsupported(KHR_stream);
     }
   }
 
   if (IsExtensionSupported(KHR_stream_consumer_gltexture)) {
-    const GLLibraryLoader::SymLoadStruct streamConsumerSymbols[] = {
+    const SymLoadStruct streamConsumerSymbols[] = {
         SYMBOL(StreamConsumerGLTextureExternalKHR),
         SYMBOL(StreamConsumerAcquireKHR), SYMBOL(StreamConsumerReleaseKHR),
         END_OF_SYMBOLS};
     if (!fnLoadSymbols(streamConsumerSymbols)) {
       NS_ERROR(
           "EGL supports KHR_stream_consumer_gltexture without exposing its "
           "functions!");
       MarkExtensionUnsupported(KHR_stream_consumer_gltexture);
     }
   }
 
   if (IsExtensionSupported(EXT_device_query)) {
-    const GLLibraryLoader::SymLoadStruct queryDisplaySymbols[] = {
-        SYMBOL(QueryDisplayAttribEXT), SYMBOL(QueryDeviceAttribEXT),
-        END_OF_SYMBOLS};
+    const SymLoadStruct queryDisplaySymbols[] = {SYMBOL(QueryDisplayAttribEXT),
+                                                 SYMBOL(QueryDeviceAttribEXT),
+                                                 END_OF_SYMBOLS};
     if (!fnLoadSymbols(queryDisplaySymbols)) {
       NS_ERROR("EGL supports EXT_device_query without exposing its functions!");
       MarkExtensionUnsupported(EXT_device_query);
     }
   }
 
   if (IsExtensionSupported(NV_stream_consumer_gltexture_yuv)) {
-    const GLLibraryLoader::SymLoadStruct nvStreamSymbols[] = {
+    const SymLoadStruct nvStreamSymbols[] = {
         SYMBOL(StreamConsumerGLTextureExternalAttribsNV), END_OF_SYMBOLS};
     if (!fnLoadSymbols(nvStreamSymbols)) {
       NS_ERROR(
           "EGL supports NV_stream_consumer_gltexture_yuv without exposing its "
           "functions!");
       MarkExtensionUnsupported(NV_stream_consumer_gltexture_yuv);
     }
   }
 
   if (IsExtensionSupported(ANGLE_stream_producer_d3d_texture)) {
-    const GLLibraryLoader::SymLoadStruct nvStreamSymbols[] = {
+    const SymLoadStruct nvStreamSymbols[] = {
         SYMBOL(CreateStreamProducerD3DTextureANGLE),
         SYMBOL(StreamPostD3DTextureANGLE), END_OF_SYMBOLS};
     if (!fnLoadSymbols(nvStreamSymbols)) {
       NS_ERROR(
           "EGL supports ANGLE_stream_producer_d3d_texture without exposing its "
           "functions!");
       MarkExtensionUnsupported(ANGLE_stream_producer_d3d_texture);
     }
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -50,17 +50,17 @@ class DataSourceSurface;
 
 namespace gl {
 
 class GLContext;
 
 void BeforeEGLCall(const char* funcName);
 void AfterEGLCall(const char* funcName);
 
-class GLLibraryEGL {
+class GLLibraryEGL final {
  protected:
   ~GLLibraryEGL() {}
 
  public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GLLibraryEGL)
 
   void InitClientExtensions();
   void InitDisplayExtensions();
@@ -107,20 +107,16 @@ class GLLibraryEGL {
   void MarkExtensionUnsupported(EGLExtensions aKnownExtension) {
     mAvailableExtensions[aKnownExtension] = false;
   }
 
  protected:
   std::bitset<Extensions_Max> mAvailableExtensions;
 
  public:
-  GLLibraryLoader::PlatformLookupFunction GetLookupFunction() const {
-    return (GLLibraryLoader::PlatformLookupFunction)mSymbols.fGetProcAddress;
-  }
-
   ////
 
 #ifdef MOZ_WIDGET_ANDROID
 #  define PROFILE_CALL AUTO_PROFILER_LABEL(__func__, GRAPHICS);
 #else
 #  define PROFILE_CALL
 #endif
 
@@ -414,16 +410,18 @@ class GLLibraryEGL {
   static bool EnsureInitialized(bool forceAccel,
                                 nsACString* const out_failureId);
 
   void Shutdown();
 
   void DumpEGLConfig(EGLConfig cfg);
   void DumpEGLConfigs();
 
+  Maybe<SymbolLoader> GetSymbolLoader() const;
+
  private:
   struct {
     EGLCastToRelevantPtr(GLAPIENTRY* fGetProcAddress)(const char* procname);
     EGLDisplay(GLAPIENTRY* fGetDisplay)(void* display_id);
     EGLDisplay(GLAPIENTRY* fGetPlatformDisplayEXT)(EGLenum platform,
                                                    void* native_display,
                                                    const EGLint* attrib_list);
     EGLBoolean(GLAPIENTRY* fTerminate)(EGLDisplay dpy);
@@ -533,22 +531,24 @@ class GLLibraryEGL {
     EGLDeviceEXT(GLAPIENTRY* fCreateDeviceANGLE)(EGLint device_type,
                                                  void* native_device,
                                                  const EGLAttrib* attrib_list);
     EGLBoolean(GLAPIENTRY* fReleaseDeviceANGLE)(EGLDeviceEXT device);
 
   } mSymbols = {};
 
  private:
+  bool DoEnsureInitialized();
   bool DoEnsureInitialized(bool forceAccel, nsACString* const out_failureId);
   EGLDisplay CreateDisplay(bool forceAccel, const nsCOMPtr<nsIGfxInfo>& gfxInfo,
                            nsACString* const out_failureId);
 
   bool mInitialized = false;
   PRLibrary* mEGLLibrary = nullptr;
+  mutable PRLibrary* mGLLibrary = nullptr;
   EGLDisplay mEGLDisplay = EGL_NO_DISPLAY;
   RefPtr<GLContext> mReadbackGL;
 
   bool mIsANGLE = false;
   bool mIsWARP = false;
   static StaticMutex sMutex;
   static StaticRefPtr<GLLibraryEGL> sEGLLibrary;
 };
--- a/gfx/gl/GLLibraryLoader.cpp
+++ b/gfx/gl/GLLibraryLoader.cpp
@@ -1,48 +1,35 @@
 /* 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 "GLLibraryLoader.h"
 
+#include <regex>
+
 #include "nsDebug.h"
 
 #ifdef WIN32
 #  include <windows.h>
 #endif
 
 namespace mozilla {
 namespace gl {
 
-bool GLLibraryLoader::OpenLibrary(const char* library) {
-  PRLibSpec lspec;
-  lspec.type = PR_LibSpec_Pathname;
-  lspec.value.pathname = library;
-
-  mLibrary = PR_LoadLibraryWithFlags(lspec, PR_LD_LAZY | PR_LD_LOCAL);
-  if (!mLibrary) return false;
-
-  return true;
+void ClearSymbols(const SymLoadStruct* const firstStruct) {
+  for (auto itr = firstStruct; itr->symPointer; ++itr) {
+    *itr->symPointer = nullptr;
+  }
 }
 
-bool GLLibraryLoader::LoadSymbols(const SymLoadStruct* firstStruct,
-                                  bool tryplatform, const char* prefix,
-                                  bool warnOnFailure) {
-  return LoadSymbols(mLibrary, firstStruct, tryplatform ? mLookupFunc : nullptr,
-                     prefix, warnOnFailure);
-}
-
-PRFuncPtr GLLibraryLoader::LookupSymbol(const char* sym) {
-  return LookupSymbol(mLibrary, sym, mLookupFunc);
-}
-
-PRFuncPtr GLLibraryLoader::LookupSymbol(PRLibrary* lib, const char* sym,
-                                        PlatformLookupFunction lookupFunction) {
-  PRFuncPtr res = 0;
+/*
+PRFuncPtr GLLibraryLoader::LookupSymbol(PRLibrary& lib, const char* const sym,
+                                        const PlatformLookupFunction lookupFunction) {
+  PRFuncPtr res = nullptr;
 
   // try finding it in the library directly, if we have one
   if (lib) {
     res = PR_FindFunctionSymbol(lib, sym);
   }
 
   // then try looking it up via the lookup symbol
   if (!res && lookupFunction) {
@@ -52,60 +39,55 @@ PRFuncPtr GLLibraryLoader::LookupSymbol(
   // finally just try finding it in the process
   if (!res) {
     PRLibrary* leakedLibRef;
     res = PR_FindFunctionSymbolAndLibrary(sym, &leakedLibRef);
   }
 
   return res;
 }
+*/
 
-bool GLLibraryLoader::LoadSymbols(PRLibrary* lib,
-                                  const SymLoadStruct* firstStruct,
-                                  PlatformLookupFunction lookupFunction,
-                                  const char* prefix, bool warnOnFailure) {
-  char sbuf[MAX_SYMBOL_LENGTH * 2];
-  int failCount = 0;
-
-  const SymLoadStruct* ss = firstStruct;
-  while (ss->symPointer) {
-    *ss->symPointer = 0;
+bool SymbolLoader::LoadSymbols(const SymLoadStruct* const firstStruct,
+                               const bool warnOnFailures) const {
+  bool ok = true;
 
-    for (int i = 0; i < MAX_SYMBOL_NAMES; i++) {
-      if (ss->symNames[i] == nullptr) break;
+  for (auto itr = firstStruct; itr->symPointer; ++itr) {
+    *itr->symPointer = nullptr;
 
-      const char* s = ss->symNames[i];
-      if (prefix && *prefix != 0) {
-        strcpy(sbuf, prefix);
-        strcat(sbuf, ss->symNames[i]);
-        s = sbuf;
-      }
+    for (const auto& s : itr->symNames) {
+      if (!s) break;
 
-      PRFuncPtr p = LookupSymbol(lib, s, lookupFunction);
+      const auto p = GetProcAddress(s);
       if (p) {
-        *ss->symPointer = p;
+        *itr->symPointer = p;
         break;
       }
     }
 
-    if (*ss->symPointer == 0) {
-      if (warnOnFailure) {
-        printf_stderr("Can't find symbol '%s'.\n", ss->symNames[0]);
+    if (!*itr->symPointer) {
+      if (warnOnFailures) {
+        printf_stderr("Can't find symbol '%s'.\n", itr->symNames[0]);
       }
-
-      failCount++;
+      ok = false;
     }
-
-    ss++;
   }
 
-  return failCount == 0 ? true : false;
+  return ok;
 }
 
-/*static*/ void GLLibraryLoader::ClearSymbols(
-    const SymLoadStruct* const firstStruct) {
-  for (auto cur = firstStruct; cur->symPointer; ++cur) {
-    *cur->symPointer = nullptr;
+// -
+
+PRFuncPtr SymbolLoader::GetProcAddress(const char* const name) const {
+#ifdef DEBUG
+  static const std::regex kRESymbol("[a-z].*");
+  if (!std::regex_match(name, kRESymbol)) {
+    gfxCriticalError() << "Bad symbol name : " << name;
   }
+#endif
+
+  if (mPfn) return mPfn(name);
+  if (mLib) return PR_FindFunctionSymbol(mLib, name);
+  return nullptr;
 }
 
-} /* namespace gl */
-} /* namespace mozilla */
+} // namespace gl
+} // namespace mozilla
--- a/gfx/gl/GLLibraryLoader.h
+++ b/gfx/gl/GLLibraryLoader.h
@@ -1,61 +1,57 @@
 /* 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/. */
 
 #ifndef GLLIBRARYLOADER_H_
 #define GLLIBRARYLOADER_H_
 
+#include <array>
 #include <stdio.h>
 
 #include "GLDefs.h"
 #include "nscore.h"
 #include "mozilla/SharedLibrary.h"
 
 namespace mozilla {
 namespace gl {
 
-class GLLibraryLoader {
- public:
-  bool OpenLibrary(const char* library);
-
-  typedef PRFuncPtr(GLAPIENTRY* PlatformLookupFunction)(const char*);
-
-  enum { MAX_SYMBOL_NAMES = 6, MAX_SYMBOL_LENGTH = 128 };
+struct SymLoadStruct final {
+  PRFuncPtr* symPointer;
+  std::array<const char*, 6> symNames;
+};
 
-  typedef struct {
-    PRFuncPtr* symPointer;
-    const char* symNames[MAX_SYMBOL_NAMES];
-  } SymLoadStruct;
-
-  bool LoadSymbols(const SymLoadStruct* firstStruct, bool tryplatform = false,
-                   const char* prefix = nullptr, bool warnOnFailure = true);
-
-  static void ClearSymbols(const SymLoadStruct* firstStruct);
+void ClearSymbols(const SymLoadStruct* firstStruct);
 
-  PRFuncPtr LookupSymbol(const char* symname);
+class SymbolLoader final {
+public:
+  typedef PRFuncPtr (GLAPIENTRY * GetProcAddressT)(const char*);
 
-  /*
-   * Static version of the functions in this class
-   */
-  static PRFuncPtr LookupSymbol(
-      PRLibrary* lib, const char* symname,
-      PlatformLookupFunction lookupFunction = nullptr);
-  static bool LoadSymbols(PRLibrary* lib, const SymLoadStruct* firstStruct,
-                          PlatformLookupFunction lookupFunction = nullptr,
-                          const char* prefix = nullptr,
-                          bool warnOnFailure = true);
+  GetProcAddressT mPfn = nullptr; // Try this first, if not null.
+  PRLibrary* mLib = nullptr;
 
- protected:
-  GLLibraryLoader() {
-    mLibrary = nullptr;
-    mLookupFunc = nullptr;
+  explicit SymbolLoader(void* (GLAPIENTRY * pfn)(const char*))
+    : mPfn(GetProcAddressT(pfn))
+  {
+    MOZ_ASSERT(mPfn);
   }
 
-  PRLibrary* mLibrary;
-  PlatformLookupFunction mLookupFunc;
+  explicit SymbolLoader(const GetProcAddressT pfn)
+    : mPfn(pfn)
+  {
+    MOZ_ASSERT(mPfn);
+  }
+
+  explicit SymbolLoader(PRLibrary& lib)
+    : mLib(&lib)
+  {
+    MOZ_ASSERT(mLib);
+  }
+
+  PRFuncPtr GetProcAddress(const char*) const;
+  bool LoadSymbols(const SymLoadStruct* firstStruct, bool warnOnFailures = true) const;
 };
 
-} /* namespace gl */
-} /* namespace mozilla */
+} // namespace gl
+} // namespace mozilla
 
-#endif /* GLLIBRARYLOADER_H_ */
+#endif // GLLIBRARYLOADER_H_
--- a/gfx/gl/GLXLibrary.h
+++ b/gfx/gl/GLXLibrary.h
@@ -25,17 +25,17 @@ typedef struct __GLXFBConfigRec* GLXFBCo
 struct PRLibrary;
 class gfxASurface;
 
 namespace mozilla {
 namespace gl {
 
 class GLContextGLX;
 
-class GLXLibrary {
+class GLXLibrary final {
  public:
   bool EnsureInitialized();
 
  private:
   void BeforeGLXCall() const;
   void AfterGLXCall() const;
 
  public:
@@ -174,18 +174,18 @@ class GLXLibrary {
   bool HasVideoMemoryPurge() { return mHasVideoMemoryPurge; }
   bool HasCreateContextAttribs() { return mHasCreateContextAttribs; }
   bool SupportsTextureFromPixmap(gfxASurface* aSurface);
   bool SupportsVideoSync();
   bool SupportsSwapControl() const { return bool(mSymbols.fSwapIntervalEXT); }
   bool IsATI() { return mIsATI; }
   bool IsMesa() { return mClientIsMesa; }
 
-  PRFuncPtr GetGetProcAddress() const {
-    return (PRFuncPtr)mSymbols.fGetProcAddress;
+  auto GetGetProcAddress() const {
+    return mSymbols.fGetProcAddress;
   }
 
  private:
   struct {
     void(GLAPIENTRY* fDestroyContext)(Display*, GLXContext);
     Bool(GLAPIENTRY* fMakeCurrent)(Display*, GLXDrawable, GLXContext);
     XVisualInfo*(GLAPIENTRY* fGetConfig)(Display*, XVisualInfo*, int, int*);
     GLXContext(GLAPIENTRY* fGetCurrentContext)();
--- a/gfx/gl/WGLLibrary.h
+++ b/gfx/gl/WGLLibrary.h
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  * 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 "GLContextTypes.h"
+#include "GLLibraryLoader.h"
 #include "mozilla/UniquePtr.h"
 #include <windows.h>
 
 struct PRLibrary;
 
 namespace mozilla {
 namespace gl {
 /*
@@ -90,16 +91,17 @@ class WGLLibrary {
   bool EnsureInitialized();
   // UniquePtr<WindowDC> CreateDummyWindow();
   HGLRC CreateContextWithFallback(HDC dc, bool tryRobustBuffers) const;
 
   bool HasDXInterop2() const { return bool(mSymbols.fDXOpenDeviceNV); }
   bool IsInitialized() const { return mInitialized; }
   auto GetOGLLibrary() const { return mOGLLibrary; }
   auto RootDc() const { return mRootDc; }
+  SymbolLoader GetSymbolLoader() const;
 
  private:
   bool mInitialized = false;
   PRLibrary* mOGLLibrary;
   bool mHasRobustness;
   HWND mDummyWindow;
   HDC mRootDc;
   HGLRC mDummyGlrc;
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -99,25 +99,27 @@ const char* gfx_wr_resource_path_overrid
 void gfx_critical_note(const char* msg) { gfxCriticalNote << msg; }
 
 void gfx_critical_error(const char* msg) { gfxCriticalError() << msg; }
 
 void gecko_printf_stderr_output(const char* msg) { printf_stderr("%s\n", msg); }
 
 void* get_proc_address_from_glcontext(void* glcontext_ptr,
                                       const char* procname) {
-  MOZ_ASSERT(glcontext_ptr);
-
   mozilla::gl::GLContext* glcontext =
       reinterpret_cast<mozilla::gl::GLContext*>(glcontext_ptr);
+  MOZ_ASSERT(glcontext);
   if (!glcontext) {
     return nullptr;
   }
-  PRFuncPtr p = glcontext->LookupSymbol(procname);
-  return reinterpret_cast<void*>(p);
+  const auto& loader = glcontext->GetSymbolLoader();
+  MOZ_ASSERT(loader);
+
+  const auto ret = loader->GetProcAddress(procname);
+  return reinterpret_cast<void*>(ret);
 }
 
 void gecko_profiler_register_thread(const char* name) {
   PROFILER_REGISTER_THREAD(name);
 }
 
 void gecko_profiler_unregister_thread() { PROFILER_UNREGISTER_THREAD(); }