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 119 kB image not shown  

SSL GLContext.h

  Interaktion und
PortierbarkeitC
 

/* -*- 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/. */


#ifndef GLCONTEXT_H_
#define GLCONTEXT_H_

#include <bitset>
#include <stdint.h>
#include <stdio.h>
#include <stack>
#include <vector>

#ifdef DEBUG
#  include <string.h>
#endif

#ifdef GetClassName
#  undef GetClassName
#endif

// Define MOZ_GL_DEBUG_BUILD unconditionally to enable GL debugging in opt
// builds.
#ifdef DEBUG
#  define MOZ_GL_DEBUG_BUILD 1
#endif

#include "mozilla/IntegerRange.h"
#include "mozilla/RefPtr.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/ThreadLocal.h"

#include "MozFramebuffer.h"
#include "nsTArray.h"
#include "GLConsts.h"
#include "GLDefs.h"
#include "GLTypes.h"
#include "nsRegionFwd.h"
#include "nsString.h"
#include "GLContextTypes.h"
#include "GLContextSymbols.h"
#include "base/platform_thread.h"  // for PlatformThreadId
#include "mozilla/GenericRefCounted.h"
#include "mozilla/WeakPtr.h"

template <class ElemT, class... More>
constexpr inline std::array<ElemT, 1 + sizeof...(More)> make_array(
    ElemT&& arg1, More&&... more) {
  return {std::forward<ElemT>(arg1), std::forward<ElemT>(more)...};
}

#ifdef MOZ_WIDGET_ANDROID
#  include "mozilla/ProfilerLabels.h"
#endif

namespace mozilla {

namespace gl {
class GLBlitHelper;
class GLLibraryEGL;
class GLReadTexImageHelper;
class SharedSurface;
class SymbolLoader;
struct SymLoadStruct;
}  // namespace gl

namespace layers {
class ColorTextureLayerProgram;
}  // namespace layers

namespace widget {
class CompositorWidget;
}  // namespace widget
}  // namespace mozilla

namespace mozilla {
namespace gl {

enum class GLFeature {
  bind_buffer_offset,
  blend_minmax,
  clear_buffers,
  copy_buffer,
  depth_clamp,
  depth_texture,
  draw_buffers,
  draw_buffers_indexed,
  draw_instanced,
  element_index_uint,
  ES2_compatibility,
  ES3_compatibility,
  EXT_color_buffer_float,
  frag_color_float,
  frag_depth,
  framebuffer_blit,
  framebuffer_multisample,
  framebuffer_object,
  framebuffer_object_EXT_OES,
  get_integer_indexed,
  get_integer64_indexed,
  get_query_object_i64v,
  get_query_object_iv,
  gpu_shader4,
  instanced_arrays,
  instanced_non_arrays,
  internalformat_query,
  invalidate_framebuffer,
  map_buffer_range,
  multiview,
  occlusion_query,
  occlusion_query_boolean,
  occlusion_query2,
  packed_depth_stencil,
  prim_restart,
  prim_restart_fixed,
  provoking_vertex,
  query_counter,
  query_objects,
  query_time_elapsed,
  read_buffer,
  renderbuffer_color_float,
  renderbuffer_color_half_float,
  robust_buffer_access_behavior,
  robustness,
  sRGB,
  sampler_objects,
  seamless_cube_map_opt_in,
  shader_texture_lod,
  split_framebuffer,
  standard_derivatives,
  sync,
  texture_3D,
  texture_3D_compressed,
  texture_3D_copy,
  texture_compression_bptc,
  texture_compression_rgtc,
  texture_float,
  texture_float_linear,
  texture_half_float,
  texture_half_float_linear,
  texture_non_power_of_two,
  texture_norm16,
  texture_rg,
  texture_storage,
  texture_swizzle,
  transform_feedback2,
  uniform_buffer_object,
  uniform_matrix_nonsquare,
  vertex_array_object,
  EnumMax
};

enum class ContextProfile : uint8_t {
  Unknown = 0,
  OpenGLCore,
  OpenGLCompatibility,
  OpenGLES
};

enum class GLRenderer {
  Adreno200,
  Adreno205,
  AdrenoTM200,
  AdrenoTM205,
  AdrenoTM305,
  AdrenoTM320,
  AdrenoTM330,
  AdrenoTM420,
  Mali400MP,
  Mali450MP,
  MaliT,
  SGX530,
  SGX540,
  SGX544MP,
  Tegra,
  AndroidEmulator,
  GalliumLlvmpipe,
  IntelHD3000,
  MicrosoftBasicRenderDriver,
  SamsungXclipse,
  Other
};

class GLContext : public GenericAtomicRefCounted, public SupportsWeakPtr {
 public:
  static MOZ_THREAD_LOCAL(const GLContext*) sCurrentContext;

  static void InvalidateCurrentContext();

  const GLContextDesc mDesc;

  bool mImplicitMakeCurrent = false;
  bool mUseTLSIsCurrent;

  static void ResetTLSCurrentContext();

  class TlsScope final {
    const WeakPtr<GLContext> mGL;
    const bool mWasTlsOk;

   public:
    explicit TlsScope(GLContext* const gl, bool invalidate = false)
        : mGL(gl), mWasTlsOk(gl && gl->mUseTLSIsCurrent) {
      if (mGL) {
        if (invalidate) {
          InvalidateCurrentContext();
        }
        mGL->mUseTLSIsCurrent = true;
      }
    }

    ~TlsScope() {
      if (mGL) {
        mGL->mUseTLSIsCurrent = mWasTlsOk;
      }
    }
  };

  // -----------------------------------------------------------------------------
  // basic getters
 public:
  /**
   * Returns true if the context is using ANGLE. This should only be overridden
   * for an ANGLE implementation.
   */

  virtual bool IsANGLE() const { return false; }

  /**
   * Returns true if the context is using WARP. This should only be overridden
   * for an ANGLE implementation.
   */

  virtual bool IsWARP() const { return false; }

  virtual void GetWSIInfo(nsCString* const out) const = 0;

  /**
   * Return true if we are running on a OpenGL core profile context
   */

  inline bool IsCoreProfile() const {
    MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");

    return mProfile == ContextProfile::OpenGLCore;
  }

  /**
   * Return true if we are running on a OpenGL compatibility profile context
   * (legacy profile 2.1 on Max OS X)
   */

  inline bool IsCompatibilityProfile() const {
    MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");

    return mProfile == ContextProfile::OpenGLCompatibility;
  }

  inline bool IsGLES() const {
    MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");

    return mProfile == ContextProfile::OpenGLES;
  }

  inline bool IsAtLeast(ContextProfile profile, unsigned int version) const {
    MOZ_ASSERT(profile != ContextProfile::Unknown,
               "IsAtLeast: bad <profile> parameter");
    MOZ_ASSERT(mProfile != ContextProfile::Unknown, "unknown context profile");
    MOZ_ASSERT(mVersion != 0, "unknown context version");

    if (version > mVersion) {
      return false;
    }

    return profile == mProfile;
  }

  /**
   * Return the version of the context.
   * Example :
   *   If this a OpenGL 2.1, that will return 210
   */

  inline uint32_t Version() const { return mVersion; }

  inline uint32_t ShadingLanguageVersion() const {
    return mShadingLanguageVersion;
  }

  GLVendor Vendor() const { return mVendor; }
  GLRenderer Renderer() const { return mRenderer; }
  bool IsMesa() const { return mIsMesa; }

  bool IsContextLost() const { return mContextLost; }

  bool CheckContextLost() const {
    mTopError = GetError();
    return IsContextLost();
  }

  bool HasPBOState() const { return (!IsGLES() || Version() >= 300); }

  /**
   * If this context is double-buffered, returns TRUE.
   */

  virtual bool IsDoubleBuffered() const { return false; }

  virtual GLContextType GetContextType() const = 0;

  virtual bool IsCurrentImpl() const = 0;
  virtual bool MakeCurrentImpl() const = 0;

  bool IsCurrent() const {
    if (mImplicitMakeCurrent) return MakeCurrent();

    return IsCurrentImpl();
  }

