Bug 1442502 - Require event loop roundtrip for WebGLSync. - r=kvark draft
authorJeff Gilbert <jgilbert@mozilla.com>
Tue, 20 Feb 2018 17:34:25 -0800
changeset 762843 73f9fbdf1152053963be8c851714912cbeb46c12
parent 762786 f4e33c42faa72907453381bc00fe40130c76b86e
push id101268
push userbmo:jgilbert@mozilla.com
push dateSat, 03 Mar 2018 01:40:06 +0000
reviewerskvark
bugs1442502
milestone60.0a1
Bug 1442502 - Require event loop roundtrip for WebGLSync. - r=kvark MozReview-Commit-ID: 6h6j2LvJdXm
dom/canvas/WebGL2ContextSync.cpp
dom/canvas/WebGLContext.cpp
dom/canvas/WebGLContext.h
dom/canvas/WebGLQuery.cpp
dom/canvas/WebGLQuery.h
dom/canvas/WebGLSync.h
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -25,16 +25,20 @@ WebGL2Context::FenceSync(GLenum conditio
     }
 
     if (flags != 0) {
         ErrorInvalidValue("fenceSync: flags must be 0");
         return nullptr;
     }
 
     RefPtr<WebGLSync> globj = new WebGLSync(this, condition, flags);
