Bug 1442502 - Require event loop roundtrip for WebGLSync. - r=kvark
authorJeff Gilbert <jgilbert@mozilla.com>
Tue, 20 Feb 2018 17:34:25 -0800
changeset 462112 77acafe9c8136da959a24c54b68d3975a2f36c7a
parent 462111 7532ccb5c0b39d6abbf7f67caee3f72c7f5addbe
child 462113 857cd5aebd4bb3b166bf37b56fb00d8f98dc6b64
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskvark
bugs1442502
milestone60.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 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;