Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/gfx/gl/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 103 kB image not shown  

Quelle  GLContext.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "GLContext.h"

#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <regex>
#include <string>
#include <vector>
#ifdef MOZ_WIDGET_ANDROID
#  include <sys/mman.h>
#endif
#if defined(XP_LINUX) && !defined(ANDROID)
// For MesaMemoryLeakWorkaround
#  include <dlfcn.h>
#  include <link.h>
#endif

#include "GLBlitHelper.h"
#include "GLReadTexImageHelper.h"
#include "GLScreenBuffer.h"

#include "gfxCrashReporterUtils.h"
#include "gfxEnv.h"
#include "gfxUtils.h"
#include "GLContextProvider.h"
#include "GLLibraryLoader.h"
#include "GLTextureImage.h"
#include "nsPrintfCString.h"
#include "nsThreadUtils.h"
#include "prenv.h"
#include "prlink.h"
#include "ScopedGLHelpers.h"
#include "SharedSurfaceGL.h"
#include "GfxTexturesReporter.h"
#include "gfx2DGlue.h"
#include "mozilla/StaticPrefs_gfx.h"
#include "mozilla/StaticPrefs_gl.h"
#include "mozilla/IntegerPrintfMacros.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/layers/BuildConstants.h"
#include "mozilla/layers/TextureForwarder.h"  // for LayersIPCChannel

#include "OGLShaderProgram.h"  // for ShaderProgramType

#include "mozilla/DebugOnly.h"
#include "mozilla/Maybe.h"

#ifdef XP_MACOSX
#  include <CoreServices/CoreServices.h>
#endif

#ifdef MOZ_WIDGET_ANDROID
#  include "mozilla/jni/Utils.h"
#endif