+
+    const auto& availRunnable = EnsureAvailabilityRunnable();
+    availRunnable->mSyncs.push_back(globj);
+
     return globj.forget();
 }
 
 bool
 WebGL2Context::IsSync(const WebGLSync* sync)
 {
     if (!ValidateIsObject("isSync", sync))
         return false;
@@ -67,16 +71,27 @@ WebGL2Context::ClientWaitSync(const WebG
     }
 
     if (timeout > kMaxClientWaitSyncTimeoutNS) {
         ErrorInvalidOperation("%s: `timeout` must not exceed %s nanoseconds.", funcName,
                               "MAX_CLIENT_WAIT_TIMEOUT_WEBGL");
         return LOCAL_GL_WAIT_FAILED;
     }
 
+    const bool canBeAvailable = (sync.mCanBeAvailable ||
+                                 gfxPrefs::WebGLImmediateQueries());
+    if (!canBeAvailable) {
+        if (timeout) {
+            GenerateWarning("%s: Sync object not yet queryable. Please wait for the event"
+                            " loop.",
+                            funcName);
+        }
+        return LOCAL_GL_WAIT_FAILED;
+    }
+
     const auto ret = gl->fClientWaitSync(sync.mGLName, flags, timeout);
 
     if (ret == LOCAL_GL_CONDITION_SATISFIED ||
         ret == LOCAL_GL_ALREADY_SIGNALED)
     {
         sync.MarkSignaled();
     }
 
@@ -115,16 +130,23 @@ WebGL2Context::GetSyncParameter(JSContex
     if (IsContextLost())
         return;
 
     if (!ValidateObject(funcName, sync))
         return;
 
     ////
 
+    const bool canBeAvailable = (sync.mCanBeAvailable ||
+                                 gfxPrefs::WebGLImmediateQueries());
+    if (!canBeAvailable && pname == LOCAL_GL_SYNC_STATUS) {
+        retval.set(JS::Int32Value(LOCAL_GL_UNSIGNALED));
+        return;
+    }
+
     GLint result = 0;
     switch (pname) {
     case LOCAL_GL_OBJECT_TYPE:
     case LOCAL_GL_SYNC_STATUS:
     case LOCAL_GL_SYNC_CONDITION:
     case LOCAL_GL_SYNC_FLAGS:
         gl->fGetSynciv(sync.mGLName, pname, 1, nullptr, &result);
 
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -227,16 +227,20 @@ WebGLContext::DestroyResourcesAndContext
     mDefaultTransformFeedback = nullptr;
 
     mQuerySlot_SamplesPassed = nullptr;
     mQuerySlot_TFPrimsWritten = nullptr;
     mQuerySlot_TimeElapsed = nullptr;
 
     mIndexedUniformBufferBindings.clear();
 
+    if (mAvailabilityRunnable) {
+        mAvailabilityRunnable->Run();
+    }
+
     //////
 
     ClearLinkedList(mBuffers);
     ClearLinkedList(mFramebuffers);
     ClearLinkedList(mPrograms);
     ClearLinkedList(mQueries);
     ClearLinkedList(mRenderbuffers);
     ClearLinkedList(mSamplers);
@@ -2446,16 +2450,64 @@ WebGLContext::UpdateMaxDrawBuffers()
     mGLMaxDrawBuffers = gl->GetIntAs<uint32_t>(LOCAL_GL_MAX_DRAW_BUFFERS);
 
     // WEBGL_draw_buffers:
     // "The value of the MAX_COLOR_ATTACHMENTS_WEBGL parameter must be greater than or
     //  equal to that of the MAX_DRAW_BUFFERS_WEBGL parameter."
     mGLMaxDrawBuffers = std::min(mGLMaxDrawBuffers, mGLMaxColorAttachments);
 }
 
+// --
+
+webgl::AvailabilityRunnable*
+WebGLContext::EnsureAvailabilityRunnable()
+{
+    if (!mAvailabilityRunnable) {
+        RefPtr<webgl::AvailabilityRunnable> runnable = new webgl::AvailabilityRunnable(this);
+
+        nsIDocument* document = GetOwnerDoc();
+        if (document) {
+            document->Dispatch(TaskCategory::Other, runnable.forget());
+        } else {
+            NS_DispatchToCurrentThread(runnable.forget());
+        }
+    }
+    return mAvailabilityRunnable;
+}
+
+webgl::AvailabilityRunnable::AvailabilityRunnable(WebGLContext* const webgl)
+    : Runnable("webgl::AvailabilityRunnable")
+    , mWebGL(webgl)
+{
+    mWebGL->mAvailabilityRunnable = this;
+}
+
+webgl::AvailabilityRunnable::~AvailabilityRunnable()
+{
+    MOZ_ASSERT(mQueries.empty());
+    MOZ_ASSERT(mSyncs.empty());
+}
+
+nsresult
+webgl::AvailabilityRunnable::Run()
+{
+    for (const auto& cur : mQueries) {
+        cur->mCanBeAvailable = true;
+    }
+    mQueries.clear();
+
+    for (const auto& cur : mSyncs) {
+        cur->mCanBeAvailable = true;
+    }
+    mSyncs.clear();
+
+    mWebGL->mAvailabilityRunnable = nullptr;
+    return NS_OK;
+}
+
 ////////////////////////////////////////////////////////////////////////////////
 // XPCOM goop
 
 void
 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& callback,
                             const std::vector<IndexedBufferBinding>& field,
                             const char* name, uint32_t flags)
 {
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -98,16 +98,17 @@ class SourceSurface;
 class VRLayerChild;
 } // namespace gfx
 
 namespace gl {
 class MozFramebuffer;
 } // namespace gl
 
 namespace webgl {
+class AvailabilityRunnable;
 struct LinkedProgramInfo;
 class ShaderValidator;
 class TexUnpackBlob;
 struct UniformInfo;
 struct UniformBlockInfo;
 } // namespace webgl
 
 WebGLTexelFormat GetWebGLTexelFormat(TexInternalFormat format);
@@ -265,16 +266,33 @@ struct TexImageSourceAdapter final : pub
     }
 
     TexImageSourceAdapter(const dom::Element* domElem, ErrorResult* const out_error) {
         mDomElem = domElem;
         mOut_error = out_error;
     }
 };
 
