bug 892978 - [WebGL 2.0] Query objects (GL_ARB_occlusion_query_boolean) - r=jgilbert
authorGuillaume Abadie <gabadie@mozilla.com>
Tue, 06 Aug 2013 17:23:46 -0400
changeset 141588 115955c895210c89887df92aa22490ff99df40d4
parent 141587 c2aa14f704977737f8d6380570bddf988caf56c8
child 141589 b3ea93ee7659f57c0260e00ad5b922e53803e6f0
push id32145
push usergabadie@mozilla.com
push dateTue, 06 Aug 2013 21:24:13 +0000
treeherdermozilla-inbound@115955c89521 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjgilbert
bugs892978
milestone26.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 892978 - [WebGL 2.0] Query objects (GL_ARB_occlusion_query_boolean) - r=jgilbert
CLOBBER
content/canvas/src/WebGLContext.cpp
content/canvas/src/WebGLContext.h
content/canvas/src/WebGLContextAsyncQueries.cpp
content/canvas/src/WebGLContextValidate.cpp
content/canvas/src/WebGLQuery.cpp
content/canvas/src/WebGLQuery.h
content/canvas/src/moz.build
dom/bindings/Bindings.conf
dom/webidl/WebGL2RenderingContext.webidl
gfx/gl/GLContext.cpp
gfx/gl/GLContext.h
gfx/gl/GLContextSymbols.h
gfx/gl/GLDefs.h
--- a/CLOBBER
+++ b/CLOBBER
@@ -13,9 +13,9 @@
 #          |               |
 #          O <-- Clobber   O  <-- Clobber
 #
 # Note: The description below will be part of the error message shown to users.
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
-Removal of XPIDL for bug 893117 requires a clobber to make sure interfaces aren't generated.
+Add an WebIDL interface for bug 892978 requires a clobber for Windows.
--- a/content/canvas/src/WebGLContext.cpp
+++ b/content/canvas/src/WebGLContext.cpp
@@ -8,16 +8,17 @@
 #include "WebGLObjectModel.h"
 #include "WebGLExtensions.h"
 #include "WebGLContextUtils.h"
 #include "WebGLBuffer.h"
 #include "WebGLVertexAttribData.h"
 #include "WebGLMemoryMultiReporterWrapper.h"
 #include "WebGLFramebuffer.h"
 #include "WebGLVertexArray.h"
+#include "WebGLQuery.h"
 
 #include "AccessCheck.h"
 #include "nsIConsoleService.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIClassInfoImpl.h"
 #include "nsContentUtils.h"
 #include "nsIXPConnect.h"
 #include "nsError.h"
@@ -232,16 +233,17 @@ WebGLContext::DestroyResourcesAndContext
 
     gl->MakeCurrent();
 
     mBound2DTextures.Clear();
     mBoundCubeMapTextures.Clear();
     mBoundArrayBuffer = nullptr;
     mCurrentProgram = nullptr;
     mBoundFramebuffer = nullptr;
+    mActiveOcclusionQuery = nullptr;
     mBoundRenderbuffer = nullptr;
     mBoundVertexArray = nullptr;
     mDefaultVertexArray = nullptr;
 
     while (!mTextures.isEmpty())
         mTextures.getLast()->DeleteOnce();
     while (!mVertexArrays.isEmpty())
         mVertexArrays.getLast()->DeleteOnce();
@@ -250,16 +252,18 @@ WebGLContext::DestroyResourcesAndContext
     while (!mRenderbuffers.isEmpty())
         mRenderbuffers.getLast()->DeleteOnce();
     while (!mFramebuffers.isEmpty())
         mFramebuffers.getLast()->DeleteOnce();
     while (!mShaders.isEmpty())
         mShaders.getLast()->DeleteOnce();
     while (!mPrograms.isEmpty())
         mPrograms.getLast()->DeleteOnce();
+    while (!mQueries.isEmpty())
+        mQueries.getLast()->DeleteOnce();
 
     if (mBlackTexturesAreInitialized) {
         gl->fDeleteTextures(1, &mBlackTexture2D);
         gl->fDeleteTextures(1, &mBlackTextureCubeMap);
         mBlackTexturesAreInitialized = false;
     }
 
     if (mFakeVertexAttrib0BufferObject) {
@@ -1594,26 +1598,27 @@ WebGLContext::GetSupportedExtensions(JSC
 
 //
 // XPCOM goop
 //
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLContext)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLContext)
 
-NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_9(WebGLContext,
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_10(WebGLContext,
   mCanvasElement,
   mExtensions,
   mBound2DTextures,
   mBoundCubeMapTextures,
   mBoundArrayBuffer,
   mCurrentProgram,
   mBoundFramebuffer,
   mBoundRenderbuffer,
-  mBoundVertexArray)
+  mBoundVertexArray,
+  mActiveOcclusionQuery)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLContext)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMWebGLRenderingContext)
   NS_INTERFACE_MAP_ENTRY(nsICanvasRenderingContextInternal)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   // If the exact way we cast to nsISupports here ever changes, fix our
   // ToSupports() method.
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -66,16 +66,17 @@ namespace mozilla {
 class WebGLMemoryPressureObserver;
 class WebGLContextBoundObject;
 class WebGLActiveInfo;
 class WebGLExtensionBase;
 class WebGLBuffer;
 class WebGLVertexAttribData;
 class WebGLShader;
 class WebGLProgram;
+class WebGLQuery;
 class WebGLUniformLocation;
 class WebGLFramebuffer;
 class WebGLRenderbuffer;
 class WebGLShaderPrecisionFormat;
 class WebGLTexture;
 class WebGLVertexArray;
 
 namespace dom {
@@ -789,16 +790,31 @@ public:
                               const WebGLfloat* ptr);
     
     void VertexAttribPointer(WebGLuint index, WebGLint size, WebGLenum type,
                              WebGLboolean normalized, WebGLsizei stride,
                              WebGLintptr byteOffset);
     void Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei height);
 
 // -----------------------------------------------------------------------------
+// Asynchronous Queries (WebGLContextAsyncQueries.cpp)
+public:
+    already_AddRefed<WebGLQuery> CreateQuery();
+    void DeleteQuery(WebGLQuery *query);
+    void BeginQuery(WebGLenum target, WebGLQuery *query);
+    void EndQuery(WebGLenum target);
+    bool IsQuery(WebGLQuery *query);
+    already_AddRefed<WebGLQuery> GetQuery(WebGLenum target, WebGLenum pname);
+    JS::Value GetQueryObject(JSContext* cx, WebGLQuery *query, WebGLenum pname);
+
+private:
+    bool ValidateTargetParameter(WebGLenum target, const char* infos);
+    WebGLRefPtr<WebGLQuery>& GetActiveQueryByTarget(WebGLenum target);
+
+// -----------------------------------------------------------------------------
 // Vertices Feature (WebGLContextVertices.cpp)
 public:
     void DrawArrays(GLenum mode, WebGLint first, WebGLsizei count);
     void DrawArraysInstanced(GLenum mode, WebGLint first, WebGLsizei count, WebGLsizei primcount);
     void DrawElements(WebGLenum mode, WebGLsizei count, WebGLenum type, WebGLintptr byteOffset);
     void DrawElementsInstanced(WebGLenum mode, WebGLsizei count, WebGLenum type,
                                WebGLintptr byteOffset, WebGLsizei primcount);
 
@@ -1083,20 +1099,22 @@ protected:
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
 
     uint32_t mMaxFramebufferColorAttachments;
 
     WebGLRefPtr<WebGLFramebuffer> mBoundFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
     WebGLRefPtr<WebGLVertexArray> mBoundVertexArray;
+    WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
 
     LinkedList<WebGLTexture> mTextures;
     LinkedList<WebGLBuffer> mBuffers;
     LinkedList<WebGLProgram> mPrograms;
+    LinkedList<WebGLQuery> mQueries;
     LinkedList<WebGLShader> mShaders;
     LinkedList<WebGLRenderbuffer> mRenderbuffers;
     LinkedList<WebGLFramebuffer> mFramebuffers;
     LinkedList<WebGLVertexArray> mVertexArrays;
 
     WebGLRefPtr<WebGLVertexArray> mDefaultVertexArray;
 
     // PixelStore parameters
@@ -1175,16 +1193,17 @@ public:
     // console logging helpers
     void GenerateWarning(const char *fmt, ...);
     void GenerateWarning(const char *fmt, va_list ap);
 
     friend class WebGLTexture;
     friend class WebGLFramebuffer;
     friend class WebGLRenderbuffer;
     friend class WebGLProgram;
