// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/gl/init/create_gr_gl_interface.h"

#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/time/time.h"
#include "base/trace_event/trace_event.h"
#include "base/traits_bag.h"
#include "build/build_config.h"
#include "ui/gl/gl_bindings.h"
#include "ui/gl/gl_display.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_utils.h"
#include "ui/gl/gl_version_info.h"
#include "ui/gl/progress_reporter.h"

#if BUILDFLAG(IS_MAC)
#include "base/mac/mac_util.h"
#endif

namespace gl::init {

// This code emulates GL fences (GL_APPLE_sync or GL_ARB_sync) via
// EGL_KHR_fence_sync extension. It's used to provide Skia ways of
// synchronization on platforms that does not have GL fences but support EGL
namespace {
struct EGLFenceData {
  EGLSync sync;
  EGLDisplay display;
};

GLsync glFenceSyncEmulateEGL(GLenum condition, GLbitfield flags) {
  DCHECK(condition == GL_SYNC_GPU_COMMANDS_COMPLETE);
  DCHECK(flags == 0);

  init::EGLFenceData* data = new EGLFenceData;

  data->display = eglGetCurrentDisplay();
  data->sync = eglCreateSyncKHR(data->display, EGL_SYNC_FENCE_KHR, nullptr);

  return reinterpret_cast<GLsync>(data);
}

void glDeleteSyncEmulateEGL(GLsync sync) {
  EGLFenceData* data = reinterpret_cast<EGLFenceData*>(sync);
  eglDestroySyncKHR(data->display, data->sync);
  delete data;
}

GLenum glClientWaitSyncEmulateEGL(GLsync sync,
                                  GLbitfield flags,
                                  GLuint64 timeout) {
  init::EGLFenceData* data = reinterpret_cast<init::EGLFenceData*>(sync);

  EGLint egl_flags = 0;

  if (flags & GL_SYNC_FLUSH_COMMANDS_BIT) {
    egl_flags |= EGL_SYNC_FLUSH_COMMANDS_BIT;
  }
  EGLint result =
      eglClientWaitSyncKHR(data->display, data->sync, egl_flags, timeout);

  switch (result) {
    case EGL_CONDITION_SATISFIED:
      return GL_CONDITION_SATISFIED;
    case EGL_TIMEOUT_EXPIRED:
      return GL_TIMEOUT_EXPIRED;
    case EGL_FALSE:
      return GL_WAIT_FAILED;
  }

  NOTREACHED();
  return 0;
}

void glWaitSyncEmulateEGL(GLsync sync, GLbitfield flags, GLuint64 timeout) {
  init::EGLFenceData* data = reinterpret_cast<init::EGLFenceData*>(sync);

  DCHECK(timeout == GL_TIMEOUT_IGNORED);
  DCHECK(flags == 0);

  if (!GetDefaultDisplayEGL()->ext->b_EGL_KHR_wait_sync) {
    eglClientWaitSyncKHR(data->display, data->sync, 0, EGL_FOREVER_KHR);
    return;
  }

  EGLint result = eglWaitSyncKHR(data->display, data->sync, 0);
  DCHECK(result);
}

GLboolean glIsSyncEmulateEGL(GLsync sync) {
  NOTREACHED();
  return true;
}

#if BUILDFLAG(IS_APPLE)
std::map<GLuint, base::TimeTicks>& GetProgramCreateTimesMap() {
  static base::NoDestructor<std::map<GLuint, base::TimeTicks>> instance;
  return *instance.get();
}
#endif

}  // namespace

namespace {

template <typename R, typename... Args>
GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_with_api(
    R (gl::GLApi::*func)(Args...),
    gl::GLApi* api) {
  return [func, api](Args... args) { return (api->*func)(args...); };
}

struct FlushHelper {
  FlushHelper() {
    TRACE_EVENT0("gpu",
                 "CreateGrGLInterface - bind_with_flush_on_mac - beforefunc");
    glFlush();
  }
  ~FlushHelper() {
    TRACE_EVENT0("gpu",
                 "CreateGrGLInterface - bind_with_flush_on_mac - afterfunc");
    glFlush();
  }
};

template <bool droppable,
          bool slow,
          bool need_flush,
          typename R,
          typename... Args>
GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind_impl(
    R(GL_BINDING_CALL* func)(Args...),
    gl::ProgressReporter* progress_reporter) {
  // Don't wrap missing functions.
  if (!func)
    return nullptr;

  constexpr bool need_wrap = droppable || slow || need_flush;
  if constexpr (need_wrap) {
    return [func, progress_reporter](Args... args) -> R {
      if constexpr (droppable) {
        if (HasInitializedNullDrawGLBindings())
          return R();
      }

      absl::optional<gl::ScopedProgressReporter> scoped_reporter;
      // Not using constexpr if here to avoid unused progress_reporter warning.
      if (slow && progress_reporter)
        scoped_reporter.emplace(progress_reporter);

      absl::optional<FlushHelper> flush_helper;
      if constexpr (need_flush)
        flush_helper.emplace();
      return func(args...);
    };
  } else {
    return func;
  }
}

// Call can be dropped for tests that setup null draw gl bindings.
struct Droppable {};
// Call needs to be wrapped with ProgressReporter.
struct Slow {};
// Call needs to be wrapped with glFlush call, used on MacOS.
struct NeedFlush {};

#if BUILDFLAG(IS_MAC)
using SlowOnMac = Slow;
using NeedFlushOnMac = NeedFlush;
#else
using SlowOnMac = void;
using NeedFlushOnMac = void;
#endif

template <typename... Traits>
struct BindWithTraits {
  template <typename R, typename... Args>
  static GrGLFunction<R GR_GL_FUNCTION_TYPE(Args...)> bind(
      R(GL_BINDING_CALL* func)(Args...),
      gl::ProgressReporter* progress_reporter,
      bool is_angle) {
    constexpr bool droppable =
        base::trait_helpers::HasTrait<Droppable, Traits...>();
    constexpr bool slow = base::trait_helpers::HasTrait<Slow, Traits...>();
    constexpr bool need_flush =
        base::trait_helpers::HasTrait<NeedFlush, Traits...>();

#if BUILDFLAG(IS_MAC)
    // If running on Apple silicon, regardless of the architecture, don't
    // perform this workaround. See https://crbug.com/1131312.
    if (need_flush && base::mac::GetCPUType() == base::mac::CPUType::kIntel &&
        !is_angle) {
      return bind_impl<droppable, slow, /*need_flush=*/true>(func,
                                                             progress_reporter);
    } else {
      return bind_impl<droppable, slow, /*need_flush=*/false>(
          func, progress_reporter);
    }
#else
    return bind_impl<droppable, slow, need_flush>(func, progress_reporter);
#endif
  }
};

const GLubyte* GetStringHook(const char* gl_version_string,
                             const char* glsl_version_string,
                             GLenum name) {
  switch (name) {
    case GL_VERSION:
      return reinterpret_cast<const GLubyte*>(gl_version_string);
    case GL_SHADING_LANGUAGE_VERSION:
      return reinterpret_cast<const GLubyte*>(glsl_version_string);
    default:
      return glGetString(name);
  }
}

const char* kBlocklistExtensions[] = {
    "GL_APPLE_framebuffer_multisample",
    "GL_ARB_ES3_1_compatibility",
    "GL_ARB_draw_indirect",
    "GL_ARB_invalidate_subdata",
    "GL_ARB_multi_draw_indirect",
    "GL_ARB_sample_shading",
    "GL_ARB_texture_barrier",
    "GL_CHROMIUM_framebuffer_mixed_samples",
    "GL_EXT_direct_state_access",
    "GL_EXT_multi_draw_indirect",
    "GL_EXT_raster_multisample",
    "GL_NV_bindless_texture",
    "GL_NV_framebuffer_mixed_samples",
    "GL_NV_texture_barrier",
    "GL_OES_sample_shading",
    "GL_EXT_draw_instanced",
};

}  // anonymous namespace

sk_sp<GrGLInterface> CreateGrGLInterface(
    const gl::GLVersionInfo& version_info,
    bool use_version_es2,
    gl::ProgressReporter* progress_reporter) {
  // Can't fake ES with desktop GL.
  use_version_es2 &= version_info.is_es;

  gl::ProcsGL* gl = &gl::g_current_gl_driver->fn;
  gl::GLApi* api = gl::g_current_gl_context;

  GrGLStandard standard =
      version_info.is_es ? kGLES_GrGLStandard : kGL_GrGLStandard;

  // Depending on the advertised version and extensions, skia checks for
  // existence of entrypoints. However some of those we don't yet handle in
  // gl_bindings, so we need to fake the version to the maximum fully supported
  // by the bindings (GL 4.1 or ES 3.0), and blocklist extensions that skia
  // handles but bindings don't.
  // TODO(piman): add bindings for missing entrypoints.
  GrGLFunction<GrGLGetStringFn> get_string;
  const bool apply_version_override = use_version_es2 ||
                                      version_info.IsAtLeastGL(4, 2) ||
                                      version_info.IsAtLeastGLES(3, 1);

  if (apply_version_override || version_info.IsVersionSubstituted()) {
    GLVersionInfo::VersionStrings version;
    if (version_info.IsVersionSubstituted()) {
      version = version_info.GetFakeVersionStrings(version_info.major_version,
                                                   version_info.minor_version);
    } else if (version_info.is_es) {
      if (use_version_es2)
        version = version_info.GetFakeVersionStrings(2, 0);
      else
        version = version_info.GetFakeVersionStrings(3, 0);
    } else {
      version = version_info.GetFakeVersionStrings(4, 1);
    }

    get_string = [version](GLenum name) {
      return GetStringHook(version.gl_version, version.glsl_version, name);
    };
  } else {
    get_string = bind_with_api(&gl::GLApi::glGetStringFn, api);
  }

  auto get_stringi = bind_with_api(&gl::GLApi::glGetStringiFn, api);
  auto get_integerv = bind_with_api(&gl::GLApi::glGetIntegervFn, api);

  GrGLExtensions extensions;
  if (!extensions.init(standard, get_string, get_stringi, get_integerv)) {
    LOG(ERROR) << "Failed to initialize extensions";
    return nullptr;
  }
  for (const char* extension : kBlocklistExtensions)
    extensions.remove(extension);

#define BIND_EXTENSION(skia_name, chrome_name, ...)            \
  functions->f##skia_name = BindWithTraits<__VA_ARGS__>::bind( \
      gl->gl##chrome_name##Fn, progress_reporter, version_info.is_angle)
#define BIND(fname, ...) BIND_EXTENSION(fname, fname, __VA_ARGS__)

  GrGLInterface* gl_interface = new GrGLInterface();
  GrGLInterface::Functions* functions = &gl_interface->fFunctions;
  BIND(ActiveTexture);
  BIND(AttachShader);
  BIND(BindAttribLocation);
  BIND(BindBuffer);
  BIND(BindFragDataLocation);
  BIND_EXTENSION(BindUniformLocation, BindUniformLocationCHROMIUM);
  BIND(BeginQuery);
  BIND(BindSampler);
  BIND(BindTexture, SlowOnMac);
  BIND_EXTENSION(BlendBarrier, BlendBarrierKHR);
  BIND(BlendColor);
  BIND(BlendEquation);
  BIND(BlendFunc);
  BIND(BufferData);
  BIND(BufferSubData);
  BIND(Clear, Droppable, SlowOnMac, NeedFlushOnMac);
  BIND(ClearColor);
  BIND(ClearStencil);
  BIND(ClearTexImage);
  BIND(ClearTexSubImage);
  BIND(ColorMask);
  BIND(CompileShader, Slow);
  BIND(CompressedTexImage2D, Slow, NeedFlushOnMac);
  BIND(CompressedTexSubImage2D, Slow);
  BIND(CopyBufferSubData);
  BIND(CopyTexSubImage2D, Slow);
#if BUILDFLAG(IS_APPLE)
  functions->fCreateProgram = [func = gl->glCreateProgramFn]() {
    auto& program_create_times = GetProgramCreateTimesMap();
    GLuint program = func();
    program_create_times[program] = base::TimeTicks::Now();
    return program;
  };
#else
  BIND(CreateProgram);
#endif
  BIND(CreateShader);
  BIND(CullFace);
  BIND_EXTENSION(DeleteBuffers, DeleteBuffersARB, Slow);
#if BUILDFLAG(IS_APPLE)
  functions->fDeleteProgram = [func = gl->glDeleteProgramFn](GLuint program) {
    auto& program_create_times = GetProgramCreateTimesMap();
    program_create_times.erase(program);
    func(program);
  };
#else
  BIND(DeleteProgram, Slow);
#endif
  BIND(DeleteQueries);
  BIND(DeleteSamplers);
  BIND(DeleteShader, Slow);
  BIND(DeleteTextures, Slow, NeedFlushOnMac);
  BIND(DepthMask);
  BIND(Disable);
  BIND(DisableVertexAttribArray);
  BIND_EXTENSION(DiscardFramebuffer, DiscardFramebufferEXT);
  BIND(DrawArrays, Droppable, SlowOnMac);
  BIND(DrawBuffer);
  BIND_EXTENSION(DrawBuffers, DrawBuffersARB);
  BIND(DrawElements, Droppable, SlowOnMac);
  BIND_EXTENSION(DrawArraysInstanced, DrawArraysInstancedANGLE, Droppable,
                 SlowOnMac);
  BIND_EXTENSION(DrawArraysInstancedBaseInstance,
                 DrawArraysInstancedBaseInstanceANGLE, Droppable, SlowOnMac);
  BIND_EXTENSION(MultiDrawArraysInstancedBaseInstance,
                 MultiDrawArraysInstancedBaseInstanceANGLE, Droppable,
                 SlowOnMac);
  BIND_EXTENSION(DrawElementsInstanced, DrawElementsInstancedANGLE, Droppable,
                 SlowOnMac);
  BIND_EXTENSION(DrawElementsInstancedBaseVertexBaseInstance,
                 DrawElementsInstancedBaseVertexBaseInstanceANGLE, Droppable,
                 SlowOnMac);
  BIND_EXTENSION(MultiDrawElementsInstancedBaseVertexBaseInstance,
                 MultiDrawElementsInstancedBaseVertexBaseInstanceANGLE,
                 Droppable, SlowOnMac);

  // GL 4.0 or GL_ARB_draw_indirect or ES 3.1
  BIND(DrawArraysIndirect, Droppable, SlowOnMac);
  BIND(DrawElementsIndirect, Droppable, SlowOnMac);

  BIND(DrawRangeElements, Droppable, SlowOnMac);
  BIND(Enable);
  BIND(EnableVertexAttribArray);
  BIND(EndQuery);
  BIND(Finish, Slow);
  BIND(Flush, Slow);
  BIND(FrontFace);
  BIND_EXTENSION(GenBuffers, GenBuffersARB);
  BIND(GetBufferParameteriv);
  BIND(GetError);
  BIND(GetFloatv);
  BIND(GetIntegerv);
  BIND(GetMultisamplefv);
  BIND(GetQueryObjectiv);
  BIND(GetQueryObjectuiv);
  BIND(GetQueryObjecti64v);
  BIND(GetQueryObjectui64v);
  BIND(QueryCounter);
  BIND(GetQueryiv);
  BIND(GetProgramBinary);
  BIND(GetProgramInfoLog);
#if BUILDFLAG(IS_APPLE)
  functions->fGetProgramiv = [func = gl->glGetProgramivFn](
                                 GLuint program, GLenum pname, GLint* params) {
    func(program, pname, params);
    if (pname == 0x8B82 /* GR_GL_LINK_STATUS */) {
      auto& program_create_times = GetProgramCreateTimesMap();
      auto found = program_create_times.find(program);
      if (found != program_create_times.end()) {
        base::TimeDelta elapsed = base::TimeTicks::Now() - found->second;
        UMA_HISTOGRAM_TIMES("Gpu.GL.ProgramBuildTime", elapsed);
        program_create_times.erase(found);
      }
    }
  };
#else
  BIND(GetProgramiv);
#endif
  BIND(GetShaderInfoLog);
  BIND(GetShaderiv);
  functions->fGetString = get_string;
  BIND(GetStringi);
  BIND(GetShaderPrecisionFormat);
  BIND(GetTexLevelParameteriv);
  BIND(GenQueries);
  BIND(GenSamplers);
  BIND(GenTextures);
  BIND(GetUniformLocation);
  BIND(IsTexture);
  BIND(LineWidth);
  BIND(LinkProgram, Slow);
  BIND(MapBuffer);

  // GL 4.3 or GL_ARB_multi_draw_indirect or ES+GL_EXT_multi_draw_indirect
  // BIND(MultiDrawArraysIndirect);
  // BIND(MultiDrawElementsIndirect);

  BIND(PatchParameteri);
  BIND(PixelStorei);
  BIND(PolygonMode);

  // TODO(vasilyt): Figure out why BIND(fProgramBinary) doesn't fit in
  // GrFunction
  functions->fProgramBinary = gl->glProgramBinaryFn;

  BIND(ProgramParameteri);

  // GL_EXT_raster_multisample
  // BIND_EXTENSION(RasterSamples , RasterSamplesEXT);

  BIND(ReadBuffer);
  BIND(ReadPixels);
  BIND(SamplerParameterf);
  BIND(SamplerParameteri);
  BIND(SamplerParameteriv);
  BIND(Scissor);
  BIND(ShaderSource);
  BIND(StencilFunc);
  BIND(StencilFuncSeparate);
  BIND(StencilMask);
  BIND(StencilMaskSeparate);
  BIND(StencilOp);
  BIND(StencilOpSeparate);
  BIND(TexBuffer);
  BIND(TexBufferRange);
  BIND(TexImage2D, Slow, NeedFlushOnMac);
  BIND(TexParameterf);
  BIND(TexParameterfv);
  BIND(TexParameteri);
  BIND(TexParameteriv);
  BIND_EXTENSION(TexStorage2D, TexStorage2DEXT, Slow, NeedFlushOnMac);
  BIND(TexSubImage2D, Slow, NeedFlushOnMac);

  // GL 4.5 or GL_ARB_texture_barrier or GL_NV_texture_barrier
  // BIND(TextureBarrier);
  // BIND_EXTENSION(TextureBarrier , TextureBarrierNV);

  BIND(Uniform1f);
  BIND(Uniform1i);
  BIND(Uniform1fv);
  BIND(Uniform1iv);
  BIND(Uniform2f);
  BIND(Uniform2i);
  BIND(Uniform2fv);
  BIND(Uniform2iv);
  BIND(Uniform3f);
  BIND(Uniform3i);
  BIND(Uniform3fv);
  BIND(Uniform3iv);
  BIND(Uniform4f);
  BIND(Uniform4i);
  BIND(Uniform4fv);
  BIND(Uniform4iv);
  BIND(UniformMatrix2fv);
  BIND(UniformMatrix3fv);
  BIND(UniformMatrix4fv);
  BIND(UnmapBuffer);
  BIND(UseProgram);
  BIND(VertexAttrib1f);
  BIND(VertexAttrib2fv);
  BIND(VertexAttrib3fv);
  BIND(VertexAttrib4fv);

  BIND_EXTENSION(VertexAttribDivisor, VertexAttribDivisorANGLE);

  BIND(VertexAttribIPointer);

  BIND(VertexAttribPointer);
  BIND(Viewport);
  BIND(BindFragDataLocationIndexed);

  BIND_EXTENSION(BindVertexArray, BindVertexArrayOES);
  BIND_EXTENSION(GenVertexArrays, GenVertexArraysOES);
  BIND_EXTENSION(DeleteVertexArrays, DeleteVertexArraysOES);

  BIND(MapBufferRange);
  BIND(FlushMappedBufferRange);

  BIND_EXTENSION(GenerateMipmap, GenerateMipmapEXT);
  BIND_EXTENSION(GenFramebuffers, GenFramebuffersEXT);
  BIND_EXTENSION(GetFramebufferAttachmentParameteriv,
                 GetFramebufferAttachmentParameterivEXT);
  BIND_EXTENSION(GetRenderbufferParameteriv, GetRenderbufferParameterivEXT);
  BIND_EXTENSION(BindFramebuffer, BindFramebufferEXT, Slow, NeedFlushOnMac);
  BIND_EXTENSION(FramebufferTexture2D, FramebufferTexture2DEXT);
  BIND_EXTENSION(CheckFramebufferStatus, CheckFramebufferStatusEXT);
  BIND_EXTENSION(DeleteFramebuffers, DeleteFramebuffersEXT, Slow,
                 NeedFlushOnMac);
  BIND_EXTENSION(RenderbufferStorage, RenderbufferStorageEXT, NeedFlushOnMac);
  BIND_EXTENSION(GenRenderbuffers, GenRenderbuffersEXT);
  BIND_EXTENSION(DeleteRenderbuffers, DeleteRenderbuffersEXT, NeedFlushOnMac);
  BIND_EXTENSION(FramebufferRenderbuffer, FramebufferRenderbufferEXT);
  BIND_EXTENSION(BindRenderbuffer, BindRenderbufferEXT);

  BIND(RenderbufferStorageMultisample, NeedFlushOnMac);
  BIND_EXTENSION(FramebufferTexture2DMultisample,
                 FramebufferTexture2DMultisampleEXT);
  BIND_EXTENSION(RenderbufferStorageMultisampleES2EXT,
                 RenderbufferStorageMultisampleEXT, NeedFlushOnMac);
  BIND(BlitFramebuffer, NeedFlushOnMac);

  BIND_EXTENSION(InsertEventMarker, InsertEventMarkerEXT);
  BIND_EXTENSION(PushGroupMarker, PushGroupMarkerEXT);
  BIND_EXTENSION(PopGroupMarker, PopGroupMarkerEXT);

  // GL 4.3 or GL_ARB_invalidate_subdata
  // BIND(InvalidateBufferData);
  // BIND(InvalidateBufferSubData);
  // BIND(InvalidateTexImage);
  // BIND(InvalidateTexSubImage);

  BIND(InvalidateFramebuffer);
  BIND(InvalidateSubFramebuffer);

  // GL_NV_bindless_texture
  // BIND_EXTENSION(GetTextureHandle , GetTextureHandleNV);
  // BIND_EXTENSION(GetTextureSamplerHandle , GetTextureSamplerHandleNV);
  // BIND_EXTENSION(MakeTextureHandleResident , MakeTextureHandleResidentNV);
  // BIND_EXTENSION(MakeTextureHandleNonResident ,
  // MakeTextureHandleNonResidentNV); BIND_EXTENSION(GetImageHandle ,
  // GetImageHandleNV); BIND_EXTENSION(MakeImageHandleResident ,
  // MakeImageHandleResidentNV); BIND_EXTENSION(MakeImageHandleNonResident ,
  // MakeImageHandleNonResidentNV); BIND_EXTENSION(IsTextureHandleResident ,
  // IsTextureHandleResidentNV); BIND_EXTENSION(IsImageHandleResident ,
  // IsImageHandleResidentNV); BIND_EXTENSION(UniformHandleui64 ,
  // UniformHandleui64NV); BIND_EXTENSION(UniformHandleui64v ,
  // UniformHandleui64vNV); BIND_EXTENSION(ProgramUniformHandleui64 ,
  // ProgramUniformHandleui64NV); BIND_EXTENSION(ProgramUniformHandleui64v ,
  // ProgramUniformHandleui64vNV);

  // GL_EXT_direct_state_access
  // BIND_EXTENSION(TextureParameteri , TextureParameteriEXT);
  // BIND_EXTENSION(TextureParameteriv , TextureParameterivEXT);
  // BIND_EXTENSION(TextureParameterf , TextureParameterfEXT);
  // BIND_EXTENSION(TextureParameterfv , TextureParameterfvEXT);
  // BIND_EXTENSION(TextureImage1D , TextureImage1DEXT);
  // BIND_EXTENSION(TextureImage2D , TextureImage2DEXT);
  // BIND_EXTENSION(TextureSubImage1D , TextureSubImage1DEXT);
  // BIND_EXTENSION(TextureSubImage2D , TextureSubImage2DEXT);
  // BIND_EXTENSION(CopyTextureImage1D , CopyTextureImage1DEXT);
  // BIND_EXTENSION(CopyTextureImage2D , CopyTextureImage2DEXT);
  // BIND_EXTENSION(CopyTextureSubImage1D , CopyTextureSubImage1DEXT);
  // BIND_EXTENSION(CopyTextureSubImage2D , CopyTextureSubImage2DEXT);
  // BIND_EXTENSION(GetNamedBufferParameteriv , GetNamedBufferParameterivEXT);
  // BIND_EXTENSION(GetNamedBufferPointerv , GetNamedBufferPointervEXT);
  // BIND_EXTENSION(GetNamedBufferSubData , GetNamedBufferSubDataEXT);
  // BIND_EXTENSION(GetTextureImage , GetTextureImageEXT);
  // BIND_EXTENSION(GetTextureParameterfv , GetTextureParameterfvEXT);
  // BIND_EXTENSION(GetTextureParameteriv , GetTextureParameterivEXT);
  // BIND_EXTENSION(GetTextureLevelParameterfv , GetTextureLevelParameterfvEXT);
  // BIND_EXTENSION(GetTextureLevelParameteriv , GetTextureLevelParameterivEXT);
  // BIND_EXTENSION(MapNamedBuffer , MapNamedBufferEXT);
  // BIND_EXTENSION(NamedBufferData , NamedBufferDataEXT);
  // BIND_EXTENSION(NamedBufferSubData , NamedBufferSubDataEXT);
  // BIND_EXTENSION(ProgramUniform1f , ProgramUniform1fEXT);
  // BIND_EXTENSION(ProgramUniform2f , ProgramUniform2fEXT);
  // BIND_EXTENSION(ProgramUniform3f , ProgramUniform3fEXT);
  // BIND_EXTENSION(ProgramUniform4f , ProgramUniform4fEXT);
  // BIND_EXTENSION(ProgramUniform1i , ProgramUniform1iEXT);
  // BIND_EXTENSION(ProgramUniform2i , ProgramUniform2iEXT);
  // BIND_EXTENSION(ProgramUniform3i , ProgramUniform3iEXT);
  // BIND_EXTENSION(ProgramUniform4i , ProgramUniform4iEXT);
  // BIND_EXTENSION(ProgramUniform1fv , ProgramUniform1fvEXT);
  // BIND_EXTENSION(ProgramUniform2fv , ProgramUniform2fvEXT);
  // BIND_EXTENSION(ProgramUniform3fv , ProgramUniform3fvEXT);
  // BIND_EXTENSION(ProgramUniform4fv , ProgramUniform4fvEXT);
  // BIND_EXTENSION(ProgramUniform1iv , ProgramUniform1ivEXT);
  // BIND_EXTENSION(ProgramUniform2iv , ProgramUniform2ivEXT);
  // BIND_EXTENSION(ProgramUniform3iv , ProgramUniform3ivEXT);
  // BIND_EXTENSION(ProgramUniform4iv , ProgramUniform4ivEXT);
  // BIND_EXTENSION(ProgramUniformMatrix2fv , ProgramUniformMatrix2fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix3fv , ProgramUniformMatrix3fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix4fv , ProgramUniformMatrix4fvEXT);
  // BIND_EXTENSION(UnmapNamedBuffer , UnmapNamedBufferEXT);
  // BIND_EXTENSION(TextureImage3D , TextureImage3DEXT);
  // BIND_EXTENSION(TextureSubImage3D , TextureSubImage3DEXT);
  // BIND_EXTENSION(CopyTextureSubImage3D , CopyTextureSubImage3DEXT);
  // BIND_EXTENSION(CompressedTextureImage3D , CompressedTextureImage3DEXT);
  // BIND_EXTENSION(CompressedTextureImage2D , CompressedTextureImage2DEXT);
  // BIND_EXTENSION(CompressedTextureImage1D , CompressedTextureImage1DEXT);
  // BIND_EXTENSION(CompressedTextureSubImage3D,
  //                CompressedTextureSubImage3DEXT);
  // BIND_EXTENSION(CompressedTextureSubImage2D,
  //                CompressedTextureSubImage2DEXT);
  // BIND_EXTENSION(CompressedTextureSubImage1D,
  //                CompressedTextureSubImage1DEXT);
  // BIND_EXTENSION(GetCompressedTextureImage, GetCompressedTextureImageEXT);
  // BIND_EXTENSION(ProgramUniformMatrix2x3fv, ProgramUniformMatrix2x3fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix3x2fv, ProgramUniformMatrix3x2fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix2x4fv, ProgramUniformMatrix2x4fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix4x2fv, ProgramUniformMatrix4x2fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix3x4fv, ProgramUniformMatrix3x4fvEXT);
  // BIND_EXTENSION(ProgramUniformMatrix4x3fv, ProgramUniformMatrix4x3fvEXT);
  // BIND_EXTENSION(NamedRenderbufferStorage, NamedRenderbufferStorageEXT);
  // BIND_EXTENSION(GetNamedRenderbufferParameteriv,
  //                GetNamedRenderbufferParameterivEXT);
  // BIND_EXTENSION(NamedRenderbufferStorageMultisample,
  //                NamedRenderbufferStorageMultisampleEXT);
  // BIND_EXTENSION(CheckNamedFramebufferStatus,
  //                CheckNamedFramebufferStatusEXT);
  // BIND_EXTENSION(NamedFramebufferTexture1D, NamedFramebufferTexture1DEXT);
  // BIND_EXTENSION(NamedFramebufferTexture2D, NamedFramebufferTexture2DEXT);
  // BIND_EXTENSION(NamedFramebufferTexture3D, NamedFramebufferTexture3DEXT);
  // BIND_EXTENSION(NamedFramebufferRenderbuffer,
  //                NamedFramebufferRenderbufferEXT);
  // BIND_EXTENSION(GetNamedFramebufferAttachmentParameteriv,
  //                GetNamedFramebufferAttachmentParameterivEXT);
  // BIND_EXTENSION(GenerateTextureMipmap, GenerateTextureMipmapEXT);
  // BIND_EXTENSION(FramebufferDrawBuffer, FramebufferDrawBufferEXT);
  // BIND_EXTENSION(FramebufferDrawBuffers, FramebufferDrawBuffersEXT);
  // BIND_EXTENSION(FramebufferReadBuffer, FramebufferReadBufferEXT);
  // BIND_EXTENSION(GetFramebufferParameteriv, GetFramebufferParameterivEXT);
  // BIND_EXTENSION(NamedCopyBufferSubData, NamedCopyBufferSubDataEXT);
  // BIND_EXTENSION(VertexArrayVertexOffset, VertexArrayVertexOffsetEXT);
  // BIND_EXTENSION(VertexArrayColorOffset, VertexArrayColorOffsetEXT);
  // BIND_EXTENSION(VertexArrayEdgeFlagOffset, VertexArrayEdgeFlagOffsetEXT);
  // BIND_EXTENSION(VertexArrayIndexOffset, VertexArrayIndexOffsetEXT);
  // BIND_EXTENSION(VertexArrayNormalOffset, VertexArrayNormalOffsetEXT);
  // BIND_EXTENSION(VertexArrayTexCoordOffset, VertexArrayTexCoordOffsetEXT);
  // BIND_EXTENSION(VertexArrayMultiTexCoordOffset,
  //                VertexArrayMultiTexCoordOffsetEXT);
  // BIND_EXTENSION(VertexArrayFogCoordOffset, VertexArrayFogCoordOffsetEXT);
  // BIND_EXTENSION(VertexArraySecondaryColorOffset,
  //                VertexArraySecondaryColorOffsetEXT);
  // BIND_EXTENSION(VertexArrayVertexAttribOffset,
  //                VertexArrayVertexAttribOffsetEXT);
  // BIND_EXTENSION(VertexArrayVertexAttribIOffset,
  //                VertexArrayVertexAttribIOffsetEXT);
  // BIND_EXTENSION(EnableVertexArray, EnableVertexArrayEXT);
  // BIND_EXTENSION(DisableVertexArray, DisableVertexArrayEXT);
  // BIND_EXTENSION(EnableVertexArrayAttrib, EnableVertexArrayAttribEXT);
  // BIND_EXTENSION(DisableVertexArrayAttrib, DisableVertexArrayAttribEXT);
  // BIND_EXTENSION(GetVertexArrayIntegerv, GetVertexArrayIntegervEXT);
  // BIND_EXTENSION(GetVertexArrayPointerv, GetVertexArrayPointervEXT);
  // BIND_EXTENSION(GetVertexArrayIntegeri_v, GetVertexArrayIntegeri_vEXT);
  // BIND_EXTENSION(GetVertexArrayPointeri_v, GetVertexArrayPointeri_vEXT);
  // BIND_EXTENSION(MapNamedBufferRange, MapNamedBufferRangeEXT);
  // BIND_EXTENSION(FlushMappedNamedBufferRange,
  //                FlushMappedNamedBufferRangeEXT);
  // BIND_EXTENSION(TextureBuffer, TextureBufferEXT);

  // Some drivers report GL_KHR_debug but do not provide functions. Validate and
  // remove reported extension from the list if necessary
  // See https://crbug.com/1008125
  if (gl->glDebugMessageControlFn && gl->glDebugMessageInsertFn &&
      gl->glDebugMessageCallbackFn && gl->glGetDebugMessageLogFn &&
      gl->glPushDebugGroupFn && gl->glPopDebugGroupFn && gl->glObjectLabelFn) {
    BIND(DebugMessageControl);
    BIND(DebugMessageInsert);
    BIND(DebugMessageCallback);
    BIND(GetDebugMessageLog);
    BIND(PushDebugGroup);
    BIND(PopDebugGroup);
    BIND(ObjectLabel);
  } else {
    extensions.remove("GL_KHR_debug");
  }

  // GL_EXT_window_rectangles
  BIND_EXTENSION(WindowRectangles, WindowRectanglesEXT);

  // GL_QCOM_tiled_rendering
  BIND_EXTENSION(StartTiling, StartTilingQCOM);
  BIND_EXTENSION(EndTiling, EndTilingQCOM);

  // EGL_KHR_image / EGL_KHR_image_base
  // functions->fCreateImage = nullptr;
  // functions->fDestroyImage = nullptr;

  BIND(FenceSync);
  BIND(IsSync);
  BIND(ClientWaitSync);
  BIND(WaitSync);
  BIND(DeleteSync);

  if (!gl->glFenceSyncFn) {
    // NOTE: Skia uses the same function pointers without APPLE suffix
#if !defined(USE_EGL)
    if (extensions.has("GL_APPLE_sync")) {
      BIND_EXTENSION(FenceSync, FenceSyncAPPLE);
      BIND_EXTENSION(IsSync, IsSyncAPPLE);
      BIND_EXTENSION(ClientWaitSync, ClientWaitSyncAPPLE);
      BIND_EXTENSION(WaitSync, WaitSyncAPPLE);
      BIND_EXTENSION(DeleteSync, DeleteSyncAPPLE);
    }
#else
    if (GetDefaultDisplayEGL()->ext->b_EGL_KHR_fence_sync) {
      // Emulate APPLE_sync via egl
      extensions.add("GL_APPLE_sync");

      functions->fFenceSync = glFenceSyncEmulateEGL;
      functions->fIsSync = glIsSyncEmulateEGL;
      functions->fClientWaitSync = glClientWaitSyncEmulateEGL;
      functions->fWaitSync = glWaitSyncEmulateEGL;
      functions->fDeleteSync = glDeleteSyncEmulateEGL;
    }
#endif  // USE_EGL
  } else if (use_version_es2) {
    // We have gl sync, but want to Skia use ES2 that doesn't have fences.
    // To provide Skia with ways of sync to prevent it calling glFinish we set
    // GL_APPLE_sync support.
    extensions.add("GL_APPLE_sync");
  }

  // Skia can fall back to GL_NV_fence if GLsync objects are not available.
  BIND_EXTENSION(DeleteFences, DeleteFencesNV);
  BIND_EXTENSION(FinishFence, FinishFenceNV);
  BIND_EXTENSION(GenFences, GenFencesNV);
  BIND_EXTENSION(SetFence, SetFenceNV);
  BIND_EXTENSION(TestFence, TestFenceNV);

  BIND(GetInternalformativ);

#undef BIND
#undef BIND_EXTENSION

  gl_interface->fStandard = standard;
  gl_interface->fExtensions.swap(&extensions);
  sk_sp<GrGLInterface> returned(gl_interface);
  return returned;
}

}  // namespace gl::init