Bug 974832 - Add support for WebGL's EXT_disjoint_timer_query. r=dglastonbury, r=smaug
authorAndrew Comminos <acomminos@mozilla.com>
Wed, 27 May 2015 07:12:00 -0400
changeset 265849 a817e0e2a86bf39a2ce86321b11de1e2a3a4bbe8
parent 265848 44ebf04352b79a4cc3b3caf6a0ce1b575d929c00
child 265850 3fa23f765209c4dff4f662832ff3ec54498cba4d
push id8157
push userjlund@mozilla.com
push dateMon, 29 Jun 2015 20:36:23 +0000
treeherdermozilla-aurora@d480e05bd276 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdglastonbury, smaug
bugs974832
milestone41.0a1
Bug 974832 - Add support for WebGL's EXT_disjoint_timer_query. r=dglastonbury, r=smaug
dom/bindings/Bindings.conf
dom/canvas/WebGLContext.h
dom/canvas/WebGLContextExtensions.cpp
dom/canvas/WebGLContextState.cpp
dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
dom/canvas/WebGLExtensions.h
dom/canvas/WebGLStrongTypes.h
dom/canvas/WebGLTimerQuery.cpp
dom/canvas/WebGLTimerQuery.h
dom/canvas/WebGLTypes.h
dom/canvas/moz.build
dom/webidl/WebGL2RenderingContext.webidl
dom/webidl/WebGLRenderingContext.webidl
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1451,16 +1451,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLExtensions.h'
 },
 
 'WebGLExtensionBlendMinMax': {
     'nativeType': 'mozilla::WebGLExtensionBlendMinMax',
     'headerFile': 'WebGLExtensions.h'
 },
 