+    friend class WebGLQuery;
     friend class WebGLBuffer;
     friend class WebGLShader;
     friend class WebGLUniformLocation;
     friend class WebGLVertexArray;
 };
 
 // used by DOM bindings in conjunction with GetParentObject
 inline nsISupports*
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLContextAsyncQueries.cpp
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "WebGLContext.h"
+#include "WebGLQuery.h"
+
+using namespace mozilla;
+
+/*
+ * We fake ANY_SAMPLES_PASSED and ANY_SAMPLES_PASSED_CONSERVATIVE with 
+ * SAMPLES_PASSED on desktop.
+ *
+ * OpenGL ES 3.0 spec 4.1.6
+ *  If the target of the query is ANY_SAMPLES_PASSED_CONSERVATIVE, an implementation
+ *  may choose to use a less precise version of the test which can additionally set
+ *  the samples-boolean state to TRUE in some other implementation-dependent cases.
+ */
+
+static const char*
+GetQueryTargetEnumString(WebGLenum target)
+{
+    switch (target)
+    {
+        case LOCAL_GL_ANY_SAMPLES_PASSED:
+            return "ANY_SAMPLES_PASSED";
+        case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
+            return "ANY_SAMPLES_PASSED_CONSERVATIVE";
+        default:
+            break;
+    }
+    
+    MOZ_ASSERT(false, "Unknown query `target`.");
+    return "UNKNOWN_QUERY_TARGET";
+}
+
+already_AddRefed<WebGLQuery>
+WebGLContext::CreateQuery()
+{
+    if (!IsContextStable())
+        return nullptr;
+
+    if (mActiveOcclusionQuery && !gl->IsGLES2()) {
+        /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
+         * Calling either GenQueriesARB or DeleteQueriesARB while any query of
+         * any target is active causes an INVALID_OPERATION error to be
+         * generated.
+         */
+        GenerateWarning("createQuery: the WebGL 2 prototype might generate INVALID_OPERATION"
+                        "when creating a query object while one other is active.");
+        /*
+         * We *need* to lock webgl2 to GL>=3.0 on desktop, but we don't have a good
+         * mechanism to do this yet. See bug 898404.
+         */
+    }
+
+    nsRefPtr<WebGLQuery> globj = new WebGLQuery(this);
+
+    return globj.forget();
+}
+
+void
+WebGLContext::DeleteQuery(WebGLQuery *query)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!query)
+        return;
+
+    if (query->IsDeleted())
+        return;
+
+    if (query->IsActive()) {
+        EndQuery(query->mType);
+    }
+
+    if (mActiveOcclusionQuery && !gl->IsGLES2()) {
+        /* http://www.opengl.org/registry/specs/ARB/occlusion_query.txt
+         * Calling either GenQueriesARB or DeleteQueriesARB while any query of
+         * any target is active causes an INVALID_OPERATION error to be
+         * generated.
+         */
+        GenerateWarning("deleteQuery: the WebGL 2 prototype might generate INVALID_OPERATION"
+                        "when deleting a query object while one other is active.");
+    }
+
+    query->RequestDelete();
+}
+
+void
+WebGLContext::BeginQuery(WebGLenum target, WebGLQuery *query)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!ValidateTargetParameter(target, "beginQuery")) {
+        return;
+    }
+
+    if (!query) {
+        /* SPECS BeginQuery.1
+         * http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt
+         * BeginQueryEXT sets the active query object name for the query type given
+         * by <target> to <id>. If BeginQueryEXT is called with an <id> of zero, if
+         * the active query object name for <target> is non-zero (for the targets
+         * ANY_SAMPLES_PASSED_EXT and ANY_SAMPLES_PASSED_CONSERVATIVE_EXT, if the
+         * active query for either target is non-zero), if <id> is the name of an
+         * existing query object whose type does not match <target>, or if <id> is the
+         * active query object name for any query type, the error INVALID_OPERATION is
+         * generated.
+         */
+        ErrorInvalidOperation("beginQuery: query should not be null");
+        return;
+    }
+
+    if (query->IsDeleted()) {
+        /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt
+         * BeginQueryEXT fails and an INVALID_OPERATION error is generated if <id>
+         * is not a name returned from a previous call to GenQueriesEXT, or if such
+         * a name has since been deleted with DeleteQueriesEXT.
+         */
+        ErrorInvalidOperation("beginQuery: query has been deleted");
+        return;
+    }
+
+    if (query->HasEverBeenActive() &&
+        query->mType != target)
+    {
+        /*
+         * See SPECS BeginQuery.1
+         */
+        ErrorInvalidOperation("beginQuery: target doesn't match with the query type");
+        return;
+    }
+
+    if (GetActiveQueryByTarget(target)) {
+        /*
+         * See SPECS BeginQuery.1
+         */
+        ErrorInvalidOperation("beginQuery: an other query already active");
+        return;
+    }
+
+    if (!query->HasEverBeenActive()) {
+        query->mType = target;
+    }
+
+    MakeContextCurrent();
+
+    if (!gl->IsGLES2()) {
+        gl->fBeginQuery(LOCAL_GL_SAMPLES_PASSED, query->mGLName);
+    } else {
+        gl->fBeginQuery(target, query->mGLName);
+    }
+
+    GetActiveQueryByTarget(target) = query;
+}
+
+void
+WebGLContext::EndQuery(WebGLenum target)
+{
+    if (!IsContextStable())
+        return;
+
+    if (!ValidateTargetParameter(target, "endQuery")) {
+        return;
+    }
+
+    if (!GetActiveQueryByTarget(target) ||
+        target != GetActiveQueryByTarget(target)->mType)
+    {
+        /* http://www.khronos.org/registry/gles/extensions/EXT/EXT_occlusion_query_boolean.txt
+         * marks the end of the sequence of commands to be tracked for the query type
+         * given by <target>. The active query object for <target> is updated to
+         * indicate that query results are not available, and the active query object
+         * name for <target> is reset to zero. When the commands issued prior to
+         * EndQueryEXT have completed and a final query result is available, the
+         * query object active when EndQueryEXT is called is updated by the GL. The
+         * query object is updated to indicate that the query results are available
+         * and to contain the query result. If the active query object name for
+         * <target> is zero when EndQueryEXT is called, the error INVALID_OPERATION
+         * is generated.
+         */
+        ErrorInvalidOperation("endQuery: There is no active query of type %s.",
+                              GetQueryTargetEnumString(target));
+        return;
+    }
+
+    MakeContextCurrent();
+
+    if (!gl->IsGLES2()) {
+        gl->fEndQuery(LOCAL_GL_SAMPLES_PASSED);
+    } else {
+        gl->fEndQuery(target);
+    }
+
+    GetActiveQueryByTarget(target) = nullptr;
+}
+
+bool
+WebGLContext::IsQuery(WebGLQuery *query)
+{
+    if (!IsContextStable())
+        return false;
+
+    if (!query)
+        return false;
+
+    return ValidateObjectAllowDeleted("isQuery", query) &&
+           !query->IsDeleted() &&
+           query->HasEverBeenActive();
+}
+
+already_AddRefed<WebGLQuery>
+WebGLContext::GetQuery(WebGLenum target, WebGLenum pname)
+{
+    if (!IsContextStable())
+        return nullptr;
+
+    if (!ValidateTargetParameter(target, "getQuery")) {
+        return nullptr;
+    }
+
+    if (pname != LOCAL_GL_CURRENT_QUERY) {
+        /* OpenGL ES 3.0 spec 6.1.7
+         *  pname must be CURRENT_QUERY.
+         */
+        ErrorInvalidEnum("getQuery: pname must be CURRENT_QUERY");
+        return nullptr;
+    }
+
+    nsRefPtr<WebGLQuery> tmp = GetActiveQueryByTarget(target).get();
+    return tmp.forget();
+}
+
+JS::Value
+WebGLContext::GetQueryObject(JSContext* cx, WebGLQuery *query, WebGLenum pname)
+{
+    if (!IsContextStable())
+        return JS::NullValue();
+
+    if (!query) {
+        /* OpenGL ES 3.0 spec 6.1.7 (spec getQueryObject 1)
+         *  If id is not the name of a query object, or if the query object named by id is
+         *  currently active, then an INVALID_OPERATION error is generated. pname must be
+         *  QUERY_RESULT or QUERY_RESULT_AVAILABLE.
+         */
+        ErrorInvalidOperation("getQueryObject: query should not be null");
+        return JS::NullValue();
+    }
+
+    if (query->IsDeleted()) {
+        // See (spec getQueryObject 1)
+        ErrorInvalidOperation("getQueryObject: query has been deleted");
+        return JS::NullValue();
+    }
+
+    if (query->IsActive()) {
+        // See (spec getQueryObject 1)
+        ErrorInvalidOperation("getQueryObject: query is active");
+        return JS::NullValue();
+    }
+
+    if (!query->HasEverBeenActive()) {
+        /* See (spec getQueryObject 1)
+         *  If this instance of WebGLQuery has never been active before, that mean that
+         *  query->mGLName is not a query object yet.
+         */
+        ErrorInvalidOperation("getQueryObject: query has never been active");
+        return JS::NullValue();
+    }
+
+    switch (pname)
+    {
+        case LOCAL_GL_QUERY_RESULT_AVAILABLE:
+        {
+            GLuint returned = 0;
+
+            MakeContextCurrent();
+            gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT_AVAILABLE, &returned);
+
+            return JS::BooleanValue(returned != 0);
+        }
+
+        case LOCAL_GL_QUERY_RESULT:
+        {
+            GLuint returned = 0;
+
+            MakeContextCurrent();
+            gl->fGetQueryObjectuiv(query->mGLName, LOCAL_GL_QUERY_RESULT, &returned);
+
+            /*
+             * test (returned != 0) is important because ARB_occlusion_query on desktop drivers
+             * return the number of samples drawed when the OpenGL ES extension
+             * ARB_occlusion_query_boolean return only a boolean if a sample has been drawed.
+             */
+            return JS::BooleanValue(returned != 0);
+        }
+
+        default:
+            break;
+    }
+
+    ErrorInvalidEnum("getQueryObject: pname must be QUERY_RESULT{_AVAILABLE}");
+    return JS::NullValue();
+}
+
+bool
+WebGLContext::ValidateTargetParameter(WebGLenum target, const char* infos)
+{
+    if (target != LOCAL_GL_ANY_SAMPLES_PASSED &&
+        target != LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE)
+    {
+        ErrorInvalidEnum("%s: target must be ANY_SAMPLES_PASSED{_CONSERVATIVE}", infos);
+        return false;
+    }
+
+    return true;
+}
+
+WebGLRefPtr<WebGLQuery>&
+WebGLContext::GetActiveQueryByTarget(WebGLenum target)
+{
+    MOZ_ASSERT(ValidateTargetParameter(target, "private WebGLContext::GetActiveQueryByTarget"));
+
+    return mActiveOcclusionQuery;
+}
+
+
--- a/content/canvas/src/WebGLContextValidate.cpp
+++ b/content/canvas/src/WebGLContextValidate.cpp
@@ -1073,19 +1073,21 @@ WebGLContext::InitAndValidateGL()
         return false;
     }
 
     if (IsWebGL2() &&
         (!IsExtensionSupported(OES_vertex_array_object) ||
          !IsExtensionSupported(WEBGL_draw_buffers) ||
          !gl->IsExtensionSupported(gl::GLContext::EXT_gpu_shader4) ||
          !gl->IsExtensionSupported(gl::GLContext::EXT_blend_minmax) ||
-         !gl->IsExtensionSupported(gl::GLContext::XXX_draw_instanced)
+         !gl->IsExtensionSupported(gl::GLContext::XXX_draw_instanced) ||
+         (gl->IsGLES2() && !gl->IsExtensionSupported(gl::GLContext::EXT_occlusion_query_boolean))
         ))
     {
+        // Todo: Bug 898404: Only allow WebGL2 on GL>=3.0 on desktop GL.
         return false;
     }
 
     mMemoryPressureObserver
         = new WebGLMemoryPressureObserver(this);
     nsCOMPtr<nsIObserverService> observerService
         = mozilla::services::GetObserverService();
     if (observerService) {
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLQuery.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 "WebGLContext.h"
+#include "WebGLQuery.h"
+#include "mozilla/dom/WebGL2RenderingContextBinding.h"
+#include "nsContentUtils.h"
+
+using namespace mozilla;
+
+JSObject*
+WebGLQuery::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope) {
+    return dom::WebGLQueryBinding::Wrap(cx, scope, this);
+}
+
+WebGLQuery::WebGLQuery(WebGLContext* context)
+    : WebGLContextBoundObject(context)
+    , mGLName(0)
+    , mType(0)
+{
+    SetIsDOMBinding();
+    mContext->mQueries.insertBack(this);
+
+    mContext->MakeContextCurrent();
+    mContext->gl->fGenQueries(1, &mGLName);
+}
+
+void WebGLQuery::Delete() {
+    mContext->MakeContextCurrent();
+    mContext->gl->fDeleteQueries(1, &mGLName);
+    LinkedListElement<WebGLQuery>::removeFrom(mContext->mQueries);
+}
+
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WebGLQuery)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WebGLQuery)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WebGLQuery)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebGLQuery)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
new file mode 100644
--- /dev/null
+++ b/content/canvas/src/WebGLQuery.h
@@ -0,0 +1,89 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* 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 WEBGLQUERY_H_
+#define WEBGLQUERY_H_
+
+#include "WebGLObjectModel.h"
+#include "WebGLContext.h"
+
+#include "nsWrapperCache.h"
+
+#include "mozilla/LinkedList.h"
+
+namespace mozilla {
+
+class WebGLQuery MOZ_FINAL
+    : public nsISupports
+    , public WebGLRefCountedObject<WebGLQuery>
+    , public LinkedListElement<WebGLQuery>
+    , public WebGLContextBoundObject
+    , public nsWrapperCache
+{
+// -----------------------------------------------------------------------------
+// PUBLIC
+public:
+
+    // -------------------------------------------------------------------------
+    // CONSTRUCTOR & DESTRUCTOR
+
+    WebGLQuery(WebGLContext *context);
+
+    ~WebGLQuery() {
+        DeleteOnce();
+    };
+
+
+    // -------------------------------------------------------------------------
+    // MEMBER FUNCTIONS
+
+    bool IsActive() const
+    {
+        return mContext->GetActiveQueryByTarget(mType) == this;
+    }
+
+    bool HasEverBeenActive()
+    {
+        return mType != 0;
+    }
+
+
+    // -------------------------------------------------------------------------
+    // IMPLEMENT WebGLRefCountedObject and WebGLContextBoundObject
+
+    void Delete();
+
+    WebGLContext* GetParentObject() const
+    {
+        return Context();
+    }
+
+
+    // -------------------------------------------------------------------------
+    // IMPLEMENT NS
+    virtual JSObject* WrapObject(JSContext *cx,
+                                 JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
+
+    NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+    NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebGLQuery)
+
+
+// -----------------------------------------------------------------------------
+// PRIVATE
+private:
+
+    // -------------------------------------------------------------------------
+    // MEMBERS
+    WebGLuint mGLName;
+    WebGLenum mType;
+
+    // -------------------------------------------------------------------------
+    // FRIENDSHIPS
+    friend class WebGLContext;
+};
+
+} // namespace mozilla
+
+#endif
--- a/content/canvas/src/moz.build
+++ b/content/canvas/src/moz.build
@@ -26,16 +26,17 @@ CPP_SOURCES += [
 
 if CONFIG['MOZ_WEBGL']:
     CPP_SOURCES += [
         'WebGLActiveInfo.cpp',
         'WebGLBuffer.cpp',
         'WebGL1Context.cpp',
         'WebGL2Context.cpp',
         'WebGLContext.cpp',
+        'WebGLContextAsyncQueries.cpp',
         'WebGLContextGL.cpp',
         'WebGLContextUtils.cpp',
         'WebGLContextReporter.cpp',
         'WebGLContextValidate.cpp',
         'WebGLContextFramebufferOperations.cpp',
         'WebGLContextVertexArray.cpp',
         'WebGLContextVertices.cpp',
         'WebGLElementArrayCache.cpp',
@@ -51,16 +52,17 @@ if CONFIG['MOZ_WEBGL']:
         'WebGLExtensionStandardDerivatives.cpp',
         'WebGLExtensionTextureFilterAnisotropic.cpp',
         'WebGLExtensionTextureFloat.cpp',
         'WebGLExtensionTextureFloatLinear.cpp',
         'WebGLExtensionVertexArray.cpp',
         'WebGLFramebuffer.cpp',
         'WebGLObjectModel.cpp',
         'WebGLProgram.cpp',
+        'WebGLQuery.cpp',
         'WebGLRenderbuffer.cpp',
         'WebGLShader.cpp',
         'WebGLShaderPrecisionFormat.cpp',
         'WebGLTexelConversions.cpp',
         'WebGLTexture.cpp',
         'WebGLUniformLocation.cpp',
         'WebGLVertexArray.cpp',
     ]
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1270,16 +1270,21 @@ DOMInterfaces = {
    'headerFile': 'WebGLFramebuffer.h'
 },
 
 'WebGLProgram': {
    'nativeType': 'mozilla::WebGLProgram',
    'headerFile': 'WebGLProgram.h'
 },
 
+'WebGLQuery': {
+   'nativeType': 'mozilla::WebGLQuery',
+   'headerFile': 'WebGLQuery.h'
+},
+
 'WebGLRenderbuffer': {
    'nativeType': 'mozilla::WebGLRenderbuffer',
    'headerFile': 'WebGLRenderbuffer.h'
 },
 
 'WebGLRenderingContext': {
   'nativeType': 'mozilla::WebGLContext',
   'headerFile': 'WebGLContext.h',
--- a/dom/webidl/WebGL2RenderingContext.webidl
+++ b/dom/webidl/WebGL2RenderingContext.webidl
@@ -1,17 +1,21 @@
 /* -*- Mode: IDL; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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/.
  *
- * This IDL depend on WebGLRenderingContext.webidl
+ * This IDL depends on WebGLRenderingContext.webidl
  */
 
 [Pref="webgl.enable-prototype-webgl2"]
+interface WebGLQuery {
+};
+
+[Pref="webgl.enable-prototype-webgl2"]
 interface WebGL2RenderingContext : WebGLRenderingContext {
 
     /* draw buffers */
     const GLenum COLOR_ATTACHMENT1           = 0x8CE1;
     const GLenum COLOR_ATTACHMENT2           = 0x8CE2;
     const GLenum COLOR_ATTACHMENT3           = 0x8CE3;
     const GLenum COLOR_ATTACHMENT4           = 0x8CE4;
     const GLenum COLOR_ATTACHMENT5           = 0x8CE5;
@@ -48,19 +52,33 @@ interface WebGL2RenderingContext : WebGL
 
     /* vertex array objects */
     const GLenum VERTEX_ARRAY_BINDING        = 0x85B5;
 
     /* blend equations */
     const GLenum MIN                         = 0x8007;
     const GLenum MAX                         = 0x8008;
 
+    /* query objects */
+    const GLenum ANY_SAMPLES_PASSED          = 0x8C2F;
+    const GLenum ANY_SAMPLES_PASSED_CONSERVATIVE = 0x8D6A;
+    const GLenum CURRENT_QUERY               = 0x8865;
+    const GLenum QUERY_RESULT                = 0x8866;
+    const GLenum QUERY_RESULT_AVAILABLE      = 0x8867;
 
+
+    void beginQuery(GLenum target, WebGLQuery? queryObject);
     void bindVertexArray(WebGLVertexArray? arrayObject);
+    WebGLQuery? createQuery();
     WebGLVertexArray? createVertexArray();
     void drawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei primcount);
     void drawBuffers(sequence<GLenum> buffers);
     void drawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei primcount);
+    void deleteQuery(WebGLQuery? queryObject);
     void deleteVertexArray(WebGLVertexArray? arrayObject);
+    void endQuery(GLenum target);
+    WebGLQuery? getQuery(GLenum target, GLenum pname);
+    any getQueryObject(WebGLQuery? queryObject, GLenum pname);
+    [WebGLHandlesContextLoss] GLboolean isQuery(WebGLQuery? queryObject);
     [WebGLHandlesContextLoss] GLboolean isVertexArray(WebGLVertexArray? arrayObject);
 
 };
 