  bool MakeCurrent(bool aForce = falseconst;

  /**
   * Get the default framebuffer for this context.
   */

  UniquePtr<MozFramebuffer> mOffscreenDefaultFb;

  bool CreateOffscreenDefaultFb(const gfx::IntSize& size);

  virtual GLuint GetDefaultFramebuffer() {
    if (mOffscreenDefaultFb) {
      return mOffscreenDefaultFb->mFB;
    }
    return 0;
  }

  /**
   * mVersion store the OpenGL's version, multiplied by 100. For example, if
   * the context is an OpenGL 2.1 context, mVersion value will be 210.
   */

  uint32_t mVersion = 0;
  ContextProfile mProfile = ContextProfile::Unknown;

  uint32_t mShadingLanguageVersion = 0;

  GLVendor mVendor = GLVendor::Other;
  GLRenderer mRenderer = GLRenderer::Other;
  bool mIsMesa = false;

  // -----------------------------------------------------------------------------
  // Extensions management
  /**
   * This mechanism is designed to know if an extension is supported. In the
   * long term, we would like to only use the extension group queries XXX_* to
   * have full compatibility with context version and profiles (especialy the
   * core that officialy don't bring any extensions).
   */


  /**
   * Known GL extensions that can be queried by
   * IsExtensionSupported.  The results of this are cached, and as
   * such it's safe to use this even in performance critical code.
   * If you add to this array, remember to add to the string names
   * in GLContext.cpp.
   */

  enum GLExtensions {
    Extension_None = 0,
    AMD_compressed_ATC_texture,
    ANGLE_depth_texture,
    ANGLE_framebuffer_blit,
    ANGLE_framebuffer_multisample,
    ANGLE_instanced_arrays,
    ANGLE_multiview,
    ANGLE_provoking_vertex,
    ANGLE_texture_compression_dxt3,
    ANGLE_texture_compression_dxt5,
    ANGLE_timer_query,
    APPLE_client_storage,
    APPLE_fence,
    APPLE_framebuffer_multisample,
    APPLE_sync,
    APPLE_texture_range,
    APPLE_vertex_array_object,
    ARB_ES2_compatibility,
    ARB_ES3_compatibility,
    ARB_color_buffer_float,
    ARB_compatibility,
    ARB_copy_buffer,
    ARB_depth_clamp,
    ARB_depth_texture,
    ARB_draw_buffers,
    ARB_draw_instanced,
    ARB_framebuffer_object,
    ARB_framebuffer_sRGB,
    ARB_geometry_shader4,
    ARB_half_float_pixel,
    ARB_instanced_arrays,
    ARB_internalformat_query,
    ARB_invalidate_subdata,
    ARB_map_buffer_range,
    ARB_occlusion_query2,
    ARB_pixel_buffer_object,
    ARB_provoking_vertex,
    ARB_robust_buffer_access_behavior,
    ARB_robustness,
    ARB_sampler_objects,
    ARB_seamless_cube_map,
    ARB_shader_texture_lod,
    ARB_sync,
    ARB_texture_compression,
    ARB_texture_compression_bptc,
    ARB_texture_compression_rgtc,
    ARB_texture_float,
    ARB_texture_non_power_of_two,
    ARB_texture_rectangle,
    ARB_texture_rg,
    ARB_texture_storage,
    ARB_texture_swizzle,
    ARB_timer_query,
    ARB_transform_feedback2,
    ARB_uniform_buffer_object,
    ARB_vertex_array_object,
    CHROMIUM_color_buffer_float_rgb,
    CHROMIUM_color_buffer_float_rgba,
    EXT_bgra,
    EXT_blend_minmax,
    EXT_color_buffer_float,
    EXT_color_buffer_half_float,
    EXT_copy_texture,
    EXT_depth_clamp,
    EXT_disjoint_timer_query,
    EXT_draw_buffers,
    EXT_draw_buffers2,
    EXT_draw_instanced,
    EXT_float_blend,
    EXT_frag_depth,
    EXT_framebuffer_blit,
    EXT_framebuffer_multisample,
    EXT_framebuffer_object,
    EXT_framebuffer_sRGB,
    EXT_gpu_shader4,
    EXT_map_buffer_range,
    EXT_multisampled_render_to_texture,
    EXT_occlusion_query_boolean,
    EXT_packed_depth_stencil,
    EXT_provoking_vertex,
    EXT_read_format_bgra,
    EXT_robustness,
    EXT_sRGB,
    EXT_sRGB_write_control,
    EXT_shader_texture_lod,
    EXT_texture_compression_bptc,
    EXT_texture_compression_dxt1,
    EXT_texture_compression_rgtc,
    EXT_texture_compression_s3tc,
    EXT_texture_compression_s3tc_srgb,
    EXT_texture_filter_anisotropic,
    EXT_texture_format_BGRA8888,
    EXT_texture_norm16,
    EXT_texture_sRGB,
    EXT_texture_storage,
    EXT_timer_query,
    EXT_transform_feedback,
    EXT_unpack_subimage,
    EXT_semaphore,
    EXT_semaphore_fd,
    EXT_memory_object,
    EXT_memory_object_fd,
    IMG_read_format,
    IMG_texture_compression_pvrtc,
    IMG_texture_npot,
    KHR_debug,
    KHR_parallel_shader_compile,
    KHR_robust_buffer_access_behavior,
    KHR_robustness,
    KHR_texture_compression_astc_hdr,
    KHR_texture_compression_astc_ldr,
    NV_draw_instanced,
    NV_fence,
    NV_framebuffer_blit,
    NV_geometry_program4,
    NV_half_float,
    NV_instanced_arrays,
    NV_primitive_restart,
    NV_texture_barrier,
    NV_transform_feedback,
    NV_transform_feedback2,
    OES_EGL_image,
    OES_EGL_image_external,
    OES_EGL_sync,
    OES_compressed_ETC1_RGB8_texture,
    OES_depth24,
    OES_depth32,
    OES_depth_texture,
    OES_draw_buffers_indexed,
    OES_element_index_uint,
    OES_fbo_render_mipmap,
    OES_framebuffer_object,
    OES_packed_depth_stencil,
    OES_rgb8_rgba8,
    OES_standard_derivatives,
    OES_stencil8,
    OES_texture_3D,
    OES_texture_float,
    OES_texture_float_linear,
    OES_texture_half_float,
    OES_texture_half_float_linear,
    OES_texture_npot,
    OES_vertex_array_object,
    OVR_multiview2,
    Extensions_Max,
    Extensions_End
  };

  bool IsExtensionSupported(GLExtensions aKnownExtension) const {
    return mAvailableExtensions[aKnownExtension];
  }

 protected:
  void MarkExtensionUnsupported(GLExtensions aKnownExtension) {
    mAvailableExtensions[aKnownExtension] = 0;
  }

  void MarkExtensionSupported(GLExtensions aKnownExtension) {
    mAvailableExtensions[aKnownExtension] = 1;
  }

  std::bitset<Extensions_Max> mAvailableExtensions;

  // -----------------------------------------------------------------------------
  // Feature queries
  /*
   * This mecahnism introduces a new way to check if a OpenGL feature is
   * supported, regardless of whether it is supported by an extension or
   * natively by the context version/profile
   */

 public:
  bool IsSupported(GLFeature feature) const {
    return mAvailableFeatures[size_t(feature)];
  }

  static const char* GetFeatureName(GLFeature feature);

 private:
  std::bitset<size_t(GLFeature::EnumMax)> mAvailableFeatures;

  /**
   * Init features regarding OpenGL extension and context version and profile
   */

  void InitFeatures();

  /**
   * Mark the feature and associated extensions as unsupported
   */

  void MarkUnsupported(GLFeature feature);

  /**
   * Is this feature supported using the core (unsuffixed) symbols?
   */

  bool IsFeatureProvidedByCoreSymbols(GLFeature feature);

  // -----------------------------------------------------------------------------
  // Error handling

 private:
  mutable bool mContextLost = false;
  mutable GLenum mTopError = 0;

 protected:
  void OnContextLostError() const;

 public:
  static std::string GLErrorToString(GLenum aError);

  static bool IsBadCallError(const GLenum err) {
    return !(err == 0 || err == LOCAL_GL_CONTEXT_LOST);
  }

  class LocalErrorScope;

 private:
  mutable std::stack<const LocalErrorScope*> mLocalErrorScopeStack;
  mutable UniquePtr<LocalErrorScope> mDebugErrorScope;

  ////////////////////////////////////
  // Use this safer option.

 public:
  class LocalErrorScope {
    const GLContext& mGL;
    GLenum mOldTop;
    bool mHasBeenChecked;

   public:
    explicit LocalErrorScope(const GLContext& gl)
        : mGL(gl), mHasBeenChecked(false) {
      mGL.mLocalErrorScopeStack.push(this);
      mOldTop = mGL.GetError();
    }

    /// Never returns CONTEXT_LOST.
    GLenum GetError() {
      MOZ_ASSERT(!mHasBeenChecked);
      mHasBeenChecked = true;

      const auto ret = mGL.GetError();
      if (ret == LOCAL_GL_CONTEXT_LOST) return 0;
      return ret;
    }

    ~LocalErrorScope() {
      MOZ_ASSERT(mHasBeenChecked);

      MOZ_ASSERT(!IsBadCallError(mGL.GetError()));

      MOZ_ASSERT(mGL.mLocalErrorScopeStack.top() == this);
      mGL.mLocalErrorScopeStack.pop();

      mGL.mTopError = mOldTop;
    }
  };

  // -

  bool GetPotentialInteger(GLenum pname, GLint* param) {
    LocalErrorScope localError(*this);

    fGetIntegerv(pname, param);

    GLenum err = localError.GetError();
    MOZ_ASSERT_IF(err != LOCAL_GL_NO_ERROR, err == LOCAL_GL_INVALID_ENUM);
    return err == LOCAL_GL_NO_ERROR;
  }

  void DebugCallback(GLenum source, GLenum type, GLuint id, GLenum severity,
                     GLsizei length, const GLchar* message);

 private:
  static void GLAPIENTRY StaticDebugCallback(GLenum source, GLenum type,
                                             GLuint id, GLenum severity,
                                             GLsizei length,
                                             const GLchar* message,
                                             const GLvoid* userParam);

  // -----------------------------------------------------------------------------
  // Debugging implementation
 private:
#ifndef MOZ_FUNCTION_NAME
#  ifdef __GNUC__
#    define MOZ_FUNCTION_NAME __PRETTY_FUNCTION__
#  elif defined(_MSC_VER)
#    define MOZ_FUNCTION_NAME __FUNCTION__
#  else
#    define MOZ_FUNCTION_NAME \
      __func__  // defined in C99, supported in various C++ compilers. Just raw
                // function name.
#  endif
#endif

#ifdef MOZ_WIDGET_ANDROID
// Record the name of the GL call for better hang stacks on Android.
#  define ANDROID_ONLY_PROFILER_LABEL AUTO_PROFILER_LABEL(__func__, GRAPHICS);
#else
#  define ANDROID_ONLY_PROFILER_LABEL
#endif

#define BEFORE_GL_CALL                               \
  ANDROID_ONLY_PROFILER_LABEL                        \
  if (MOZ_LIKELY(BeforeGLCall(MOZ_FUNCTION_NAME))) { \
    do {                                             \
  } while (0)

#define AFTER_GL_CALL             \
  AfterGLCall(MOZ_FUNCTION_NAME); \
  }                               \
  do {                            \
  } while (0)

  void BeforeGLCall_Debug(const char* funcName) const;
  void AfterGLCall_Debug(const char* funcName) const;
  static void OnImplicitMakeCurrentFailure(const char* funcName);

  bool BeforeGLCall(const charconst funcName) const {
    if (mImplicitMakeCurrent) {
      if (MOZ_UNLIKELY(!MakeCurrent())) {
        if (!mContextLost) {
          OnImplicitMakeCurrentFailure(funcName);
        }
        return false;
      }
    }
    MOZ_GL_ASSERT(this, IsCurrentImpl());

    if (MOZ_UNLIKELY(mDebugFlags)) {
      BeforeGLCall_Debug(funcName);
    }
    return true;
  }

  void AfterGLCall(const charconst funcName) const {
    if (MOZ_UNLIKELY(mDebugFlags)) {
      AfterGLCall_Debug(funcName);
    }
  }

  GLContext* TrackingContext() {
    GLContext* tip = this;
    while (tip->mSharedContext) tip = tip->mSharedContext;
    return tip;
  }

  static void AssertNotPassingStackBufferToTheGL(const void* ptr);

#ifdef MOZ_GL_DEBUG_BUILD

#  define TRACKING_CONTEXT(a) \
    do {                      \
      TrackingContext()->a;   \
    } while (0)

#  define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) \
    AssertNotPassingStackBufferToTheGL(ptr)

#  define ASSERT_SYMBOL_PRESENT(func)                                    \
    do {                                                                 \
      MOZ_ASSERT(strstr(MOZ_FUNCTION_NAME, #func) != nullptr,            \
                 "Mismatched symbol check.");                            \
      if (MOZ_UNLIKELY(!mSymbols.func)) {                                \
        printf_stderr("RUNTIME ASSERT: Uninitialized GL function: %s\n", \
                      #func);                                            \
        MOZ_CRASH("GFX: Uninitialized GL function");                     \
      }                                                                  \
    } while (0)

#else  // ifdef MOZ_GL_DEBUG_BUILD

#  define TRACKING_CONTEXT(a) \
    do {                      \
    } while (0)
#  define ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(ptr) \
    do {                                             \
    } while (0)
#  define ASSERT_SYMBOL_PRESENT(func) \
    do {                              \
    } while (0)

#endif  // ifdef MOZ_GL_DEBUG_BUILD

  // Do whatever setup is necessary to draw to our offscreen FBO, if it's
  // bound.
  void BeforeGLDrawCall() {}

  // Do whatever tear-down is necessary after drawing to our offscreen FBO,
  // if it's bound.
  void AfterGLDrawCall() { mHeavyGLCallsSinceLastFlush = true; }

  // Do whatever setup is necessary to read from our offscreen FBO, if it's
  // bound.
  void BeforeGLReadCall() {}

  // Do whatever tear-down is necessary after reading from our offscreen FBO,
  // if it's bound.
  void AfterGLReadCall() {}

 public:
  void OnSyncCall() const { mSyncGLCallCount++; }

  uint64_t GetSyncCallCount() const { return mSyncGLCallCount; }

  void ResetSyncCallCount(const char* resetReason) const;

  // -----------------------------------------------------------------------------
  // GL official entry points
 public:
  // We smash all errors together, so you never have to loop on this. We
  // guarantee that immediately after this call, there are no errors left.
  // Always returns the top-most error, except if followed by CONTEXT_LOST, then
  // return that instead.
  GLenum GetError() const;

  GLenum fGetError() { return GetError(); }

  GLenum fGetGraphicsResetStatus() const;

  // -

  void fActiveTexture(GLenum texture) {
    BEFORE_GL_CALL;
    mSymbols.fActiveTexture(texture);
    AFTER_GL_CALL;
  }

  void fAttachShader(GLuint program, GLuint shader) {
    BEFORE_GL_CALL;
    mSymbols.fAttachShader(program, shader);
    AFTER_GL_CALL;
  }

  void fBeginQuery(GLenum target, GLuint id) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBeginQuery);
    mSymbols.fBeginQuery(target, id);
    AFTER_GL_CALL;
  }

  void fBindAttribLocation(GLuint program, GLuint index, const GLchar* name) {
    BEFORE_GL_CALL;
    mSymbols.fBindAttribLocation(program, index, name);
    AFTER_GL_CALL;
  }

  void fBindBuffer(GLenum target, GLuint buffer) {
    BEFORE_GL_CALL;
    mSymbols.fBindBuffer(target, buffer);
    AFTER_GL_CALL;
  }

  void InvalidateFramebuffer(GLenum target) {
    constexpr auto ATTACHMENTS = make_array(GLenum{LOCAL_GL_COLOR_ATTACHMENT0},
                                            LOCAL_GL_DEPTH_STENCIL_ATTACHMENT);
    fInvalidateFramebuffer(target, ATTACHMENTS.size(), ATTACHMENTS.data());
  }

  void fInvalidateFramebuffer(GLenum target, GLsizei numAttachments,
                              const GLenum* attachments) {
    if (!mSymbols.fInvalidateFramebuffer) return;
    BeforeGLDrawCall();
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fInvalidateFramebuffer);
    mSymbols.fInvalidateFramebuffer(target, numAttachments, attachments);
    AFTER_GL_CALL;
    AfterGLDrawCall();
  }

  void fInvalidateSubFramebuffer(GLenum target, GLsizei numAttachments,
                                 const GLenum* attachments, GLint x, GLint y,
                                 GLsizei width, GLsizei height) {
    if (!mSymbols.fInvalidateSubFramebuffer) return;
    BeforeGLDrawCall();
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fInvalidateSubFramebuffer);
    mSymbols.fInvalidateSubFramebuffer(target, numAttachments, attachments, x,
                                       y, width, height);
    AFTER_GL_CALL;
    AfterGLDrawCall();
  }

  void fBindTexture(GLenum target, GLuint texture) {
    BEFORE_GL_CALL;
    mSymbols.fBindTexture(target, texture);
    AFTER_GL_CALL;
  }

  void BindSamplerTexture(GLuint texUnitId, GLuint samplerHandle,
                          GLenum texTarget, GLuint texHandle) {
    fBindSampler(texUnitId, samplerHandle);
    fActiveTexture(LOCAL_GL_TEXTURE0 + texUnitId);
    fBindTexture(texTarget, texHandle);
  }

  void fBlendColor(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) {
    BEFORE_GL_CALL;
    mSymbols.fBlendColor(red, green, blue, alpha);
    AFTER_GL_CALL;
  }

  void fBlendEquation(GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fBlendEquation(mode);
    AFTER_GL_CALL;
  }

  void fBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) {
    BEFORE_GL_CALL;
    mSymbols.fBlendEquationSeparate(modeRGB, modeAlpha);
    AFTER_GL_CALL;
  }

  void fBlendFunc(GLenum sfactor, GLenum dfactor) {
    BEFORE_GL_CALL;
    mSymbols.fBlendFunc(sfactor, dfactor);
    AFTER_GL_CALL;
  }