+'WebGLExtensionDisjointTimerQuery': {
+    'nativeType': 'mozilla::WebGLExtensionDisjointTimerQuery',
+    'headerFile': 'WebGLExtensions.h'
+},
+
 'WebGLFramebuffer': {
     'nativeType': 'mozilla::WebGLFramebuffer',
     'headerFile': 'WebGLFramebuffer.h'
 },
 
 'WebGLProgram': {
     'nativeType': 'mozilla::WebGLProgram',
     'headerFile': 'WebGLProgram.h'
@@ -1509,16 +1514,21 @@ DOMInterfaces = {
     'headerFile': 'WebGLSync.h'
 },
 
 'WebGLTexture': {
     'nativeType': 'mozilla::WebGLTexture',
     'headerFile': 'WebGLTexture.h'
 },
 
+'WebGLTimerQuery': {
+    'nativeType': 'mozilla::WebGLTimerQuery',
+    'headerFile': 'WebGLTimerQuery.h'
+},
+
 'WebGLTransformFeedback': {
     'nativeType': 'mozilla::WebGLTransformFeedback',
     'headerFile': 'WebGLTransformFeedback.h'
 },
 
 'WebGLUniformLocation': {
     'nativeType': 'mozilla::WebGLUniformLocation',
     'headerFile': 'WebGLUniformLocation.h'
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -165,16 +165,17 @@ class WebGLContext
 {
     friend class WebGL2Context;
     friend class WebGLContextUserData;
     friend class WebGLExtensionCompressedTextureATC;
     friend class WebGLExtensionCompressedTextureETC1;
     friend class WebGLExtensionCompressedTexturePVRTC;
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionDepthTexture;
+    friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
     friend class WebGLObserver;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
@@ -1596,16 +1597,17 @@ public:
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
     friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLSampler;
     friend class WebGLShader;
     friend class WebGLSync;
+    friend class WebGLTimerQuery;
     friend class WebGLTransformFeedback;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
     friend class WebGLVertexArrayFake;
     friend class WebGLVertexArrayGL;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
--- a/dom/canvas/WebGLContextExtensions.cpp
+++ b/dom/canvas/WebGLContextExtensions.cpp
@@ -31,16 +31,17 @@ WebGLContext::GetExtensionString(WebGLEx
 
         WEBGL_EXTENSION_IDENTIFIER(ANGLE_instanced_arrays)
         WEBGL_EXTENSION_IDENTIFIER(EXT_blend_minmax)
         WEBGL_EXTENSION_IDENTIFIER(EXT_color_buffer_half_float)
         WEBGL_EXTENSION_IDENTIFIER(EXT_frag_depth)
         WEBGL_EXTENSION_IDENTIFIER(EXT_shader_texture_lod)
         WEBGL_EXTENSION_IDENTIFIER(EXT_sRGB)
         WEBGL_EXTENSION_IDENTIFIER(EXT_texture_filter_anisotropic)
+        WEBGL_EXTENSION_IDENTIFIER(EXT_disjoint_timer_query)
         WEBGL_EXTENSION_IDENTIFIER(OES_element_index_uint)
         WEBGL_EXTENSION_IDENTIFIER(OES_standard_derivatives)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float)
         WEBGL_EXTENSION_IDENTIFIER(OES_texture_half_float_linear)
         WEBGL_EXTENSION_IDENTIFIER(OES_vertex_array_object)
         WEBGL_EXTENSION_IDENTIFIER(WEBGL_color_buffer_float)
@@ -174,23 +175,23 @@ WebGLContext::IsExtensionSupported(WebGL
     default:
         // For warnings-as-errors.
         break;
     }
 
     if (Preferences::GetBool("webgl.enable-draft-extensions", false) ||
         IsWebGL2())
     {
-        /* None for now.
         switch (ext) {
+        case WebGLExtensionID::EXT_disjoint_timer_query:
+            return WebGLExtensionDisjointTimerQuery::IsSupported(this);
         default:
             // For warnings-as-errors.
             break;
         }
-        */
     }
 
     return false;
 }
 
 static bool
 CompareWebGLExtensionName(const nsACString& name, const char* other)
 {
@@ -307,16 +308,19 @@ WebGLContext::EnableExtension(WebGLExten
 
     // EXT_
     case WebGLExtensionID::EXT_blend_minmax:
         obj = new WebGLExtensionBlendMinMax(this);
         break;
     case WebGLExtensionID::EXT_color_buffer_half_float:
         obj = new WebGLExtensionColorBufferHalfFloat(this);
         break;
+    case WebGLExtensionID::EXT_disjoint_timer_query:
+        obj = new WebGLExtensionDisjointTimerQuery(this);
+        break;
     case WebGLExtensionID::EXT_frag_depth:
         obj = new WebGLExtensionFragDepth(this);
         break;
     case WebGLExtensionID::EXT_shader_texture_lod:
         obj = new WebGLExtensionShaderTextureLod(this);
         break;
     case WebGLExtensionID::EXT_sRGB:
         obj = new WebGLExtensionSRGB(this);
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -172,16 +172,31 @@ WebGLContext::GetParameter(JSContext* cx
             if (mBoundVertexArray == mDefaultVertexArray){
                 return WebGLObjectAsJSValue(cx, (WebGLVertexArray *) nullptr, rv);
             }
 
             return WebGLObjectAsJSValue(cx, mBoundVertexArray.get(), rv);
         }
     }
 
+    if (IsExtensionEnabled(WebGLExtensionID::EXT_disjoint_timer_query)) {
+        if (pname == LOCAL_GL_TIMESTAMP_EXT) {
+            GLuint64 iv = 0;
+            gl->fGetInteger64v(pname, (GLint64*) &iv);
+            return JS::NumberValue(uint64_t(iv));
+        } else if (pname == LOCAL_GL_GPU_DISJOINT_EXT) {
+            // When disjoint isn't supported, leave as false.
+            realGLboolean disjoint = 0;
+            if (gl->IsExtensionSupported(gl::GLContext::EXT_disjoint_timer_query)) {
+                gl->fGetBooleanv(pname, &disjoint);
+            }
+            return JS::BooleanValue(bool(disjoint));
+        }
+    }
+
     if (IsWebGL2()) {
         switch (pname) {
             case LOCAL_GL_MAX_SAMPLES:
             case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE:
             case LOCAL_GL_MAX_VERTEX_UNIFORM_COMPONENTS: {
                 GLint val;
                 gl->fGetIntegerv(pname, &val);
                 return JS::NumberValue(uint32_t(val));
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLExtensionDisjointTimerQuery.cpp
@@ -0,0 +1,247 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLExtensions.h"
+
+#include "mozilla/dom/ToJSValue.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "GLContext.h"
+#include "WebGLContext.h"
+#include "WebGLTimerQuery.h"
+
+namespace mozilla {
+
+WebGLExtensionDisjointTimerQuery::WebGLExtensionDisjointTimerQuery(WebGLContext* webgl)
+  : WebGLExtensionBase(webgl)
+  , mActiveQuery(nullptr)
+{
+  MOZ_ASSERT(IsSupported(webgl), "Don't construct extension if unsupported.");
+}
+
+WebGLExtensionDisjointTimerQuery::~WebGLExtensionDisjointTimerQuery()
+{
+}
+
+already_AddRefed<WebGLTimerQuery>
+WebGLExtensionDisjointTimerQuery::CreateQueryEXT()
+{
+  if (mIsLost)
+    return nullptr;
+
+  nsRefPtr<WebGLTimerQuery> query = WebGLTimerQuery::Create(mContext);
+  return query.forget();
+}
+
+void
+WebGLExtensionDisjointTimerQuery::DeleteQueryEXT(WebGLTimerQuery* query)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("deleteQueryEXT", query))
+    return;
+
+  query->RequestDelete();
+}
+
+bool
+WebGLExtensionDisjointTimerQuery::IsQueryEXT(WebGLTimerQuery* query)
+{
+  if (!query)
+    return false;
+
+  if (!mContext->ValidateObjectAllowDeleted("isQueryEXT", query))
+    return false;
+
+  if (query->IsDeleted())
+    return false;
+
+  return true;
+}
+
+void
+WebGLExtensionDisjointTimerQuery::BeginQueryEXT(GLenum target,
+                                                WebGLTimerQuery* query)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("beginQueryEXT", query))
+    return;
+
+  if (query->HasEverBeenBound() && query->Target() != target) {
+    mContext->ErrorInvalidOperation("beginQueryEXT: Query is already bound"
+                                    " to a different target.");
+    return;
+  }
+
+  if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
+    mContext->ErrorInvalidEnumInfo("beginQueryEXT: Can only begin on target"
+                                   " TIME_ELAPSED_EXT.", target);
+    return;
+  }
+
+  if (mActiveQuery) {
+    mContext->ErrorInvalidOperation("beginQueryEXT: A query is already"
+                                    " active.");
+    return;
+  }
+
+  mContext->MakeContextCurrent();
+  gl::GLContext* gl = mContext->GL();
+  gl->fBeginQuery(target, query->GLName());
+  query->BindTo(LOCAL_GL_TIME_ELAPSED_EXT);
+  mActiveQuery = query;
+}
+
+void
+WebGLExtensionDisjointTimerQuery::EndQueryEXT(GLenum target)
+{
+  if (mIsLost)
+    return;
+
+  if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
+    mContext->ErrorInvalidEnumInfo("endQueryEXT: Can only end on"
+                                   " TIME_ELAPSED_EXT.", target);
+    return;
+  }
+
+  if (!mActiveQuery) {
+    mContext->ErrorInvalidOperation("endQueryEXT: A query is not active.");
+    return;
+  }
+
+  mContext->MakeContextCurrent();
+  mContext->GL()->fEndQuery(target);
+  mActiveQuery = nullptr;
+}
+
+void
+WebGLExtensionDisjointTimerQuery::QueryCounterEXT(WebGLTimerQuery* query,
+                                                  GLenum target)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("queryCounterEXT", query))
+    return;
+
+  if (target != LOCAL_GL_TIMESTAMP_EXT) {
+    mContext->ErrorInvalidEnumInfo("queryCounterEXT: requires"
+                                   " TIMESTAMP_EXT.", target);
+    return;
+  }
+
+  mContext->MakeContextCurrent();
+  mContext->GL()->fQueryCounter(query->GLName(), target);
+  query->BindTo(LOCAL_GL_TIMESTAMP_EXT);
+}
+
+void
+WebGLExtensionDisjointTimerQuery::GetQueryEXT(JSContext* cx, GLenum target,
+                                              GLenum pname,
+                                              JS::MutableHandle<JS::Value> retval)
+{
+  if (mIsLost)
+    return;
+
+  mContext->MakeContextCurrent();
+  switch (pname) {
+  case LOCAL_GL_CURRENT_QUERY_EXT: {
+    if (target != LOCAL_GL_TIME_ELAPSED_EXT) {
+      mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
+                                     target);
+      return;
+    }
+    if (mActiveQuery) {
+      JS::Rooted<JS::Value> v(cx);
+      dom::GetOrCreateDOMReflector(cx, mActiveQuery.get(), &v);
+      retval.set(v);
+    } else {
+      retval.set(JS::NullValue());
+    }
+    break;
+  }
+  case LOCAL_GL_QUERY_COUNTER_BITS_EXT: {
+    if (target != LOCAL_GL_TIME_ELAPSED_EXT &&
+        target != LOCAL_GL_TIMESTAMP_EXT) {
+      mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query target.",
+                                     target);
+      return;
+    }
+    GLint bits = 0;
+    mContext->GL()->fGetQueryiv(target, pname, &bits);
+    retval.set(JS::Int32Value(int32_t(bits)));
+    break;
+  }
+  default:
+    mContext->ErrorInvalidEnumInfo("getQueryEXT: Invalid query property.",
+                                   pname);
+    break;
+  }
+}
+
+void
+WebGLExtensionDisjointTimerQuery::GetQueryObjectEXT(JSContext* cx,
+                                                    WebGLTimerQuery* query,
+                                                    GLenum pname,
+                                                    JS::MutableHandle<JS::Value> retval)
+{
+  if (mIsLost)
+    return;
+
+  if (!mContext->ValidateObject("getQueryObjectEXT", query))
+    return;
+
+  if (query == mActiveQuery.get()) {
+    mContext->ErrorInvalidOperation("getQueryObjectEXT: Query must not be"
+                                    " active.");
+  }
+
+  mContext->MakeContextCurrent();
+  // XXX: Note that the query result *may change* within the same task!
+  // This does not follow the specification, which states that all calls
+  // checking query results must return the same value until the event loop
+  // is empty.
+  switch (pname) {
+  case LOCAL_GL_QUERY_RESULT_EXT: {
+    GLuint64 result = 0;
+    mContext->GL()->fGetQueryObjectui64v(query->GLName(),
+                                         LOCAL_GL_QUERY_RESULT_EXT,
+                                         &result);
+    retval.set(JS::NumberValue(result));
+    break;
+  }
+  case LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT: {
+    GLuint avail = 0;
+    mContext->GL()->fGetQueryObjectuiv(query->GLName(),
+                                       LOCAL_GL_QUERY_RESULT_AVAILABLE_EXT,
+                                       &avail);
+    retval.set(JS::BooleanValue(bool(avail)));
+    break;
+  }
+  default:
+    mContext->ErrorInvalidEnumInfo("getQueryObjectEXT: Invalid query"
+                                   " property.", pname);
+    break;
+  }
+}
+
+bool
+WebGLExtensionDisjointTimerQuery::IsSupported(const WebGLContext* webgl)
+{
+  webgl->MakeContextCurrent();
+  gl::GLContext* gl = webgl->GL();
+  return gl->IsSupported(gl::GLFeature::query_objects) &&
+         gl->IsSupported(gl::GLFeature::get_query_object_i64v) &&
+         gl->IsSupported(gl::GLFeature::query_counter); // provides GL_TIMESTAMP
+}
+
+
+IMPL_WEBGL_EXTENSION_GOOP(WebGLExtensionDisjointTimerQuery)
+
+} // namespace mozilla
--- a/dom/canvas/WebGLExtensions.h
+++ b/dom/canvas/WebGLExtensions.h
@@ -11,16 +11,18 @@
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 #include "WebGLTypes.h"
 
 namespace mozilla {
 
 class WebGLContext;
 class WebGLShader;
+class WebGLQuery;
+class WebGLTimerQuery;
 class WebGLVertexArray;
 
 class WebGLExtensionBase
     : public nsWrapperCache
     , public WebGLContextBoundObject
 {
 public:
     explicit WebGLExtensionBase(WebGLContext* webgl);
@@ -324,11 +326,41 @@ public:
     explicit WebGLExtensionBlendMinMax(WebGLContext* webgl);
     virtual ~WebGLExtensionBlendMinMax();
 
     static bool IsSupported(const WebGLContext*);
 
     DECL_WEBGL_EXTENSION_GOOP
 };
 
+class WebGLExtensionDisjointTimerQuery
+    : public WebGLExtensionBase
+{
+public:
+    explicit WebGLExtensionDisjointTimerQuery(WebGLContext* webgl);
+    virtual ~WebGLExtensionDisjointTimerQuery();
+
+    already_AddRefed<WebGLTimerQuery> CreateQueryEXT();
+    void DeleteQueryEXT(WebGLTimerQuery* query);
+    bool IsQueryEXT(WebGLTimerQuery* query);
+    void BeginQueryEXT(GLenum target, WebGLTimerQuery* query);
+    void EndQueryEXT(GLenum target);
+    void QueryCounterEXT(WebGLTimerQuery* query, GLenum target);
+    void GetQueryEXT(JSContext *cx, GLenum target, GLenum pname,
+                     JS::MutableHandle<JS::Value> retval);
+    void GetQueryObjectEXT(JSContext *cx, WebGLTimerQuery* query,
+                           GLenum pname,
+                           JS::MutableHandle<JS::Value> retval);
+
+    static bool IsSupported(const WebGLContext*);
+
+    DECL_WEBGL_EXTENSION_GOOP
+
+private:
+    /**
+     * An active TIME_ELAPSED query participating in a begin/end block.
+     */
+    WebGLRefPtr<WebGLTimerQuery> mActiveQuery;
+};
+
 } // namespace mozilla
 
 #endif // WEBGL_EXTENSIONS_H_