--- a/gfx/gl/GLContext.cpp
+++ b/gfx/gl/GLContext.cpp
@@ -93,16 +93,17 @@ static const char *sExtensionNames[] = {
     "GL_ARB_draw_buffers",
     "GL_EXT_draw_buffers",
     "GL_EXT_gpu_shader4",
     "GL_EXT_blend_minmax",
     "GL_ARB_draw_instanced",
     "GL_EXT_draw_instanced",
     "GL_NV_draw_instanced",
     "GL_ANGLE_instanced_array",
+    "GL_EXT_occlusion_query_boolean",
     nullptr
 };
 
 static int64_t sTextureMemoryUsage = 0;
 
 static int64_t
 GetTextureMemoryUsage()
 {
@@ -316,16 +317,17 @@ GLContext::InitWithPrefix(const char *pr
                 { (PRFuncPtr*) &mSymbols.fPointParameterf, { "PointParameterf", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQuery", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuiv", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueries", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueries", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryiv", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetQueryObjectiv, { "GetQueryObjectiv", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQuery", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQuery", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fDrawBuffer, { "DrawBuffer", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fDrawBuffers, { "DrawBuffers", nullptr } },
                 { nullptr, { nullptr } },
             };
 
             if (!LoadSymbols(&symbols_desktop[0], trygl, prefix)) {
                 NS_ERROR("Desktop symbols failed to load.");
                 mInitialized = false;
@@ -627,16 +629,45 @@ GLContext::InitWithPrefix(const char *pr
                 NS_ERROR("GL supports instanced draws without supplying its functions.");
 
                 MarkExtensionGroupUnsupported(XXX_draw_instanced);
                 mSymbols.fDrawArraysInstanced = nullptr;
                 mSymbols.fDrawElementsInstanced = nullptr;
             }
         }
 
+        if (IsGLES2() &&
+            IsExtensionSupported(EXT_occlusion_query_boolean)) {
+            SymLoadStruct queryObjectsSymbols[] = {
+                { (PRFuncPtr*) &mSymbols.fBeginQuery, { "BeginQuery", "BeginQueryEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGenQueries, { "GenQueries", "GenQueriesEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fDeleteQueries, { "DeleteQueries", "DeleteQueriesEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fEndQuery, { "EndQuery", "EndQueryEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryiv, { "GetQueryiv", "GetQueryivEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fGetQueryObjectuiv, { "GetQueryObjectuiv", "GetQueryObjectuivEXT", nullptr } },
+                { (PRFuncPtr*) &mSymbols.fIsQuery, { "IsQuery", "IsQueryEXT", nullptr } },
+                { nullptr, { nullptr } },
+            };
+
+            if (!LoadSymbols(queryObjectsSymbols, trygl, prefix)) {
+                NS_ERROR("GL ES supports query objects without supplying its functions.");
+
+                MarkExtensionUnsupported(EXT_occlusion_query_boolean);
+                mSymbols.fBeginQuery = nullptr;
+                mSymbols.fGenQueries = nullptr;
+                mSymbols.fDeleteQueries = nullptr;
+                mSymbols.fEndQuery = nullptr;
+                mSymbols.fGetQueryiv = nullptr;
+                mSymbols.fGetQueryObjectiv = nullptr;
+                mSymbols.fGetQueryObjectuiv = nullptr;
+                mSymbols.fIsQuery = nullptr;
+            }
+        }
+
+
         // Load developer symbols, don't fail if we can't find them.
         SymLoadStruct auxSymbols[] = {
                 { (PRFuncPtr*) &mSymbols.fGetTexImage, { "GetTexImage", nullptr } },
                 { (PRFuncPtr*) &mSymbols.fGetTexLevelParameteriv, { "GetTexLevelParameteriv", nullptr } },
                 { nullptr, { nullptr } },
         };
         bool warnOnFailures = DebugMode();
         LoadSymbols(&auxSymbols[0], trygl, prefix, warnOnFailures);
--- a/gfx/gl/GLContext.h
+++ b/gfx/gl/GLContext.h
@@ -376,16 +376,17 @@ public:
         ARB_draw_buffers,
         EXT_draw_buffers,
         EXT_gpu_shader4,
         EXT_blend_minmax,
         ARB_draw_instanced,
         EXT_draw_instanced,
         NV_draw_instanced,
         ANGLE_instanced_array,
+        EXT_occlusion_query_boolean,
         Extensions_Max,
         Extensions_End
     };
 
     bool IsExtensionSupported(GLExtensions aKnownExtension) const {
         return mAvailableExtensions[aKnownExtension];
     }
 
@@ -727,16 +728,17 @@ public:
     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;
@@ -986,16 +988,17 @@ public:
     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();
         AFTER_GL_CALL;
@@ -1035,28 +1038,31 @@ public:
         BEFORE_GL_CALL;
         GLint retval = mSymbols.fGetAttribLocation(program, name);
         AFTER_GL_CALL;
         return retval;
     }
 
     void fGetQueryiv(GLenum target, GLenum pname, GLint* params) {
         BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fGetQueryiv);
         mSymbols.fGetQueryiv(target, pname, params);
         AFTER_GL_CALL;
     }
 
     void fGetQueryObjectiv(GLuint id, GLenum pname, GLint* params) {
         BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fGetQueryObjectiv);
         mSymbols.fGetQueryObjectiv(id, pname, params);
         AFTER_GL_CALL;
     }
 
     void fGetQueryObjectuiv(GLuint id, GLenum pname, GLuint* params) {
         BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fGetQueryObjectuiv);
         mSymbols.fGetQueryObjectuiv(id, pname, params);
         AFTER_GL_CALL;
     }
 
 private:
     void raw_fGetIntegerv(GLenum pname, GLint *params) {
         BEFORE_GL_CALL;
         mSymbols.fGetIntegerv(pname, params);
@@ -1259,16 +1265,24 @@ public:
 
     realGLboolean fIsProgram(GLuint program) {
         BEFORE_GL_CALL;
         realGLboolean retval = mSymbols.fIsProgram(program);
         AFTER_GL_CALL;
         return retval;
     }
 
+    realGLboolean fIsQuery(GLuint query) {
+        BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fIsQuery);
+        realGLboolean retval = mSymbols.fIsQuery(query);
+        AFTER_GL_CALL;
+        return retval;
+    }
+
     realGLboolean fIsShader(GLuint shader) {
         BEFORE_GL_CALL;
         realGLboolean retval = mSymbols.fIsShader(shader);
         AFTER_GL_CALL;
         return retval;
     }
 
     realGLboolean fIsTexture(GLuint texture) {
@@ -1821,16 +1835,17 @@ private:
     void GLAPIENTRY raw_fGenFramebuffers(GLsizei n, GLuint* names) {
         BEFORE_GL_CALL;
         mSymbols.fGenFramebuffers(n, names);
         AFTER_GL_CALL;
     }
 
     void GLAPIENTRY raw_fGenQueries(GLsizei n, GLuint* names) {
         BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fGenQueries);
         mSymbols.fGenQueries(n, names);
         AFTER_GL_CALL;
     }
 
     void GLAPIENTRY raw_fGenRenderbuffers(GLsizei n, GLuint* names) {
         BEFORE_GL_CALL;
         mSymbols.fGenRenderbuffers(n, names);
         AFTER_GL_CALL;
@@ -1914,16 +1929,17 @@ private:
     void GLAPIENTRY raw_fDeleteTextures(GLsizei n, GLuint *names) {
         BEFORE_GL_CALL;
         mSymbols.fDeleteTextures(n, names);
         AFTER_GL_CALL;
     }
 
     void GLAPIENTRY raw_fDeleteQueries(GLsizei n, GLuint* names) {
         BEFORE_GL_CALL;
+        ASSERT_SYMBOL_PRESENT(fDeleteQueries);
         mSymbols.fDeleteQueries(n, names);
         AFTER_GL_CALL;
     }
 
 public:
     void GLAPIENTRY fDeleteQueries(GLsizei n, GLuint* names) {
         raw_fDeleteQueries(n, names);
         TRACKING_CONTEXT(DeletedQueries(this, n, names));
--- a/gfx/gl/GLContextSymbols.h
+++ b/gfx/gl/GLContextSymbols.h
@@ -168,16 +168,18 @@ struct GLContextSymbols
     typedef void (GLAPIENTRY * PFNGLHINTPROC) (GLenum target, GLenum mode);
     PFNGLHINTPROC fHint;
     typedef realGLboolean (GLAPIENTRY * PFNGLISBUFFERPROC) (GLuint buffer);
     PFNGLISBUFFERPROC fIsBuffer;
     typedef realGLboolean (GLAPIENTRY * PFNGLISENABLEDPROC) (GLenum cap);
     PFNGLISENABLEDPROC fIsEnabled;
     typedef realGLboolean (GLAPIENTRY * PFNGLISPROGRAMPROC) (GLuint program);
     PFNGLISPROGRAMPROC fIsProgram;
+    typedef realGLboolean (GLAPIENTRY * PFNGLISQUERYPROC) (GLuint id);
+    PFNGLISQUERYPROC fIsQuery;
     typedef realGLboolean (GLAPIENTRY * PFNGLISSHADERPROC) (GLuint shader);
     PFNGLISSHADERPROC fIsShader;
     typedef realGLboolean (GLAPIENTRY * PFNGLISTEXTUREPROC) (GLuint texture);
     PFNGLISTEXTUREPROC fIsTexture;
     typedef void (GLAPIENTRY * PFNGLLINEWIDTHPROC) (GLfloat width);
     PFNGLLINEWIDTHPROC fLineWidth;
     typedef void (GLAPIENTRY * PFNGLLINKPROGRAMPROC) (GLuint program);
     PFNGLLINKPROGRAMPROC fLinkProgram;
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -3044,16 +3044,22 @@ typedef uint64_t EGLTime;
 #define LOCAL_GL_SIGNALED                         0x9119
 #define LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT          0x00000001
 #define LOCAL_GL_TIMEOUT_IGNORED                  0xFFFFFFFFFFFFFFFFull
 #define LOCAL_GL_ALREADY_SIGNALED                 0x911A
 #define LOCAL_GL_TIMEOUT_EXPIRED                  0x911B
 #define LOCAL_GL_CONDITION_SATISFIED              0x911C
 #define LOCAL_GL_WAIT_FAILED                      0x911D
 
+// ARB_occlusion_query2
+#define LOCAL_GL_ANY_SAMPLES_PASSED               0x8C2F
+
+// EXT_occlusion_query_boolean
+#define LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE  0x8D6A
+
 // OES_EGL_image_external
 #define LOCAL_GL_TEXTURE_EXTERNAL                 0x8D65
 
 #define LOCAL_GL_MAX_VERTEX_UNIFORM_VECTORS       0x8DFB
 #define LOCAL_GL_MAX_VARYING_VECTORS              0x8DFC
 #define LOCAL_GL_MAX_FRAGMENT_UNIFORM_VECTORS     0x8DFD
 #define LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE   0x8B9A
 #define LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT 0x8B9B