  void fBlendFuncSeparate(GLenum sfactorRGB, GLenum dfactorRGB,
                          GLenum sfactorAlpha, GLenum dfactorAlpha) {
    BEFORE_GL_CALL;
    mSymbols.fBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha,
                                dfactorAlpha);
    AFTER_GL_CALL;
  }

 private:
  void raw_fBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
                       GLenum usage) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(data);
    BEFORE_GL_CALL;
    mSymbols.fBufferData(target, size, data, usage);
    OnSyncCall();
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

 public:
  void fBufferData(GLenum target, GLsizeiptr size, const GLvoid* data,
                   GLenum usage) {
    raw_fBufferData(target, size, data, usage);

    // bug 744888
    if (WorkAroundDriverBugs() && !data && Vendor() == GLVendor::NVIDIA) {
      UniquePtr<char[]> buf = MakeUnique<char[]>(1);
      buf[0] = 0;
      fBufferSubData(target, size - 1, 1, buf.get());
    }
  }

  void fBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size,
                      const GLvoid* data) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(data);
    BEFORE_GL_CALL;
    mSymbols.fBufferSubData(target, offset, size, data);
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

 private:
  void raw_fClear(GLbitfield mask) {
    BEFORE_GL_CALL;
    mSymbols.fClear(mask);
    AFTER_GL_CALL;
  }

 public:
  void fClear(GLbitfield mask) {
    BeforeGLDrawCall();
    raw_fClear(mask);
    AfterGLDrawCall();
  }

  void fClearBufferfi(GLenum buffer, GLint drawbuffer, GLfloat depth,
                      GLint stencil) {
    BeforeGLDrawCall();
    BEFORE_GL_CALL;
    mSymbols.fClearBufferfi(buffer, drawbuffer, depth, stencil);
    AFTER_GL_CALL;
    AfterGLDrawCall();
  }

  void fClearBufferfv(GLenum buffer, GLint drawbuffer, const GLfloat* value) {
    BeforeGLDrawCall();
    BEFORE_GL_CALL;
    mSymbols.fClearBufferfv(buffer, drawbuffer, value);
    AFTER_GL_CALL;
    AfterGLDrawCall();
  }

  void fClearBufferiv(GLenum buffer, GLint drawbuffer, const GLint* value) {
    BeforeGLDrawCall();
    BEFORE_GL_CALL;
    mSymbols.fClearBufferiv(buffer, drawbuffer, value);
    AFTER_GL_CALL;
    AfterGLDrawCall();
  }

  void fClearBufferuiv(GLenum buffer, GLint drawbuffer, const GLuint* value) {
    BeforeGLDrawCall();
    BEFORE_GL_CALL;
    mSymbols.fClearBufferuiv(buffer, drawbuffer, value);
    AFTER_GL_CALL;
    AfterGLDrawCall();
  }

  void fClearColor(GLfloat r, GLfloat g, GLfloat b, GLfloat a) {
    BEFORE_GL_CALL;
    mSymbols.fClearColor(r, g, b, a);
    AFTER_GL_CALL;
  }

  void fClearStencil(GLint s) {
    BEFORE_GL_CALL;
    mSymbols.fClearStencil(s);
    AFTER_GL_CALL;
  }

  void fClientActiveTexture(GLenum texture) {
    BEFORE_GL_CALL;
    mSymbols.fClientActiveTexture(texture);
    AFTER_GL_CALL;
  }

  void fColorMask(realGLboolean red, realGLboolean green, realGLboolean blue,
                  realGLboolean alpha) {
    BEFORE_GL_CALL;
    mSymbols.fColorMask(red, green, blue, alpha);
    AFTER_GL_CALL;
  }

  void fCompressedTexImage2D(GLenum target, GLint level, GLenum internalformat,
                             GLsizei width, GLsizei height, GLint border,
                             GLsizei imageSize, const GLvoid* pixels) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
    BEFORE_GL_CALL;
    mSymbols.fCompressedTexImage2D(target, level, internalformat, width, height,
                                   border, imageSize, pixels);
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

  void fCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset,
                                GLint yoffset, GLsizei width, GLsizei height,
                                GLenum format, GLsizei imageSize,
                                const GLvoid* pixels) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
    BEFORE_GL_CALL;
    mSymbols.fCompressedTexSubImage2D(target, level, xoffset, yoffset, width,
                                      height, format, imageSize, pixels);
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

  void fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
                       GLint x, GLint y, GLsizei width, GLsizei height,
                       GLint border);

  void fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset,
                          GLint yoffset, GLint x, GLint y, GLsizei width,
                          GLsizei height) {
    BeforeGLReadCall();
    raw_fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width,
                           height);
    AfterGLReadCall();
  }

  void fCullFace(GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fCullFace(mode);
    AFTER_GL_CALL;
  }

  void fDebugMessageCallback(GLDEBUGPROC callback, const GLvoid* userParam) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDebugMessageCallback);
    mSymbols.fDebugMessageCallback(callback, userParam);
    AFTER_GL_CALL;
  }

  void fDebugMessageControl(GLenum source, GLenum type, GLenum severity,
                            GLsizei count, const GLuint* ids,
                            realGLboolean enabled) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDebugMessageControl);
    mSymbols.fDebugMessageControl(source, type, severity, count, ids, enabled);
    AFTER_GL_CALL;
  }

  void fDebugMessageInsert(GLenum source, GLenum type, GLuint id,
                           GLenum severity, GLsizei length, const GLchar* buf) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDebugMessageInsert);
    mSymbols.fDebugMessageInsert(source, type, id, severity, length, buf);
    AFTER_GL_CALL;
  }

  void fDetachShader(GLuint program, GLuint shader) {
    BEFORE_GL_CALL;
    mSymbols.fDetachShader(program, shader);
    AFTER_GL_CALL;
  }

  void fDepthFunc(GLenum func) {
    BEFORE_GL_CALL;
    mSymbols.fDepthFunc(func);
    AFTER_GL_CALL;
  }

  void fDepthMask(realGLboolean flag) {
    BEFORE_GL_CALL;
    mSymbols.fDepthMask(flag);
    AFTER_GL_CALL;
  }

  void fDisable(GLenum capability) {
    BEFORE_GL_CALL;
    mSymbols.fDisable(capability);
    AFTER_GL_CALL;
  }

  void fDisableClientState(GLenum capability) {
    BEFORE_GL_CALL;
    mSymbols.fDisableClientState(capability);
    AFTER_GL_CALL;
  }

  void fDisableVertexAttribArray(GLuint index) {
    BEFORE_GL_CALL;
    mSymbols.fDisableVertexAttribArray(index);
    AFTER_GL_CALL;
  }

  void fDrawBuffer(GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fDrawBuffer(mode);
    AFTER_GL_CALL;
  }

 private:
  void raw_fDrawArrays(GLenum mode, GLint first, GLsizei count) {
    BEFORE_GL_CALL;
    mSymbols.fDrawArrays(mode, first, count);
    AFTER_GL_CALL;
  }

  void raw_fDrawElements(GLenum mode, GLsizei count, GLenum type,
                         const GLvoid* indices) {
    BEFORE_GL_CALL;
    mSymbols.fDrawElements(mode, count, type, indices);
    AFTER_GL_CALL;
  }

 public:
  void fDrawArrays(GLenum mode, GLint first, GLsizei count) {
    BeforeGLDrawCall();
    raw_fDrawArrays(mode, first, count);
    AfterGLDrawCall();
  }

  void fDrawElements(GLenum mode, GLsizei count, GLenum type,
                     const GLvoid* indices) {
    BeforeGLDrawCall();
    raw_fDrawElements(mode, count, type, indices);
    AfterGLDrawCall();
  }

  void fEnable(GLenum capability) {
    BEFORE_GL_CALL;
    mSymbols.fEnable(capability);
    AFTER_GL_CALL;
  }

  void fEnableClientState(GLenum capability) {
    BEFORE_GL_CALL;
    mSymbols.fEnableClientState(capability);
    AFTER_GL_CALL;
  }

  void fEnableVertexAttribArray(GLuint index) {
    BEFORE_GL_CALL;
    mSymbols.fEnableVertexAttribArray(index);
    AFTER_GL_CALL;
  }

  void fEndQuery(GLenum target) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fEndQuery);
    mSymbols.fEndQuery(target);
    AFTER_GL_CALL;
  }

  void fFinish() {
    BEFORE_GL_CALL;
    mSymbols.fFinish();
    OnSyncCall();
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = false;
  }

  void fFlush() {
    BEFORE_GL_CALL;
    mSymbols.fFlush();
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = false;
  }

  void fFrontFace(GLenum face) {
    BEFORE_GL_CALL;
    mSymbols.fFrontFace(face);
    AFTER_GL_CALL;
  }

  void fGetActiveAttrib(GLuint program, GLuint index, GLsizei maxLength,
                        GLsizei* length, GLint* size, GLenum* type,
                        GLchar* name) {
    BEFORE_GL_CALL;
    mSymbols.fGetActiveAttrib(program, index, maxLength, length, size, type,
                              name);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetActiveUniform(GLuint program, GLuint index, GLsizei maxLength,
                         GLsizei* length, GLint* size, GLenum* type,
                         GLchar* name) {
    BEFORE_GL_CALL;
    mSymbols.fGetActiveUniform(program, index, maxLength, length, size, type,
                               name);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetAttachedShaders(GLuint program, GLsizei maxCount, GLsizei* count,
                           GLuint* shaders) {
    BEFORE_GL_CALL;
    mSymbols.fGetAttachedShaders(program, maxCount, count, shaders);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  GLint fGetAttribLocation(GLuint program, const GLchar* name) {
    GLint retval = 0;
    BEFORE_GL_CALL;
    retval = mSymbols.fGetAttribLocation(program, name);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

 private:
  void raw_fGetIntegerv(GLenum pname, GLint* params) const {
    BEFORE_GL_CALL;
    mSymbols.fGetIntegerv(pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

 public:
  void fGetIntegerv(GLenum pname, GLint* params) const;

  template <typename T>
  void GetInt(const GLenum pname, T* const params) const {
    static_assert(sizeof(T) == sizeof(GLint), "Invalid T.");
    fGetIntegerv(pname, reinterpret_cast<GLint*>(params));
  }

  void GetUIntegerv(GLenum pname, GLuint* params) const {
    GetInt(pname, params);
  }

  template <typename T>
  T GetIntAs(GLenum pname) const {
    static_assert(sizeof(T) == sizeof(GLint), "Invalid T.");
    T ret = 0;
    fGetIntegerv(pname, (GLint*)&ret);
    return ret;
  }

  void fGetFloatv(GLenum pname, GLfloat* params) const {
    BEFORE_GL_CALL;
    mSymbols.fGetFloatv(pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetBooleanv(GLenum pname, realGLboolean* params) const {
    BEFORE_GL_CALL;
    mSymbols.fGetBooleanv(pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetBufferParameteriv(target, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  GLuint fGetDebugMessageLog(GLuint count, GLsizei bufsize, GLenum* sources,
                             GLenum* types, GLuint* ids, GLenum* severities,
                             GLsizei* lengths, GLchar* messageLog) {
    GLuint ret = 0;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetDebugMessageLog);
    ret = mSymbols.fGetDebugMessageLog(count, bufsize, sources, types, ids,
                                       severities, lengths, messageLog);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  void fGetPointerv(GLenum pname, GLvoid** params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetPointerv);
    mSymbols.fGetPointerv(pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetObjectLabel(GLenum identifier, GLuint name, GLsizei bufSize,
                       GLsizei* length, GLchar* label) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetObjectLabel);
    mSymbols.fGetObjectLabel(identifier, name, bufSize, length, label);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetObjectPtrLabel(const GLvoid* ptr, GLsizei bufSize, GLsizei* length,
                          GLchar* label) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetObjectPtrLabel);
    mSymbols.fGetObjectPtrLabel(ptr, bufSize, length, label);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGenerateMipmap(GLenum target) {
    BEFORE_GL_CALL;
    mSymbols.fGenerateMipmap(target);
    AFTER_GL_CALL;
  }

  void fGetProgramiv(GLuint program, GLenum pname, GLint* param) {
    BEFORE_GL_CALL;
    mSymbols.fGetProgramiv(program, pname, param);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetProgramInfoLog(GLuint program, GLsizei bufSize, GLsizei* length,
                          GLchar* infoLog) {
    BEFORE_GL_CALL;
    mSymbols.fGetProgramInfoLog(program, bufSize, length, infoLog);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fTexParameteri(GLenum target, GLenum pname, GLint param) {
    BEFORE_GL_CALL;
    mSymbols.fTexParameteri(target, pname, param);
    AFTER_GL_CALL;
  }

  void fTexParameteriv(GLenum target, GLenum pname, const GLint* params) {
    BEFORE_GL_CALL;
    mSymbols.fTexParameteriv(target, pname, params);
    AFTER_GL_CALL;
  }

  void fTexParameterf(GLenum target, GLenum pname, GLfloat param) {
    BEFORE_GL_CALL;
    mSymbols.fTexParameterf(target, pname, param);
    AFTER_GL_CALL;
  }

  const GLubyte* fGetString(GLenum name) {
    const GLubyte* result = nullptr;
    BEFORE_GL_CALL;
    result = mSymbols.fGetString(name);
    OnSyncCall();
    AFTER_GL_CALL;
    return result;
  }

  void fGetTexImage(GLenum target, GLint level, GLenum format, GLenum type,
                    GLvoid* img) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetTexImage);
    mSymbols.fGetTexImage(target, level, format, type, img);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetTexLevelParameteriv(GLenum target, GLint level, GLenum pname,
                               GLint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetTexLevelParameteriv);
    mSymbols.fGetTexLevelParameteriv(target, level, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetTexParameterfv(target, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetTexParameteriv(GLenum target, GLenum pname, GLint* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetTexParameteriv(target, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetUniformfv(GLuint program, GLint location, GLfloat* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetUniformfv(program, location, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetUniformiv(GLuint program, GLint location, GLint* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetUniformiv(program, location, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetUniformuiv(GLuint program, GLint location, GLuint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetUniformuiv);
    mSymbols.fGetUniformuiv(program, location, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  GLint fGetUniformLocation(GLuint programObj, const GLchar* name) {
    GLint retval = 0;
    BEFORE_GL_CALL;
    retval = mSymbols.fGetUniformLocation(programObj, name);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

  void fGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* retval) {
    BEFORE_GL_CALL;
    mSymbols.fGetVertexAttribfv(index, pname, retval);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetVertexAttribiv(GLuint index, GLenum pname, GLint* retval) {
    BEFORE_GL_CALL;
    mSymbols.fGetVertexAttribiv(index, pname, retval);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** retval) {
    BEFORE_GL_CALL;
    mSymbols.fGetVertexAttribPointerv(index, pname, retval);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fHint(GLenum target, GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fHint(target, mode);
    AFTER_GL_CALL;
  }

  realGLboolean fIsBuffer(GLuint buffer) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsBuffer(buffer);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

  realGLboolean fIsEnabled(GLenum capability) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsEnabled(capability);
    AFTER_GL_CALL;
    return retval;
  }

  void SetEnabled(const GLenum cap, const bool val) {
    if (val) {
      fEnable(cap);
    } else {
      fDisable(cap);
    }
  }

  bool PushEnabled(const GLenum cap, const bool newVal) {
    const bool oldVal = fIsEnabled(cap);
    if (oldVal != newVal) {
      SetEnabled(cap, newVal);
    }
    return oldVal;
  }

  realGLboolean fIsProgram(GLuint program) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsProgram(program);
    AFTER_GL_CALL;
    return retval;
  }

  realGLboolean fIsShader(GLuint shader) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsShader(shader);
    AFTER_GL_CALL;
    return retval;
  }

  realGLboolean fIsTexture(GLuint texture) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsTexture(texture);
    AFTER_GL_CALL;
    return retval;
  }

  void fLineWidth(GLfloat width) {
    BEFORE_GL_CALL;
    mSymbols.fLineWidth(width);
    AFTER_GL_CALL;
  }

  void fLinkProgram(GLuint program) {
    BEFORE_GL_CALL;
    mSymbols.fLinkProgram(program);
    AFTER_GL_CALL;
  }

  void fObjectLabel(GLenum identifier, GLuint name, GLsizei length,
                    const GLchar* label) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fObjectLabel);
    mSymbols.fObjectLabel(identifier, name, length, label);
    AFTER_GL_CALL;
  }

  void fObjectPtrLabel(const GLvoid* ptr, GLsizei length, const GLchar* label) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fObjectPtrLabel);
    mSymbols.fObjectPtrLabel(ptr, length, label);
    AFTER_GL_CALL;
  }

  void fLoadIdentity() {
    BEFORE_GL_CALL;
    mSymbols.fLoadIdentity();
    AFTER_GL_CALL;
  }

  void fLoadMatrixf(const GLfloat* matrix) {
    BEFORE_GL_CALL;
    mSymbols.fLoadMatrixf(matrix);
    AFTER_GL_CALL;
  }

  void fMatrixMode(GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fMatrixMode(mode);
    AFTER_GL_CALL;
  }

  void fPixelStorei(GLenum pname, GLint param) {
    BEFORE_GL_CALL;
    mSymbols.fPixelStorei(pname, param);
    AFTER_GL_CALL;
  }

  void fTextureRangeAPPLE(GLenum target, GLsizei length, GLvoid* pointer) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pointer);
    BEFORE_GL_CALL;
    mSymbols.fTextureRangeAPPLE(target, length, pointer);
    AFTER_GL_CALL;
  }

  void fPointParameterf(GLenum pname, GLfloat param) {
    BEFORE_GL_CALL;
    mSymbols.fPointParameterf(pname, param);
    AFTER_GL_CALL;
  }

  void fPolygonMode(GLenum face, GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fPolygonMode(face, mode);
    AFTER_GL_CALL;
  }

  void fPolygonOffset(GLfloat factor, GLfloat bias) {
    BEFORE_GL_CALL;
    mSymbols.fPolygonOffset(factor, bias);
    AFTER_GL_CALL;
  }

  void fPopDebugGroup() {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fPopDebugGroup);
    mSymbols.fPopDebugGroup();
    AFTER_GL_CALL;
  }

  void fPushDebugGroup(GLenum source, GLuint id, GLsizei length,
                       const GLchar* message) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fPushDebugGroup);
    mSymbols.fPushDebugGroup(source, id, length, message);
    AFTER_GL_CALL;
  }

  void fReadBuffer(GLenum mode) {
    BEFORE_GL_CALL;
    mSymbols.fReadBuffer(mode);
    AFTER_GL_CALL;
  }

  void raw_fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                       GLenum format, GLenum type, GLvoid* pixels) {
    BEFORE_GL_CALL;
    mSymbols.fReadPixels(x, y, width, height, format, type, pixels);
    OnSyncCall();
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

  void fReadPixels(GLint x, GLint y, GLsizei width, GLsizei height,
                   GLenum format, GLenum type, GLvoid* pixels);

 public:
  void fSampleCoverage(GLclampf value, realGLboolean invert) {
    BEFORE_GL_CALL;
    mSymbols.fSampleCoverage(value, invert);
    AFTER_GL_CALL;
  }

  void fScissor(GLint x, GLint y, GLsizei width, GLsizei height) {
    if (mScissorRect[0] == x && mScissorRect[1] == y &&
        mScissorRect[2] == width && mScissorRect[3] == height) {
      return;
    }
    mScissorRect[0] = x;
    mScissorRect[1] = y;
    mScissorRect[2] = width;
    mScissorRect[3] = height;
    BEFORE_GL_CALL;
    mSymbols.fScissor(x, y, width, height);
    AFTER_GL_CALL;
  }

  void fStencilFunc(GLenum func, GLint reference, GLuint mask) {
    BEFORE_GL_CALL;
    mSymbols.fStencilFunc(func, reference, mask);
    AFTER_GL_CALL;
  }

  void fStencilFuncSeparate(GLenum frontfunc, GLenum backfunc, GLint reference,
                            GLuint mask) {
    BEFORE_GL_CALL;
    mSymbols.fStencilFuncSeparate(frontfunc, backfunc, reference, mask);
    AFTER_GL_CALL;
  }

  void fStencilMask(GLuint mask) {
    BEFORE_GL_CALL;
    mSymbols.fStencilMask(mask);
    AFTER_GL_CALL;
  }

  void fStencilMaskSeparate(GLenum face, GLuint mask) {
    BEFORE_GL_CALL;
    mSymbols.fStencilMaskSeparate(face, mask);
    AFTER_GL_CALL;
  }

  void fStencilOp(GLenum fail, GLenum zfail, GLenum zpass) {
    BEFORE_GL_CALL;
    mSymbols.fStencilOp(fail, zfail, zpass);
    AFTER_GL_CALL;
  }

  void fStencilOpSeparate(GLenum face, GLenum sfail, GLenum dpfail,
                          GLenum dppass) {
    BEFORE_GL_CALL;
    mSymbols.fStencilOpSeparate(face, sfail, dpfail, dppass);
    AFTER_GL_CALL;
  }

  void fTexGeni(GLenum coord, GLenum pname, GLint param) {
    BEFORE_GL_CALL;
    mSymbols.fTexGeni(coord, pname, param);
    AFTER_GL_CALL;
  }

  void fTexGenf(GLenum coord, GLenum pname, GLfloat param) {
    BEFORE_GL_CALL;
    mSymbols.fTexGenf(coord, pname, param);
    AFTER_GL_CALL;
  }

  void fTexGenfv(GLenum coord, GLenum pname, const GLfloat* params) {
    BEFORE_GL_CALL;
    mSymbols.fTexGenfv(coord, pname, params);
    AFTER_GL_CALL;
  }

 private:
  void raw_fTexImage2D(GLenum target, GLint level, GLint internalformat,
                       GLsizei width, GLsizei height, GLint border,
                       GLenum format, GLenum type, const GLvoid* pixels) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
    BEFORE_GL_CALL;
    mSymbols.fTexImage2D(target, level, internalformat, width, height, border,
                         format, type, pixels);
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

 public:
  void fTexImage2D(GLenum target, GLint level, GLint internalformat,
                   GLsizei width, GLsizei height, GLint border, GLenum format,
                   GLenum type, const GLvoid* pixels);

  void fTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                      GLsizei width, GLsizei height, GLenum format, GLenum type,
                      const GLvoid* pixels) {
    ASSERT_NOT_PASSING_STACK_BUFFER_TO_GL(pixels);
    BEFORE_GL_CALL;
    mSymbols.fTexSubImage2D(target, level, xoffset, yoffset, width, height,
                            format, type, pixels);
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

  void fUniform1f(GLint location, GLfloat v0) {
    BEFORE_GL_CALL;
    mSymbols.fUniform1f(location, v0);
    AFTER_GL_CALL;
  }

  void fUniform1fv(GLint location, GLsizei count, const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform1fv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform1i(GLint location, GLint v0) {
    BEFORE_GL_CALL;
    mSymbols.fUniform1i(location, v0);
    AFTER_GL_CALL;
  }

  void fUniform1iv(GLint location, GLsizei count, const GLint* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform1iv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform2f(GLint location, GLfloat v0, GLfloat v1) {
    BEFORE_GL_CALL;
    mSymbols.fUniform2f(location, v0, v1);
    AFTER_GL_CALL;
  }

  void fUniform2fv(GLint location, GLsizei count, const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform2fv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform2i(GLint location, GLint v0, GLint v1) {
    BEFORE_GL_CALL;
    mSymbols.fUniform2i(location, v0, v1);
    AFTER_GL_CALL;
  }

  void fUniform2iv(GLint location, GLsizei count, const GLint* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform2iv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform3f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2) {
    BEFORE_GL_CALL;
    mSymbols.fUniform3f(location, v0, v1, v2);
    AFTER_GL_CALL;
  }

  void fUniform3fv(GLint location, GLsizei count, const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform3fv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform3i(GLint location, GLint v0, GLint v1, GLint v2) {
    BEFORE_GL_CALL;
    mSymbols.fUniform3i(location, v0, v1, v2);
    AFTER_GL_CALL;
  }

  void fUniform3iv(GLint location, GLsizei count, const GLint* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform3iv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform4f(GLint location, GLfloat v0, GLfloat v1, GLfloat v2,
                  GLfloat v3) {
    BEFORE_GL_CALL;
    mSymbols.fUniform4f(location, v0, v1, v2, v3);
    AFTER_GL_CALL;
  }

  void fUniform4fv(GLint location, GLsizei count, const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform4fv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform4i(GLint location, GLint v0, GLint v1, GLint v2, GLint v3) {
    BEFORE_GL_CALL;
    mSymbols.fUniform4i(location, v0, v1, v2, v3);
    AFTER_GL_CALL;
  }

  void fUniform4iv(GLint location, GLsizei count, const GLint* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniform4iv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix2fv(GLint location, GLsizei count, realGLboolean transpose,
                         const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniformMatrix2fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix2x3fv(GLint location, GLsizei count,
                           realGLboolean transpose, const GLfloat* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniformMatrix2x3fv);
    mSymbols.fUniformMatrix2x3fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix2x4fv(GLint location, GLsizei count,
                           realGLboolean transpose, const GLfloat* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniformMatrix2x4fv);
    mSymbols.fUniformMatrix2x4fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix3fv(GLint location, GLsizei count, realGLboolean transpose,
                         const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniformMatrix3fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix3x2fv(GLint location, GLsizei count,
                           realGLboolean transpose, const GLfloat* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniformMatrix3x2fv);
    mSymbols.fUniformMatrix3x2fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix3x4fv(GLint location, GLsizei count,
                           realGLboolean transpose, const GLfloat* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniformMatrix3x4fv);
    mSymbols.fUniformMatrix3x4fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix4fv(GLint location, GLsizei count, realGLboolean transpose,
                         const GLfloat* value) {
    BEFORE_GL_CALL;
    mSymbols.fUniformMatrix4fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix4x2fv(GLint location, GLsizei count,
                           realGLboolean transpose, const GLfloat* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniformMatrix4x2fv);
    mSymbols.fUniformMatrix4x2fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUniformMatrix4x3fv(GLint location, GLsizei count,
                           realGLboolean transpose, const GLfloat* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniformMatrix4x3fv);
    mSymbols.fUniformMatrix4x3fv(location, count, transpose, value);
    AFTER_GL_CALL;
  }

  void fUseProgram(GLuint program) {
    BEFORE_GL_CALL;
    mSymbols.fUseProgram(program);
    AFTER_GL_CALL;
  }

  void fValidateProgram(GLuint program) {
    BEFORE_GL_CALL;
    mSymbols.fValidateProgram(program);
    AFTER_GL_CALL;
  }

  void fVertexAttribPointer(GLuint index, GLint size, GLenum type,
                            realGLboolean normalized, GLsizei stride,
                            const GLvoid* pointer) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttribPointer(index, size, type, normalized, stride,
                                  pointer);
    AFTER_GL_CALL;
  }

  void fVertexAttrib1f(GLuint index, GLfloat x) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib1f(index, x);
    AFTER_GL_CALL;
  }

  void fVertexAttrib2f(GLuint index, GLfloat x, GLfloat y) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib2f(index, x, y);
    AFTER_GL_CALL;
  }

  void fVertexAttrib3f(GLuint index, GLfloat x, GLfloat y, GLfloat z) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib3f(index, x, y, z);
    AFTER_GL_CALL;
  }

  void fVertexAttrib4f(GLuint index, GLfloat x, GLfloat y, GLfloat z,
                       GLfloat w) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib4f(index, x, y, z, w);
    AFTER_GL_CALL;
  }

  void fVertexAttrib1fv(GLuint index, const GLfloat* v) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib1fv(index, v);
    AFTER_GL_CALL;
  }

  void fVertexAttrib2fv(GLuint index, const GLfloat* v) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib2fv(index, v);
    AFTER_GL_CALL;
  }

  void fVertexAttrib3fv(GLuint index, const GLfloat* v) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib3fv(index, v);
    AFTER_GL_CALL;
  }

  void fVertexAttrib4fv(GLuint index, const GLfloat* v) {
    BEFORE_GL_CALL;
    mSymbols.fVertexAttrib4fv(index, v);
    AFTER_GL_CALL;
  }

  void fVertexPointer(GLint size, GLenum type, GLsizei stride,
                      const GLvoid* pointer) {
    BEFORE_GL_CALL;
    mSymbols.fVertexPointer(size, type, stride, pointer);
    AFTER_GL_CALL;
  }

  void fViewport(GLint x, GLint y, GLsizei width, GLsizei height) {
    if (mViewportRect[0] == x && mViewportRect[1] == y &&
        mViewportRect[2] == width && mViewportRect[3] == height) {
      return;
    }
    mViewportRect[0] = x;
    mViewportRect[1] = y;
    mViewportRect[2] = width;
    mViewportRect[3] = height;
    BEFORE_GL_CALL;
    mSymbols.fViewport(x, y, width, height);
    AFTER_GL_CALL;
  }

  void fCompileShader(GLuint shader) {
    BEFORE_GL_CALL;
    mSymbols.fCompileShader(shader);
    AFTER_GL_CALL;
  }

 private:
  friend class SharedSurface_IOSurface;

  void raw_fCopyTexImage2D(GLenum target, GLint level, GLenum internalformat,
                           GLint x, GLint y, GLsizei width, GLsizei height,
                           GLint border) {
    BEFORE_GL_CALL;
    mSymbols.fCopyTexImage2D(target, level, internalformat, x, y, width, height,
                             border);
    AFTER_GL_CALL;
  }

  void raw_fCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset,
                              GLint yoffset, GLint x, GLint y, GLsizei width,
                              GLsizei height) {
    BEFORE_GL_CALL;
    mSymbols.fCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width,
                                height);
    AFTER_GL_CALL;
  }

 public:
  void fGetShaderiv(GLuint shader, GLenum pname, GLint* param) {
    BEFORE_GL_CALL;
    mSymbols.fGetShaderiv(shader, pname, param);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetShaderInfoLog(GLuint shader, GLsizei bufSize, GLsizei* length,
                         GLchar* infoLog) {
    BEFORE_GL_CALL;
    mSymbols.fGetShaderInfoLog(shader, bufSize, length, infoLog);
    OnSyncCall();
    AFTER_GL_CALL;
  }

 private:
  void raw_fGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype,
                                     GLint* range, GLint* precision) {
    MOZ_ASSERT(IsGLES());

    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetShaderPrecisionFormat);
    mSymbols.fGetShaderPrecisionFormat(shadertype, precisiontype, range,
                                       precision);
    OnSyncCall();
    AFTER_GL_CALL;
  }

 public:
  void fGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype,
                                 GLint* range, GLint* precision) {
    if (IsGLES()) {
      raw_fGetShaderPrecisionFormat(shadertype, precisiontype, range,
                                    precision);
    } else {
      // Fall back to automatic values because almost all desktop hardware
      // supports the OpenGL standard precisions.
      GetShaderPrecisionFormatNonES2(shadertype, precisiontype, range,
                                     precision);
    }
  }

  void fGetShaderSource(GLint obj, GLsizei maxLength, GLsizei* length,
                        GLchar* source) {
    BEFORE_GL_CALL;
    mSymbols.fGetShaderSource(obj, maxLength, length, source);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fShaderSource(GLuint shader, GLsizei count, const GLchar* const* strings,
                     const GLint* lengths) {
    BEFORE_GL_CALL;
    mSymbols.fShaderSource(shader, count, strings, lengths);
    AFTER_GL_CALL;
  }

 private:
  mutable GLuint mCachedDrawFb = 0;
  mutable GLuint mCachedReadFb = 0;

 public:
  bool mElideDuplicateBindFramebuffers = false;

  // If e.g. GL_DRAW_FRAMEBUFFER isn't supported, will bind GL_FRAMEBUFFER.
  void fBindFramebuffer(GLenum target, const GLuint fb) const {
    if (!IsSupported(gl::GLFeature::framebuffer_blit)) {
      target = LOCAL_GL_FRAMEBUFFER;
    }
    if (mElideDuplicateBindFramebuffers) {
      MOZ_ASSERT(mCachedDrawFb ==
                 GetIntAs<GLuint>(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING));
      MOZ_ASSERT(mCachedReadFb ==
                 GetIntAs<GLuint>(LOCAL_GL_READ_FRAMEBUFFER_BINDING));

      switch (target) {
        case LOCAL_GL_FRAMEBUFFER:
          if (mCachedDrawFb == fb && mCachedReadFb == fb) return;
          break;
        case LOCAL_GL_DRAW_FRAMEBUFFER:
          if (mCachedDrawFb == fb) return;
          break;
        case LOCAL_GL_READ_FRAMEBUFFER:
          if (mCachedReadFb == fb) return;
          break;
      }
    }

    BEFORE_GL_CALL;
    mSymbols.fBindFramebuffer(target, fb);
    AFTER_GL_CALL;

    switch (target) {
      case LOCAL_GL_FRAMEBUFFER:
        mCachedDrawFb = fb;
        mCachedReadFb = fb;
        break;
      case LOCAL_GL_DRAW_FRAMEBUFFER:
        mCachedDrawFb = fb;
        break;
      case LOCAL_GL_READ_FRAMEBUFFER:
        mCachedReadFb = fb;
        break;
    }
  }

  void fBindRenderbuffer(GLenum target, GLuint renderbuffer) {
    BEFORE_GL_CALL;
    mSymbols.fBindRenderbuffer(target, renderbuffer);
    AFTER_GL_CALL;
  }

  GLenum fCheckFramebufferStatus(GLenum target) {
    GLenum retval = 0;
    BEFORE_GL_CALL;
    retval = mSymbols.fCheckFramebufferStatus(target);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

  void fFramebufferRenderbuffer(GLenum target, GLenum attachmentPoint,
                                GLenum renderbufferTarget,
                                GLuint renderbuffer) {
    BEFORE_GL_CALL;
    mSymbols.fFramebufferRenderbuffer(target, attachmentPoint,
                                      renderbufferTarget, renderbuffer);
    AFTER_GL_CALL;
  }

  void fFramebufferTexture2D(GLenum target, GLenum attachmentPoint,
                             GLenum textureTarget, GLuint texture,
                             GLint level) {
    BEFORE_GL_CALL;
    mSymbols.fFramebufferTexture2D(target, attachmentPoint, textureTarget,
                                   texture, level);
    AFTER_GL_CALL;
  }

  void fFramebufferTextureLayer(GLenum target, GLenum attachment,
                                GLuint texture, GLint level, GLint layer) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fFramebufferTextureLayer);
    mSymbols.fFramebufferTextureLayer(target, attachment, texture, level,
                                      layer);
    AFTER_GL_CALL;
  }

  void fGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachment,
                                            GLenum pname, GLint* value) {
    BEFORE_GL_CALL;
    mSymbols.fGetFramebufferAttachmentParameteriv(target, attachment, pname,
                                                  value);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* value) {
    BEFORE_GL_CALL;
    mSymbols.fGetRenderbufferParameteriv(target, pname, value);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  realGLboolean fIsFramebuffer(GLuint framebuffer) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsFramebuffer(framebuffer);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

 public:
  realGLboolean fIsRenderbuffer(GLuint renderbuffer) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    retval = mSymbols.fIsRenderbuffer(renderbuffer);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

  void fRenderbufferStorage(GLenum target, GLenum internalFormat, GLsizei width,
                            GLsizei height) {
    BEFORE_GL_CALL;
    mSymbols.fRenderbufferStorage(target, internalFormat, width, height);
    AFTER_GL_CALL;
  }

 private:
  void raw_fDepthRange(GLclampf a, GLclampf b) {
    MOZ_ASSERT(!IsGLES());

    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDepthRange);
    mSymbols.fDepthRange(a, b);
    AFTER_GL_CALL;
  }

  void raw_fDepthRangef(GLclampf a, GLclampf b) {
    MOZ_ASSERT(IsGLES());

    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDepthRangef);
    mSymbols.fDepthRangef(a, b);
    AFTER_GL_CALL;
  }

  void raw_fClearDepth(GLclampf v) {
    MOZ_ASSERT(!IsGLES());

    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fClearDepth);
    mSymbols.fClearDepth(v);
    AFTER_GL_CALL;
  }

  void raw_fClearDepthf(GLclampf v) {
    MOZ_ASSERT(IsGLES());

    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fClearDepthf);
    mSymbols.fClearDepthf(v);
    AFTER_GL_CALL;
  }

 public:
  void fDepthRange(GLclampf a, GLclampf b) {
    if (IsGLES()) {
      raw_fDepthRangef(a, b);
    } else {
      raw_fDepthRange(a, b);
    }
  }

  void fClearDepth(GLclampf v) {
    if (IsGLES()) {
      raw_fClearDepthf(v);
    } else {
      raw_fClearDepth(v);
    }
  }

  void* fMapBuffer(GLenum target, GLenum access) {
    void* ret = nullptr;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fMapBuffer);
    ret = mSymbols.fMapBuffer(target, access);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  realGLboolean fUnmapBuffer(GLenum target) {
    realGLboolean ret = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUnmapBuffer);
    ret = mSymbols.fUnmapBuffer(target);
    AFTER_GL_CALL;
    return ret;
  }

 private:
  GLuint raw_fCreateProgram() {
    GLuint ret = 0;
    BEFORE_GL_CALL;
    ret = mSymbols.fCreateProgram();
    AFTER_GL_CALL;
    return ret;
  }

  GLuint raw_fCreateShader(GLenum t) {
    GLuint ret = 0;
    BEFORE_GL_CALL;
    ret = mSymbols.fCreateShader(t);
    AFTER_GL_CALL;
    return ret;
  }

  void raw_fGenBuffers(GLsizei n, GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fGenBuffers(n, names);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void raw_fGenFramebuffers(GLsizei n, GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fGenFramebuffers(n, names);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void raw_fGenRenderbuffers(GLsizei n, GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fGenRenderbuffers(n, names);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void raw_fGenTextures(GLsizei n, GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fGenTextures(n, names);
    OnSyncCall();
    AFTER_GL_CALL;
  }

 public:
  GLuint fCreateProgram() {
    GLuint ret = raw_fCreateProgram();
    TRACKING_CONTEXT(CreatedProgram(this, ret));
    return ret;
  }

  GLuint fCreateShader(GLenum t) {
    GLuint ret = raw_fCreateShader(t);
    TRACKING_CONTEXT(CreatedShader(this, ret));
    return ret;
  }

  void fGenBuffers(GLsizei n, GLuint* names) {
    raw_fGenBuffers(n, names);
    TRACKING_CONTEXT(CreatedBuffers(this, n, names));
  }

  void fGenFramebuffers(GLsizei n, GLuint* names) {
    raw_fGenFramebuffers(n, names);
    TRACKING_CONTEXT(CreatedFramebuffers(this, n, names));
  }

  void fGenRenderbuffers(GLsizei n, GLuint* names) {
    raw_fGenRenderbuffers(n, names);
    TRACKING_CONTEXT(CreatedRenderbuffers(this, n, names));
  }

  void fGenTextures(GLsizei n, GLuint* names) {
    raw_fGenTextures(n, names);
    TRACKING_CONTEXT(CreatedTextures(this, n, names));
  }

 private:
  void raw_fDeleteProgram(GLuint program) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteProgram(program);
    AFTER_GL_CALL;
  }

  void raw_fDeleteShader(GLuint shader) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteShader(shader);
    AFTER_GL_CALL;
  }

  void raw_fDeleteBuffers(GLsizei n, const GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteBuffers(n, names);
    AFTER_GL_CALL;
  }

  void raw_fDeleteFramebuffers(GLsizei n, const GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteFramebuffers(n, names);
    AFTER_GL_CALL;

    for (const auto i : IntegerRange(n)) {
      const auto fb = names[i];
      if (mCachedDrawFb == fb) {
        mCachedDrawFb = 0;
      }
      if (mCachedReadFb == fb) {
        mCachedReadFb = 0;
      }
    }
  }

  void raw_fDeleteRenderbuffers(GLsizei n, const GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteRenderbuffers(n, names);
    AFTER_GL_CALL;
  }

  void raw_fDeleteTextures(GLsizei n, const GLuint* names) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteTextures(n, names);
    AFTER_GL_CALL;
  }

 public:
  void fDeleteProgram(GLuint program) {
    raw_fDeleteProgram(program);
    TRACKING_CONTEXT(DeletedProgram(this, program));
  }

  void fDeleteShader(GLuint shader) {
    raw_fDeleteShader(shader);
    TRACKING_CONTEXT(DeletedShader(this, shader));
  }

  void fDeleteBuffers(GLsizei n, const GLuint* names) {
    raw_fDeleteBuffers(n, names);
    TRACKING_CONTEXT(DeletedBuffers(this, n, names));
  }

  void fDeleteFramebuffers(GLsizei n, const GLuint* names);

  void fDeleteRenderbuffers(GLsizei n, const GLuint* names) {
    raw_fDeleteRenderbuffers(n, names);
    TRACKING_CONTEXT(DeletedRenderbuffers(this, n, names));
  }

  void fDeleteTextures(GLsizei n, const GLuint* names) {
#ifdef XP_MACOSX
    // On the Mac the call to fDeleteTextures() triggers a flush. But it
    // happens at the wrong time, which can lead to crashes. To work around
    // this we call fFlush() explicitly ourselves, before the call to
    // fDeleteTextures(). This fixes bug 1666293.
    fFlush();
#endif
    raw_fDeleteTextures(n, names);
    TRACKING_CONTEXT(DeletedTextures(this, n, names));
  }

  // -----------------------------------------------------------------------------
  // Extension ARB_sync (GL)
 public:
  GLsync fFenceSync(GLenum condition, GLbitfield flags) {
    GLsync ret = 0;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fFenceSync);
    ret = mSymbols.fFenceSync(condition, flags);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  realGLboolean fIsSync(GLsync sync) {
    realGLboolean ret = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fIsSync);
    ret = mSymbols.fIsSync(sync);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  void fDeleteSync(GLsync sync) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDeleteSync);
    mSymbols.fDeleteSync(sync);
    AFTER_GL_CALL;
  }

  GLenum fClientWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
    GLenum ret = 0;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fClientWaitSync);
    ret = mSymbols.fClientWaitSync(sync, flags, timeout);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  void fWaitSync(GLsync sync, GLbitfield flags, GLuint64 timeout) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fWaitSync);
    mSymbols.fWaitSync(sync, flags, timeout);
    AFTER_GL_CALL;
  }

  void fGetInteger64v(GLenum pname, GLint64* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetInteger64v);
    mSymbols.fGetInteger64v(pname, params);
    AFTER_GL_CALL;
  }

  void fGetSynciv(GLsync sync, GLenum pname, GLsizei bufSize, GLsizei* length,
                  GLint* values) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetSynciv);
    mSymbols.fGetSynciv(sync, pname, bufSize, length, values);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Extension OES_EGL_image (GLES)
 public:
  void fEGLImageTargetTexture2D(GLenum target, GLeglImage image) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fEGLImageTargetTexture2D);
    mSymbols.fEGLImageTargetTexture2D(target, image);
    AFTER_GL_CALL;
    mHeavyGLCallsSinceLastFlush = true;
  }

  void fEGLImageTargetRenderbufferStorage(GLenum target, GLeglImage image) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fEGLImageTargetRenderbufferStorage);
    mSymbols.fEGLImageTargetRenderbufferStorage(target, image);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_bind_buffer_offset
 public:
  void fBindBufferOffset(GLenum target, GLuint index, GLuint buffer,
                         GLintptr offset) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBindBufferOffset);
    mSymbols.fBindBufferOffset(target, index, buffer, offset);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_draw_buffers
 public:
  void fDrawBuffers(GLsizei n, const GLenum* bufs) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDrawBuffers);
    mSymbols.fDrawBuffers(n, bufs);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_draw_instanced
 public:
  void fDrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                            GLsizei primcount) {
    BeforeGLDrawCall();
    raw_fDrawArraysInstanced(mode, first, count, primcount);
    AfterGLDrawCall();
  }

  void fDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                              const GLvoid* indices, GLsizei primcount) {
    BeforeGLDrawCall();
    raw_fDrawElementsInstanced(mode, count, type, indices, primcount);
    AfterGLDrawCall();
  }

 private:
  void raw_fDrawArraysInstanced(GLenum mode, GLint first, GLsizei count,
                                GLsizei primcount) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDrawArraysInstanced);
    mSymbols.fDrawArraysInstanced(mode, first, count, primcount);
    AFTER_GL_CALL;
  }

  void raw_fDrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                  const GLvoid* indices, GLsizei primcount) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDrawElementsInstanced);
    mSymbols.fDrawElementsInstanced(mode, count, type, indices, primcount);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_framebuffer_blit
 public:
  // Draw/Read
  void fBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                        GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                        GLbitfield mask, GLenum filter) {
    BeforeGLDrawCall();
    BeforeGLReadCall();
    raw_fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1,
                         mask, filter);
    AfterGLReadCall();
    AfterGLDrawCall();
  }

 private:
  void raw_fBlitFramebuffer(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1,
                            GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1,
                            GLbitfield mask, GLenum filter) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBlitFramebuffer);
    mSymbols.fBlitFramebuffer(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1,
                              dstY1, mask, filter);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_framebuffer_multisample
 public:
  void fRenderbufferStorageMultisample(GLenum target, GLsizei samples,
                                       GLenum internalFormat, GLsizei width,
                                       GLsizei height) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fRenderbufferStorageMultisample);
    mSymbols.fRenderbufferStorageMultisample(target, samples, internalFormat,
                                             width, height);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  //  GL 3.0, GL ES 3.0 & EXT_gpu_shader4
 public:
  void fGetVertexAttribIiv(GLuint index, GLenum pname, GLint* params) {
    ASSERT_SYMBOL_PRESENT(fGetVertexAttribIiv);
    BEFORE_GL_CALL;
    mSymbols.fGetVertexAttribIiv(index, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetVertexAttribIuiv(GLuint index, GLenum pname, GLuint* params) {
    ASSERT_SYMBOL_PRESENT(fGetVertexAttribIuiv);
    BEFORE_GL_CALL;
    mSymbols.fGetVertexAttribIuiv(index, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fVertexAttribI4i(GLuint index, GLint x, GLint y, GLint z, GLint w) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fVertexAttribI4i);
    mSymbols.fVertexAttribI4i(index, x, y, z, w);
    AFTER_GL_CALL;
  }

  void fVertexAttribI4iv(GLuint index, const GLint* v) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fVertexAttribI4iv);
    mSymbols.fVertexAttribI4iv(index, v);
    AFTER_GL_CALL;
  }

  void fVertexAttribI4ui(GLuint index, GLuint x, GLuint y, GLuint z, GLuint w) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fVertexAttribI4ui);
    mSymbols.fVertexAttribI4ui(index, x, y, z, w);
    AFTER_GL_CALL;
  }

  void fVertexAttribI4uiv(GLuint index, const GLuint* v) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fVertexAttribI4uiv);
    mSymbols.fVertexAttribI4uiv(index, v);
    AFTER_GL_CALL;
  }

  void fVertexAttribIPointer(GLuint index, GLint size, GLenum type,
                             GLsizei stride, const GLvoid* offset) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fVertexAttribIPointer);
    mSymbols.fVertexAttribIPointer(index, size, type, stride, offset);
    AFTER_GL_CALL;
  }

  void fUniform1ui(GLint location, GLuint v0) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform1ui);
    mSymbols.fUniform1ui(location, v0);
    AFTER_GL_CALL;
  }

  void fUniform2ui(GLint location, GLuint v0, GLuint v1) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform2ui);
    mSymbols.fUniform2ui(location, v0, v1);
    AFTER_GL_CALL;
  }

  void fUniform3ui(GLint location, GLuint v0, GLuint v1, GLuint v2) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform3ui);
    mSymbols.fUniform3ui(location, v0, v1, v2);
    AFTER_GL_CALL;
  }

  void fUniform4ui(GLint location, GLuint v0, GLuint v1, GLuint v2, GLuint v3) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform4ui);
    mSymbols.fUniform4ui(location, v0, v1, v2, v3);
    AFTER_GL_CALL;
  }

  void fUniform1uiv(GLint location, GLsizei count, const GLuint* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform1uiv);
    mSymbols.fUniform1uiv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform2uiv(GLint location, GLsizei count, const GLuint* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform2uiv);
    mSymbols.fUniform2uiv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform3uiv(GLint location, GLsizei count, const GLuint* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform3uiv);
    mSymbols.fUniform3uiv(location, count, value);
    AFTER_GL_CALL;
  }

  void fUniform4uiv(GLint location, GLsizei count, const GLuint* value) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fUniform4uiv);
    mSymbols.fUniform4uiv(location, count, value);
    AFTER_GL_CALL;
  }

  GLint fGetFragDataLocation(GLuint program, const GLchar* name) {
    GLint result = 0;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetFragDataLocation);
    result = mSymbols.fGetFragDataLocation(program, name);
    OnSyncCall();
    AFTER_GL_CALL;
    return result;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_instanced_arrays
 public:
  void fVertexAttribDivisor(GLuint index, GLuint divisor) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fVertexAttribDivisor);
    mSymbols.fVertexAttribDivisor(index, divisor);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Feature internalformat_query
 public:
  void fGetInternalformativ(GLenum target, GLenum internalformat, GLenum pname,
                            GLsizei bufSize, GLint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetInternalformativ);
    mSymbols.fGetInternalformativ(target, internalformat, pname, bufSize,
                                  params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_query_counter
  /**
   * XXX_query_counter:
   *  - depends on XXX_query_objects
   *  - provide all followed entry points
   *  - provide GL_TIMESTAMP
   */

 public:
  void fQueryCounter(GLuint id, GLenum target) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fQueryCounter);
    mSymbols.fQueryCounter(id, target);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_query_objects
  /**
   * XXX_query_objects:
   *  - provide all followed entry points
   *
   * XXX_occlusion_query2:
   *  - depends on XXX_query_objects
   *  - provide ANY_SAMPLES_PASSED
   *
   * XXX_occlusion_query_boolean:
   *  - depends on XXX_occlusion_query2
   *  - provide ANY_SAMPLES_PASSED_CONSERVATIVE
   */

 public:
  void fDeleteQueries(GLsizei n, const GLuint* names) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDeleteQueries);
    mSymbols.fDeleteQueries(n, names);
    AFTER_GL_CALL;
    TRACKING_CONTEXT(DeletedQueries(this, n, names));
  }

  void fGenQueries(GLsizei n, GLuint* names) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGenQueries);
    mSymbols.fGenQueries(n, names);
    AFTER_GL_CALL;
    TRACKING_CONTEXT(CreatedQueries(this, n, names));
  }

  void fGetQueryiv(GLenum target, GLenum pname, GLint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetQueryiv);
    mSymbols.fGetQueryiv(target, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetQueryObjectuiv);
    mSymbols.fGetQueryObjectuiv(id, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  realGLboolean fIsQuery(GLuint query) {
    realGLboolean retval = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fIsQuery);
    retval = mSymbols.fIsQuery(query);
    OnSyncCall();
    AFTER_GL_CALL;
    return retval;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_get_query_object_i64v
  /**
   * XXX_get_query_object_i64v:
   *  - depends on XXX_query_objects
   *  - provide the followed entry point
   */

 public:
  void fGetQueryObjecti64v(GLuint id, GLenum pname, GLint64* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetQueryObjecti64v);
    mSymbols.fGetQueryObjecti64v(id, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetQueryObjectui64v(GLuint id, GLenum pname, GLuint64* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetQueryObjectui64v);
    mSymbols.fGetQueryObjectui64v(id, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_get_query_object_iv
  /**
   * XXX_get_query_object_iv:
   *  - depends on XXX_query_objects
   *  - provide the followed entry point
   *
   * XXX_occlusion_query:
   *  - depends on XXX_get_query_object_iv
   *  - provide LOCAL_GL_SAMPLES_PASSED
   */

 public:
  void fGetQueryObjectiv(GLuint id, GLenum pname, GLint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetQueryObjectiv);
    mSymbols.fGetQueryObjectiv(id, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // GL 4.0, GL ES 3.0, ARB_transform_feedback2, NV_transform_feedback2
 public:
  void fBindBufferBase(GLenum target, GLuint index, GLuint buffer) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBindBufferBase);
    mSymbols.fBindBufferBase(target, index, buffer);
    AFTER_GL_CALL;
  }

  void fBindBufferRange(GLenum target, GLuint index, GLuint buffer,
                        GLintptr offset, GLsizeiptr size) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBindBufferRange);
    mSymbols.fBindBufferRange(target, index, buffer, offset, size);
    AFTER_GL_CALL;
  }

  void fGenTransformFeedbacks(GLsizei n, GLuint* ids) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGenTransformFeedbacks);
    mSymbols.fGenTransformFeedbacks(n, ids);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fDeleteTransformFeedbacks(GLsizei n, const GLuint* ids) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDeleteTransformFeedbacks);
    mSymbols.fDeleteTransformFeedbacks(n, ids);
    AFTER_GL_CALL;
  }

  realGLboolean fIsTransformFeedback(GLuint id) {
    realGLboolean result = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fIsTransformFeedback);
    result = mSymbols.fIsTransformFeedback(id);
    OnSyncCall();
    AFTER_GL_CALL;
    return result;
  }

  void fBindTransformFeedback(GLenum target, GLuint id) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBindTransformFeedback);
    mSymbols.fBindTransformFeedback(target, id);
    AFTER_GL_CALL;
  }

  void fBeginTransformFeedback(GLenum primitiveMode) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBeginTransformFeedback);
    mSymbols.fBeginTransformFeedback(primitiveMode);
    AFTER_GL_CALL;
  }

  void fEndTransformFeedback() {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fEndTransformFeedback);
    mSymbols.fEndTransformFeedback();
    AFTER_GL_CALL;
  }

  void fTransformFeedbackVaryings(GLuint program, GLsizei count,
                                  const GLchar* const* varyings,
                                  GLenum bufferMode) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fTransformFeedbackVaryings);
    mSymbols.fTransformFeedbackVaryings(program, count, varyings, bufferMode);
    AFTER_GL_CALL;
  }

  void fGetTransformFeedbackVarying(GLuint program, GLuint index,
                                    GLsizei bufSize, GLsizei* length,
                                    GLsizei* size, GLenum* type, GLchar* name) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetTransformFeedbackVarying);
    mSymbols.fGetTransformFeedbackVarying(program, index, bufSize, length, size,
                                          type, name);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fPauseTransformFeedback() {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fPauseTransformFeedback);
    mSymbols.fPauseTransformFeedback();
    AFTER_GL_CALL;
  }

  void fResumeTransformFeedback() {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fResumeTransformFeedback);
    mSymbols.fResumeTransformFeedback();
    AFTER_GL_CALL;
  }

  void fGetIntegeri_v(GLenum param, GLuint index, GLint* values) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetIntegeri_v);
    mSymbols.fGetIntegeri_v(param, index, values);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetInteger64i_v(GLenum target, GLuint index, GLint64* data) {
    ASSERT_SYMBOL_PRESENT(fGetInteger64i_v);
    BEFORE_GL_CALL;
    mSymbols.fGetInteger64i_v(target, index, data);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Package XXX_vertex_array_object
 public:
  void fBindVertexArray(GLuint array) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBindVertexArray);
    mSymbols.fBindVertexArray(array);
    AFTER_GL_CALL;
  }

  void fDeleteVertexArrays(GLsizei n, const GLuint* arrays) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDeleteVertexArrays);
    mSymbols.fDeleteVertexArrays(n, arrays);
    AFTER_GL_CALL;
  }

  void fGenVertexArrays(GLsizei n, GLuint* arrays) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGenVertexArrays);
    mSymbols.fGenVertexArrays(n, arrays);
    AFTER_GL_CALL;
  }

  realGLboolean fIsVertexArray(GLuint array) {
    realGLboolean ret = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fIsVertexArray);
    ret = mSymbols.fIsVertexArray(array);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  // -----------------------------------------------------------------------------
  // Extension NV_fence
 public:
  void fGenFences(GLsizei n, GLuint* fences) {
    ASSERT_SYMBOL_PRESENT(fGenFences);
    BEFORE_GL_CALL;
    mSymbols.fGenFences(n, fences);
    AFTER_GL_CALL;
  }

  void fDeleteFences(GLsizei n, const GLuint* fences) {
    ASSERT_SYMBOL_PRESENT(fDeleteFences);
    BEFORE_GL_CALL;
    mSymbols.fDeleteFences(n, fences);
    AFTER_GL_CALL;
  }

  void fSetFence(GLuint fence, GLenum condition) {
    ASSERT_SYMBOL_PRESENT(fSetFence);
    BEFORE_GL_CALL;
    mSymbols.fSetFence(fence, condition);
    AFTER_GL_CALL;
  }

  realGLboolean fTestFence(GLuint fence) {
    realGLboolean ret = false;
    ASSERT_SYMBOL_PRESENT(fTestFence);
    BEFORE_GL_CALL;
    ret = mSymbols.fTestFence(fence);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  void fFinishFence(GLuint fence) {
    ASSERT_SYMBOL_PRESENT(fFinishFence);
    BEFORE_GL_CALL;
    mSymbols.fFinishFence(fence);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  realGLboolean fIsFence(GLuint fence) {
    realGLboolean ret = false;
    ASSERT_SYMBOL_PRESENT(fIsFence);
    BEFORE_GL_CALL;
    ret = mSymbols.fIsFence(fence);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  void fGetFenceiv(GLuint fence, GLenum pname, GLint* params) {
    ASSERT_SYMBOL_PRESENT(fGetFenceiv);
    BEFORE_GL_CALL;
    mSymbols.fGetFenceiv(fence, pname, params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Extension NV_texture_barrier
 public:
  void fTextureBarrier() {
    ASSERT_SYMBOL_PRESENT(fTextureBarrier);
    BEFORE_GL_CALL;
    mSymbols.fTextureBarrier();
    AFTER_GL_CALL;
  }

  // Core GL & Extension ARB_copy_buffer
 public:
  void fCopyBufferSubData(GLenum readtarget, GLenum writetarget,
                          GLintptr readoffset, GLintptr writeoffset,
                          GLsizeiptr size) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fCopyBufferSubData);
    mSymbols.fCopyBufferSubData(readtarget, writetarget, readoffset,
                                writeoffset, size);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Core GL & Extension ARB_map_buffer_range
 public:
  void* fMapBufferRange(GLenum target, GLintptr offset, GLsizeiptr length,
                        GLbitfield access) {
    void* data = nullptr;
    ASSERT_SYMBOL_PRESENT(fMapBufferRange);
    BEFORE_GL_CALL;
    data = mSymbols.fMapBufferRange(target, offset, length, access);
    OnSyncCall();
    AFTER_GL_CALL;
    return data;
  }

  void fFlushMappedBufferRange(GLenum target, GLintptr offset,
                               GLsizeiptr length) {
    ASSERT_SYMBOL_PRESENT(fFlushMappedBufferRange);
    BEFORE_GL_CALL;
    mSymbols.fFlushMappedBufferRange(target, offset, length);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Core GL & Extension ARB_sampler_objects
 public:
  void fGenSamplers(GLsizei count, GLuint* samplers) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGenSamplers);
    mSymbols.fGenSamplers(count, samplers);
    AFTER_GL_CALL;
  }

  void fDeleteSamplers(GLsizei count, const GLuint* samplers) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fDeleteSamplers);
    mSymbols.fDeleteSamplers(count, samplers);
    AFTER_GL_CALL;
  }

  realGLboolean fIsSampler(GLuint sampler) {
    realGLboolean result = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fIsSampler);
    result = mSymbols.fIsSampler(sampler);
    OnSyncCall();
    AFTER_GL_CALL;
    return result;
  }

  void fBindSampler(GLuint unit, GLuint sampler) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fBindSampler);
    mSymbols.fBindSampler(unit, sampler);
    AFTER_GL_CALL;
  }

  void fSamplerParameteri(GLuint sampler, GLenum pname, GLint param) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fSamplerParameteri);
    mSymbols.fSamplerParameteri(sampler, pname, param);
    AFTER_GL_CALL;
  }

  void fSamplerParameteriv(GLuint sampler, GLenum pname, const GLint* param) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fSamplerParameteriv);
    mSymbols.fSamplerParameteriv(sampler, pname, param);
    AFTER_GL_CALL;
  }

  void fSamplerParameterf(GLuint sampler, GLenum pname, GLfloat param) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fSamplerParameterf);
    mSymbols.fSamplerParameterf(sampler, pname, param);
    AFTER_GL_CALL;
  }

  void fSamplerParameterfv(GLuint sampler, GLenum pname, const GLfloat* param) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fSamplerParameterfv);
    mSymbols.fSamplerParameterfv(sampler, pname, param);
    AFTER_GL_CALL;
  }

  void fGetSamplerParameteriv(GLuint sampler, GLenum pname, GLint* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetSamplerParameteriv);
    mSymbols.fGetSamplerParameteriv(sampler, pname, params);
    AFTER_GL_CALL;
  }

  void fGetSamplerParameterfv(GLuint sampler, GLenum pname, GLfloat* params) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetSamplerParameterfv);
    mSymbols.fGetSamplerParameterfv(sampler, pname, params);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Core GL & Extension ARB_uniform_buffer_object
 public:
  void fGetUniformIndices(GLuint program, GLsizei uniformCount,
                          const GLchar* const* uniformNames,
                          GLuint* uniformIndices) {
    ASSERT_SYMBOL_PRESENT(fGetUniformIndices);
    BEFORE_GL_CALL;
    mSymbols.fGetUniformIndices(program, uniformCount, uniformNames,
                                uniformIndices);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetActiveUniformsiv(GLuint program, GLsizei uniformCount,
                            const GLuint* uniformIndices, GLenum pname,
                            GLint* params) {
    ASSERT_SYMBOL_PRESENT(fGetActiveUniformsiv);
    BEFORE_GL_CALL;
    mSymbols.fGetActiveUniformsiv(program, uniformCount, uniformIndices, pname,
                                  params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  GLuint fGetUniformBlockIndex(GLuint program, const GLchar* uniformBlockName) {
    GLuint result = 0;
    ASSERT_SYMBOL_PRESENT(fGetUniformBlockIndex);
    BEFORE_GL_CALL;
    result = mSymbols.fGetUniformBlockIndex(program, uniformBlockName);
    OnSyncCall();
    AFTER_GL_CALL;
    return result;
  }

  void fGetActiveUniformBlockiv(GLuint program, GLuint uniformBlockIndex,
                                GLenum pname, GLint* params) {
    ASSERT_SYMBOL_PRESENT(fGetActiveUniformBlockiv);
    BEFORE_GL_CALL;
    mSymbols.fGetActiveUniformBlockiv(program, uniformBlockIndex, pname,
                                      params);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fGetActiveUniformBlockName(GLuint program, GLuint uniformBlockIndex,
                                  GLsizei bufSize, GLsizei* length,
                                  GLchar* uniformBlockName) {
    ASSERT_SYMBOL_PRESENT(fGetActiveUniformBlockName);
    BEFORE_GL_CALL;
    mSymbols.fGetActiveUniformBlockName(program, uniformBlockIndex, bufSize,
                                        length, uniformBlockName);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fUniformBlockBinding(GLuint program, GLuint uniformBlockIndex,
                            GLuint uniformBlockBinding) {
    ASSERT_SYMBOL_PRESENT(fUniformBlockBinding);
    BEFORE_GL_CALL;
    mSymbols.fUniformBlockBinding(program, uniformBlockIndex,
                                  uniformBlockBinding);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Core GL 4.2, GL ES 3.0 & Extension ARB_texture_storage/EXT_texture_storage
  void fTexStorage2D(GLenum target, GLsizei levels, GLenum internalformat,
                     GLsizei width, GLsizei height) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fTexStorage2D);
    mSymbols.fTexStorage2D(target, levels, internalformat, width, height);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fTexStorage3D(GLenum target, GLsizei levels, GLenum internalformat,
                     GLsizei width, GLsizei height, GLsizei depth) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fTexStorage3D);
    mSymbols.fTexStorage3D(target, levels, internalformat, width, height,
                           depth);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // 3D Textures
  void fTexImage3D(GLenum target, GLint level, GLint internalFormat,
                   GLsizei width, GLsizei height, GLsizei depth, GLint border,
                   GLenum format, GLenum type, const GLvoid* data) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fTexImage3D);
    mSymbols.fTexImage3D(target, level, internalFormat, width, height, depth,
                         border, format, type, data);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fTexSubImage3D(GLenum target, GLint level, GLint xoffset, GLint yoffset,
                      GLint zoffset, GLsizei width, GLsizei height,
                      GLsizei depth, GLenum format, GLenum type,
                      const GLvoid* pixels) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fTexSubImage3D);
    mSymbols.fTexSubImage3D(target, level, xoffset, yoffset, zoffset, width,
                            height, depth, format, type, pixels);
    OnSyncCall();
    AFTER_GL_CALL;
  }

  void fCopyTexSubImage3D(GLenum target, GLint level, GLint xoffset,
                          GLint yoffset, GLint zoffset, GLint x, GLint y,
                          GLsizei width, GLsizei height) {
    BeforeGLReadCall();
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fCopyTexSubImage3D);
    mSymbols.fCopyTexSubImage3D(target, level, xoffset, yoffset, zoffset, x, y,
                                width, height);
    AFTER_GL_CALL;
    AfterGLReadCall();
  }

  void fCompressedTexImage3D(GLenum target, GLint level, GLenum internalformat,
                             GLsizei width, GLsizei height, GLsizei depth,
                             GLint border, GLsizei imageSize,
                             const GLvoid* data) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fCompressedTexImage3D);
    mSymbols.fCompressedTexImage3D(target, level, internalformat, width, height,
                                   depth, border, imageSize, data);
    AFTER_GL_CALL;
  }

  void fCompressedTexSubImage3D(GLenum target, GLint level, GLint xoffset,
                                GLint yoffset, GLint zoffset, GLsizei width,
                                GLsizei height, GLsizei depth, GLenum format,
                                GLsizei imageSize, const GLvoid* data) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fCompressedTexSubImage3D);
    mSymbols.fCompressedTexSubImage3D(target, level, xoffset, yoffset, zoffset,
                                      width, height, depth, format, imageSize,
                                      data);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // GL3+, ES3+

  const GLubyte* fGetStringi(GLenum name, GLuint index) {
    const GLubyte* ret = nullptr;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fGetStringi);
    ret = mSymbols.fGetStringi(name, index);
    OnSyncCall();
    AFTER_GL_CALL;
    return ret;
  }

  // -----------------------------------------------------------------------------
  // APPLE_framebuffer_multisample

  void fResolveMultisampleFramebufferAPPLE() {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fResolveMultisampleFramebufferAPPLE);
    mSymbols.fResolveMultisampleFramebufferAPPLE();
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // APPLE_fence

  void fFinishObjectAPPLE(GLenum object, GLint name) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fFinishObjectAPPLE);
    mSymbols.fFinishObjectAPPLE(object, name);
    AFTER_GL_CALL;
  }

  realGLboolean fTestObjectAPPLE(GLenum object, GLint name) {
    realGLboolean ret = false;
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fTestObjectAPPLE);
    ret = mSymbols.fTestObjectAPPLE(object, name);
    AFTER_GL_CALL;
    return ret;
  }

  // -----------------------------------------------------------------------------
  // prim_restart

  void fPrimitiveRestartIndex(GLuint index) {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fPrimitiveRestartIndex);
    mSymbols.fPrimitiveRestartIndex(index);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // multiview

  void fFramebufferTextureMultiview(GLenum target, GLenum attachment,
                                    GLuint texture, GLint level,
                                    GLint baseViewIndex,
                                    GLsizei numViews) const {
    BEFORE_GL_CALL;
    ASSERT_SYMBOL_PRESENT(fFramebufferTextureMultiview);
    mSymbols.fFramebufferTextureMultiview(target, attachment, texture, level,
                                          baseViewIndex, numViews);
    AFTER_GL_CALL;
  }

  // -
  // draw_buffers_indexed

  void fBlendEquationSeparatei(GLuint i, GLenum modeRGB,
                               GLenum modeAlpha) const {
    BEFORE_GL_CALL;
    mSymbols.fBlendEquationSeparatei(i, modeRGB, modeAlpha);
    AFTER_GL_CALL;
  }

  void fBlendFuncSeparatei(GLuint i, GLenum sfactorRGB, GLenum dfactorRGB,
                           GLenum sfactorAlpha, GLenum dfactorAlpha) const {
    BEFORE_GL_CALL;
    mSymbols.fBlendFuncSeparatei(i, sfactorRGB, dfactorRGB, sfactorAlpha,
                                 dfactorAlpha);
    AFTER_GL_CALL;
  }

  void fColorMaski(GLuint i, realGLboolean red, realGLboolean green,
                   realGLboolean blue, realGLboolean alpha) const {
    BEFORE_GL_CALL;
    mSymbols.fColorMaski(i, red, green, blue, alpha);
    AFTER_GL_CALL;
  }

  void fDisablei(GLenum capability, GLuint i) const {
    BEFORE_GL_CALL;
    mSymbols.fDisablei(capability, i);
    AFTER_GL_CALL;
  }

  void fEnablei(GLenum capability, GLuint i) const {
    BEFORE_GL_CALL;
    mSymbols.fEnablei(capability, i);
    AFTER_GL_CALL;
  }

  // -

  void fProvokingVertex(GLenum mode) const {
    BEFORE_GL_CALL;
    mSymbols.fProvokingVertex(mode);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // GL_EXT_semaphore
  void fDeleteSemaphoresEXT(GLsizei n, const GLuint* semaphores) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteSemaphoresEXT(n, semaphores);
    AFTER_GL_CALL;
  }

  void fGenSemaphoresEXT(GLsizei n, GLuint* semaphores) {
    BEFORE_GL_CALL;
    mSymbols.fGenSemaphoresEXT(n, semaphores);
    AFTER_GL_CALL;
  }

  void fGetSemaphoreParameterui64vEXT(GLuint semaphore, GLenum pname,
                                      GLuint64* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetSemaphoreParameterui64vEXT(semaphore, pname, params);
    AFTER_GL_CALL;
  }

  realGLboolean fIsSemaphoreEXT(GLuint semaphore) {
    realGLboolean ret = false;
    BEFORE_GL_CALL;
    ret = mSymbols.fIsSemaphoreEXT(semaphore);
    AFTER_GL_CALL;
    return ret;
  }

  void fSemaphoreParameterui64vEXT(GLuint semaphore, GLenum pname,
                                   const GLuint64* params) {
    BEFORE_GL_CALL;
    mSymbols.fSemaphoreParameterui64vEXT(semaphore, pname, params);
    AFTER_GL_CALL;
  }

  void fSignalSemaphoreEXT(GLuint semaphore, GLuint numBufferBarriers,
                           const GLuint* buffers, GLuint numTextureBarriers,
                           const GLuint* textures, const GLenum* dstLayouts) {
    BEFORE_GL_CALL;
    mSymbols.fSignalSemaphoreEXT(semaphore, numBufferBarriers, buffers,
                                 numTextureBarriers, textures, dstLayouts);
    AFTER_GL_CALL;
  }

  void fWaitSemaphoreEXT(GLuint semaphore, GLuint numBufferBarriers,
                         const GLuint* buffers, GLuint numTextureBarriers,
                         const GLuint* textures, const GLenum* srcLayouts) {
    BEFORE_GL_CALL;
    mSymbols.fWaitSemaphoreEXT(semaphore, numBufferBarriers, buffers,
                               numTextureBarriers, textures, srcLayouts);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // GL_EXT_semaphore_fd
  void fImportSemaphoreFdEXT(GLuint semaphore, GLenum handleType, GLint fd) {
    BEFORE_GL_CALL;
    mSymbols.fImportSemaphoreFdEXT(semaphore, handleType, fd);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Extension EXT_memory_object
  void fGetUnsignedBytevEXT(GLenum pname, GLubyte* data) {
    BEFORE_GL_CALL;
    mSymbols.fGetUnsignedBytevEXT(pname, data);
    AFTER_GL_CALL;
  }

  void fGetUnsignedBytei_vEXT(GLenum target, GLuint index, GLubyte* data) {
    BEFORE_GL_CALL;
    mSymbols.fGetUnsignedBytei_vEXT(target, index, data);
    AFTER_GL_CALL;
  }

  void fDeleteMemoryObjectsEXT(GLsizei n, const GLuint* memoryObjects) {
    BEFORE_GL_CALL;
    mSymbols.fDeleteMemoryObjectsEXT(n, memoryObjects);
    AFTER_GL_CALL;
  }

  void fIsMemoryObjectEXT(GLuint memoryObject) {
    BEFORE_GL_CALL;
    mSymbols.fIsMemoryObjectEXT(memoryObject);
    AFTER_GL_CALL;
  }

  void fCreateMemoryObjectsEXT(GLsizei n, GLuint* memoryObjects) {
    BEFORE_GL_CALL;
    mSymbols.fCreateMemoryObjectsEXT(n, memoryObjects);
    AFTER_GL_CALL;
  }

  void fMemoryObjectParameterivEXT(GLuint memoryObject, GLenum pname,
                                   const GLint* params) {
    BEFORE_GL_CALL;
    mSymbols.fMemoryObjectParameterivEXT(memoryObject, pname, params);
    AFTER_GL_CALL;
  }

  void fGetMemoryObjectParameterivEXT(GLuint memoryObject, GLenum pname,
                                      GLint* params) {
    BEFORE_GL_CALL;
    mSymbols.fGetMemoryObjectParameterivEXT(memoryObject, pname, params);
    AFTER_GL_CALL;
  }

  void fTexStorageMem2DEXT(GLenum target, GLsizei levels, GLenum internalFormat,
                           GLsizei width, GLsizei height, GLuint memory,
                           GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTexStorageMem2DEXT(target, levels, internalFormat, width, height,
                                 memory, offset);
    AFTER_GL_CALL;
  }

  void fTexStorageMem2DMultisampleEXT(GLenum target, GLsizei samples,
                                      GLenum internalFormat, GLsizei width,
                                      GLsizei height,
                                      realGLboolean fixedSampleLocations,
                                      GLuint memory, GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTexStorageMem2DMultisampleEXT(target, samples, internalFormat,
                                            width, height, fixedSampleLocations,
                                            memory, offset);
    AFTER_GL_CALL;
  }

  void fTexStorageMem3DEXT(GLenum target, GLsizei levels, GLenum internalFormat,
                           GLsizei width, GLsizei height, GLsizei depth,
                           GLuint memory, GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTexStorageMem3DEXT(target, levels, internalFormat, width, height,
                                 depth, memory, offset);
    AFTER_GL_CALL;
  }

  void fTexStorageMem3DMultisampleEXT(GLenum target, GLsizei samples,
                                      GLenum internalFormat, GLsizei width,
                                      GLsizei height, GLsizei depth,
                                      realGLboolean fixedSampleLocations,
                                      GLuint memory, GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTexStorageMem3DMultisampleEXT(
        target, samples, internalFormat, width, height, depth,
        fixedSampleLocations, memory, offset);
    AFTER_GL_CALL;
  }

  void fBufferStorageMemEXT(GLenum target, GLsizeiptr size, GLuint memory,
                            GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fBufferStorageMemEXT(target, size, memory, offset);
    AFTER_GL_CALL;
  }

  void fTextureStorageMem2DEXT(GLuint texture, GLsizei levels,
                               GLenum internalFormat, GLsizei width,
                               GLsizei height, GLuint memory, GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTextureStorageMem2DEXT(texture, levels, internalFormat, width,
                                     height, memory, offset);
    AFTER_GL_CALL;
  }

  void fTextureStorageMem2DMultisampleEXT(GLuint texture, GLsizei samples,
                                          GLenum internalFormat, GLsizei width,
                                          GLsizei height,
                                          realGLboolean fixedSampleLocations,
                                          GLuint memory, GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTextureStorageMem2DMultisampleEXT(
        texture, samples, internalFormat, width, height, fixedSampleLocations,
        memory, offset);
    AFTER_GL_CALL;
  }

  void fTextureStorageMem3DEXT(GLuint texture, GLsizei levels,
                               GLenum internalFormat, GLsizei width,
                               GLsizei height, GLsizei depth, GLuint memory,
                               GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTextureStorageMem3DEXT(texture, levels, internalFormat, width,
                                     height, depth, memory, offset);
    AFTER_GL_CALL;
  }

  void fTextureStorageMem3DMultisampleEXT(GLuint texture, GLsizei samples,
                                          GLenum internalFormat, GLsizei width,
                                          GLsizei height, GLsizei depth,
                                          realGLboolean fixedSampleLocations,
                                          GLuint memory, GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fTextureStorageMem3DMultisampleEXT(
        texture, samples, internalFormat, width, height, depth,
        fixedSampleLocations, memory, offset);
    AFTER_GL_CALL;
  }

  void fNamedBufferStorageMemEXT(GLuint buffer, GLsizeiptr size, GLuint memory,
                                 GLuint64 offset) {
    BEFORE_GL_CALL;
    mSymbols.fNamedBufferStorageMemEXT(buffer, size, memory, offset);
    AFTER_GL_CALL;
  }

  // -----------------------------------------------------------------------------
  // Extension EXT_memory_object_fd

  void fImportMemoryFdEXT(GLuint memory, GLuint64 size, GLenum handleType,
                          GLint fd) {
    BEFORE_GL_CALL;
    mSymbols.fImportMemoryFdEXT(memory, size, handleType, fd);
    AFTER_GL_CALL;
  }

  // -

#undef BEFORE_GL_CALL
#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(const GLContextDesc&, GLContext* sharedContext = nullptr,
                     bool canUseTLSIsCurrent = false);

  // -----------------------------------------------------------------------------
  // Destructor
 public:
  virtual ~GLContext();

  // Mark this context as destroyed.  This will nullptr out all
  // the GL function pointers!
  void MarkDestroyed();

 protected:
  virtual void OnMarkDestroyed() {}

  // -----------------------------------------------------------------------------
  // Everything that isn't standard GL APIs
 protected:
  typedef gfx::SurfaceFormat SurfaceFormat;

 public:
  virtual void ReleaseSurface() {}

  bool IsDestroyed() const {
    // MarkDestroyed will mark all these as null.
    return mContextLost && mSymbols.fUseProgram == nullptr;
  }

  GLContext* GetSharedContext() { return mSharedContext; }

  /**
   * Returns true if the thread on which this context was created is the
   * currently executing thread.
   */

  bool IsValidOwningThread() const;

  static void PlatformStartup();

 public:
  /**
   * If this context wraps a double-buffered target, swap the back
   * and front buffers.  It should be assumed that after a swap, the
   * contents of the new back buffer are undefined.
   */

  virtual bool SwapBuffers() { return false; }

  /**
   * Stores a damage region (in origin bottom left coordinates), which
   * makes the next SwapBuffers call do eglSwapBuffersWithDamage if supported.
   *
   * Note that even if only part of the context is damaged, the entire buffer
   * needs to be filled with up-to-date contents. This region is only a hint
   * telling the system compositor which parts of the buffer were updated.
   */

  virtual void SetDamage(const nsIntRegion& aDamageRegion) {}

  /**
   * Get the buffer age. If it returns 0, that indicates the buffer state is
   * unknown and the entire frame should be redrawn.
   */

  virtual GLint GetBufferAge() const { return 0; }

  /**
   * 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;

  void BindFB(GLuint fb) {
    fBindFramebuffer(LOCAL_GL_FRAMEBUFFER, fb);
    MOZ_GL_ASSERT(this, !fb || fIsFramebuffer(fb));
  }

  void BindDrawFB(GLuint fb) {
    fBindFramebuffer(LOCAL_GL_DRAW_FRAMEBUFFER_EXT, fb);
  }

  void BindReadFB(GLuint fb) {
    fBindFramebuffer(LOCAL_GL_READ_FRAMEBUFFER_EXT, fb);
  }

  GLuint GetDrawFB() const {
    return GetIntAs<GLuint>(LOCAL_GL_DRAW_FRAMEBUFFER_BINDING_EXT);
  }

  GLuint GetReadFB() const {
    auto bindEnum = LOCAL_GL_READ_FRAMEBUFFER_BINDING_EXT;
    if (!IsSupported(GLFeature::split_framebuffer)) {
      bindEnum = LOCAL_GL_FRAMEBUFFER_BINDING;
    }
    return GetIntAs<GLuint>(bindEnum);
  }

  GLuint GetFB() const {
    const auto ret = GetDrawFB();
    MOZ_ASSERT(ret == GetReadFB());
    return ret;
  }

 private:
  void GetShaderPrecisionFormatNonES2(GLenum shadertype, GLenum precisiontype,
                                      GLint* range, GLint* precision) {
    switch (precisiontype) {
      case LOCAL_GL_LOW_FLOAT:
      case LOCAL_GL_MEDIUM_FLOAT:
      case LOCAL_GL_HIGH_FLOAT:
        // Assume IEEE 754 precision
        range[0] = 127;
        range[1] = 127;
        *precision = 23;
        break;
      case LOCAL_GL_LOW_INT:
      case LOCAL_GL_MEDIUM_INT:
      case LOCAL_GL_HIGH_INT:
        // Some (most) hardware only supports single-precision floating-point
        // numbers, which can accurately represent integers up to +/-16777216
        range[0] = 24;
        range[1] = 24;
        *precision = 0;
        break;
    }
  }

 public:
  virtual GLenum GetPreferredARGB32Format() const { return LOCAL_GL_RGBA; }

  virtual GLenum GetPreferredEGLImageTextureTarget() const {
#ifdef MOZ_WIDGET_GTK
    return LOCAL_GL_TEXTURE_2D;
#else
    if (IsExtensionSupported(OES_EGL_image_external) &&
        mRenderer != GLRenderer::AndroidEmulator) {
      return LOCAL_GL_TEXTURE_EXTERNAL;
    }
    return LOCAL_GL_TEXTURE_2D;
#endif
  }

  virtual bool RenewSurface(widget::CompositorWidget* aWidget) { return false; }

  // Shared code for GL extensions and GLX extensions.
  static bool ListHasExtension(const GLubyte* extensions,
                               const char* extension);

 public:
  enum {
    DebugFlagEnabled = 1 << 0,
    DebugFlagTrace = 1 << 1,
    DebugFlagAbortOnError = 1 << 2
  };

  const uint8_t mDebugFlags;
  static uint8_t ChooseDebugFlags(CreateContextFlags createFlags);

 protected:
  RefPtr<GLContext> mSharedContext;

 public:
  // The thread id which this context was created.
  Maybe<PlatformThreadId> mOwningThreadId;

 protected:
  GLContextSymbols mSymbols = {};

  UniquePtr<GLBlitHelper> mBlitHelper;
  UniquePtr<GLReadTexImageHelper> mReadTexImageHelper;

 public:
  GLBlitHelper* BlitHelper();
  GLReadTexImageHelper* ReadTexImageHelper();

  // Assumes shares are created by all sharing with the same global context.
  bool SharesWith(const GLContext* other) const {
    MOZ_ASSERT(!this->mSharedContext || !this->mSharedContext->mSharedContext);
    MOZ_ASSERT(!other->mSharedContext ||
               !other->mSharedContext->mSharedContext);
    MOZ_ASSERT(!this->mSharedContext || !other->mSharedContext ||
               this->mSharedContext == other->mSharedContext);

    const GLContext* thisShared =
        this->mSharedContext ? this->mSharedContext : this;
    const GLContext* otherShared =
        other->mSharedContext ? other->mSharedContext : other;

    return thisShared == otherShared;
  }

  bool IsFramebufferComplete(GLuint fb, GLenum* status = nullptr);

  // Does not check completeness.
  void AttachBuffersToFB(GLuint colorTex, GLuint colorRB, GLuint depthRB,
                         GLuint stencilRB, GLuint fb,
                         GLenum target = LOCAL_GL_TEXTURE_2D);

  // Passing null is fine if the value you'd get is 0.
  bool AssembleOffscreenFBs(const GLuint colorMSRB, const GLuint depthRB,
                            const GLuint stencilRB, const GLuint texture,
                            GLuint* drawFB, GLuint* readFB);

 protected:
  SharedSurface* mLockedSurface = nullptr;

 public:
  void LockSurface(SharedSurface* surf) { mLockedSurface = surf; }

  void UnlockSurface(SharedSurface* surf) {
    MOZ_ASSERT(mLockedSurface == surf);
    mLockedSurface = nullptr;
  }

  SharedSurface* GetLockedSurface() const { return mLockedSurface; }

  bool IsOffscreen() const { return mDesc.isOffscreen; }

  bool WorkAroundDriverBugs() const { return mWorkAroundDriverBugs; }

  bool IsOffscreenSizeAllowed(const gfx::IntSize& aSize) const;

  virtual bool Init();

 private:
  bool InitImpl();
  void LoadMoreSymbols(const SymbolLoader& loader);
  bool LoadExtSymbols(const SymbolLoader& loader, const SymLoadStruct* list,
                      GLExtensions ext);
  bool LoadFeatureSymbols(const SymbolLoader& loader, const SymLoadStruct* list,
                          GLFeature feature);

 protected:
  void InitExtensions();

  GLint mViewportRect[4] = {};
  GLint mScissorRect[4] = {};

  uint32_t mMaxTexOrRbSize = 0;
  GLint mMaxTextureSize = 0;
  GLint mMaxCubeMapTextureSize = 0;
  GLint mMaxRenderbufferSize = 0;
  GLint mMaxViewportDims[2] = {};
  GLsizei mMaxSamples = 0;
  bool mNeedsTextureSizeChecks = false;
  bool mNeedsFlushBeforeDeleteFB = false;
  bool mTextureAllocCrashesOnMapFailure = false;
  const bool mWorkAroundDriverBugs;
  mutable uint64_t mSyncGLCallCount = 0;

  bool IsTextureSizeSafeToPassToDriver(GLenum target, GLsizei width,
                                       GLsizei height) const {
    if (mNeedsTextureSizeChecks) {
      // some drivers incorrectly handle some large texture sizes that are below
      // the max texture size that they report. So we check ourselves against
      // our own values (mMax[CubeMap]TextureSize). see bug 737182 for Mac Intel
      // 2D textures see bug 684882 for Mac Intel cube map textures see bug
      // 814716 for Mesa Nouveau
      GLsizei maxSize =
          target == LOCAL_GL_TEXTURE_CUBE_MAP ||
                  (target >= LOCAL_GL_TEXTURE_CUBE_MAP_POSITIVE_X &&
                   target <= LOCAL_GL_TEXTURE_CUBE_MAP_NEGATIVE_Z)
              ? mMaxCubeMapTextureSize
              : mMaxTextureSize;
      return width <= maxSize && height <= maxSize;
    }
    return true;
  }

 public:
  auto MaxSamples() const { return uint32_t(mMaxSamples); }
  auto MaxTextureSize() const { return uint32_t(mMaxTextureSize); }
  auto MaxRenderbufferSize() const { return uint32_t(mMaxRenderbufferSize); }
  auto MaxTexOrRbSize() const { return mMaxTexOrRbSize; }

#ifdef MOZ_GL_DEBUG_BUILD
  void CreatedProgram(GLContext* aOrigin, GLuint aName);
  void CreatedShader(GLContext* aOrigin, GLuint aName);
  void CreatedBuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
  void CreatedQueries(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
  void CreatedTextures(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
  void CreatedFramebuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
  void CreatedRenderbuffers(GLContext* aOrigin, GLsizei aCount, GLuint* aNames);
  void DeletedProgram(GLContext* aOrigin, GLuint aName);
  void DeletedShader(GLContext* aOrigin, GLuint aName);
  void DeletedBuffers(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
  void DeletedQueries(GLContext* aOrigin, GLsizei aCount, const GLuint* aNames);
  void DeletedTextures(GLContext* aOrigin, GLsizei aCount,
                       const GLuint* aNames);
  void DeletedFramebuffers(GLContext* aOrigin, GLsizei aCount,
                           const GLuint* aNames);
  void DeletedRenderbuffers(GLContext* aOrigin, GLsizei aCount,
                            const GLuint* aNames);

  void SharedContextDestroyed(GLContext* aChild);
  void ReportOutstandingNames();

  struct NamedResource {
    NamedResource() : origin(nullptr), name(0), originDeleted(false) {}

    NamedResource(GLContext* aOrigin, GLuint aName)
        : origin(aOrigin), name(aName), originDeleted(false) {}

    GLContext* origin;
    GLuint name;
    bool originDeleted;

    // for sorting
    bool operator<(const NamedResource& aOther) const {
      if (intptr_t(origin) < intptr_t(aOther.origin)) return true;
      if (name < aOther.name) return true;
      return false;
    }
    bool operator==(const NamedResource& aOther) const {
      return origin == aOther.origin && name == aOther.name &&
             originDeleted == aOther.originDeleted;
    }
  };

  nsTArray<NamedResource> mTrackedPrograms;
  nsTArray<NamedResource> mTrackedShaders;
  nsTArray<NamedResource> mTrackedTextures;
  nsTArray<NamedResource> mTrackedFramebuffers;
  nsTArray<NamedResource> mTrackedRenderbuffers;
  nsTArray<NamedResource> mTrackedBuffers;
  nsTArray<NamedResource> mTrackedQueries;
#endif

 protected:
  bool mHeavyGLCallsSinceLastFlush = false;

 public:
  void FlushIfHeavyGLCallsSinceLastFlush();
  static bool ShouldSpew();
  static bool ShouldDumpExts();

  // --

  void TexParams_SetClampNoMips(GLenum target = LOCAL_GL_TEXTURE_2D) {
    fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
    fTexParameteri(target, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
    fTexParameteri(target, LOCAL_GL_TEXTURE_MAG_FILTER, LOCAL_GL_NEAREST);
    fTexParameteri(target, LOCAL_GL_TEXTURE_MIN_FILTER, LOCAL_GL_NEAREST);
  }

  // --

  GLuint CreateFramebuffer() {
    GLuint x = 0;
    fGenFramebuffers(1, &x);
    return x;
  }
  GLuint CreateRenderbuffer() {
    GLuint x = 0;
    fGenRenderbuffers(1, &x);
    return x;
  }
  GLuint CreateTexture() {
    GLuint x = 0;
    fGenTextures(1, &x);
    return x;
  }

  void DeleteFramebuffer(const GLuint x) { fDeleteFramebuffers(1, &x); }
  void DeleteRenderbuffer(const GLuint x) { fDeleteRenderbuffers(1, &x); }
  void DeleteTexture(const GLuint x) { fDeleteTextures(1, &x); }
};

bool DoesStringMatch(const char* aString, const char* aWantedString);

void SplitByChar(const nsACString& str, const char delim,
                 std::vector<nsCString>* const out);

template <size_t N>
bool MarkBitfieldByString(const nsACString& str,
                          const charconst (&markStrList)[N],
                          std::bitset<N>* const out_markList) {
  for (size_t i = 0; i < N; i++) {
    if (str.Equals(markStrList[i])) {
      (*out_markList)[i] = 1;
      return true;
    }
  }
  return false;
}

template <size_t N>
void MarkBitfieldByStrings(const std::vector<nsCString>& strList,
                           bool dumpStrings,
                           const charconst (&markStrList)[N],
                           std::bitset<N>* const out_markList) {
  for (auto itr = strList.begin(); itr != strList.end(); ++itr) {
    const nsACString& str = *itr;
    const bool wasMarked = MarkBitfieldByString(str, markStrList, out_markList);
    if (dumpStrings)
      printf_stderr("  %s%s\n", str.BeginReading(), wasMarked ? "(*)" : "");
  }
}

// -

class Renderbuffer final {
 public:
  const WeakPtr<GLContext> weakGl;
  const GLuint name;

 private:
  static GLuint Create(GLContext& gl) {
    GLuint ret = 0;
    gl.fGenRenderbuffers(1, &ret);
    return ret;
  }

 public:
  explicit Renderbuffer(GLContext& gl) : weakGl(&gl), name(Create(gl)) {}

  ~Renderbuffer() {
    const RefPtr<GLContext> gl = weakGl.get();
    if (!gl || !gl->MakeCurrent()) return;
    gl->fDeleteRenderbuffers(1, &name);
  }
};

// -

class Texture final {
 public:
  const WeakPtr<GLContext> weakGl;
  const GLuint name;

 private:
  static GLuint Create(GLContext& gl) {
    GLuint ret = 0;
    gl.fGenTextures(1, &ret);
    return ret;
  }

 public:
  explicit Texture(GLContext& gl) : weakGl(&gl), name(Create(gl)) {}

  ~Texture() {
    const RefPtr<GLContext> gl = weakGl.get();
    if (!gl || !gl->MakeCurrent()) return;
    gl->fDeleteTextures(1, &name);
  }
};

// -

class Sampler final {
 public:
  const WeakPtr<GLContext> weakGl;
  const GLuint name;

 private:
  static GLuint Create(GLContext& gl) {
    GLuint ret = 0;
    gl.fGenSamplers(1, &ret);
    return ret;
  }

 public:
  explicit Sampler(GLContext& gl) : weakGl(&gl), name(Create(gl)) {}

  ~Sampler() {
    const RefPtr<GLContext> gl = weakGl.get();
    if (!gl || !gl->MakeCurrent()) return;
    gl->fDeleteSamplers(1, &name);
  }
};

/**
 * Helper function that creates a 2D texture aSize.width x aSize.height with
 * storage type specified by aFormats. Returns GL texture object id.
 *
 * See mozilla::gl::CreateTexture.
 */

UniquePtr<Texture> CreateTexture(GLContext&, const gfx::IntSize& size);

/**
 * Helper function that calculates the number of bytes required per
 * texel for a texture from its format and type.
 */

uint32_t GetBytesPerTexel(GLenum format, GLenum type);

void MesaMemoryLeakWorkaround();

/* namespace gl */
/* namespace mozilla */

#endif /* GLCONTEXT_H_ */

Messung V0.5 in Prozent
C=92 H=91 G=91

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.75Angebot  (Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-25) ¤

*Eine klare Vorstellung vom Zielzustand






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.