--- a/dom/canvas/WebGLStrongTypes.h
+++ b/dom/canvas/WebGLStrongTypes.h
@@ -449,9 +449,15 @@ STRONG_GLENUM_BEGIN(BufferBinding)
     STRONG_GLENUM_VALUE(ARRAY_BUFFER),              // 0x8892
     STRONG_GLENUM_VALUE(ELEMENT_ARRAY_BUFFER),      // 0x8893
     STRONG_GLENUM_VALUE(PIXEL_PACK_BUFFER),         // 0x88EB
     STRONG_GLENUM_VALUE(PIXEL_UNPACK_BUFFER),       // 0x88EC
     STRONG_GLENUM_VALUE(UNIFORM_BUFFER),            // 0x8A11
     STRONG_GLENUM_VALUE(TRANSFORM_FEEDBACK_BUFFER), // 0x8C8E
 STRONG_GLENUM_END(BufferBinding)
 
+STRONG_GLENUM_BEGIN(QueryBinding)
+    STRONG_GLENUM_VALUE(NONE),
+    STRONG_GLENUM_VALUE(TIME_ELAPSED_EXT),
+    STRONG_GLENUM_VALUE(TIMESTAMP_EXT),
+STRONG_GLENUM_END(QueryBinding)
+
 #endif // WEBGL_STRONG_TYPES_H_
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLTimerQuery.cpp
@@ -0,0 +1,49 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "WebGLTimerQuery.h"
+
+#include "GLContext.h"
+#include "mozilla/dom/WebGLRenderingContextBinding.h"
+#include "nsContentUtils.h"
+#include "WebGLContext.h"
+
+namespace mozilla {
+
+JSObject*
+WebGLTimerQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
+{
+  return dom::WebGLTimerQueryBinding::Wrap(cx, this, aGivenProto);
+}
+
+WebGLTimerQuery::WebGLTimerQuery(WebGLContext* webgl, GLuint aName)
+  : WebGLBindableName<QueryBinding>(aName)
+  , WebGLContextBoundObject(webgl)
+{
+}
+
+WebGLTimerQuery*
+WebGLTimerQuery::Create(WebGLContext* webgl)
+{
+  GLuint name = 0;
+  webgl->MakeContextCurrent();
+  webgl->gl->fGenQueries(1, &name);
+  return new WebGLTimerQuery(webgl, name);
+}
+
+void
+WebGLTimerQuery::Delete()
+{
+  mContext->MakeContextCurrent();
+  mContext->gl->fDeleteQueries(1, &mGLName);
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLTimerQuery)
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WebGLTimerQuery, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WebGLTimerQuery, Release)
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/canvas/WebGLTimerQuery.h
@@ -0,0 +1,49 @@
+/* -*- 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 WEBGL_TIMER_QUERY_H_
+#define WEBGL_TIMER_QUERY_H_
+
+#include "nsWrapperCache.h"
+#include "WebGLObjectModel.h"
+
+namespace mozilla {
+
+class WebGLTimerQuery final
+  : public nsWrapperCache
+  , public WebGLBindableName<QueryBinding>
+  , public WebGLRefCountedObject<WebGLTimerQuery>
+  , public WebGLContextBoundObject
+{
+public:
+  static WebGLTimerQuery* Create(WebGLContext* webgl);
+
+  // WebGLRefCountedObject
+  void Delete();
+
+  // nsWrapperCache
+  WebGLContext* GetParentObject() const {
+    return Context();
+  }
+
+  // NS
+  virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override;
+
+  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WebGLTimerQuery)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WebGLTimerQuery)
+
+private:
+  explicit WebGLTimerQuery(WebGLContext* webgl, GLuint aName);
+  ~WebGLTimerQuery() {
+    DeleteOnce();
+  }
+
+  friend class WebGLExtensionDisjointTimerQuery;
+};
+
+} // namespace mozilla
+
+#endif // WEBGL_TIMER_QUERY_H_
--- a/dom/canvas/WebGLTypes.h
+++ b/dom/canvas/WebGLTypes.h
@@ -146,16 +146,17 @@ enum class WebGLTexDimensions : uint8_t 
 enum class WebGLExtensionID : uint8_t {
     ANGLE_instanced_arrays,
     EXT_blend_minmax,
     EXT_color_buffer_half_float,
     EXT_frag_depth,
     EXT_sRGB,
     EXT_shader_texture_lod,
     EXT_texture_filter_anisotropic,
+    EXT_disjoint_timer_query,
     OES_element_index_uint,
     OES_standard_derivatives,
     OES_texture_float,
     OES_texture_float_linear,
     OES_texture_half_float,
     OES_texture_half_float_linear,
     OES_vertex_array_object,
     WEBGL_color_buffer_float,
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -88,16 +88,17 @@ UNIFIED_SOURCES += [
     'WebGLExtensionColorBufferHalfFloat.cpp',
     'WebGLExtensionCompressedTextureATC.cpp',
     'WebGLExtensionCompressedTextureETC1.cpp',
     'WebGLExtensionCompressedTexturePVRTC.cpp',
     'WebGLExtensionCompressedTextureS3TC.cpp',
     'WebGLExtensionDebugRendererInfo.cpp',
     'WebGLExtensionDebugShaders.cpp',
     'WebGLExtensionDepthTexture.cpp',
+    'WebGLExtensionDisjointTimerQuery.cpp',
     'WebGLExtensionDrawBuffers.cpp',
     'WebGLExtensionElementIndexUint.cpp',
     'WebGLExtensionFragDepth.cpp',
     'WebGLExtensionInstancedArrays.cpp',
     'WebGLExtensionLoseContext.cpp',
     'WebGLExtensionShaderTextureLod.cpp',
     'WebGLExtensionSRGB.cpp',
     'WebGLExtensionStandardDerivatives.cpp',
@@ -115,16 +116,17 @@ UNIFIED_SOURCES += [
     'WebGLRenderbuffer.cpp',
     'WebGLSampler.cpp',
     'WebGLShader.cpp',
     'WebGLShaderPrecisionFormat.cpp',
     'WebGLShaderValidator.cpp',
     'WebGLSync.cpp',
     'WebGLTexelConversions.cpp',
     'WebGLTexture.cpp',
+    'WebGLTimerQuery.cpp',
     'WebGLTransformFeedback.cpp',
     'WebGLUniformLocation.cpp',
     'WebGLValidateStrings.cpp',
     'WebGLVertexArray.cpp',
     'WebGLVertexArrayFake.cpp',
     'WebGLVertexArrayGL.cpp',
 ]
 LOCAL_INCLUDES += [
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -3,17 +3,16 @@
  * 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/.
  *
  * The source for this IDL is found at https://www.khronos.org/registry/webgl/specs/latest/2.0
  * This IDL depends on WebGLRenderingContext.webidl
  */
 
 typedef long long GLint64; // Should this be int64?
