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 277082 a817e0e2a86bf39a2ce86321b11de1e2a3a4bbe8
parent 277081 44ebf04352b79a4cc3b3caf6a0ce1b575d929c00
child 277083 3fa23f765209c4dff4f662832ff3ec54498cba4d
push id4932
push userjlund@mozilla.com
push dateMon, 10 Aug 2015 18:23:06 +0000
treeherdermozilla-beta@6dd5a4f5f745 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdglastonbury, smaug
bugs974832
milestone41.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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);
+};