+// --
+
+namespace webgl {
+class AvailabilityRunnable final : public Runnable
+{
+public:
+    const RefPtr<WebGLContext> mWebGL; // Prevent CC
+    std::vector<RefPtr<WebGLQuery>> mQueries;
+    std::vector<RefPtr<WebGLSync>> mSyncs;
+
+    explicit AvailabilityRunnable(WebGLContext* webgl);
+    ~AvailabilityRunnable();
+
+    NS_IMETHOD Run() override;
+};
+} // namespace webgl
+
 ////////////////////////////////////////////////////////////////////////////////
 
 class WebGLContext
     : public nsICanvasRenderingContextInternal
     , public nsSupportsWeakReference
     , public WebGLContextUnchecked
     , public nsWrapperCache
 {
@@ -292,16 +310,17 @@ class WebGLContext
     friend class WebGLExtensionCompressedTextureS3TC;
     friend class WebGLExtensionCompressedTextureS3TC_SRGB;
     friend class WebGLExtensionDepthTexture;
     friend class WebGLExtensionDisjointTimerQuery;
     friend class WebGLExtensionDrawBuffers;
     friend class WebGLExtensionLoseContext;
     friend class WebGLExtensionVertexArray;
     friend class WebGLMemoryTracker;
+    friend class webgl::AvailabilityRunnable;
     friend struct webgl::LinkedProgramInfo;
     friend struct webgl::UniformBlockInfo;
 
     enum {
         UNPACK_FLIP_Y_WEBGL = 0x9240,
         UNPACK_PREMULTIPLY_ALPHA_WEBGL = 0x9241,
         // We throw InvalidOperation in TexImage if we fail to use GPU fast-path
         // for texture copy when it is set to true, only for debug purpose.
@@ -2045,16 +2064,22 @@ public:
     virtual UniquePtr<webgl::FormatUsageAuthority>
     CreateFormatUsage(gl::GLContext* gl) const = 0;
 
 
     const decltype(mBound2DTextures)* TexListForElemType(GLenum elemType) const;
 
     void UpdateMaxDrawBuffers();
 
+    // --
+private:
+    webgl::AvailabilityRunnable* mAvailabilityRunnable = nullptr;
+public:
+    webgl::AvailabilityRunnable* EnsureAvailabilityRunnable();
+
     // Friend list
     friend class ScopedCopyTexImageSource;
     friend class ScopedResolveTexturesForDraw;
     friend class ScopedUnpackReset;
     friend class webgl::TexUnpackBlob;
     friend class webgl::TexUnpackBytes;
     friend class webgl::TexUnpackImage;
     friend class webgl::TexUnpackSurface;
--- a/dom/canvas/WebGLQuery.cpp
+++ b/dom/canvas/WebGLQuery.cpp
@@ -8,74 +8,42 @@
 #include "gfxPrefs.h"
 #include "GLContext.h"
 #include "mozilla/dom/WebGL2RenderingContextBinding.h"
 #include "nsContentUtils.h"
 #include "WebGLContext.h"
 
 namespace mozilla {
 
-class AvailableRunnable final : public Runnable
-{
-    const RefPtr<WebGLQuery> mQuery;
-
-public:
-  explicit AvailableRunnable(WebGLQuery* query)
-    : Runnable("AvailableRunnable")
-    , mQuery(query)
-  {
-  }
-
-  NS_IMETHOD Run() override
-  {
-    mQuery->mCanBeAvailable = true;
-    return NS_OK;
-    }
-};
-
 ////
 
 static GLuint
 GenQuery(gl::GLContext* gl)
 {
     GLuint ret = 0;
     gl->fGenQueries(1, &ret);
     return ret;
 }
 
 WebGLQuery::WebGLQuery(WebGLContext* webgl)
     : WebGLRefCountedObject(webgl)
     , mGLName(GenQuery(mContext->gl))
     , mTarget(0)
     , mActiveSlot(nullptr)
-    , mCanBeAvailable(false)
 {
     mContext->mQueries.insertBack(this);
 }
 
 void
 WebGLQuery::Delete()
 {
     mContext->gl->fDeleteQueries(1, &mGLName);
     LinkedListElement<WebGLQuery>::removeFrom(mContext->mQueries);
 }
 
-static void
-DispatchAvailableRunnable(WebGLQuery* query)
-{
-    RefPtr<AvailableRunnable> runnable = new AvailableRunnable(query);
-
-    nsIDocument* document = query->mContext->GetOwnerDoc();
-    if (document) {
-        document->Dispatch(TaskCategory::Other, runnable.forget());
-        return;
-    }
-    NS_DispatchToCurrentThread(runnable.forget());
-}
-
 ////
 
 static GLenum
 TargetForDriver(const gl::GLContext* gl, GLenum target)
 {
     switch (target) {
     case LOCAL_GL_ANY_SAMPLES_PASSED:
     case LOCAL_GL_ANY_SAMPLES_PASSED_CONSERVATIVE:
@@ -129,17 +97,18 @@ WebGLQuery::EndQuery()
 
     const auto& gl = mContext->gl;
 
     const auto driverTarget = TargetForDriver(gl, mTarget);
     gl->fEndQuery(driverTarget);
 
     ////
 
-    DispatchAvailableRunnable(this);
+    const auto& availRunnable = mContext->EnsureAvailabilityRunnable();
+    availRunnable->mQueries.push_back(this);
 }
 
 void
 WebGLQuery::GetQueryParameter(GLenum pname, JS::MutableHandleValue retval) const
 {
     const char funcName[] = "getQueryParameter";
 
     switch (pname) {
@@ -252,17 +221,18 @@ WebGLQuery::QueryCounter(const char* fun
     }
 
     mTarget = target;
     mCanBeAvailable = false;
 
     const auto& gl = mContext->gl;
     gl->fQueryCounter(mGLName, mTarget);
 
-    DispatchAvailableRunnable(this);
+    const auto& availRunnable = mContext->EnsureAvailabilityRunnable();
+    availRunnable->mQueries.push_back(this);
 }
 
 ////
 
 JSObject*
 WebGLQuery::WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto)
 {
     return dom::WebGLQueryBinding::Wrap(cx, this, givenProto);
--- a/dom/canvas/WebGLQuery.h
+++ b/dom/canvas/WebGLQuery.h
@@ -8,32 +8,35 @@
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 
 #include "WebGLObjectModel.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
+namespace webgl {
+class AvailabilityRunnable;
+} // namespace webgl
 
 class WebGLQuery final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLQuery>
     , public LinkedListElement<WebGLQuery>
 {
-    friend class AvailableRunnable;
+    friend class webgl::AvailabilityRunnable;
     friend class WebGLRefCountedObject<WebGLQuery>;
 
 public:
     const GLuint mGLName;
 private:
     GLenum mTarget;
     WebGLRefPtr<WebGLQuery>* mActiveSlot;
 
-    bool mCanBeAvailable; // Track whether the event loop has spun
+    bool mCanBeAvailable = false; // Track whether the event loop has spun
 
     ////
 public:
     GLenum Target() const { return mTarget; }
     bool IsActive() const { return bool(mActiveSlot); }
 
     ////
 
--- a/dom/canvas/WebGLSync.h
+++ b/dom/canvas/WebGLSync.h
@@ -6,26 +6,31 @@
 #ifndef WEBGL_SYNC_H_
 #define WEBGL_SYNC_H_
 
 #include "mozilla/LinkedList.h"
 #include "nsWrapperCache.h"
 #include "WebGLObjectModel.h"
 
 namespace mozilla {
+namespace webgl {
+class AvailabilityRunnable;
+} // namespace webgl
 
 class WebGLSync final
     : public nsWrapperCache
     , public WebGLRefCountedObject<WebGLSync>
     , public LinkedListElement<WebGLSync>
 {
     friend class WebGL2Context;
+    friend class webgl::AvailabilityRunnable;
 
     const GLsync mGLName;
     const uint64_t mFenceId;
+    bool mCanBeAvailable = false;
 
 public:
     WebGLSync(WebGLContext* webgl, GLenum condition, GLbitfield flags);
 
     void Delete();
     WebGLContext* GetParentObject() const;
 
     virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> givenProto) override;