namespace mozilla {
namespace gl {

using namespace mozilla::gfx;
using namespace mozilla::layers;

// Zero-initialized after init().
MOZ_THREAD_LOCAL(const GLContext*) 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, {{ "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 charconst sExtensionNames[] = {
    "NO_EXTENSION",
    "GL_AMD_compressed_ATC_texture",
    "GL_ANGLE_depth_texture",
    "GL_ANGLE_framebuffer_blit",
    "GL_ANGLE_framebuffer_multisample",
    "GL_ANGLE_instanced_arrays",
    "GL_ANGLE_multiview",
    "GL_ANGLE_provoking_vertex",
    "GL_ANGLE_texture_compression_dxt3",
    "GL_ANGLE_texture_compression_dxt5",
    "GL_ANGLE_timer_query",
    "GL_APPLE_client_storage",
    "GL_APPLE_fence",
    "GL_APPLE_framebuffer_multisample",
    "GL_APPLE_sync",
    "GL_APPLE_texture_range",
    "GL_APPLE_vertex_array_object",
    "GL_ARB_ES2_compatibility",
    "GL_ARB_ES3_compatibility",
    "GL_ARB_color_buffer_float",
    "GL_ARB_compatibility",
    "GL_ARB_copy_buffer",
    "GL_ARB_depth_clamp",
    "GL_ARB_depth_texture",
    "GL_ARB_draw_buffers",
    "GL_ARB_draw_instanced",
    "GL_ARB_framebuffer_object",
    "GL_ARB_framebuffer_sRGB",
    "GL_ARB_geometry_shader4",
    "GL_ARB_half_float_pixel",
    "GL_ARB_instanced_arrays",
    "GL_ARB_internalformat_query",
    "GL_ARB_invalidate_subdata",
    "GL_ARB_map_buffer_range",
    "GL_ARB_occlusion_query2",
    "GL_ARB_pixel_buffer_object",
    "GL_ARB_provoking_vertex",
    "GL_ARB_robust_buffer_access_behavior",
    "GL_ARB_robustness",
    "GL_ARB_sampler_objects",
    "GL_ARB_seamless_cube_map",
    "GL_ARB_shader_texture_lod",
    "GL_ARB_sync",
    "GL_ARB_texture_compression",
    "GL_ARB_texture_compression_bptc",
    "GL_ARB_texture_compression_rgtc",
    "GL_ARB_texture_float",
    "GL_ARB_texture_non_power_of_two",
    "GL_ARB_texture_rectangle",
    "GL_ARB_texture_rg",
    "GL_ARB_texture_storage",
    "GL_ARB_texture_swizzle",
    "GL_ARB_timer_query",
    "GL_ARB_transform_feedback2",
    "GL_ARB_uniform_buffer_object",
    "GL_ARB_vertex_array_object",
    "GL_CHROMIUM_color_buffer_float_rgb",
    "GL_CHROMIUM_color_buffer_float_rgba",
    "GL_EXT_bgra",
    "GL_EXT_blend_minmax",
    "GL_EXT_color_buffer_float",
    "GL_EXT_color_buffer_half_float",
    "GL_EXT_copy_texture",
    "GL_EXT_depth_clamp",
    "GL_EXT_disjoint_timer_query",
    "GL_EXT_draw_buffers",
    "GL_EXT_draw_buffers2",
    "GL_EXT_draw_instanced",
    "GL_EXT_float_blend",
    "GL_EXT_frag_depth",
    "GL_EXT_framebuffer_blit",
    "GL_EXT_framebuffer_multisample",
    "GL_EXT_framebuffer_object",
    "GL_EXT_framebuffer_sRGB",
    "GL_EXT_gpu_shader4",
    "GL_EXT_map_buffer_range",
    "GL_EXT_multisampled_render_to_texture",
    "GL_EXT_occlusion_query_boolean",
    "GL_EXT_packed_depth_stencil",
    "GL_EXT_provoking_vertex",
    "GL_EXT_read_format_bgra",
    "GL_EXT_robustness",
    "GL_EXT_sRGB",
    "GL_EXT_sRGB_write_control",
    "GL_EXT_shader_texture_lod",
    "GL_EXT_texture_compression_bptc",
    "GL_EXT_texture_compression_dxt1",
    "GL_EXT_texture_compression_rgtc",
    "GL_EXT_texture_compression_s3tc",
    "GL_EXT_texture_compression_s3tc_srgb",
    "GL_EXT_texture_filter_anisotropic",
    "GL_EXT_texture_format_BGRA8888",
    "GL_EXT_texture_norm16",
    "GL_EXT_texture_sRGB",
    "GL_EXT_texture_storage",
    "GL_EXT_timer_query",
    "GL_EXT_transform_feedback",
    "GL_EXT_unpack_subimage",
    "GL_EXT_semaphore",
    "GL_EXT_semaphore_fd",
    "GL_EXT_memory_object",
    "GL_EXT_memory_object_fd",
    "GL_IMG_read_format",
    "GL_IMG_texture_compression_pvrtc",
    "GL_IMG_texture_npot",
    "GL_KHR_debug",
    "GL_KHR_parallel_shader_compile",
    "GL_KHR_robust_buffer_access_behavior",
    "GL_KHR_robustness",
    "GL_KHR_texture_compression_astc_hdr",
    "GL_KHR_texture_compression_astc_ldr",
    "GL_NV_draw_instanced",
    "GL_NV_fence",
    "GL_NV_framebuffer_blit",
    "GL_NV_geometry_program4",
    "GL_NV_half_float",
    "GL_NV_instanced_arrays",
    "GL_NV_primitive_restart",
    "GL_NV_texture_barrier",
    "GL_NV_transform_feedback",
    "GL_NV_transform_feedback2",
    "GL_OES_EGL_image",
    "GL_OES_EGL_image_external",
    "GL_OES_EGL_sync",
    "GL_OES_compressed_ETC1_RGB8_texture",
    "GL_OES_depth24",
    "GL_OES_depth32",
    "GL_OES_depth_texture",
    "GL_OES_draw_buffers_indexed",
    "GL_OES_element_index_uint",
    "GL_OES_fbo_render_mipmap",
    "GL_OES_framebuffer_object",
    "GL_OES_packed_depth_stencil",
    "GL_OES_rgb8_rgba8",
    "GL_OES_standard_derivatives",
    "GL_OES_stencil8",
    "GL_OES_texture_3D",
    "GL_OES_texture_float",
    "GL_OES_texture_float_linear",
    "GL_OES_texture_half_float",
    "GL_OES_texture_half_float_linear",
    "GL_OES_texture_npot",
    "GL_OES_vertex_array_object",
    "GL_OVR_multiview2"};

static bool ShouldUseTLSIsCurrent(bool useTLSIsCurrent) {
  if (StaticPrefs::gl_use_tls_is_current() == 0) {
    return useTLSIsCurrent;
  }

  return StaticPrefs::gl_use_tls_is_current() > 0;
}

static bool ParseVersion(const std::string& versionStr,
                         uint32_t* const out_major, uint32_t* const out_minor) {
  static const std::regex kVersionRegex("([0-9]+)\\.([0-9]+)");
  std::smatch match;
  if (!std::regex_search(versionStr, match, kVersionRegex)) return false;

  const auto& majorStr = match.str(1);
  const auto& minorStr = match.str(2);
  *out_major = atoi(majorStr.c_str());
  *out_minor = atoi(minorStr.c_str());
  return true;
}

/*static*/
uint8_t GLContext::ChooseDebugFlags(const CreateContextFlags createFlags) {
  uint8_t debugFlags = 0;

#ifdef MOZ_GL_DEBUG_BUILD
  if (gfxEnv::MOZ_GL_DEBUG()) {
    debugFlags |= GLContext::DebugFlagEnabled;
  }

  // Enables extra verbose output, informing of the start and finish of every GL
  // call. Useful e.g. to record information to investigate graphics system
  // crashes/lockups
  if (gfxEnv::MOZ_GL_DEBUG_VERBOSE()) {
    debugFlags |= GLContext::DebugFlagTrace;
  }

  // Aborts on GL error. Can be useful to debug quicker code that is known not
  // to generate any GL error in principle.
  bool abortOnError = false;

  if (createFlags & CreateContextFlags::NO_VALIDATION) {
    abortOnError = true;

    const auto& env = gfxEnv::MOZ_GL_DEBUG_ABORT_ON_ERROR();
    if (env.as_str == "0") {
      abortOnError = false;
    }
  }

  if (abortOnError) {
    debugFlags |= GLContext::DebugFlagAbortOnError;
  }
#endif

  return debugFlags;
}

GLContext::GLContext(const GLContextDesc& desc, GLContext* sharedContext,
                     bool useTLSIsCurrent)
    : mDesc(desc),
      mUseTLSIsCurrent(ShouldUseTLSIsCurrent(useTLSIsCurrent)),
      mDebugFlags(ChooseDebugFlags(mDesc.flags)),
      mSharedContext(sharedContext),
      mOwningThreadId(Some(PlatformThread::CurrentId())),
      mWorkAroundDriverBugs(
          StaticPrefs::gfx_work_around_driver_bugs_AtStartup()) {}

GLContext::~GLContext() {
  NS_ASSERTION(
      IsDestroyed(),
      "GLContext implementation must call MarkDestroyed in destructor!");
#ifdef MOZ_GL_DEBUG_BUILD
  if (mSharedContext) {
    GLContext* tip = mSharedContext;
    while (tip->mSharedContext) tip = tip->mSharedContext;
    tip->SharedContextDestroyed(this);
    tip->ReportOutstandingNames();
  } else {
    ReportOutstandingNames();
  }
#endif
  // Ensure we clear sCurrentContext if we were the last context set and avoid
  // the memory getting reused.
  if (sCurrentContext.init() && sCurrentContext.get() == this) {
    sCurrentContext.set(nullptr);
  }
}

/*static*/
void GLContext::InvalidateCurrentContext() {
  if (sCurrentContext.init()) {
    sCurrentContext.set(nullptr);
  }
}

/*static*/
void GLContext::StaticDebugCallback(GLenum source, GLenum type, GLuint id,
                                    GLenum severity, GLsizei length,
                                    const GLchar* message,
                                    const GLvoid* userParam) {
  GLContext* gl = (GLContext*)userParam;
  gl->DebugCallback(source, type, id, severity, length, message);
}

bool GLContext::Init() {
  MOZ_RELEASE_ASSERT(!mSymbols.fBindFramebuffer,
                     "GFX: GLContext::Init should only be called once.");

  ScopedGfxFeatureReporter reporter("GL Context");

  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 LoadSymbolsWithDesc(const SymbolLoader& loader,
                                const SymLoadStruct* list, const char* desc) {
  const auto warnOnFailure = bool(desc);
  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 SymbolLoader& loader,
                               const SymLoadStruct* list, GLExtensions ext) {
  const char* extName = sExtensionNames[size_t(ext)];
  if (!LoadSymbolsWithDesc(loader, list, extName)) {
    MarkExtensionUnsupported(ext);
    return false;
  }
  return true;
};

bool GLContext::LoadFeatureSymbols(const SymbolLoader& loader,
                                   const SymLoadStruct* list,
                                   GLFeature feature) {
  const char* featureName = GetFeatureName(feature);
  if (!LoadSymbolsWithDesc(loader, list, featureName)) {
    MarkUnsupported(feature);
    return false;
  }
  return true;
};

bool GLContext::InitImpl() {
  if (!MakeCurrent(true)) return false;

  const auto loader = GetSymbolLoader();
  if (!loader) return false;

  const auto fnLoadSymbols = [&](const SymLoadStruct* const list,
                                 const charconst desc) {
    return LoadSymbolsWithDesc(*loader, list, desc);
  };

  // clang-format off
    const SymLoadStruct coreSymbols[] = {
        { (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 (!fnLoadSymbols(coreSymbols, "GL")) return false;

  {
    const SymLoadStruct symbols[] = {
        {(PRFuncPtr*)&mSymbols.fGetGraphicsResetStatus,
         {{"glGetGraphicsResetStatus""glGetGraphicsResetStatusARB",
           "glGetGraphicsResetStatusKHR""glGetGraphicsResetStatusEXT"}}},
        END_SYMBOLS};
    (void)fnLoadSymbols(symbols, nullptr);

    // We need to call the fGetError symbol directly here because if there is an
    // unflushed reset status, we don't want to mark the context as lost. That
    // would prevent us from recovering.
    auto err = mSymbols.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);
      }
      err = fGetError();
      MOZ_ASSERT(!err);
    }
    if (err) {
      MOZ_ASSERT(false);
      return false;
    }
  }

  ////////////////

  const autoconst versionRawStr = (const char*)fGetString(LOCAL_GL_VERSION);
  if (!versionRawStr || !*versionRawStr) {
    // This can happen with Pernosco.
    NS_WARNING("Empty GL version string");
    return false;
  }

  const std::string versionStr = versionRawStr;
  if (versionStr.find("OpenGL ES") == 0) {
    mProfile = ContextProfile::OpenGLES;
  }

  uint32_t majorVer, minorVer;
  if (!ParseVersion(versionStr, &majorVer, &minorVer)) {
    MOZ_ASSERT(false"Failed to parse GL_VERSION");
    return false;
  }
  MOZ_ASSERT(majorVer < 10);
  MOZ_ASSERT(minorVer < 10);
  mVersion = majorVer * 100 + minorVer * 10;
  if (mVersion < 200) return false;

  ////

  const auto glslVersionStr =
      (const char*)fGetString(LOCAL_GL_SHADING_LANGUAGE_VERSION);
  if (!glslVersionStr) {
    // This happens on the Android emulators. We'll just return 100
    mShadingLanguageVersion = 100;
  } else if (ParseVersion(glslVersionStr, &majorVer, &minorVer)) {
    MOZ_ASSERT(majorVer < 10);
    MOZ_ASSERT(minorVer < 100);
    mShadingLanguageVersion = majorVer * 100 + minorVer;
  } else {
    MOZ_ASSERT(false"Failed to parse GL_SHADING_LANGUAGE_VERSION");
    return false;
  }

  if (ShouldSpew()) {
    printf_stderr("GL version detected: %u\n", mVersion);
    printf_stderr("GLSL version detected: %u\n", mShadingLanguageVersion);
    printf_stderr("OpenGL vendor: %s\n", fGetString(LOCAL_GL_VENDOR));
    printf_stderr("OpenGL renderer: %s\n", fGetString(LOCAL_GL_RENDERER));
  }

  ////////////////

  // 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 (!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 (!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;

  // The order of these strings must match up with the order of the enum
  // defined in GLContext.h for vendor IDs.
  const char* vendorMatchStrings[size_t(GLVendor::Other) + 1] = {
      "Intel",   "NVIDIA",  "ATI",          "Qualcomm""Imagination",
      "nouveau""Vivante""VMware, Inc.""ARM",      "Unknown"};

  mVendor = GLVendor::Other;
  for (size_t i = 0; i < size_t(GLVendor::Other); ++i) {
    if (DoesStringMatch(glVendorString, vendorMatchStrings[i])) {
      mVendor = GLVendor(i);
      break;
    }
  }

  // The order of these strings must match up with the order of the enum
  // defined in GLContext.h for renderer IDs.
  const char* rendererMatchStrings[size_t(GLRenderer::Other) + 1] = {
      "Adreno 200",
      "Adreno 205",
      "Adreno (TM) 200",
      "Adreno (TM) 205",
      "Adreno (TM) 305",
      "Adreno (TM) 320",
      "Adreno (TM) 330",
      "Adreno (TM) 420",
      "Mali-400 MP",
      "Mali-450 MP",
      "Mali-T",
      "PowerVR SGX 530",
      "PowerVR SGX 540",
      "PowerVR SGX 544MP",
      "NVIDIA Tegra",
      "Android Emulator",
      "Gallium 0.4 on llvmpipe",
      "Intel HD Graphics 3000 OpenGL Engine",
      "Microsoft Basic Render Driver",
      "Samsung Xclipse",
      "Unknown"};

  mRenderer = GLRenderer::Other;
  for (size_t i = 0; i < size_t(GLRenderer::Other); ++i) {
    if (DoesStringMatch(glRendererString, rendererMatchStrings[i])) {
      mRenderer = GLRenderer(i);
      break;
    }
  }

  {
    const auto versionStr = (const char*)fGetString(LOCAL_GL_VERSION);
    if (strstr(versionStr, "Mesa")) {
      mIsMesa = true;
    }
  }

  const auto Once = []() {
    static bool did = false;
    if (did) return false;
    did = true;
    return true;
  };

  bool printRenderer = ShouldSpew();
  printRenderer |= (kIsDebug && Once());
  if (printRenderer) {
    printf_stderr("GL_VENDOR: %s\n", glVendorString);
    printf_stderr("mVendor: %s\n", vendorMatchStrings[size_t(mVendor)]);
    printf_stderr("GL_RENDERER: %s\n", glRendererString);
    printf_stderr("mRenderer: %s\n", rendererMatchStrings[size_t(mRenderer)]);
    printf_stderr("mIsMesa: %i\n"int(mIsMesa));
  }

  ////////////////

  if (mVersion >= 300) {  // Both GL3 and ES3.
    const SymLoadStruct symbols[] = {
        {(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)) {
      mProfile = ContextProfile::OpenGLCore;
    } else {
      mProfile = ContextProfile::OpenGLCompatibility;
    }
  }
  MOZ_ASSERT(mProfile != ContextProfile::Unknown);

  if (ShouldSpew()) {
    const char* profileStr = "";
    if (mProfile == ContextProfile::OpenGLES) {
      profileStr = " es";
    } else if (mProfile == ContextProfile::OpenGLCore) {
      profileStr = " core";
    }
    printf_stderr("Detected profile: %u%s\n", mVersion, profileStr);
  }

  InitFeatures();

  ////

  // Disable extensions with partial or incorrect support.
  if (WorkAroundDriverBugs()) {
    if (Renderer() == GLRenderer::AdrenoTM320) {
      MarkUnsupported(GLFeature::standard_derivatives);
    }

    if (Renderer() == GLRenderer::AndroidEmulator) {
      // Bug 1665300
      mSymbols.fGetGraphicsResetStatus = 0;
    }

    if (Vendor() == GLVendor::Vivante) {
      // bug 958256
      MarkUnsupported(GLFeature::standard_derivatives);
    }

    if (Renderer() == GLRenderer::MicrosoftBasicRenderDriver) {
      // Bug 978966: on Microsoft's "Basic Render Driver" (software renderer)
      // multisampling hardcodes blending with the default blendfunc, which
      // breaks WebGL.
      MarkUnsupported(GLFeature::framebuffer_multisample);
    }

    if (IsMesa()) {
      // DrawElementsInstanced hangs the driver.
      MarkUnsupported(GLFeature::robust_buffer_access_behavior);
    }

    if (Renderer() == GLRenderer::SamsungXclipse) {
      MarkUnsupported(GLFeature::invalidate_framebuffer);
    }
  }

  if (IsExtensionSupported(GLContext::ARB_pixel_buffer_object)) {
    MOZ_ASSERT(
        (mSymbols.fMapBuffer && mSymbols.fUnmapBuffer),
        "ARB_pixel_buffer_object supported without glMapBuffer/UnmapBuffer"
        " being available!");
  }

  ////////////////////////////////////////////////////////////////////////////

  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),
        CORE_SYMBOL(DeleteRenderbuffers),
        CORE_SYMBOL(GenRenderbuffers),
        CORE_SYMBOL(RenderbufferStorage),
        CORE_SYMBOL(RenderbufferStorageMultisample),
        CORE_SYMBOL(GetRenderbufferParameteriv),
        CORE_SYMBOL(IsFramebuffer),
        CORE_SYMBOL(BindFramebuffer),
        CORE_SYMBOL(DeleteFramebuffers),
        CORE_SYMBOL(GenFramebuffers),
        CORE_SYMBOL(CheckFramebufferStatus),
        CORE_SYMBOL(FramebufferTexture2D),
        CORE_SYMBOL(FramebufferTextureLayer),
        CORE_SYMBOL(FramebufferRenderbuffer),
        CORE_SYMBOL(GetFramebufferAttachmentParameteriv),
        CORE_SYMBOL(BlitFramebuffer),
        CORE_SYMBOL(GenerateMipmap),
        END_SYMBOLS};
    fnLoadForFeature(symbols, GLFeature::framebuffer_object);
  }

  if (!IsSupported(GLFeature::framebuffer_object)) {
    // Check for aux symbols based on extensions
    if (IsSupported(GLFeature::framebuffer_object_EXT_OES)) {
      const SymLoadStruct symbols[] = {
          CORE_EXT_SYMBOL2(IsRenderbuffer, EXT, OES),
          CORE_EXT_SYMBOL2(BindRenderbuffer, EXT, OES),
          CORE_EXT_SYMBOL2(DeleteRenderbuffers, EXT, OES),
          CORE_EXT_SYMBOL2(GenRenderbuffers, EXT, OES),
          CORE_EXT_SYMBOL2(RenderbufferStorage, EXT, OES),
          CORE_EXT_SYMBOL2(GetRenderbufferParameteriv, EXT, OES),
          CORE_EXT_SYMBOL2(IsFramebuffer, EXT, OES),
          CORE_EXT_SYMBOL2(BindFramebuffer, EXT, OES),
          CORE_EXT_SYMBOL2(DeleteFramebuffers, EXT, OES),
          CORE_EXT_SYMBOL2(GenFramebuffers, EXT, OES),
          CORE_EXT_SYMBOL2(CheckFramebufferStatus, EXT, OES),
          CORE_EXT_SYMBOL2(FramebufferTexture2D, EXT, OES),
          CORE_EXT_SYMBOL2(FramebufferRenderbuffer, EXT, OES),
          CORE_EXT_SYMBOL2(GetFramebufferAttachmentParameteriv, EXT, OES),
          CORE_EXT_SYMBOL2(GenerateMipmap, EXT, OES),
          END_SYMBOLS};
      fnLoadForFeature(symbols, GLFeature::framebuffer_object_EXT_OES);
    }

    if (IsSupported(GLFeature::framebuffer_blit)) {
      const SymLoadStruct symbols[] = {
          EXT_SYMBOL3(BlitFramebuffer, ANGLE, EXT, NV), END_SYMBOLS};
      fnLoadForFeature(symbols, GLFeature::framebuffer_blit);
    }

    if (IsSupported(GLFeature::framebuffer_multisample)) {
      const SymLoadStruct symbols[] = {
          EXT_SYMBOL3(RenderbufferStorageMultisample, ANGLE, APPLE, EXT),
          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 (!fnLoadSymbols(symbols,
                         "ARB_geometry_shader4/NV_geometry_program4")) {
        MarkExtensionUnsupported(GLContext::ARB_geometry_shader4);
        MarkExtensionUnsupported(GLContext::NV_geometry_program4);
      }
    }
  }

  if (!IsSupported(GLFeature::framebuffer_object) &&
      !IsSupported(GLFeature::framebuffer_object_EXT_OES)) {
    NS_ERROR("GLContext requires support for framebuffer objects.");
    return false;
  }
  MOZ_RELEASE_ASSERT(mSymbols.fBindFramebuffer,
                     "GFX: mSymbols.fBindFramebuffer zero or not set.");

  ////////////////

  const auto err = fGetError();
  MOZ_RELEASE_ASSERT(!IsBadCallError(err));
  if (err) return false;

  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);
  raw_fGetIntegerv(LOCAL_GL_MAX_VIEWPORT_DIMS, mMaxViewportDims);

  if (mWorkAroundDriverBugs) {
    int maxTexSize = INT32_MAX;
    int maxCubeSize = INT32_MAX;
#ifdef XP_MACOSX
    // https://bugzilla.mozilla.org/show_bug.cgi?id=1544446
    // Mojave exposes 16k textures, but gives FRAMEBUFFER_UNSUPPORTED for any
    // 16k*16k FB except rgba8 without depth/stencil.
    // The max supported sizes changes based on involved formats.
    // (RGBA32F more restrictive than RGBA16F)
    maxTexSize = 8192;
#endif
#ifdef MOZ_X11
    if (mVendor == GLVendor::Nouveau) {
      // see bug 814716. Clamp MaxCubeMapTextureSize at 2K for Nouveau.
      maxCubeSize = 2048;
    } else if (mVendor == GLVendor::Intel) {
      // Bug 1199923. Driver seems to report a larger max size than
      // actually supported.
      maxTexSize = mMaxTextureSize / 2;
    }
    // Bug 1367570. Explicitly set vertex attributes [1,3] to opaque
    // black because Nvidia doesn't do it for us.
    if (mVendor == GLVendor::NVIDIA) {
      for (size_t i = 1; i <= 3; ++i) {
        mSymbols.fVertexAttrib4f(i, 0, 0, 0, 1);
      }
    }
#endif
    if (Renderer() == GLRenderer::AdrenoTM420) {
      // see bug 1194923. Calling glFlush before glDeleteFramebuffers
      // prevents occasional driver crash.
      mNeedsFlushBeforeDeleteFB = true;
    }

    // -

    const auto fnLimit = [&](intconst driver, const int limit) {
      if (*driver > limit) {
        *driver = limit;
        mNeedsTextureSizeChecks = true;
      }
    };

    fnLimit(&mMaxTextureSize, maxTexSize);
    fnLimit(&mMaxRenderbufferSize, maxTexSize);

    maxCubeSize = std::min(maxCubeSize, maxTexSize);
    fnLimit(&mMaxCubeMapTextureSize, maxCubeSize);
  }

  if (IsSupported(GLFeature::framebuffer_multisample)) {
    fGetIntegerv(LOCAL_GL_MAX_SAMPLES, (GLint*)&mMaxSamples);
  }

  mMaxTexOrRbSize = std::min(mMaxTextureSize, mMaxRenderbufferSize);

  ////////////////////////////////////////////////////////////////////////////

  // We're ready for final setup.
  fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, 0);
  MOZ_GL_ASSERT(this, IsCurrent());