-typedef unsigned long long GLuint64; // Should this be uint64?
 
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLQuery {
 };
 
 [Pref="webgl.enable-prototype-webgl2"]
 interface WebGLSampler {
 };
--- a/dom/webidl/WebGLRenderingContext.webidl
+++ b/dom/webidl/WebGLRenderingContext.webidl
@@ -23,16 +23,17 @@ typedef short          GLshort;
 typedef long           GLint;
 typedef long           GLsizei;
 typedef long long      GLintptr;
 typedef long long      GLsizeiptr;
 // Ideally the typedef below would use 'unsigned byte', but that doesn't currently exist in Web IDL.
 typedef octet          GLubyte;        /* 'octet' should be an unsigned 8 bit type. */
 typedef unsigned short GLushort;
 typedef unsigned long  GLuint;
+typedef unsigned long long GLuint64;
 typedef unrestricted float GLfloat;
 typedef unrestricted float GLclampf;
 
 dictionary WebGLContextAttributes {
     // boolean alpha = true;
     // We deviate from the spec here.
     // If alpha isn't specified, we rely on a pref ("webgl.default-no-alpha")
     boolean alpha;
@@ -970,8 +971,34 @@ interface WebGLExtensionInstancedArrays 
     void vertexAttribDivisorANGLE(GLuint index, GLuint divisor);
 };
 
 [NoInterfaceObject]
 interface WebGLExtensionBlendMinMax {
     const GLenum MIN_EXT = 0x8007;
     const GLenum MAX_EXT = 0x8008;
 };
+
+// FIXME: Spec interface name is WebGLTimerQueryEXT.
+[NoInterfaceObject]
+interface WebGLTimerQuery {
+};
+
+// FIXME: Spec interface name is EXT_disjoint_timer_query.
+[NoInterfaceObject]
+interface WebGLExtensionDisjointTimerQuery {
+    const GLenum QUERY_COUNTER_BITS_EXT = 0x8864;
+    const GLenum CURRENT_QUERY_EXT = 0x8865;
+    const GLenum QUERY_RESULT_EXT = 0x8866;
+    const GLenum QUERY_RESULT_AVAILABLE_EXT = 0x8867;
+    const GLenum TIME_ELAPSED_EXT = 0x88BF;
+    const GLenum TIMESTAMP_EXT = 0x8E28;
+    const GLenum GPU_DISJOINT_EXT = 0x8FBB;
+
+    WebGLTimerQuery? createQueryEXT();
+    void deleteQueryEXT(WebGLTimerQuery? query);
+    [WebGLHandlesContextLoss] boolean isQueryEXT(WebGLTimerQuery? query);
+    void beginQueryEXT(GLenum target, WebGLTimerQuery? query);
+    void endQueryEXT(GLenum target);
+    void queryCounterEXT(WebGLTimerQuery? query, GLenum target);
+    any getQueryEXT(GLenum target, GLenum pname);
+    any getQueryObjectEXT(WebGLTimerQuery? query, GLenum pname);
+};