  if (ShouldSpew() && IsExtensionSupported(KHR_debug)) {
    fEnable(LOCAL_GL_DEBUG_OUTPUT);
    fDisable(LOCAL_GL_DEBUG_OUTPUT_SYNCHRONOUS);
    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 SymbolLoader& loader) {
  const auto fnLoadForExt = [&](const SymLoadStruct* list, GLExtensions ext) {
    return this->LoadExtSymbols(loader, list, ext);
  };

  const auto fnLoadForFeature = [&](const SymLoadStruct* list,
                                    GLFeature feature) {
    return this->LoadFeatureSymbols(loader, list, feature);
  };

  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)) {
    const auto resetStrategy =
        GetIntAs<GLuint>(LOCAL_GL_RESET_NOTIFICATION_STRATEGY);
    if (resetStrategy != LOCAL_GL_LOSE_CONTEXT_ON_RESET) {
      NS_WARNING(
          "Robustness supported, strategy is not LOSE_CONTEXT_ON_RESET!");
      if (ShouldSpew()) {
        const bool isDisabled =
            (resetStrategy == LOCAL_GL_NO_RESET_NOTIFICATION);
        printf_stderr("Strategy: %s (0x%04x)",
                      (isDisabled ? "disabled" : "unrecognized"),
                      resetStrategy);
      }
      MarkUnsupported(GLFeature::robustness);
    }
  }

  if (IsSupported(GLFeature::sync)) {
    const SymLoadStruct symbols[] = {
        CORE_SYMBOL(FenceSync),  CORE_SYMBOL(IsSync),
        CORE_SYMBOL(DeleteSync), CORE_SYMBOL(ClientWaitSync),
        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,
         {{"glEGLImageTargetTexture2DOES"}}},
        {(PRFuncPtr*)&mSymbols.fEGLImageTargetRenderbufferStorage,
         {{"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);
  }

  if (IsExtensionSupported(APPLE_fence)) {
    const SymLoadStruct symbols[] = {CORE_SYMBOL(FinishObjectAPPLE),
                                     CORE_SYMBOL(TestObjectAPPLE), END_SYMBOLS};
    fnLoadForExt(symbols, APPLE_fence);
  }

  // clang-format off

    if (IsSupported(GLFeature::vertex_array_object)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fIsVertexArray, {{ "glIsVertexArray" }} },
            { (PRFuncPtr*) &mSymbols.fGenVertexArrays, {{ "glGenVertexArrays" }} },
            { (PRFuncPtr*) &mSymbols.fBindVertexArray, {{ "glBindVertexArray" }} },
            { (PRFuncPtr*) &mSymbols.fDeleteVertexArrays, {{ "glDeleteVertexArrays" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "glDrawArraysInstanced" }} },
            { (PRFuncPtr*) &mSymbols.fDrawElementsInstanced, {{ "glDrawElementsInstanced" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "glVertexAttribDivisor" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "glTexStorage2D" }} },
            { (PRFuncPtr*) &mSymbols.fTexStorage3D, {{ "glTexStorage3D" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "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, {{ "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, {{ "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::transform_feedback2)) {
            // Also mark bind_buffer_offset as unsupported.
            MarkUnsupported(GLFeature::bind_buffer_offset);
        }
    }

    if (IsSupported(GLFeature::bind_buffer_offset)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fBindBufferOffset, {{ "glBindBufferOffset" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fBindBufferOffset,
              {{ "glBindBufferOffsetEXT""glBindBufferOffsetNV" }}
            },
            END_SYMBOLS
        };
        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::bind_buffer_offset);
    }

    if (IsSupported(GLFeature::query_counter)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fQueryCounter, {{ "glQueryCounter" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fQueryCounter, {{ "glQueryCounterEXT""glQueryCounterANGLE" }} },
            END_SYMBOLS
        };
        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::query_counter);
    }

    if (IsSupported(GLFeature::query_objects)) {
        const SymLoadStruct coreSymbols[] = {
            { (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, {{ "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, {{ "glGetQueryObjecti64v" }} },
            { (PRFuncPtr*) &mSymbols.fGetQueryObjectui64v, {{ "glGetQueryObjectui64v" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "glGetQueryObjectiv" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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,  {{ "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, {{ "glCopyBufferSubData" }} },
            END_SYMBOLS
        };
        fnLoadForFeature(symbols, GLFeature::copy_buffer);
    }

    if (IsSupported(GLFeature::draw_buffers)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fDrawBuffers, {{ "glDrawBuffers" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fDrawBuffers, {{ "glDrawBuffersARB""glDrawBuffersEXT" }} },
            END_SYMBOLS
        };
        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_buffers);
    }

    if (IsSupported(GLFeature::draw_buffers_indexed)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fBlendEquationSeparatei, {{ "glBlendEquationSeparatei" }} },
            { (PRFuncPtr*) &mSymbols.fBlendFuncSeparatei, {{ "glBlendFuncSeparatei" }} },
            { (PRFuncPtr*) &mSymbols.fColorMaski, {{ "glColorMaski" }} },
            { (PRFuncPtr*) &mSymbols.fDisablei, {{ "glDisablei" }} },
            { (PRFuncPtr*) &mSymbols.fEnablei, {{ "glEnablei" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fBlendEquationSeparatei, {{ "glBlendEquationSeparateiOES" }} },
            { (PRFuncPtr*) &mSymbols.fBlendFuncSeparatei, {{ "glBlendFuncSeparateiOES" }} },
            { (PRFuncPtr*) &mSymbols.fColorMaski, {{ "glColorMaskiOES" }} },
            { (PRFuncPtr*) &mSymbols.fDisablei, {{ "glDisableiOES" }} },
            { (PRFuncPtr*) &mSymbols.fEnablei, {{ "glEnableiOES" }} },
            END_SYMBOLS
        };
        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::draw_buffers_indexed);
    }

    if (IsSupported(GLFeature::get_integer_indexed)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fGetIntegeri_v, {{ "glGetIntegeri_v" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] ={
            { (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, {{ "glGetInteger64i_v" }} },
            END_SYMBOLS
        };
        fnLoadForFeature(symbols, GLFeature::get_integer64_indexed);
    }

    if (IsSupported(GLFeature::gpu_shader4)) {
        const SymLoadStruct symbols[] = {
            { (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, {{ "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, {{ "glTexImage3D" }} },
            { (PRFuncPtr*) &mSymbols.fTexSubImage3D, {{ "glTexSubImage3D" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fTexImage3D, {{ "glTexImage3DOES" }} },
            { (PRFuncPtr*) &mSymbols.fTexSubImage3D, {{ "glTexSubImage3DOES" }} },
            END_SYMBOLS
        };
        fnLoadFeatureByCore(coreSymbols, extSymbols, GLFeature::texture_3D);
    }

    if (IsSupported(GLFeature::texture_3D_compressed)) {
        const SymLoadStruct coreSymbols[] = {
            { (PRFuncPtr*) &mSymbols.fCompressedTexImage3D, {{ "glCompressedTexImage3D" }} },
            { (PRFuncPtr*) &mSymbols.fCompressedTexSubImage3D, {{ "glCompressedTexSubImage3D" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "glCopyTexSubImage3D" }} },
            END_SYMBOLS
        };
        const SymLoadStruct extSymbols[] = {
            { (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, {{ "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, {{ "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,    {{ "glInvalidateFramebuffer" }} },
            { (PRFuncPtr*) &mSymbols.fInvalidateSubFramebuffer, {{ "glInvalidateSubFramebuffer" }} },
            END_SYMBOLS
        };
        fnLoadForFeature(symbols, GLFeature::invalidate_framebuffer);
    }

    if (IsSupported(GLFeature::multiview)) {
        const SymLoadStruct symbols[] = {
            { (PRFuncPtr*) &mSymbols.fFramebufferTextureMultiview, {{
              "glFramebufferTextureMultiviewOVR",
              "glFramebufferTextureMultiviewLayeredANGLE"
            }} },
            END_SYMBOLS
        };
        fnLoadForFeature(symbols, GLFeature::multiview);
    }

    if (IsSupported(GLFeature::prim_restart)) {
        const SymLoadStruct symbols[] = {
            { (PRFuncPtr*) &mSymbols.fPrimitiveRestartIndex,    {{ "glPrimitiveRestartIndex""glPrimitiveRestartIndexNV" }} },
            END_SYMBOLS
        };
        fnLoadForFeature(symbols, GLFeature::prim_restart);
    }

    if (IsExtensionSupported(KHR_debug)) {
        const SymLoadStruct symbols[] = {
            { (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,    {{ "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, {{"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);
  }

  if (IsExtensionSupported(APPLE_framebuffer_multisample)) {
    const SymLoadStruct symbols[] = {
        CORE_SYMBOL(ResolveMultisampleFramebufferAPPLE), END_SYMBOLS};
    fnLoadForExt(symbols, APPLE_framebuffer_multisample);
  }

  if (IsSupported(GLFeature::provoking_vertex)) {
    const SymLoadStruct symbols[] = {{(PRFuncPtr*)&mSymbols.fProvokingVertex,
                                      {{
                                          "glProvokingVertex",
                                          "glProvokingVertexANGLE",
                                          "glProvokingVertexEXT",
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=96 H=98 G=96

¤ Dauer der Verarbeitung: 0.22 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.